• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.42
/port/cpl_conv.cpp
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
// and OpenBSD with libcxx 19.1.7 requires 800 for vasprintf
19
// (cf https://github.com/OSGeo/gdal/issues/12619)
20
// (not sure if the following is still up to date...) but on Solaris, we don't
21
// have uselocale and we cannot have std=c++11 with _XOPEN_SOURCE != 600
22
#if defined(__sun__) && __cplusplus >= 201103L
23
#if _XOPEN_SOURCE != 600
24
#ifdef _XOPEN_SOURCE
25
#undef _XOPEN_SOURCE
26
#endif
27
#define _XOPEN_SOURCE 600
28
#endif
29
#else
30
#ifdef _XOPEN_SOURCE
31
#undef _XOPEN_SOURCE
32
#endif
33
#define _XOPEN_SOURCE 800
34
#endif
35
#endif
36

37
// For atoll (at least for NetBSD)
38
#ifndef _ISOC99_SOURCE
39
#define _ISOC99_SOURCE
40
#endif
41

42
#ifdef MSVC_USE_VLD
43
#include <vld.h>
44
#endif
45

46
#include "cpl_conv.h"
47

48
#include <algorithm>
49
#include <atomic>
50
#include <cctype>
51
#include <cerrno>
52
#include <climits>
53
#include <clocale>
54
#include <cmath>
55
#include <cstdlib>
56
#include <cstring>
57
#include <ctime>
58
#include <mutex>
59
#include <set>
60

61
#if HAVE_UNISTD_H
62
#include <unistd.h>
63
#endif
64
#if HAVE_XLOCALE_H
65
#include <xlocale.h>  // for LC_NUMERIC_MASK on MacOS
66
#endif
67

68
#include <sys/types.h>  // open
69
#include <sys/stat.h>   // open
70
#include <fcntl.h>      // open
71

72
#ifdef _WIN32
73
#include <io.h>  // _isatty, _wopen
74
#else
75
#include <unistd.h>  // isatty
76
#endif
77

78
#include <string>
79

80
#if __cplusplus >= 202002L
81
#include <bit>  // For std::endian
82
#endif
83

84
#include "cpl_config.h"
85
#include "cpl_multiproc.h"
86
#include "cpl_string.h"
87
#include "cpl_vsi.h"
88
#include "cpl_vsil_curl_priv.h"
89
#include "cpl_known_config_options.h"
90

91
#ifdef DEBUG
92
#define OGRAPISPY_ENABLED
93
#endif
94
#ifdef OGRAPISPY_ENABLED
95
// Keep in sync with ograpispy.cpp
96
void OGRAPISPYCPLSetConfigOption(const char *, const char *);
97
void OGRAPISPYCPLSetThreadLocalConfigOption(const char *, const char *);
98
#endif
99

100
// Uncomment to get list of options that have been fetched and set.
101
// #define DEBUG_CONFIG_OPTIONS
102

103
static CPLMutex *hConfigMutex = nullptr;
104
static volatile char **g_papszConfigOptions = nullptr;
105
static bool gbIgnoreEnvVariables =
106
    false;  // if true, only take into account configuration options set through
107
            // configuration file or
108
            // CPLSetConfigOption()/CPLSetThreadLocalConfigOption()
109

110
static std::vector<std::pair<CPLSetConfigOptionSubscriber, void *>>
111
    gSetConfigOptionSubscribers{};
112

113
// Used by CPLOpenShared() and friends.
114
static CPLMutex *hSharedFileMutex = nullptr;
115
static int nSharedFileCount = 0;
116
static CPLSharedFileInfo *pasSharedFileList = nullptr;
117

118
// Used by CPLsetlocale().
119
static CPLMutex *hSetLocaleMutex = nullptr;
120

121
// Note: ideally this should be added in CPLSharedFileInfo*
122
// but CPLSharedFileInfo is exposed in the API, hence that trick
123
// to hide this detail.
124
typedef struct
125
{
126
    GIntBig nPID;  // pid of opening thread.
127
} CPLSharedFileInfoExtra;
128

129
static volatile CPLSharedFileInfoExtra *pasSharedFileListExtra = nullptr;
130

131
/************************************************************************/
132
/*                             CPLCalloc()                              */
133
/************************************************************************/
134

135
/**
136
 * Safe version of calloc().
137
 *
138
 * This function is like the C library calloc(), but raises a CE_Fatal
139
 * error with CPLError() if it fails to allocate the desired memory.  It
140
 * should be used for small memory allocations that are unlikely to fail
141
 * and for which the application is unwilling to test for out of memory
142
 * conditions.  It uses VSICalloc() to get the memory, so any hooking of
143
 * VSICalloc() will apply to CPLCalloc() as well.  CPLFree() or VSIFree()
144
 * can be used free memory allocated by CPLCalloc().
145
 *
146
 * @param nCount number of objects to allocate.
147
 * @param nSize size (in bytes) of object to allocate.
148
 * @return pointer to newly allocated memory, only NULL if nSize * nCount is
149
 * NULL.
150
 */
151

152
void *CPLCalloc(size_t nCount, size_t nSize)
3,433,910✔
153

154
{
155
    if (nSize * nCount == 0)
3,433,910✔
156
        return nullptr;
9,015✔
157

158
    void *pReturn = CPLMalloc(nCount * nSize);
3,424,890✔
159
    memset(pReturn, 0, nCount * nSize);
3,424,870✔
160
    return pReturn;
3,424,870✔
161
}
162

163
/************************************************************************/
164
/*                             CPLMalloc()                              */
165
/************************************************************************/
166

167
/**
168
 * Safe version of malloc().
169
 *
170
 * This function is like the C library malloc(), but raises a CE_Fatal
171
 * error with CPLError() if it fails to allocate the desired memory.  It
172
 * should be used for small memory allocations that are unlikely to fail
173
 * and for which the application is unwilling to test for out of memory
174
 * conditions.  It uses VSIMalloc() to get the memory, so any hooking of
175
 * VSIMalloc() will apply to CPLMalloc() as well.  CPLFree() or VSIFree()
176
 * can be used free memory allocated by CPLMalloc().
177
 *
178
 * @param nSize size (in bytes) of memory block to allocate.
179
 * @return pointer to newly allocated memory, only NULL if nSize is zero.
180
 */
181

182
void *CPLMalloc(size_t nSize)
19,744,100✔
183

184
{
185
    if (nSize == 0)
19,744,100✔
186
        return nullptr;
5,444✔
187

188
    if ((nSize >> (8 * sizeof(nSize) - 1)) != 0)
19,738,600✔
189
    {
190
        // coverity[dead_error_begin]
191
        CPLError(CE_Failure, CPLE_AppDefined,
×
192
                 "CPLMalloc(%ld): Silly size requested.",
193
                 static_cast<long>(nSize));
194
        return nullptr;
×
195
    }
196

197
    void *pReturn = VSIMalloc(nSize);
19,738,600✔
198
    if (pReturn == nullptr)
19,738,700✔
199
    {
200
        if (nSize < 2000)
×
201
        {
202
            CPLEmergencyError("CPLMalloc(): Out of memory allocating a small "
×
203
                              "number of bytes.");
204
        }
205

206
        CPLError(CE_Fatal, CPLE_OutOfMemory,
×
207
                 "CPLMalloc(): Out of memory allocating %ld bytes.",
208
                 static_cast<long>(nSize));
209
    }
210

211
    return pReturn;
19,738,700✔
212
}
213

214
/************************************************************************/
215
/*                             CPLRealloc()                             */
216
/************************************************************************/
217

218
/**
219
 * Safe version of realloc().
220
 *
221
 * This function is like the C library realloc(), but raises a CE_Fatal
222
 * error with CPLError() if it fails to allocate the desired memory.  It
223
 * should be used for small memory allocations that are unlikely to fail
224
 * and for which the application is unwilling to test for out of memory
225
 * conditions.  It uses VSIRealloc() to get the memory, so any hooking of
226
 * VSIRealloc() will apply to CPLRealloc() as well.  CPLFree() or VSIFree()
227
 * can be used free memory allocated by CPLRealloc().
228
 *
229
 * It is also safe to pass NULL in as the existing memory block for
230
 * CPLRealloc(), in which case it uses VSIMalloc() to allocate a new block.
231
 *
232
 * @param pData existing memory block which should be copied to the new block.
233
 * @param nNewSize new size (in bytes) of memory block to allocate.
234
 * @return pointer to allocated memory, only NULL if nNewSize is zero.
235
 */
236

237
void *CPLRealloc(void *pData, size_t nNewSize)
3,229,190✔
238

239
{
240
    if (nNewSize == 0)
3,229,190✔
241
    {
242
        VSIFree(pData);
45✔
243
        return nullptr;
45✔
244
    }
245

246
    if ((nNewSize >> (8 * sizeof(nNewSize) - 1)) != 0)
3,229,150✔
247
    {
248
        // coverity[dead_error_begin]
249
        CPLError(CE_Failure, CPLE_AppDefined,
×
250
                 "CPLRealloc(%ld): Silly size requested.",
251
                 static_cast<long>(nNewSize));
252
        return nullptr;
×
253
    }
254

255
    void *pReturn = nullptr;
3,229,150✔
256

257
    if (pData == nullptr)
3,229,150✔
258
        pReturn = VSIMalloc(nNewSize);
2,055,720✔
259
    else
260
        pReturn = VSIRealloc(pData, nNewSize);
1,173,420✔
261

262
    if (pReturn == nullptr)
3,214,120✔
263
    {
264
        if (nNewSize < 2000)
×
265
        {
266
            char szSmallMsg[80] = {};
×
267

268
            snprintf(szSmallMsg, sizeof(szSmallMsg),
×
269
                     "CPLRealloc(): Out of memory allocating %ld bytes.",
270
                     static_cast<long>(nNewSize));
271
            CPLEmergencyError(szSmallMsg);
×
272
        }
273
        else
274
        {
275
            CPLError(CE_Fatal, CPLE_OutOfMemory,
×
276
                     "CPLRealloc(): Out of memory allocating %ld bytes.",
277
                     static_cast<long>(nNewSize));
278
        }
279
    }
280

281
    return pReturn;
3,222,740✔
282
}
283

284
/************************************************************************/
285
/*                             CPLStrdup()                              */
286
/************************************************************************/
287

288
/**
289
 * Safe version of strdup() function.
290
 *
291
 * This function is similar to the C library strdup() function, but if
292
 * the memory allocation fails it will issue a CE_Fatal error with
293
 * CPLError() instead of returning NULL. Memory
294
 * allocated with CPLStrdup() can be freed with CPLFree() or VSIFree().
295
 *
296
 * It is also safe to pass a NULL string into CPLStrdup().  CPLStrdup()
297
 * will allocate and return a zero length string (as opposed to a NULL
298
 * string).
299
 *
300
 * @param pszString input string to be duplicated.  May be NULL.
301
 * @return pointer to a newly allocated copy of the string.  Free with
302
 * CPLFree() or VSIFree().
303
 */
304

305
char *CPLStrdup(const char *pszString)
7,817,840✔
306

307
{
308
    if (pszString == nullptr)
7,817,840✔
309
        pszString = "";
1,090,010✔
310

311
    const size_t nLen = strlen(pszString);
7,817,840✔
312
    char *pszReturn = static_cast<char *>(CPLMalloc(nLen + 1));
7,817,840✔
313
    memcpy(pszReturn, pszString, nLen + 1);
7,817,780✔
314
    return (pszReturn);
7,817,780✔
315
}
316

317
/************************************************************************/
318
/*                             CPLStrlwr()                              */
319
/************************************************************************/
320

321
/**
322
 * Convert each characters of the string to lower case.
323
 *
324
 * For example, "ABcdE" will be converted to "abcde".
325
 * Starting with GDAL 3.9, this function is no longer locale dependent.
326
 *
327
 * @param pszString input string to be converted.
328
 * @return pointer to the same string, pszString.
329
 */
330

331
char *CPLStrlwr(char *pszString)
3✔
332

333
{
334
    if (pszString == nullptr)
3✔
335
        return nullptr;
×
336

337
    char *pszTemp = pszString;
3✔
338

339
    while (*pszTemp)
24✔
340
    {
341
        *pszTemp =
21✔
342
            static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszTemp)));
21✔
343
        pszTemp++;
21✔
344
    }
345

346
    return pszString;
3✔
347
}
348

349
/************************************************************************/
350
/*                              CPLFGets()                              */
351
/*                                                                      */
352
/*      Note: LF = \n = ASCII 10                                        */
353
/*            CR = \r = ASCII 13                                        */
354
/************************************************************************/
355

356
// ASCII characters.
357
constexpr char knLF = 10;
358
constexpr char knCR = 13;
359

360
/**
361
 * Reads in at most one less than nBufferSize characters from the fp
362
 * stream and stores them into the buffer pointed to by pszBuffer.
363
 * Reading stops after an EOF or a newline. If a newline is read, it
364
 * is _not_ stored into the buffer. A '\\0' is stored after the last
365
 * character in the buffer. All three types of newline terminators
366
 * recognized by the CPLFGets(): single '\\r' and '\\n' and '\\r\\n'
367
 * combination.
368
 *
369
 * @param pszBuffer pointer to the targeting character buffer.
370
 * @param nBufferSize maximum size of the string to read (not including
371
 * terminating '\\0').
372
 * @param fp file pointer to read from.
373
 * @return pointer to the pszBuffer containing a string read
374
 * from the file or NULL if the error or end of file was encountered.
375
 */
376

377
char *CPLFGets(char *pszBuffer, int nBufferSize, FILE *fp)
×
378

379
{
380
    if (nBufferSize == 0 || pszBuffer == nullptr || fp == nullptr)
×
381
        return nullptr;
×
382

383
    /* -------------------------------------------------------------------- */
384
    /*      Let the OS level call read what it things is one line.  This    */
385
    /*      will include the newline.  On windows, if the file happens      */
386
    /*      to be in text mode, the CRLF will have been converted to        */
387
    /*      just the newline (LF).  If it is in binary mode it may well     */
388
    /*      have both.                                                      */
389
    /* -------------------------------------------------------------------- */
390
    const long nOriginalOffset = VSIFTell(fp);
×
391
    if (VSIFGets(pszBuffer, nBufferSize, fp) == nullptr)
×
392
        return nullptr;
×
393

394
    int nActuallyRead = static_cast<int>(strlen(pszBuffer));
×
395
    if (nActuallyRead == 0)
×
396
        return nullptr;
×
397

398
    /* -------------------------------------------------------------------- */
399
    /*      If we found \r and out buffer is full, it is possible there     */
400
    /*      is also a pending \n.  Check for it.                            */
401
    /* -------------------------------------------------------------------- */
402
    if (nBufferSize == nActuallyRead + 1 &&
×
403
        pszBuffer[nActuallyRead - 1] == knCR)
×
404
    {
405
        const int chCheck = fgetc(fp);
×
406
        if (chCheck != knLF)
×
407
        {
408
            // unget the character.
409
            if (VSIFSeek(fp, nOriginalOffset + nActuallyRead, SEEK_SET) == -1)
×
410
            {
411
                CPLError(CE_Failure, CPLE_FileIO,
×
412
                         "Unable to unget a character");
413
            }
414
        }
415
    }
416

417
    /* -------------------------------------------------------------------- */
418
    /*      Trim off \n, \r or \r\n if it appears at the end.  We don't     */
419
    /*      need to do any "seeking" since we want the newline eaten.       */
420
    /* -------------------------------------------------------------------- */
421
    if (nActuallyRead > 1 && pszBuffer[nActuallyRead - 1] == knLF &&
×
422
        pszBuffer[nActuallyRead - 2] == knCR)
×
423
    {
424
        pszBuffer[nActuallyRead - 2] = '\0';
×
425
    }
426
    else if (pszBuffer[nActuallyRead - 1] == knLF ||
×
427
             pszBuffer[nActuallyRead - 1] == knCR)
×
428
    {
429
        pszBuffer[nActuallyRead - 1] = '\0';
×
430
    }
431

432
    /* -------------------------------------------------------------------- */
433
    /*      Search within the string for a \r (MacOS convention             */
434
    /*      apparently), and if we find it we need to trim the string,      */
435
    /*      and seek back.                                                  */
436
    /* -------------------------------------------------------------------- */
437
    char *pszExtraNewline = strchr(pszBuffer, knCR);
×
438

439
    if (pszExtraNewline != nullptr)
×
440
    {
441
        nActuallyRead = static_cast<int>(pszExtraNewline - pszBuffer + 1);
×
442

443
        *pszExtraNewline = '\0';
×
444
        if (VSIFSeek(fp, nOriginalOffset + nActuallyRead - 1, SEEK_SET) != 0)
×
445
            return nullptr;
×
446

447
        // This hackery is necessary to try and find our correct
448
        // spot on win32 systems with text mode line translation going
449
        // on.  Sometimes the fseek back overshoots, but it doesn't
450
        // "realize it" till a character has been read. Try to read till
451
        // we get to the right spot and get our CR.
452
        int chCheck = fgetc(fp);
×
453
        while ((chCheck != knCR && chCheck != EOF) ||
×
454
               VSIFTell(fp) < nOriginalOffset + nActuallyRead)
×
455
        {
456
            static bool bWarned = false;
457

458
            if (!bWarned)
×
459
            {
460
                bWarned = true;
×
461
                CPLDebug("CPL",
×
462
                         "CPLFGets() correcting for DOS text mode translation "
463
                         "seek problem.");
464
            }
465
            chCheck = fgetc(fp);
×
466
        }
467
    }
468

469
    return pszBuffer;
×
470
}
471

472
/************************************************************************/
473
/*                         CPLReadLineBuffer()                          */
474
/*                                                                      */
475
/*      Fetch readline buffer, and ensure it is the desired size,       */
476
/*      reallocating if needed.  Manages TLS (thread local storage)     */
477
/*      issues for the buffer.                                          */
478
/*      We use a special trick to track the actual size of the buffer   */
479
/*      The first 4 bytes are reserved to store it as a int, hence the  */
480
/*      -4 / +4 hacks with the size and pointer.                        */
481
/************************************************************************/
482
static char *CPLReadLineBuffer(int nRequiredSize)
2,736,530✔
483

484
{
485

486
    /* -------------------------------------------------------------------- */
487
    /*      A required size of -1 means the buffer should be freed.         */
488
    /* -------------------------------------------------------------------- */
489
    if (nRequiredSize == -1)
2,736,530✔
490
    {
491
        int bMemoryError = FALSE;
2,367✔
492
        void *pRet = CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError);
2,367✔
493
        if (pRet != nullptr)
2,367✔
494
        {
495
            CPLFree(pRet);
2,115✔
496
            CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
2,115✔
497
        }
498
        return nullptr;
2,367✔
499
    }
500

501
    /* -------------------------------------------------------------------- */
502
    /*      If the buffer doesn't exist yet, create it.                     */
503
    /* -------------------------------------------------------------------- */
504
    int bMemoryError = FALSE;
2,734,160✔
505
    GUInt32 *pnAlloc =
506
        static_cast<GUInt32 *>(CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError));
2,734,160✔
507
    if (bMemoryError)
2,734,160✔
508
        return nullptr;
×
509

510
    if (pnAlloc == nullptr)
2,734,160✔
511
    {
512
        pnAlloc = static_cast<GUInt32 *>(VSI_MALLOC_VERBOSE(200));
3,602✔
513
        if (pnAlloc == nullptr)
3,602✔
514
            return nullptr;
×
515
        *pnAlloc = 196;
3,602✔
516
        CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
3,602✔
517
    }
518

519
    /* -------------------------------------------------------------------- */
520
    /*      If it is too small, grow it bigger.                             */
521
    /* -------------------------------------------------------------------- */
522
    if (static_cast<int>(*pnAlloc) - 1 < nRequiredSize)
2,734,160✔
523
    {
524
        const int nNewSize = nRequiredSize + 4 + 500;
2,786✔
525
        if (nNewSize <= 0)
2,786✔
526
        {
527
            VSIFree(pnAlloc);
×
528
            CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
×
529
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
530
                     "CPLReadLineBuffer(): Trying to allocate more than "
531
                     "2 GB.");
532
            return nullptr;
×
533
        }
534

535
        GUInt32 *pnAllocNew =
536
            static_cast<GUInt32 *>(VSI_REALLOC_VERBOSE(pnAlloc, nNewSize));
2,786✔
537
        if (pnAllocNew == nullptr)
2,786✔
538
        {
539
            VSIFree(pnAlloc);
×
540
            CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
×
541
            return nullptr;
×
542
        }
543
        pnAlloc = pnAllocNew;
2,786✔
544

545
        *pnAlloc = nNewSize - 4;
2,786✔
546
        CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
2,786✔
547
    }
548

549
    return reinterpret_cast<char *>(pnAlloc + 1);
2,734,160✔
550
}
551

552
/************************************************************************/
553
/*                            CPLReadLine()                             */
554
/************************************************************************/
555

556
/**
557
 * Simplified line reading from text file.
558
 *
559
 * Read a line of text from the given file handle, taking care
560
 * to capture CR and/or LF and strip off ... equivalent of
561
 * DKReadLine().  Pointer to an internal buffer is returned.
562
 * The application shouldn't free it, or depend on its value
563
 * past the next call to CPLReadLine().
564
 *
565
 * Note that CPLReadLine() uses VSIFGets(), so any hooking of VSI file
566
 * services should apply to CPLReadLine() as well.
567
 *
568
 * CPLReadLine() maintains an internal buffer, which will appear as a
569
 * single block memory leak in some circumstances.  CPLReadLine() may
570
 * be called with a NULL FILE * at any time to free this working buffer.
571
 *
572
 * @param fp file pointer opened with VSIFOpen().
573
 *
574
 * @return pointer to an internal buffer containing a line of text read
575
 * from the file or NULL if the end of file was encountered.
576
 */
577

578
const char *CPLReadLine(FILE *fp)
5✔
579

580
{
581
    /* -------------------------------------------------------------------- */
582
    /*      Cleanup case.                                                   */
583
    /* -------------------------------------------------------------------- */
584
    if (fp == nullptr)
5✔
585
    {
586
        CPLReadLineBuffer(-1);
5✔
587
        return nullptr;
5✔
588
    }
589

590
    /* -------------------------------------------------------------------- */
591
    /*      Loop reading chunks of the line till we get to the end of       */
592
    /*      the line.                                                       */
593
    /* -------------------------------------------------------------------- */
594
    size_t nBytesReadThisTime = 0;
×
595
    char *pszRLBuffer = nullptr;
×
596
    size_t nReadSoFar = 0;
×
597

598
    do
×
599
    {
600
        /* --------------------------------------------------------------------
601
         */
602
        /*      Grow the working buffer if we have it nearly full.  Fail out */
603
        /*      of read line if we can't reallocate it big enough (for */
604
        /*      instance for a _very large_ file with no newlines). */
605
        /* --------------------------------------------------------------------
606
         */
607
        if (nReadSoFar > 100 * 1024 * 1024)
×
608
            // It is dubious that we need to read a line longer than 100 MB.
609
            return nullptr;
×
610
        pszRLBuffer = CPLReadLineBuffer(static_cast<int>(nReadSoFar) + 129);
×
611
        if (pszRLBuffer == nullptr)
×
612
            return nullptr;
×
613

614
        /* --------------------------------------------------------------------
615
         */
616
        /*      Do the actual read. */
617
        /* --------------------------------------------------------------------
618
         */
619
        if (CPLFGets(pszRLBuffer + nReadSoFar, 128, fp) == nullptr &&
×
620
            nReadSoFar == 0)
621
            return nullptr;
×
622

623
        nBytesReadThisTime = strlen(pszRLBuffer + nReadSoFar);
×
624
        nReadSoFar += nBytesReadThisTime;
×
625
    } while (nBytesReadThisTime >= 127 && pszRLBuffer[nReadSoFar - 1] != knCR &&
×
626
             pszRLBuffer[nReadSoFar - 1] != knLF);
×
627

628
    return pszRLBuffer;
×
629
}
630

631
/************************************************************************/
632
/*                            CPLReadLineL()                            */
633
/************************************************************************/
634

635
/**
636
 * Simplified line reading from text file.
637
 *
638
 * Similar to CPLReadLine(), but reading from a large file API handle.
639
 *
640
 * @param fp file pointer opened with VSIFOpenL().
641
 *
642
 * @return pointer to an internal buffer containing a line of text read
643
 * from the file or NULL if the end of file was encountered.
644
 */
645

646
const char *CPLReadLineL(VSILFILE *fp)
196,395✔
647
{
648
    return CPLReadLine2L(fp, -1, nullptr);
196,395✔
649
}
650

651
/************************************************************************/
652
/*                           CPLReadLine2L()                            */
653
/************************************************************************/
654

655
/**
656
 * Simplified line reading from text file.
657
 *
658
 * Similar to CPLReadLine(), but reading from a large file API handle.
659
 *
660
 * @param fp file pointer opened with VSIFOpenL().
661
 * @param nMaxCars  maximum number of characters allowed, or -1 for no limit.
662
 * @param papszOptions NULL-terminated array of options. Unused for now.
663

664
 * @return pointer to an internal buffer containing a line of text read
665
 * from the file or NULL if the end of file was encountered or the maximum
666
 * number of characters allowed reached.
667
 *
668
 * @since GDAL 1.7.0
669
 */
670

671
const char *CPLReadLine2L(VSILFILE *fp, int nMaxCars,
1,322,580✔
672
                          CPL_UNUSED CSLConstList papszOptions)
673

674
{
675
    int nBufLength;
676
    return CPLReadLine3L(fp, nMaxCars, &nBufLength, papszOptions);
2,645,170✔
677
}
678

679
/************************************************************************/
680
/*                           CPLReadLine3L()                            */
681
/************************************************************************/
682

683
/**
684
 * Simplified line reading from text file.
685
 *
686
 * Similar to CPLReadLine(), but reading from a large file API handle.
687
 *
688
 * @param fp file pointer opened with VSIFOpenL().
689
 * @param nMaxCars  maximum number of characters allowed, or -1 for no limit.
690
 * @param papszOptions NULL-terminated array of options. Unused for now.
691
 * @param[out] pnBufLength size of output string (must be non-NULL)
692

693
 * @return pointer to an internal buffer containing a line of text read
694
 * from the file or NULL if the end of file was encountered or the maximum
695
 * number of characters allowed reached.
696
 *
697
 * @since GDAL 2.3.0
698
 */
699
const char *CPLReadLine3L(VSILFILE *fp, int nMaxCars, int *pnBufLength,
1,385,790✔
700
                          CPL_UNUSED CSLConstList papszOptions)
701
{
702
    /* -------------------------------------------------------------------- */
703
    /*      Cleanup case.                                                   */
704
    /* -------------------------------------------------------------------- */
705
    if (fp == nullptr)
1,385,790✔
706
    {
707
        CPLReadLineBuffer(-1);
2,362✔
708
        return nullptr;
2,362✔
709
    }
710

711
    /* -------------------------------------------------------------------- */
712
    /*      Loop reading chunks of the line till we get to the end of       */
713
    /*      the line.                                                       */
714
    /* -------------------------------------------------------------------- */
715
    char *pszRLBuffer = nullptr;
1,383,430✔
716
    const size_t nChunkSize = 40;
1,383,430✔
717
    char szChunk[nChunkSize] = {};
1,383,430✔
718
    size_t nChunkBytesRead = 0;
1,383,430✔
719
    size_t nChunkBytesConsumed = 0;
1,383,430✔
720

721
    *pnBufLength = 0;
1,383,430✔
722
    szChunk[0] = 0;
1,383,430✔
723

724
    while (true)
725
    {
726
        /* --------------------------------------------------------------------
727
         */
728
        /*      Read a chunk from the input file. */
729
        /* --------------------------------------------------------------------
730
         */
731
        if (*pnBufLength > INT_MAX - static_cast<int>(nChunkSize) - 1)
2,734,160✔
732
        {
733
            CPLError(CE_Failure, CPLE_AppDefined,
×
734
                     "Too big line : more than 2 billion characters!.");
735
            CPLReadLineBuffer(-1);
×
736
            return nullptr;
×
737
        }
738

739
        pszRLBuffer =
740
            CPLReadLineBuffer(static_cast<int>(*pnBufLength + nChunkSize + 1));
2,734,160✔
741
        if (pszRLBuffer == nullptr)
2,734,160✔
742
            return nullptr;
×
743

744
        if (nChunkBytesRead == nChunkBytesConsumed + 1)
2,734,160✔
745
        {
746

747
            // case where one character is left over from last read.
748
            szChunk[0] = szChunk[nChunkBytesConsumed];
1,350,730✔
749

750
            nChunkBytesConsumed = 0;
1,350,730✔
751
            nChunkBytesRead = VSIFReadL(szChunk + 1, 1, nChunkSize - 1, fp) + 1;
1,350,730✔
752
        }
753
        else
754
        {
755
            nChunkBytesConsumed = 0;
1,383,430✔
756

757
            // fresh read.
758
            nChunkBytesRead = VSIFReadL(szChunk, 1, nChunkSize, fp);
1,383,430✔
759
            if (nChunkBytesRead == 0)
1,383,430✔
760
            {
761
                if (*pnBufLength == 0)
11,392✔
762
                    return nullptr;
11,392✔
763

764
                break;
×
765
            }
766
        }
767

768
        /* --------------------------------------------------------------------
769
         */
770
        /*      copy over characters watching for end-of-line. */
771
        /* --------------------------------------------------------------------
772
         */
773
        bool bBreak = false;
2,722,770✔
774
        while (nChunkBytesConsumed < nChunkBytesRead - 1 && !bBreak)
75,200,800✔
775
        {
776
            if ((szChunk[nChunkBytesConsumed] == knCR &&
72,478,000✔
777
                 szChunk[nChunkBytesConsumed + 1] == knLF) ||
172,639✔
778
                (szChunk[nChunkBytesConsumed] == knLF &&
72,305,700✔
779
                 szChunk[nChunkBytesConsumed + 1] == knCR))
1,187,570✔
780
            {
781
                nChunkBytesConsumed += 2;
172,362✔
782
                bBreak = true;
172,362✔
783
            }
784
            else if (szChunk[nChunkBytesConsumed] == knLF ||
72,305,700✔
785
                     szChunk[nChunkBytesConsumed] == knCR)
71,118,100✔
786
            {
787
                nChunkBytesConsumed += 1;
1,187,840✔
788
                bBreak = true;
1,187,840✔
789
            }
790
            else
791
            {
792
                pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
71,117,800✔
793
                if (nMaxCars >= 0 && *pnBufLength == nMaxCars)
71,117,800✔
794
                {
795
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
796
                             "Maximum number of characters allowed reached.");
797
                    return nullptr;
1✔
798
                }
799
            }
800
        }
801

802
        if (bBreak)
2,722,770✔
803
            break;
1,360,210✔
804

805
        /* --------------------------------------------------------------------
806
         */
807
        /*      If there is a remaining character and it is not a newline */
808
        /*      consume it.  If it is a newline, but we are clearly at the */
809
        /*      end of the file then consume it. */
810
        /* --------------------------------------------------------------------
811
         */
812
        if (nChunkBytesConsumed == nChunkBytesRead - 1 &&
1,362,560✔
813
            nChunkBytesRead < nChunkSize)
814
        {
815
            if (szChunk[nChunkBytesConsumed] == knLF ||
11,827✔
816
                szChunk[nChunkBytesConsumed] == knCR)
1,788✔
817
            {
818
                nChunkBytesConsumed++;
10,039✔
819
                break;
10,039✔
820
            }
821

822
            pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
1,788✔
823
            break;
1,788✔
824
        }
825
    }
1,350,730✔
826

827
    /* -------------------------------------------------------------------- */
828
    /*      If we have left over bytes after breaking out, seek back to     */
829
    /*      ensure they remain to be read next time.                        */
830
    /* -------------------------------------------------------------------- */
831
    if (nChunkBytesConsumed < nChunkBytesRead)
1,372,030✔
832
    {
833
        const size_t nBytesToPush = nChunkBytesRead - nChunkBytesConsumed;
1,354,690✔
834

835
        if (VSIFSeekL(fp, VSIFTellL(fp) - nBytesToPush, SEEK_SET) != 0)
1,354,690✔
836
            return nullptr;
×
837
    }
838

839
    pszRLBuffer[*pnBufLength] = '\0';
1,372,030✔
840

841
    return pszRLBuffer;
1,372,030✔
842
}
843

844
/************************************************************************/
845
/*                            CPLScanString()                           */
846
/************************************************************************/
847

848
/**
849
 * Scan up to a maximum number of characters from a given string,
850
 * allocate a buffer for a new string and fill it with scanned characters.
851
 *
852
 * @param pszString String containing characters to be scanned. It may be
853
 * terminated with a null character.
854
 *
855
 * @param nMaxLength The maximum number of character to read. Less
856
 * characters will be read if a null character is encountered.
857
 *
858
 * @param bTrimSpaces If TRUE, trim ending spaces from the input string.
859
 * Character considered as empty using isspace(3) function.
860
 *
861
 * @param bNormalize If TRUE, replace ':' symbol with the '_'. It is needed if
862
 * resulting string will be used in CPL dictionaries.
863
 *
864
 * @return Pointer to the resulting string buffer. Caller responsible to free
865
 * this buffer with CPLFree().
866
 */
867

868
char *CPLScanString(const char *pszString, int nMaxLength, int bTrimSpaces,
5,220✔
869
                    int bNormalize)
870
{
871
    if (!pszString)
5,220✔
872
        return nullptr;
×
873

874
    if (!nMaxLength)
5,220✔
875
        return CPLStrdup("");
2✔
876

877
    char *pszBuffer = static_cast<char *>(CPLMalloc(nMaxLength + 1));
5,218✔
878
    if (!pszBuffer)
5,218✔
879
        return nullptr;
×
880

881
    strncpy(pszBuffer, pszString, nMaxLength);
5,218✔
882
    pszBuffer[nMaxLength] = '\0';
5,218✔
883

884
    if (bTrimSpaces)
5,218✔
885
    {
886
        size_t i = strlen(pszBuffer);
5,218✔
887
        while (i > 0)
6,342✔
888
        {
889
            i--;
6,308✔
890
            if (!isspace(static_cast<unsigned char>(pszBuffer[i])))
6,308✔
891
                break;
5,184✔
892
            pszBuffer[i] = '\0';
1,124✔
893
        }
894
    }
895

896
    if (bNormalize)
5,218✔
897
    {
898
        size_t i = strlen(pszBuffer);
5,100✔
899
        while (i > 0)
38,870✔
900
        {
901
            i--;
33,770✔
902
            if (pszBuffer[i] == ':')
33,770✔
903
                pszBuffer[i] = '_';
×
904
        }
905
    }
906

907
    return pszBuffer;
5,218✔
908
}
909

910
/************************************************************************/
911
/*                             CPLScanLong()                            */
912
/************************************************************************/
913

914
/**
915
 * Scan up to a maximum number of characters from a string and convert
916
 * the result to a long.
917
 *
918
 * @param pszString String containing characters to be scanned. It may be
919
 * terminated with a null character.
920
 *
921
 * @param nMaxLength The maximum number of character to consider as part
922
 * of the number. Less characters will be considered if a null character
923
 * is encountered.
924
 *
925
 * @return Long value, converted from its ASCII form.
926
 */
927

928
long CPLScanLong(const char *pszString, int nMaxLength)
571✔
929
{
930
    CPLAssert(nMaxLength >= 0);
571✔
931
    if (pszString == nullptr)
571✔
932
        return 0;
×
933
    const size_t nLength = CPLStrnlen(pszString, nMaxLength);
571✔
934
    const std::string osValue(pszString, nLength);
1,142✔
935
    return atol(osValue.c_str());
571✔
936
}
937

938
/************************************************************************/
939
/*                            CPLScanULong()                            */
940
/************************************************************************/
941

942
/**
943
 * Scan up to a maximum number of characters from a string and convert
944
 * the result to a unsigned long.
945
 *
946
 * @param pszString String containing characters to be scanned. It may be
947
 * terminated with a null character.
948
 *
949
 * @param nMaxLength The maximum number of character to consider as part
950
 * of the number. Less characters will be considered if a null character
951
 * is encountered.
952
 *
953
 * @return Unsigned long value, converted from its ASCII form.
954
 */
955

956
unsigned long CPLScanULong(const char *pszString, int nMaxLength)
×
957
{
958
    CPLAssert(nMaxLength >= 0);
×
959
    if (pszString == nullptr)
×
960
        return 0;
×
961
    const size_t nLength = CPLStrnlen(pszString, nMaxLength);
×
962
    const std::string osValue(pszString, nLength);
×
963
    return strtoul(osValue.c_str(), nullptr, 10);
×
964
}
965

966
/************************************************************************/
967
/*                           CPLScanUIntBig()                           */
968
/************************************************************************/
969

970
/**
971
 * Extract big integer from string.
972
 *
973
 * Scan up to a maximum number of characters from a string and convert
974
 * the result to a GUIntBig.
975
 *
976
 * @param pszString String containing characters to be scanned. It may be
977
 * terminated with a null character.
978
 *
979
 * @param nMaxLength The maximum number of character to consider as part
980
 * of the number. Less characters will be considered if a null character
981
 * is encountered.
982
 *
983
 * @return GUIntBig value, converted from its ASCII form.
984
 */
985

986
GUIntBig CPLScanUIntBig(const char *pszString, int nMaxLength)
16,057✔
987
{
988
    CPLAssert(nMaxLength >= 0);
16,057✔
989
    if (pszString == nullptr)
16,057✔
990
        return 0;
×
991
    const size_t nLength = CPLStrnlen(pszString, nMaxLength);
16,057✔
992
    const std::string osValue(pszString, nLength);
32,114✔
993

994
    /* -------------------------------------------------------------------- */
995
    /*      Fetch out the result                                            */
996
    /* -------------------------------------------------------------------- */
997
    return strtoull(osValue.c_str(), nullptr, 10);
16,057✔
998
}
999

1000
/************************************************************************/
1001
/*                           CPLAtoGIntBig()                            */
1002
/************************************************************************/
1003

1004
/**
1005
 * Convert a string to a 64 bit signed integer.
1006
 *
1007
 * @param pszString String containing 64 bit signed integer.
1008
 * @return 64 bit signed integer.
1009
 * @since GDAL 2.0
1010
 */
1011

1012
GIntBig CPLAtoGIntBig(const char *pszString)
111,264✔
1013
{
1014
    return atoll(pszString);
111,264✔
1015
}
1016

1017
#if defined(__MINGW32__) || defined(__sun__)
1018

1019
// mingw atoll() doesn't return ERANGE in case of overflow
1020
static int CPLAtoGIntBigExHasOverflow(const char *pszString, GIntBig nVal)
1021
{
1022
    if (strlen(pszString) <= 18)
1023
        return FALSE;
1024
    while (*pszString == ' ')
1025
        pszString++;
1026
    if (*pszString == '+')
1027
        pszString++;
1028
    char szBuffer[32] = {};
1029
/* x86_64-w64-mingw32-g++ (GCC) 4.8.2 annoyingly warns */
1030
#ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1031
#pragma GCC diagnostic push
1032
#pragma GCC diagnostic ignored "-Wformat"
1033
#endif
1034
    snprintf(szBuffer, sizeof(szBuffer), CPL_FRMT_GIB, nVal);
1035
#ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1036
#pragma GCC diagnostic pop
1037
#endif
1038
    return strcmp(szBuffer, pszString) != 0;
1039
}
1040

1041
#endif
1042

1043
/************************************************************************/
1044
/*                          CPLAtoGIntBigEx()                           */
1045
/************************************************************************/
1046

1047
/**
1048
 * Convert a string to a 64 bit signed integer.
1049
 *
1050
 * @param pszString String containing 64 bit signed integer.
1051
 * @param bWarn Issue a warning if an overflow occurs during conversion
1052
 * @param pbOverflow Pointer to an integer to store if an overflow occurred, or
1053
 *        NULL
1054
 * @return 64 bit signed integer.
1055
 * @since GDAL 2.0
1056
 */
1057

1058
GIntBig CPLAtoGIntBigEx(const char *pszString, int bWarn, int *pbOverflow)
103,608✔
1059
{
1060
    errno = 0;
103,608✔
1061
    GIntBig nVal = strtoll(pszString, nullptr, 10);
103,608✔
1062
    if (errno == ERANGE
103,608✔
1063
#if defined(__MINGW32__) || defined(__sun__)
1064
        || CPLAtoGIntBigExHasOverflow(pszString, nVal)
1065
#endif
1066
    )
1067
    {
1068
        if (pbOverflow)
4✔
1069
            *pbOverflow = TRUE;
2✔
1070
        if (bWarn)
4✔
1071
        {
1072
            CPLError(CE_Warning, CPLE_AppDefined,
2✔
1073
                     "64 bit integer overflow when converting %s", pszString);
1074
        }
1075
        while (*pszString == ' ')
4✔
1076
            pszString++;
×
1077
        return (*pszString == '-') ? GINTBIG_MIN : GINTBIG_MAX;
4✔
1078
    }
1079
    else if (pbOverflow)
103,604✔
1080
    {
1081
        *pbOverflow = FALSE;
3,874✔
1082
    }
1083
    return nVal;
103,604✔
1084
}
1085

1086
/************************************************************************/
1087
/*                           CPLScanPointer()                           */
1088
/************************************************************************/
1089

1090
/**
1091
 * Extract pointer from string.
1092
 *
1093
 * Scan up to a maximum number of characters from a string and convert
1094
 * the result to a pointer.
1095
 *
1096
 * @param pszString String containing characters to be scanned. It may be
1097
 * terminated with a null character.
1098
 *
1099
 * @param nMaxLength The maximum number of character to consider as part
1100
 * of the number. Less characters will be considered if a null character
1101
 * is encountered.
1102
 *
1103
 * @return pointer value, converted from its ASCII form.
1104
 */
1105

1106
void *CPLScanPointer(const char *pszString, int nMaxLength)
2,486✔
1107
{
1108
    char szTemp[128] = {};
2,486✔
1109

1110
    /* -------------------------------------------------------------------- */
1111
    /*      Compute string into local buffer, and terminate it.             */
1112
    /* -------------------------------------------------------------------- */
1113
    if (nMaxLength > static_cast<int>(sizeof(szTemp)) - 1)
2,486✔
1114
        nMaxLength = sizeof(szTemp) - 1;
×
1115

1116
    strncpy(szTemp, pszString, nMaxLength);
2,486✔
1117
    szTemp[nMaxLength] = '\0';
2,486✔
1118

1119
    /* -------------------------------------------------------------------- */
1120
    /*      On MSVC we have to scanf pointer values without the 0x          */
1121
    /*      prefix.                                                         */
1122
    /* -------------------------------------------------------------------- */
1123
    if (STARTS_WITH_CI(szTemp, "0x"))
2,486✔
1124
    {
1125
        void *pResult = nullptr;
2,491✔
1126

1127
#if defined(__MSVCRT__) || (defined(_WIN32) && defined(_MSC_VER))
1128
        // cppcheck-suppress invalidscanf
1129
        sscanf(szTemp + 2, "%p", &pResult);
1130
#else
1131
        // cppcheck-suppress invalidscanf
1132
        sscanf(szTemp, "%p", &pResult);
2,491✔
1133

1134
        // Solaris actually behaves like MSVCRT.
1135
        if (pResult == nullptr)
2,491✔
1136
        {
1137
            // cppcheck-suppress invalidscanf
1138
            sscanf(szTemp + 2, "%p", &pResult);
×
1139
        }
1140
#endif
1141
        return pResult;
2,491✔
1142
    }
1143

1144
#if SIZEOF_VOIDP == 8
1145
    return reinterpret_cast<void *>(CPLScanUIntBig(szTemp, nMaxLength));
×
1146
#else
1147
    return reinterpret_cast<void *>(CPLScanULong(szTemp, nMaxLength));
1148
#endif
1149
}
1150

1151
/************************************************************************/
1152
/*                             CPLScanDouble()                          */
1153
/************************************************************************/
1154

1155
/**
1156
 * Extract double from string.
1157
 *
1158
 * Scan up to a maximum number of characters from a string and convert the
1159
 * result to a double. This function uses CPLAtof() to convert string to
1160
 * double value, so it uses a comma as a decimal delimiter.
1161
 *
1162
 * @param pszString String containing characters to be scanned. It may be
1163
 * terminated with a null character.
1164
 *
1165
 * @param nMaxLength The maximum number of character to consider as part
1166
 * of the number. Less characters will be considered if a null character
1167
 * is encountered.
1168
 *
1169
 * @return Double value, converted from its ASCII form.
1170
 */
1171

1172
double CPLScanDouble(const char *pszString, int nMaxLength)
317✔
1173
{
1174
    char szValue[32] = {};
317✔
1175
    char *pszValue = nullptr;
317✔
1176

1177
    if (nMaxLength + 1 < static_cast<int>(sizeof(szValue)))
317✔
1178
        pszValue = szValue;
317✔
1179
    else
1180
        pszValue = static_cast<char *>(CPLMalloc(nMaxLength + 1));
×
1181

1182
    /* -------------------------------------------------------------------- */
1183
    /*      Compute string into local buffer, and terminate it.             */
1184
    /* -------------------------------------------------------------------- */
1185
    strncpy(pszValue, pszString, nMaxLength);
317✔
1186
    pszValue[nMaxLength] = '\0';
317✔
1187

1188
    /* -------------------------------------------------------------------- */
1189
    /*      Make a pass through converting 'D's to 'E's.                    */
1190
    /* -------------------------------------------------------------------- */
1191
    for (int i = 0; i < nMaxLength; i++)
6,436✔
1192
        if (pszValue[i] == 'd' || pszValue[i] == 'D')
6,119✔
1193
            pszValue[i] = 'E';
45✔
1194

1195
    /* -------------------------------------------------------------------- */
1196
    /*      The conversion itself.                                          */
1197
    /* -------------------------------------------------------------------- */
1198
    const double dfValue = CPLAtof(pszValue);
317✔
1199

1200
    if (pszValue != szValue)
317✔
1201
        CPLFree(pszValue);
×
1202
    return dfValue;
317✔
1203
}
1204

1205
/************************************************************************/
1206
/*                      CPLPrintString()                                */
1207
/************************************************************************/
1208

1209
/**
1210
 * Copy the string pointed to by pszSrc, NOT including the terminating
1211
 * `\\0' character, to the array pointed to by pszDest.
1212
 *
1213
 * @param pszDest Pointer to the destination string buffer. Should be
1214
 * large enough to hold the resulting string.
1215
 *
1216
 * @param pszSrc Pointer to the source buffer.
1217
 *
1218
 * @param nMaxLen Maximum length of the resulting string. If string length
1219
 * is greater than nMaxLen, it will be truncated.
1220
 *
1221
 * @return Number of characters printed.
1222
 */
1223

1224
int CPLPrintString(char *pszDest, const char *pszSrc, int nMaxLen)
12,352✔
1225
{
1226
    if (!pszDest)
12,352✔
1227
        return 0;
×
1228

1229
    if (!pszSrc)
12,352✔
1230
    {
1231
        *pszDest = '\0';
×
1232
        return 1;
×
1233
    }
1234

1235
    int nChars = 0;
12,352✔
1236
    char *pszTemp = pszDest;
12,352✔
1237

1238
    while (nChars < nMaxLen && *pszSrc)
183,910✔
1239
    {
1240
        *pszTemp++ = *pszSrc++;
171,558✔
1241
        nChars++;
171,558✔
1242
    }
1243

1244
    return nChars;
12,352✔
1245
}
1246

1247
/************************************************************************/
1248
/*                         CPLPrintStringFill()                         */
1249
/************************************************************************/
1250

1251
/**
1252
 * Copy the string pointed to by pszSrc, NOT including the terminating
1253
 * `\\0' character, to the array pointed to by pszDest. Remainder of the
1254
 * destination string will be filled with space characters. This is only
1255
 * difference from the PrintString().
1256
 *
1257
 * @param pszDest Pointer to the destination string buffer. Should be
1258
 * large enough to hold the resulting string.
1259
 *
1260
 * @param pszSrc Pointer to the source buffer.
1261
 *
1262
 * @param nMaxLen Maximum length of the resulting string. If string length
1263
 * is greater than nMaxLen, it will be truncated.
1264
 *
1265
 * @return Number of characters printed.
1266
 */
1267

1268
int CPLPrintStringFill(char *pszDest, const char *pszSrc, int nMaxLen)
209✔
1269
{
1270
    if (!pszDest)
209✔
1271
        return 0;
×
1272

1273
    if (!pszSrc)
209✔
1274
    {
1275
        memset(pszDest, ' ', nMaxLen);
×
1276
        return nMaxLen;
×
1277
    }
1278

1279
    char *pszTemp = pszDest;
209✔
1280
    while (nMaxLen && *pszSrc)
1,257✔
1281
    {
1282
        *pszTemp++ = *pszSrc++;
1,048✔
1283
        nMaxLen--;
1,048✔
1284
    }
1285

1286
    if (nMaxLen)
209✔
1287
        memset(pszTemp, ' ', nMaxLen);
71✔
1288

1289
    return nMaxLen;
209✔
1290
}
1291

1292
/************************************************************************/
1293
/*                          CPLPrintInt32()                             */
1294
/************************************************************************/
1295

1296
/**
1297
 * Print GInt32 value into specified string buffer. This string will not
1298
 * be NULL-terminated.
1299
 *
1300
 * @param pszBuffer Pointer to the destination string buffer. Should be
1301
 * large enough to hold the resulting string. Note, that the string will
1302
 * not be NULL-terminated, so user should do this himself, if needed.
1303
 *
1304
 * @param iValue Numerical value to print.
1305
 *
1306
 * @param nMaxLen Maximum length of the resulting string. If string length
1307
 * is greater than nMaxLen, it will be truncated.
1308
 *
1309
 * @return Number of characters printed.
1310
 */
1311

1312
int CPLPrintInt32(char *pszBuffer, GInt32 iValue, int nMaxLen)
9✔
1313
{
1314
    if (!pszBuffer)
9✔
1315
        return 0;
×
1316

1317
    if (nMaxLen >= 64)
9✔
1318
        nMaxLen = 63;
×
1319

1320
    char szTemp[64] = {};
9✔
1321

1322
#if UINT_MAX == 65535
1323
    snprintf(szTemp, sizeof(szTemp), "%*ld", nMaxLen, iValue);
1324
#else
1325
    snprintf(szTemp, sizeof(szTemp), "%*d", nMaxLen, iValue);
9✔
1326
#endif
1327

1328
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
9✔
1329
}
1330

1331
/************************************************************************/
1332
/*                          CPLPrintUIntBig()                           */
1333
/************************************************************************/
1334

1335
/**
1336
 * Print GUIntBig value into specified string buffer. This string will not
1337
 * be NULL-terminated.
1338
 *
1339
 * @param pszBuffer Pointer to the destination string buffer. Should be
1340
 * large enough to hold the resulting string. Note, that the string will
1341
 * not be NULL-terminated, so user should do this himself, if needed.
1342
 *
1343
 * @param iValue Numerical value to print.
1344
 *
1345
 * @param nMaxLen Maximum length of the resulting string. If string length
1346
 * is greater than nMaxLen, it will be truncated.
1347
 *
1348
 * @return Number of characters printed.
1349
 */
1350

1351
int CPLPrintUIntBig(char *pszBuffer, GUIntBig iValue, int nMaxLen)
24✔
1352
{
1353
    if (!pszBuffer)
24✔
1354
        return 0;
×
1355

1356
    if (nMaxLen >= 64)
24✔
1357
        nMaxLen = 63;
×
1358

1359
    char szTemp[64] = {};
24✔
1360

1361
#if defined(__MSVCRT__) || (defined(_WIN32) && defined(_MSC_VER))
1362
/* x86_64-w64-mingw32-g++ (GCC) 4.8.2 annoyingly warns */
1363
#ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1364
#pragma GCC diagnostic push
1365
#pragma GCC diagnostic ignored "-Wformat"
1366
#pragma GCC diagnostic ignored "-Wformat-extra-args"
1367
#endif
1368
    snprintf(szTemp, sizeof(szTemp), "%*I64u", nMaxLen, iValue);
1369
#ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1370
#pragma GCC diagnostic pop
1371
#endif
1372
#else
1373
    snprintf(szTemp, sizeof(szTemp), "%*llu", nMaxLen, iValue);
24✔
1374
#endif
1375

1376
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
24✔
1377
}
1378

1379
/************************************************************************/
1380
/*                          CPLPrintPointer()                           */
1381
/************************************************************************/
1382

1383
/**
1384
 * Print pointer value into specified string buffer. This string will not
1385
 * be NULL-terminated.
1386
 *
1387
 * @param pszBuffer Pointer to the destination string buffer. Should be
1388
 * large enough to hold the resulting string. Note, that the string will
1389
 * not be NULL-terminated, so user should do this himself, if needed.
1390
 *
1391
 * @param pValue Pointer to ASCII encode.
1392
 *
1393
 * @param nMaxLen Maximum length of the resulting string. If string length
1394
 * is greater than nMaxLen, it will be truncated.
1395
 *
1396
 * @return Number of characters printed.
1397
 */
1398

1399
int CPLPrintPointer(char *pszBuffer, void *pValue, int nMaxLen)
12,285✔
1400
{
1401
    if (!pszBuffer)
12,285✔
1402
        return 0;
×
1403

1404
    if (nMaxLen >= 64)
12,285✔
1405
        nMaxLen = 63;
9,774✔
1406

1407
    char szTemp[64] = {};
12,285✔
1408

1409
    snprintf(szTemp, sizeof(szTemp), "%p", pValue);
12,285✔
1410

1411
    // On windows, and possibly some other platforms the sprintf("%p")
1412
    // does not prefix things with 0x so it is hard to know later if the
1413
    // value is hex encoded.  Fix this up here.
1414

1415
    if (!STARTS_WITH_CI(szTemp, "0x"))
12,285✔
1416
        snprintf(szTemp, sizeof(szTemp), "0x%p", pValue);
×
1417

1418
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
12,285✔
1419
}
1420

1421
/************************************************************************/
1422
/*                          CPLPrintDouble()                            */
1423
/************************************************************************/
1424

1425
/**
1426
 * Print double value into specified string buffer. Exponential character
1427
 * flag 'E' (or 'e') will be replaced with 'D', as in Fortran. Resulting
1428
 * string will not to be NULL-terminated.
1429
 *
1430
 * @param pszBuffer Pointer to the destination string buffer. Should be
1431
 * large enough to hold the resulting string. Note, that the string will
1432
 * not be NULL-terminated, so user should do this himself, if needed.
1433
 *
1434
 * @param pszFormat Format specifier (for example, "%16.9E").
1435
 *
1436
 * @param dfValue Numerical value to print.
1437
 *
1438
 * @param pszLocale Unused.
1439
 *
1440
 * @return Number of characters printed.
1441
 */
1442

1443
int CPLPrintDouble(char *pszBuffer, const char *pszFormat, double dfValue,
×
1444
                   CPL_UNUSED const char *pszLocale)
1445
{
1446
    if (!pszBuffer)
×
1447
        return 0;
×
1448

1449
    const int knDoubleBufferSize = 64;
×
1450
    char szTemp[knDoubleBufferSize] = {};
×
1451

1452
    CPLsnprintf(szTemp, knDoubleBufferSize, pszFormat, dfValue);
×
1453
    szTemp[knDoubleBufferSize - 1] = '\0';
×
1454

1455
    for (int i = 0; szTemp[i] != '\0'; i++)
×
1456
    {
1457
        if (szTemp[i] == 'E' || szTemp[i] == 'e')
×
1458
            szTemp[i] = 'D';
×
1459
    }
1460

1461
    return CPLPrintString(pszBuffer, szTemp, 64);
×
1462
}
1463

1464
/************************************************************************/
1465
/*                            CPLPrintTime()                            */
1466
/************************************************************************/
1467

1468
/**
1469
 * Print specified time value accordingly to the format options and
1470
 * specified locale name. This function does following:
1471
 *
1472
 *  - if locale parameter is not NULL, the current locale setting will be
1473
 *  stored and replaced with the specified one;
1474
 *  - format time value with the strftime(3) function;
1475
 *  - restore back current locale, if was saved.
1476
 *
1477
 * @param pszBuffer Pointer to the destination string buffer. Should be
1478
 * large enough to hold the resulting string. Note, that the string will
1479
 * not be NULL-terminated, so user should do this himself, if needed.
1480
 *
1481
 * @param nMaxLen Maximum length of the resulting string. If string length is
1482
 * greater than nMaxLen, it will be truncated.
1483
 *
1484
 * @param pszFormat Controls the output format. Options are the same as
1485
 * for strftime(3) function.
1486
 *
1487
 * @param poBrokenTime Pointer to the broken-down time structure. May be
1488
 * requested with the VSIGMTime() and VSILocalTime() functions.
1489
 *
1490
 * @param pszLocale Pointer to a character string containing locale name
1491
 * ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL we will not
1492
 * manipulate with locale settings and current process locale will be used for
1493
 * printing. Be aware that it may be unsuitable to use current locale for
1494
 * printing time, because all names will be printed in your native language,
1495
 * as well as time format settings also may be adjusted differently from the
1496
 * C/POSIX defaults. To solve these problems this option was introduced.
1497
 *
1498
 * @return Number of characters printed.
1499
 */
1500

1501
int CPLPrintTime(char *pszBuffer, int nMaxLen, const char *pszFormat,
34✔
1502
                 const struct tm *poBrokenTime, const char *pszLocale)
1503
{
1504
    char *pszTemp =
1505
        static_cast<char *>(CPLMalloc((nMaxLen + 1) * sizeof(char)));
34✔
1506

1507
    if (pszLocale && EQUAL(pszLocale, "C") &&
34✔
1508
        strcmp(pszFormat, "%a, %d %b %Y %H:%M:%S GMT") == 0)
34✔
1509
    {
1510
        // Particular case when formatting RFC822 datetime, to avoid locale
1511
        // change
1512
        static const char *const aszMonthStr[] = {"Jan", "Feb", "Mar", "Apr",
1513
                                                  "May", "Jun", "Jul", "Aug",
1514
                                                  "Sep", "Oct", "Nov", "Dec"};
1515
        static const char *const aszDayOfWeek[] = {"Sun", "Mon", "Tue", "Wed",
1516
                                                   "Thu", "Fri", "Sat"};
1517
        snprintf(pszTemp, nMaxLen + 1, "%s, %02d %s %04d %02d:%02d:%02d GMT",
68✔
1518
                 aszDayOfWeek[std::max(0, std::min(6, poBrokenTime->tm_wday))],
34✔
1519
                 poBrokenTime->tm_mday,
34✔
1520
                 aszMonthStr[std::max(0, std::min(11, poBrokenTime->tm_mon))],
34✔
1521
                 poBrokenTime->tm_year + 1900, poBrokenTime->tm_hour,
34✔
1522
                 poBrokenTime->tm_min, poBrokenTime->tm_sec);
68✔
1523
    }
1524
    else
1525
    {
1526
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
1527
        char *pszCurLocale = NULL;
1528

1529
        if (pszLocale || EQUAL(pszLocale, ""))
1530
        {
1531
            // Save the current locale.
1532
            pszCurLocale = CPLsetlocale(LC_ALL, NULL);
1533
            // Set locale to the specified value.
1534
            CPLsetlocale(LC_ALL, pszLocale);
1535
        }
1536
#else
1537
        (void)pszLocale;
1538
#endif
1539

1540
        if (!strftime(pszTemp, nMaxLen + 1, pszFormat, poBrokenTime))
×
1541
            memset(pszTemp, 0, nMaxLen + 1);
×
1542

1543
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
1544
        // Restore stored locale back.
1545
        if (pszCurLocale)
1546
            CPLsetlocale(LC_ALL, pszCurLocale);
1547
#endif
1548
    }
1549

1550
    const int nChars = CPLPrintString(pszBuffer, pszTemp, nMaxLen);
34✔
1551

1552
    CPLFree(pszTemp);
34✔
1553

1554
    return nChars;
34✔
1555
}
1556

1557
/************************************************************************/
1558
/*                       CPLVerifyConfiguration()                       */
1559
/************************************************************************/
1560

1561
void CPLVerifyConfiguration()
×
1562

1563
{
1564
    /* -------------------------------------------------------------------- */
1565
    /*      Verify data types.                                              */
1566
    /* -------------------------------------------------------------------- */
1567
    static_assert(sizeof(short) == 2);   // We unfortunately rely on this
1568
    static_assert(sizeof(int) == 4);     // We unfortunately rely on this
1569
    static_assert(sizeof(float) == 4);   // We unfortunately rely on this
1570
    static_assert(sizeof(double) == 8);  // We unfortunately rely on this
1571
    static_assert(sizeof(GInt64) == 8);
1572
    static_assert(sizeof(GInt32) == 4);
1573
    static_assert(sizeof(GInt16) == 2);
1574
    static_assert(sizeof(GByte) == 1);
1575

1576
    /* -------------------------------------------------------------------- */
1577
    /*      Verify byte order                                               */
1578
    /* -------------------------------------------------------------------- */
1579
#ifdef CPL_LSB
1580
#if __cplusplus >= 202002L
1581
    static_assert(std::endian::native == std::endian::little);
1582
#elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
1583
    static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
1584
#endif
1585
#elif defined(CPL_MSB)
1586
#if __cplusplus >= 202002L
1587
    static_assert(std::endian::native == std::endian::big);
1588
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
1589
    static_assert(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
1590
#endif
1591
#else
1592
#error "CPL_LSB or CPL_MSB must be defined"
1593
#endif
1594
}
×
1595

1596
#ifdef DEBUG_CONFIG_OPTIONS
1597

1598
static CPLMutex *hRegisterConfigurationOptionMutex = nullptr;
1599
static std::set<CPLString> *paoGetKeys = nullptr;
1600
static std::set<CPLString> *paoSetKeys = nullptr;
1601

1602
/************************************************************************/
1603
/*                      CPLShowAccessedOptions()                        */
1604
/************************************************************************/
1605

1606
static void CPLShowAccessedOptions()
1607
{
1608
    std::set<CPLString>::iterator aoIter;
1609

1610
    printf("Configuration options accessed in reading : "); /*ok*/
1611
    aoIter = paoGetKeys->begin();
1612
    while (aoIter != paoGetKeys->end())
1613
    {
1614
        printf("%s, ", (*aoIter).c_str()); /*ok*/
1615
        ++aoIter;
1616
    }
1617
    printf("\n"); /*ok*/
1618

1619
    printf("Configuration options accessed in writing : "); /*ok*/
1620
    aoIter = paoSetKeys->begin();
1621
    while (aoIter != paoSetKeys->end())
1622
    {
1623
        printf("%s, ", (*aoIter).c_str()); /*ok*/
1624
        ++aoIter;
1625
    }
1626
    printf("\n"); /*ok*/
1627

1628
    delete paoGetKeys;
1629
    delete paoSetKeys;
1630
    paoGetKeys = nullptr;
1631
    paoSetKeys = nullptr;
1632
}
1633

1634
/************************************************************************/
1635
/*                       CPLAccessConfigOption()                        */
1636
/************************************************************************/
1637

1638
static void CPLAccessConfigOption(const char *pszKey, bool bGet)
1639
{
1640
    CPLMutexHolderD(&hRegisterConfigurationOptionMutex);
1641
    if (paoGetKeys == nullptr)
1642
    {
1643
        paoGetKeys = new std::set<CPLString>;
1644
        paoSetKeys = new std::set<CPLString>;
1645
        atexit(CPLShowAccessedOptions);
1646
    }
1647
    if (bGet)
1648
        paoGetKeys->insert(pszKey);
1649
    else
1650
        paoSetKeys->insert(pszKey);
1651
}
1652
#endif
1653

1654
/************************************************************************/
1655
/*                         CPLGetConfigOption()                         */
1656
/************************************************************************/
1657

1658
/**
1659
 * Get the value of a configuration option.
1660
 *
1661
 * The value is the value of a (key, value) option set with
1662
 * CPLSetConfigOption(), or CPLSetThreadLocalConfigOption() of the same
1663
 * thread. If the given option was no defined with
1664
 * CPLSetConfigOption(), it tries to find it in environment variables.
1665
 *
1666
 * Note: the string returned by CPLGetConfigOption() might be short-lived, and
1667
 * in particular it will become invalid after a call to CPLSetConfigOption()
1668
 * with the same key.
1669
 *
1670
 * To override temporary a potentially existing option with a new value, you
1671
 * can use the following snippet :
1672
 * \code{.cpp}
1673
 *     // backup old value
1674
 *     const char* pszOldValTmp = CPLGetConfigOption(pszKey, NULL);
1675
 *     char* pszOldVal = pszOldValTmp ? CPLStrdup(pszOldValTmp) : NULL;
1676
 *     // override with new value
1677
 *     CPLSetConfigOption(pszKey, pszNewVal);
1678
 *     // do something useful
1679
 *     // restore old value
1680
 *     CPLSetConfigOption(pszKey, pszOldVal);
1681
 *     CPLFree(pszOldVal);
1682
 * \endcode
1683
 *
1684
 * @param pszKey the key of the option to retrieve
1685
 * @param pszDefault a default value if the key does not match existing defined
1686
 *     options (may be NULL)
1687
 * @return the value associated to the key, or the default value if not found
1688
 *
1689
 * @see CPLSetConfigOption(), https://gdal.org/user/configoptions.html
1690
 */
1691
const char *CPL_STDCALL CPLGetConfigOption(const char *pszKey,
6,556,220✔
1692
                                           const char *pszDefault)
1693

1694
{
1695
    const char *pszResult = CPLGetThreadLocalConfigOption(pszKey, nullptr);
6,556,220✔
1696

1697
    if (pszResult == nullptr)
6,555,680✔
1698
    {
1699
        pszResult = CPLGetGlobalConfigOption(pszKey, nullptr);
6,516,870✔
1700
    }
1701

1702
    if (gbIgnoreEnvVariables)
6,557,310✔
1703
    {
1704
        const char *pszEnvVar = getenv(pszKey);
6✔
1705
        if (pszEnvVar != nullptr)
6✔
1706
        {
1707
            CPLDebug("CPL",
1✔
1708
                     "Ignoring environment variable %s=%s because of "
1709
                     "ignore-env-vars=yes setting in configuration file",
1710
                     pszKey, pszEnvVar);
1711
        }
1712
    }
1713
    else if (pszResult == nullptr)
6,557,300✔
1714
    {
1715
        pszResult = getenv(pszKey);
6,510,830✔
1716
    }
1717

1718
    if (pszResult == nullptr)
6,557,270✔
1719
        return pszDefault;
6,499,880✔
1720

1721
    return pszResult;
57,396✔
1722
}
1723

1724
/************************************************************************/
1725
/*                         CPLGetConfigOptions()                        */
1726
/************************************************************************/
1727

1728
/**
1729
 * Return the list of configuration options as KEY=VALUE pairs.
1730
 *
1731
 * The list is the one set through the CPLSetConfigOption() API.
1732
 *
1733
 * Options that through environment variables or with
1734
 * CPLSetThreadLocalConfigOption() will *not* be listed.
1735
 *
1736
 * @return a copy of the list, to be freed with CSLDestroy().
1737
 * @since GDAL 2.2
1738
 */
1739
char **CPLGetConfigOptions(void)
7✔
1740
{
1741
    CPLMutexHolderD(&hConfigMutex);
14✔
1742
    return CSLDuplicate(const_cast<char **>(g_papszConfigOptions));
14✔
1743
}
1744

1745
/************************************************************************/
1746
/*                         CPLSetConfigOptions()                        */
1747
/************************************************************************/
1748

1749
/**
1750
 * Replace the full list of configuration options with the passed list of
1751
 * KEY=VALUE pairs.
1752
 *
1753
 * This has the same effect of clearing the existing list, and setting
1754
 * individually each pair with the CPLSetConfigOption() API.
1755
 *
1756
 * This does not affect options set through environment variables or with
1757
 * CPLSetThreadLocalConfigOption().
1758
 *
1759
 * The passed list is copied by the function.
1760
 *
1761
 * @param papszConfigOptions the new list (or NULL).
1762
 *
1763
 * @since GDAL 2.2
1764
 */
1765
void CPLSetConfigOptions(const char *const *papszConfigOptions)
95✔
1766
{
1767
    CPLMutexHolderD(&hConfigMutex);
95✔
1768
    CSLDestroy(const_cast<char **>(g_papszConfigOptions));
95✔
1769
    g_papszConfigOptions = const_cast<volatile char **>(
95✔
1770
        CSLDuplicate(const_cast<char **>(papszConfigOptions)));
95✔
1771
}
95✔
1772

1773
/************************************************************************/
1774
/*                   CPLGetThreadLocalConfigOption()                    */
1775
/************************************************************************/
1776

1777
/** Same as CPLGetConfigOption() but only with options set with
1778
 * CPLSetThreadLocalConfigOption() */
1779
const char *CPL_STDCALL CPLGetThreadLocalConfigOption(const char *pszKey,
6,585,170✔
1780
                                                      const char *pszDefault)
1781

1782
{
1783
#ifdef DEBUG_CONFIG_OPTIONS
1784
    CPLAccessConfigOption(pszKey, TRUE);
1785
#endif
1786

1787
    const char *pszResult = nullptr;
6,585,170✔
1788

1789
    int bMemoryError = FALSE;
6,585,170✔
1790
    char **papszTLConfigOptions = reinterpret_cast<char **>(
1791
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
6,585,170✔
1792
    if (papszTLConfigOptions != nullptr)
6,584,460✔
1793
        pszResult = CSLFetchNameValue(papszTLConfigOptions, pszKey);
6,168,260✔
1794

1795
    if (pszResult == nullptr)
6,584,560✔
1796
        return pszDefault;
6,545,040✔
1797

1798
    return pszResult;
39,511✔
1799
}
1800

1801
/************************************************************************/
1802
/*                   CPLGetGlobalConfigOption()                         */
1803
/************************************************************************/
1804

1805
/** Same as CPLGetConfigOption() but excludes environment variables and
1806
 *  options set with CPLSetThreadLocalConfigOption().
1807
 *  This function should generally not be used by applications, which should
1808
 *  use CPLGetConfigOption() instead.
1809
 *  @since 3.8 */
1810
const char *CPL_STDCALL CPLGetGlobalConfigOption(const char *pszKey,
6,519,000✔
1811
                                                 const char *pszDefault)
1812
{
1813
#ifdef DEBUG_CONFIG_OPTIONS
1814
    CPLAccessConfigOption(pszKey, TRUE);
1815
#endif
1816

1817
    CPLMutexHolderD(&hConfigMutex);
13,039,400✔
1818

1819
    const char *pszResult =
1820
        CSLFetchNameValue(const_cast<char **>(g_papszConfigOptions), pszKey);
6,520,390✔
1821

1822
    if (pszResult == nullptr)
6,520,390✔
1823
        return pszDefault;
6,512,650✔
1824

1825
    return pszResult;
7,739✔
1826
}
1827

1828
/************************************************************************/
1829
/*                    CPLSubscribeToSetConfigOption()                   */
1830
/************************************************************************/
1831

1832
/**
1833
 * Install a callback that will be notified of calls to CPLSetConfigOption()/
1834
 * CPLSetThreadLocalConfigOption()
1835
 *
1836
 * @param pfnCallback Callback. Must not be NULL
1837
 * @param pUserData Callback user data. May be NULL.
1838
 * @return subscriber ID that can be used with CPLUnsubscribeToSetConfigOption()
1839
 * @since GDAL 3.7
1840
 */
1841

1842
int CPLSubscribeToSetConfigOption(CPLSetConfigOptionSubscriber pfnCallback,
1,200✔
1843
                                  void *pUserData)
1844
{
1845
    CPLMutexHolderD(&hConfigMutex);
2,400✔
1846
    for (int nId = 0;
1,205✔
1847
         nId < static_cast<int>(gSetConfigOptionSubscribers.size()); ++nId)
1,205✔
1848
    {
1849
        if (!gSetConfigOptionSubscribers[nId].first)
6✔
1850
        {
1851
            gSetConfigOptionSubscribers[nId].first = pfnCallback;
1✔
1852
            gSetConfigOptionSubscribers[nId].second = pUserData;
1✔
1853
            return nId;
1✔
1854
        }
1855
    }
1856
    int nId = static_cast<int>(gSetConfigOptionSubscribers.size());
1,199✔
1857
    gSetConfigOptionSubscribers.push_back(
1,199✔
1858
        std::pair<CPLSetConfigOptionSubscriber, void *>(pfnCallback,
1,199✔
1859
                                                        pUserData));
1860
    return nId;
1,199✔
1861
}
1862

1863
/************************************************************************/
1864
/*                  CPLUnsubscribeToSetConfigOption()                   */
1865
/************************************************************************/
1866

1867
/**
1868
 * Remove a subscriber installed with CPLSubscribeToSetConfigOption()
1869
 *
1870
 * @param nId Subscriber id returned by CPLSubscribeToSetConfigOption()
1871
 * @since GDAL 3.7
1872
 */
1873

1874
void CPLUnsubscribeToSetConfigOption(int nId)
4✔
1875
{
1876
    CPLMutexHolderD(&hConfigMutex);
8✔
1877
    if (nId == static_cast<int>(gSetConfigOptionSubscribers.size()) - 1)
4✔
1878
    {
1879
        gSetConfigOptionSubscribers.resize(gSetConfigOptionSubscribers.size() -
3✔
1880
                                           1);
1881
    }
1882
    else if (nId >= 0 &&
2✔
1883
             nId < static_cast<int>(gSetConfigOptionSubscribers.size()))
1✔
1884
    {
1885
        gSetConfigOptionSubscribers[nId].first = nullptr;
1✔
1886
    }
1887
}
4✔
1888

1889
/************************************************************************/
1890
/*                  NotifyOtherComponentsConfigOptionChanged()          */
1891
/************************************************************************/
1892

1893
static void NotifyOtherComponentsConfigOptionChanged(const char *pszKey,
67,780✔
1894
                                                     const char *pszValue,
1895
                                                     bool bThreadLocal)
1896
{
1897
    // When changing authentication parameters of virtual file systems,
1898
    // partially invalidate cached state about file availability.
1899
    if (STARTS_WITH_CI(pszKey, "AWS_") || STARTS_WITH_CI(pszKey, "GS_") ||
67,780✔
1900
        STARTS_WITH_CI(pszKey, "GOOGLE_") ||
65,131✔
1901
        STARTS_WITH_CI(pszKey, "GDAL_HTTP_HEADER_FILE") ||
65,089✔
1902
        STARTS_WITH_CI(pszKey, "AZURE_") ||
65,042✔
1903
        (STARTS_WITH_CI(pszKey, "SWIFT_") && !EQUAL(pszKey, "SWIFT_MAX_KEYS")))
64,820✔
1904
    {
1905
        VSICurlAuthParametersChanged();
3,064✔
1906
    }
1907

1908
    if (!gSetConfigOptionSubscribers.empty())
67,719✔
1909
    {
1910
        for (const auto &iter : gSetConfigOptionSubscribers)
134,277✔
1911
        {
1912
            if (iter.first)
67,227✔
1913
                iter.first(pszKey, pszValue, bThreadLocal, iter.second);
67,337✔
1914
        }
1915
    }
1916
}
67,634✔
1917

1918
/************************************************************************/
1919
/*                       CPLIsDebugEnabled()                            */
1920
/************************************************************************/
1921

1922
static int gnDebug = -1;
1923

1924
/** Returns whether CPL_DEBUG is enabled.
1925
 *
1926
 * @since 3.11
1927
 */
1928
bool CPLIsDebugEnabled()
73,330✔
1929
{
1930
    if (gnDebug < 0)
73,330✔
1931
    {
1932
        // Check that apszKnownConfigOptions is correctly sorted with
1933
        // STRCASECMP() criterion.
1934
        for (size_t i = 1; i < CPL_ARRAYSIZE(apszKnownConfigOptions); ++i)
443,271✔
1935
        {
1936
            if (STRCASECMP(apszKnownConfigOptions[i - 1],
442,854✔
1937
                           apszKnownConfigOptions[i]) >= 0)
1938
            {
1939
                CPLError(CE_Failure, CPLE_AppDefined,
×
1940
                         "ERROR: apszKnownConfigOptions[] isn't correctly "
1941
                         "sorted: %s >= %s",
1942
                         apszKnownConfigOptions[i - 1],
×
1943
                         apszKnownConfigOptions[i]);
×
1944
            }
1945
        }
1946
        gnDebug = CPLTestBool(CPLGetConfigOption("CPL_DEBUG", "OFF"));
417✔
1947
    }
1948

1949
    return gnDebug != 0;
73,313✔
1950
}
1951

1952
/************************************************************************/
1953
/*                       CPLDeclareKnownConfigOption()                  */
1954
/************************************************************************/
1955

1956
static std::mutex goMutexDeclaredKnownConfigOptions;
1957
static std::set<CPLString> goSetKnownConfigOptions;
1958

1959
/** Declare that the specified configuration option is known.
1960
 *
1961
 * This is useful to avoid a warning to be emitted on unknown configuration
1962
 * options when CPL_DEBUG is enabled.
1963
 *
1964
 * @param pszKey Name of the configuration option to declare.
1965
 * @param pszDefinition Unused for now. Must be set to nullptr.
1966
 * @since 3.11
1967
 */
1968
void CPLDeclareKnownConfigOption(const char *pszKey,
1✔
1969
                                 [[maybe_unused]] const char *pszDefinition)
1970
{
1971
    std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
1✔
1972
    goSetKnownConfigOptions.insert(CPLString(pszKey).toupper());
1✔
1973
}
1✔
1974

1975
/************************************************************************/
1976
/*                       CPLGetKnownConfigOptions()                     */
1977
/************************************************************************/
1978

1979
/** Return the list of known configuration options.
1980
 *
1981
 * Must be freed with CSLDestroy().
1982
 * @since 3.11
1983
 */
1984
char **CPLGetKnownConfigOptions()
4✔
1985
{
1986
    std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
8✔
1987
    CPLStringList aosList;
8✔
1988
    for (const char *pszKey : apszKnownConfigOptions)
4,256✔
1989
        aosList.AddString(pszKey);
4,252✔
1990
    for (const auto &osKey : goSetKnownConfigOptions)
5✔
1991
        aosList.AddString(osKey);
1✔
1992
    return aosList.StealList();
8✔
1993
}
1994

1995
/************************************************************************/
1996
/*           CPLSetConfigOptionDetectUnknownConfigOption()              */
1997
/************************************************************************/
1998

1999
static void CPLSetConfigOptionDetectUnknownConfigOption(const char *pszKey,
67,888✔
2000
                                                        const char *pszValue)
2001
{
2002
    if (EQUAL(pszKey, "CPL_DEBUG"))
67,888✔
2003
    {
2004
        gnDebug = pszValue ? CPLTestBool(pszValue) : false;
98✔
2005
    }
2006
    else if (CPLIsDebugEnabled())
67,790✔
2007
    {
2008
        if (!std::binary_search(std::begin(apszKnownConfigOptions),
72✔
2009
                                std::end(apszKnownConfigOptions), pszKey,
2010
                                [](const char *a, const char *b)
794✔
2011
                                { return STRCASECMP(a, b) < 0; }))
794✔
2012
        {
2013
            bool bFound;
2014
            {
2015
                std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
4✔
2016
                bFound = cpl::contains(goSetKnownConfigOptions,
8✔
2017
                                       CPLString(pszKey).toupper());
4✔
2018
            }
2019
            if (!bFound)
4✔
2020
            {
2021
                const char *pszOldValue = CPLGetConfigOption(pszKey, nullptr);
2✔
2022
                if (!((!pszValue && !pszOldValue) ||
2✔
2023
                      (pszValue && pszOldValue &&
1✔
2024
                       EQUAL(pszValue, pszOldValue))))
×
2025
                {
2026
                    CPLError(CE_Warning, CPLE_AppDefined,
2✔
2027
                             "Unknown configuration option '%s'.", pszKey);
2028
                }
2029
            }
2030
        }
2031
    }
2032
}
67,797✔
2033

2034
/************************************************************************/
2035
/*                         CPLSetConfigOption()                         */
2036
/************************************************************************/
2037

2038
/**
2039
 * Set a configuration option for GDAL/OGR use.
2040
 *
2041
 * Those options are defined as a (key, value) couple. The value corresponding
2042
 * to a key can be got later with the CPLGetConfigOption() method.
2043
 *
2044
 * This mechanism is similar to environment variables, but options set with
2045
 * CPLSetConfigOption() overrides, for CPLGetConfigOption() point of view,
2046
 * values defined in the environment.
2047
 *
2048
 * If CPLSetConfigOption() is called several times with the same key, the
2049
 * value provided during the last call will be used.
2050
 *
2051
 * Options can also be passed on the command line of most GDAL utilities
2052
 * with '\--config KEY VALUE' (or '\--config KEY=VALUE' since GDAL 3.10).
2053
 * For example, ogrinfo \--config CPL_DEBUG ON ~/data/test/point.shp
2054
 *
2055
 * This function can also be used to clear a setting by passing NULL as the
2056
 * value (note: passing NULL will not unset an existing environment variable;
2057
 * it will just unset a value previously set by CPLSetConfigOption()).
2058
 *
2059
 * Starting with GDAL 3.11, if CPL_DEBUG is enabled prior to this call, and
2060
 * CPLSetConfigOption() is called with a key that is neither a known
2061
 * configuration option of GDAL itself, or one that has been declared with
2062
 * CPLDeclareKnownConfigOption(), a warning will be emitted.
2063
 *
2064
 * @param pszKey the key of the option
2065
 * @param pszValue the value of the option, or NULL to clear a setting.
2066
 *
2067
 * @see https://gdal.org/user/configoptions.html
2068
 */
2069
void CPL_STDCALL CPLSetConfigOption(const char *pszKey, const char *pszValue)
4,729✔
2070

2071
{
2072
#ifdef DEBUG_CONFIG_OPTIONS
2073
    CPLAccessConfigOption(pszKey, FALSE);
2074
#endif
2075
    CPLMutexHolderD(&hConfigMutex);
9,458✔
2076

2077
#ifdef OGRAPISPY_ENABLED
2078
    OGRAPISPYCPLSetConfigOption(pszKey, pszValue);
4,729✔
2079
#endif
2080

2081
    CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
4,729✔
2082

2083
    g_papszConfigOptions = const_cast<volatile char **>(CSLSetNameValue(
4,729✔
2084
        const_cast<char **>(g_papszConfigOptions), pszKey, pszValue));
2085

2086
    NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
4,729✔
2087
                                             /*bTheadLocal=*/false);
2088
}
4,729✔
2089

2090
/************************************************************************/
2091
/*                   CPLSetThreadLocalTLSFreeFunc()                     */
2092
/************************************************************************/
2093

2094
/* non-stdcall wrapper function for CSLDestroy() (#5590) */
2095
static void CPLSetThreadLocalTLSFreeFunc(void *pData)
101✔
2096
{
2097
    CSLDestroy(reinterpret_cast<char **>(pData));
101✔
2098
}
101✔
2099

2100
/************************************************************************/
2101
/*                   CPLSetThreadLocalConfigOption()                    */
2102
/************************************************************************/
2103

2104
/**
2105
 * Set a configuration option for GDAL/OGR use.
2106
 *
2107
 * Those options are defined as a (key, value) couple. The value corresponding
2108
 * to a key can be got later with the CPLGetConfigOption() method.
2109
 *
2110
 * This function sets the configuration option that only applies in the
2111
 * current thread, as opposed to CPLSetConfigOption() which sets an option
2112
 * that applies on all threads. CPLSetThreadLocalConfigOption() will override
2113
 * the effect of CPLSetConfigOption) for the current thread.
2114
 *
2115
 * This function can also be used to clear a setting by passing NULL as the
2116
 * value (note: passing NULL will not unset an existing environment variable or
2117
 * a value set through CPLSetConfigOption();
2118
 * it will just unset a value previously set by
2119
 * CPLSetThreadLocalConfigOption()).
2120
 *
2121
 * @param pszKey the key of the option
2122
 * @param pszValue the value of the option, or NULL to clear a setting.
2123
 */
2124

2125
void CPL_STDCALL CPLSetThreadLocalConfigOption(const char *pszKey,
63,188✔
2126
                                               const char *pszValue)
2127

2128
{
2129
#ifdef DEBUG_CONFIG_OPTIONS
2130
    CPLAccessConfigOption(pszKey, FALSE);
2131
#endif
2132

2133
#ifdef OGRAPISPY_ENABLED
2134
    OGRAPISPYCPLSetThreadLocalConfigOption(pszKey, pszValue);
63,188✔
2135
#endif
2136

2137
    int bMemoryError = FALSE;
63,154✔
2138
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2139
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
63,154✔
2140
    if (bMemoryError)
63,133✔
2141
        return;
×
2142

2143
    CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
63,133✔
2144

2145
    papszTLConfigOptions =
2146
        CSLSetNameValue(papszTLConfigOptions, pszKey, pszValue);
63,071✔
2147

2148
    CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
63,053✔
2149
                          CPLSetThreadLocalTLSFreeFunc);
2150

2151
    NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
62,939✔
2152
                                             /*bTheadLocal=*/true);
2153
}
2154

2155
/************************************************************************/
2156
/*                   CPLGetThreadLocalConfigOptions()                   */
2157
/************************************************************************/
2158

2159
/**
2160
 * Return the list of thread local configuration options as KEY=VALUE pairs.
2161
 *
2162
 * Options that through environment variables or with
2163
 * CPLSetConfigOption() will *not* be listed.
2164
 *
2165
 * @return a copy of the list, to be freed with CSLDestroy().
2166
 * @since GDAL 2.2
2167
 */
2168
char **CPLGetThreadLocalConfigOptions(void)
726,977✔
2169
{
2170
    int bMemoryError = FALSE;
726,977✔
2171
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2172
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
726,977✔
2173
    if (bMemoryError)
710,535✔
2174
        return nullptr;
×
2175
    return CSLDuplicate(papszTLConfigOptions);
710,535✔
2176
}
2177

2178
/************************************************************************/
2179
/*                   CPLSetThreadLocalConfigOptions()                   */
2180
/************************************************************************/
2181

2182
/**
2183
 * Replace the full list of thread local configuration options with the
2184
 * passed list of KEY=VALUE pairs.
2185
 *
2186
 * This has the same effect of clearing the existing list, and setting
2187
 * individually each pair with the CPLSetThreadLocalConfigOption() API.
2188
 *
2189
 * This does not affect options set through environment variables or with
2190
 * CPLSetConfigOption().
2191
 *
2192
 * The passed list is copied by the function.
2193
 *
2194
 * @param papszConfigOptions the new list (or NULL).
2195
 *
2196
 * @since GDAL 2.2
2197
 */
2198
void CPLSetThreadLocalConfigOptions(const char *const *papszConfigOptions)
1,465,230✔
2199
{
2200
    int bMemoryError = FALSE;
1,465,230✔
2201
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2202
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
1,465,230✔
2203
    if (bMemoryError)
1,435,490✔
2204
        return;
×
2205
    CSLDestroy(papszTLConfigOptions);
1,435,490✔
2206
    papszTLConfigOptions =
2207
        CSLDuplicate(const_cast<char **>(papszConfigOptions));
1,439,950✔
2208
    CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
1,435,570✔
2209
                          CPLSetThreadLocalTLSFreeFunc);
2210
}
2211

2212
/************************************************************************/
2213
/*                           CPLFreeConfig()                            */
2214
/************************************************************************/
2215

2216
void CPL_STDCALL CPLFreeConfig()
1,540✔
2217

2218
{
2219
    {
2220
        CPLMutexHolderD(&hConfigMutex);
3,080✔
2221

2222
        CSLDestroy(const_cast<char **>(g_papszConfigOptions));
1,540✔
2223
        g_papszConfigOptions = nullptr;
1,540✔
2224

2225
        int bMemoryError = FALSE;
1,540✔
2226
        char **papszTLConfigOptions = reinterpret_cast<char **>(
2227
            CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
1,540✔
2228
        if (papszTLConfigOptions != nullptr)
1,540✔
2229
        {
2230
            CSLDestroy(papszTLConfigOptions);
203✔
2231
            CPLSetTLS(CTLS_CONFIGOPTIONS, nullptr, FALSE);
203✔
2232
        }
2233
    }
2234
    CPLDestroyMutex(hConfigMutex);
1,540✔
2235
    hConfigMutex = nullptr;
1,540✔
2236
}
1,540✔
2237

2238
/************************************************************************/
2239
/*                    CPLLoadConfigOptionsFromFile()                    */
2240
/************************************************************************/
2241

2242
/** Load configuration from a given configuration file.
2243

2244
A configuration file is a text file in a .ini style format, that lists
2245
configuration options and their values.
2246
Lines starting with # are comment lines.
2247

2248
Example:
2249
\verbatim
2250
[configoptions]
2251
# set BAR as the value of configuration option FOO
2252
FOO=BAR
2253
\endverbatim
2254

2255
Starting with GDAL 3.5, a configuration file can also contain credentials
2256
(or more generally options related to a virtual file system) for a given path
2257
prefix, that can also be set with VSISetPathSpecificOption(). Credentials should
2258
be put under a [credentials] section, and for each path prefix, under a relative
2259
subsection whose name starts with "[." (e.g. "[.some_arbitrary_name]"), and
2260
whose first key is "path".
2261

2262
Example:
2263
\verbatim
2264
[credentials]
2265

2266
[.private_bucket]
2267
path=/vsis3/my_private_bucket
2268
AWS_SECRET_ACCESS_KEY=...
2269
AWS_ACCESS_KEY_ID=...
2270

2271
[.sentinel_s2_l1c]
2272
path=/vsis3/sentinel-s2-l1c
2273
AWS_REQUEST_PAYER=requester
2274
\endverbatim
2275

2276
Starting with GDAL 3.6, a leading [directives] section might be added with
2277
a "ignore-env-vars=yes" setting to indicate that, starting with that point,
2278
all environment variables should be ignored, and only configuration options
2279
defined in the [configoptions] sections or through the CPLSetConfigOption() /
2280
CPLSetThreadLocalConfigOption() functions should be taken into account.
2281

2282
This function is typically called by CPLLoadConfigOptionsFromPredefinedFiles()
2283

2284
@param pszFilename File where to load configuration from.
2285
@param bOverrideEnvVars Whether configuration options from the configuration
2286
                        file should override environment variables.
2287
@since GDAL 3.3
2288
 */
2289
void CPLLoadConfigOptionsFromFile(const char *pszFilename, int bOverrideEnvVars)
3,272✔
2290
{
2291
    VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
3,272✔
2292
    if (fp == nullptr)
3,272✔
2293
        return;
3,263✔
2294
    CPLDebug("CPL", "Loading configuration from %s", pszFilename);
9✔
2295
    const char *pszLine;
2296
    enum class Section
2297
    {
2298
        NONE,
2299
        GENERAL,
2300
        CONFIG_OPTIONS,
2301
        CREDENTIALS,
2302
    };
2303
    Section eCurrentSection = Section::NONE;
9✔
2304
    bool bInSubsection = false;
9✔
2305
    std::string osPath;
18✔
2306
    int nSectionCounter = 0;
9✔
2307

2308
    const auto IsSpaceOnly = [](const char *pszStr)
56✔
2309
    {
2310
        for (; *pszStr; ++pszStr)
56✔
2311
        {
2312
            if (!isspace(static_cast<unsigned char>(*pszStr)))
47✔
2313
                return false;
41✔
2314
        }
2315
        return true;
9✔
2316
    };
2317

2318
    while ((pszLine = CPLReadLine2L(fp, -1, nullptr)) != nullptr)
59✔
2319
    {
2320
        if (IsSpaceOnly(pszLine))
50✔
2321
        {
2322
            // Blank line
2323
        }
2324
        else if (pszLine[0] == '#')
41✔
2325
        {
2326
            // Comment line
2327
        }
2328
        else if (strcmp(pszLine, "[configoptions]") == 0)
35✔
2329
        {
2330
            nSectionCounter++;
6✔
2331
            eCurrentSection = Section::CONFIG_OPTIONS;
6✔
2332
        }
2333
        else if (strcmp(pszLine, "[credentials]") == 0)
29✔
2334
        {
2335
            nSectionCounter++;
4✔
2336
            eCurrentSection = Section::CREDENTIALS;
4✔
2337
            bInSubsection = false;
4✔
2338
            osPath.clear();
4✔
2339
        }
2340
        else if (strcmp(pszLine, "[directives]") == 0)
25✔
2341
        {
2342
            nSectionCounter++;
2✔
2343
            if (nSectionCounter != 1)
2✔
2344
            {
2345
                CPLError(CE_Warning, CPLE_AppDefined,
×
2346
                         "The [directives] section should be the first one in "
2347
                         "the file, otherwise some its settings might not be "
2348
                         "used correctly.");
2349
            }
2350
            eCurrentSection = Section::GENERAL;
2✔
2351
        }
2352
        else if (eCurrentSection == Section::GENERAL)
23✔
2353
        {
2354
            char *pszKey = nullptr;
2✔
2355
            const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
2✔
2356
            if (pszKey && pszValue)
2✔
2357
            {
2358
                if (strcmp(pszKey, "ignore-env-vars") == 0)
2✔
2359
                {
2360
                    gbIgnoreEnvVariables = CPLTestBool(pszValue);
2✔
2361
                }
2362
                else
2363
                {
2364
                    CPLError(CE_Warning, CPLE_AppDefined,
×
2365
                             "Ignoring %s line in [directives] section",
2366
                             pszLine);
2367
                }
2368
            }
2369
            CPLFree(pszKey);
2✔
2370
        }
2371
        else if (eCurrentSection == Section::CREDENTIALS)
21✔
2372
        {
2373
            if (strncmp(pszLine, "[.", 2) == 0)
15✔
2374
            {
2375
                bInSubsection = true;
4✔
2376
                osPath.clear();
4✔
2377
            }
2378
            else if (bInSubsection)
11✔
2379
            {
2380
                char *pszKey = nullptr;
10✔
2381
                const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
10✔
2382
                if (pszKey && pszValue)
10✔
2383
                {
2384
                    if (strcmp(pszKey, "path") == 0)
10✔
2385
                    {
2386
                        if (!osPath.empty())
4✔
2387
                        {
2388
                            CPLError(
1✔
2389
                                CE_Warning, CPLE_AppDefined,
2390
                                "Duplicated 'path' key in the same subsection. "
2391
                                "Ignoring %s=%s",
2392
                                pszKey, pszValue);
2393
                        }
2394
                        else
2395
                        {
2396
                            osPath = pszValue;
3✔
2397
                        }
2398
                    }
2399
                    else if (osPath.empty())
6✔
2400
                    {
2401
                        CPLError(CE_Warning, CPLE_AppDefined,
1✔
2402
                                 "First entry in a credentials subsection "
2403
                                 "should be 'path'.");
2404
                    }
2405
                    else
2406
                    {
2407
                        VSISetPathSpecificOption(osPath.c_str(), pszKey,
5✔
2408
                                                 pszValue);
2409
                    }
2410
                }
2411
                CPLFree(pszKey);
10✔
2412
            }
2413
            else if (pszLine[0] == '[')
1✔
2414
            {
2415
                eCurrentSection = Section::NONE;
×
2416
            }
2417
            else
2418
            {
2419
                CPLError(CE_Warning, CPLE_AppDefined,
1✔
2420
                         "Ignoring content in [credential] section that is not "
2421
                         "in a [.xxxxx] subsection");
2422
            }
2423
        }
2424
        else if (pszLine[0] == '[')
6✔
2425
        {
2426
            eCurrentSection = Section::NONE;
×
2427
        }
2428
        else if (eCurrentSection == Section::CONFIG_OPTIONS)
6✔
2429
        {
2430
            char *pszKey = nullptr;
6✔
2431
            const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
6✔
2432
            if (pszKey && pszValue)
6✔
2433
            {
2434
                if (bOverrideEnvVars || gbIgnoreEnvVariables ||
11✔
2435
                    getenv(pszKey) == nullptr)
5✔
2436
                {
2437
                    CPLDebugOnly("CPL", "Setting configuration option %s=%s",
5✔
2438
                                 pszKey, pszValue);
2439
                    CPLSetConfigOption(pszKey, pszValue);
5✔
2440
                }
2441
                else
2442
                {
2443
                    CPLDebug("CPL",
1✔
2444
                             "Ignoring configuration option %s=%s from "
2445
                             "configuration file as it is already set "
2446
                             "as an environment variable",
2447
                             pszKey, pszValue);
2448
                }
2449
            }
2450
            CPLFree(pszKey);
6✔
2451
        }
2452
    }
2453
    VSIFCloseL(fp);
9✔
2454
}
2455

2456
/************************************************************************/
2457
/*                CPLLoadConfigOptionsFromPredefinedFiles()             */
2458
/************************************************************************/
2459

2460
/** Load configuration from a set of predefined files.
2461
 *
2462
 * If the environment variable (or configuration option) GDAL_CONFIG_FILE is
2463
 * set, then CPLLoadConfigOptionsFromFile() will be called with the value of
2464
 * this configuration option as the file location.
2465
 *
2466
 * Otherwise, for Unix builds, CPLLoadConfigOptionsFromFile() will be called
2467
 * with ${sysconfdir}/gdal/gdalrc first where ${sysconfdir} evaluates
2468
 * to ${prefix}/etc, unless the \--sysconfdir switch of configure has been
2469
 * invoked.
2470
 *
2471
 * Then CPLLoadConfigOptionsFromFile() will be called with ${HOME}/.gdal/gdalrc
2472
 * on Unix builds (potentially overriding what was loaded with the sysconfdir)
2473
 * or ${USERPROFILE}/.gdal/gdalrc on Windows builds.
2474
 *
2475
 * CPLLoadConfigOptionsFromFile() will be called with bOverrideEnvVars = false,
2476
 * that is the value of environment variables previously set will be used
2477
 * instead of the value set in the configuration files (unless the configuration
2478
 * file contains a leading [directives] section with a "ignore-env-vars=yes"
2479
 * setting).
2480
 *
2481
 * This function is automatically called by GDALDriverManager() constructor
2482
 *
2483
 * @since GDAL 3.3
2484
 */
2485
void CPLLoadConfigOptionsFromPredefinedFiles()
1,633✔
2486
{
2487
    const char *pszFile = CPLGetConfigOption("GDAL_CONFIG_FILE", nullptr);
1,633✔
2488
    if (pszFile != nullptr)
1,633✔
2489
    {
2490
        CPLLoadConfigOptionsFromFile(pszFile, false);
2✔
2491
    }
2492
    else
2493
    {
2494
#ifdef SYSCONFDIR
2495
        CPLLoadConfigOptionsFromFile(
1,631✔
2496
            CPLFormFilenameSafe(
3,262✔
2497
                CPLFormFilenameSafe(SYSCONFDIR, "gdal", nullptr).c_str(),
3,262✔
2498
                "gdalrc", nullptr)
2499
                .c_str(),
2500
            false);
2501
#endif
2502

2503
#ifdef _WIN32
2504
        const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
2505
#else
2506
        const char *pszHome = CPLGetConfigOption("HOME", nullptr);
1,631✔
2507
#endif
2508
        if (pszHome != nullptr)
1,631✔
2509
        {
2510
            CPLLoadConfigOptionsFromFile(
1,631✔
2511
                CPLFormFilenameSafe(
3,262✔
2512
                    CPLFormFilenameSafe(pszHome, ".gdal", nullptr).c_str(),
3,262✔
2513
                    "gdalrc", nullptr)
2514
                    .c_str(),
2515
                false);
2516
        }
2517
    }
2518
}
1,633✔
2519

2520
/************************************************************************/
2521
/*                              CPLStat()                               */
2522
/************************************************************************/
2523

2524
/** Same as VSIStat() except it works on "C:" as if it were "C:\". */
2525

2526
int CPLStat(const char *pszPath, VSIStatBuf *psStatBuf)
×
2527

2528
{
2529
    if (strlen(pszPath) == 2 && pszPath[1] == ':')
×
2530
    {
2531
        char szAltPath[4] = {pszPath[0], pszPath[1], '\\', '\0'};
×
2532
        return VSIStat(szAltPath, psStatBuf);
×
2533
    }
2534

2535
    return VSIStat(pszPath, psStatBuf);
×
2536
}
2537

2538
/************************************************************************/
2539
/*                            proj_strtod()                             */
2540
/************************************************************************/
2541
static double proj_strtod(char *nptr, char **endptr)
18✔
2542

2543
{
2544
    char c = '\0';
18✔
2545
    char *cp = nptr;
18✔
2546

2547
    // Scan for characters which cause problems with VC++ strtod().
2548
    while ((c = *cp) != '\0')
84✔
2549
    {
2550
        if (c == 'd' || c == 'D')
72✔
2551
        {
2552
            // Found one, so NUL it out, call strtod(),
2553
            // then restore it and return.
2554
            *cp = '\0';
6✔
2555
            const double result = CPLStrtod(nptr, endptr);
6✔
2556
            *cp = c;
6✔
2557
            return result;
6✔
2558
        }
2559
        ++cp;
66✔
2560
    }
2561

2562
    // No offending characters, just handle normally.
2563

2564
    return CPLStrtod(nptr, endptr);
12✔
2565
}
2566

2567
/************************************************************************/
2568
/*                            CPLDMSToDec()                             */
2569
/************************************************************************/
2570

2571
static const char *sym = "NnEeSsWw";
2572
constexpr double vm[] = {1.0, 0.0166666666667, 0.00027777778};
2573

2574
/** CPLDMSToDec */
2575
double CPLDMSToDec(const char *is)
6✔
2576

2577
{
2578
    // Copy string into work space.
2579
    while (isspace(static_cast<unsigned char>(*is)))
6✔
2580
        ++is;
×
2581

2582
    const char *p = is;
6✔
2583
    char work[64] = {};
6✔
2584
    char *s = work;
6✔
2585
    int n = sizeof(work);
6✔
2586
    for (; isgraph(*p) && --n;)
60✔
2587
        *s++ = *p++;
54✔
2588
    *s = '\0';
6✔
2589
    // It is possible that a really odd input (like lots of leading
2590
    // zeros) could be truncated in copying into work.  But...
2591
    s = work;
6✔
2592
    int sign = *s;
6✔
2593

2594
    if (sign == '+' || sign == '-')
6✔
2595
        s++;
×
2596
    else
2597
        sign = '+';
6✔
2598

2599
    int nl = 0;
6✔
2600
    double v = 0.0;
6✔
2601
    for (; nl < 3; nl = n + 1)
24✔
2602
    {
2603
        if (!(isdigit(static_cast<unsigned char>(*s)) || *s == '.'))
18✔
2604
            break;
×
2605
        const double tv = proj_strtod(s, &s);
18✔
2606
        if (tv == HUGE_VAL)
18✔
2607
            return tv;
×
2608
        switch (*s)
18✔
2609
        {
2610
            case 'D':
6✔
2611
            case 'd':
2612
                n = 0;
6✔
2613
                break;
6✔
2614
            case '\'':
6✔
2615
                n = 1;
6✔
2616
                break;
6✔
2617
            case '"':
6✔
2618
                n = 2;
6✔
2619
                break;
6✔
2620
            case 'r':
×
2621
            case 'R':
2622
                if (nl)
×
2623
                {
2624
                    return 0.0;
×
2625
                }
2626
                ++s;
×
2627
                v = tv;
×
2628
                goto skip;
×
2629
            default:
×
2630
                v += tv * vm[nl];
×
2631
            skip:
×
2632
                n = 4;
×
2633
                continue;
×
2634
        }
2635
        if (n < nl)
18✔
2636
        {
2637
            return 0.0;
×
2638
        }
2639
        v += tv * vm[n];
18✔
2640
        ++s;
18✔
2641
    }
2642
    // Postfix sign.
2643
    if (*s && ((p = strchr(sym, *s))) != nullptr)
6✔
2644
    {
2645
        sign = (p - sym) >= 4 ? '-' : '+';
×
2646
        ++s;
×
2647
    }
2648
    if (sign == '-')
6✔
2649
        v = -v;
×
2650

2651
    return v;
6✔
2652
}
2653

2654
/************************************************************************/
2655
/*                            CPLDecToDMS()                             */
2656
/************************************************************************/
2657

2658
/** Translate a decimal degrees value to a DMS string with hemisphere. */
2659

2660
const char *CPLDecToDMS(double dfAngle, const char *pszAxis, int nPrecision)
550✔
2661

2662
{
2663
    VALIDATE_POINTER1(pszAxis, "CPLDecToDMS", "");
550✔
2664

2665
    if (std::isnan(dfAngle))
550✔
2666
        return "Invalid angle";
×
2667

2668
    const double dfEpsilon = (0.5 / 3600.0) * pow(0.1, nPrecision);
550✔
2669
    const double dfABSAngle = std::abs(dfAngle) + dfEpsilon;
550✔
2670
    if (dfABSAngle > 361.0)
550✔
2671
    {
2672
        return "Invalid angle";
×
2673
    }
2674

2675
    const int nDegrees = static_cast<int>(dfABSAngle);
550✔
2676
    const int nMinutes = static_cast<int>((dfABSAngle - nDegrees) * 60);
550✔
2677
    double dfSeconds = dfABSAngle * 3600 - nDegrees * 3600 - nMinutes * 60;
550✔
2678

2679
    if (dfSeconds > dfEpsilon * 3600.0)
550✔
2680
        dfSeconds -= dfEpsilon * 3600.0;
544✔
2681

2682
    const char *pszHemisphere = nullptr;
550✔
2683
    if (EQUAL(pszAxis, "Long") && dfAngle < 0.0)
550✔
2684
        pszHemisphere = "W";
238✔
2685
    else if (EQUAL(pszAxis, "Long"))
312✔
2686
        pszHemisphere = "E";
37✔
2687
    else if (dfAngle < 0.0)
275✔
2688
        pszHemisphere = "S";
22✔
2689
    else
2690
        pszHemisphere = "N";
253✔
2691

2692
    char szFormat[30] = {};
550✔
2693
    CPLsnprintf(szFormat, sizeof(szFormat), "%%3dd%%2d\'%%%d.%df\"%s",
550✔
2694
                nPrecision + 3, nPrecision, pszHemisphere);
2695

2696
    static CPL_THREADLOCAL char szBuffer[50] = {};
2697
    CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, nDegrees, nMinutes,
550✔
2698
                dfSeconds);
2699

2700
    return szBuffer;
550✔
2701
}
2702

2703
/************************************************************************/
2704
/*                         CPLPackedDMSToDec()                          */
2705
/************************************************************************/
2706

2707
/**
2708
 * Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
2709
 *
2710
 * This function converts a packed DMS angle to seconds. The standard
2711
 * packed DMS format is:
2712
 *
2713
 *  degrees * 1000000 + minutes * 1000 + seconds
2714
 *
2715
 * Example:     angle = 120025045.25 yields
2716
 *              deg = 120
2717
 *              min = 25
2718
 *              sec = 45.25
2719
 *
2720
 * The algorithm used for the conversion is as follows:
2721
 *
2722
 * 1.  The absolute value of the angle is used.
2723
 *
2724
 * 2.  The degrees are separated out:
2725
 *     deg = angle/1000000                    (fractional portion truncated)
2726
 *
2727
 * 3.  The minutes are separated out:
2728
 *     min = (angle - deg * 1000000) / 1000   (fractional portion truncated)
2729
 *
2730
 * 4.  The seconds are then computed:
2731
 *     sec = angle - deg * 1000000 - min * 1000
2732
 *
2733
 * 5.  The total angle in seconds is computed:
2734
 *     sec = deg * 3600.0 + min * 60.0 + sec
2735
 *
2736
 * 6.  The sign of sec is set to that of the input angle.
2737
 *
2738
 * Packed DMS values used by the USGS GCTP package and probably by other
2739
 * software.
2740
 *
2741
 * NOTE: This code does not validate input value. If you give the wrong
2742
 * value, you will get the wrong result.
2743
 *
2744
 * @param dfPacked Angle in packed DMS format.
2745
 *
2746
 * @return Angle in decimal degrees.
2747
 *
2748
 */
2749

2750
double CPLPackedDMSToDec(double dfPacked)
34✔
2751
{
2752
    const double dfSign = dfPacked < 0.0 ? -1 : 1;
34✔
2753

2754
    double dfSeconds = std::abs(dfPacked);
34✔
2755
    double dfDegrees = floor(dfSeconds / 1000000.0);
34✔
2756
    dfSeconds -= dfDegrees * 1000000.0;
34✔
2757
    const double dfMinutes = floor(dfSeconds / 1000.0);
34✔
2758
    dfSeconds -= dfMinutes * 1000.0;
34✔
2759
    dfSeconds = dfSign * (dfDegrees * 3600.0 + dfMinutes * 60.0 + dfSeconds);
34✔
2760
    dfDegrees = dfSeconds / 3600.0;
34✔
2761

2762
    return dfDegrees;
34✔
2763
}
2764

2765
/************************************************************************/
2766
/*                         CPLDecToPackedDMS()                          */
2767
/************************************************************************/
2768
/**
2769
 * Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
2770
 *
2771
 * This function converts a value, specified in decimal degrees into
2772
 * packed DMS angle. The standard packed DMS format is:
2773
 *
2774
 *  degrees * 1000000 + minutes * 1000 + seconds
2775
 *
2776
 * See also CPLPackedDMSToDec().
2777
 *
2778
 * @param dfDec Angle in decimal degrees.
2779
 *
2780
 * @return Angle in packed DMS format.
2781
 *
2782
 */
2783

2784
double CPLDecToPackedDMS(double dfDec)
8✔
2785
{
2786
    const double dfSign = dfDec < 0.0 ? -1 : 1;
8✔
2787

2788
    dfDec = std::abs(dfDec);
8✔
2789
    const double dfDegrees = floor(dfDec);
8✔
2790
    const double dfMinutes = floor((dfDec - dfDegrees) * 60.0);
8✔
2791
    const double dfSeconds = (dfDec - dfDegrees) * 3600.0 - dfMinutes * 60.0;
8✔
2792

2793
    return dfSign * (dfDegrees * 1000000.0 + dfMinutes * 1000.0 + dfSeconds);
8✔
2794
}
2795

2796
/************************************************************************/
2797
/*                         CPLStringToComplex()                         */
2798
/************************************************************************/
2799

2800
/** Fetch the real and imaginary part of a serialized complex number */
2801
CPLErr CPL_DLL CPLStringToComplex(const char *pszString, double *pdfReal,
2,423✔
2802
                                  double *pdfImag)
2803

2804
{
2805
    while (*pszString == ' ')
2,423✔
2806
        pszString++;
1✔
2807

2808
    char *end;
2809
    *pdfReal = CPLStrtod(pszString, &end);
2,422✔
2810

2811
    int iPlus = -1;
2,422✔
2812
    int iImagEnd = -1;
2,422✔
2813

2814
    if (pszString == end)
2,422✔
2815
    {
2816
        goto error;
5✔
2817
    }
2818

2819
    *pdfImag = 0.0;
2,417✔
2820

2821
    for (int i = static_cast<int>(end - pszString);
2,471✔
2822
         i < 100 && pszString[i] != '\0' && pszString[i] != ' '; i++)
2,471✔
2823
    {
2824
        if (pszString[i] == '+')
56✔
2825
        {
2826
            if (iPlus != -1)
8✔
2827
                goto error;
×
2828
            iPlus = i;
8✔
2829
        }
2830
        if (pszString[i] == '-')
56✔
2831
        {
2832
            if (iPlus != -1)
2✔
2833
                goto error;
1✔
2834
            iPlus = i;
1✔
2835
        }
2836
        if (pszString[i] == 'i')
55✔
2837
        {
2838
            if (iPlus == -1)
9✔
2839
                goto error;
1✔
2840
            iImagEnd = i;
8✔
2841
        }
2842
    }
2843

2844
    // If we have a "+" or "-" we must also have an "i"
2845
    if ((iPlus == -1) != (iImagEnd == -1))
2,415✔
2846
    {
2847
        goto error;
1✔
2848
    }
2849

2850
    // Parse imaginary component, if any
2851
    if (iPlus > -1)
2,414✔
2852
    {
2853
        *pdfImag = CPLStrtod(pszString + iPlus, &end);
7✔
2854
    }
2855

2856
    // Check everything remaining is whitespace
2857
    for (; *end != '\0'; end++)
2,419✔
2858
    {
2859
        if (!isspace(*end) && end - pszString != iImagEnd)
11✔
2860
        {
2861
            goto error;
6✔
2862
        }
2863
    }
2864

2865
    return CE_None;
2,408✔
2866

2867
error:
14✔
2868
    CPLError(CE_Failure, CPLE_AppDefined, "Failed to parse number: %s",
14✔
2869
             pszString);
2870
    return CE_Failure;
14✔
2871
}
2872

2873
/************************************************************************/
2874
/*                           CPLOpenShared()                            */
2875
/************************************************************************/
2876

2877
/**
2878
 * Open a shared file handle.
2879
 *
2880
 * Some operating systems have limits on the number of file handles that can
2881
 * be open at one time.  This function attempts to maintain a registry of
2882
 * already open file handles, and reuse existing ones if the same file
2883
 * is requested by another part of the application.
2884
 *
2885
 * Note that access is only shared for access types "r", "rb", "r+" and
2886
 * "rb+".  All others will just result in direct VSIOpen() calls.  Keep in
2887
 * mind that a file is only reused if the file name is exactly the same.
2888
 * Different names referring to the same file will result in different
2889
 * handles.
2890
 *
2891
 * The VSIFOpen() or VSIFOpenL() function is used to actually open the file,
2892
 * when an existing file handle can't be shared.
2893
 *
2894
 * @param pszFilename the name of the file to open.
2895
 * @param pszAccess the normal fopen()/VSIFOpen() style access string.
2896
 * @param bLargeIn If TRUE VSIFOpenL() (for large files) will be used instead of
2897
 * VSIFOpen().
2898
 *
2899
 * @return a file handle or NULL if opening fails.
2900
 */
2901

2902
FILE *CPLOpenShared(const char *pszFilename, const char *pszAccess,
35✔
2903
                    int bLargeIn)
2904

2905
{
2906
    const bool bLarge = CPL_TO_BOOL(bLargeIn);
35✔
2907
    CPLMutexHolderD(&hSharedFileMutex);
70✔
2908
    const GIntBig nPID = CPLGetPID();
35✔
2909

2910
    /* -------------------------------------------------------------------- */
2911
    /*      Is there an existing file we can use?                           */
2912
    /* -------------------------------------------------------------------- */
2913
    const bool bReuse = EQUAL(pszAccess, "rb") || EQUAL(pszAccess, "rb+");
35✔
2914

2915
    for (int i = 0; bReuse && i < nSharedFileCount; i++)
39✔
2916
    {
2917
        if (strcmp(pasSharedFileList[i].pszFilename, pszFilename) == 0 &&
20✔
2918
            !bLarge == !pasSharedFileList[i].bLarge &&
4✔
2919
            EQUAL(pasSharedFileList[i].pszAccess, pszAccess) &&
16✔
2920
            nPID == pasSharedFileListExtra[i].nPID)
4✔
2921
        {
2922
            pasSharedFileList[i].nRefCount++;
4✔
2923
            return pasSharedFileList[i].fp;
4✔
2924
        }
2925
    }
2926

2927
    /* -------------------------------------------------------------------- */
2928
    /*      Open the file.                                                  */
2929
    /* -------------------------------------------------------------------- */
2930
    FILE *fp = bLarge
2931
                   ? reinterpret_cast<FILE *>(VSIFOpenL(pszFilename, pszAccess))
31✔
2932
                   : VSIFOpen(pszFilename, pszAccess);
×
2933

2934
    if (fp == nullptr)
31✔
2935
        return nullptr;
8✔
2936

2937
    /* -------------------------------------------------------------------- */
2938
    /*      Add an entry to the list.                                       */
2939
    /* -------------------------------------------------------------------- */
2940
    nSharedFileCount++;
23✔
2941

2942
    pasSharedFileList = static_cast<CPLSharedFileInfo *>(
23✔
2943
        CPLRealloc(const_cast<CPLSharedFileInfo *>(pasSharedFileList),
46✔
2944
                   sizeof(CPLSharedFileInfo) * nSharedFileCount));
23✔
2945
    pasSharedFileListExtra = static_cast<CPLSharedFileInfoExtra *>(
23✔
2946
        CPLRealloc(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra),
46✔
2947
                   sizeof(CPLSharedFileInfoExtra) * nSharedFileCount));
23✔
2948

2949
    pasSharedFileList[nSharedFileCount - 1].fp = fp;
23✔
2950
    pasSharedFileList[nSharedFileCount - 1].nRefCount = 1;
23✔
2951
    pasSharedFileList[nSharedFileCount - 1].bLarge = bLarge;
23✔
2952
    pasSharedFileList[nSharedFileCount - 1].pszFilename =
46✔
2953
        CPLStrdup(pszFilename);
23✔
2954
    pasSharedFileList[nSharedFileCount - 1].pszAccess = CPLStrdup(pszAccess);
23✔
2955
    pasSharedFileListExtra[nSharedFileCount - 1].nPID = nPID;
23✔
2956

2957
    return fp;
23✔
2958
}
2959

2960
/************************************************************************/
2961
/*                           CPLCloseShared()                           */
2962
/************************************************************************/
2963

2964
/**
2965
 * Close shared file.
2966
 *
2967
 * Dereferences the indicated file handle, and closes it if the reference
2968
 * count has dropped to zero.  A CPLError() is issued if the file is not
2969
 * in the shared file list.
2970
 *
2971
 * @param fp file handle from CPLOpenShared() to deaccess.
2972
 */
2973

2974
void CPLCloseShared(FILE *fp)
27✔
2975

2976
{
2977
    CPLMutexHolderD(&hSharedFileMutex);
27✔
2978

2979
    /* -------------------------------------------------------------------- */
2980
    /*      Search for matching information.                                */
2981
    /* -------------------------------------------------------------------- */
2982
    int i = 0;
27✔
2983
    for (; i < nSharedFileCount && fp != pasSharedFileList[i].fp; i++)
29✔
2984
    {
2985
    }
2986

2987
    if (i == nSharedFileCount)
27✔
2988
    {
2989
        CPLError(CE_Failure, CPLE_AppDefined,
×
2990
                 "Unable to find file handle %p in CPLCloseShared().", fp);
2991
        return;
×
2992
    }
2993

2994
    /* -------------------------------------------------------------------- */
2995
    /*      Dereference and return if there are still some references.      */
2996
    /* -------------------------------------------------------------------- */
2997
    if (--pasSharedFileList[i].nRefCount > 0)
27✔
2998
        return;
4✔
2999

3000
    /* -------------------------------------------------------------------- */
3001
    /*      Close the file, and remove the information.                     */
3002
    /* -------------------------------------------------------------------- */
3003
    if (pasSharedFileList[i].bLarge)
23✔
3004
    {
3005
        if (VSIFCloseL(reinterpret_cast<VSILFILE *>(pasSharedFileList[i].fp)) !=
23✔
3006
            0)
3007
        {
3008
            CPLError(CE_Failure, CPLE_FileIO, "Error while closing %s",
×
3009
                     pasSharedFileList[i].pszFilename);
×
3010
        }
3011
    }
3012
    else
3013
    {
3014
        VSIFClose(pasSharedFileList[i].fp);
×
3015
    }
3016

3017
    CPLFree(pasSharedFileList[i].pszFilename);
23✔
3018
    CPLFree(pasSharedFileList[i].pszAccess);
23✔
3019

3020
    nSharedFileCount--;
23✔
3021
    memmove(
23✔
3022
        const_cast<CPLSharedFileInfo *>(pasSharedFileList + i),
23✔
3023
        const_cast<CPLSharedFileInfo *>(pasSharedFileList + nSharedFileCount),
23✔
3024
        sizeof(CPLSharedFileInfo));
3025
    memmove(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra + i),
23✔
3026
            const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra +
23✔
3027
                                                 nSharedFileCount),
23✔
3028
            sizeof(CPLSharedFileInfoExtra));
3029

3030
    if (nSharedFileCount == 0)
23✔
3031
    {
3032
        CPLFree(const_cast<CPLSharedFileInfo *>(pasSharedFileList));
20✔
3033
        pasSharedFileList = nullptr;
20✔
3034
        CPLFree(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra));
20✔
3035
        pasSharedFileListExtra = nullptr;
20✔
3036
    }
3037
}
3038

3039
/************************************************************************/
3040
/*                   CPLCleanupSharedFileMutex()                        */
3041
/************************************************************************/
3042

3043
void CPLCleanupSharedFileMutex()
1,113✔
3044
{
3045
    if (hSharedFileMutex != nullptr)
1,113✔
3046
    {
3047
        CPLDestroyMutex(hSharedFileMutex);
×
3048
        hSharedFileMutex = nullptr;
×
3049
    }
3050
}
1,113✔
3051

3052
/************************************************************************/
3053
/*                          CPLGetSharedList()                          */
3054
/************************************************************************/
3055

3056
/**
3057
 * Fetch list of open shared files.
3058
 *
3059
 * @param pnCount place to put the count of entries.
3060
 *
3061
 * @return the pointer to the first in the array of shared file info
3062
 * structures.
3063
 */
3064

3065
CPLSharedFileInfo *CPLGetSharedList(int *pnCount)
×
3066

3067
{
3068
    if (pnCount != nullptr)
×
3069
        *pnCount = nSharedFileCount;
×
3070

3071
    return const_cast<CPLSharedFileInfo *>(pasSharedFileList);
×
3072
}
3073

3074
/************************************************************************/
3075
/*                         CPLDumpSharedList()                          */
3076
/************************************************************************/
3077

3078
/**
3079
 * Report open shared files.
3080
 *
3081
 * Dumps all open shared files to the indicated file handle.  If the
3082
 * file handle is NULL information is sent via the CPLDebug() call.
3083
 *
3084
 * @param fp File handle to write to.
3085
 */
3086

3087
void CPLDumpSharedList(FILE *fp)
103✔
3088

3089
{
3090
    if (nSharedFileCount > 0)
103✔
3091
    {
3092
        if (fp == nullptr)
×
3093
            CPLDebug("CPL", "%d Shared files open.", nSharedFileCount);
×
3094
        else
3095
            fprintf(fp, "%d Shared files open.", nSharedFileCount);
×
3096
    }
3097

3098
    for (int i = 0; i < nSharedFileCount; i++)
103✔
3099
    {
3100
        if (fp == nullptr)
×
3101
            CPLDebug("CPL", "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
×
3102
                     pasSharedFileList[i].bLarge,
×
3103
                     pasSharedFileList[i].pszAccess,
×
3104
                     pasSharedFileList[i].pszFilename);
×
3105
        else
3106
            fprintf(fp, "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
×
3107
                    pasSharedFileList[i].bLarge, pasSharedFileList[i].pszAccess,
×
3108
                    pasSharedFileList[i].pszFilename);
×
3109
    }
3110
}
103✔
3111

3112
/************************************************************************/
3113
/*                           CPLUnlinkTree()                            */
3114
/************************************************************************/
3115

3116
/** Recursively unlink a directory.
3117
 *
3118
 * @return 0 on successful completion, -1 if function fails.
3119
 */
3120

3121
int CPLUnlinkTree(const char *pszPath)
52✔
3122

3123
{
3124
    /* -------------------------------------------------------------------- */
3125
    /*      First, ensure there is such a file.                             */
3126
    /* -------------------------------------------------------------------- */
3127
    VSIStatBufL sStatBuf;
3128

3129
    if (VSIStatL(pszPath, &sStatBuf) != 0)
52✔
3130
    {
3131
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
3132
                 "It seems no file system object called '%s' exists.", pszPath);
3133

3134
        return -1;
2✔
3135
    }
3136

3137
    /* -------------------------------------------------------------------- */
3138
    /*      If it is a simple file, just delete it.                         */
3139
    /* -------------------------------------------------------------------- */
3140
    if (VSI_ISREG(sStatBuf.st_mode))
50✔
3141
    {
3142
        if (VSIUnlink(pszPath) != 0)
34✔
3143
        {
3144
            CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
×
3145
                     pszPath);
3146

3147
            return -1;
×
3148
        }
3149

3150
        return 0;
34✔
3151
    }
3152

3153
    /* -------------------------------------------------------------------- */
3154
    /*      If it is a directory recurse then unlink the directory.         */
3155
    /* -------------------------------------------------------------------- */
3156
    else if (VSI_ISDIR(sStatBuf.st_mode))
16✔
3157
    {
3158
        char **papszItems = VSIReadDir(pszPath);
16✔
3159

3160
        for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
32✔
3161
        {
3162
            if (papszItems[i][0] == '\0' || EQUAL(papszItems[i], ".") ||
16✔
3163
                EQUAL(papszItems[i], ".."))
16✔
3164
                continue;
×
3165

3166
            const std::string osSubPath =
3167
                CPLFormFilenameSafe(pszPath, papszItems[i], nullptr);
16✔
3168

3169
            const int nErr = CPLUnlinkTree(osSubPath.c_str());
16✔
3170

3171
            if (nErr != 0)
16✔
3172
            {
3173
                CSLDestroy(papszItems);
×
3174
                return nErr;
×
3175
            }
3176
        }
3177

3178
        CSLDestroy(papszItems);
16✔
3179

3180
        if (VSIRmdir(pszPath) != 0)
16✔
3181
        {
3182
            CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
×
3183
                     pszPath);
3184

3185
            return -1;
×
3186
        }
3187

3188
        return 0;
16✔
3189
    }
3190

3191
    /* -------------------------------------------------------------------- */
3192
    /*      otherwise report an error.                                      */
3193
    /* -------------------------------------------------------------------- */
3194
    CPLError(CE_Failure, CPLE_AppDefined,
×
3195
             "Failed to unlink %s.\nUnrecognised filesystem object.", pszPath);
3196
    return 1000;
×
3197
}
3198

3199
/************************************************************************/
3200
/*                            CPLCopyFile()                             */
3201
/************************************************************************/
3202

3203
/** Copy a file */
3204
int CPLCopyFile(const char *pszNewPath, const char *pszOldPath)
2,250✔
3205

3206
{
3207
    return VSICopyFile(pszOldPath, pszNewPath, nullptr,
2,250✔
3208
                       static_cast<vsi_l_offset>(-1), nullptr, nullptr,
3209
                       nullptr);
2,250✔
3210
}
3211

3212
/************************************************************************/
3213
/*                            CPLCopyTree()                             */
3214
/************************************************************************/
3215

3216
/** Recursively copy a tree */
3217
int CPLCopyTree(const char *pszNewPath, const char *pszOldPath)
4✔
3218

3219
{
3220
    VSIStatBufL sStatBuf;
3221
    if (VSIStatL(pszNewPath, &sStatBuf) == 0)
4✔
3222
    {
3223
        CPLError(
1✔
3224
            CE_Failure, CPLE_AppDefined,
3225
            "It seems that a file system object called '%s' already exists.",
3226
            pszNewPath);
3227

3228
        return -1;
1✔
3229
    }
3230

3231
    if (VSIStatL(pszOldPath, &sStatBuf) != 0)
3✔
3232
    {
3233
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
3234
                 "It seems no file system object called '%s' exists.",
3235
                 pszOldPath);
3236

3237
        return -1;
1✔
3238
    }
3239

3240
    if (VSI_ISDIR(sStatBuf.st_mode))
2✔
3241
    {
3242
        if (VSIMkdir(pszNewPath, 0755) != 0)
1✔
3243
        {
3244
            CPLError(CE_Failure, CPLE_AppDefined,
×
3245
                     "Cannot create directory '%s'.", pszNewPath);
3246

3247
            return -1;
×
3248
        }
3249

3250
        char **papszItems = VSIReadDir(pszOldPath);
1✔
3251

3252
        for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
4✔
3253
        {
3254
            if (EQUAL(papszItems[i], ".") || EQUAL(papszItems[i], ".."))
3✔
3255
                continue;
2✔
3256

3257
            const std::string osNewSubPath =
3258
                CPLFormFilenameSafe(pszNewPath, papszItems[i], nullptr);
1✔
3259
            const std::string osOldSubPath =
3260
                CPLFormFilenameSafe(pszOldPath, papszItems[i], nullptr);
1✔
3261

3262
            const int nErr =
3263
                CPLCopyTree(osNewSubPath.c_str(), osOldSubPath.c_str());
1✔
3264

3265
            if (nErr != 0)
1✔
3266
            {
3267
                CSLDestroy(papszItems);
×
3268
                return nErr;
×
3269
            }
3270
        }
3271
        CSLDestroy(papszItems);
1✔
3272

3273
        return 0;
1✔
3274
    }
3275
    else if (VSI_ISREG(sStatBuf.st_mode))
1✔
3276
    {
3277
        return CPLCopyFile(pszNewPath, pszOldPath);
1✔
3278
    }
3279
    else
3280
    {
3281
        CPLError(CE_Failure, CPLE_AppDefined,
×
3282
                 "Unrecognized filesystem object : '%s'.", pszOldPath);
3283
        return -1;
×
3284
    }
3285
}
3286

3287
/************************************************************************/
3288
/*                            CPLMoveFile()                             */
3289
/************************************************************************/
3290

3291
/** Move a file */
3292
int CPLMoveFile(const char *pszNewPath, const char *pszOldPath)
186✔
3293

3294
{
3295
    if (VSIRename(pszOldPath, pszNewPath) == 0)
186✔
3296
        return 0;
183✔
3297

3298
    const int nRet = CPLCopyFile(pszNewPath, pszOldPath);
3✔
3299

3300
    if (nRet == 0)
3✔
3301
        VSIUnlink(pszOldPath);
3✔
3302
    return nRet;
3✔
3303
}
3304

3305
/************************************************************************/
3306
/*                             CPLSymlink()                             */
3307
/************************************************************************/
3308

3309
/** Create a symbolic link */
3310
#ifdef _WIN32
3311
int CPLSymlink(const char *, const char *, CSLConstList)
3312
{
3313
    return -1;
3314
}
3315
#else
3316
int CPLSymlink(const char *pszOldPath, const char *pszNewPath,
×
3317
               CSLConstList /* papszOptions */)
3318
{
3319
    return symlink(pszOldPath, pszNewPath);
×
3320
}
3321
#endif
3322

3323
/************************************************************************/
3324
/* ==================================================================== */
3325
/*                              CPLLocaleC                              */
3326
/* ==================================================================== */
3327
/************************************************************************/
3328

3329
//! @cond Doxygen_Suppress
3330
/************************************************************************/
3331
/*                             CPLLocaleC()                             */
3332
/************************************************************************/
3333

3334
CPLLocaleC::CPLLocaleC() : pszOldLocale(nullptr)
130✔
3335
{
3336
    if (CPLTestBool(CPLGetConfigOption("GDAL_DISABLE_CPLLOCALEC", "NO")))
130✔
3337
        return;
×
3338

3339
    pszOldLocale = CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr));
130✔
3340
    if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
130✔
3341
        CPLsetlocale(LC_NUMERIC, "C") == nullptr)
×
3342
    {
3343
        CPLFree(pszOldLocale);
130✔
3344
        pszOldLocale = nullptr;
130✔
3345
    }
3346
}
3347

3348
/************************************************************************/
3349
/*                            ~CPLLocaleC()                             */
3350
/************************************************************************/
3351

3352
CPLLocaleC::~CPLLocaleC()
×
3353

3354
{
3355
    if (pszOldLocale == nullptr)
130✔
3356
        return;
130✔
3357

3358
    CPLsetlocale(LC_NUMERIC, pszOldLocale);
×
3359
    CPLFree(pszOldLocale);
×
3360
}
130✔
3361

3362
/************************************************************************/
3363
/*                        CPLThreadLocaleCPrivate                       */
3364
/************************************************************************/
3365

3366
#ifdef HAVE_USELOCALE
3367

3368
class CPLThreadLocaleCPrivate
3369
{
3370
    locale_t nNewLocale;
3371
    locale_t nOldLocale;
3372

3373
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3374

3375
  public:
3376
    CPLThreadLocaleCPrivate();
3377
    ~CPLThreadLocaleCPrivate();
3378
};
3379

3380
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
×
3381
    : nNewLocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)),
×
3382
      nOldLocale(uselocale(nNewLocale))
×
3383
{
3384
}
×
3385

3386
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
×
3387
{
3388
    uselocale(nOldLocale);
×
3389
    freelocale(nNewLocale);
×
3390
}
×
3391

3392
#elif defined(_MSC_VER)
3393

3394
class CPLThreadLocaleCPrivate
3395
{
3396
    int nOldValConfigThreadLocale;
3397
    char *pszOldLocale;
3398

3399
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3400

3401
  public:
3402
    CPLThreadLocaleCPrivate();
3403
    ~CPLThreadLocaleCPrivate();
3404
};
3405

3406
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3407
{
3408
    nOldValConfigThreadLocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
3409
    pszOldLocale = setlocale(LC_NUMERIC, "C");
3410
    if (pszOldLocale)
3411
        pszOldLocale = CPLStrdup(pszOldLocale);
3412
}
3413

3414
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3415
{
3416
    if (pszOldLocale != nullptr)
3417
    {
3418
        setlocale(LC_NUMERIC, pszOldLocale);
3419
        CPLFree(pszOldLocale);
3420
    }
3421
    _configthreadlocale(nOldValConfigThreadLocale);
3422
}
3423

3424
#else
3425

3426
class CPLThreadLocaleCPrivate
3427
{
3428
    char *pszOldLocale;
3429

3430
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3431

3432
  public:
3433
    CPLThreadLocaleCPrivate();
3434
    ~CPLThreadLocaleCPrivate();
3435
};
3436

3437
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3438
    : pszOldLocale(CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr)))
3439
{
3440
    if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
3441
        CPLsetlocale(LC_NUMERIC, "C") == nullptr)
3442
    {
3443
        CPLFree(pszOldLocale);
3444
        pszOldLocale = nullptr;
3445
    }
3446
}
3447

3448
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3449
{
3450
    if (pszOldLocale != nullptr)
3451
    {
3452
        CPLsetlocale(LC_NUMERIC, pszOldLocale);
3453
        CPLFree(pszOldLocale);
3454
    }
3455
}
3456

3457
#endif
3458

3459
/************************************************************************/
3460
/*                        CPLThreadLocaleC()                            */
3461
/************************************************************************/
3462

3463
CPLThreadLocaleC::CPLThreadLocaleC() : m_private(new CPLThreadLocaleCPrivate)
×
3464
{
3465
}
×
3466

3467
/************************************************************************/
3468
/*                       ~CPLThreadLocaleC()                            */
3469
/************************************************************************/
3470

3471
CPLThreadLocaleC::~CPLThreadLocaleC()
×
3472

3473
{
3474
    delete m_private;
×
3475
}
×
3476

3477
//! @endcond
3478

3479
/************************************************************************/
3480
/*                          CPLsetlocale()                              */
3481
/************************************************************************/
3482

3483
/**
3484
 * Prevents parallel executions of setlocale().
3485
 *
3486
 * Calling setlocale() concurrently from two or more threads is a
3487
 * potential data race. A mutex is used to provide a critical region so
3488
 * that only one thread at a time can be executing setlocale().
3489
 *
3490
 * The return should not be freed, and copied quickly as it may be invalidated
3491
 * by a following next call to CPLsetlocale().
3492
 *
3493
 * @param category See your compiler's documentation on setlocale.
3494
 * @param locale See your compiler's documentation on setlocale.
3495
 *
3496
 * @return See your compiler's documentation on setlocale.
3497
 */
3498
char *CPLsetlocale(int category, const char *locale)
132✔
3499
{
3500
    CPLMutexHolder oHolder(&hSetLocaleMutex);
264✔
3501
    char *pszRet = setlocale(category, locale);
132✔
3502
    if (pszRet == nullptr)
132✔
3503
        return pszRet;
×
3504

3505
    // Make it thread-locale storage.
3506
    return const_cast<char *>(CPLSPrintf("%s", pszRet));
132✔
3507
}
3508

3509
/************************************************************************/
3510
/*                       CPLCleanupSetlocaleMutex()                     */
3511
/************************************************************************/
3512

3513
void CPLCleanupSetlocaleMutex(void)
1,113✔
3514
{
3515
    if (hSetLocaleMutex != nullptr)
1,113✔
3516
        CPLDestroyMutex(hSetLocaleMutex);
5✔
3517
    hSetLocaleMutex = nullptr;
1,113✔
3518
}
1,113✔
3519

3520
/************************************************************************/
3521
/*                            IsPowerOfTwo()                            */
3522
/************************************************************************/
3523

3524
int CPLIsPowerOfTwo(unsigned int i)
150✔
3525
{
3526
    if (i == 0)
150✔
3527
        return FALSE;
×
3528
    return (i & (i - 1)) == 0 ? TRUE : FALSE;
150✔
3529
}
3530

3531
/************************************************************************/
3532
/*                          CPLCheckForFile()                           */
3533
/************************************************************************/
3534

3535
/**
3536
 * Check for file existence.
3537
 *
3538
 * The function checks if a named file exists in the filesystem, hopefully
3539
 * in an efficient fashion if a sibling file list is available.   It exists
3540
 * primarily to do faster file checking for functions like GDAL open methods
3541
 * that get a list of files from the target directory.
3542
 *
3543
 * If the sibling file list exists (is not NULL) it is assumed to be a list
3544
 * of files in the same directory as the target file, and it will be checked
3545
 * (case insensitively) for a match.  If a match is found, pszFilename is
3546
 * updated with the correct case and TRUE is returned.
3547
 *
3548
 * If papszSiblingFiles is NULL, a VSIStatL() is used to test for the files
3549
 * existence, and no case insensitive testing is done.
3550
 *
3551
 * @param pszFilename name of file to check for - filename case updated in
3552
 * some cases.
3553
 * @param papszSiblingFiles a list of files in the same directory as
3554
 * pszFilename if available, or NULL. This list should have no path components.
3555
 *
3556
 * @return TRUE if a match is found, or FALSE if not.
3557
 */
3558

3559
int CPLCheckForFile(char *pszFilename, char **papszSiblingFiles)
164,632✔
3560

3561
{
3562
    /* -------------------------------------------------------------------- */
3563
    /*      Fallback case if we don't have a sibling file list.             */
3564
    /* -------------------------------------------------------------------- */
3565
    if (papszSiblingFiles == nullptr)
164,632✔
3566
    {
3567
        VSIStatBufL sStatBuf;
3568

3569
        return VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
11,392✔
3570
    }
3571

3572
    /* -------------------------------------------------------------------- */
3573
    /*      We have sibling files, compare the non-path filename portion    */
3574
    /*      of pszFilename too all entries.                                 */
3575
    /* -------------------------------------------------------------------- */
3576
    const CPLString osFileOnly = CPLGetFilename(pszFilename);
306,477✔
3577

3578
    for (int i = 0; papszSiblingFiles[i] != nullptr; i++)
15,293,500✔
3579
    {
3580
        if (EQUAL(papszSiblingFiles[i], osFileOnly))
15,140,500✔
3581
        {
3582
            strcpy(pszFilename + strlen(pszFilename) - osFileOnly.size(),
272✔
3583
                   papszSiblingFiles[i]);
116✔
3584
            return TRUE;
156✔
3585
        }
3586
    }
3587

3588
    return FALSE;
153,081✔
3589
}
3590

3591
/************************************************************************/
3592
/*      Stub implementation of zip services if we don't have libz.      */
3593
/************************************************************************/
3594

3595
#if !defined(HAVE_LIBZ)
3596

3597
void *CPLCreateZip(const char *, char **)
3598

3599
{
3600
    CPLError(CE_Failure, CPLE_NotSupported,
3601
             "This GDAL/OGR build does not include zlib and zip services.");
3602
    return nullptr;
3603
}
3604

3605
CPLErr CPLCreateFileInZip(void *, const char *, char **)
3606
{
3607
    return CE_Failure;
3608
}
3609

3610
CPLErr CPLWriteFileInZip(void *, const void *, int)
3611
{
3612
    return CE_Failure;
3613
}
3614

3615
CPLErr CPLCloseFileInZip(void *)
3616
{
3617
    return CE_Failure;
3618
}
3619

3620
CPLErr CPLCloseZip(void *)
3621
{
3622
    return CE_Failure;
3623
}
3624

3625
void *CPLZLibDeflate(const void *, size_t, int, void *, size_t,
3626
                     size_t *pnOutBytes)
3627
{
3628
    if (pnOutBytes != nullptr)
3629
        *pnOutBytes = 0;
3630
    return nullptr;
3631
}
3632

3633
void *CPLZLibInflate(const void *, size_t, void *, size_t, size_t *pnOutBytes)
3634
{
3635
    if (pnOutBytes != nullptr)
3636
        *pnOutBytes = 0;
3637
    return nullptr;
3638
}
3639

3640
#endif /* !defined(HAVE_LIBZ) */
3641

3642
/************************************************************************/
3643
/* ==================================================================== */
3644
/*                          CPLConfigOptionSetter                       */
3645
/* ==================================================================== */
3646
/************************************************************************/
3647

3648
//! @cond Doxygen_Suppress
3649
/************************************************************************/
3650
/*                         CPLConfigOptionSetter()                      */
3651
/************************************************************************/
3652

3653
CPLConfigOptionSetter::CPLConfigOptionSetter(const char *pszKey,
25,999✔
3654
                                             const char *pszValue,
3655
                                             bool bSetOnlyIfUndefined)
25,999✔
3656
    : m_pszKey(CPLStrdup(pszKey)), m_pszOldValue(nullptr),
25,999✔
3657
      m_bRestoreOldValue(false)
25,965✔
3658
{
3659
    const char *pszOldValue = CPLGetThreadLocalConfigOption(pszKey, nullptr);
25,965✔
3660
    if ((bSetOnlyIfUndefined &&
41,952✔
3661
         CPLGetConfigOption(pszKey, nullptr) == nullptr) ||
36,020✔
3662
        !bSetOnlyIfUndefined)
10,054✔
3663
    {
3664
        m_bRestoreOldValue = true;
25,994✔
3665
        if (pszOldValue)
25,994✔
3666
            m_pszOldValue = CPLStrdup(pszOldValue);
651✔
3667
        CPLSetThreadLocalConfigOption(pszKey, pszValue);
25,994✔
3668
    }
3669
}
25,915✔
3670

3671
/************************************************************************/
3672
/*                        ~CPLConfigOptionSetter()                      */
3673
/************************************************************************/
3674

3675
CPLConfigOptionSetter::~CPLConfigOptionSetter()
51,791✔
3676
{
3677
    if (m_bRestoreOldValue)
25,994✔
3678
    {
3679
        CPLSetThreadLocalConfigOption(m_pszKey, m_pszOldValue);
25,986✔
3680
        CPLFree(m_pszOldValue);
25,827✔
3681
    }
3682
    CPLFree(m_pszKey);
25,769✔
3683
}
25,797✔
3684

3685
//! @endcond
3686

3687
/************************************************************************/
3688
/*                          CPLIsInteractive()                          */
3689
/************************************************************************/
3690

3691
/** Returns whether the provided file refers to a terminal.
3692
 *
3693
 * This function is a wrapper of the ``isatty()`` POSIX function.
3694
 *
3695
 * @param f File to test. Typically stdin, stdout or stderr
3696
 * @return true if it is an open file referring to a terminal.
3697
 * @since GDAL 3.11
3698
 */
3699
bool CPLIsInteractive(FILE *f)
634✔
3700
{
3701
#ifndef _WIN32
3702
    return isatty(static_cast<int>(fileno(f)));
634✔
3703
#else
3704
    return _isatty(_fileno(f));
3705
#endif
3706
}
3707

3708
/************************************************************************/
3709
/*                          CPLLockFileStruct                          */
3710
/************************************************************************/
3711

3712
//! @cond Doxygen_Suppress
3713
struct CPLLockFileStruct
3714
{
3715
    std::string osLockFilename{};
3716
    std::atomic<bool> bStop = false;
3717
    CPLJoinableThread *hThread = nullptr;
3718
};
3719

3720
//! @endcond
3721

3722
/************************************************************************/
3723
/*                          CPLLockFileEx()                             */
3724
/************************************************************************/
3725

3726
/** Create and acquire a lock file.
3727
 *
3728
 * Only one caller can acquire the lock file at a time. The O_CREAT|O_EXCL
3729
 * flags of open() are used for that purpose (there might be limitations for
3730
 * network file systems).
3731
 *
3732
 * The lock file is continuously touched by a thread started by this function,
3733
 * to indicate it is still alive. If an existing lock file is found that has
3734
 * not been recently refreshed it will be considered stalled, and will be
3735
 * deleted before attempting to recreate it.
3736
 *
3737
 * This function must be paired with CPLUnlockFileEx().
3738
 *
3739
 * Available options are:
3740
 * <ul>
3741
 * <li>WAIT_TIME=value_in_sec/inf: Maximum amount of time in second that this
3742
 *     function can spend waiting for the lock. If not set, default to infinity.
3743
 * </li>
3744
 * <li>STALLED_DELAY=value_in_sec: Delay in second to consider that an existing
3745
 * lock file that has not been touched since STALLED_DELAY is stalled, and can
3746
 * be re-acquired. Defaults to 10 seconds.
3747
 * </li>
3748
 * <li>VERBOSE_WAIT_MESSAGE=YES/NO: Whether to emit a CE_Warning message while
3749
 * waiting for a busy lock. Default to NO.
3750
 * </li>
3751
 * </ul>
3752

3753
 * @param pszLockFileName Lock file name. The directory must already exist.
3754
 *                        Must not be NULL.
3755
 * @param[out] phLockFileHandle Pointer to at location where to store the lock
3756
 *                              handle that must be passed to CPLUnlockFileEx().
3757
 *                              *phLockFileHandle will be null if the return
3758
 *                              code of that function is not CLFS_OK.
3759
 * @param papszOptions NULL terminated list of strings, or NULL.
3760
 *
3761
 * @return lock file status.
3762
 *
3763
 * @since 3.11
3764
 */
3765
CPLLockFileStatus CPLLockFileEx(const char *pszLockFileName,
15✔
3766
                                CPLLockFileHandle *phLockFileHandle,
3767
                                CSLConstList papszOptions)
3768
{
3769
    if (!pszLockFileName || !phLockFileHandle)
15✔
3770
        return CLFS_API_MISUSE;
2✔
3771

3772
    *phLockFileHandle = nullptr;
13✔
3773

3774
    const double dfWaitTime =
3775
        CPLAtof(CSLFetchNameValueDef(papszOptions, "WAIT_TIME", "inf"));
13✔
3776
    const double dfStalledDelay =
3777
        CPLAtof(CSLFetchNameValueDef(papszOptions, "STALLED_DELAY", "10"));
13✔
3778
    const bool bVerboseWait =
3779
        CPLFetchBool(papszOptions, "VERBOSE_WAIT_MESSAGE", false);
13✔
3780

3781
    for (int i = 0; i < 2; ++i)
14✔
3782
    {
3783
#ifdef _WIN32
3784
        wchar_t *pwszFilename =
3785
            CPLRecodeToWChar(pszLockFileName, CPL_ENC_UTF8, CPL_ENC_UCS2);
3786
        int fd = _wopen(pwszFilename, _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);
3787
        CPLFree(pwszFilename);
3788
#else
3789
        int fd = open(pszLockFileName, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
14✔
3790
#endif
3791
        if (fd == -1)
14✔
3792
        {
3793
            if (errno != EEXIST || i == 1)
3✔
3794
            {
3795
                return CLFS_CANNOT_CREATE_LOCK;
×
3796
            }
3797
            else
3798
            {
3799
                // Wait for the .lock file to have been removed or
3800
                // not refreshed since dfStalledDelay seconds.
3801
                double dfCurWaitTime = dfWaitTime;
3✔
3802
                VSIStatBufL sStat;
3803
                while (VSIStatL(pszLockFileName, &sStat) == 0 &&
15✔
3804
                       static_cast<double>(sStat.st_mtime) + dfStalledDelay >
7✔
3805
                           static_cast<double>(time(nullptr)))
7✔
3806
                {
3807
                    if (dfCurWaitTime <= 1e-5)
6✔
3808
                        return CLFS_LOCK_BUSY;
2✔
3809

3810
                    if (bVerboseWait)
5✔
3811
                    {
3812
                        CPLError(CE_Warning, CPLE_AppDefined,
4✔
3813
                                 "Waiting for %s to be freed...",
3814
                                 pszLockFileName);
3815
                    }
3816
                    else
3817
                    {
3818
                        CPLDebug("CPL", "Waiting for %s to be freed...",
1✔
3819
                                 pszLockFileName);
3820
                    }
3821

3822
                    const double dfPauseDelay = std::min(0.5, dfWaitTime);
5✔
3823
                    CPLSleep(dfPauseDelay);
5✔
3824
                    dfCurWaitTime -= dfPauseDelay;
5✔
3825
                }
3826

3827
                if (VSIUnlink(pszLockFileName) != 0)
2✔
3828
                {
3829
                    return CLFS_CANNOT_CREATE_LOCK;
1✔
3830
                }
3831
            }
3832
        }
3833
        else
3834
        {
3835
            close(fd);
11✔
3836
            break;
11✔
3837
        }
3838
    }
3839

3840
    // Touch regularly the lock file to show it is still alive
3841
    struct KeepAliveLockFile
3842
    {
3843
        static void func(void *user_data)
11✔
3844
        {
3845
            CPLLockFileHandle hLockFileHandle =
11✔
3846
                static_cast<CPLLockFileHandle>(user_data);
3847
            while (!hLockFileHandle->bStop)
23✔
3848
            {
3849
                auto f = VSIVirtualHandleUniquePtr(
3850
                    VSIFOpenL(hLockFileHandle->osLockFilename.c_str(), "wb"));
24✔
3851
                if (f)
12✔
3852
                {
3853
                    f.reset();
12✔
3854
                }
3855
                constexpr double REFRESH_DELAY = 0.5;
12✔
3856
                CPLSleep(REFRESH_DELAY);
12✔
3857
            }
3858
        }
11✔
3859
    };
3860

3861
    *phLockFileHandle = new CPLLockFileStruct();
11✔
3862
    (*phLockFileHandle)->osLockFilename = pszLockFileName;
11✔
3863

3864
    (*phLockFileHandle)->hThread =
22✔
3865
        CPLCreateJoinableThread(KeepAliveLockFile::func, *phLockFileHandle);
11✔
3866
    if ((*phLockFileHandle)->hThread == nullptr)
11✔
3867
    {
3868
        VSIUnlink(pszLockFileName);
×
3869
        delete *phLockFileHandle;
×
3870
        *phLockFileHandle = nullptr;
×
3871
        return CLFS_THREAD_CREATION_FAILED;
×
3872
    }
3873

3874
    return CLFS_OK;
11✔
3875
}
3876

3877
/************************************************************************/
3878
/*                         CPLUnlockFileEx()                            */
3879
/************************************************************************/
3880

3881
/** Release and delete a lock file.
3882
 *
3883
 * This function must be paired with CPLLockFileEx().
3884
 *
3885
 * @param hLockFileHandle Lock handle (value of *phLockFileHandle argument
3886
 *                        set by CPLLockFileEx()), or NULL.
3887
 *
3888
 * @since 3.11
3889
 */
3890
void CPLUnlockFileEx(CPLLockFileHandle hLockFileHandle)
12✔
3891
{
3892
    if (hLockFileHandle)
12✔
3893
    {
3894
        // Remove .lock file
3895
        hLockFileHandle->bStop = true;
11✔
3896
        CPLJoinThread(hLockFileHandle->hThread);
11✔
3897
        VSIUnlink(hLockFileHandle->osLockFilename.c_str());
11✔
3898

3899
        delete hLockFileHandle;
11✔
3900
    }
3901
}
12✔
3902

3903
/************************************************************************/
3904
/*                       CPLFormatReadableFileSize()                    */
3905
/************************************************************************/
3906

3907
template <class T>
3908
static std::string CPLFormatReadableFileSizeInternal(T nSizeInBytes)
10✔
3909
{
3910
    constexpr T ONE_MEGA_BYTE = 1000 * 1000;
10✔
3911
    constexpr T ONE_GIGA_BYTE = 1000 * ONE_MEGA_BYTE;
10✔
3912
    constexpr T ONE_TERA_BYTE = 1000 * ONE_GIGA_BYTE;
10✔
3913
    constexpr T ONE_PETA_BYTE = 1000 * ONE_TERA_BYTE;
10✔
3914
    constexpr T ONE_HEXA_BYTE = 1000 * ONE_PETA_BYTE;
10✔
3915

3916
    if (nSizeInBytes > ONE_HEXA_BYTE)
10✔
3917
        return CPLSPrintf("%.02f HB", static_cast<double>(nSizeInBytes) /
3918
                                          static_cast<double>(ONE_HEXA_BYTE));
2✔
3919

3920
    if (nSizeInBytes > ONE_PETA_BYTE)
8✔
3921
        return CPLSPrintf("%.02f PB", static_cast<double>(nSizeInBytes) /
3922
                                          static_cast<double>(ONE_PETA_BYTE));
2✔
3923

3924
    if (nSizeInBytes > ONE_TERA_BYTE)
6✔
3925
        return CPLSPrintf("%.02f TB", static_cast<double>(nSizeInBytes) /
3926
                                          static_cast<double>(ONE_TERA_BYTE));
1✔
3927

3928
    if (nSizeInBytes > ONE_GIGA_BYTE)
5✔
3929
        return CPLSPrintf("%.02f GB", static_cast<double>(nSizeInBytes) /
3930
                                          static_cast<double>(ONE_GIGA_BYTE));
3✔
3931

3932
    if (nSizeInBytes > ONE_MEGA_BYTE)
2✔
3933
        return CPLSPrintf("%.02f MB", static_cast<double>(nSizeInBytes) /
3934
                                          static_cast<double>(ONE_MEGA_BYTE));
1✔
3935

3936
    return CPLSPrintf("%03d,%03d bytes", static_cast<int>(nSizeInBytes) / 1000,
3937
                      static_cast<int>(nSizeInBytes) % 1000);
1✔
3938
}
3939

3940
/** Return a file size in a human readable way.
3941
 *
3942
 * e.g 1200000 -> "1.20 MB"
3943
 *
3944
 * @since 3.12
3945
 */
3946
std::string CPLFormatReadableFileSize(uint64_t nSizeInBytes)
3✔
3947
{
3948
    return CPLFormatReadableFileSizeInternal(nSizeInBytes);
3✔
3949
}
3950

3951
/** Return a file size in a human readable way.
3952
 *
3953
 * e.g 1200000 -> "1.20 MB"
3954
 *
3955
 * @since 3.12
3956
 */
3957
std::string CPLFormatReadableFileSize(double dfSizeInBytes)
7✔
3958
{
3959
    return CPLFormatReadableFileSizeInternal(dfSizeInBytes);
7✔
3960
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc