Coverage Report

Created: 2025-06-13 06:29

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