Coverage Report

Created: 2026-04-01 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cpl_conv.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  CPL - Common Portability Library
4
 * Purpose:  Convenience functions.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1998, Frank Warmerdam
9
 * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_config.h"
15
16
#if defined(HAVE_USELOCALE) && !defined(__FreeBSD__)
17
// For uselocale, define _XOPEN_SOURCE = 700
18
// and OpenBSD with libcxx 19.1.7 requires 800 for vasprintf
19
// (cf https://github.com/OSGeo/gdal/issues/12619)
20
// (not sure if the following is still up to date...) but on Solaris, we don't
21
// have uselocale and we cannot have std=c++11 with _XOPEN_SOURCE != 600
22
#if defined(__sun__) && __cplusplus >= 201103L
23
#if _XOPEN_SOURCE != 600
24
#ifdef _XOPEN_SOURCE
25
#undef _XOPEN_SOURCE
26
#endif
27
#define _XOPEN_SOURCE 600
28
#endif
29
#else
30
#ifdef _XOPEN_SOURCE
31
#undef _XOPEN_SOURCE
32
#endif
33
#define _XOPEN_SOURCE 800
34
#endif
35
#endif
36
37
// For atoll (at least for NetBSD)
38
#ifndef _ISOC99_SOURCE
39
#define _ISOC99_SOURCE
40
#endif
41
42
#ifdef MSVC_USE_VLD
43
#include <vld.h>
44
#endif
45
46
#include "cpl_conv.h"
47
48
#include <algorithm>
49
#include <atomic>
50
#include <cctype>
51
#include <cerrno>
52
#include <climits>
53
#include <clocale>
54
#include <cmath>
55
#include <cstdlib>
56
#include <cstring>
57
#include <ctime>
58
#include <mutex>
59
#include <set>
60
61
#if HAVE_UNISTD_H
62
#include <unistd.h>
63
#endif
64
#if HAVE_XLOCALE_H
65
#include <xlocale.h>  // for LC_NUMERIC_MASK on MacOS
66
#endif
67
68
#include <sys/types.h>  // open
69
70
#if defined(__FreeBSD__)
71
#include <sys/user.h>  // must be after sys/types.h
72
#include <sys/sysctl.h>
73
#endif
74
75
#include <sys/stat.h>  // open
76
#include <fcntl.h>     // open, fcntl
77
78
#ifdef _WIN32
79
#include <io.h>  // _isatty, _wopen
80
#else
81
#include <unistd.h>  // isatty, fcntl
82
#if HAVE_GETRLIMIT
83
#include <sys/resource.h>  // getrlimit
84
#include <sys/time.h>      // getrlimit
85
#endif
86
#endif
87
88
#include <string>
89
90
#if __cplusplus >= 202002L
91
#include <bit>  // For std::endian
92
#endif
93
94
#include "cpl_config.h"
95
#include "cpl_multiproc.h"
96
#include "cpl_string.h"
97
#include "cpl_vsi.h"
98
#include "cpl_vsil_curl_priv.h"
99
#include "cpl_known_config_options.h"
100
101
#ifdef DEBUG
102
#define OGRAPISPY_ENABLED
103
#endif
104
#ifdef OGRAPISPY_ENABLED
105
// Keep in sync with ograpispy.cpp
106
void OGRAPISPYCPLSetConfigOption(const char *, const char *);
107
void OGRAPISPYCPLSetThreadLocalConfigOption(const char *, const char *);
108
#endif
109
110
// Uncomment to get list of options that have been fetched and set.
111
// #define DEBUG_CONFIG_OPTIONS
112
113
static CPLMutex *hConfigMutex = nullptr;
114
static volatile char **g_papszConfigOptions = nullptr;
115
static bool gbIgnoreEnvVariables =
116
    false;  // if true, only take into account configuration options set through
117
            // configuration file or
118
            // CPLSetConfigOption()/CPLSetThreadLocalConfigOption()
119
120
static std::vector<std::pair<CPLSetConfigOptionSubscriber, void *>>
121
    gSetConfigOptionSubscribers{};
122
123
// Used by CPLOpenShared() and friends.
124
static CPLMutex *hSharedFileMutex = nullptr;
125
static int nSharedFileCount = 0;
126
static CPLSharedFileInfo *pasSharedFileList = nullptr;
127
128
// Used by CPLsetlocale().
129
static CPLMutex *hSetLocaleMutex = nullptr;
130
131
// Note: ideally this should be added in CPLSharedFileInfo*
132
// but CPLSharedFileInfo is exposed in the API, hence that trick
133
// to hide this detail.
134
typedef struct
135
{
136
    GIntBig nPID;  // pid of opening thread.
137
} CPLSharedFileInfoExtra;
138
139
static volatile CPLSharedFileInfoExtra *pasSharedFileListExtra = nullptr;
140
141
static const char *
142
CPLGetThreadLocalConfigOption(const char *pszKey, const char *pszDefault,
143
                              bool bSubstituteNullValueMarkerWithNull);
144
145
static const char *
146
CPLGetGlobalConfigOption(const char *pszKey, const char *pszDefault,
147
                         bool bSubstituteNullValueMarkerWithNull);
148
149
/************************************************************************/
150
/*                             CPLCalloc()                              */
151
/************************************************************************/
152
153
/**
154
 * Safe version of calloc().
155
 *
156
 * This function is like the C library calloc(), but raises a CE_Fatal
157
 * error with CPLError() if it fails to allocate the desired memory.  It
158
 * should be used for small memory allocations that are unlikely to fail
159
 * and for which the application is unwilling to test for out of memory
160
 * conditions.  It uses VSICalloc() to get the memory, so any hooking of
161
 * VSICalloc() will apply to CPLCalloc() as well.  CPLFree() or VSIFree()
162
 * can be used free memory allocated by CPLCalloc().
163
 *
164
 * @param nCount number of objects to allocate.
165
 * @param nSize size (in bytes) of object to allocate.
166
 * @return pointer to newly allocated memory, only NULL if nSize * nCount is
167
 * NULL.
168
 */
169
170
void *CPLCalloc(size_t nCount, size_t nSize)
171
172
19.5k
{
173
19.5k
    if (nSize * nCount == 0)
174
0
        return nullptr;
175
176
19.5k
    void *pReturn = CPLMalloc(nCount * nSize);
177
19.5k
    memset(pReturn, 0, nCount * nSize);
178
19.5k
    return pReturn;
179
19.5k
}
180
181
/************************************************************************/
182
/*                             CPLMalloc()                              */
183
/************************************************************************/
184
185
/**
186
 * Safe version of malloc().
187
 *
188
 * This function is like the C library malloc(), but raises a CE_Fatal
189
 * error with CPLError() if it fails to allocate the desired memory.  It
190
 * should be used for small memory allocations that are unlikely to fail
191
 * and for which the application is unwilling to test for out of memory
192
 * conditions.  It uses VSIMalloc() to get the memory, so any hooking of
193
 * VSIMalloc() will apply to CPLMalloc() as well.  CPLFree() or VSIFree()
194
 * can be used free memory allocated by CPLMalloc().
195
 *
196
 * @param nSize size (in bytes) of memory block to allocate.
197
 * @return pointer to newly allocated memory, only NULL if nSize is zero.
198
 */
199
200
void *CPLMalloc(size_t nSize)
201
202
124k
{
203
124k
    if (nSize == 0)
204
24.5k
        return nullptr;
205
206
100k
    if ((nSize >> (8 * sizeof(nSize) - 1)) != 0)
207
0
    {
208
        // coverity[dead_error_begin]
209
0
        CPLError(CE_Failure, CPLE_AppDefined,
210
0
                 "CPLMalloc(%ld): Silly size requested.",
211
0
                 static_cast<long>(nSize));
212
0
        return nullptr;
213
0
    }
214
215
100k
    void *pReturn = VSIMalloc(nSize);
216
100k
    if (pReturn == nullptr)
217
0
    {
218
0
        if (nSize < 2000)
219
0
        {
220
0
            CPLEmergencyError("CPLMalloc(): Out of memory allocating a small "
221
0
                              "number of bytes.");
222
0
        }
223
224
0
        CPLError(CE_Fatal, CPLE_OutOfMemory,
225
0
                 "CPLMalloc(): Out of memory allocating %ld bytes.",
226
0
                 static_cast<long>(nSize));
227
0
    }
228
229
100k
    return pReturn;
230
100k
}
231
232
/************************************************************************/
233
/*                             CPLRealloc()                             */
234
/************************************************************************/
235
236
/**
237
 * Safe version of realloc().
238
 *
239
 * This function is like the C library realloc(), but raises a CE_Fatal
240
 * error with CPLError() if it fails to allocate the desired memory.  It
241
 * should be used for small memory allocations that are unlikely to fail
242
 * and for which the application is unwilling to test for out of memory
243
 * conditions.  It uses VSIRealloc() to get the memory, so any hooking of
244
 * VSIRealloc() will apply to CPLRealloc() as well.  CPLFree() or VSIFree()
245
 * can be used free memory allocated by CPLRealloc().
246
 *
247
 * It is also safe to pass NULL in as the existing memory block for
248
 * CPLRealloc(), in which case it uses VSIMalloc() to allocate a new block.
249
 *
250
 * @param pData existing memory block which should be copied to the new block.
251
 * @param nNewSize new size (in bytes) of memory block to allocate.
252
 * @return pointer to allocated memory, only NULL if nNewSize is zero.
253
 */
254
255
void *CPLRealloc(void *pData, size_t nNewSize)
256
257
8.46k
{
258
8.46k
    if (nNewSize == 0)
259
0
    {
260
0
        VSIFree(pData);
261
0
        return nullptr;
262
0
    }
263
264
8.46k
    if ((nNewSize >> (8 * sizeof(nNewSize) - 1)) != 0)
265
0
    {
266
        // coverity[dead_error_begin]
267
0
        CPLError(CE_Failure, CPLE_AppDefined,
268
0
                 "CPLRealloc(%ld): Silly size requested.",
269
0
                 static_cast<long>(nNewSize));
270
0
        return nullptr;
271
0
    }
272
273
8.46k
    void *pReturn = nullptr;
274
275
8.46k
    if (pData == nullptr)
276
5.66k
        pReturn = VSIMalloc(nNewSize);
277
2.80k
    else
278
2.80k
        pReturn = VSIRealloc(pData, nNewSize);
279
280
8.46k
    if (pReturn == nullptr)
281
0
    {
282
0
        if (nNewSize < 2000)
283
0
        {
284
0
            char szSmallMsg[80] = {};
285
286
0
            snprintf(szSmallMsg, sizeof(szSmallMsg),
287
0
                     "CPLRealloc(): Out of memory allocating %ld bytes.",
288
0
                     static_cast<long>(nNewSize));
289
0
            CPLEmergencyError(szSmallMsg);
290
0
        }
291
0
        else
292
0
        {
293
0
            CPLError(CE_Fatal, CPLE_OutOfMemory,
294
0
                     "CPLRealloc(): Out of memory allocating %ld bytes.",
295
0
                     static_cast<long>(nNewSize));
296
0
        }
297
0
    }
298
299
8.46k
    return pReturn;
300
8.46k
}
301
302
/************************************************************************/
303
/*                             CPLStrdup()                              */
304
/************************************************************************/
305
306
/**
307
 * Safe version of strdup() function.
308
 *
309
 * This function is similar to the C library strdup() function, but if
310
 * the memory allocation fails it will issue a CE_Fatal error with
311
 * CPLError() instead of returning NULL. Memory
312
 * allocated with CPLStrdup() can be freed with CPLFree() or VSIFree().
313
 *
314
 * It is also safe to pass a NULL string into CPLStrdup().  CPLStrdup()
315
 * will allocate and return a zero length string (as opposed to a NULL
316
 * string).
317
 *
318
 * @param pszString input string to be duplicated.  May be NULL.
319
 * @return pointer to a newly allocated copy of the string.  Free with
320
 * CPLFree() or VSIFree().
321
 */
322
323
char *CPLStrdup(const char *pszString)
324
325
17.3k
{
326
17.3k
    if (pszString == nullptr)
327
0
        pszString = "";
328
329
17.3k
    const size_t nLen = strlen(pszString);
330
17.3k
    char *pszReturn = static_cast<char *>(CPLMalloc(nLen + 1));
331
17.3k
    memcpy(pszReturn, pszString, nLen + 1);
332
17.3k
    return (pszReturn);
333
17.3k
}
334
335
/************************************************************************/
336
/*                             CPLStrlwr()                              */
337
/************************************************************************/
338
339
/**
340
 * Convert each characters of the string to lower case.
341
 *
342
 * For example, "ABcdE" will be converted to "abcde".
343
 * Starting with GDAL 3.9, this function is no longer locale dependent.
344
 *
345
 * @param pszString input string to be converted.
346
 * @return pointer to the same string, pszString.
347
 */
348
349
char *CPLStrlwr(char *pszString)
350
351
0
{
352
0
    if (pszString == nullptr)
353
0
        return nullptr;
354
355
0
    char *pszTemp = pszString;
356
357
0
    while (*pszTemp)
358
0
    {
359
0
        *pszTemp =
360
0
            static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszTemp)));
361
0
        pszTemp++;
362
0
    }
363
364
0
    return pszString;
365
0
}
366
367
/************************************************************************/
368
/*                              CPLFGets()                              */
369
/*                                                                      */
370
/*      Note: LF = \n = ASCII 10                                        */
371
/*            CR = \r = ASCII 13                                        */
372
/************************************************************************/
373
374
// ASCII characters.
375
constexpr char knLF = 10;
376
constexpr char knCR = 13;
377
378
/**
379
 * Reads in at most one less than nBufferSize characters from the fp
380
 * stream and stores them into the buffer pointed to by pszBuffer.
381
 * Reading stops after an EOF or a newline. If a newline is read, it
382
 * is _not_ stored into the buffer. A '\\0' is stored after the last
383
 * character in the buffer. All three types of newline terminators
384
 * recognized by the CPLFGets(): single '\\r' and '\\n' and '\\r\\n'
385
 * combination.
386
 *
387
 * @param pszBuffer pointer to the targeting character buffer.
388
 * @param nBufferSize maximum size of the string to read (not including
389
 * terminating '\\0').
390
 * @param fp file pointer to read from.
391
 * @return pointer to the pszBuffer containing a string read
392
 * from the file or NULL if the error or end of file was encountered.
393
 */
394
395
char *CPLFGets(char *pszBuffer, int nBufferSize, FILE *fp)
396
397
0
{
398
0
    if (nBufferSize == 0 || pszBuffer == nullptr || fp == nullptr)
399
0
        return nullptr;
400
401
    /* -------------------------------------------------------------------- */
402
    /*      Let the OS level call read what it things is one line.  This    */
403
    /*      will include the newline.  On windows, if the file happens      */
404
    /*      to be in text mode, the CRLF will have been converted to        */
405
    /*      just the newline (LF).  If it is in binary mode it may well     */
406
    /*      have both.                                                      */
407
    /* -------------------------------------------------------------------- */
408
0
    const long nOriginalOffset = VSIFTell(fp);
409
0
    if (VSIFGets(pszBuffer, nBufferSize, fp) == nullptr)
410
0
        return nullptr;
411
412
0
    int nActuallyRead = static_cast<int>(strlen(pszBuffer));
413
0
    if (nActuallyRead == 0)
414
0
        return nullptr;
415
416
    /* -------------------------------------------------------------------- */
417
    /*      If we found \r and out buffer is full, it is possible there     */
418
    /*      is also a pending \n.  Check for it.                            */
419
    /* -------------------------------------------------------------------- */
420
0
    if (nBufferSize == nActuallyRead + 1 &&
421
0
        pszBuffer[nActuallyRead - 1] == knCR)
422
0
    {
423
0
        const int chCheck = fgetc(fp);
424
0
        if (chCheck != knLF)
425
0
        {
426
            // unget the character.
427
0
            if (VSIFSeek(fp, nOriginalOffset + nActuallyRead, SEEK_SET) == -1)
428
0
            {
429
0
                CPLError(CE_Failure, CPLE_FileIO,
430
0
                         "Unable to unget a character");
431
0
            }
432
0
        }
433
0
    }
434
435
    /* -------------------------------------------------------------------- */
436
    /*      Trim off \n, \r or \r\n if it appears at the end.  We don't     */
437
    /*      need to do any "seeking" since we want the newline eaten.       */
438
    /* -------------------------------------------------------------------- */
439
0
    if (nActuallyRead > 1 && pszBuffer[nActuallyRead - 1] == knLF &&
440
0
        pszBuffer[nActuallyRead - 2] == knCR)
441
0
    {
442
0
        pszBuffer[nActuallyRead - 2] = '\0';
443
0
    }
444
0
    else if (pszBuffer[nActuallyRead - 1] == knLF ||
445
0
             pszBuffer[nActuallyRead - 1] == knCR)
446
0
    {
447
0
        pszBuffer[nActuallyRead - 1] = '\0';
448
0
    }
449
450
    /* -------------------------------------------------------------------- */
451
    /*      Search within the string for a \r (MacOS convention             */
452
    /*      apparently), and if we find it we need to trim the string,      */
453
    /*      and seek back.                                                  */
454
    /* -------------------------------------------------------------------- */
455
0
    char *pszExtraNewline = strchr(pszBuffer, knCR);
456
457
0
    if (pszExtraNewline != nullptr)
458
0
    {
459
0
        nActuallyRead = static_cast<int>(pszExtraNewline - pszBuffer + 1);
460
461
0
        *pszExtraNewline = '\0';
462
0
        if (VSIFSeek(fp, nOriginalOffset + nActuallyRead - 1, SEEK_SET) != 0)
463
0
            return nullptr;
464
465
        // This hackery is necessary to try and find our correct
466
        // spot on win32 systems with text mode line translation going
467
        // on.  Sometimes the fseek back overshoots, but it doesn't
468
        // "realize it" till a character has been read. Try to read till
469
        // we get to the right spot and get our CR.
470
0
        int chCheck = fgetc(fp);
471
0
        while ((chCheck != knCR && chCheck != EOF) ||
472
0
               VSIFTell(fp) < nOriginalOffset + nActuallyRead)
473
0
        {
474
0
            static bool bWarned = false;
475
476
0
            if (!bWarned)
477
0
            {
478
0
                bWarned = true;
479
0
                CPLDebug("CPL",
480
0
                         "CPLFGets() correcting for DOS text mode translation "
481
0
                         "seek problem.");
482
0
            }
483
0
            chCheck = fgetc(fp);
484
0
        }
485
0
    }
486
487
0
    return pszBuffer;
488
0
}
489
490
/************************************************************************/
491
/*                         CPLReadLineBuffer()                          */
492
/*                                                                      */
493
/*      Fetch readline buffer, and ensure it is the desired size,       */
494
/*      reallocating if needed.  Manages TLS (thread local storage)     */
495
/*      issues for the buffer.                                          */
496
/*      We use a special trick to track the actual size of the buffer   */
497
/*      The first 4 bytes are reserved to store it as a int, hence the  */
498
/*      -4 / +4 hacks with the size and pointer.                        */
499
/************************************************************************/
500
static char *CPLReadLineBuffer(int nRequiredSize)
501
502
145
{
503
504
    /* -------------------------------------------------------------------- */
505
    /*      A required size of -1 means the buffer should be freed.         */
506
    /* -------------------------------------------------------------------- */
507
145
    if (nRequiredSize == -1)
508
0
    {
509
0
        int bMemoryError = FALSE;
510
0
        void *pRet = CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError);
511
0
        if (pRet != nullptr)
512
0
        {
513
0
            CPLFree(pRet);
514
0
            CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
515
0
        }
516
0
        return nullptr;
517
0
    }
518
519
    /* -------------------------------------------------------------------- */
520
    /*      If the buffer doesn't exist yet, create it.                     */
521
    /* -------------------------------------------------------------------- */
522
145
    int bMemoryError = FALSE;
523
145
    GUInt32 *pnAlloc =
524
145
        static_cast<GUInt32 *>(CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError));
525
145
    if (bMemoryError)
526
0
        return nullptr;
527
528
145
    if (pnAlloc == nullptr)
529
1
    {
530
1
        pnAlloc = static_cast<GUInt32 *>(VSI_MALLOC_VERBOSE(200));
531
1
        if (pnAlloc == nullptr)
532
0
            return nullptr;
533
1
        *pnAlloc = 196;
534
1
        CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
535
1
    }
536
537
    /* -------------------------------------------------------------------- */
538
    /*      If it is too small, grow it bigger.                             */
539
    /* -------------------------------------------------------------------- */
540
145
    if (static_cast<int>(*pnAlloc) - 1 < nRequiredSize)
541
0
    {
542
0
        const int nNewSize = nRequiredSize + 4 + 500;
543
0
        if (nNewSize <= 0)
544
0
        {
545
0
            VSIFree(pnAlloc);
546
0
            CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
547
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
548
0
                     "CPLReadLineBuffer(): Trying to allocate more than "
549
0
                     "2 GB.");
550
0
            return nullptr;
551
0
        }
552
553
0
        GUInt32 *pnAllocNew =
554
0
            static_cast<GUInt32 *>(VSI_REALLOC_VERBOSE(pnAlloc, nNewSize));
555
0
        if (pnAllocNew == nullptr)
556
0
        {
557
0
            VSIFree(pnAlloc);
558
0
            CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
559
0
            return nullptr;
560
0
        }
561
0
        pnAlloc = pnAllocNew;
562
563
0
        *pnAlloc = nNewSize - 4;
564
0
        CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
565
0
    }
566
567
145
    return reinterpret_cast<char *>(pnAlloc + 1);
568
145
}
569
570
/************************************************************************/
571
/*                            CPLReadLine()                             */
572
/************************************************************************/
573
574
/**
575
 * Simplified line reading from text file.
576
 *
577
 * Read a line of text from the given file handle, taking care
578
 * to capture CR and/or LF and strip off ... equivalent of
579
 * DKReadLine().  Pointer to an internal buffer is returned.
580
 * The application shouldn't free it, or depend on its value
581
 * past the next call to CPLReadLine().
582
 *
583
 * Note that CPLReadLine() uses VSIFGets(), so any hooking of VSI file
584
 * services should apply to CPLReadLine() as well.
585
 *
586
 * CPLReadLine() maintains an internal buffer, which will appear as a
587
 * single block memory leak in some circumstances.  CPLReadLine() may
588
 * be called with a NULL FILE * at any time to free this working buffer.
589
 *
590
 * @param fp file pointer opened with VSIFOpen().
591
 *
592
 * @return pointer to an internal buffer containing a line of text read
593
 * from the file or NULL if the end of file was encountered.
594
 */
595
596
const char *CPLReadLine(FILE *fp)
597
598
0
{
599
    /* -------------------------------------------------------------------- */
600
    /*      Cleanup case.                                                   */
601
    /* -------------------------------------------------------------------- */
602
0
    if (fp == nullptr)
603
0
    {
604
0
        CPLReadLineBuffer(-1);
605
0
        return nullptr;
606
0
    }
607
608
    /* -------------------------------------------------------------------- */
609
    /*      Loop reading chunks of the line till we get to the end of       */
610
    /*      the line.                                                       */
611
    /* -------------------------------------------------------------------- */
612
0
    size_t nBytesReadThisTime = 0;
613
0
    char *pszRLBuffer = nullptr;
614
0
    size_t nReadSoFar = 0;
615
616
0
    do
617
0
    {
618
        /* --------------------------------------------------------------------
619
         */
620
        /*      Grow the working buffer if we have it nearly full.  Fail out */
621
        /*      of read line if we can't reallocate it big enough (for */
622
        /*      instance for a _very large_ file with no newlines). */
623
        /* --------------------------------------------------------------------
624
         */
625
0
        if (nReadSoFar > 100 * 1024 * 1024)
626
            // It is dubious that we need to read a line longer than 100 MB.
627
0
            return nullptr;
628
0
        pszRLBuffer = CPLReadLineBuffer(static_cast<int>(nReadSoFar) + 129);
629
0
        if (pszRLBuffer == nullptr)
630
0
            return nullptr;
631
632
        /* --------------------------------------------------------------------
633
         */
634
        /*      Do the actual read. */
635
        /* --------------------------------------------------------------------
636
         */
637
0
        if (CPLFGets(pszRLBuffer + nReadSoFar, 128, fp) == nullptr &&
638
0
            nReadSoFar == 0)
639
0
            return nullptr;
640
641
0
        nBytesReadThisTime = strlen(pszRLBuffer + nReadSoFar);
642
0
        nReadSoFar += nBytesReadThisTime;
643
0
    } while (nBytesReadThisTime >= 127 && pszRLBuffer[nReadSoFar - 1] != knCR &&
644
0
             pszRLBuffer[nReadSoFar - 1] != knLF);
645
646
0
    return pszRLBuffer;
647
0
}
648
649
/************************************************************************/
650
/*                            CPLReadLineL()                            */
651
/************************************************************************/
652
653
/**
654
 * Simplified line reading from text file.
655
 *
656
 * Similar to CPLReadLine(), but reading from a large file API handle.
657
 *
658
 * @param fp file pointer opened with VSIFOpenL().
659
 *
660
 * @return pointer to an internal buffer containing a line of text read
661
 * from the file or NULL if the end of file was encountered.
662
 */
663
664
const char *CPLReadLineL(VSILFILE *fp)
665
145
{
666
145
    return CPLReadLine2L(fp, -1, nullptr);
667
145
}
668
669
/************************************************************************/
670
/*                           CPLReadLine2L()                            */
671
/************************************************************************/
672
673
/**
674
 * Simplified line reading from text file.
675
 *
676
 * Similar to CPLReadLine(), but reading from a large file API handle.
677
 *
678
 * @param fp file pointer opened with VSIFOpenL().
679
 * @param nMaxCars  maximum number of characters allowed, or -1 for no limit.
680
 * @param papszOptions NULL-terminated array of options. Unused for now.
681
682
 * @return pointer to an internal buffer containing a line of text read
683
 * from the file or NULL if the end of file was encountered or the maximum
684
 * number of characters allowed reached.
685
 *
686
 */
687
688
const char *CPLReadLine2L(VSILFILE *fp, int nMaxCars,
689
                          CPL_UNUSED CSLConstList papszOptions)
690
691
145
{
692
145
    int nBufLength;
693
145
    return CPLReadLine3L(fp, nMaxCars, &nBufLength, papszOptions);
694
145
}
695
696
/************************************************************************/
697
/*                           CPLReadLine3L()                            */
698
/************************************************************************/
699
700
/**
701
 * Simplified line reading from text file.
702
 *
703
 * Similar to CPLReadLine(), but reading from a large file API handle.
704
 *
705
 * @param fp file pointer opened with VSIFOpenL().
706
 * @param nMaxCars  maximum number of characters allowed, or -1 for no limit.
707
 * @param papszOptions NULL-terminated array of options. Unused for now.
708
 * @param[out] pnBufLength size of output string (must be non-NULL)
709
710
 * @return pointer to an internal buffer containing a line of text read
711
 * from the file or NULL if the end of file was encountered or the maximum
712
 * number of characters allowed reached.
713
 *
714
 */
715
const char *CPLReadLine3L(VSILFILE *fp, int nMaxCars, int *pnBufLength,
716
                          CPL_UNUSED CSLConstList papszOptions)
717
145
{
718
    /* -------------------------------------------------------------------- */
719
    /*      Cleanup case.                                                   */
720
    /* -------------------------------------------------------------------- */
721
145
    if (fp == nullptr)
722
0
    {
723
0
        CPLReadLineBuffer(-1);
724
0
        return nullptr;
725
0
    }
726
727
    /* -------------------------------------------------------------------- */
728
    /*      Loop reading chunks of the line till we get to the end of       */
729
    /*      the line.                                                       */
730
    /* -------------------------------------------------------------------- */
731
145
    char *pszRLBuffer = nullptr;
732
145
    const size_t nChunkSize = 40;
733
145
    char szChunk[nChunkSize] = {};
734
145
    size_t nChunkBytesRead = 0;
735
145
    size_t nChunkBytesConsumed = 0;
736
737
145
    *pnBufLength = 0;
738
145
    szChunk[0] = 0;
739
740
145
    while (true)
741
145
    {
742
        /* --------------------------------------------------------------------
743
         */
744
        /*      Read a chunk from the input file. */
745
        /* --------------------------------------------------------------------
746
         */
747
145
        if (*pnBufLength > INT_MAX - static_cast<int>(nChunkSize) - 1)
748
0
        {
749
0
            CPLError(CE_Failure, CPLE_AppDefined,
750
0
                     "Too big line : more than 2 billion characters!.");
751
0
            CPLReadLineBuffer(-1);
752
0
            return nullptr;
753
0
        }
754
755
145
        pszRLBuffer =
756
145
            CPLReadLineBuffer(static_cast<int>(*pnBufLength + nChunkSize + 1));
757
145
        if (pszRLBuffer == nullptr)
758
0
            return nullptr;
759
760
145
        if (nChunkBytesRead == nChunkBytesConsumed + 1)
761
0
        {
762
763
            // case where one character is left over from last read.
764
0
            szChunk[0] = szChunk[nChunkBytesConsumed];
765
766
0
            nChunkBytesConsumed = 0;
767
0
            nChunkBytesRead = VSIFReadL(szChunk + 1, 1, nChunkSize - 1, fp) + 1;
768
0
        }
769
145
        else
770
145
        {
771
145
            nChunkBytesConsumed = 0;
772
773
            // fresh read.
774
145
            nChunkBytesRead = VSIFReadL(szChunk, 1, nChunkSize, fp);
775
145
            if (nChunkBytesRead == 0)
776
145
            {
777
145
                if (*pnBufLength == 0)
778
145
                    return nullptr;
779
780
0
                break;
781
145
            }
782
145
        }
783
784
        /* --------------------------------------------------------------------
785
         */
786
        /*      copy over characters watching for end-of-line. */
787
        /* --------------------------------------------------------------------
788
         */
789
0
        bool bBreak = false;
790
0
        while (nChunkBytesConsumed < nChunkBytesRead - 1 && !bBreak)
791
0
        {
792
0
            if ((szChunk[nChunkBytesConsumed] == knCR &&
793
0
                 szChunk[nChunkBytesConsumed + 1] == knLF) ||
794
0
                (szChunk[nChunkBytesConsumed] == knLF &&
795
0
                 szChunk[nChunkBytesConsumed + 1] == knCR))
796
0
            {
797
0
                nChunkBytesConsumed += 2;
798
0
                bBreak = true;
799
0
            }
800
0
            else if (szChunk[nChunkBytesConsumed] == knLF ||
801
0
                     szChunk[nChunkBytesConsumed] == knCR)
802
0
            {
803
0
                nChunkBytesConsumed += 1;
804
0
                bBreak = true;
805
0
            }
806
0
            else
807
0
            {
808
0
                pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
809
0
                if (nMaxCars >= 0 && *pnBufLength == nMaxCars)
810
0
                {
811
0
                    CPLError(CE_Failure, CPLE_AppDefined,
812
0
                             "Maximum number of characters allowed reached.");
813
0
                    return nullptr;
814
0
                }
815
0
            }
816
0
        }
817
818
0
        if (bBreak)
819
0
            break;
820
821
        /* --------------------------------------------------------------------
822
         */
823
        /*      If there is a remaining character and it is not a newline */
824
        /*      consume it.  If it is a newline, but we are clearly at the */
825
        /*      end of the file then consume it. */
826
        /* --------------------------------------------------------------------
827
         */
828
0
        if (nChunkBytesConsumed == nChunkBytesRead - 1 &&
829
0
            nChunkBytesRead < nChunkSize)
830
0
        {
831
0
            if (szChunk[nChunkBytesConsumed] == knLF ||
832
0
                szChunk[nChunkBytesConsumed] == knCR)
833
0
            {
834
0
                nChunkBytesConsumed++;
835
0
                break;
836
0
            }
837
838
0
            pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
839
0
            break;
840
0
        }
841
0
    }
842
843
    /* -------------------------------------------------------------------- */
844
    /*      If we have left over bytes after breaking out, seek back to     */
845
    /*      ensure they remain to be read next time.                        */
846
    /* -------------------------------------------------------------------- */
847
0
    if (nChunkBytesConsumed < nChunkBytesRead)
848
0
    {
849
0
        const size_t nBytesToPush = nChunkBytesRead - nChunkBytesConsumed;
850
851
0
        if (VSIFSeekL(fp, VSIFTellL(fp) - nBytesToPush, SEEK_SET) != 0)
852
0
            return nullptr;
853
0
    }
854
855
0
    pszRLBuffer[*pnBufLength] = '\0';
856
857
0
    return pszRLBuffer;
858
0
}
859
860
/************************************************************************/
861
/*                           CPLScanString()                            */
862
/************************************************************************/
863
864
/**
865
 * Scan up to a maximum number of characters from a given string,
866
 * allocate a buffer for a new string and fill it with scanned characters.
867
 *
868
 * @param pszString String containing characters to be scanned. It may be
869
 * terminated with a null character.
870
 *
871
 * @param nMaxLength The maximum number of character to read. Less
872
 * characters will be read if a null character is encountered.
873
 *
874
 * @param bTrimSpaces If TRUE, trim ending spaces from the input string.
875
 * Character considered as empty using isspace(3) function.
876
 *
877
 * @param bNormalize If TRUE, replace ':' symbol with the '_'. It is needed if
878
 * resulting string will be used in CPL dictionaries.
879
 *
880
 * @return Pointer to the resulting string buffer. Caller responsible to free
881
 * this buffer with CPLFree().
882
 */
883
884
char *CPLScanString(const char *pszString, int nMaxLength, int bTrimSpaces,
885
                    int bNormalize)
886
0
{
887
0
    if (!pszString)
888
0
        return nullptr;
889
890
0
    if (!nMaxLength)
891
0
        return CPLStrdup("");
892
893
0
    char *pszBuffer = static_cast<char *>(CPLMalloc(nMaxLength + 1));
894
0
    if (!pszBuffer)
895
0
        return nullptr;
896
897
0
    strncpy(pszBuffer, pszString, nMaxLength);
898
0
    pszBuffer[nMaxLength] = '\0';
899
900
0
    if (bTrimSpaces)
901
0
    {
902
0
        size_t i = strlen(pszBuffer);
903
0
        while (i > 0)
904
0
        {
905
0
            i--;
906
0
            if (!isspace(static_cast<unsigned char>(pszBuffer[i])))
907
0
                break;
908
0
            pszBuffer[i] = '\0';
909
0
        }
910
0
    }
911
912
0
    if (bNormalize)
913
0
    {
914
0
        size_t i = strlen(pszBuffer);
915
0
        while (i > 0)
916
0
        {
917
0
            i--;
918
0
            if (pszBuffer[i] == ':')
919
0
                pszBuffer[i] = '_';
920
0
        }
921
0
    }
922
923
0
    return pszBuffer;
924
0
}
925
926
/************************************************************************/
927
/*                            CPLScanLong()                             */
928
/************************************************************************/
929
930
/**
931
 * Scan up to a maximum number of characters from a string and convert
932
 * the result to a long.
933
 *
934
 * @param pszString String containing characters to be scanned. It may be
935
 * terminated with a null character.
936
 *
937
 * @param nMaxLength The maximum number of character to consider as part
938
 * of the number. Less characters will be considered if a null character
939
 * is encountered.
940
 *
941
 * @return Long value, converted from its ASCII form.
942
 */
943
944
long CPLScanLong(const char *pszString, int nMaxLength)
945
0
{
946
0
    CPLAssert(nMaxLength >= 0);
947
0
    if (pszString == nullptr)
948
0
        return 0;
949
0
    const size_t nLength = CPLStrnlen(pszString, nMaxLength);
950
0
    const std::string osValue(pszString, nLength);
951
0
    return atol(osValue.c_str());
952
0
}
953
954
/************************************************************************/
955
/*                            CPLScanULong()                            */
956
/************************************************************************/
957
958
/**
959
 * Scan up to a maximum number of characters from a string and convert
960
 * the result to a unsigned long.
961
 *
962
 * @param pszString String containing characters to be scanned. It may be
963
 * terminated with a null character.
964
 *
965
 * @param nMaxLength The maximum number of character to consider as part
966
 * of the number. Less characters will be considered if a null character
967
 * is encountered.
968
 *
969
 * @return Unsigned long value, converted from its ASCII form.
970
 */
971
972
unsigned long CPLScanULong(const char *pszString, int nMaxLength)
973
0
{
974
0
    CPLAssert(nMaxLength >= 0);
975
0
    if (pszString == nullptr)
976
0
        return 0;
977
0
    const size_t nLength = CPLStrnlen(pszString, nMaxLength);
978
0
    const std::string osValue(pszString, nLength);
979
0
    return strtoul(osValue.c_str(), nullptr, 10);
980
0
}
981
982
/************************************************************************/
983
/*                           CPLScanUIntBig()                           */
984
/************************************************************************/
985
986
/**
987
 * Extract big integer from string.
988
 *
989
 * Scan up to a maximum number of characters from a string and convert
990
 * the result to a GUIntBig.
991
 *
992
 * @param pszString String containing characters to be scanned. It may be
993
 * terminated with a null character.
994
 *
995
 * @param nMaxLength The maximum number of character to consider as part
996
 * of the number. Less characters will be considered if a null character
997
 * is encountered.
998
 *
999
 * @return GUIntBig value, converted from its ASCII form.
1000
 */
1001
1002
GUIntBig CPLScanUIntBig(const char *pszString, int nMaxLength)
1003
8.35k
{
1004
8.35k
    CPLAssert(nMaxLength >= 0);
1005
8.35k
    if (pszString == nullptr)
1006
0
        return 0;
1007
8.35k
    const size_t nLength = CPLStrnlen(pszString, nMaxLength);
1008
8.35k
    const std::string osValue(pszString, nLength);
1009
1010
    /* -------------------------------------------------------------------- */
1011
    /*      Fetch out the result                                            */
1012
    /* -------------------------------------------------------------------- */
1013
8.35k
    return strtoull(osValue.c_str(), nullptr, 10);
1014
8.35k
}
1015
1016
/************************************************************************/
1017
/*                           CPLAtoGIntBig()                            */
1018
/************************************************************************/
1019
1020
/**
1021
 * Convert a string to a 64 bit signed integer.
1022
 *
1023
 * @param pszString String containing 64 bit signed integer.
1024
 * @return 64 bit signed integer.
1025
 */
1026
1027
GIntBig CPLAtoGIntBig(const char *pszString)
1028
238
{
1029
238
    return atoll(pszString);
1030
238
}
1031
1032
#if defined(__MINGW32__) || defined(__sun__)
1033
1034
// mingw atoll() doesn't return ERANGE in case of overflow
1035
static int CPLAtoGIntBigExHasOverflow(const char *pszString, GIntBig nVal)
1036
{
1037
    if (strlen(pszString) <= 18)
1038
        return FALSE;
1039
    while (*pszString == ' ')
1040
        pszString++;
1041
    if (*pszString == '+')
1042
        pszString++;
1043
    char szBuffer[32] = {};
1044
/* x86_64-w64-mingw32-g++ (GCC) 4.8.2 annoyingly warns */
1045
#ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1046
#pragma GCC diagnostic push
1047
#pragma GCC diagnostic ignored "-Wformat"
1048
#endif
1049
    snprintf(szBuffer, sizeof(szBuffer), CPL_FRMT_GIB, nVal);
1050
#ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1051
#pragma GCC diagnostic pop
1052
#endif
1053
    return strcmp(szBuffer, pszString) != 0;
1054
}
1055
1056
#endif
1057
1058
/************************************************************************/
1059
/*                          CPLAtoGIntBigEx()                           */
1060
/************************************************************************/
1061
1062
/**
1063
 * Convert a string to a 64 bit signed integer.
1064
 *
1065
 * @param pszString String containing 64 bit signed integer.
1066
 * @param bWarn Issue a warning if an overflow occurs during conversion
1067
 * @param pbOverflow Pointer to an integer to store if an overflow occurred, or
1068
 *        NULL
1069
 * @return 64 bit signed integer.
1070
 */
1071
1072
GIntBig CPLAtoGIntBigEx(const char *pszString, int bWarn, int *pbOverflow)
1073
0
{
1074
0
    errno = 0;
1075
0
    GIntBig nVal = strtoll(pszString, nullptr, 10);
1076
0
    if (errno == ERANGE
1077
#if defined(__MINGW32__) || defined(__sun__)
1078
        || CPLAtoGIntBigExHasOverflow(pszString, nVal)
1079
#endif
1080
0
    )
1081
0
    {
1082
0
        if (pbOverflow)
1083
0
            *pbOverflow = TRUE;
1084
0
        if (bWarn)
1085
0
        {
1086
0
            CPLError(CE_Warning, CPLE_AppDefined,
1087
0
                     "64 bit integer overflow when converting %s", pszString);
1088
0
        }
1089
0
        while (*pszString == ' ')
1090
0
            pszString++;
1091
0
        return (*pszString == '-') ? GINTBIG_MIN : GINTBIG_MAX;
1092
0
    }
1093
0
    else if (pbOverflow)
1094
0
    {
1095
0
        *pbOverflow = FALSE;
1096
0
    }
1097
0
    return nVal;
1098
0
}
1099
1100
/************************************************************************/
1101
/*                           CPLScanPointer()                           */
1102
/************************************************************************/
1103
1104
/**
1105
 * Extract pointer from string.
1106
 *
1107
 * Scan up to a maximum number of characters from a string and convert
1108
 * the result to a pointer.
1109
 *
1110
 * @param pszString String containing characters to be scanned. It may be
1111
 * terminated with a null character.
1112
 *
1113
 * @param nMaxLength The maximum number of character to consider as part
1114
 * of the number. Less characters will be considered if a null character
1115
 * is encountered.
1116
 *
1117
 * @return pointer value, converted from its ASCII form.
1118
 */
1119
1120
void *CPLScanPointer(const char *pszString, int nMaxLength)
1121
0
{
1122
0
    char szTemp[128] = {};
1123
1124
    /* -------------------------------------------------------------------- */
1125
    /*      Compute string into local buffer, and terminate it.             */
1126
    /* -------------------------------------------------------------------- */
1127
0
    if (nMaxLength > static_cast<int>(sizeof(szTemp)) - 1)
1128
0
        nMaxLength = sizeof(szTemp) - 1;
1129
1130
0
    strncpy(szTemp, pszString, nMaxLength);
1131
0
    szTemp[nMaxLength] = '\0';
1132
1133
    /* -------------------------------------------------------------------- */
1134
    /*      On MSVC we have to scanf pointer values without the 0x          */
1135
    /*      prefix.                                                         */
1136
    /* -------------------------------------------------------------------- */
1137
0
    if (STARTS_WITH_CI(szTemp, "0x"))
1138
0
    {
1139
0
        void *pResult = nullptr;
1140
1141
#if defined(__MSVCRT__) || (defined(_WIN32) && defined(_MSC_VER))
1142
        // cppcheck-suppress invalidscanf
1143
        sscanf(szTemp + 2, "%p", &pResult);
1144
#else
1145
        // cppcheck-suppress invalidscanf
1146
0
        sscanf(szTemp, "%p", &pResult);
1147
1148
        // Solaris actually behaves like MSVCRT.
1149
0
        if (pResult == nullptr)
1150
0
        {
1151
            // cppcheck-suppress invalidscanf
1152
0
            sscanf(szTemp + 2, "%p", &pResult);
1153
0
        }
1154
0
#endif
1155
0
        return pResult;
1156
0
    }
1157
1158
0
#if SIZEOF_VOIDP == 8
1159
0
    return reinterpret_cast<void *>(CPLScanUIntBig(szTemp, nMaxLength));
1160
#else
1161
    return reinterpret_cast<void *>(CPLScanULong(szTemp, nMaxLength));
1162
#endif
1163
0
}
1164
1165
/************************************************************************/
1166
/*                           CPLScanDouble()                            */
1167
/************************************************************************/
1168
1169
/**
1170
 * Extract double from string.
1171
 *
1172
 * Scan up to a maximum number of characters from a string and convert the
1173
 * result to a double. This function uses CPLAtof() to convert string to
1174
 * double value, so it uses a comma as a decimal delimiter.
1175
 *
1176
 * @param pszString String containing characters to be scanned. It may be
1177
 * terminated with a null character.
1178
 *
1179
 * @param nMaxLength The maximum number of character to consider as part
1180
 * of the number. Less characters will be considered if a null character
1181
 * is encountered.
1182
 *
1183
 * @return Double value, converted from its ASCII form.
1184
 */
1185
1186
double CPLScanDouble(const char *pszString, int nMaxLength)
1187
0
{
1188
0
    char szValue[32] = {};
1189
0
    char *pszValue = nullptr;
1190
1191
0
    if (nMaxLength + 1 < static_cast<int>(sizeof(szValue)))
1192
0
        pszValue = szValue;
1193
0
    else
1194
0
        pszValue = static_cast<char *>(CPLMalloc(nMaxLength + 1));
1195
1196
    /* -------------------------------------------------------------------- */
1197
    /*      Compute string into local buffer, and terminate it.             */
1198
    /* -------------------------------------------------------------------- */
1199
0
    strncpy(pszValue, pszString, nMaxLength);
1200
0
    pszValue[nMaxLength] = '\0';
1201
1202
    /* -------------------------------------------------------------------- */
1203
    /*      Make a pass through converting 'D's to 'E's.                    */
1204
    /* -------------------------------------------------------------------- */
1205
0
    for (int i = 0; i < nMaxLength; i++)
1206
0
        if (pszValue[i] == 'd' || pszValue[i] == 'D')
1207
0
            pszValue[i] = 'E';
1208
1209
    /* -------------------------------------------------------------------- */
1210
    /*      The conversion itself.                                          */
1211
    /* -------------------------------------------------------------------- */
1212
0
    const double dfValue = CPLAtof(pszValue);
1213
1214
0
    if (pszValue != szValue)
1215
0
        CPLFree(pszValue);
1216
0
    return dfValue;
1217
0
}
1218
1219
/************************************************************************/
1220
/*                           CPLPrintString()                           */
1221
/************************************************************************/
1222
1223
/**
1224
 * Copy the string pointed to by pszSrc, NOT including the terminating
1225
 * `\\0' character, to the array pointed to by pszDest.
1226
 *
1227
 * @param pszDest Pointer to the destination string buffer. Should be
1228
 * large enough to hold the resulting string.
1229
 *
1230
 * @param pszSrc Pointer to the source buffer.
1231
 *
1232
 * @param nMaxLen Maximum length of the resulting string. If string length
1233
 * is greater than nMaxLen, it will be truncated.
1234
 *
1235
 * @return Number of characters printed.
1236
 */
1237
1238
int CPLPrintString(char *pszDest, const char *pszSrc, int nMaxLen)
1239
0
{
1240
0
    if (!pszDest)
1241
0
        return 0;
1242
1243
0
    if (!pszSrc)
1244
0
    {
1245
0
        *pszDest = '\0';
1246
0
        return 1;
1247
0
    }
1248
1249
0
    int nChars = 0;
1250
0
    char *pszTemp = pszDest;
1251
1252
0
    while (nChars < nMaxLen && *pszSrc)
1253
0
    {
1254
0
        *pszTemp++ = *pszSrc++;
1255
0
        nChars++;
1256
0
    }
1257
1258
0
    return nChars;
1259
0
}
1260
1261
/************************************************************************/
1262
/*                         CPLPrintStringFill()                         */
1263
/************************************************************************/
1264
1265
/**
1266
 * Copy the string pointed to by pszSrc, NOT including the terminating
1267
 * `\\0' character, to the array pointed to by pszDest. Remainder of the
1268
 * destination string will be filled with space characters. This is only
1269
 * difference from the PrintString().
1270
 *
1271
 * @param pszDest Pointer to the destination string buffer. Should be
1272
 * large enough to hold the resulting string.
1273
 *
1274
 * @param pszSrc Pointer to the source buffer.
1275
 *
1276
 * @param nMaxLen Maximum length of the resulting string. If string length
1277
 * is greater than nMaxLen, it will be truncated.
1278
 *
1279
 * @return Number of characters printed.
1280
 */
1281
1282
int CPLPrintStringFill(char *pszDest, const char *pszSrc, int nMaxLen)
1283
0
{
1284
0
    if (!pszDest)
1285
0
        return 0;
1286
1287
0
    if (!pszSrc)
1288
0
    {
1289
0
        memset(pszDest, ' ', nMaxLen);
1290
0
        return nMaxLen;
1291
0
    }
1292
1293
0
    char *pszTemp = pszDest;
1294
0
    while (nMaxLen && *pszSrc)
1295
0
    {
1296
0
        *pszTemp++ = *pszSrc++;
1297
0
        nMaxLen--;
1298
0
    }
1299
1300
0
    if (nMaxLen)
1301
0
        memset(pszTemp, ' ', nMaxLen);
1302
1303
0
    return nMaxLen;
1304
0
}
1305
1306
/************************************************************************/
1307
/*                           CPLPrintInt32()                            */
1308
/************************************************************************/
1309
1310
/**
1311
 * Print GInt32 value into specified string buffer. This string will not
1312
 * be NULL-terminated.
1313
 *
1314
 * @param pszBuffer Pointer to the destination string buffer. Should be
1315
 * large enough to hold the resulting string. Note, that the string will
1316
 * not be NULL-terminated, so user should do this himself, if needed.
1317
 *
1318
 * @param iValue Numerical value to print.
1319
 *
1320
 * @param nMaxLen Maximum length of the resulting string. If string length
1321
 * is greater than nMaxLen, it will be truncated.
1322
 *
1323
 * @return Number of characters printed.
1324
 */
1325
1326
int CPLPrintInt32(char *pszBuffer, GInt32 iValue, int nMaxLen)
1327
0
{
1328
0
    if (!pszBuffer)
1329
0
        return 0;
1330
1331
0
    if (nMaxLen >= 64)
1332
0
        nMaxLen = 63;
1333
1334
0
    char szTemp[64] = {};
1335
1336
#if UINT_MAX == 65535
1337
    snprintf(szTemp, sizeof(szTemp), "%*ld", nMaxLen, iValue);
1338
#else
1339
0
    snprintf(szTemp, sizeof(szTemp), "%*d", nMaxLen, iValue);
1340
0
#endif
1341
1342
0
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
1343
0
}
1344
1345
/************************************************************************/
1346
/*                          CPLPrintUIntBig()                           */
1347
/************************************************************************/
1348
1349
/**
1350
 * Print GUIntBig value into specified string buffer. This string will not
1351
 * be NULL-terminated.
1352
 *
1353
 * @param pszBuffer Pointer to the destination string buffer. Should be
1354
 * large enough to hold the resulting string. Note, that the string will
1355
 * not be NULL-terminated, so user should do this himself, if needed.
1356
 *
1357
 * @param iValue Numerical value to print.
1358
 *
1359
 * @param nMaxLen Maximum length of the resulting string. If string length
1360
 * is greater than nMaxLen, it will be truncated.
1361
 *
1362
 * @return Number of characters printed.
1363
 */
1364
1365
int CPLPrintUIntBig(char *pszBuffer, GUIntBig iValue, int nMaxLen)
1366
0
{
1367
0
    if (!pszBuffer)
1368
0
        return 0;
1369
1370
0
    if (nMaxLen >= 64)
1371
0
        nMaxLen = 63;
1372
1373
0
    char szTemp[64] = {};
1374
1375
#if defined(__MSVCRT__) || (defined(_WIN32) && defined(_MSC_VER))
1376
/* x86_64-w64-mingw32-g++ (GCC) 4.8.2 annoyingly warns */
1377
#ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1378
#pragma GCC diagnostic push
1379
#pragma GCC diagnostic ignored "-Wformat"
1380
#pragma GCC diagnostic ignored "-Wformat-extra-args"
1381
#endif
1382
    snprintf(szTemp, sizeof(szTemp), "%*I64u", nMaxLen, iValue);
1383
#ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1384
#pragma GCC diagnostic pop
1385
#endif
1386
#else
1387
0
    snprintf(szTemp, sizeof(szTemp), "%*llu", nMaxLen, iValue);
1388
0
#endif
1389
1390
0
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
1391
0
}
1392
1393
/************************************************************************/
1394
/*                          CPLPrintPointer()                           */
1395
/************************************************************************/
1396
1397
/**
1398
 * Print pointer value into specified string buffer. This string will not
1399
 * be NULL-terminated.
1400
 *
1401
 * @param pszBuffer Pointer to the destination string buffer. Should be
1402
 * large enough to hold the resulting string. Note, that the string will
1403
 * not be NULL-terminated, so user should do this himself, if needed.
1404
 *
1405
 * @param pValue Pointer to ASCII encode.
1406
 *
1407
 * @param nMaxLen Maximum length of the resulting string. If string length
1408
 * is greater than nMaxLen, it will be truncated.
1409
 *
1410
 * @return Number of characters printed.
1411
 */
1412
1413
int CPLPrintPointer(char *pszBuffer, void *pValue, int nMaxLen)
1414
0
{
1415
0
    if (!pszBuffer)
1416
0
        return 0;
1417
1418
0
    if (nMaxLen >= 64)
1419
0
        nMaxLen = 63;
1420
1421
0
    char szTemp[64] = {};
1422
1423
0
    snprintf(szTemp, sizeof(szTemp), "%p", pValue);
1424
1425
    // On windows, and possibly some other platforms the sprintf("%p")
1426
    // does not prefix things with 0x so it is hard to know later if the
1427
    // value is hex encoded.  Fix this up here.
1428
1429
0
    if (!STARTS_WITH_CI(szTemp, "0x"))
1430
0
        snprintf(szTemp, sizeof(szTemp), "0x%p", pValue);
1431
1432
0
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
1433
0
}
1434
1435
/************************************************************************/
1436
/*                           CPLPrintDouble()                           */
1437
/************************************************************************/
1438
1439
/**
1440
 * Print double value into specified string buffer. Exponential character
1441
 * flag 'E' (or 'e') will be replaced with 'D', as in Fortran. Resulting
1442
 * string will not to be NULL-terminated.
1443
 *
1444
 * @param pszBuffer Pointer to the destination string buffer. Should be
1445
 * large enough to hold the resulting string. Note, that the string will
1446
 * not be NULL-terminated, so user should do this himself, if needed.
1447
 *
1448
 * @param pszFormat Format specifier (for example, "%16.9E").
1449
 *
1450
 * @param dfValue Numerical value to print.
1451
 *
1452
 * @param pszLocale Unused.
1453
 *
1454
 * @return Number of characters printed.
1455
 */
1456
1457
int CPLPrintDouble(char *pszBuffer, const char *pszFormat, double dfValue,
1458
                   CPL_UNUSED const char *pszLocale)
1459
0
{
1460
0
    if (!pszBuffer)
1461
0
        return 0;
1462
1463
0
    const int knDoubleBufferSize = 64;
1464
0
    char szTemp[knDoubleBufferSize] = {};
1465
1466
0
    CPLsnprintf(szTemp, knDoubleBufferSize, pszFormat, dfValue);
1467
0
    szTemp[knDoubleBufferSize - 1] = '\0';
1468
1469
0
    for (int i = 0; szTemp[i] != '\0'; i++)
1470
0
    {
1471
0
        if (szTemp[i] == 'E' || szTemp[i] == 'e')
1472
0
            szTemp[i] = 'D';
1473
0
    }
1474
1475
0
    return CPLPrintString(pszBuffer, szTemp, 64);
1476
0
}
1477
1478
/************************************************************************/
1479
/*                            CPLPrintTime()                            */
1480
/************************************************************************/
1481
1482
/**
1483
 * Print specified time value accordingly to the format options and
1484
 * specified locale name. This function does following:
1485
 *
1486
 *  - if locale parameter is not NULL, the current locale setting will be
1487
 *  stored and replaced with the specified one;
1488
 *  - format time value with the strftime(3) function;
1489
 *  - restore back current locale, if was saved.
1490
 *
1491
 * @param pszBuffer Pointer to the destination string buffer. Should be
1492
 * large enough to hold the resulting string. Note, that the string will
1493
 * not be NULL-terminated, so user should do this himself, if needed.
1494
 *
1495
 * @param nMaxLen Maximum length of the resulting string. If string length is
1496
 * greater than nMaxLen, it will be truncated.
1497
 *
1498
 * @param pszFormat Controls the output format. Options are the same as
1499
 * for strftime(3) function.
1500
 *
1501
 * @param poBrokenTime Pointer to the broken-down time structure. May be
1502
 * requested with the VSIGMTime() and VSILocalTime() functions.
1503
 *
1504
 * @param pszLocale Pointer to a character string containing locale name
1505
 * ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL we will not
1506
 * manipulate with locale settings and current process locale will be used for
1507
 * printing. Be aware that it may be unsuitable to use current locale for
1508
 * printing time, because all names will be printed in your native language,
1509
 * as well as time format settings also may be adjusted differently from the
1510
 * C/POSIX defaults. To solve these problems this option was introduced.
1511
 *
1512
 * @return Number of characters printed.
1513
 */
1514
1515
int CPLPrintTime(char *pszBuffer, int nMaxLen, const char *pszFormat,
1516
                 const struct tm *poBrokenTime, const char *pszLocale)
1517
0
{
1518
0
    char *pszTemp =
1519
0
        static_cast<char *>(CPLMalloc((nMaxLen + 1) * sizeof(char)));
1520
1521
0
    if (pszLocale && EQUAL(pszLocale, "C") &&
1522
0
        strcmp(pszFormat, "%a, %d %b %Y %H:%M:%S GMT") == 0)
1523
0
    {
1524
        // Particular case when formatting RFC822 datetime, to avoid locale
1525
        // change
1526
0
        static const char *const aszMonthStr[] = {"Jan", "Feb", "Mar", "Apr",
1527
0
                                                  "May", "Jun", "Jul", "Aug",
1528
0
                                                  "Sep", "Oct", "Nov", "Dec"};
1529
0
        static const char *const aszDayOfWeek[] = {"Sun", "Mon", "Tue", "Wed",
1530
0
                                                   "Thu", "Fri", "Sat"};
1531
0
        snprintf(pszTemp, nMaxLen + 1, "%s, %02d %s %04d %02d:%02d:%02d GMT",
1532
0
                 aszDayOfWeek[std::max(0, std::min(6, poBrokenTime->tm_wday))],
1533
0
                 poBrokenTime->tm_mday,
1534
0
                 aszMonthStr[std::max(0, std::min(11, poBrokenTime->tm_mon))],
1535
0
                 poBrokenTime->tm_year + 1900, poBrokenTime->tm_hour,
1536
0
                 poBrokenTime->tm_min, poBrokenTime->tm_sec);
1537
0
    }
1538
0
    else
1539
0
    {
1540
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
1541
        char *pszCurLocale = NULL;
1542
1543
        if (pszLocale || EQUAL(pszLocale, ""))
1544
        {
1545
            // Save the current locale.
1546
            pszCurLocale = CPLsetlocale(LC_ALL, NULL);
1547
            // Set locale to the specified value.
1548
            CPLsetlocale(LC_ALL, pszLocale);
1549
        }
1550
#else
1551
0
        (void)pszLocale;
1552
0
#endif
1553
1554
0
        if (!strftime(pszTemp, nMaxLen + 1, pszFormat, poBrokenTime))
1555
0
            memset(pszTemp, 0, nMaxLen + 1);
1556
1557
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
1558
        // Restore stored locale back.
1559
        if (pszCurLocale)
1560
            CPLsetlocale(LC_ALL, pszCurLocale);
1561
#endif
1562
0
    }
1563
1564
0
    const int nChars = CPLPrintString(pszBuffer, pszTemp, nMaxLen);
1565
1566
0
    CPLFree(pszTemp);
1567
1568
0
    return nChars;
1569
0
}
1570
1571
/************************************************************************/
1572
/*                       CPLVerifyConfiguration()                       */
1573
/************************************************************************/
1574
1575
void CPLVerifyConfiguration()
1576
1577
0
{
1578
    /* -------------------------------------------------------------------- */
1579
    /*      Verify data types.                                              */
1580
    /* -------------------------------------------------------------------- */
1581
0
    static_assert(sizeof(short) == 2);   // We unfortunately rely on this
1582
0
    static_assert(sizeof(int) == 4);     // We unfortunately rely on this
1583
0
    static_assert(sizeof(float) == 4);   // We unfortunately rely on this
1584
0
    static_assert(sizeof(double) == 8);  // We unfortunately rely on this
1585
0
    static_assert(sizeof(GInt64) == 8);
1586
0
    static_assert(sizeof(GInt32) == 4);
1587
0
    static_assert(sizeof(GInt16) == 2);
1588
0
    static_assert(sizeof(GByte) == 1);
1589
1590
    /* -------------------------------------------------------------------- */
1591
    /*      Verify byte order                                               */
1592
    /* -------------------------------------------------------------------- */
1593
0
#ifdef CPL_LSB
1594
#if __cplusplus >= 202002L
1595
    static_assert(std::endian::native == std::endian::little);
1596
#elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
1597
    static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
1598
0
#endif
1599
#elif defined(CPL_MSB)
1600
#if __cplusplus >= 202002L
1601
    static_assert(std::endian::native == std::endian::big);
1602
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
1603
    static_assert(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
1604
#endif
1605
#else
1606
#error "CPL_LSB or CPL_MSB must be defined"
1607
#endif
1608
0
}
1609
1610
#ifdef DEBUG_CONFIG_OPTIONS
1611
1612
static CPLMutex *hRegisterConfigurationOptionMutex = nullptr;
1613
static std::set<CPLString> *paoGetKeys = nullptr;
1614
static std::set<CPLString> *paoSetKeys = nullptr;
1615
1616
/************************************************************************/
1617
/*                       CPLShowAccessedOptions()                       */
1618
/************************************************************************/
1619
1620
static void CPLShowAccessedOptions()
1621
{
1622
    std::set<CPLString>::iterator aoIter;
1623
1624
    printf("Configuration options accessed in reading : "); /*ok*/
1625
    aoIter = paoGetKeys->begin();
1626
    while (aoIter != paoGetKeys->end())
1627
    {
1628
        printf("%s, ", (*aoIter).c_str()); /*ok*/
1629
        ++aoIter;
1630
    }
1631
    printf("\n"); /*ok*/
1632
1633
    printf("Configuration options accessed in writing : "); /*ok*/
1634
    aoIter = paoSetKeys->begin();
1635
    while (aoIter != paoSetKeys->end())
1636
    {
1637
        printf("%s, ", (*aoIter).c_str()); /*ok*/
1638
        ++aoIter;
1639
    }
1640
    printf("\n"); /*ok*/
1641
1642
    delete paoGetKeys;
1643
    delete paoSetKeys;
1644
    paoGetKeys = nullptr;
1645
    paoSetKeys = nullptr;
1646
}
1647
1648
/************************************************************************/
1649
/*                       CPLAccessConfigOption()                        */
1650
/************************************************************************/
1651
1652
static void CPLAccessConfigOption(const char *pszKey, bool bGet)
1653
{
1654
    CPLMutexHolderD(&hRegisterConfigurationOptionMutex);
1655
    if (paoGetKeys == nullptr)
1656
    {
1657
        paoGetKeys = new std::set<CPLString>;
1658
        paoSetKeys = new std::set<CPLString>;
1659
        atexit(CPLShowAccessedOptions);
1660
    }
1661
    if (bGet)
1662
        paoGetKeys->insert(pszKey);
1663
    else
1664
        paoSetKeys->insert(pszKey);
1665
}
1666
#endif
1667
1668
/************************************************************************/
1669
/*                         CPLGetConfigOption()                         */
1670
/************************************************************************/
1671
1672
/**
1673
 * Get the value of a configuration option.
1674
 *
1675
 * The value is the value of a (key, value) option set with
1676
 * CPLSetConfigOption(), or CPLSetThreadLocalConfigOption() of the same
1677
 * thread. If the given option was no defined with
1678
 * CPLSetConfigOption(), it tries to find it in environment variables.
1679
 *
1680
 * Note: the string returned by CPLGetConfigOption() might be short-lived, and
1681
 * in particular it will become invalid after a call to CPLSetConfigOption()
1682
 * with the same key.
1683
 *
1684
 * To override temporary a potentially existing option with a new value, you
1685
 * can use the following snippet :
1686
 * \code{.cpp}
1687
 *     // backup old value
1688
 *     const char* pszOldValTmp = CPLGetConfigOption(pszKey, NULL);
1689
 *     char* pszOldVal = pszOldValTmp ? CPLStrdup(pszOldValTmp) : NULL;
1690
 *     // override with new value
1691
 *     CPLSetConfigOption(pszKey, pszNewVal);
1692
 *     // do something useful
1693
 *     // restore old value
1694
 *     CPLSetConfigOption(pszKey, pszOldVal);
1695
 *     CPLFree(pszOldVal);
1696
 * \endcode
1697
 *
1698
 * @param pszKey the key of the option to retrieve
1699
 * @param pszDefault a default value if the key does not match existing defined
1700
 *     options (may be NULL)
1701
 * @return the value associated to the key, or the default value if not found
1702
 *
1703
 * @see CPLSetConfigOption(), https://gdal.org/user/configoptions.html
1704
 */
1705
const char *CPL_STDCALL CPLGetConfigOption(const char *pszKey,
1706
                                           const char *pszDefault)
1707
1708
158k
{
1709
158k
    const char *pszResult = CPLGetThreadLocalConfigOption(
1710
158k
        pszKey, nullptr, /* bSubstituteNullValueMarkerWithNull = */ false);
1711
1712
158k
    if (pszResult == nullptr)
1713
158k
    {
1714
158k
        pszResult = CPLGetGlobalConfigOption(
1715
158k
            pszKey, nullptr, /* bSubstituteNullValueMarkerWithNull = */ false);
1716
158k
    }
1717
1718
158k
    if (gbIgnoreEnvVariables)
1719
0
    {
1720
0
        const char *pszEnvVar = getenv(pszKey);
1721
        // Skipping for CPL_DEBUG to avoid infinite recursion since CPLvDebug()
1722
        // calls CPLGetConfigOption()...
1723
0
        if (pszEnvVar != nullptr && !EQUAL(pszKey, "CPL_DEBUG"))
1724
0
        {
1725
0
            CPLDebug("CPL",
1726
0
                     "Ignoring environment variable %s=%s because of "
1727
0
                     "ignore-env-vars=yes setting in configuration file",
1728
0
                     pszKey, pszEnvVar);
1729
0
        }
1730
0
    }
1731
158k
    else if (pszResult == nullptr)
1732
117k
    {
1733
117k
        pszResult = getenv(pszKey);
1734
117k
    }
1735
1736
158k
    if (pszResult == nullptr || strcmp(pszResult, CPL_NULL_VALUE) == 0)
1737
117k
        return pszDefault;
1738
1739
40.5k
    return pszResult;
1740
158k
}
1741
1742
/************************************************************************/
1743
/*                        CPLGetConfigOptions()                         */
1744
/************************************************************************/
1745
1746
/**
1747
 * Return the list of configuration options as KEY=VALUE pairs.
1748
 *
1749
 * The list is the one set through the CPLSetConfigOption() API.
1750
 *
1751
 * Options that through environment variables or with
1752
 * CPLSetThreadLocalConfigOption() will *not* be listed.
1753
 *
1754
 * @return a copy of the list, to be freed with CSLDestroy().
1755
 */
1756
char **CPLGetConfigOptions(void)
1757
0
{
1758
0
    CPLMutexHolderD(&hConfigMutex);
1759
0
    return CSLDuplicate(const_cast<char **>(g_papszConfigOptions));
1760
0
}
1761
1762
/************************************************************************/
1763
/*                        CPLSetConfigOptions()                         */
1764
/************************************************************************/
1765
1766
/**
1767
 * Replace the full list of configuration options with the passed list of
1768
 * KEY=VALUE pairs.
1769
 *
1770
 * This has the same effect of clearing the existing list, and setting
1771
 * individually each pair with the CPLSetConfigOption() API.
1772
 *
1773
 * This does not affect options set through environment variables or with
1774
 * CPLSetThreadLocalConfigOption().
1775
 *
1776
 * The passed list is copied by the function.
1777
 *
1778
 * @param papszConfigOptions the new list (or NULL).
1779
 *
1780
 */
1781
void CPLSetConfigOptions(const char *const *papszConfigOptions)
1782
0
{
1783
0
    CPLMutexHolderD(&hConfigMutex);
1784
0
    CSLDestroy(const_cast<char **>(g_papszConfigOptions));
1785
0
    g_papszConfigOptions = const_cast<volatile char **>(
1786
0
        CSLDuplicate(const_cast<char **>(papszConfigOptions)));
1787
0
}
1788
1789
/************************************************************************/
1790
/*                   CPLGetThreadLocalConfigOption()                    */
1791
/************************************************************************/
1792
1793
/** Same as CPLGetConfigOption() but only with options set with
1794
 * CPLSetThreadLocalConfigOption() */
1795
const char *CPL_STDCALL CPLGetThreadLocalConfigOption(const char *pszKey,
1796
                                                      const char *pszDefault)
1797
1798
0
{
1799
0
    return CPLGetThreadLocalConfigOption(pszKey, pszDefault, true);
1800
0
}
1801
1802
static const char *
1803
CPLGetThreadLocalConfigOption(const char *pszKey, const char *pszDefault,
1804
                              bool bSubstituteNullValueMarkerWithNull)
1805
158k
{
1806
#ifdef DEBUG_CONFIG_OPTIONS
1807
    CPLAccessConfigOption(pszKey, TRUE);
1808
#endif
1809
1810
158k
    const char *pszResult = nullptr;
1811
1812
158k
    int bMemoryError = FALSE;
1813
158k
    char **papszTLConfigOptions = reinterpret_cast<char **>(
1814
158k
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
1815
158k
    if (papszTLConfigOptions != nullptr)
1816
0
        pszResult = CSLFetchNameValue(papszTLConfigOptions, pszKey);
1817
1818
158k
    if (pszResult == nullptr || (bSubstituteNullValueMarkerWithNull &&
1819
0
                                 strcmp(pszResult, CPL_NULL_VALUE) == 0))
1820
158k
        return pszDefault;
1821
1822
0
    return pszResult;
1823
158k
}
1824
1825
/************************************************************************/
1826
/*                      CPLGetGlobalConfigOption()                      */
1827
/************************************************************************/
1828
1829
/** Same as CPLGetConfigOption() but excludes environment variables and
1830
 *  options set with CPLSetThreadLocalConfigOption().
1831
 *  This function should generally not be used by applications, which should
1832
 *  use CPLGetConfigOption() instead.
1833
 *  @since 3.8 */
1834
const char *CPL_STDCALL CPLGetGlobalConfigOption(const char *pszKey,
1835
                                                 const char *pszDefault)
1836
0
{
1837
0
    return CPLGetGlobalConfigOption(
1838
0
        pszKey, pszDefault, /* bSubstituteNullValueMarkerWithNull = */ true);
1839
0
}
1840
1841
static const char *
1842
CPLGetGlobalConfigOption(const char *pszKey, const char *pszDefault,
1843
                         bool bSubstituteNullValueMarkerWithNull)
1844
158k
{
1845
1846
#ifdef DEBUG_CONFIG_OPTIONS
1847
    CPLAccessConfigOption(pszKey, TRUE);
1848
#endif
1849
1850
158k
    CPLMutexHolderD(&hConfigMutex);
1851
1852
158k
    const char *pszResult =
1853
158k
        CSLFetchNameValue(const_cast<char **>(g_papszConfigOptions), pszKey);
1854
1855
158k
    if (pszResult == nullptr || (bSubstituteNullValueMarkerWithNull &&
1856
0
                                 strcmp(pszResult, CPL_NULL_VALUE) == 0))
1857
117k
        return pszDefault;
1858
1859
40.5k
    return pszResult;
1860
158k
}
1861
1862
/************************************************************************/
1863
/*                   CPLSubscribeToSetConfigOption()                    */
1864
/************************************************************************/
1865
1866
/**
1867
 * Install a callback that will be notified of calls to CPLSetConfigOption()/
1868
 * CPLSetThreadLocalConfigOption()
1869
 *
1870
 * @param pfnCallback Callback. Must not be NULL
1871
 * @param pUserData Callback user data. May be NULL.
1872
 * @return subscriber ID that can be used with CPLUnsubscribeToSetConfigOption()
1873
 * @since GDAL 3.7
1874
 */
1875
1876
int CPLSubscribeToSetConfigOption(CPLSetConfigOptionSubscriber pfnCallback,
1877
                                  void *pUserData)
1878
2
{
1879
2
    CPLMutexHolderD(&hConfigMutex);
1880
2
    for (int nId = 0;
1881
2
         nId < static_cast<int>(gSetConfigOptionSubscribers.size()); ++nId)
1882
0
    {
1883
0
        if (!gSetConfigOptionSubscribers[nId].first)
1884
0
        {
1885
0
            gSetConfigOptionSubscribers[nId].first = pfnCallback;
1886
0
            gSetConfigOptionSubscribers[nId].second = pUserData;
1887
0
            return nId;
1888
0
        }
1889
0
    }
1890
2
    int nId = static_cast<int>(gSetConfigOptionSubscribers.size());
1891
2
    gSetConfigOptionSubscribers.push_back(
1892
2
        std::pair<CPLSetConfigOptionSubscriber, void *>(pfnCallback,
1893
2
                                                        pUserData));
1894
2
    return nId;
1895
2
}
1896
1897
/************************************************************************/
1898
/*                  CPLUnsubscribeToSetConfigOption()                   */
1899
/************************************************************************/
1900
1901
/**
1902
 * Remove a subscriber installed with CPLSubscribeToSetConfigOption()
1903
 *
1904
 * @param nId Subscriber id returned by CPLSubscribeToSetConfigOption()
1905
 * @since GDAL 3.7
1906
 */
1907
1908
void CPLUnsubscribeToSetConfigOption(int nId)
1909
0
{
1910
0
    CPLMutexHolderD(&hConfigMutex);
1911
0
    if (nId == static_cast<int>(gSetConfigOptionSubscribers.size()) - 1)
1912
0
    {
1913
0
        gSetConfigOptionSubscribers.resize(gSetConfigOptionSubscribers.size() -
1914
0
                                           1);
1915
0
    }
1916
0
    else if (nId >= 0 &&
1917
0
             nId < static_cast<int>(gSetConfigOptionSubscribers.size()))
1918
0
    {
1919
0
        gSetConfigOptionSubscribers[nId].first = nullptr;
1920
0
    }
1921
0
}
1922
1923
/************************************************************************/
1924
/*              NotifyOtherComponentsConfigOptionChanged()              */
1925
/************************************************************************/
1926
1927
static void NotifyOtherComponentsConfigOptionChanged(const char *pszKey,
1928
                                                     const char *pszValue,
1929
                                                     bool bThreadLocal)
1930
9.43k
{
1931
    // When changing authentication parameters of virtual file systems,
1932
    // partially invalidate cached state about file availability.
1933
9.43k
    if (STARTS_WITH_CI(pszKey, "AWS_") || STARTS_WITH_CI(pszKey, "GS_") ||
1934
9.43k
        STARTS_WITH_CI(pszKey, "GOOGLE_") ||
1935
9.43k
        STARTS_WITH_CI(pszKey, "GDAL_HTTP_HEADER_FILE") ||
1936
9.43k
        STARTS_WITH_CI(pszKey, "AZURE_") ||
1937
9.11k
        (STARTS_WITH_CI(pszKey, "SWIFT_") && !EQUAL(pszKey, "SWIFT_MAX_KEYS")))
1938
454
    {
1939
454
        VSICurlAuthParametersChanged();
1940
454
    }
1941
1942
9.43k
    if (!gSetConfigOptionSubscribers.empty())
1943
8.59k
    {
1944
8.59k
        for (const auto &iter : gSetConfigOptionSubscribers)
1945
8.59k
        {
1946
8.59k
            if (iter.first)
1947
8.59k
                iter.first(pszKey, pszValue, bThreadLocal, iter.second);
1948
8.59k
        }
1949
8.59k
    }
1950
9.43k
}
1951
1952
/************************************************************************/
1953
/*                         CPLIsDebugEnabled()                          */
1954
/************************************************************************/
1955
1956
static int gnDebug = -1;
1957
1958
/** Returns whether CPL_DEBUG is enabled.
1959
 *
1960
 * @since 3.11
1961
 */
1962
bool CPLIsDebugEnabled()
1963
9.11k
{
1964
9.11k
    if (gnDebug < 0)
1965
2
    {
1966
        // Check that apszKnownConfigOptions is correctly sorted with
1967
        // STRCASECMP() criterion.
1968
2.20k
        for (size_t i = 1; i < CPL_ARRAYSIZE(apszKnownConfigOptions); ++i)
1969
2.20k
        {
1970
2.20k
            if (STRCASECMP(apszKnownConfigOptions[i - 1],
1971
2.20k
                           apszKnownConfigOptions[i]) >= 0)
1972
0
            {
1973
0
                CPLError(CE_Failure, CPLE_AppDefined,
1974
0
                         "ERROR: apszKnownConfigOptions[] isn't correctly "
1975
0
                         "sorted: %s >= %s",
1976
0
                         apszKnownConfigOptions[i - 1],
1977
0
                         apszKnownConfigOptions[i]);
1978
0
            }
1979
2.20k
        }
1980
2
        gnDebug = CPLTestBool(CPLGetConfigOption("CPL_DEBUG", "OFF"));
1981
2
    }
1982
1983
9.11k
    return gnDebug != 0;
1984
9.11k
}
1985
1986
/************************************************************************/
1987
/*                    CPLDeclareKnownConfigOption()                     */
1988
/************************************************************************/
1989
1990
static std::mutex goMutexDeclaredKnownConfigOptions;
1991
static std::set<CPLString> goSetKnownConfigOptions;
1992
1993
/** Declare that the specified configuration option is known.
1994
 *
1995
 * This is useful to avoid a warning to be emitted on unknown configuration
1996
 * options when CPL_DEBUG is enabled.
1997
 *
1998
 * @param pszKey Name of the configuration option to declare.
1999
 * @param pszDefinition Unused for now. Must be set to nullptr.
2000
 * @since 3.11
2001
 */
2002
void CPLDeclareKnownConfigOption(const char *pszKey,
2003
                                 [[maybe_unused]] const char *pszDefinition)
2004
0
{
2005
0
    std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
2006
0
    goSetKnownConfigOptions.insert(CPLString(pszKey).toupper());
2007
0
}
2008
2009
/************************************************************************/
2010
/*                      CPLGetKnownConfigOptions()                      */
2011
/************************************************************************/
2012
2013
/** Return the list of known configuration options.
2014
 *
2015
 * Must be freed with CSLDestroy().
2016
 * @since 3.11
2017
 */
2018
char **CPLGetKnownConfigOptions()
2019
0
{
2020
0
    std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
2021
0
    CPLStringList aosList;
2022
0
    for (const char *pszKey : apszKnownConfigOptions)
2023
0
        aosList.AddString(pszKey);
2024
0
    for (const auto &osKey : goSetKnownConfigOptions)
2025
0
        aosList.AddString(osKey);
2026
0
    return aosList.StealList();
2027
0
}
2028
2029
/************************************************************************/
2030
/*            CPLSetConfigOptionDetectUnknownConfigOption()             */
2031
/************************************************************************/
2032
2033
static void CPLSetConfigOptionDetectUnknownConfigOption(const char *pszKey,
2034
                                                        const char *pszValue)
2035
9.43k
{
2036
9.43k
    if (EQUAL(pszKey, "CPL_DEBUG"))
2037
318
    {
2038
318
        gnDebug = pszValue ? CPLTestBool(pszValue) : false;
2039
318
    }
2040
9.11k
    else if (CPLIsDebugEnabled())
2041
7.95k
    {
2042
7.95k
        if (!std::binary_search(std::begin(apszKnownConfigOptions),
2043
7.95k
                                std::end(apszKnownConfigOptions), pszKey,
2044
7.95k
                                [](const char *a, const char *b)
2045
88.8k
                                { return STRCASECMP(a, b) < 0; }))
2046
7.34k
        {
2047
7.34k
            bool bFound;
2048
7.34k
            {
2049
7.34k
                std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
2050
7.34k
                bFound = cpl::contains(goSetKnownConfigOptions,
2051
7.34k
                                       CPLString(pszKey).toupper());
2052
7.34k
            }
2053
7.34k
            if (!bFound)
2054
7.34k
            {
2055
7.34k
                const char *pszOldValue = CPLGetConfigOption(pszKey, nullptr);
2056
7.34k
                if (!((!pszValue && !pszOldValue) ||
2057
7.34k
                      (pszValue && pszOldValue &&
2058
4.83k
                       EQUAL(pszValue, pszOldValue))))
2059
4.69k
                {
2060
4.69k
                    CPLError(CE_Warning, CPLE_AppDefined,
2061
4.69k
                             "Unknown configuration option '%s'.", pszKey);
2062
4.69k
                }
2063
7.34k
            }
2064
7.34k
        }
2065
7.95k
    }
2066
9.43k
}
2067
2068
/************************************************************************/
2069
/*                         CPLSetConfigOption()                         */
2070
/************************************************************************/
2071
2072
/**
2073
 * Set a configuration option for GDAL/OGR use.
2074
 *
2075
 * Those options are defined as a (key, value) couple. The value corresponding
2076
 * to a key can be got later with the CPLGetConfigOption() method.
2077
 *
2078
 * This mechanism is similar to environment variables, but options set with
2079
 * CPLSetConfigOption() overrides, for CPLGetConfigOption() point of view,
2080
 * values defined in the environment.
2081
 *
2082
 * If CPLSetConfigOption() is called several times with the same key, the
2083
 * value provided during the last call will be used.
2084
 *
2085
 * Options can also be passed on the command line of most GDAL utilities
2086
 * with '\--config KEY VALUE' (or '\--config KEY=VALUE' since GDAL 3.10).
2087
 * For example, ogrinfo \--config CPL_DEBUG ON ~/data/test/point.shp
2088
 *
2089
 * This function can also be used to clear a setting by passing NULL as the
2090
 * value (note: passing NULL will not unset an existing environment variable;
2091
 * it will just unset a value previously set by CPLSetConfigOption()).
2092
 *
2093
 * Note that setting the GDAL_CACHEMAX configuration option after at least one
2094
 * raster has been read will be without effect. Use GDALSetCacheMax64()
2095
 * instead.
2096
 *
2097
 * Starting with GDAL 3.11, if CPL_DEBUG is enabled prior to this call, and
2098
 * CPLSetConfigOption() is called with a key that is neither a known
2099
 * configuration option of GDAL itself, or one that has been declared with
2100
 * CPLDeclareKnownConfigOption(), a warning will be emitted.
2101
 *
2102
 * Starting with GDAL 3.13, the CPL_NULL_VALUE macro can be used as the value
2103
 * to indicate that callers of CPLGetConfigOption() should see the default value,
2104
 * instead of the value of the corresponding environment variable.
2105
 *
2106
 * @param pszKey the key of the option
2107
 * @param pszValue the value of the option, NULL to clear a setting, or
2108
 *                 macro CPL_NULL_VALUE.
2109
 * @see https://gdal.org/user/configoptions.html
2110
 */
2111
void CPL_STDCALL CPLSetConfigOption(const char *pszKey, const char *pszValue)
2112
2113
9.43k
{
2114
#ifdef DEBUG_CONFIG_OPTIONS
2115
    CPLAccessConfigOption(pszKey, FALSE);
2116
#endif
2117
9.43k
    CPLMutexHolderD(&hConfigMutex);
2118
2119
9.43k
#ifdef OGRAPISPY_ENABLED
2120
9.43k
    OGRAPISPYCPLSetConfigOption(pszKey, pszValue);
2121
9.43k
#endif
2122
2123
9.43k
    CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
2124
2125
9.43k
    g_papszConfigOptions = const_cast<volatile char **>(CSLSetNameValue(
2126
9.43k
        const_cast<char **>(g_papszConfigOptions), pszKey, pszValue));
2127
2128
9.43k
    NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
2129
9.43k
                                             /*bTheadLocal=*/false);
2130
9.43k
}
2131
2132
/************************************************************************/
2133
/*                    CPLSetThreadLocalTLSFreeFunc()                    */
2134
/************************************************************************/
2135
2136
/* non-stdcall wrapper function for CSLDestroy() (#5590) */
2137
static void CPLSetThreadLocalTLSFreeFunc(void *pData)
2138
0
{
2139
0
    CSLDestroy(reinterpret_cast<char **>(pData));
2140
0
}
2141
2142
/************************************************************************/
2143
/*                   CPLSetThreadLocalConfigOption()                    */
2144
/************************************************************************/
2145
2146
/**
2147
 * Set a configuration option for GDAL/OGR use.
2148
 *
2149
 * Those options are defined as a (key, value) couple. The value corresponding
2150
 * to a key can be got later with the CPLGetConfigOption() method.
2151
 *
2152
 * This function sets the configuration option that only applies in the
2153
 * current thread, as opposed to CPLSetConfigOption() which sets an option
2154
 * that applies on all threads. CPLSetThreadLocalConfigOption() will override
2155
 * the effect of CPLSetConfigOption) for the current thread.
2156
 *
2157
 * This function can also be used to clear a setting by passing NULL as the
2158
 * value (note: passing NULL will not unset an existing environment variable or
2159
 * a value set through CPLSetConfigOption();
2160
 * it will just unset a value previously set by
2161
 * CPLSetThreadLocalConfigOption()).
2162
 *
2163
 * Note that setting the GDAL_CACHEMAX configuration option after at least one
2164
 * raster has been read will be without effect. Use GDALSetCacheMax64()
2165
 * instead.
2166
 *
2167
 * Starting with GDAL 3.13, the CPL_NULL_VALUE macro can be used as the value
2168
 * to indicate that callers of CPLGetConfigOption() should see the default value,
2169
 * instead of the value of the corresponding environment variable.
2170
 *
2171
 * @param pszKey the key of the option
2172
 * @param pszValue the value of the option, NULL to clear a setting, or
2173
 *                 macro CPL_NULL_VALUE.
2174
 */
2175
2176
void CPL_STDCALL CPLSetThreadLocalConfigOption(const char *pszKey,
2177
                                               const char *pszValue)
2178
2179
0
{
2180
#ifdef DEBUG_CONFIG_OPTIONS
2181
    CPLAccessConfigOption(pszKey, FALSE);
2182
#endif
2183
2184
0
#ifdef OGRAPISPY_ENABLED
2185
0
    OGRAPISPYCPLSetThreadLocalConfigOption(pszKey, pszValue);
2186
0
#endif
2187
2188
0
    int bMemoryError = FALSE;
2189
0
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2190
0
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
2191
0
    if (bMemoryError)
2192
0
        return;
2193
2194
0
    CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
2195
2196
0
    papszTLConfigOptions =
2197
0
        CSLSetNameValue(papszTLConfigOptions, pszKey, pszValue);
2198
2199
0
    CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
2200
0
                          CPLSetThreadLocalTLSFreeFunc);
2201
2202
0
    NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
2203
0
                                             /*bTheadLocal=*/true);
2204
0
}
2205
2206
/************************************************************************/
2207
/*                   CPLGetThreadLocalConfigOptions()                   */
2208
/************************************************************************/
2209
2210
/**
2211
 * Return the list of thread local configuration options as KEY=VALUE pairs.
2212
 *
2213
 * Options that through environment variables or with
2214
 * CPLSetConfigOption() will *not* be listed.
2215
 *
2216
 * @return a copy of the list, to be freed with CSLDestroy().
2217
 */
2218
char **CPLGetThreadLocalConfigOptions(void)
2219
0
{
2220
0
    int bMemoryError = FALSE;
2221
0
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2222
0
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
2223
0
    if (bMemoryError)
2224
0
        return nullptr;
2225
0
    return CSLDuplicate(papszTLConfigOptions);
2226
0
}
2227
2228
/************************************************************************/
2229
/*                   CPLSetThreadLocalConfigOptions()                   */
2230
/************************************************************************/
2231
2232
/**
2233
 * Replace the full list of thread local configuration options with the
2234
 * passed list of KEY=VALUE pairs.
2235
 *
2236
 * This has the same effect of clearing the existing list, and setting
2237
 * individually each pair with the CPLSetThreadLocalConfigOption() API.
2238
 *
2239
 * This does not affect options set through environment variables or with
2240
 * CPLSetConfigOption().
2241
 *
2242
 * The passed list is copied by the function.
2243
 *
2244
 * @param papszConfigOptions the new list (or NULL).
2245
 *
2246
 */
2247
void CPLSetThreadLocalConfigOptions(const char *const *papszConfigOptions)
2248
0
{
2249
0
    int bMemoryError = FALSE;
2250
0
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2251
0
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
2252
0
    if (bMemoryError)
2253
0
        return;
2254
0
    CSLDestroy(papszTLConfigOptions);
2255
0
    papszTLConfigOptions =
2256
0
        CSLDuplicate(const_cast<char **>(papszConfigOptions));
2257
0
    CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
2258
0
                          CPLSetThreadLocalTLSFreeFunc);
2259
0
}
2260
2261
/************************************************************************/
2262
/*                           CPLFreeConfig()                            */
2263
/************************************************************************/
2264
2265
void CPL_STDCALL CPLFreeConfig()
2266
2267
0
{
2268
0
    {
2269
0
        CPLMutexHolderD(&hConfigMutex);
2270
2271
0
        CSLDestroy(const_cast<char **>(g_papszConfigOptions));
2272
0
        g_papszConfigOptions = nullptr;
2273
2274
0
        int bMemoryError = FALSE;
2275
0
        char **papszTLConfigOptions = reinterpret_cast<char **>(
2276
0
            CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
2277
0
        if (papszTLConfigOptions != nullptr)
2278
0
        {
2279
0
            CSLDestroy(papszTLConfigOptions);
2280
0
            CPLSetTLS(CTLS_CONFIGOPTIONS, nullptr, FALSE);
2281
0
        }
2282
0
    }
2283
0
    CPLDestroyMutex(hConfigMutex);
2284
0
    hConfigMutex = nullptr;
2285
0
}
2286
2287
/************************************************************************/
2288
/*                    CPLLoadConfigOptionsFromFile()                    */
2289
/************************************************************************/
2290
2291
/** Load configuration from a given configuration file.
2292
2293
A configuration file is a text file in a .ini style format, that lists
2294
configuration options and their values.
2295
Lines starting with # are comment lines.
2296
2297
Example:
2298
\verbatim
2299
[configoptions]
2300
# set BAR as the value of configuration option FOO
2301
FOO=BAR
2302
\endverbatim
2303
2304
Starting with GDAL 3.5, a configuration file can also contain credentials
2305
(or more generally options related to a virtual file system) for a given path
2306
prefix, that can also be set with VSISetPathSpecificOption(). Credentials should
2307
be put under a [credentials] section, and for each path prefix, under a relative
2308
subsection whose name starts with "[." (e.g. "[.some_arbitrary_name]"), and
2309
whose first key is "path".
2310
2311
Example:
2312
\verbatim
2313
[credentials]
2314
2315
[.private_bucket]
2316
path=/vsis3/my_private_bucket
2317
AWS_SECRET_ACCESS_KEY=...
2318
AWS_ACCESS_KEY_ID=...
2319
2320
[.sentinel_s2_l1c]
2321
path=/vsis3/sentinel-s2-l1c
2322
AWS_REQUEST_PAYER=requester
2323
\endverbatim
2324
2325
Starting with GDAL 3.6, a leading [directives] section might be added with
2326
a "ignore-env-vars=yes" setting to indicate that, starting with that point,
2327
all environment variables should be ignored, and only configuration options
2328
defined in the [configoptions] sections or through the CPLSetConfigOption() /
2329
CPLSetThreadLocalConfigOption() functions should be taken into account.
2330
2331
This function is typically called by CPLLoadConfigOptionsFromPredefinedFiles()
2332
2333
@param pszFilename File where to load configuration from.
2334
@param bOverrideEnvVars Whether configuration options from the configuration
2335
                        file should override environment variables.
2336
@since GDAL 3.3
2337
 */
2338
void CPLLoadConfigOptionsFromFile(const char *pszFilename, int bOverrideEnvVars)
2339
0
{
2340
0
    VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
2341
0
    if (fp == nullptr)
2342
0
        return;
2343
0
    CPLDebug("CPL", "Loading configuration from %s", pszFilename);
2344
0
    const char *pszLine;
2345
0
    enum class Section
2346
0
    {
2347
0
        NONE,
2348
0
        GENERAL,
2349
0
        CONFIG_OPTIONS,
2350
0
        CREDENTIALS,
2351
0
    };
2352
0
    Section eCurrentSection = Section::NONE;
2353
0
    bool bInSubsection = false;
2354
0
    std::string osPath;
2355
0
    int nSectionCounter = 0;
2356
2357
0
    const auto IsSpaceOnly = [](const char *pszStr)
2358
0
    {
2359
0
        for (; *pszStr; ++pszStr)
2360
0
        {
2361
0
            if (!isspace(static_cast<unsigned char>(*pszStr)))
2362
0
                return false;
2363
0
        }
2364
0
        return true;
2365
0
    };
2366
2367
0
    while ((pszLine = CPLReadLine2L(fp, -1, nullptr)) != nullptr)
2368
0
    {
2369
0
        if (IsSpaceOnly(pszLine))
2370
0
        {
2371
            // Blank line
2372
0
        }
2373
0
        else if (pszLine[0] == '#')
2374
0
        {
2375
            // Comment line
2376
0
        }
2377
0
        else if (strcmp(pszLine, "[configoptions]") == 0)
2378
0
        {
2379
0
            nSectionCounter++;
2380
0
            eCurrentSection = Section::CONFIG_OPTIONS;
2381
0
        }
2382
0
        else if (strcmp(pszLine, "[credentials]") == 0)
2383
0
        {
2384
0
            nSectionCounter++;
2385
0
            eCurrentSection = Section::CREDENTIALS;
2386
0
            bInSubsection = false;
2387
0
            osPath.clear();
2388
0
        }
2389
0
        else if (strcmp(pszLine, "[directives]") == 0)
2390
0
        {
2391
0
            nSectionCounter++;
2392
0
            if (nSectionCounter != 1)
2393
0
            {
2394
0
                CPLError(CE_Warning, CPLE_AppDefined,
2395
0
                         "The [directives] section should be the first one in "
2396
0
                         "the file, otherwise some its settings might not be "
2397
0
                         "used correctly.");
2398
0
            }
2399
0
            eCurrentSection = Section::GENERAL;
2400
0
        }
2401
0
        else if (eCurrentSection == Section::GENERAL)
2402
0
        {
2403
0
            char *pszKey = nullptr;
2404
0
            const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
2405
0
            if (pszKey && pszValue)
2406
0
            {
2407
0
                if (strcmp(pszKey, "ignore-env-vars") == 0)
2408
0
                {
2409
0
                    gbIgnoreEnvVariables = CPLTestBool(pszValue);
2410
0
                }
2411
0
                else
2412
0
                {
2413
0
                    CPLError(CE_Warning, CPLE_AppDefined,
2414
0
                             "Ignoring %s line in [directives] section",
2415
0
                             pszLine);
2416
0
                }
2417
0
            }
2418
0
            CPLFree(pszKey);
2419
0
        }
2420
0
        else if (eCurrentSection == Section::CREDENTIALS)
2421
0
        {
2422
0
            if (strncmp(pszLine, "[.", 2) == 0)
2423
0
            {
2424
0
                bInSubsection = true;
2425
0
                osPath.clear();
2426
0
            }
2427
0
            else if (bInSubsection)
2428
0
            {
2429
0
                char *pszKey = nullptr;
2430
0
                const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
2431
0
                if (pszKey && pszValue)
2432
0
                {
2433
0
                    if (strcmp(pszKey, "path") == 0)
2434
0
                    {
2435
0
                        if (!osPath.empty())
2436
0
                        {
2437
0
                            CPLError(
2438
0
                                CE_Warning, CPLE_AppDefined,
2439
0
                                "Duplicated 'path' key in the same subsection. "
2440
0
                                "Ignoring %s=%s",
2441
0
                                pszKey, pszValue);
2442
0
                        }
2443
0
                        else
2444
0
                        {
2445
0
                            osPath = pszValue;
2446
0
                        }
2447
0
                    }
2448
0
                    else if (osPath.empty())
2449
0
                    {
2450
0
                        CPLError(CE_Warning, CPLE_AppDefined,
2451
0
                                 "First entry in a credentials subsection "
2452
0
                                 "should be 'path'.");
2453
0
                    }
2454
0
                    else
2455
0
                    {
2456
0
                        VSISetPathSpecificOption(osPath.c_str(), pszKey,
2457
0
                                                 pszValue);
2458
0
                    }
2459
0
                }
2460
0
                CPLFree(pszKey);
2461
0
            }
2462
0
            else if (pszLine[0] == '[')
2463
0
            {
2464
0
                eCurrentSection = Section::NONE;
2465
0
            }
2466
0
            else
2467
0
            {
2468
0
                CPLError(CE_Warning, CPLE_AppDefined,
2469
0
                         "Ignoring content in [credential] section that is not "
2470
0
                         "in a [.xxxxx] subsection");
2471
0
            }
2472
0
        }
2473
0
        else if (pszLine[0] == '[')
2474
0
        {
2475
0
            eCurrentSection = Section::NONE;
2476
0
        }
2477
0
        else if (eCurrentSection == Section::CONFIG_OPTIONS)
2478
0
        {
2479
0
            char *pszKey = nullptr;
2480
0
            const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
2481
0
            if (pszKey && pszValue)
2482
0
            {
2483
0
                if (bOverrideEnvVars || gbIgnoreEnvVariables ||
2484
0
                    getenv(pszKey) == nullptr)
2485
0
                {
2486
0
                    CPLDebugOnly("CPL", "Setting configuration option %s=%s",
2487
0
                                 pszKey, pszValue);
2488
0
                    CPLSetConfigOption(pszKey, pszValue);
2489
0
                }
2490
0
                else
2491
0
                {
2492
0
                    CPLDebug("CPL",
2493
0
                             "Ignoring configuration option %s=%s from "
2494
0
                             "configuration file as it is already set "
2495
0
                             "as an environment variable",
2496
0
                             pszKey, pszValue);
2497
0
                }
2498
0
            }
2499
0
            CPLFree(pszKey);
2500
0
        }
2501
0
    }
2502
0
    VSIFCloseL(fp);
2503
0
}
2504
2505
/************************************************************************/
2506
/*              CPLLoadConfigOptionsFromPredefinedFiles()               */
2507
/************************************************************************/
2508
2509
/** Load configuration from a set of predefined files.
2510
 *
2511
 * If the environment variable (or configuration option) GDAL_CONFIG_FILE is
2512
 * set, then CPLLoadConfigOptionsFromFile() will be called with the value of
2513
 * this configuration option as the file location.
2514
 *
2515
 * Otherwise, for Unix builds, CPLLoadConfigOptionsFromFile() will be called
2516
 * with ${sysconfdir}/gdal/gdalrc first where ${sysconfdir} evaluates
2517
 * to ${prefix}/etc, unless the \--sysconfdir switch of configure has been
2518
 * invoked.
2519
 *
2520
 * Then CPLLoadConfigOptionsFromFile() will be called with ${HOME}/.gdal/gdalrc
2521
 * on Unix builds (potentially overriding what was loaded with the sysconfdir)
2522
 * or ${USERPROFILE}/.gdal/gdalrc on Windows builds.
2523
 *
2524
 * CPLLoadConfigOptionsFromFile() will be called with bOverrideEnvVars = false,
2525
 * that is the value of environment variables previously set will be used
2526
 * instead of the value set in the configuration files (unless the configuration
2527
 * file contains a leading [directives] section with a "ignore-env-vars=yes"
2528
 * setting).
2529
 *
2530
 * This function is automatically called by GDALDriverManager() constructor
2531
 *
2532
 * @since GDAL 3.3
2533
 */
2534
void CPLLoadConfigOptionsFromPredefinedFiles()
2535
0
{
2536
0
    const char *pszFile = CPLGetConfigOption("GDAL_CONFIG_FILE", nullptr);
2537
0
    if (pszFile != nullptr)
2538
0
    {
2539
0
        CPLLoadConfigOptionsFromFile(pszFile, false);
2540
0
    }
2541
0
    else
2542
0
    {
2543
0
#ifdef SYSCONFDIR
2544
0
        CPLLoadConfigOptionsFromFile(
2545
0
            CPLFormFilenameSafe(
2546
0
                CPLFormFilenameSafe(SYSCONFDIR, "gdal", nullptr).c_str(),
2547
0
                "gdalrc", nullptr)
2548
0
                .c_str(),
2549
0
            false);
2550
0
#endif
2551
2552
#ifdef _WIN32
2553
        const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
2554
#else
2555
0
        const char *pszHome = CPLGetConfigOption("HOME", nullptr);
2556
0
#endif
2557
0
        if (pszHome != nullptr)
2558
0
        {
2559
0
            CPLLoadConfigOptionsFromFile(
2560
0
                CPLFormFilenameSafe(
2561
0
                    CPLFormFilenameSafe(pszHome, ".gdal", nullptr).c_str(),
2562
0
                    "gdalrc", nullptr)
2563
0
                    .c_str(),
2564
0
                false);
2565
0
        }
2566
0
    }
2567
0
}
2568
2569
/************************************************************************/
2570
/*                              CPLStat()                               */
2571
/************************************************************************/
2572
2573
/** Same as VSIStat() except it works on "C:" as if it were "C:\". */
2574
2575
int CPLStat(const char *pszPath, VSIStatBuf *psStatBuf)
2576
2577
0
{
2578
0
    if (strlen(pszPath) == 2 && pszPath[1] == ':')
2579
0
    {
2580
0
        char szAltPath[4] = {pszPath[0], pszPath[1], '\\', '\0'};
2581
0
        return VSIStat(szAltPath, psStatBuf);
2582
0
    }
2583
2584
0
    return VSIStat(pszPath, psStatBuf);
2585
0
}
2586
2587
/************************************************************************/
2588
/*                            proj_strtod()                             */
2589
/************************************************************************/
2590
static double proj_strtod(char *nptr, char **endptr)
2591
2592
0
{
2593
0
    char c = '\0';
2594
0
    char *cp = nptr;
2595
2596
    // Scan for characters which cause problems with VC++ strtod().
2597
0
    while ((c = *cp) != '\0')
2598
0
    {
2599
0
        if (c == 'd' || c == 'D')
2600
0
        {
2601
            // Found one, so NUL it out, call strtod(),
2602
            // then restore it and return.
2603
0
            *cp = '\0';
2604
0
            const double result = CPLStrtod(nptr, endptr);
2605
0
            *cp = c;
2606
0
            return result;
2607
0
        }
2608
0
        ++cp;
2609
0
    }
2610
2611
    // No offending characters, just handle normally.
2612
2613
0
    return CPLStrtod(nptr, endptr);
2614
0
}
2615
2616
/************************************************************************/
2617
/*                            CPLDMSToDec()                             */
2618
/************************************************************************/
2619
2620
static const char *sym = "NnEeSsWw";
2621
constexpr double vm[] = {1.0, 0.0166666666667, 0.00027777778};
2622
2623
/** CPLDMSToDec */
2624
double CPLDMSToDec(const char *is)
2625
2626
0
{
2627
    // Copy string into work space.
2628
0
    while (isspace(static_cast<unsigned char>(*is)))
2629
0
        ++is;
2630
2631
0
    const char *p = is;
2632
0
    char work[64] = {};
2633
0
    char *s = work;
2634
0
    int n = sizeof(work);
2635
0
    for (; isgraph(*p) && --n;)
2636
0
        *s++ = *p++;
2637
0
    *s = '\0';
2638
    // It is possible that a really odd input (like lots of leading
2639
    // zeros) could be truncated in copying into work.  But...
2640
0
    s = work;
2641
0
    int sign = *s;
2642
2643
0
    if (sign == '+' || sign == '-')
2644
0
        s++;
2645
0
    else
2646
0
        sign = '+';
2647
2648
0
    int nl = 0;
2649
0
    double v = 0.0;
2650
0
    for (; nl < 3; nl = n + 1)
2651
0
    {
2652
0
        if (!(isdigit(static_cast<unsigned char>(*s)) || *s == '.'))
2653
0
            break;
2654
0
        const double tv = proj_strtod(s, &s);
2655
0
        if (tv == HUGE_VAL)
2656
0
            return tv;
2657
0
        switch (*s)
2658
0
        {
2659
0
            case 'D':
2660
0
            case 'd':
2661
0
                n = 0;
2662
0
                break;
2663
0
            case '\'':
2664
0
                n = 1;
2665
0
                break;
2666
0
            case '"':
2667
0
                n = 2;
2668
0
                break;
2669
0
            case 'r':
2670
0
            case 'R':
2671
0
                if (nl)
2672
0
                {
2673
0
                    return 0.0;
2674
0
                }
2675
0
                ++s;
2676
0
                v = tv;
2677
0
                goto skip;
2678
0
            default:
2679
0
                v += tv * vm[nl];
2680
0
            skip:
2681
0
                n = 4;
2682
0
                continue;
2683
0
        }
2684
0
        if (n < nl)
2685
0
        {
2686
0
            return 0.0;
2687
0
        }
2688
0
        v += tv * vm[n];
2689
0
        ++s;
2690
0
    }
2691
    // Postfix sign.
2692
0
    if (*s && ((p = strchr(sym, *s))) != nullptr)
2693
0
    {
2694
0
        sign = (p - sym) >= 4 ? '-' : '+';
2695
0
        ++s;
2696
0
    }
2697
0
    if (sign == '-')
2698
0
        v = -v;
2699
2700
0
    return v;
2701
0
}
2702
2703
/************************************************************************/
2704
/*                            CPLDecToDMS()                             */
2705
/************************************************************************/
2706
2707
/** Translate a decimal degrees value to a DMS string with hemisphere. */
2708
2709
const char *CPLDecToDMS(double dfAngle, const char *pszAxis, int nPrecision)
2710
2711
0
{
2712
0
    VALIDATE_POINTER1(pszAxis, "CPLDecToDMS", "");
2713
2714
0
    if (std::isnan(dfAngle))
2715
0
        return "Invalid angle";
2716
2717
0
    const double dfEpsilon = (0.5 / 3600.0) * pow(0.1, nPrecision);
2718
0
    const double dfABSAngle = std::abs(dfAngle) + dfEpsilon;
2719
0
    if (dfABSAngle > 361.0)
2720
0
    {
2721
0
        return "Invalid angle";
2722
0
    }
2723
2724
0
    const int nDegrees = static_cast<int>(dfABSAngle);
2725
0
    const int nMinutes = static_cast<int>((dfABSAngle - nDegrees) * 60);
2726
0
    double dfSeconds = dfABSAngle * 3600 - nDegrees * 3600 - nMinutes * 60;
2727
2728
0
    if (dfSeconds > dfEpsilon * 3600.0)
2729
0
        dfSeconds -= dfEpsilon * 3600.0;
2730
2731
0
    const char *pszHemisphere = nullptr;
2732
0
    if (EQUAL(pszAxis, "Long") && dfAngle < 0.0)
2733
0
        pszHemisphere = "W";
2734
0
    else if (EQUAL(pszAxis, "Long"))
2735
0
        pszHemisphere = "E";
2736
0
    else if (dfAngle < 0.0)
2737
0
        pszHemisphere = "S";
2738
0
    else
2739
0
        pszHemisphere = "N";
2740
2741
0
    char szFormat[30] = {};
2742
0
    CPLsnprintf(szFormat, sizeof(szFormat), "%%3dd%%2d\'%%%d.%df\"%s",
2743
0
                nPrecision + 3, nPrecision, pszHemisphere);
2744
2745
0
    static CPL_THREADLOCAL char szBuffer[50] = {};
2746
0
    CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, nDegrees, nMinutes,
2747
0
                dfSeconds);
2748
2749
0
    return szBuffer;
2750
0
}
2751
2752
/************************************************************************/
2753
/*                         CPLPackedDMSToDec()                          */
2754
/************************************************************************/
2755
2756
/**
2757
 * Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
2758
 *
2759
 * This function converts a packed DMS angle to seconds. The standard
2760
 * packed DMS format is:
2761
 *
2762
 *  degrees * 1000000 + minutes * 1000 + seconds
2763
 *
2764
 * Example:     angle = 120025045.25 yields
2765
 *              deg = 120
2766
 *              min = 25
2767
 *              sec = 45.25
2768
 *
2769
 * The algorithm used for the conversion is as follows:
2770
 *
2771
 * 1.  The absolute value of the angle is used.
2772
 *
2773
 * 2.  The degrees are separated out:
2774
 *     deg = angle/1000000                    (fractional portion truncated)
2775
 *
2776
 * 3.  The minutes are separated out:
2777
 *     min = (angle - deg * 1000000) / 1000   (fractional portion truncated)
2778
 *
2779
 * 4.  The seconds are then computed:
2780
 *     sec = angle - deg * 1000000 - min * 1000
2781
 *
2782
 * 5.  The total angle in seconds is computed:
2783
 *     sec = deg * 3600.0 + min * 60.0 + sec
2784
 *
2785
 * 6.  The sign of sec is set to that of the input angle.
2786
 *
2787
 * Packed DMS values used by the USGS GCTP package and probably by other
2788
 * software.
2789
 *
2790
 * NOTE: This code does not validate input value. If you give the wrong
2791
 * value, you will get the wrong result.
2792
 *
2793
 * @param dfPacked Angle in packed DMS format.
2794
 *
2795
 * @return Angle in decimal degrees.
2796
 *
2797
 */
2798
2799
double CPLPackedDMSToDec(double dfPacked)
2800
0
{
2801
0
    const double dfSign = dfPacked < 0.0 ? -1 : 1;
2802
2803
0
    double dfSeconds = std::abs(dfPacked);
2804
0
    double dfDegrees = floor(dfSeconds / 1000000.0);
2805
0
    dfSeconds -= dfDegrees * 1000000.0;
2806
0
    const double dfMinutes = floor(dfSeconds / 1000.0);
2807
0
    dfSeconds -= dfMinutes * 1000.0;
2808
0
    dfSeconds = dfSign * (dfDegrees * 3600.0 + dfMinutes * 60.0 + dfSeconds);
2809
0
    dfDegrees = dfSeconds / 3600.0;
2810
2811
0
    return dfDegrees;
2812
0
}
2813
2814
/************************************************************************/
2815
/*                         CPLDecToPackedDMS()                          */
2816
/************************************************************************/
2817
/**
2818
 * Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
2819
 *
2820
 * This function converts a value, specified in decimal degrees into
2821
 * packed DMS angle. The standard packed DMS format is:
2822
 *
2823
 *  degrees * 1000000 + minutes * 1000 + seconds
2824
 *
2825
 * See also CPLPackedDMSToDec().
2826
 *
2827
 * @param dfDec Angle in decimal degrees.
2828
 *
2829
 * @return Angle in packed DMS format.
2830
 *
2831
 */
2832
2833
double CPLDecToPackedDMS(double dfDec)
2834
0
{
2835
0
    const double dfSign = dfDec < 0.0 ? -1 : 1;
2836
2837
0
    dfDec = std::abs(dfDec);
2838
0
    const double dfDegrees = floor(dfDec);
2839
0
    const double dfMinutes = floor((dfDec - dfDegrees) * 60.0);
2840
0
    const double dfSeconds = (dfDec - dfDegrees) * 3600.0 - dfMinutes * 60.0;
2841
2842
0
    return dfSign * (dfDegrees * 1000000.0 + dfMinutes * 1000.0 + dfSeconds);
2843
0
}
2844
2845
/************************************************************************/
2846
/*                         CPLStringToComplex()                         */
2847
/************************************************************************/
2848
2849
/** Fetch the real and imaginary part of a serialized complex number */
2850
CPLErr CPL_DLL CPLStringToComplex(const char *pszString, double *pdfReal,
2851
                                  double *pdfImag)
2852
2853
0
{
2854
0
    while (*pszString == ' ')
2855
0
        pszString++;
2856
2857
0
    char *end;
2858
0
    *pdfReal = CPLStrtod(pszString, &end);
2859
2860
0
    int iPlus = -1;
2861
0
    int iImagEnd = -1;
2862
2863
0
    if (pszString == end)
2864
0
    {
2865
0
        goto error;
2866
0
    }
2867
2868
0
    *pdfImag = 0.0;
2869
2870
0
    for (int i = static_cast<int>(end - pszString);
2871
0
         i < 100 && pszString[i] != '\0' && pszString[i] != ' '; i++)
2872
0
    {
2873
0
        if (pszString[i] == '+')
2874
0
        {
2875
0
            if (iPlus != -1)
2876
0
                goto error;
2877
0
            iPlus = i;
2878
0
        }
2879
0
        if (pszString[i] == '-')
2880
0
        {
2881
0
            if (iPlus != -1)
2882
0
                goto error;
2883
0
            iPlus = i;
2884
0
        }
2885
0
        if (pszString[i] == 'i')
2886
0
        {
2887
0
            if (iPlus == -1)
2888
0
                goto error;
2889
0
            iImagEnd = i;
2890
0
        }
2891
0
    }
2892
2893
    // If we have a "+" or "-" we must also have an "i"
2894
0
    if ((iPlus == -1) != (iImagEnd == -1))
2895
0
    {
2896
0
        goto error;
2897
0
    }
2898
2899
    // Parse imaginary component, if any
2900
0
    if (iPlus > -1)
2901
0
    {
2902
0
        *pdfImag = CPLStrtod(pszString + iPlus, &end);
2903
0
    }
2904
2905
    // Check everything remaining is whitespace
2906
0
    for (; *end != '\0'; end++)
2907
0
    {
2908
0
        if (!isspace(*end) && end - pszString != iImagEnd)
2909
0
        {
2910
0
            goto error;
2911
0
        }
2912
0
    }
2913
2914
0
    return CE_None;
2915
2916
0
error:
2917
0
    CPLError(CE_Failure, CPLE_AppDefined, "Failed to parse number: %s",
2918
0
             pszString);
2919
0
    return CE_Failure;
2920
0
}
2921
2922
/************************************************************************/
2923
/*                           CPLOpenShared()                            */
2924
/************************************************************************/
2925
2926
/**
2927
 * Open a shared file handle.
2928
 *
2929
 * Some operating systems have limits on the number of file handles that can
2930
 * be open at one time.  This function attempts to maintain a registry of
2931
 * already open file handles, and reuse existing ones if the same file
2932
 * is requested by another part of the application.
2933
 *
2934
 * Note that access is only shared for access types "r", "rb", "r+" and
2935
 * "rb+".  All others will just result in direct VSIOpen() calls.  Keep in
2936
 * mind that a file is only reused if the file name is exactly the same.
2937
 * Different names referring to the same file will result in different
2938
 * handles.
2939
 *
2940
 * The VSIFOpen() or VSIFOpenL() function is used to actually open the file,
2941
 * when an existing file handle can't be shared.
2942
 *
2943
 * @param pszFilename the name of the file to open.
2944
 * @param pszAccess the normal fopen()/VSIFOpen() style access string.
2945
 * @param bLargeIn If TRUE VSIFOpenL() (for large files) will be used instead of
2946
 * VSIFOpen().
2947
 *
2948
 * @return a file handle or NULL if opening fails.
2949
 */
2950
2951
FILE *CPLOpenShared(const char *pszFilename, const char *pszAccess,
2952
                    int bLargeIn)
2953
2954
0
{
2955
0
    const bool bLarge = CPL_TO_BOOL(bLargeIn);
2956
0
    CPLMutexHolderD(&hSharedFileMutex);
2957
0
    const GIntBig nPID = CPLGetPID();
2958
2959
    /* -------------------------------------------------------------------- */
2960
    /*      Is there an existing file we can use?                           */
2961
    /* -------------------------------------------------------------------- */
2962
0
    const bool bReuse = EQUAL(pszAccess, "rb") || EQUAL(pszAccess, "rb+");
2963
2964
0
    for (int i = 0; bReuse && i < nSharedFileCount; i++)
2965
0
    {
2966
0
        if (strcmp(pasSharedFileList[i].pszFilename, pszFilename) == 0 &&
2967
0
            !bLarge == !pasSharedFileList[i].bLarge &&
2968
0
            EQUAL(pasSharedFileList[i].pszAccess, pszAccess) &&
2969
0
            nPID == pasSharedFileListExtra[i].nPID)
2970
0
        {
2971
0
            pasSharedFileList[i].nRefCount++;
2972
0
            return pasSharedFileList[i].fp;
2973
0
        }
2974
0
    }
2975
2976
    /* -------------------------------------------------------------------- */
2977
    /*      Open the file.                                                  */
2978
    /* -------------------------------------------------------------------- */
2979
0
    FILE *fp = bLarge
2980
0
                   ? reinterpret_cast<FILE *>(VSIFOpenL(pszFilename, pszAccess))
2981
0
                   : VSIFOpen(pszFilename, pszAccess);
2982
2983
0
    if (fp == nullptr)
2984
0
        return nullptr;
2985
2986
    /* -------------------------------------------------------------------- */
2987
    /*      Add an entry to the list.                                       */
2988
    /* -------------------------------------------------------------------- */
2989
0
    nSharedFileCount++;
2990
2991
0
    pasSharedFileList = static_cast<CPLSharedFileInfo *>(
2992
0
        CPLRealloc(const_cast<CPLSharedFileInfo *>(pasSharedFileList),
2993
0
                   sizeof(CPLSharedFileInfo) * nSharedFileCount));
2994
0
    pasSharedFileListExtra = static_cast<CPLSharedFileInfoExtra *>(
2995
0
        CPLRealloc(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra),
2996
0
                   sizeof(CPLSharedFileInfoExtra) * nSharedFileCount));
2997
2998
0
    pasSharedFileList[nSharedFileCount - 1].fp = fp;
2999
0
    pasSharedFileList[nSharedFileCount - 1].nRefCount = 1;
3000
0
    pasSharedFileList[nSharedFileCount - 1].bLarge = bLarge;
3001
0
    pasSharedFileList[nSharedFileCount - 1].pszFilename =
3002
0
        CPLStrdup(pszFilename);
3003
0
    pasSharedFileList[nSharedFileCount - 1].pszAccess = CPLStrdup(pszAccess);
3004
0
    pasSharedFileListExtra[nSharedFileCount - 1].nPID = nPID;
3005
3006
0
    return fp;
3007
0
}
3008
3009
/************************************************************************/
3010
/*                           CPLCloseShared()                           */
3011
/************************************************************************/
3012
3013
/**
3014
 * Close shared file.
3015
 *
3016
 * Dereferences the indicated file handle, and closes it if the reference
3017
 * count has dropped to zero.  A CPLError() is issued if the file is not
3018
 * in the shared file list.
3019
 *
3020
 * @param fp file handle from CPLOpenShared() to deaccess.
3021
 */
3022
3023
void CPLCloseShared(FILE *fp)
3024
3025
0
{
3026
0
    CPLMutexHolderD(&hSharedFileMutex);
3027
3028
    /* -------------------------------------------------------------------- */
3029
    /*      Search for matching information.                                */
3030
    /* -------------------------------------------------------------------- */
3031
0
    int i = 0;
3032
0
    for (; i < nSharedFileCount && fp != pasSharedFileList[i].fp; i++)
3033
0
    {
3034
0
    }
3035
3036
0
    if (i == nSharedFileCount)
3037
0
    {
3038
0
        CPLError(CE_Failure, CPLE_AppDefined,
3039
0
                 "Unable to find file handle %p in CPLCloseShared().", fp);
3040
0
        return;
3041
0
    }
3042
3043
    /* -------------------------------------------------------------------- */
3044
    /*      Dereference and return if there are still some references.      */
3045
    /* -------------------------------------------------------------------- */
3046
0
    if (--pasSharedFileList[i].nRefCount > 0)
3047
0
        return;
3048
3049
    /* -------------------------------------------------------------------- */
3050
    /*      Close the file, and remove the information.                     */
3051
    /* -------------------------------------------------------------------- */
3052
0
    if (pasSharedFileList[i].bLarge)
3053
0
    {
3054
0
        if (VSIFCloseL(reinterpret_cast<VSILFILE *>(pasSharedFileList[i].fp)) !=
3055
0
            0)
3056
0
        {
3057
0
            CPLError(CE_Failure, CPLE_FileIO, "Error while closing %s",
3058
0
                     pasSharedFileList[i].pszFilename);
3059
0
        }
3060
0
    }
3061
0
    else
3062
0
    {
3063
0
        VSIFClose(pasSharedFileList[i].fp);
3064
0
    }
3065
3066
0
    CPLFree(pasSharedFileList[i].pszFilename);
3067
0
    CPLFree(pasSharedFileList[i].pszAccess);
3068
3069
0
    nSharedFileCount--;
3070
0
    memmove(
3071
0
        const_cast<CPLSharedFileInfo *>(pasSharedFileList + i),
3072
0
        const_cast<CPLSharedFileInfo *>(pasSharedFileList + nSharedFileCount),
3073
0
        sizeof(CPLSharedFileInfo));
3074
0
    memmove(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra + i),
3075
0
            const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra +
3076
0
                                                 nSharedFileCount),
3077
0
            sizeof(CPLSharedFileInfoExtra));
3078
3079
0
    if (nSharedFileCount == 0)
3080
0
    {
3081
0
        CPLFree(const_cast<CPLSharedFileInfo *>(pasSharedFileList));
3082
0
        pasSharedFileList = nullptr;
3083
0
        CPLFree(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra));
3084
0
        pasSharedFileListExtra = nullptr;
3085
0
    }
3086
0
}
3087
3088
/************************************************************************/
3089
/*                     CPLCleanupSharedFileMutex()                      */
3090
/************************************************************************/
3091
3092
void CPLCleanupSharedFileMutex()
3093
0
{
3094
0
    if (hSharedFileMutex != nullptr)
3095
0
    {
3096
0
        CPLDestroyMutex(hSharedFileMutex);
3097
0
        hSharedFileMutex = nullptr;
3098
0
    }
3099
0
}
3100
3101
/************************************************************************/
3102
/*                          CPLGetSharedList()                          */
3103
/************************************************************************/
3104
3105
/**
3106
 * Fetch list of open shared files.
3107
 *
3108
 * @param pnCount place to put the count of entries.
3109
 *
3110
 * @return the pointer to the first in the array of shared file info
3111
 * structures.
3112
 */
3113
3114
CPLSharedFileInfo *CPLGetSharedList(int *pnCount)
3115
3116
0
{
3117
0
    if (pnCount != nullptr)
3118
0
        *pnCount = nSharedFileCount;
3119
3120
0
    return const_cast<CPLSharedFileInfo *>(pasSharedFileList);
3121
0
}
3122
3123
/************************************************************************/
3124
/*                         CPLDumpSharedList()                          */
3125
/************************************************************************/
3126
3127
/**
3128
 * Report open shared files.
3129
 *
3130
 * Dumps all open shared files to the indicated file handle.  If the
3131
 * file handle is NULL information is sent via the CPLDebug() call.
3132
 *
3133
 * @param fp File handle to write to.
3134
 */
3135
3136
void CPLDumpSharedList(FILE *fp)
3137
3138
0
{
3139
0
    if (nSharedFileCount > 0)
3140
0
    {
3141
0
        if (fp == nullptr)
3142
0
            CPLDebug("CPL", "%d Shared files open.", nSharedFileCount);
3143
0
        else
3144
0
            fprintf(fp, "%d Shared files open.", nSharedFileCount);
3145
0
    }
3146
3147
0
    for (int i = 0; i < nSharedFileCount; i++)
3148
0
    {
3149
0
        if (fp == nullptr)
3150
0
            CPLDebug("CPL", "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
3151
0
                     pasSharedFileList[i].bLarge,
3152
0
                     pasSharedFileList[i].pszAccess,
3153
0
                     pasSharedFileList[i].pszFilename);
3154
0
        else
3155
0
            fprintf(fp, "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
3156
0
                    pasSharedFileList[i].bLarge, pasSharedFileList[i].pszAccess,
3157
0
                    pasSharedFileList[i].pszFilename);
3158
0
    }
3159
0
}
3160
3161
/************************************************************************/
3162
/*                           CPLUnlinkTree()                            */
3163
/************************************************************************/
3164
3165
/** Recursively unlink a directory.
3166
 *
3167
 * @return 0 on successful completion, -1 if function fails.
3168
 */
3169
3170
int CPLUnlinkTree(const char *pszPath)
3171
3172
0
{
3173
    /* -------------------------------------------------------------------- */
3174
    /*      First, ensure there is such a file.                             */
3175
    /* -------------------------------------------------------------------- */
3176
0
    VSIStatBufL sStatBuf;
3177
3178
0
    if (VSIStatL(pszPath, &sStatBuf) != 0)
3179
0
    {
3180
0
        CPLError(CE_Failure, CPLE_AppDefined,
3181
0
                 "It seems no file system object called '%s' exists.", pszPath);
3182
3183
0
        return -1;
3184
0
    }
3185
3186
    /* -------------------------------------------------------------------- */
3187
    /*      If it is a simple file, just delete it.                         */
3188
    /* -------------------------------------------------------------------- */
3189
0
    if (VSI_ISREG(sStatBuf.st_mode))
3190
0
    {
3191
0
        if (VSIUnlink(pszPath) != 0)
3192
0
        {
3193
0
            CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
3194
0
                     pszPath);
3195
3196
0
            return -1;
3197
0
        }
3198
3199
0
        return 0;
3200
0
    }
3201
3202
    /* -------------------------------------------------------------------- */
3203
    /*      If it is a directory recurse then unlink the directory.         */
3204
    /* -------------------------------------------------------------------- */
3205
0
    else if (VSI_ISDIR(sStatBuf.st_mode))
3206
0
    {
3207
0
        char **papszItems = VSIReadDir(pszPath);
3208
3209
0
        for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
3210
0
        {
3211
0
            if (papszItems[i][0] == '\0' || EQUAL(papszItems[i], ".") ||
3212
0
                EQUAL(papszItems[i], ".."))
3213
0
                continue;
3214
3215
0
            const std::string osSubPath =
3216
0
                CPLFormFilenameSafe(pszPath, papszItems[i], nullptr);
3217
3218
0
            const int nErr = CPLUnlinkTree(osSubPath.c_str());
3219
3220
0
            if (nErr != 0)
3221
0
            {
3222
0
                CSLDestroy(papszItems);
3223
0
                return nErr;
3224
0
            }
3225
0
        }
3226
3227
0
        CSLDestroy(papszItems);
3228
3229
0
        if (VSIRmdir(pszPath) != 0)
3230
0
        {
3231
0
            CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
3232
0
                     pszPath);
3233
3234
0
            return -1;
3235
0
        }
3236
3237
0
        return 0;
3238
0
    }
3239
3240
    /* -------------------------------------------------------------------- */
3241
    /*      otherwise report an error.                                      */
3242
    /* -------------------------------------------------------------------- */
3243
0
    CPLError(CE_Failure, CPLE_AppDefined,
3244
0
             "Failed to unlink %s.\nUnrecognised filesystem object.", pszPath);
3245
0
    return 1000;
3246
0
}
3247
3248
/************************************************************************/
3249
/*                            CPLCopyFile()                             */
3250
/************************************************************************/
3251
3252
/** Copy a file */
3253
int CPLCopyFile(const char *pszNewPath, const char *pszOldPath)
3254
3255
0
{
3256
0
    return VSICopyFile(pszOldPath, pszNewPath, nullptr,
3257
0
                       static_cast<vsi_l_offset>(-1), nullptr, nullptr,
3258
0
                       nullptr);
3259
0
}
3260
3261
/************************************************************************/
3262
/*                            CPLCopyTree()                             */
3263
/************************************************************************/
3264
3265
/** Recursively copy a tree */
3266
int CPLCopyTree(const char *pszNewPath, const char *pszOldPath)
3267
3268
0
{
3269
0
    VSIStatBufL sStatBuf;
3270
0
    if (VSIStatL(pszNewPath, &sStatBuf) == 0)
3271
0
    {
3272
0
        CPLError(
3273
0
            CE_Failure, CPLE_AppDefined,
3274
0
            "It seems that a file system object called '%s' already exists.",
3275
0
            pszNewPath);
3276
3277
0
        return -1;
3278
0
    }
3279
3280
0
    if (VSIStatL(pszOldPath, &sStatBuf) != 0)
3281
0
    {
3282
0
        CPLError(CE_Failure, CPLE_AppDefined,
3283
0
                 "It seems no file system object called '%s' exists.",
3284
0
                 pszOldPath);
3285
3286
0
        return -1;
3287
0
    }
3288
3289
0
    if (VSI_ISDIR(sStatBuf.st_mode))
3290
0
    {
3291
0
        if (VSIMkdir(pszNewPath, 0755) != 0)
3292
0
        {
3293
0
            CPLError(CE_Failure, CPLE_AppDefined,
3294
0
                     "Cannot create directory '%s'.", pszNewPath);
3295
3296
0
            return -1;
3297
0
        }
3298
3299
0
        char **papszItems = VSIReadDir(pszOldPath);
3300
3301
0
        for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
3302
0
        {
3303
0
            if (EQUAL(papszItems[i], ".") || EQUAL(papszItems[i], ".."))
3304
0
                continue;
3305
3306
0
            const std::string osNewSubPath =
3307
0
                CPLFormFilenameSafe(pszNewPath, papszItems[i], nullptr);
3308
0
            const std::string osOldSubPath =
3309
0
                CPLFormFilenameSafe(pszOldPath, papszItems[i], nullptr);
3310
3311
0
            const int nErr =
3312
0
                CPLCopyTree(osNewSubPath.c_str(), osOldSubPath.c_str());
3313
3314
0
            if (nErr != 0)
3315
0
            {
3316
0
                CSLDestroy(papszItems);
3317
0
                return nErr;
3318
0
            }
3319
0
        }
3320
0
        CSLDestroy(papszItems);
3321
3322
0
        return 0;
3323
0
    }
3324
0
    else if (VSI_ISREG(sStatBuf.st_mode))
3325
0
    {
3326
0
        return CPLCopyFile(pszNewPath, pszOldPath);
3327
0
    }
3328
0
    else
3329
0
    {
3330
0
        CPLError(CE_Failure, CPLE_AppDefined,
3331
0
                 "Unrecognized filesystem object : '%s'.", pszOldPath);
3332
0
        return -1;
3333
0
    }
3334
0
}
3335
3336
/************************************************************************/
3337
/*                            CPLMoveFile()                             */
3338
/************************************************************************/
3339
3340
/** Move a file */
3341
int CPLMoveFile(const char *pszNewPath, const char *pszOldPath)
3342
3343
0
{
3344
0
    if (VSIRename(pszOldPath, pszNewPath) == 0)
3345
0
        return 0;
3346
3347
0
    const int nRet = CPLCopyFile(pszNewPath, pszOldPath);
3348
3349
0
    if (nRet == 0)
3350
0
    {
3351
0
        if (VSIUnlink(pszOldPath) != 0)
3352
0
        {
3353
0
            CPLError(CE_Warning, CPLE_AppDefined, "Cannot delete '%s'",
3354
0
                     pszOldPath);
3355
0
        }
3356
0
    }
3357
0
    return nRet;
3358
0
}
3359
3360
/************************************************************************/
3361
/*                             CPLSymlink()                             */
3362
/************************************************************************/
3363
3364
/** Create a symbolic link */
3365
#ifdef _WIN32
3366
int CPLSymlink(const char *, const char *, CSLConstList)
3367
{
3368
    return -1;
3369
}
3370
#else
3371
int CPLSymlink(const char *pszOldPath, const char *pszNewPath,
3372
               CSLConstList /* papszOptions */)
3373
0
{
3374
0
    return symlink(pszOldPath, pszNewPath);
3375
0
}
3376
#endif
3377
3378
/************************************************************************/
3379
/* ==================================================================== */
3380
/*                              CPLLocaleC                              */
3381
/* ==================================================================== */
3382
/************************************************************************/
3383
3384
//! @cond Doxygen_Suppress
3385
/************************************************************************/
3386
/*                             CPLLocaleC()                             */
3387
/************************************************************************/
3388
3389
0
CPLLocaleC::CPLLocaleC() : pszOldLocale(nullptr)
3390
0
{
3391
0
    if (CPLTestBool(CPLGetConfigOption("GDAL_DISABLE_CPLLOCALEC", "NO")))
3392
0
        return;
3393
3394
0
    pszOldLocale = CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr));
3395
0
    if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
3396
0
        CPLsetlocale(LC_NUMERIC, "C") == nullptr)
3397
0
    {
3398
0
        CPLFree(pszOldLocale);
3399
0
        pszOldLocale = nullptr;
3400
0
    }
3401
0
}
3402
3403
/************************************************************************/
3404
/*                            ~CPLLocaleC()                             */
3405
/************************************************************************/
3406
3407
CPLLocaleC::~CPLLocaleC()
3408
3409
0
{
3410
0
    if (pszOldLocale == nullptr)
3411
0
        return;
3412
3413
0
    CPLsetlocale(LC_NUMERIC, pszOldLocale);
3414
0
    CPLFree(pszOldLocale);
3415
0
}
3416
3417
/************************************************************************/
3418
/*                       CPLThreadLocaleCPrivate                        */
3419
/************************************************************************/
3420
3421
#ifdef HAVE_USELOCALE
3422
3423
class CPLThreadLocaleCPrivate
3424
{
3425
    locale_t nNewLocale;
3426
    locale_t nOldLocale;
3427
3428
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3429
3430
  public:
3431
    CPLThreadLocaleCPrivate();
3432
    ~CPLThreadLocaleCPrivate();
3433
};
3434
3435
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3436
0
    : nNewLocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)),
3437
0
      nOldLocale(uselocale(nNewLocale))
3438
0
{
3439
0
}
3440
3441
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3442
0
{
3443
0
    uselocale(nOldLocale);
3444
0
    freelocale(nNewLocale);
3445
0
}
3446
3447
#elif defined(_MSC_VER)
3448
3449
class CPLThreadLocaleCPrivate
3450
{
3451
    int nOldValConfigThreadLocale;
3452
    char *pszOldLocale;
3453
3454
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3455
3456
  public:
3457
    CPLThreadLocaleCPrivate();
3458
    ~CPLThreadLocaleCPrivate();
3459
};
3460
3461
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3462
{
3463
    nOldValConfigThreadLocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
3464
    pszOldLocale = setlocale(LC_NUMERIC, "C");
3465
    if (pszOldLocale)
3466
        pszOldLocale = CPLStrdup(pszOldLocale);
3467
}
3468
3469
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3470
{
3471
    if (pszOldLocale != nullptr)
3472
    {
3473
        setlocale(LC_NUMERIC, pszOldLocale);
3474
        CPLFree(pszOldLocale);
3475
    }
3476
    _configthreadlocale(nOldValConfigThreadLocale);
3477
}
3478
3479
#else
3480
3481
class CPLThreadLocaleCPrivate
3482
{
3483
    char *pszOldLocale;
3484
3485
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3486
3487
  public:
3488
    CPLThreadLocaleCPrivate();
3489
    ~CPLThreadLocaleCPrivate();
3490
};
3491
3492
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3493
    : pszOldLocale(CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr)))
3494
{
3495
    if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
3496
        CPLsetlocale(LC_NUMERIC, "C") == nullptr)
3497
    {
3498
        CPLFree(pszOldLocale);
3499
        pszOldLocale = nullptr;
3500
    }
3501
}
3502
3503
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3504
{
3505
    if (pszOldLocale != nullptr)
3506
    {
3507
        CPLsetlocale(LC_NUMERIC, pszOldLocale);
3508
        CPLFree(pszOldLocale);
3509
    }
3510
}
3511
3512
#endif
3513
3514
/************************************************************************/
3515
/*                          CPLThreadLocaleC()                          */
3516
/************************************************************************/
3517
3518
0
CPLThreadLocaleC::CPLThreadLocaleC() : m_private(new CPLThreadLocaleCPrivate)
3519
0
{
3520
0
}
3521
3522
/************************************************************************/
3523
/*                         ~CPLThreadLocaleC()                          */
3524
/************************************************************************/
3525
3526
CPLThreadLocaleC::~CPLThreadLocaleC()
3527
3528
0
{
3529
0
    delete m_private;
3530
0
}
3531
3532
//! @endcond
3533
3534
/************************************************************************/
3535
/*                            CPLsetlocale()                            */
3536
/************************************************************************/
3537
3538
/**
3539
 * Prevents parallel executions of setlocale().
3540
 *
3541
 * Calling setlocale() concurrently from two or more threads is a
3542
 * potential data race. A mutex is used to provide a critical region so
3543
 * that only one thread at a time can be executing setlocale().
3544
 *
3545
 * The return should not be freed, and copied quickly as it may be invalidated
3546
 * by a following next call to CPLsetlocale().
3547
 *
3548
 * @param category See your compiler's documentation on setlocale.
3549
 * @param locale See your compiler's documentation on setlocale.
3550
 *
3551
 * @return See your compiler's documentation on setlocale.
3552
 */
3553
char *CPLsetlocale(int category, const char *locale)
3554
0
{
3555
0
    CPLMutexHolder oHolder(&hSetLocaleMutex);
3556
0
    char *pszRet = setlocale(category, locale);
3557
0
    if (pszRet == nullptr)
3558
0
        return pszRet;
3559
3560
    // Make it thread-locale storage.
3561
0
    return const_cast<char *>(CPLSPrintf("%s", pszRet));
3562
0
}
3563
3564
/************************************************************************/
3565
/*                      CPLCleanupSetlocaleMutex()                      */
3566
/************************************************************************/
3567
3568
void CPLCleanupSetlocaleMutex(void)
3569
0
{
3570
0
    if (hSetLocaleMutex != nullptr)
3571
0
        CPLDestroyMutex(hSetLocaleMutex);
3572
0
    hSetLocaleMutex = nullptr;
3573
0
}
3574
3575
/************************************************************************/
3576
/*                            IsPowerOfTwo()                            */
3577
/************************************************************************/
3578
3579
int CPLIsPowerOfTwo(unsigned int i)
3580
0
{
3581
0
    if (i == 0)
3582
0
        return FALSE;
3583
0
    return (i & (i - 1)) == 0 ? TRUE : FALSE;
3584
0
}
3585
3586
/************************************************************************/
3587
/*                          CPLCheckForFile()                           */
3588
/************************************************************************/
3589
3590
/**
3591
 * Check for file existence.
3592
 *
3593
 * The function checks if a named file exists in the filesystem, hopefully
3594
 * in an efficient fashion if a sibling file list is available.   It exists
3595
 * primarily to do faster file checking for functions like GDAL open methods
3596
 * that get a list of files from the target directory.
3597
 *
3598
 * If the sibling file list exists (is not NULL) it is assumed to be a list
3599
 * of files in the same directory as the target file, and it will be checked
3600
 * (case insensitively) for a match.  If a match is found, pszFilename is
3601
 * updated with the correct case and TRUE is returned.
3602
 *
3603
 * If papszSiblingFiles is NULL, a VSIStatL() is used to test for the files
3604
 * existence, and no case insensitive testing is done.
3605
 *
3606
 * @param pszFilename name of file to check for - filename case updated in
3607
 * some cases.
3608
 * @param papszSiblingFiles a list of files in the same directory as
3609
 * pszFilename if available, or NULL. This list should have no path components.
3610
 *
3611
 * @return TRUE if a match is found, or FALSE if not.
3612
 */
3613
3614
int CPLCheckForFile(char *pszFilename, CSLConstList papszSiblingFiles)
3615
3616
0
{
3617
    /* -------------------------------------------------------------------- */
3618
    /*      Fallback case if we don't have a sibling file list.             */
3619
    /* -------------------------------------------------------------------- */
3620
0
    if (papszSiblingFiles == nullptr)
3621
0
    {
3622
0
        VSIStatBufL sStatBuf;
3623
3624
0
        return VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
3625
0
    }
3626
3627
    /* -------------------------------------------------------------------- */
3628
    /*      We have sibling files, compare the non-path filename portion    */
3629
    /*      of pszFilename too all entries.                                 */
3630
    /* -------------------------------------------------------------------- */
3631
0
    const CPLString osFileOnly = CPLGetFilename(pszFilename);
3632
3633
0
    for (int i = 0; papszSiblingFiles[i] != nullptr; i++)
3634
0
    {
3635
0
        if (EQUAL(papszSiblingFiles[i], osFileOnly))
3636
0
        {
3637
0
            strcpy(pszFilename + strlen(pszFilename) - osFileOnly.size(),
3638
0
                   papszSiblingFiles[i]);
3639
0
            return TRUE;
3640
0
        }
3641
0
    }
3642
3643
0
    return FALSE;
3644
0
}
3645
3646
/************************************************************************/
3647
/*      Stub implementation of zip services if we don't have libz.      */
3648
/************************************************************************/
3649
3650
#if !defined(HAVE_LIBZ)
3651
3652
void *CPLCreateZip(const char *, char **)
3653
3654
{
3655
    CPLError(CE_Failure, CPLE_NotSupported,
3656
             "This GDAL/OGR build does not include zlib and zip services.");
3657
    return nullptr;
3658
}
3659
3660
CPLErr CPLCreateFileInZip(void *, const char *, char **)
3661
{
3662
    return CE_Failure;
3663
}
3664
3665
CPLErr CPLWriteFileInZip(void *, const void *, int)
3666
{
3667
    return CE_Failure;
3668
}
3669
3670
CPLErr CPLCloseFileInZip(void *)
3671
{
3672
    return CE_Failure;
3673
}
3674
3675
CPLErr CPLCloseZip(void *)
3676
{
3677
    return CE_Failure;
3678
}
3679
3680
void *CPLZLibDeflate(const void *, size_t, int, void *, size_t,
3681
                     size_t *pnOutBytes)
3682
{
3683
    if (pnOutBytes != nullptr)
3684
        *pnOutBytes = 0;
3685
    return nullptr;
3686
}
3687
3688
void *CPLZLibInflate(const void *, size_t, void *, size_t, size_t *pnOutBytes)
3689
{
3690
    if (pnOutBytes != nullptr)
3691
        *pnOutBytes = 0;
3692
    return nullptr;
3693
}
3694
3695
#endif /* !defined(HAVE_LIBZ) */
3696
3697
/************************************************************************/
3698
/* ==================================================================== */
3699
/*                          CPLConfigOptionSetter                       */
3700
/* ==================================================================== */
3701
/************************************************************************/
3702
3703
//! @cond Doxygen_Suppress
3704
/************************************************************************/
3705
/*                       CPLConfigOptionSetter()                        */
3706
/************************************************************************/
3707
3708
CPLConfigOptionSetter::CPLConfigOptionSetter(const char *pszKey,
3709
                                             const char *pszValue,
3710
                                             bool bSetOnlyIfUndefined)
3711
0
    : m_pszKey(CPLStrdup(pszKey)), m_pszOldValue(nullptr),
3712
0
      m_bRestoreOldValue(false)
3713
0
{
3714
0
    const char *pszOldValue = CPLGetThreadLocalConfigOption(pszKey, nullptr);
3715
0
    if ((bSetOnlyIfUndefined &&
3716
0
         CPLGetConfigOption(pszKey, nullptr) == nullptr) ||
3717
0
        !bSetOnlyIfUndefined)
3718
0
    {
3719
0
        m_bRestoreOldValue = true;
3720
0
        if (pszOldValue)
3721
0
            m_pszOldValue = CPLStrdup(pszOldValue);
3722
0
        CPLSetThreadLocalConfigOption(pszKey,
3723
0
                                      pszValue ? pszValue : CPL_NULL_VALUE);
3724
0
    }
3725
0
}
3726
3727
/************************************************************************/
3728
/*                       ~CPLConfigOptionSetter()                       */
3729
/************************************************************************/
3730
3731
CPLConfigOptionSetter::~CPLConfigOptionSetter()
3732
0
{
3733
0
    if (m_bRestoreOldValue)
3734
0
    {
3735
0
        CPLSetThreadLocalConfigOption(m_pszKey, m_pszOldValue);
3736
0
        CPLFree(m_pszOldValue);
3737
0
    }
3738
0
    CPLFree(m_pszKey);
3739
0
}
3740
3741
//! @endcond
3742
3743
/************************************************************************/
3744
/*                          CPLIsInteractive()                          */
3745
/************************************************************************/
3746
3747
/** Returns whether the provided file refers to a terminal.
3748
 *
3749
 * This function is a wrapper of the ``isatty()`` POSIX function.
3750
 *
3751
 * @param f File to test. Typically stdin, stdout or stderr
3752
 * @return true if it is an open file referring to a terminal.
3753
 * @since GDAL 3.11
3754
 */
3755
bool CPLIsInteractive(FILE *f)
3756
770
{
3757
770
#ifndef _WIN32
3758
770
    return isatty(static_cast<int>(fileno(f)));
3759
#else
3760
    return _isatty(_fileno(f));
3761
#endif
3762
770
}
3763
3764
/************************************************************************/
3765
/*                          CPLLockFileStruct                           */
3766
/************************************************************************/
3767
3768
//! @cond Doxygen_Suppress
3769
struct CPLLockFileStruct
3770
{
3771
    std::string osLockFilename{};
3772
    std::atomic<bool> bStop = false;
3773
    CPLJoinableThread *hThread = nullptr;
3774
};
3775
3776
//! @endcond
3777
3778
/************************************************************************/
3779
/*                           CPLLockFileEx()                            */
3780
/************************************************************************/
3781
3782
/** Create and acquire a lock file.
3783
 *
3784
 * Only one caller can acquire the lock file at a time. The O_CREAT|O_EXCL
3785
 * flags of open() are used for that purpose (there might be limitations for
3786
 * network file systems).
3787
 *
3788
 * The lock file is continuously touched by a thread started by this function,
3789
 * to indicate it is still alive. If an existing lock file is found that has
3790
 * not been recently refreshed it will be considered stalled, and will be
3791
 * deleted before attempting to recreate it.
3792
 *
3793
 * This function must be paired with CPLUnlockFileEx().
3794
 *
3795
 * Available options are:
3796
 * <ul>
3797
 * <li>WAIT_TIME=value_in_sec/inf: Maximum amount of time in second that this
3798
 *     function can spend waiting for the lock. If not set, default to infinity.
3799
 * </li>
3800
 * <li>STALLED_DELAY=value_in_sec: Delay in second to consider that an existing
3801
 * lock file that has not been touched since STALLED_DELAY is stalled, and can
3802
 * be re-acquired. Defaults to 10 seconds.
3803
 * </li>
3804
 * <li>VERBOSE_WAIT_MESSAGE=YES/NO: Whether to emit a CE_Warning message while
3805
 * waiting for a busy lock. Default to NO.
3806
 * </li>
3807
 * </ul>
3808
3809
 * @param pszLockFileName Lock file name. The directory must already exist.
3810
 *                        Must not be NULL.
3811
 * @param[out] phLockFileHandle Pointer to at location where to store the lock
3812
 *                              handle that must be passed to CPLUnlockFileEx().
3813
 *                              *phLockFileHandle will be null if the return
3814
 *                              code of that function is not CLFS_OK.
3815
 * @param papszOptions NULL terminated list of strings, or NULL.
3816
 *
3817
 * @return lock file status.
3818
 *
3819
 * @since 3.11
3820
 */
3821
CPLLockFileStatus CPLLockFileEx(const char *pszLockFileName,
3822
                                CPLLockFileHandle *phLockFileHandle,
3823
                                CSLConstList papszOptions)
3824
0
{
3825
0
    if (!pszLockFileName || !phLockFileHandle)
3826
0
        return CLFS_API_MISUSE;
3827
3828
0
    *phLockFileHandle = nullptr;
3829
3830
0
    const double dfWaitTime =
3831
0
        CPLAtof(CSLFetchNameValueDef(papszOptions, "WAIT_TIME", "inf"));
3832
0
    const double dfStalledDelay =
3833
0
        CPLAtof(CSLFetchNameValueDef(papszOptions, "STALLED_DELAY", "10"));
3834
0
    const bool bVerboseWait =
3835
0
        CPLFetchBool(papszOptions, "VERBOSE_WAIT_MESSAGE", false);
3836
3837
0
    for (int i = 0; i < 2; ++i)
3838
0
    {
3839
#ifdef _WIN32
3840
        wchar_t *pwszFilename =
3841
            CPLRecodeToWChar(pszLockFileName, CPL_ENC_UTF8, CPL_ENC_UCS2);
3842
        int fd = _wopen(pwszFilename, _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);
3843
        CPLFree(pwszFilename);
3844
#else
3845
0
        int fd = open(pszLockFileName, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
3846
0
#endif
3847
0
        if (fd == -1)
3848
0
        {
3849
0
            if (errno != EEXIST || i == 1)
3850
0
            {
3851
0
                return CLFS_CANNOT_CREATE_LOCK;
3852
0
            }
3853
0
            else
3854
0
            {
3855
                // Wait for the .lock file to have been removed or
3856
                // not refreshed since dfStalledDelay seconds.
3857
0
                double dfCurWaitTime = dfWaitTime;
3858
0
                VSIStatBufL sStat;
3859
0
                while (VSIStatL(pszLockFileName, &sStat) == 0 &&
3860
0
                       static_cast<double>(sStat.st_mtime) + dfStalledDelay >
3861
0
                           static_cast<double>(time(nullptr)))
3862
0
                {
3863
0
                    if (dfCurWaitTime <= 1e-5)
3864
0
                        return CLFS_LOCK_BUSY;
3865
3866
0
                    if (bVerboseWait)
3867
0
                    {
3868
0
                        CPLError(CE_Warning, CPLE_AppDefined,
3869
0
                                 "Waiting for %s to be freed...",
3870
0
                                 pszLockFileName);
3871
0
                    }
3872
0
                    else
3873
0
                    {
3874
0
                        CPLDebug("CPL", "Waiting for %s to be freed...",
3875
0
                                 pszLockFileName);
3876
0
                    }
3877
3878
0
                    const double dfPauseDelay = std::min(0.5, dfWaitTime);
3879
0
                    CPLSleep(dfPauseDelay);
3880
0
                    dfCurWaitTime -= dfPauseDelay;
3881
0
                }
3882
3883
0
                if (VSIUnlink(pszLockFileName) != 0)
3884
0
                {
3885
0
                    return CLFS_CANNOT_CREATE_LOCK;
3886
0
                }
3887
0
            }
3888
0
        }
3889
0
        else
3890
0
        {
3891
0
            close(fd);
3892
0
            break;
3893
0
        }
3894
0
    }
3895
3896
    // Touch regularly the lock file to show it is still alive
3897
0
    struct KeepAliveLockFile
3898
0
    {
3899
0
        static void func(void *user_data)
3900
0
        {
3901
0
            CPLLockFileHandle hLockFileHandle =
3902
0
                static_cast<CPLLockFileHandle>(user_data);
3903
0
            while (!hLockFileHandle->bStop)
3904
0
            {
3905
0
                auto f = VSIVirtualHandleUniquePtr(
3906
0
                    VSIFOpenL(hLockFileHandle->osLockFilename.c_str(), "wb"));
3907
0
                if (f)
3908
0
                {
3909
0
                    f.reset();
3910
0
                }
3911
0
                constexpr double REFRESH_DELAY = 0.5;
3912
0
                CPLSleep(REFRESH_DELAY);
3913
0
            }
3914
0
        }
3915
0
    };
3916
3917
0
    *phLockFileHandle = new CPLLockFileStruct();
3918
0
    (*phLockFileHandle)->osLockFilename = pszLockFileName;
3919
3920
0
    (*phLockFileHandle)->hThread =
3921
0
        CPLCreateJoinableThread(KeepAliveLockFile::func, *phLockFileHandle);
3922
0
    if ((*phLockFileHandle)->hThread == nullptr)
3923
0
    {
3924
0
        VSIUnlink(pszLockFileName);
3925
0
        delete *phLockFileHandle;
3926
0
        *phLockFileHandle = nullptr;
3927
0
        return CLFS_THREAD_CREATION_FAILED;
3928
0
    }
3929
3930
0
    return CLFS_OK;
3931
0
}
3932
3933
/************************************************************************/
3934
/*                          CPLUnlockFileEx()                           */
3935
/************************************************************************/
3936
3937
/** Release and delete a lock file.
3938
 *
3939
 * This function must be paired with CPLLockFileEx().
3940
 *
3941
 * @param hLockFileHandle Lock handle (value of *phLockFileHandle argument
3942
 *                        set by CPLLockFileEx()), or NULL.
3943
 *
3944
 * @since 3.11
3945
 */
3946
void CPLUnlockFileEx(CPLLockFileHandle hLockFileHandle)
3947
0
{
3948
0
    if (hLockFileHandle)
3949
0
    {
3950
        // Remove .lock file
3951
0
        hLockFileHandle->bStop = true;
3952
0
        CPLJoinThread(hLockFileHandle->hThread);
3953
0
        VSIUnlink(hLockFileHandle->osLockFilename.c_str());
3954
3955
0
        delete hLockFileHandle;
3956
0
    }
3957
0
}
3958
3959
/************************************************************************/
3960
/*                     CPLFormatReadableFileSize()                      */
3961
/************************************************************************/
3962
3963
template <class T>
3964
static std::string CPLFormatReadableFileSizeInternal(T nSizeInBytes)
3965
0
{
3966
0
    constexpr T ONE_MEGA_BYTE = 1000 * 1000;
3967
0
    constexpr T ONE_GIGA_BYTE = 1000 * ONE_MEGA_BYTE;
3968
0
    constexpr T ONE_TERA_BYTE = 1000 * ONE_GIGA_BYTE;
3969
0
    constexpr T ONE_PETA_BYTE = 1000 * ONE_TERA_BYTE;
3970
0
    constexpr T ONE_HEXA_BYTE = 1000 * ONE_PETA_BYTE;
3971
3972
0
    if (nSizeInBytes > ONE_HEXA_BYTE)
3973
0
        return CPLSPrintf("%.02f HB", static_cast<double>(nSizeInBytes) /
3974
0
                                          static_cast<double>(ONE_HEXA_BYTE));
3975
3976
0
    if (nSizeInBytes > ONE_PETA_BYTE)
3977
0
        return CPLSPrintf("%.02f PB", static_cast<double>(nSizeInBytes) /
3978
0
                                          static_cast<double>(ONE_PETA_BYTE));
3979
3980
0
    if (nSizeInBytes > ONE_TERA_BYTE)
3981
0
        return CPLSPrintf("%.02f TB", static_cast<double>(nSizeInBytes) /
3982
0
                                          static_cast<double>(ONE_TERA_BYTE));
3983
3984
0
    if (nSizeInBytes > ONE_GIGA_BYTE)
3985
0
        return CPLSPrintf("%.02f GB", static_cast<double>(nSizeInBytes) /
3986
0
                                          static_cast<double>(ONE_GIGA_BYTE));
3987
3988
0
    if (nSizeInBytes > ONE_MEGA_BYTE)
3989
0
        return CPLSPrintf("%.02f MB", static_cast<double>(nSizeInBytes) /
3990
0
                                          static_cast<double>(ONE_MEGA_BYTE));
3991
3992
0
    return CPLSPrintf("%03d,%03d bytes", static_cast<int>(nSizeInBytes) / 1000,
3993
0
                      static_cast<int>(nSizeInBytes) % 1000);
3994
0
}
Unexecuted instantiation: cpl_conv.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > CPLFormatReadableFileSizeInternal<unsigned long>(unsigned long)
Unexecuted instantiation: cpl_conv.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > CPLFormatReadableFileSizeInternal<double>(double)
3995
3996
/** Return a file size in a human readable way.
3997
 *
3998
 * e.g 1200000 -> "1.20 MB"
3999
 *
4000
 * @since 3.12
4001
 */
4002
std::string CPLFormatReadableFileSize(uint64_t nSizeInBytes)
4003
0
{
4004
0
    return CPLFormatReadableFileSizeInternal(nSizeInBytes);
4005
0
}
4006
4007
/** Return a file size in a human readable way.
4008
 *
4009
 * e.g 1200000 -> "1.20 MB"
4010
 *
4011
 * @since 3.12
4012
 */
4013
std::string CPLFormatReadableFileSize(double dfSizeInBytes)
4014
0
{
4015
0
    return CPLFormatReadableFileSizeInternal(dfSizeInBytes);
4016
0
}
4017
4018
/************************************************************************/
4019
/*                 CPLGetRemainingFileDescriptorCount()                 */
4020
/************************************************************************/
4021
4022
/** \fn CPLGetRemainingFileDescriptorCount()
4023
 *
4024
 * Return the number of file descriptors that can still be opened by the
4025
 * current process.
4026
 *
4027
 * Only implemented on non-Windows operating systems
4028
 *
4029
 * Return a negative value in case of error or not implemented.
4030
 *
4031
 * @since 3.12
4032
 */
4033
4034
#if defined(__FreeBSD__)
4035
4036
int CPLGetRemainingFileDescriptorCount()
4037
{
4038
    struct rlimit limitNumberOfFilesPerProcess;
4039
    if (getrlimit(RLIMIT_NOFILE, &limitNumberOfFilesPerProcess) != 0)
4040
    {
4041
        return -1;
4042
    }
4043
    const int maxNumberOfFilesPerProcess =
4044
        static_cast<int>(limitNumberOfFilesPerProcess.rlim_cur);
4045
4046
    const pid_t pid = getpid();
4047
    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_FILEDESC,
4048
                  static_cast<int>(pid)};
4049
4050
    size_t len = 0;
4051
4052
    if (sysctl(mib, 4, nullptr, &len, nullptr, 0) == -1)
4053
    {
4054
        return -1;
4055
    }
4056
4057
    return maxNumberOfFilesPerProcess -
4058
           static_cast<int>(len / sizeof(struct kinfo_file));
4059
}
4060
4061
#else
4062
4063
int CPLGetRemainingFileDescriptorCount()
4064
0
{
4065
0
#if !defined(_WIN32) && HAVE_GETRLIMIT
4066
0
    struct rlimit limitNumberOfFilesPerProcess;
4067
0
    if (getrlimit(RLIMIT_NOFILE, &limitNumberOfFilesPerProcess) != 0)
4068
0
    {
4069
0
        return -1;
4070
0
    }
4071
0
    const int maxNumberOfFilesPerProcess =
4072
0
        static_cast<int>(limitNumberOfFilesPerProcess.rlim_cur);
4073
4074
0
    int countFilesInUse = 0;
4075
0
    {
4076
0
        const char *const apszOptions[] = {"NAME_AND_TYPE_ONLY=YES", nullptr};
4077
0
#ifdef __linux
4078
0
        VSIDIR *dir = VSIOpenDir("/proc/self/fd", 0, apszOptions);
4079
#else
4080
        // MacOSX
4081
        VSIDIR *dir = VSIOpenDir("/dev/fd", 0, apszOptions);
4082
#endif
4083
0
        if (dir)
4084
0
        {
4085
0
            while (VSIGetNextDirEntry(dir))
4086
0
                ++countFilesInUse;
4087
0
            countFilesInUse -= 2;  // do not count . and ..
4088
0
            VSICloseDir(dir);
4089
0
        }
4090
0
    }
4091
4092
0
    if (countFilesInUse <= 0)
4093
0
    {
4094
        // Fallback if above method does not work
4095
0
        for (int fd = 0; fd < maxNumberOfFilesPerProcess; fd++)
4096
0
        {
4097
0
            errno = 0;
4098
0
            if (fcntl(fd, F_GETFD) != -1 || errno != EBADF)
4099
0
            {
4100
0
                countFilesInUse++;
4101
0
            }
4102
0
        }
4103
0
    }
4104
4105
0
    return maxNumberOfFilesPerProcess - countFilesInUse;
4106
#else
4107
    return -1;
4108
#endif
4109
0
}
4110
4111
#endif