Coverage Report

Created: 2026-09-14 06:50

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