Coverage Report

Created: 2025-11-16 06:25

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