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

OSGeo / gdal / 12706066811

10 Jan 2025 08:38AM UTC coverage: 70.084% (-2.5%) from 72.549%
12706066811

Pull #11629

github

web-flow
Merge 9418dc48f into 0df468c56
Pull Request #11629: add uv documentation for python package

563296 of 803749 relevant lines covered (70.08%)

223434.74 hits per line

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

77.36
/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
// but on Solaris, we don't have uselocale and we cannot have
19
// std=c++11 with _XOPEN_SOURCE != 600
20
#if defined(__sun__) && __cplusplus >= 201103L
21
#if _XOPEN_SOURCE != 600
22
#ifdef _XOPEN_SOURCE
23
#undef _XOPEN_SOURCE
24
#endif
25
#define _XOPEN_SOURCE 600
26
#endif
27
#else
28
#ifdef _XOPEN_SOURCE
29
#undef _XOPEN_SOURCE
30
#endif
31
#define _XOPEN_SOURCE 700
32
#endif
33
#endif
34

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

40
#ifdef MSVC_USE_VLD
41
#include <vld.h>
42
#endif
43

44
#include "cpl_conv.h"
45

46
#include <algorithm>
47
#include <cctype>
48
#include <cerrno>
49
#include <climits>
50
#include <clocale>
51
#include <cmath>
52
#include <cstdlib>
53
#include <cstring>
54
#include <ctime>
55
#if HAVE_FCNTL_H
56
#include <fcntl.h>
57
#endif
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
#ifdef _WIN32
69
#include <io.h>  // _isatty
70
#else
71
#include <unistd.h>  // isatty
72
#endif
73

74
#include <string>
75

76
#if __cplusplus >= 202002L
77
#include <bit>  // For std::endian
78
#endif
79

80
#include "cpl_config.h"
81
#include "cpl_multiproc.h"
82
#include "cpl_string.h"
83
#include "cpl_vsi.h"
84
#include "cpl_vsil_curl_priv.h"
85
#include "cpl_known_config_options.h"
86

87
#ifdef DEBUG
88
#define OGRAPISPY_ENABLED
89
#endif
90
#ifdef OGRAPISPY_ENABLED
91
// Keep in sync with ograpispy.cpp
92
void OGRAPISPYCPLSetConfigOption(const char *, const char *);
93
void OGRAPISPYCPLSetThreadLocalConfigOption(const char *, const char *);
94
#endif
95

96
// Uncomment to get list of options that have been fetched and set.
97
// #define DEBUG_CONFIG_OPTIONS
98

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

106
static std::vector<std::pair<CPLSetConfigOptionSubscriber, void *>>
107
    gSetConfigOptionSubscribers{};
108

109
// Used by CPLOpenShared() and friends.
110
static CPLMutex *hSharedFileMutex = nullptr;
111
static int nSharedFileCount = 0;
112
static CPLSharedFileInfo *pasSharedFileList = nullptr;
113

114
// Used by CPLsetlocale().
115
static CPLMutex *hSetLocaleMutex = nullptr;
116

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

125
static volatile CPLSharedFileInfoExtra *pasSharedFileListExtra = nullptr;
126

127
/************************************************************************/
128
/*                             CPLCalloc()                              */
129
/************************************************************************/
130

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

148
void *CPLCalloc(size_t nCount, size_t nSize)
3,217,590✔
149

150
{
151
    if (nSize * nCount == 0)
3,217,590✔
152
        return nullptr;
9,138✔
153

154
    void *pReturn = CPLMalloc(nCount * nSize);
3,208,450✔
155
    memset(pReturn, 0, nCount * nSize);
3,208,440✔
156
    return pReturn;
3,208,440✔
157
}
158

159
/************************************************************************/
160
/*                             CPLMalloc()                              */
161
/************************************************************************/
162

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

178
void *CPLMalloc(size_t nSize)
18,939,400✔
179

180
{
181
    if (nSize == 0)
18,939,400✔
182
        return nullptr;
5,665✔
183

184
    if ((nSize >> (8 * sizeof(nSize) - 1)) != 0)
18,933,800✔
185
    {
186
        // coverity[dead_error_begin]
187
        CPLError(CE_Failure, CPLE_AppDefined,
×
188
                 "CPLMalloc(%ld): Silly size requested.",
189
                 static_cast<long>(nSize));
190
        return nullptr;
×
191
    }
192

193
    void *pReturn = VSIMalloc(nSize);
18,933,800✔
194
    if (pReturn == nullptr)
18,933,800✔
195
    {
196
        if (nSize < 2000)
×
197
        {
198
            CPLEmergencyError("CPLMalloc(): Out of memory allocating a small "
×
199
                              "number of bytes.");
200
        }
201

202
        CPLError(CE_Fatal, CPLE_OutOfMemory,
×
203
                 "CPLMalloc(): Out of memory allocating %ld bytes.",
204
                 static_cast<long>(nSize));
205
    }
206

207
    return pReturn;
18,933,700✔
208
}
209

210
/************************************************************************/
211
/*                             CPLRealloc()                             */
212
/************************************************************************/
213

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

233
void *CPLRealloc(void *pData, size_t nNewSize)
1,834,700✔
234

235
{
236
    if (nNewSize == 0)
1,834,700✔
237
    {
238
        VSIFree(pData);
45✔
239
        return nullptr;
45✔
240
    }
241

242
    if ((nNewSize >> (8 * sizeof(nNewSize) - 1)) != 0)
1,834,660✔
243
    {
244
        // coverity[dead_error_begin]
245
        CPLError(CE_Failure, CPLE_AppDefined,
×
246
                 "CPLRealloc(%ld): Silly size requested.",
247
                 static_cast<long>(nNewSize));
248
        return nullptr;
×
249
    }
250

251
    void *pReturn = nullptr;
1,834,660✔
252

253
    if (pData == nullptr)
1,834,660✔
254
        pReturn = VSIMalloc(nNewSize);
857,354✔
255
    else
256
        pReturn = VSIRealloc(pData, nNewSize);
977,304✔
257

258
    if (pReturn == nullptr)
1,834,660✔
259
    {
260
        if (nNewSize < 2000)
×
261
        {
262
            char szSmallMsg[80] = {};
×
263

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

277
    return pReturn;
1,834,660✔
278
}
279

280
/************************************************************************/
281
/*                             CPLStrdup()                              */
282
/************************************************************************/
283

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

301
char *CPLStrdup(const char *pszString)
7,400,910✔
302

303
{
304
    if (pszString == nullptr)
7,400,910✔
305
        pszString = "";
985,408✔
306

307
    const size_t nLen = strlen(pszString);
7,400,910✔
308
    char *pszReturn = static_cast<char *>(CPLMalloc(nLen + 1));
7,400,910✔
309
    memcpy(pszReturn, pszString, nLen + 1);
7,400,860✔
310
    return (pszReturn);
7,400,860✔
311
}
312

313
/************************************************************************/
314
/*                             CPLStrlwr()                              */
315
/************************************************************************/
316

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

327
char *CPLStrlwr(char *pszString)
32✔
328

329
{
330
    if (pszString == nullptr)
32✔
331
        return nullptr;
×
332

333
    char *pszTemp = pszString;
32✔
334

335
    while (*pszTemp)
140✔
336
    {
337
        *pszTemp =
108✔
338
            static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszTemp)));
108✔
339
        pszTemp++;
108✔
340
    }
341

342
    return pszString;
32✔
343
}
344

345
/************************************************************************/
346
/*                              CPLFGets()                              */
347
/*                                                                      */
348
/*      Note: LF = \n = ASCII 10                                        */
349
/*            CR = \r = ASCII 13                                        */
350
/************************************************************************/
351

352
// ASCII characters.
353
constexpr char knLF = 10;
354
constexpr char knCR = 13;
355

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

373
char *CPLFGets(char *pszBuffer, int nBufferSize, FILE *fp)
×
374

375
{
376
    if (nBufferSize == 0 || pszBuffer == nullptr || fp == nullptr)
×
377
        return nullptr;
×
378

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

390
    int nActuallyRead = static_cast<int>(strlen(pszBuffer));
×
391
    if (nActuallyRead == 0)
×
392
        return nullptr;
×
393

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

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

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

435
    if (pszExtraNewline != nullptr)
×
436
    {
437
        nActuallyRead = static_cast<int>(pszExtraNewline - pszBuffer + 1);
×
438

439
        *pszExtraNewline = '\0';
×
440
        if (VSIFSeek(fp, nOriginalOffset + nActuallyRead - 1, SEEK_SET) != 0)
×
441
            return nullptr;
×
442

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

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

465
    return pszBuffer;
×
466
}
467

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

480
{
481

482
    /* -------------------------------------------------------------------- */
483
    /*      A required size of -1 means the buffer should be freed.         */
484
    /* -------------------------------------------------------------------- */
485
    if (nRequiredSize == -1)
2,390,700✔
486
    {
487
        int bMemoryError = FALSE;
2,335✔
488
        void *pRet = CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError);
2,335✔
489
        if (pRet != nullptr)
2,335✔
490
        {
491
            CPLFree(pRet);
2,083✔
492
            CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
2,083✔
493
        }
494
        return nullptr;
2,335✔
495
    }
496

497
    /* -------------------------------------------------------------------- */
498
    /*      If the buffer doesn't exist yet, create it.                     */
499
    /* -------------------------------------------------------------------- */
500
    int bMemoryError = FALSE;
2,388,360✔
501
    GUInt32 *pnAlloc =
502
        static_cast<GUInt32 *>(CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError));
2,388,360✔
503
    if (bMemoryError)
2,388,360✔
504
        return nullptr;
×
505

506
    if (pnAlloc == nullptr)
2,388,360✔
507
    {
508
        pnAlloc = static_cast<GUInt32 *>(VSI_MALLOC_VERBOSE(200));
3,338✔
509
        if (pnAlloc == nullptr)
3,338✔
510
            return nullptr;
×
511
        *pnAlloc = 196;
3,338✔
512
        CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
3,338✔
513
    }
514

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

531
        GUInt32 *pnAllocNew =
532
            static_cast<GUInt32 *>(VSI_REALLOC_VERBOSE(pnAlloc, nNewSize));
681✔
533
        if (pnAllocNew == nullptr)
681✔
534
        {
535
            VSIFree(pnAlloc);
×
536
            CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
×
537
            return nullptr;
×
538
        }
539
        pnAlloc = pnAllocNew;
681✔
540

541
        *pnAlloc = nNewSize - 4;
681✔
542
        CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
681✔
543
    }
544

545
    return reinterpret_cast<char *>(pnAlloc + 1);
2,388,360✔
546
}
547

548
/************************************************************************/
549
/*                            CPLReadLine()                             */
550
/************************************************************************/
551

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

574
const char *CPLReadLine(FILE *fp)
6✔
575

576
{
577
    /* -------------------------------------------------------------------- */
578
    /*      Cleanup case.                                                   */
579
    /* -------------------------------------------------------------------- */
580
    if (fp == nullptr)
6✔
581
    {
582
        CPLReadLineBuffer(-1);
6✔
583
        return nullptr;
6✔
584
    }
585

586
    /* -------------------------------------------------------------------- */
587
    /*      Loop reading chunks of the line till we get to the end of       */
588
    /*      the line.                                                       */
589
    /* -------------------------------------------------------------------- */
590
    size_t nBytesReadThisTime = 0;
×
591
    char *pszRLBuffer = nullptr;
×
592
    size_t nReadSoFar = 0;
×
593

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

610
        /* --------------------------------------------------------------------
611
         */
612
        /*      Do the actual read. */
613
        /* --------------------------------------------------------------------
614
         */
615
        if (CPLFGets(pszRLBuffer + nReadSoFar, 128, fp) == nullptr &&
×
616
            nReadSoFar == 0)
617
            return nullptr;
×
618

619
        nBytesReadThisTime = strlen(pszRLBuffer + nReadSoFar);
×
620
        nReadSoFar += nBytesReadThisTime;
×
621
    } while (nBytesReadThisTime >= 127 && pszRLBuffer[nReadSoFar - 1] != knCR &&
×
622
             pszRLBuffer[nReadSoFar - 1] != knLF);
×
623

624
    return pszRLBuffer;
×
625
}
626

627
/************************************************************************/
628
/*                            CPLReadLineL()                            */
629
/************************************************************************/
630

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

642
const char *CPLReadLineL(VSILFILE *fp)
194,633✔
643
{
644
    return CPLReadLine2L(fp, -1, nullptr);
194,633✔
645
}
646

647
/************************************************************************/
648
/*                           CPLReadLine2L()                            */
649
/************************************************************************/
650

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

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

667
const char *CPLReadLine2L(VSILFILE *fp, int nMaxCars,
1,283,350✔
668
                          CPL_UNUSED CSLConstList papszOptions)
669

670
{
671
    int nBufLength;
672
    return CPLReadLine3L(fp, nMaxCars, &nBufLength, papszOptions);
2,566,700✔
673
}
674

675
/************************************************************************/
676
/*                           CPLReadLine3L()                            */
677
/************************************************************************/
678

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

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

707
    /* -------------------------------------------------------------------- */
708
    /*      Loop reading chunks of the line till we get to the end of       */
709
    /*      the line.                                                       */
710
    /* -------------------------------------------------------------------- */
711
    char *pszRLBuffer = nullptr;
1,343,940✔
712
    const size_t nChunkSize = 40;
1,343,940✔
713
    char szChunk[nChunkSize] = {};
1,343,940✔
714
    size_t nChunkBytesRead = 0;
1,343,940✔
715
    size_t nChunkBytesConsumed = 0;
1,343,940✔
716

717
    *pnBufLength = 0;
1,343,940✔
718
    szChunk[0] = 0;
1,343,940✔
719

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

735
        pszRLBuffer =
736
            CPLReadLineBuffer(static_cast<int>(*pnBufLength + nChunkSize + 1));
2,388,360✔
737
        if (pszRLBuffer == nullptr)
2,388,360✔
738
            return nullptr;
×
739

740
        if (nChunkBytesRead == nChunkBytesConsumed + 1)
2,388,360✔
741
        {
742

743
            // case where one character is left over from last read.
744
            szChunk[0] = szChunk[nChunkBytesConsumed];
1,044,420✔
745

746
            nChunkBytesConsumed = 0;
1,044,420✔
747
            nChunkBytesRead = VSIFReadL(szChunk + 1, 1, nChunkSize - 1, fp) + 1;
1,044,420✔
748
        }
749
        else
750
        {
751
            nChunkBytesConsumed = 0;
1,343,940✔
752

753
            // fresh read.
754
            nChunkBytesRead = VSIFReadL(szChunk, 1, nChunkSize, fp);
1,343,940✔
755
            if (nChunkBytesRead == 0)
1,343,940✔
756
            {
757
                if (*pnBufLength == 0)
11,108✔
758
                    return nullptr;
11,108✔
759

760
                break;
×
761
            }
762
        }
763

764
        /* --------------------------------------------------------------------
765
         */
766
        /*      copy over characters watching for end-of-line. */
767
        /* --------------------------------------------------------------------
768
         */
769
        bool bBreak = false;
2,377,250✔
770
        while (nChunkBytesConsumed < nChunkBytesRead - 1 && !bBreak)
62,541,200✔
771
        {
772
            if ((szChunk[nChunkBytesConsumed] == knCR &&
60,163,900✔
773
                 szChunk[nChunkBytesConsumed + 1] == knLF) ||
172,272✔
774
                (szChunk[nChunkBytesConsumed] == knLF &&
59,991,900✔
775
                 szChunk[nChunkBytesConsumed + 1] == knCR))
1,149,020✔
776
            {
777
                nChunkBytesConsumed += 2;
171,995✔
778
                bBreak = true;
171,995✔
779
            }
780
            else if (szChunk[nChunkBytesConsumed] == knLF ||
59,991,900✔
781
                     szChunk[nChunkBytesConsumed] == knCR)
58,842,900✔
782
            {
783
                nChunkBytesConsumed += 1;
1,149,290✔
784
                bBreak = true;
1,149,290✔
785
            }
786
            else
787
            {
788
                pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
58,842,600✔
789
                if (nMaxCars >= 0 && *pnBufLength == nMaxCars)
58,842,600✔
790
                {
791
                    CPLError(CE_Failure, CPLE_AppDefined,
×
792
                             "Maximum number of characters allowed reached.");
793
                    return nullptr;
×
794
                }
795
            }
796
        }
797

798
        if (bBreak)
2,377,250✔
799
            break;
1,321,290✔
800

801
        /* --------------------------------------------------------------------
802
         */
803
        /*      If there is a remaining character and it is not a newline */
804
        /*      consume it.  If it is a newline, but we are clearly at the */
805
        /*      end of the file then consume it. */
806
        /* --------------------------------------------------------------------
807
         */
808
        if (nChunkBytesConsumed == nChunkBytesRead - 1 &&
1,055,960✔
809
            nChunkBytesRead < nChunkSize)
810
        {
811
            if (szChunk[nChunkBytesConsumed] == knLF ||
11,541✔
812
                szChunk[nChunkBytesConsumed] == knCR)
1,673✔
813
            {
814
                nChunkBytesConsumed++;
9,868✔
815
                break;
9,868✔
816
            }
817

818
            pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
1,673✔
819
            break;
1,673✔
820
        }
821
    }
1,044,420✔
822

823
    /* -------------------------------------------------------------------- */
824
    /*      If we have left over bytes after breaking out, seek back to     */
825
    /*      ensure they remain to be read next time.                        */
826
    /* -------------------------------------------------------------------- */
827
    if (nChunkBytesConsumed < nChunkBytesRead)
1,332,830✔
828
    {
829
        const size_t nBytesToPush = nChunkBytesRead - nChunkBytesConsumed;
1,315,780✔
830

831
        if (VSIFSeekL(fp, VSIFTellL(fp) - nBytesToPush, SEEK_SET) != 0)
1,315,780✔
832
            return nullptr;
×
833
    }
834

835
    pszRLBuffer[*pnBufLength] = '\0';
1,332,830✔
836

837
    return pszRLBuffer;
1,332,830✔
838
}
839

840
/************************************************************************/
841
/*                            CPLScanString()                           */
842
/************************************************************************/
843

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

864
char *CPLScanString(const char *pszString, int nMaxLength, int bTrimSpaces,
5,084✔
865
                    int bNormalize)
866
{
867
    if (!pszString)
5,084✔
868
        return nullptr;
×
869

870
    if (!nMaxLength)
5,084✔
871
        return CPLStrdup("");
2✔
872

873
    char *pszBuffer = static_cast<char *>(CPLMalloc(nMaxLength + 1));
5,082✔
874
    if (!pszBuffer)
5,082✔
875
        return nullptr;
×
876

877
    strncpy(pszBuffer, pszString, nMaxLength);
5,082✔
878
    pszBuffer[nMaxLength] = '\0';
5,082✔
879

880
    if (bTrimSpaces)
5,082✔
881
    {
882
        size_t i = strlen(pszBuffer);
5,079✔
883
        while (i > 0)
6,203✔
884
        {
885
            i--;
6,169✔
886
            if (!isspace(static_cast<unsigned char>(pszBuffer[i])))
6,169✔
887
                break;
5,045✔
888
            pszBuffer[i] = '\0';
1,124✔
889
        }
890
    }
891

892
    if (bNormalize)
5,082✔
893
    {
894
        size_t i = strlen(pszBuffer);
4,961✔
895
        while (i > 0)
37,987✔
896
        {
897
            i--;
33,026✔
898
            if (pszBuffer[i] == ':')
33,026✔
899
                pszBuffer[i] = '_';
×
900
        }
901
    }
902

903
    return pszBuffer;
5,082✔
904
}
905

906
/************************************************************************/
907
/*                             CPLScanLong()                            */
908
/************************************************************************/
909

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

924
long CPLScanLong(const char *pszString, int nMaxLength)
1,358✔
925
{
926
    CPLAssert(nMaxLength >= 0);
1,358✔
927
    if (pszString == nullptr)
1,358✔
928
        return 0;
×
929
    const size_t nLength = CPLStrnlen(pszString, nMaxLength);
1,358✔
930
    const std::string osValue(pszString, nLength);
2,716✔
931
    return atol(osValue.c_str());
1,358✔
932
}
933

934
/************************************************************************/
935
/*                            CPLScanULong()                            */
936
/************************************************************************/
937

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

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

962
/************************************************************************/
963
/*                           CPLScanUIntBig()                           */
964
/************************************************************************/
965

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

982
GUIntBig CPLScanUIntBig(const char *pszString, int nMaxLength)
20,583✔
983
{
984
    CPLAssert(nMaxLength >= 0);
20,583✔
985
    if (pszString == nullptr)
20,583✔
986
        return 0;
×
987
    const size_t nLength = CPLStrnlen(pszString, nMaxLength);
20,583✔
988
    const std::string osValue(pszString, nLength);
41,166✔
989

990
    /* -------------------------------------------------------------------- */
991
    /*      Fetch out the result                                            */
992
    /* -------------------------------------------------------------------- */
993
    return strtoull(osValue.c_str(), nullptr, 10);
20,583✔
994
}
995

996
/************************************************************************/
997
/*                           CPLAtoGIntBig()                            */
998
/************************************************************************/
999

1000
/**
1001
 * Convert a string to a 64 bit signed integer.
1002
 *
1003
 * @param pszString String containing 64 bit signed integer.
1004
 * @return 64 bit signed integer.
1005
 * @since GDAL 2.0
1006
 */
1007

1008
GIntBig CPLAtoGIntBig(const char *pszString)
105,324✔
1009
{
1010
    return atoll(pszString);
105,324✔
1011
}
1012

1013
#if defined(__MINGW32__) || defined(__sun__)
1014

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

1037
#endif
1038

1039
/************************************************************************/
1040
/*                          CPLAtoGIntBigEx()                           */
1041
/************************************************************************/
1042

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

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

1082
/************************************************************************/
1083
/*                           CPLScanPointer()                           */
1084
/************************************************************************/
1085

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

1102
void *CPLScanPointer(const char *pszString, int nMaxLength)
973✔
1103
{
1104
    char szTemp[128] = {};
973✔
1105

1106
    /* -------------------------------------------------------------------- */
1107
    /*      Compute string into local buffer, and terminate it.             */
1108
    /* -------------------------------------------------------------------- */
1109
    if (nMaxLength > static_cast<int>(sizeof(szTemp)) - 1)
973✔
1110
        nMaxLength = sizeof(szTemp) - 1;
×
1111

1112
    strncpy(szTemp, pszString, nMaxLength);
973✔
1113
    szTemp[nMaxLength] = '\0';
973✔
1114

1115
    /* -------------------------------------------------------------------- */
1116
    /*      On MSVC we have to scanf pointer values without the 0x          */
1117
    /*      prefix.                                                         */
1118
    /* -------------------------------------------------------------------- */
1119
    if (STARTS_WITH_CI(szTemp, "0x"))
973✔
1120
    {
1121
        void *pResult = nullptr;
974✔
1122

1123
#if defined(__MSVCRT__) || (defined(_WIN32) && defined(_MSC_VER))
1124
        // cppcheck-suppress invalidscanf
1125
        sscanf(szTemp + 2, "%p", &pResult);
1126
#else
1127
        // cppcheck-suppress invalidscanf
1128
        sscanf(szTemp, "%p", &pResult);
974✔
1129

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

1140
#if SIZEOF_VOIDP == 8
1141
    return reinterpret_cast<void *>(CPLScanUIntBig(szTemp, nMaxLength));
×
1142
#else
1143
    return reinterpret_cast<void *>(CPLScanULong(szTemp, nMaxLength));
1144
#endif
1145
}
1146

1147
/************************************************************************/
1148
/*                             CPLScanDouble()                          */
1149
/************************************************************************/
1150

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

1168
double CPLScanDouble(const char *pszString, int nMaxLength)
317✔
1169
{
1170
    char szValue[32] = {};
317✔
1171
    char *pszValue = nullptr;
317✔
1172

1173
    if (nMaxLength + 1 < static_cast<int>(sizeof(szValue)))
317✔
1174
        pszValue = szValue;
317✔
1175
    else
1176
        pszValue = static_cast<char *>(CPLMalloc(nMaxLength + 1));
×
1177

1178
    /* -------------------------------------------------------------------- */
1179
    /*      Compute string into local buffer, and terminate it.             */
1180
    /* -------------------------------------------------------------------- */
1181
    strncpy(pszValue, pszString, nMaxLength);
317✔
1182
    pszValue[nMaxLength] = '\0';
317✔
1183

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

1191
    /* -------------------------------------------------------------------- */
1192
    /*      The conversion itself.                                          */
1193
    /* -------------------------------------------------------------------- */
1194
    const double dfValue = CPLAtof(pszValue);
317✔
1195

1196
    if (pszValue != szValue)
317✔
1197
        CPLFree(pszValue);
×
1198
    return dfValue;
317✔
1199
}
1200

1201
/************************************************************************/
1202
/*                      CPLPrintString()                                */
1203
/************************************************************************/
1204

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

1220
int CPLPrintString(char *pszDest, const char *pszSrc, int nMaxLen)
9,330✔
1221
{
1222
    if (!pszDest)
9,330✔
1223
        return 0;
×
1224

1225
    if (!pszSrc)
9,330✔
1226
    {
1227
        *pszDest = '\0';
×
1228
        return 1;
×
1229
    }
1230

1231
    int nChars = 0;
9,330✔
1232
    char *pszTemp = pszDest;
9,330✔
1233

1234
    while (nChars < nMaxLen && *pszSrc)
138,413✔
1235
    {
1236
        *pszTemp++ = *pszSrc++;
129,083✔
1237
        nChars++;
129,083✔
1238
    }
1239

1240
    return nChars;
9,330✔
1241
}
1242

1243
/************************************************************************/
1244
/*                         CPLPrintStringFill()                         */
1245
/************************************************************************/
1246

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

1264
int CPLPrintStringFill(char *pszDest, const char *pszSrc, int nMaxLen)
203✔
1265
{
1266
    if (!pszDest)
203✔
1267
        return 0;
×
1268

1269
    if (!pszSrc)
203✔
1270
    {
1271
        memset(pszDest, ' ', nMaxLen);
×
1272
        return nMaxLen;
×
1273
    }
1274

1275
    char *pszTemp = pszDest;
203✔
1276
    while (nMaxLen && *pszSrc)
1,219✔
1277
    {
1278
        *pszTemp++ = *pszSrc++;
1,016✔
1279
        nMaxLen--;
1,016✔
1280
    }
1281

1282
    if (nMaxLen)
203✔
1283
        memset(pszTemp, ' ', nMaxLen);
69✔
1284

1285
    return nMaxLen;
203✔
1286
}
1287

1288
/************************************************************************/
1289
/*                          CPLPrintInt32()                             */
1290
/************************************************************************/
1291

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

1308
int CPLPrintInt32(char *pszBuffer, GInt32 iValue, int nMaxLen)
9✔
1309
{
1310
    if (!pszBuffer)
9✔
1311
        return 0;
×
1312

1313
    if (nMaxLen >= 64)
9✔
1314
        nMaxLen = 63;
×
1315

1316
    char szTemp[64] = {};
9✔
1317

1318
#if UINT_MAX == 65535
1319
    snprintf(szTemp, sizeof(szTemp), "%*ld", nMaxLen, iValue);
1320
#else
1321
    snprintf(szTemp, sizeof(szTemp), "%*d", nMaxLen, iValue);
9✔
1322
#endif
1323

1324
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
9✔
1325
}
1326

1327
/************************************************************************/
1328
/*                          CPLPrintUIntBig()                           */
1329
/************************************************************************/
1330

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

1347
int CPLPrintUIntBig(char *pszBuffer, GUIntBig iValue, int nMaxLen)
26✔
1348
{
1349
    if (!pszBuffer)
26✔
1350
        return 0;
×
1351

1352
    if (nMaxLen >= 64)
26✔
1353
        nMaxLen = 63;
×
1354

1355
    char szTemp[64] = {};
26✔
1356

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

1372
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
26✔
1373
}
1374

1375
/************************************************************************/
1376
/*                          CPLPrintPointer()                           */
1377
/************************************************************************/
1378

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

1395
int CPLPrintPointer(char *pszBuffer, void *pValue, int nMaxLen)
9,269✔
1396
{
1397
    if (!pszBuffer)
9,269✔
1398
        return 0;
×
1399

1400
    if (nMaxLen >= 64)
9,269✔
1401
        nMaxLen = 63;
8,298✔
1402

1403
    char szTemp[64] = {};
9,269✔
1404

1405
    snprintf(szTemp, sizeof(szTemp), "%p", pValue);
9,269✔
1406

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

1411
    if (!STARTS_WITH_CI(szTemp, "0x"))
9,269✔
1412
        snprintf(szTemp, sizeof(szTemp), "0x%p", pValue);
×
1413

1414
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
9,269✔
1415
}
1416

1417
/************************************************************************/
1418
/*                          CPLPrintDouble()                            */
1419
/************************************************************************/
1420

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

1439
int CPLPrintDouble(char *pszBuffer, const char *pszFormat, double dfValue,
×
1440
                   CPL_UNUSED const char *pszLocale)
1441
{
1442
    if (!pszBuffer)
×
1443
        return 0;
×
1444

1445
    const int knDoubleBufferSize = 64;
×
1446
    char szTemp[knDoubleBufferSize] = {};
×
1447

1448
    CPLsnprintf(szTemp, knDoubleBufferSize, pszFormat, dfValue);
×
1449
    szTemp[knDoubleBufferSize - 1] = '\0';
×
1450

1451
    for (int i = 0; szTemp[i] != '\0'; i++)
×
1452
    {
1453
        if (szTemp[i] == 'E' || szTemp[i] == 'e')
×
1454
            szTemp[i] = 'D';
×
1455
    }
1456

1457
    return CPLPrintString(pszBuffer, szTemp, 64);
×
1458
}
1459

1460
/************************************************************************/
1461
/*                            CPLPrintTime()                            */
1462
/************************************************************************/
1463

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

1497
int CPLPrintTime(char *pszBuffer, int nMaxLen, const char *pszFormat,
31✔
1498
                 const struct tm *poBrokenTime, const char *pszLocale)
1499
{
1500
    char *pszTemp =
1501
        static_cast<char *>(CPLMalloc((nMaxLen + 1) * sizeof(char)));
31✔
1502

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

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

1536
        if (!strftime(pszTemp, nMaxLen + 1, pszFormat, poBrokenTime))
×
1537
            memset(pszTemp, 0, nMaxLen + 1);
×
1538

1539
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
1540
        // Restore stored locale back.
1541
        if (pszCurLocale)
1542
            CPLsetlocale(LC_ALL, pszCurLocale);
1543
#endif
1544
    }
1545

1546
    const int nChars = CPLPrintString(pszBuffer, pszTemp, nMaxLen);
31✔
1547

1548
    CPLFree(pszTemp);
31✔
1549

1550
    return nChars;
31✔
1551
}
1552

1553
/************************************************************************/
1554
/*                       CPLVerifyConfiguration()                       */
1555
/************************************************************************/
1556

1557
void CPLVerifyConfiguration()
×
1558

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

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

1592
#ifdef DEBUG_CONFIG_OPTIONS
1593

1594
static CPLMutex *hRegisterConfigurationOptionMutex = nullptr;
1595
static std::set<CPLString> *paoGetKeys = nullptr;
1596
static std::set<CPLString> *paoSetKeys = nullptr;
1597

1598
/************************************************************************/
1599
/*                      CPLShowAccessedOptions()                        */
1600
/************************************************************************/
1601

1602
static void CPLShowAccessedOptions()
1603
{
1604
    std::set<CPLString>::iterator aoIter;
1605

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

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

1624
    delete paoGetKeys;
1625
    delete paoSetKeys;
1626
    paoGetKeys = nullptr;
1627
    paoSetKeys = nullptr;
1628
}
1629

1630
/************************************************************************/
1631
/*                       CPLAccessConfigOption()                        */
1632
/************************************************************************/
1633

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

1650
/************************************************************************/
1651
/*                         CPLGetConfigOption()                         */
1652
/************************************************************************/
1653

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

1690
{
1691
    const char *pszResult = CPLGetThreadLocalConfigOption(pszKey, nullptr);
4,829,290✔
1692

1693
    if (pszResult == nullptr)
4,828,150✔
1694
    {
1695
        pszResult = CPLGetGlobalConfigOption(pszKey, nullptr);
4,790,750✔
1696
    }
1697

1698
    if (gbIgnoreEnvVariables)
4,829,900✔
1699
    {
1700
        const char *pszEnvVar = getenv(pszKey);
6✔
1701
        if (pszEnvVar != nullptr)
6✔
1702
        {
1703
            CPLDebug("CPL",
1✔
1704
                     "Ignoring environment variable %s=%s because of "
1705
                     "ignore-env-vars=yes setting in configuration file",
1706
                     pszKey, pszEnvVar);
1707
        }
1708
    }
1709
    else if (pszResult == nullptr)
4,829,890✔
1710
    {
1711
        pszResult = getenv(pszKey);
4,784,820✔
1712
    }
1713

1714
    if (pszResult == nullptr)
4,829,820✔
1715
        return pszDefault;
4,775,750✔
1716

1717
    return pszResult;
54,077✔
1718
}
1719

1720
/************************************************************************/
1721
/*                         CPLGetConfigOptions()                        */
1722
/************************************************************************/
1723

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

1741
/************************************************************************/
1742
/*                         CPLSetConfigOptions()                        */
1743
/************************************************************************/
1744

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

1769
/************************************************************************/
1770
/*                   CPLGetThreadLocalConfigOption()                    */
1771
/************************************************************************/
1772

1773
/** Same as CPLGetConfigOption() but only with options set with
1774
 * CPLSetThreadLocalConfigOption() */
1775
const char *CPL_STDCALL CPLGetThreadLocalConfigOption(const char *pszKey,
4,855,140✔
1776
                                                      const char *pszDefault)
1777

1778
{
1779
#ifdef DEBUG_CONFIG_OPTIONS
1780
    CPLAccessConfigOption(pszKey, TRUE);
1781
#endif
1782

1783
    const char *pszResult = nullptr;
4,855,140✔
1784

1785
    int bMemoryError = FALSE;
4,855,140✔
1786
    char **papszTLConfigOptions = reinterpret_cast<char **>(
1787
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
4,855,140✔
1788
    if (papszTLConfigOptions != nullptr)
4,854,280✔
1789
        pszResult = CSLFetchNameValue(papszTLConfigOptions, pszKey);
4,457,530✔
1790

1791
    if (pszResult == nullptr)
4,854,370✔
1792
        return pszDefault;
4,816,460✔
1793

1794
    return pszResult;
37,909✔
1795
}
1796

1797
/************************************************************************/
1798
/*                   CPLGetGlobalConfigOption()                         */
1799
/************************************************************************/
1800

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

1813
    CPLMutexHolderD(&hConfigMutex);
9,587,550✔
1814

1815
    const char *pszResult =
1816
        CSLFetchNameValue(const_cast<char **>(g_papszConfigOptions), pszKey);
4,794,380✔
1817

1818
    if (pszResult == nullptr)
4,794,380✔
1819
        return pszDefault;
4,786,510✔
1820

1821
    return pszResult;
7,875✔
1822
}
1823

1824
/************************************************************************/
1825
/*                    CPLSubscribeToSetConfigOption()                   */
1826
/************************************************************************/
1827

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

1838
int CPLSubscribeToSetConfigOption(CPLSetConfigOptionSubscriber pfnCallback,
1,207✔
1839
                                  void *pUserData)
1840
{
1841
    CPLMutexHolderD(&hConfigMutex);
2,414✔
1842
    for (int nId = 0;
1,212✔
1843
         nId < static_cast<int>(gSetConfigOptionSubscribers.size()); ++nId)
1,212✔
1844
    {
1845
        if (!gSetConfigOptionSubscribers[nId].first)
6✔
1846
        {
1847
            gSetConfigOptionSubscribers[nId].first = pfnCallback;
1✔
1848
            gSetConfigOptionSubscribers[nId].second = pUserData;
1✔
1849
            return nId;
1✔
1850
        }
1851
    }
1852
    int nId = static_cast<int>(gSetConfigOptionSubscribers.size());
1,206✔
1853
    gSetConfigOptionSubscribers.push_back(
1,206✔
1854
        std::pair<CPLSetConfigOptionSubscriber, void *>(pfnCallback,
1,206✔
1855
                                                        pUserData));
1856
    return nId;
1,206✔
1857
}
1858

1859
/************************************************************************/
1860
/*                  CPLUnsubscribeToSetConfigOption()                   */
1861
/************************************************************************/
1862

1863
/**
1864
 * Remove a subscriber installed with CPLSubscribeToSetConfigOption()
1865
 *
1866
 * @param nId Subscriber id returned by CPLSubscribeToSetConfigOption()
1867
 * @since GDAL 3.7
1868
 */
1869

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

1885
/************************************************************************/
1886
/*                  NotifyOtherComponentsConfigOptionChanged()          */
1887
/************************************************************************/
1888

1889
static void NotifyOtherComponentsConfigOptionChanged(const char *pszKey,
61,847✔
1890
                                                     const char *pszValue,
1891
                                                     bool bThreadLocal)
1892
{
1893
    // Hack
1894
    if (STARTS_WITH_CI(pszKey, "AWS_"))
61,847✔
1895
        VSICurlAuthParametersChanged();
2,352✔
1896

1897
    if (!gSetConfigOptionSubscribers.empty())
61,847✔
1898
    {
1899
        for (const auto &iter : gSetConfigOptionSubscribers)
122,322✔
1900
        {
1901
            if (iter.first)
61,284✔
1902
                iter.first(pszKey, pszValue, bThreadLocal, iter.second);
61,399✔
1903
        }
1904
    }
1905
}
61,718✔
1906

1907
/************************************************************************/
1908
/*                       CPLIsDebugEnabled()                            */
1909
/************************************************************************/
1910

1911
static int gnDebug = -1;
1912

1913
/** Returns whether CPL_DEBUG is enabled.
1914
 *
1915
 * @since 3.11
1916
 */
1917
bool CPLIsDebugEnabled()
67,202✔
1918
{
1919
    if (gnDebug < 0)
67,202✔
1920
    {
1921
        // Check that apszKnownConfigOptions is correctly sorted with
1922
        // STRCASECMP() criterion.
1923
        for (size_t i = 1; i < CPL_ARRAYSIZE(apszKnownConfigOptions); ++i)
476,707✔
1924
        {
1925
            if (STRCASECMP(apszKnownConfigOptions[i - 1],
476,256✔
1926
                           apszKnownConfigOptions[i]) >= 0)
1927
            {
1928
                CPLError(CE_Failure, CPLE_AppDefined,
×
1929
                         "ERROR: apszKnownConfigOptions[] isn't correctly "
1930
                         "sorted: %s >= %s",
1931
                         apszKnownConfigOptions[i - 1],
×
1932
                         apszKnownConfigOptions[i]);
×
1933
            }
1934
        }
1935
        gnDebug = CPLTestBool(CPLGetConfigOption("CPL_DEBUG", "OFF"));
451✔
1936
    }
1937

1938
    return gnDebug != 0;
67,162✔
1939
}
1940

1941
/************************************************************************/
1942
/*                       CPLDeclareKnownConfigOption()                  */
1943
/************************************************************************/
1944

1945
static std::mutex goMutexDeclaredKnownConfigOptions;
1946
static std::set<CPLString> goSetKnownConfigOptions;
1947

1948
/** Declare that the specified configuration option is known.
1949
 *
1950
 * This is useful to avoid a warning to be emitted on unknown configuration
1951
 * options when CPL_DEBUG is enabled.
1952
 *
1953
 * @param pszKey Name of the configuration option to declare.
1954
 * @param pszDefinition Unused for now. Must be set to nullptr.
1955
 * @since 3.11
1956
 */
1957
void CPLDeclareKnownConfigOption(const char *pszKey,
1✔
1958
                                 [[maybe_unused]] const char *pszDefinition)
1959
{
1960
    std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
1✔
1961
    goSetKnownConfigOptions.insert(CPLString(pszKey).toupper());
1✔
1962
}
1✔
1963

1964
/************************************************************************/
1965
/*                       CPLGetKnownConfigOptions()                     */
1966
/************************************************************************/
1967

1968
/** Return the list of known configuration options.
1969
 *
1970
 * Must be freed with CSLDestroy().
1971
 * @since 3.11
1972
 */
1973
char **CPLGetKnownConfigOptions()
3✔
1974
{
1975
    std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
6✔
1976
    CPLStringList aosList;
6✔
1977
    for (const char *pszKey : apszKnownConfigOptions)
3,174✔
1978
        aosList.AddString(pszKey);
3,171✔
1979
    for (const auto &osKey : goSetKnownConfigOptions)
4✔
1980
        aosList.AddString(osKey);
1✔
1981
    return aosList.StealList();
6✔
1982
}
1983

1984
/************************************************************************/
1985
/*           CPLSetConfigOptionDetectUnknownConfigOption()              */
1986
/************************************************************************/
1987

1988
static void CPLSetConfigOptionDetectUnknownConfigOption(const char *pszKey,
61,971✔
1989
                                                        const char *pszValue)
1990
{
1991
    if (EQUAL(pszKey, "CPL_DEBUG"))
61,971✔
1992
    {
1993
        gnDebug = pszValue ? CPLTestBool(pszValue) : false;
96✔
1994
    }
1995
    else if (CPLIsDebugEnabled())
61,875✔
1996
    {
1997
        if (!std::binary_search(std::begin(apszKnownConfigOptions),
87✔
1998
                                std::end(apszKnownConfigOptions), pszKey,
1999
                                [](const char *a, const char *b)
963✔
2000
                                { return STRCASECMP(a, b) < 0; }))
963✔
2001
        {
2002
            bool bFound;
2003
            {
2004
                std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
4✔
2005
                bFound = cpl::contains(goSetKnownConfigOptions,
8✔
2006
                                       CPLString(pszKey).toupper());
4✔
2007
            }
2008
            if (!bFound)
4✔
2009
            {
2010
                const char *pszOldValue = CPLGetConfigOption(pszKey, nullptr);
2✔
2011
                if (!((!pszValue && !pszOldValue) ||
2✔
2012
                      (pszValue && pszOldValue &&
1✔
2013
                       EQUAL(pszValue, pszOldValue))))
×
2014
                {
2015
                    CPLError(CE_Warning, CPLE_AppDefined,
2✔
2016
                             "Unknown configuration option '%s'.", pszKey);
2017
                }
2018
            }
2019
        }
2020
    }
2021
}
61,857✔
2022

2023
/************************************************************************/
2024
/*                         CPLSetConfigOption()                         */
2025
/************************************************************************/
2026

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

2060
{
2061
#ifdef DEBUG_CONFIG_OPTIONS
2062
    CPLAccessConfigOption(pszKey, FALSE);
2063
#endif
2064
    CPLMutexHolderD(&hConfigMutex);
9,516✔
2065

2066
#ifdef OGRAPISPY_ENABLED
2067
    OGRAPISPYCPLSetConfigOption(pszKey, pszValue);
4,758✔
2068
#endif
2069

2070
    CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
4,758✔
2071

2072
    g_papszConfigOptions = const_cast<volatile char **>(CSLSetNameValue(
4,758✔
2073
        const_cast<char **>(g_papszConfigOptions), pszKey, pszValue));
2074

2075
    NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
4,758✔
2076
                                             /*bTheadLocal=*/false);
2077
}
4,758✔
2078

2079
/************************************************************************/
2080
/*                   CPLSetThreadLocalTLSFreeFunc()                     */
2081
/************************************************************************/
2082

2083
/* non-stdcall wrapper function for CSLDestroy() (#5590) */
2084
static void CPLSetThreadLocalTLSFreeFunc(void *pData)
6✔
2085
{
2086
    CSLDestroy(reinterpret_cast<char **>(pData));
6✔
2087
}
6✔
2088

2089
/************************************************************************/
2090
/*                   CPLSetThreadLocalConfigOption()                    */
2091
/************************************************************************/
2092

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

2114
void CPL_STDCALL CPLSetThreadLocalConfigOption(const char *pszKey,
57,269✔
2115
                                               const char *pszValue)
2116

2117
{
2118
#ifdef DEBUG_CONFIG_OPTIONS
2119
    CPLAccessConfigOption(pszKey, FALSE);
2120
#endif
2121

2122
#ifdef OGRAPISPY_ENABLED
2123
    OGRAPISPYCPLSetThreadLocalConfigOption(pszKey, pszValue);
57,269✔
2124
#endif
2125

2126
    int bMemoryError = FALSE;
57,203✔
2127
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2128
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
57,203✔
2129
    if (bMemoryError)
57,171✔
2130
        return;
×
2131

2132
    CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
57,171✔
2133

2134
    papszTLConfigOptions =
2135
        CSLSetNameValue(papszTLConfigOptions, pszKey, pszValue);
57,114✔
2136

2137
    CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
57,062✔
2138
                          CPLSetThreadLocalTLSFreeFunc);
2139

2140
    NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
56,989✔
2141
                                             /*bTheadLocal=*/true);
2142
}
2143

2144
/************************************************************************/
2145
/*                   CPLGetThreadLocalConfigOptions()                   */
2146
/************************************************************************/
2147

2148
/**
2149
 * Return the list of thread local configuration options as KEY=VALUE pairs.
2150
 *
2151
 * Options that through environment variables or with
2152
 * CPLSetConfigOption() will *not* be listed.
2153
 *
2154
 * @return a copy of the list, to be freed with CSLDestroy().
2155
 * @since GDAL 2.2
2156
 */
2157
char **CPLGetThreadLocalConfigOptions(void)
744,836✔
2158
{
2159
    int bMemoryError = FALSE;
744,836✔
2160
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2161
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
744,836✔
2162
    if (bMemoryError)
740,965✔
2163
        return nullptr;
×
2164
    return CSLDuplicate(papszTLConfigOptions);
740,965✔
2165
}
2166

2167
/************************************************************************/
2168
/*                   CPLSetThreadLocalConfigOptions()                   */
2169
/************************************************************************/
2170

2171
/**
2172
 * Replace the full list of thread local configuration options with the
2173
 * passed list of KEY=VALUE pairs.
2174
 *
2175
 * This has the same effect of clearing the existing list, and setting
2176
 * individually each pair with the CPLSetThreadLocalConfigOption() API.
2177
 *
2178
 * This does not affect options set through environment variables or with
2179
 * CPLSetConfigOption().
2180
 *
2181
 * The passed list is copied by the function.
2182
 *
2183
 * @param papszConfigOptions the new list (or NULL).
2184
 *
2185
 * @since GDAL 2.2
2186
 */
2187
void CPLSetThreadLocalConfigOptions(const char *const *papszConfigOptions)
1,484,260✔
2188
{
2189
    int bMemoryError = FALSE;
1,484,260✔
2190
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2191
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
1,484,260✔
2192
    if (bMemoryError)
1,479,720✔
2193
        return;
×
2194
    CSLDestroy(papszTLConfigOptions);
1,479,720✔
2195
    papszTLConfigOptions =
2196
        CSLDuplicate(const_cast<char **>(papszConfigOptions));
1,475,590✔
2197
    CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
1,480,260✔
2198
                          CPLSetThreadLocalTLSFreeFunc);
2199
}
2200

2201
/************************************************************************/
2202
/*                           CPLFreeConfig()                            */
2203
/************************************************************************/
2204

2205
void CPL_STDCALL CPLFreeConfig()
1,380✔
2206

2207
{
2208
    {
2209
        CPLMutexHolderD(&hConfigMutex);
2,760✔
2210

2211
        CSLDestroy(const_cast<char **>(g_papszConfigOptions));
1,380✔
2212
        g_papszConfigOptions = nullptr;
1,380✔
2213

2214
        int bMemoryError = FALSE;
1,380✔
2215
        char **papszTLConfigOptions = reinterpret_cast<char **>(
2216
            CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
1,380✔
2217
        if (papszTLConfigOptions != nullptr)
1,380✔
2218
        {
2219
            CSLDestroy(papszTLConfigOptions);
221✔
2220
            CPLSetTLS(CTLS_CONFIGOPTIONS, nullptr, FALSE);
221✔
2221
        }
2222
    }
2223
    CPLDestroyMutex(hConfigMutex);
1,380✔
2224
    hConfigMutex = nullptr;
1,380✔
2225
}
1,380✔
2226

2227
/************************************************************************/
2228
/*                    CPLLoadConfigOptionsFromFile()                    */
2229
/************************************************************************/
2230

2231
/** Load configuration from a given configuration file.
2232

2233
A configuration file is a text file in a .ini style format, that lists
2234
configuration options and their values.
2235
Lines starting with # are comment lines.
2236

2237
Example:
2238
\verbatim
2239
[configoptions]
2240
# set BAR as the value of configuration option FOO
2241
FOO=BAR
2242
\endverbatim
2243

2244
Starting with GDAL 3.5, a configuration file can also contain credentials
2245
(or more generally options related to a virtual file system) for a given path
2246
prefix, that can also be set with VSISetPathSpecificOption(). Credentials should
2247
be put under a [credentials] section, and for each path prefix, under a relative
2248
subsection whose name starts with "[." (e.g. "[.some_arbitrary_name]"), and
2249
whose first key is "path".
2250

2251
Example:
2252
\verbatim
2253
[credentials]
2254

2255
[.private_bucket]
2256
path=/vsis3/my_private_bucket
2257
AWS_SECRET_ACCESS_KEY=...
2258
AWS_ACCESS_KEY_ID=...
2259

2260
[.sentinel_s2_l1c]
2261
path=/vsis3/sentinel-s2-l1c
2262
AWS_REQUEST_PAYER=requester
2263
\endverbatim
2264

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

2271
This function is typically called by CPLLoadConfigOptionsFromPredefinedFiles()
2272

2273
@param pszFilename File where to load configuration from.
2274
@param bOverrideEnvVars Whether configuration options from the configuration
2275
                        file should override environment variables.
2276
@since GDAL 3.3
2277
 */
2278
void CPLLoadConfigOptionsFromFile(const char *pszFilename, int bOverrideEnvVars)
2,780✔
2279
{
2280
    VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
2,780✔
2281
    if (fp == nullptr)
2,780✔
2282
        return;
2,771✔
2283
    CPLDebug("CPL", "Loading configuration from %s", pszFilename);
9✔
2284
    const char *pszLine;
2285
    enum class Section
2286
    {
2287
        NONE,
2288
        GENERAL,
2289
        CONFIG_OPTIONS,
2290
        CREDENTIALS,
2291
    };
2292
    Section eCurrentSection = Section::NONE;
9✔
2293
    bool bInSubsection = false;
9✔
2294
    std::string osPath;
18✔
2295
    int nSectionCounter = 0;
9✔
2296

2297
    const auto IsSpaceOnly = [](const char *pszStr)
56✔
2298
    {
2299
        for (; *pszStr; ++pszStr)
56✔
2300
        {
2301
            if (!isspace(static_cast<unsigned char>(*pszStr)))
47✔
2302
                return false;
41✔
2303
        }
2304
        return true;
9✔
2305
    };
2306

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

2445
/************************************************************************/
2446
/*                CPLLoadConfigOptionsFromPredefinedFiles()             */
2447
/************************************************************************/
2448

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

2489
#ifdef _WIN32
2490
        const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
2491
#else
2492
        const char *pszHome = CPLGetConfigOption("HOME", nullptr);
1,385✔
2493
#endif
2494
        if (pszHome != nullptr)
1,385✔
2495
        {
2496
            pszFile = CPLFormFilename(
1,385✔
2497
                CPLFormFilename(pszHome, ".gdal", nullptr), "gdalrc", nullptr);
2498
            CPLLoadConfigOptionsFromFile(pszFile, false);
1,385✔
2499
        }
2500
    }
2501
}
1,387✔
2502

2503
/************************************************************************/
2504
/*                              CPLStat()                               */
2505
/************************************************************************/
2506

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

2509
int CPLStat(const char *pszPath, VSIStatBuf *psStatBuf)
66✔
2510

2511
{
2512
    if (strlen(pszPath) == 2 && pszPath[1] == ':')
66✔
2513
    {
2514
        char szAltPath[4] = {pszPath[0], pszPath[1], '\\', '\0'};
×
2515
        return VSIStat(szAltPath, psStatBuf);
×
2516
    }
2517

2518
    return VSIStat(pszPath, psStatBuf);
66✔
2519
}
2520

2521
/************************************************************************/
2522
/*                            proj_strtod()                             */
2523
/************************************************************************/
2524
static double proj_strtod(char *nptr, char **endptr)
20✔
2525

2526
{
2527
    char c = '\0';
20✔
2528
    char *cp = nptr;
20✔
2529

2530
    // Scan for characters which cause problems with VC++ strtod().
2531
    while ((c = *cp) != '\0')
92✔
2532
    {
2533
        if (c == 'd' || c == 'D')
78✔
2534
        {
2535
            // Found one, so NUL it out, call strtod(),
2536
            // then restore it and return.
2537
            *cp = '\0';
6✔
2538
            const double result = CPLStrtod(nptr, endptr);
6✔
2539
            *cp = c;
6✔
2540
            return result;
6✔
2541
        }
2542
        ++cp;
72✔
2543
    }
2544

2545
    // No offending characters, just handle normally.
2546

2547
    return CPLStrtod(nptr, endptr);
14✔
2548
}
2549

2550
/************************************************************************/
2551
/*                            CPLDMSToDec()                             */
2552
/************************************************************************/
2553

2554
static const char *sym = "NnEeSsWw";
2555
constexpr double vm[] = {1.0, 0.0166666666667, 0.00027777778};
2556

2557
/** CPLDMSToDec */
2558
double CPLDMSToDec(const char *is)
8✔
2559

2560
{
2561
    // Copy string into work space.
2562
    while (isspace(static_cast<unsigned char>(*is)))
8✔
2563
        ++is;
×
2564

2565
    const char *p = is;
8✔
2566
    char work[64] = {};
8✔
2567
    char *s = work;
8✔
2568
    int n = sizeof(work);
8✔
2569
    for (; isgraph(*p) && --n;)
68✔
2570
        *s++ = *p++;
60✔
2571
    *s = '\0';
8✔
2572
    // It is possible that a really odd input (like lots of leading
2573
    // zeros) could be truncated in copying into work.  But...
2574
    s = work;
8✔
2575
    int sign = *s;
8✔
2576

2577
    if (sign == '+' || sign == '-')
8✔
2578
        s++;
×
2579
    else
2580
        sign = '+';
8✔
2581

2582
    int nl = 0;
8✔
2583
    double v = 0.0;
8✔
2584
    for (; nl < 3; nl = n + 1)
28✔
2585
    {
2586
        if (!(isdigit(static_cast<unsigned char>(*s)) || *s == '.'))
20✔
2587
            break;
×
2588
        const double tv = proj_strtod(s, &s);
20✔
2589
        if (tv == HUGE_VAL)
20✔
2590
            return tv;
×
2591
        switch (*s)
20✔
2592
        {
2593
            case 'D':
6✔
2594
            case 'd':
2595
                n = 0;
6✔
2596
                break;
6✔
2597
            case '\'':
6✔
2598
                n = 1;
6✔
2599
                break;
6✔
2600
            case '"':
6✔
2601
                n = 2;
6✔
2602
                break;
6✔
2603
            case 'r':
×
2604
            case 'R':
2605
                if (nl)
×
2606
                {
2607
                    return 0.0;
×
2608
                }
2609
                ++s;
×
2610
                v = tv;
×
2611
                goto skip;
×
2612
            default:
2✔
2613
                v += tv * vm[nl];
2✔
2614
            skip:
2✔
2615
                n = 4;
2✔
2616
                continue;
2✔
2617
        }
2618
        if (n < nl)
18✔
2619
        {
2620
            return 0.0;
×
2621
        }
2622
        v += tv * vm[n];
18✔
2623
        ++s;
18✔
2624
    }
2625
    // Postfix sign.
2626
    if (*s && ((p = strchr(sym, *s))) != nullptr)
8✔
2627
    {
2628
        sign = (p - sym) >= 4 ? '-' : '+';
2✔
2629
        ++s;
2✔
2630
    }
2631
    if (sign == '-')
8✔
2632
        v = -v;
1✔
2633

2634
    return v;
8✔
2635
}
2636

2637
/************************************************************************/
2638
/*                            CPLDecToDMS()                             */
2639
/************************************************************************/
2640

2641
/** Translate a decimal degrees value to a DMS string with hemisphere. */
2642

2643
const char *CPLDecToDMS(double dfAngle, const char *pszAxis, int nPrecision)
550✔
2644

2645
{
2646
    VALIDATE_POINTER1(pszAxis, "CPLDecToDMS", "");
550✔
2647

2648
    if (std::isnan(dfAngle))
550✔
2649
        return "Invalid angle";
×
2650

2651
    const double dfEpsilon = (0.5 / 3600.0) * pow(0.1, nPrecision);
550✔
2652
    const double dfABSAngle = std::abs(dfAngle) + dfEpsilon;
550✔
2653
    if (dfABSAngle > 361.0)
550✔
2654
    {
2655
        return "Invalid angle";
×
2656
    }
2657

2658
    const int nDegrees = static_cast<int>(dfABSAngle);
550✔
2659
    const int nMinutes = static_cast<int>((dfABSAngle - nDegrees) * 60);
550✔
2660
    double dfSeconds = dfABSAngle * 3600 - nDegrees * 3600 - nMinutes * 60;
550✔
2661

2662
    if (dfSeconds > dfEpsilon * 3600.0)
550✔
2663
        dfSeconds -= dfEpsilon * 3600.0;
544✔
2664

2665
    const char *pszHemisphere = nullptr;
550✔
2666
    if (EQUAL(pszAxis, "Long") && dfAngle < 0.0)
550✔
2667
        pszHemisphere = "W";
238✔
2668
    else if (EQUAL(pszAxis, "Long"))
312✔
2669
        pszHemisphere = "E";
37✔
2670
    else if (dfAngle < 0.0)
275✔
2671
        pszHemisphere = "S";
22✔
2672
    else
2673
        pszHemisphere = "N";
253✔
2674

2675
    char szFormat[30] = {};
550✔
2676
    CPLsnprintf(szFormat, sizeof(szFormat), "%%3dd%%2d\'%%%d.%df\"%s",
550✔
2677
                nPrecision + 3, nPrecision, pszHemisphere);
2678

2679
    static CPL_THREADLOCAL char szBuffer[50] = {};
2680
    CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, nDegrees, nMinutes,
550✔
2681
                dfSeconds);
2682

2683
    return szBuffer;
550✔
2684
}
2685

2686
/************************************************************************/
2687
/*                         CPLPackedDMSToDec()                          */
2688
/************************************************************************/
2689

2690
/**
2691
 * Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
2692
 *
2693
 * This function converts a packed DMS angle to seconds. The standard
2694
 * packed DMS format is:
2695
 *
2696
 *  degrees * 1000000 + minutes * 1000 + seconds
2697
 *
2698
 * Example:     angle = 120025045.25 yields
2699
 *              deg = 120
2700
 *              min = 25
2701
 *              sec = 45.25
2702
 *
2703
 * The algorithm used for the conversion is as follows:
2704
 *
2705
 * 1.  The absolute value of the angle is used.
2706
 *
2707
 * 2.  The degrees are separated out:
2708
 *     deg = angle/1000000                    (fractional portion truncated)
2709
 *
2710
 * 3.  The minutes are separated out:
2711
 *     min = (angle - deg * 1000000) / 1000   (fractional portion truncated)
2712
 *
2713
 * 4.  The seconds are then computed:
2714
 *     sec = angle - deg * 1000000 - min * 1000
2715
 *
2716
 * 5.  The total angle in seconds is computed:
2717
 *     sec = deg * 3600.0 + min * 60.0 + sec
2718
 *
2719
 * 6.  The sign of sec is set to that of the input angle.
2720
 *
2721
 * Packed DMS values used by the USGS GCTP package and probably by other
2722
 * software.
2723
 *
2724
 * NOTE: This code does not validate input value. If you give the wrong
2725
 * value, you will get the wrong result.
2726
 *
2727
 * @param dfPacked Angle in packed DMS format.
2728
 *
2729
 * @return Angle in decimal degrees.
2730
 *
2731
 */
2732

2733
double CPLPackedDMSToDec(double dfPacked)
34✔
2734
{
2735
    const double dfSign = dfPacked < 0.0 ? -1 : 1;
34✔
2736

2737
    double dfSeconds = std::abs(dfPacked);
34✔
2738
    double dfDegrees = floor(dfSeconds / 1000000.0);
34✔
2739
    dfSeconds -= dfDegrees * 1000000.0;
34✔
2740
    const double dfMinutes = floor(dfSeconds / 1000.0);
34✔
2741
    dfSeconds -= dfMinutes * 1000.0;
34✔
2742
    dfSeconds = dfSign * (dfDegrees * 3600.0 + dfMinutes * 60.0 + dfSeconds);
34✔
2743
    dfDegrees = dfSeconds / 3600.0;
34✔
2744

2745
    return dfDegrees;
34✔
2746
}
2747

2748
/************************************************************************/
2749
/*                         CPLDecToPackedDMS()                          */
2750
/************************************************************************/
2751
/**
2752
 * Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
2753
 *
2754
 * This function converts a value, specified in decimal degrees into
2755
 * packed DMS angle. The standard packed DMS format is:
2756
 *
2757
 *  degrees * 1000000 + minutes * 1000 + seconds
2758
 *
2759
 * See also CPLPackedDMSToDec().
2760
 *
2761
 * @param dfDec Angle in decimal degrees.
2762
 *
2763
 * @return Angle in packed DMS format.
2764
 *
2765
 */
2766

2767
double CPLDecToPackedDMS(double dfDec)
8✔
2768
{
2769
    const double dfSign = dfDec < 0.0 ? -1 : 1;
8✔
2770

2771
    dfDec = std::abs(dfDec);
8✔
2772
    const double dfDegrees = floor(dfDec);
8✔
2773
    const double dfMinutes = floor((dfDec - dfDegrees) * 60.0);
8✔
2774
    const double dfSeconds = (dfDec - dfDegrees) * 3600.0 - dfMinutes * 60.0;
8✔
2775

2776
    return dfSign * (dfDegrees * 1000000.0 + dfMinutes * 1000.0 + dfSeconds);
8✔
2777
}
2778

2779
/************************************************************************/
2780
/*                         CPLStringToComplex()                         */
2781
/************************************************************************/
2782

2783
/** Fetch the real and imaginary part of a serialized complex number */
2784
void CPL_DLL CPLStringToComplex(const char *pszString, double *pdfReal,
2,155✔
2785
                                double *pdfImag)
2786

2787
{
2788
    while (*pszString == ' ')
2,155✔
2789
        pszString++;
×
2790

2791
    *pdfReal = CPLAtof(pszString);
2,155✔
2792
    *pdfImag = 0.0;
2,155✔
2793

2794
    int iPlus = -1;
2,155✔
2795
    int iImagEnd = -1;
2,155✔
2796

2797
    for (int i = 0; i < 100 && pszString[i] != '\0' && pszString[i] != ' '; i++)
4,560✔
2798
    {
2799
        if (pszString[i] == '+' && i > 0)
2,405✔
2800
            iPlus = i;
×
2801
        if (pszString[i] == '-' && i > 0)
2,405✔
2802
            iPlus = i;
×
2803
        if (pszString[i] == 'i')
2,405✔
2804
            iImagEnd = i;
2✔
2805
    }
2806

2807
    if (iPlus > -1 && iImagEnd > -1 && iPlus < iImagEnd)
2,155✔
2808
    {
2809
        *pdfImag = CPLAtof(pszString + iPlus);
×
2810
    }
2811

2812
    return;
2,155✔
2813
}
2814

2815
/************************************************************************/
2816
/*                           CPLOpenShared()                            */
2817
/************************************************************************/
2818

2819
/**
2820
 * Open a shared file handle.
2821
 *
2822
 * Some operating systems have limits on the number of file handles that can
2823
 * be open at one time.  This function attempts to maintain a registry of
2824
 * already open file handles, and reuse existing ones if the same file
2825
 * is requested by another part of the application.
2826
 *
2827
 * Note that access is only shared for access types "r", "rb", "r+" and
2828
 * "rb+".  All others will just result in direct VSIOpen() calls.  Keep in
2829
 * mind that a file is only reused if the file name is exactly the same.
2830
 * Different names referring to the same file will result in different
2831
 * handles.
2832
 *
2833
 * The VSIFOpen() or VSIFOpenL() function is used to actually open the file,
2834
 * when an existing file handle can't be shared.
2835
 *
2836
 * @param pszFilename the name of the file to open.
2837
 * @param pszAccess the normal fopen()/VSIFOpen() style access string.
2838
 * @param bLargeIn If TRUE VSIFOpenL() (for large files) will be used instead of
2839
 * VSIFOpen().
2840
 *
2841
 * @return a file handle or NULL if opening fails.
2842
 */
2843

2844
FILE *CPLOpenShared(const char *pszFilename, const char *pszAccess,
35✔
2845
                    int bLargeIn)
2846

2847
{
2848
    const bool bLarge = CPL_TO_BOOL(bLargeIn);
35✔
2849
    CPLMutexHolderD(&hSharedFileMutex);
70✔
2850
    const GIntBig nPID = CPLGetPID();
35✔
2851

2852
    /* -------------------------------------------------------------------- */
2853
    /*      Is there an existing file we can use?                           */
2854
    /* -------------------------------------------------------------------- */
2855
    const bool bReuse = EQUAL(pszAccess, "rb") || EQUAL(pszAccess, "rb+");
35✔
2856

2857
    for (int i = 0; bReuse && i < nSharedFileCount; i++)
39✔
2858
    {
2859
        if (strcmp(pasSharedFileList[i].pszFilename, pszFilename) == 0 &&
20✔
2860
            !bLarge == !pasSharedFileList[i].bLarge &&
4✔
2861
            EQUAL(pasSharedFileList[i].pszAccess, pszAccess) &&
16✔
2862
            nPID == pasSharedFileListExtra[i].nPID)
4✔
2863
        {
2864
            pasSharedFileList[i].nRefCount++;
4✔
2865
            return pasSharedFileList[i].fp;
4✔
2866
        }
2867
    }
2868

2869
    /* -------------------------------------------------------------------- */
2870
    /*      Open the file.                                                  */
2871
    /* -------------------------------------------------------------------- */
2872
    FILE *fp = bLarge
2873
                   ? reinterpret_cast<FILE *>(VSIFOpenL(pszFilename, pszAccess))
31✔
2874
                   : VSIFOpen(pszFilename, pszAccess);
×
2875

2876
    if (fp == nullptr)
31✔
2877
        return nullptr;
8✔
2878

2879
    /* -------------------------------------------------------------------- */
2880
    /*      Add an entry to the list.                                       */
2881
    /* -------------------------------------------------------------------- */
2882
    nSharedFileCount++;
23✔
2883

2884
    pasSharedFileList = static_cast<CPLSharedFileInfo *>(
23✔
2885
        CPLRealloc(const_cast<CPLSharedFileInfo *>(pasSharedFileList),
46✔
2886
                   sizeof(CPLSharedFileInfo) * nSharedFileCount));
23✔
2887
    pasSharedFileListExtra = static_cast<CPLSharedFileInfoExtra *>(
23✔
2888
        CPLRealloc(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra),
46✔
2889
                   sizeof(CPLSharedFileInfoExtra) * nSharedFileCount));
23✔
2890

2891
    pasSharedFileList[nSharedFileCount - 1].fp = fp;
23✔
2892
    pasSharedFileList[nSharedFileCount - 1].nRefCount = 1;
23✔
2893
    pasSharedFileList[nSharedFileCount - 1].bLarge = bLarge;
23✔
2894
    pasSharedFileList[nSharedFileCount - 1].pszFilename =
46✔
2895
        CPLStrdup(pszFilename);
23✔
2896
    pasSharedFileList[nSharedFileCount - 1].pszAccess = CPLStrdup(pszAccess);
23✔
2897
    pasSharedFileListExtra[nSharedFileCount - 1].nPID = nPID;
23✔
2898

2899
    return fp;
23✔
2900
}
2901

2902
/************************************************************************/
2903
/*                           CPLCloseShared()                           */
2904
/************************************************************************/
2905

2906
/**
2907
 * Close shared file.
2908
 *
2909
 * Dereferences the indicated file handle, and closes it if the reference
2910
 * count has dropped to zero.  A CPLError() is issued if the file is not
2911
 * in the shared file list.
2912
 *
2913
 * @param fp file handle from CPLOpenShared() to deaccess.
2914
 */
2915

2916
void CPLCloseShared(FILE *fp)
27✔
2917

2918
{
2919
    CPLMutexHolderD(&hSharedFileMutex);
27✔
2920

2921
    /* -------------------------------------------------------------------- */
2922
    /*      Search for matching information.                                */
2923
    /* -------------------------------------------------------------------- */
2924
    int i = 0;
27✔
2925
    for (; i < nSharedFileCount && fp != pasSharedFileList[i].fp; i++)
29✔
2926
    {
2927
    }
2928

2929
    if (i == nSharedFileCount)
27✔
2930
    {
2931
        CPLError(CE_Failure, CPLE_AppDefined,
×
2932
                 "Unable to find file handle %p in CPLCloseShared().", fp);
2933
        return;
×
2934
    }
2935

2936
    /* -------------------------------------------------------------------- */
2937
    /*      Dereference and return if there are still some references.      */
2938
    /* -------------------------------------------------------------------- */
2939
    if (--pasSharedFileList[i].nRefCount > 0)
27✔
2940
        return;
4✔
2941

2942
    /* -------------------------------------------------------------------- */
2943
    /*      Close the file, and remove the information.                     */
2944
    /* -------------------------------------------------------------------- */
2945
    if (pasSharedFileList[i].bLarge)
23✔
2946
    {
2947
        if (VSIFCloseL(reinterpret_cast<VSILFILE *>(pasSharedFileList[i].fp)) !=
23✔
2948
            0)
2949
        {
2950
            CPLError(CE_Failure, CPLE_FileIO, "Error while closing %s",
×
2951
                     pasSharedFileList[i].pszFilename);
×
2952
        }
2953
    }
2954
    else
2955
    {
2956
        VSIFClose(pasSharedFileList[i].fp);
×
2957
    }
2958

2959
    CPLFree(pasSharedFileList[i].pszFilename);
23✔
2960
    CPLFree(pasSharedFileList[i].pszAccess);
23✔
2961

2962
    nSharedFileCount--;
23✔
2963
    memmove(
23✔
2964
        const_cast<CPLSharedFileInfo *>(pasSharedFileList + i),
23✔
2965
        const_cast<CPLSharedFileInfo *>(pasSharedFileList + nSharedFileCount),
23✔
2966
        sizeof(CPLSharedFileInfo));
2967
    memmove(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra + i),
23✔
2968
            const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra +
23✔
2969
                                                 nSharedFileCount),
23✔
2970
            sizeof(CPLSharedFileInfoExtra));
2971

2972
    if (nSharedFileCount == 0)
23✔
2973
    {
2974
        CPLFree(const_cast<CPLSharedFileInfo *>(pasSharedFileList));
20✔
2975
        pasSharedFileList = nullptr;
20✔
2976
        CPLFree(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra));
20✔
2977
        pasSharedFileListExtra = nullptr;
20✔
2978
    }
2979
}
2980

2981
/************************************************************************/
2982
/*                   CPLCleanupSharedFileMutex()                        */
2983
/************************************************************************/
2984

2985
void CPLCleanupSharedFileMutex()
940✔
2986
{
2987
    if (hSharedFileMutex != nullptr)
940✔
2988
    {
2989
        CPLDestroyMutex(hSharedFileMutex);
×
2990
        hSharedFileMutex = nullptr;
×
2991
    }
2992
}
940✔
2993

2994
/************************************************************************/
2995
/*                          CPLGetSharedList()                          */
2996
/************************************************************************/
2997

2998
/**
2999
 * Fetch list of open shared files.
3000
 *
3001
 * @param pnCount place to put the count of entries.
3002
 *
3003
 * @return the pointer to the first in the array of shared file info
3004
 * structures.
3005
 */
3006

3007
CPLSharedFileInfo *CPLGetSharedList(int *pnCount)
×
3008

3009
{
3010
    if (pnCount != nullptr)
×
3011
        *pnCount = nSharedFileCount;
×
3012

3013
    return const_cast<CPLSharedFileInfo *>(pasSharedFileList);
×
3014
}
3015

3016
/************************************************************************/
3017
/*                         CPLDumpSharedList()                          */
3018
/************************************************************************/
3019

3020
/**
3021
 * Report open shared files.
3022
 *
3023
 * Dumps all open shared files to the indicated file handle.  If the
3024
 * file handle is NULL information is sent via the CPLDebug() call.
3025
 *
3026
 * @param fp File handle to write to.
3027
 */
3028

3029
void CPLDumpSharedList(FILE *fp)
103✔
3030

3031
{
3032
    if (nSharedFileCount > 0)
103✔
3033
    {
3034
        if (fp == nullptr)
×
3035
            CPLDebug("CPL", "%d Shared files open.", nSharedFileCount);
×
3036
        else
3037
            fprintf(fp, "%d Shared files open.", nSharedFileCount);
×
3038
    }
3039

3040
    for (int i = 0; i < nSharedFileCount; i++)
103✔
3041
    {
3042
        if (fp == nullptr)
×
3043
            CPLDebug("CPL", "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
×
3044
                     pasSharedFileList[i].bLarge,
×
3045
                     pasSharedFileList[i].pszAccess,
×
3046
                     pasSharedFileList[i].pszFilename);
×
3047
        else
3048
            fprintf(fp, "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
×
3049
                    pasSharedFileList[i].bLarge, pasSharedFileList[i].pszAccess,
×
3050
                    pasSharedFileList[i].pszFilename);
×
3051
    }
3052
}
103✔
3053

3054
/************************************************************************/
3055
/*                           CPLUnlinkTree()                            */
3056
/************************************************************************/
3057

3058
/** Recursively unlink a directory.
3059
 *
3060
 * @return 0 on successful completion, -1 if function fails.
3061
 */
3062

3063
int CPLUnlinkTree(const char *pszPath)
52✔
3064

3065
{
3066
    /* -------------------------------------------------------------------- */
3067
    /*      First, ensure there is such a file.                             */
3068
    /* -------------------------------------------------------------------- */
3069
    VSIStatBufL sStatBuf;
3070

3071
    if (VSIStatL(pszPath, &sStatBuf) != 0)
52✔
3072
    {
3073
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
3074
                 "It seems no file system object called '%s' exists.", pszPath);
3075

3076
        return -1;
2✔
3077
    }
3078

3079
    /* -------------------------------------------------------------------- */
3080
    /*      If it is a simple file, just delete it.                         */
3081
    /* -------------------------------------------------------------------- */
3082
    if (VSI_ISREG(sStatBuf.st_mode))
50✔
3083
    {
3084
        if (VSIUnlink(pszPath) != 0)
34✔
3085
        {
3086
            CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
×
3087
                     pszPath);
3088

3089
            return -1;
×
3090
        }
3091

3092
        return 0;
34✔
3093
    }
3094

3095
    /* -------------------------------------------------------------------- */
3096
    /*      If it is a directory recurse then unlink the directory.         */
3097
    /* -------------------------------------------------------------------- */
3098
    else if (VSI_ISDIR(sStatBuf.st_mode))
16✔
3099
    {
3100
        char **papszItems = VSIReadDir(pszPath);
16✔
3101

3102
        for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
32✔
3103
        {
3104
            if (papszItems[i][0] == '\0' || EQUAL(papszItems[i], ".") ||
16✔
3105
                EQUAL(papszItems[i], ".."))
16✔
3106
                continue;
×
3107

3108
            const std::string osSubPath =
3109
                CPLFormFilename(pszPath, papszItems[i], nullptr);
16✔
3110

3111
            const int nErr = CPLUnlinkTree(osSubPath.c_str());
16✔
3112

3113
            if (nErr != 0)
16✔
3114
            {
3115
                CSLDestroy(papszItems);
×
3116
                return nErr;
×
3117
            }
3118
        }
3119

3120
        CSLDestroy(papszItems);
16✔
3121

3122
        if (VSIRmdir(pszPath) != 0)
16✔
3123
        {
3124
            CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
×
3125
                     pszPath);
3126

3127
            return -1;
×
3128
        }
3129

3130
        return 0;
16✔
3131
    }
3132

3133
    /* -------------------------------------------------------------------- */
3134
    /*      otherwise report an error.                                      */
3135
    /* -------------------------------------------------------------------- */
3136
    CPLError(CE_Failure, CPLE_AppDefined,
×
3137
             "Failed to unlink %s.\nUnrecognised filesystem object.", pszPath);
3138
    return 1000;
×
3139
}
3140

3141
/************************************************************************/
3142
/*                            CPLCopyFile()                             */
3143
/************************************************************************/
3144

3145
/** Copy a file */
3146
int CPLCopyFile(const char *pszNewPath, const char *pszOldPath)
2,211✔
3147

3148
{
3149
    return VSICopyFile(pszOldPath, pszNewPath, nullptr,
2,211✔
3150
                       static_cast<vsi_l_offset>(-1), nullptr, nullptr,
3151
                       nullptr);
2,211✔
3152
}
3153

3154
/************************************************************************/
3155
/*                            CPLCopyTree()                             */
3156
/************************************************************************/
3157

3158
/** Recursively copy a tree */
3159
int CPLCopyTree(const char *pszNewPath, const char *pszOldPath)
4✔
3160

3161
{
3162
    VSIStatBufL sStatBuf;
3163
    if (VSIStatL(pszNewPath, &sStatBuf) == 0)
4✔
3164
    {
3165
        CPLError(
1✔
3166
            CE_Failure, CPLE_AppDefined,
3167
            "It seems that a file system object called '%s' already exists.",
3168
            pszNewPath);
3169

3170
        return -1;
1✔
3171
    }
3172

3173
    if (VSIStatL(pszOldPath, &sStatBuf) != 0)
3✔
3174
    {
3175
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
3176
                 "It seems no file system object called '%s' exists.",
3177
                 pszOldPath);
3178

3179
        return -1;
1✔
3180
    }
3181

3182
    if (VSI_ISDIR(sStatBuf.st_mode))
2✔
3183
    {
3184
        if (VSIMkdir(pszNewPath, 0755) != 0)
1✔
3185
        {
3186
            CPLError(CE_Failure, CPLE_AppDefined,
×
3187
                     "Cannot create directory '%s'.", pszNewPath);
3188

3189
            return -1;
×
3190
        }
3191

3192
        char **papszItems = VSIReadDir(pszOldPath);
1✔
3193

3194
        for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
4✔
3195
        {
3196
            if (EQUAL(papszItems[i], ".") || EQUAL(papszItems[i], ".."))
3✔
3197
                continue;
2✔
3198

3199
            const std::string osNewSubPath =
3200
                CPLFormFilename(pszNewPath, papszItems[i], nullptr);
1✔
3201
            const std::string osOldSubPath =
3202
                CPLFormFilename(pszOldPath, papszItems[i], nullptr);
1✔
3203

3204
            const int nErr =
3205
                CPLCopyTree(osNewSubPath.c_str(), osOldSubPath.c_str());
1✔
3206

3207
            if (nErr != 0)
1✔
3208
            {
3209
                CSLDestroy(papszItems);
×
3210
                return nErr;
×
3211
            }
3212
        }
3213
        CSLDestroy(papszItems);
1✔
3214

3215
        return 0;
1✔
3216
    }
3217
    else if (VSI_ISREG(sStatBuf.st_mode))
1✔
3218
    {
3219
        return CPLCopyFile(pszNewPath, pszOldPath);
1✔
3220
    }
3221
    else
3222
    {
3223
        CPLError(CE_Failure, CPLE_AppDefined,
×
3224
                 "Unrecognized filesystem object : '%s'.", pszOldPath);
3225
        return -1;
×
3226
    }
3227
}
3228

3229
/************************************************************************/
3230
/*                            CPLMoveFile()                             */
3231
/************************************************************************/
3232

3233
/** Move a file */
3234
int CPLMoveFile(const char *pszNewPath, const char *pszOldPath)
184✔
3235

3236
{
3237
    if (VSIRename(pszOldPath, pszNewPath) == 0)
184✔
3238
        return 0;
181✔
3239

3240
    const int nRet = CPLCopyFile(pszNewPath, pszOldPath);
3✔
3241

3242
    if (nRet == 0)
3✔
3243
        VSIUnlink(pszOldPath);
3✔
3244
    return nRet;
3✔
3245
}
3246

3247
/************************************************************************/
3248
/*                             CPLSymlink()                             */
3249
/************************************************************************/
3250

3251
/** Create a symbolic link */
3252
#ifdef _WIN32
3253
int CPLSymlink(const char *, const char *, CSLConstList)
3254
{
3255
    return -1;
3256
}
3257
#else
3258
int CPLSymlink(const char *pszOldPath, const char *pszNewPath,
×
3259
               CSLConstList /* papszOptions */)
3260
{
3261
    return symlink(pszOldPath, pszNewPath);
×
3262
}
3263
#endif
3264

3265
/************************************************************************/
3266
/* ==================================================================== */
3267
/*                              CPLLocaleC                              */
3268
/* ==================================================================== */
3269
/************************************************************************/
3270

3271
//! @cond Doxygen_Suppress
3272
/************************************************************************/
3273
/*                             CPLLocaleC()                             */
3274
/************************************************************************/
3275

3276
CPLLocaleC::CPLLocaleC() : pszOldLocale(nullptr)
127✔
3277
{
3278
    if (CPLTestBool(CPLGetConfigOption("GDAL_DISABLE_CPLLOCALEC", "NO")))
127✔
3279
        return;
×
3280

3281
    pszOldLocale = CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr));
127✔
3282
    if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
127✔
3283
        CPLsetlocale(LC_NUMERIC, "C") == nullptr)
×
3284
    {
3285
        CPLFree(pszOldLocale);
127✔
3286
        pszOldLocale = nullptr;
127✔
3287
    }
3288
}
3289

3290
/************************************************************************/
3291
/*                            ~CPLLocaleC()                             */
3292
/************************************************************************/
3293

3294
CPLLocaleC::~CPLLocaleC()
×
3295

3296
{
3297
    if (pszOldLocale == nullptr)
127✔
3298
        return;
127✔
3299

3300
    CPLsetlocale(LC_NUMERIC, pszOldLocale);
×
3301
    CPLFree(pszOldLocale);
×
3302
}
127✔
3303

3304
/************************************************************************/
3305
/*                        CPLThreadLocaleCPrivate                       */
3306
/************************************************************************/
3307

3308
#ifdef HAVE_USELOCALE
3309

3310
class CPLThreadLocaleCPrivate
3311
{
3312
    locale_t nNewLocale;
3313
    locale_t nOldLocale;
3314

3315
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3316

3317
  public:
3318
    CPLThreadLocaleCPrivate();
3319
    ~CPLThreadLocaleCPrivate();
3320
};
3321

3322
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
×
3323
    : nNewLocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)),
×
3324
      nOldLocale(uselocale(nNewLocale))
×
3325
{
3326
}
×
3327

3328
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
×
3329
{
3330
    uselocale(nOldLocale);
×
3331
    freelocale(nNewLocale);
×
3332
}
×
3333

3334
#elif defined(_MSC_VER)
3335

3336
class CPLThreadLocaleCPrivate
3337
{
3338
    int nOldValConfigThreadLocale;
3339
    char *pszOldLocale;
3340

3341
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3342

3343
  public:
3344
    CPLThreadLocaleCPrivate();
3345
    ~CPLThreadLocaleCPrivate();
3346
};
3347

3348
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3349
{
3350
    nOldValConfigThreadLocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
3351
    pszOldLocale = setlocale(LC_NUMERIC, "C");
3352
    if (pszOldLocale)
3353
        pszOldLocale = CPLStrdup(pszOldLocale);
3354
}
3355

3356
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3357
{
3358
    if (pszOldLocale != nullptr)
3359
    {
3360
        setlocale(LC_NUMERIC, pszOldLocale);
3361
        CPLFree(pszOldLocale);
3362
    }
3363
    _configthreadlocale(nOldValConfigThreadLocale);
3364
}
3365

3366
#else
3367

3368
class CPLThreadLocaleCPrivate
3369
{
3370
    char *pszOldLocale;
3371

3372
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3373

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

3379
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3380
    : pszOldLocale(CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr)))
3381
{
3382
    if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
3383
        CPLsetlocale(LC_NUMERIC, "C") == nullptr)
3384
    {
3385
        CPLFree(pszOldLocale);
3386
        pszOldLocale = nullptr;
3387
    }
3388
}
3389

3390
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3391
{
3392
    if (pszOldLocale != nullptr)
3393
    {
3394
        CPLsetlocale(LC_NUMERIC, pszOldLocale);
3395
        CPLFree(pszOldLocale);
3396
    }
3397
}
3398

3399
#endif
3400

3401
/************************************************************************/
3402
/*                        CPLThreadLocaleC()                            */
3403
/************************************************************************/
3404

3405
CPLThreadLocaleC::CPLThreadLocaleC() : m_private(new CPLThreadLocaleCPrivate)
×
3406
{
3407
}
×
3408

3409
/************************************************************************/
3410
/*                       ~CPLThreadLocaleC()                            */
3411
/************************************************************************/
3412

3413
CPLThreadLocaleC::~CPLThreadLocaleC()
×
3414

3415
{
3416
    delete m_private;
×
3417
}
×
3418

3419
//! @endcond
3420

3421
/************************************************************************/
3422
/*                          CPLsetlocale()                              */
3423
/************************************************************************/
3424

3425
/**
3426
 * Prevents parallel executions of setlocale().
3427
 *
3428
 * Calling setlocale() concurrently from two or more threads is a
3429
 * potential data race. A mutex is used to provide a critical region so
3430
 * that only one thread at a time can be executing setlocale().
3431
 *
3432
 * The return should not be freed, and copied quickly as it may be invalidated
3433
 * by a following next call to CPLsetlocale().
3434
 *
3435
 * @param category See your compiler's documentation on setlocale.
3436
 * @param locale See your compiler's documentation on setlocale.
3437
 *
3438
 * @return See your compiler's documentation on setlocale.
3439
 */
3440
char *CPLsetlocale(int category, const char *locale)
129✔
3441
{
3442
    CPLMutexHolder oHolder(&hSetLocaleMutex);
258✔
3443
    char *pszRet = setlocale(category, locale);
129✔
3444
    if (pszRet == nullptr)
129✔
3445
        return pszRet;
×
3446

3447
    // Make it thread-locale storage.
3448
    return const_cast<char *>(CPLSPrintf("%s", pszRet));
129✔
3449
}
3450

3451
/************************************************************************/
3452
/*                       CPLCleanupSetlocaleMutex()                     */
3453
/************************************************************************/
3454

3455
void CPLCleanupSetlocaleMutex(void)
940✔
3456
{
3457
    if (hSetLocaleMutex != nullptr)
940✔
3458
        CPLDestroyMutex(hSetLocaleMutex);
5✔
3459
    hSetLocaleMutex = nullptr;
940✔
3460
}
940✔
3461

3462
/************************************************************************/
3463
/*                            IsPowerOfTwo()                            */
3464
/************************************************************************/
3465

3466
int CPLIsPowerOfTwo(unsigned int i)
134✔
3467
{
3468
    if (i == 0)
134✔
3469
        return FALSE;
×
3470
    return (i & (i - 1)) == 0 ? TRUE : FALSE;
134✔
3471
}
3472

3473
/************************************************************************/
3474
/*                          CPLCheckForFile()                           */
3475
/************************************************************************/
3476

3477
/**
3478
 * Check for file existence.
3479
 *
3480
 * The function checks if a named file exists in the filesystem, hopefully
3481
 * in an efficient fashion if a sibling file list is available.   It exists
3482
 * primarily to do faster file checking for functions like GDAL open methods
3483
 * that get a list of files from the target directory.
3484
 *
3485
 * If the sibling file list exists (is not NULL) it is assumed to be a list
3486
 * of files in the same directory as the target file, and it will be checked
3487
 * (case insensitively) for a match.  If a match is found, pszFilename is
3488
 * updated with the correct case and TRUE is returned.
3489
 *
3490
 * If papszSiblingFiles is NULL, a VSIStatL() is used to test for the files
3491
 * existence, and no case insensitive testing is done.
3492
 *
3493
 * @param pszFilename name of file to check for - filename case updated in
3494
 * some cases.
3495
 * @param papszSiblingFiles a list of files in the same directory as
3496
 * pszFilename if available, or NULL. This list should have no path components.
3497
 *
3498
 * @return TRUE if a match is found, or FALSE if not.
3499
 */
3500

3501
int CPLCheckForFile(char *pszFilename, char **papszSiblingFiles)
156,997✔
3502

3503
{
3504
    /* -------------------------------------------------------------------- */
3505
    /*      Fallback case if we don't have a sibling file list.             */
3506
    /* -------------------------------------------------------------------- */
3507
    if (papszSiblingFiles == nullptr)
156,997✔
3508
    {
3509
        VSIStatBufL sStatBuf;
3510

3511
        return VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
10,804✔
3512
    }
3513

3514
    /* -------------------------------------------------------------------- */
3515
    /*      We have sibling files, compare the non-path filename portion    */
3516
    /*      of pszFilename too all entries.                                 */
3517
    /* -------------------------------------------------------------------- */
3518
    const CPLString osFileOnly = CPLGetFilename(pszFilename);
292,386✔
3519

3520
    for (int i = 0; papszSiblingFiles[i] != nullptr; i++)
13,753,900✔
3521
    {
3522
        if (EQUAL(papszSiblingFiles[i], osFileOnly))
13,607,800✔
3523
        {
3524
            strcpy(pszFilename + strlen(pszFilename) - osFileOnly.size(),
314✔
3525
                   papszSiblingFiles[i]);
157✔
3526
            return TRUE;
157✔
3527
        }
3528
    }
3529

3530
    return FALSE;
146,036✔
3531
}
3532

3533
/************************************************************************/
3534
/*      Stub implementation of zip services if we don't have libz.      */
3535
/************************************************************************/
3536

3537
#if !defined(HAVE_LIBZ)
3538

3539
void *CPLCreateZip(const char *, char **)
3540

3541
{
3542
    CPLError(CE_Failure, CPLE_NotSupported,
3543
             "This GDAL/OGR build does not include zlib and zip services.");
3544
    return nullptr;
3545
}
3546

3547
CPLErr CPLCreateFileInZip(void *, const char *, char **)
3548
{
3549
    return CE_Failure;
3550
}
3551

3552
CPLErr CPLWriteFileInZip(void *, const void *, int)
3553
{
3554
    return CE_Failure;
3555
}
3556

3557
CPLErr CPLCloseFileInZip(void *)
3558
{
3559
    return CE_Failure;
3560
}
3561

3562
CPLErr CPLCloseZip(void *)
3563
{
3564
    return CE_Failure;
3565
}
3566

3567
void *CPLZLibDeflate(const void *, size_t, int, void *, size_t,
3568
                     size_t *pnOutBytes)
3569
{
3570
    if (pnOutBytes != nullptr)
3571
        *pnOutBytes = 0;
3572
    return nullptr;
3573
}
3574

3575
void *CPLZLibInflate(const void *, size_t, void *, size_t, size_t *pnOutBytes)
3576
{
3577
    if (pnOutBytes != nullptr)
3578
        *pnOutBytes = 0;
3579
    return nullptr;
3580
}
3581

3582
#endif /* !defined(HAVE_LIBZ) */
3583

3584
/************************************************************************/
3585
/* ==================================================================== */
3586
/*                          CPLConfigOptionSetter                       */
3587
/* ==================================================================== */
3588
/************************************************************************/
3589

3590
//! @cond Doxygen_Suppress
3591
/************************************************************************/
3592
/*                         CPLConfigOptionSetter()                      */
3593
/************************************************************************/
3594

3595
CPLConfigOptionSetter::CPLConfigOptionSetter(const char *pszKey,
23,416✔
3596
                                             const char *pszValue,
3597
                                             bool bSetOnlyIfUndefined)
23,416✔
3598
    : m_pszKey(CPLStrdup(pszKey)), m_pszOldValue(nullptr),
23,416✔
3599
      m_bRestoreOldValue(false)
23,399✔
3600
{
3601
    const char *pszOldValue = CPLGetThreadLocalConfigOption(pszKey, nullptr);
23,399✔
3602
    if ((bSetOnlyIfUndefined &&
37,105✔
3603
         CPLGetConfigOption(pszKey, nullptr) == nullptr) ||
33,096✔
3604
        !bSetOnlyIfUndefined)
9,711✔
3605
    {
3606
        m_bRestoreOldValue = true;
23,396✔
3607
        if (pszOldValue)
23,396✔
3608
            m_pszOldValue = CPLStrdup(pszOldValue);
644✔
3609
        CPLSetThreadLocalConfigOption(pszKey, pszValue);
23,396✔
3610
    }
3611
}
23,284✔
3612

3613
/************************************************************************/
3614
/*                        ~CPLConfigOptionSetter()                      */
3615
/************************************************************************/
3616

3617
CPLConfigOptionSetter::~CPLConfigOptionSetter()
46,692✔
3618
{
3619
    if (m_bRestoreOldValue)
23,415✔
3620
    {
3621
        CPLSetThreadLocalConfigOption(m_pszKey, m_pszOldValue);
23,407✔
3622
        CPLFree(m_pszOldValue);
23,227✔
3623
    }
3624
    CPLFree(m_pszKey);
23,224✔
3625
}
23,277✔
3626

3627
//! @endcond
3628

3629
/************************************************************************/
3630
/*                          CPLIsInteractive()                          */
3631
/************************************************************************/
3632

3633
/** Returns whether the provided file refers to a terminal.
3634
 *
3635
 * This function is a wrapper of the ``isatty()`` POSIX function.
3636
 *
3637
 * @param f File to test. Typically stdin, stdout or stderr
3638
 * @return true if it is an open file referring to a terminal.
3639
 * @since GDAL 3.11
3640
 */
3641
bool CPLIsInteractive(FILE *f)
642✔
3642
{
3643
#ifndef _WIN32
3644
    return isatty(static_cast<int>(fileno(f)));
642✔
3645
#else
3646
    return _isatty(_fileno(f));
3647
#endif
3648
}
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