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

OSGeo / gdal / 13705748401

06 Mar 2025 06:36PM UTC coverage: 70.348% (-0.001%) from 70.349%
13705748401

Pull #11928

github

web-flow
Merge 00ef26df0 into 6d20ab96f
Pull Request #11928: gdal reproject: add a --size argument, add --bbox-crs as well

26 of 28 new or added lines in 1 file covered. (92.86%)

56 existing lines in 32 files now uncovered.

552790 of 785788 relevant lines covered (70.35%)

221695.43 hits per line

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

76.26
/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,175,150✔
149

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

154
    void *pReturn = CPLMalloc(nCount * nSize);
3,166,010✔
155
    memset(pReturn, 0, nCount * nSize);
3,165,980✔
156
    return pReturn;
3,165,980✔
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,854,000✔
179

180
{
181
    if (nSize == 0)
18,854,000✔
182
        return nullptr;
5,723✔
183

184
    if ((nSize >> (8 * sizeof(nSize) - 1)) != 0)
18,848,300✔
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,848,300✔
194
    if (pReturn == nullptr)
18,848,300✔
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,848,300✔
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,824,230✔
234

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

242
    if ((nNewSize >> (8 * sizeof(nNewSize) - 1)) != 0)
1,824,180✔
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,824,180✔
252

253
    if (pData == nullptr)
1,824,180✔
254
        pReturn = VSIMalloc(nNewSize);
854,725✔
255
    else
256
        pReturn = VSIRealloc(pData, nNewSize);
969,458✔
257

258
    if (pReturn == nullptr)
1,824,180✔
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,824,180✔
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,340,880✔
302

303
{
304
    if (pszString == nullptr)
7,340,880✔
305
        pszString = "";
982,776✔
306

307
    const size_t nLen = strlen(pszString);
7,340,880✔
308
    char *pszReturn = static_cast<char *>(CPLMalloc(nLen + 1));
7,340,880✔
309
    memcpy(pszReturn, pszString, nLen + 1);
7,340,800✔
310
    return (pszReturn);
7,340,800✔
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)
3✔
328

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

333
    char *pszTemp = pszString;
3✔
334

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

342
    return pszString;
3✔
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,387,490✔
479

480
{
481

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

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

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

515
    /* -------------------------------------------------------------------- */
516
    /*      If it is too small, grow it bigger.                             */
517
    /* -------------------------------------------------------------------- */
518
    if (static_cast<int>(*pnAlloc) - 1 < nRequiredSize)
2,385,260✔
519
    {
520
        const int nNewSize = nRequiredSize + 4 + 500;
702✔
521
        if (nNewSize <= 0)
702✔
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));
702✔
533
        if (pnAllocNew == nullptr)
702✔
534
        {
535
            VSIFree(pnAlloc);
×
536
            CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
×
537
            return nullptr;
×
538
        }
539
        pnAlloc = pnAllocNew;
702✔
540

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

545
    return reinterpret_cast<char *>(pnAlloc + 1);
2,385,260✔
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)
5✔
575

576
{
577
    /* -------------------------------------------------------------------- */
578
    /*      Cleanup case.                                                   */
579
    /* -------------------------------------------------------------------- */
580
    if (fp == nullptr)
5✔
581
    {
582
        CPLReadLineBuffer(-1);
5✔
583
        return nullptr;
5✔
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)
196,287✔
643
{
644
    return CPLReadLine2L(fp, -1, nullptr);
196,287✔
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,263,580✔
668
                          CPL_UNUSED CSLConstList papszOptions)
669

670
{
671
    int nBufLength;
672
    return CPLReadLine3L(fp, nMaxCars, &nBufLength, papszOptions);
2,527,160✔
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,326,530✔
696
                          CPL_UNUSED CSLConstList papszOptions)
697
{
698
    /* -------------------------------------------------------------------- */
699
    /*      Cleanup case.                                                   */
700
    /* -------------------------------------------------------------------- */
701
    if (fp == nullptr)
1,326,530✔
702
    {
703
        CPLReadLineBuffer(-1);
2,231✔
704
        return nullptr;
2,231✔
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,324,300✔
712
    const size_t nChunkSize = 40;
1,324,300✔
713
    char szChunk[nChunkSize] = {};
1,324,300✔
714
    size_t nChunkBytesRead = 0;
1,324,300✔
715
    size_t nChunkBytesConsumed = 0;
1,324,300✔
716

717
    *pnBufLength = 0;
1,324,300✔
718
    szChunk[0] = 0;
1,324,300✔
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,385,260✔
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,385,260✔
737
        if (pszRLBuffer == nullptr)
2,385,260✔
738
            return nullptr;
×
739

740
        if (nChunkBytesRead == nChunkBytesConsumed + 1)
2,385,260✔
741
        {
742

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

746
            nChunkBytesConsumed = 0;
1,060,960✔
747
            nChunkBytesRead = VSIFReadL(szChunk + 1, 1, nChunkSize - 1, fp) + 1;
1,060,960✔
748
        }
749
        else
750
        {
751
            nChunkBytesConsumed = 0;
1,324,300✔
752

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

760
                break;
×
761
            }
762
        }
763

764
        /* --------------------------------------------------------------------
765
         */
766
        /*      copy over characters watching for end-of-line. */
767
        /* --------------------------------------------------------------------
768
         */
769
        bool bBreak = false;
2,374,160✔
770
        while (nChunkBytesConsumed < nChunkBytesRead - 1 && !bBreak)
63,112,800✔
771
        {
772
            if ((szChunk[nChunkBytesConsumed] == knCR &&
60,738,700✔
773
                 szChunk[nChunkBytesConsumed + 1] == knLF) ||
172,272✔
774
                (szChunk[nChunkBytesConsumed] == knLF &&
60,566,700✔
775
                 szChunk[nChunkBytesConsumed + 1] == knCR))
1,129,530✔
776
            {
777
                nChunkBytesConsumed += 2;
171,995✔
778
                bBreak = true;
171,995✔
779
            }
780
            else if (szChunk[nChunkBytesConsumed] == knLF ||
60,566,700✔
781
                     szChunk[nChunkBytesConsumed] == knCR)
59,437,100✔
782
            {
783
                nChunkBytesConsumed += 1;
1,129,810✔
784
                bBreak = true;
1,129,810✔
785
            }
786
            else
787
            {
788
                pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
59,436,900✔
789
                if (nMaxCars >= 0 && *pnBufLength == nMaxCars)
59,436,900✔
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,374,160✔
799
            break;
1,301,810✔
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,072,350✔
809
            nChunkBytesRead < nChunkSize)
810
        {
811
            if (szChunk[nChunkBytesConsumed] == knLF ||
11,398✔
812
                szChunk[nChunkBytesConsumed] == knCR)
1,655✔
813
            {
814
                nChunkBytesConsumed++;
9,743✔
815
                break;
9,743✔
816
            }
817

818
            pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
1,655✔
819
            break;
1,655✔
820
        }
821
    }
1,060,960✔
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,313,200✔
828
    {
829
        const size_t nBytesToPush = nChunkBytesRead - nChunkBytesConsumed;
1,296,300✔
830

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

835
    pszRLBuffer[*pnBufLength] = '\0';
1,313,200✔
836

837
    return pszRLBuffer;
1,313,200✔
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,144✔
865
                    int bNormalize)
866
{
867
    if (!pszString)
5,144✔
868
        return nullptr;
×
869

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

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

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

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

892
    if (bNormalize)
5,142✔
893
    {
894
        size_t i = strlen(pszBuffer);
5,024✔
895
        while (i > 0)
38,392✔
896
        {
897
            i--;
33,368✔
898
            if (pszBuffer[i] == ':')
33,368✔
899
                pszBuffer[i] = '_';
×
900
        }
901
    }
902

903
    return pszBuffer;
5,142✔
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)
15,692✔
983
{
984
    CPLAssert(nMaxLength >= 0);
15,692✔
985
    if (pszString == nullptr)
15,692✔
986
        return 0;
×
987
    const size_t nLength = CPLStrnlen(pszString, nMaxLength);
15,692✔
988
    const std::string osValue(pszString, nLength);
31,384✔
989

990
    /* -------------------------------------------------------------------- */
991
    /*      Fetch out the result                                            */
992
    /* -------------------------------------------------------------------- */
993
    return strtoull(osValue.c_str(), nullptr, 10);
15,692✔
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)
104,286✔
1009
{
1010
    return atoll(pszString);
104,286✔
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,087✔
1055
{
1056
    errno = 0;
100,087✔
1057
    GIntBig nVal = strtoll(pszString, nullptr, 10);
100,087✔
1058
    if (errno == ERANGE
100,087✔
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,083✔
1076
    {
1077
        *pbOverflow = FALSE;
3,874✔
1078
    }
1079
    return nVal;
100,083✔
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)
928✔
1103
{
1104
    char szTemp[128] = {};
928✔
1105

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

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

1115
    /* -------------------------------------------------------------------- */
1116
    /*      On MSVC we have to scanf pointer values without the 0x          */
1117
    /*      prefix.                                                         */
1118
    /* -------------------------------------------------------------------- */
1119
    if (STARTS_WITH_CI(szTemp, "0x"))
928✔
1120
    {
1121
        void *pResult = nullptr;
933✔
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);
933✔
1129

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

1140
#if SIZEOF_VOIDP == 8
UNCOV
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,220✔
1221
{
1222
    if (!pszDest)
9,220✔
1223
        return 0;
×
1224

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

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

1234
    while (nChars < nMaxLen && *pszSrc)
136,087✔
1235
    {
1236
        *pszTemp++ = *pszSrc++;
126,867✔
1237
        nChars++;
126,867✔
1238
    }
1239

1240
    return nChars;
9,220✔
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)
209✔
1265
{
1266
    if (!pszDest)
209✔
1267
        return 0;
×
1268

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

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

1282
    if (nMaxLen)
209✔
1283
        memset(pszTemp, ' ', nMaxLen);
71✔
1284

1285
    return nMaxLen;
209✔
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)
24✔
1348
{
1349
    if (!pszBuffer)
24✔
1350
        return 0;
×
1351

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

1355
    char szTemp[64] = {};
24✔
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);
24✔
1370
#endif
1371

1372
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
24✔
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,149✔
1396
{
1397
    if (!pszBuffer)
9,149✔
1398
        return 0;
×
1399

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

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

1405
    snprintf(szTemp, sizeof(szTemp), "%p", pValue);
9,149✔
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,149✔
1412
        snprintf(szTemp, sizeof(szTemp), "0x%p", pValue);
×
1413

1414
    return CPLPrintString(pszBuffer, szTemp, nMaxLen);
9,149✔
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,818,980✔
1688
                                           const char *pszDefault)
1689

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

1693
    if (pszResult == nullptr)
4,817,720✔
1694
    {
1695
        pszResult = CPLGetGlobalConfigOption(pszKey, nullptr);
4,780,220✔
1696
    }
1697

1698
    if (gbIgnoreEnvVariables)
4,819,890✔
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,819,890✔
1710
    {
1711
        pszResult = getenv(pszKey);
4,774,850✔
1712
    }
1713

1714
    if (pszResult == nullptr)
4,819,930✔
1715
        return pszDefault;
4,765,900✔
1716

1717
    return pszResult;
54,027✔
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,844,820✔
1776
                                                      const char *pszDefault)
1777

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

1783
    const char *pszResult = nullptr;
4,844,820✔
1784

1785
    int bMemoryError = FALSE;
4,844,820✔
1786
    char **papszTLConfigOptions = reinterpret_cast<char **>(
1787
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
4,844,820✔
1788
    if (papszTLConfigOptions != nullptr)
4,844,200✔
1789
        pszResult = CSLFetchNameValue(papszTLConfigOptions, pszKey);
4,448,720✔
1790

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

1794
    return pszResult;
38,089✔
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,782,960✔
1807
                                                 const char *pszDefault)
1808
{
1809
#ifdef DEBUG_CONFIG_OPTIONS
1810
    CPLAccessConfigOption(pszKey, TRUE);
1811
#endif
1812

1813
    CPLMutexHolderD(&hConfigMutex);
9,567,320✔
1814

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

1818
    if (pszResult == nullptr)
4,784,360✔
1819
        return pszDefault;
4,776,540✔
1820

1821
    return pszResult;
7,819✔
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,187✔
1839
                                  void *pUserData)
1840
{
1841
    CPLMutexHolderD(&hConfigMutex);
2,374✔
1842
    for (int nId = 0;
1,192✔
1843
         nId < static_cast<int>(gSetConfigOptionSubscribers.size()); ++nId)
1,192✔
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,186✔
1853
    gSetConfigOptionSubscribers.push_back(
1,186✔
1854
        std::pair<CPLSetConfigOptionSubscriber, void *>(pfnCallback,
1,186✔
1855
                                                        pUserData));
1856
    return nId;
1,186✔
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,846✔
1890
                                                     const char *pszValue,
1891
                                                     bool bThreadLocal)
1892
{
1893
    // Hack
1894
    if (STARTS_WITH_CI(pszKey, "AWS_"))
61,846✔
1895
        VSICurlAuthParametersChanged();
2,350✔
1896

1897
    if (!gSetConfigOptionSubscribers.empty())
61,846✔
1898
    {
1899
        for (const auto &iter : gSetConfigOptionSubscribers)
122,227✔
1900
        {
1901
            if (iter.first)
61,168✔
1902
                iter.first(pszKey, pszValue, bThreadLocal, iter.second);
61,327✔
1903
        }
1904
    }
1905
}
61,618✔
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,271✔
1918
{
1919
    if (gnDebug < 0)
67,271✔
1920
    {
1921
        // Check that apszKnownConfigOptions is correctly sorted with
1922
        // STRCASECMP() criterion.
1923
        for (size_t i = 1; i < CPL_ARRAYSIZE(apszKnownConfigOptions); ++i)
454,464✔
1924
        {
1925
            if (STRCASECMP(apszKnownConfigOptions[i - 1],
454,032✔
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"));
432✔
1936
    }
1937

1938
    return gnDebug != 0;
67,254✔
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,159✔
1978
        aosList.AddString(pszKey);
3,156✔
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,930✔
1989
                                                        const char *pszValue)
1990
{
1991
    if (EQUAL(pszKey, "CPL_DEBUG"))
61,930✔
1992
    {
1993
        gnDebug = pszValue ? CPLTestBool(pszValue) : false;
100✔
1994
    }
1995
    else if (CPLIsDebugEnabled())
61,830✔
1996
    {
1997
        if (!std::binary_search(std::begin(apszKnownConfigOptions),
89✔
1998
                                std::end(apszKnownConfigOptions), pszKey,
1999
                                [](const char *a, const char *b)
987✔
2000
                                { return STRCASECMP(a, b) < 0; }))
987✔
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,875✔
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,734✔
2059

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

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

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

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

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

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

2083
/* non-stdcall wrapper function for CSLDestroy() (#5590) */
2084
static void CPLSetThreadLocalTLSFreeFunc(void *pData)
7✔
2085
{
2086
    CSLDestroy(reinterpret_cast<char **>(pData));
7✔
2087
}
7✔
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,250✔
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,250✔
2124
#endif
2125

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

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

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

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

2140
    NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
56,947✔
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)
741,215✔
2158
{
2159
    int bMemoryError = FALSE;
741,215✔
2160
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2161
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
741,215✔
2162
    if (bMemoryError)
714,911✔
2163
        return nullptr;
×
2164
    return CSLDuplicate(papszTLConfigOptions);
714,911✔
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,394,290✔
2188
{
2189
    int bMemoryError = FALSE;
1,394,290✔
2190
    char **papszTLConfigOptions = reinterpret_cast<char **>(
2191
        CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
1,394,290✔
2192
    if (bMemoryError)
1,346,570✔
2193
        return;
×
2194
    CSLDestroy(papszTLConfigOptions);
1,346,570✔
2195
    papszTLConfigOptions =
2196
        CSLDuplicate(const_cast<char **>(papszConfigOptions));
1,373,950✔
2197
    CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
1,381,350✔
2198
                          CPLSetThreadLocalTLSFreeFunc);
2199
}
2200

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

2205
void CPL_STDCALL CPLFreeConfig()
1,342✔
2206

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

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

2214
        int bMemoryError = FALSE;
1,342✔
2215
        char **papszTLConfigOptions = reinterpret_cast<char **>(
2216
            CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
1,342✔
2217
        if (papszTLConfigOptions != nullptr)
1,342✔
2218
        {
2219
            CSLDestroy(papszTLConfigOptions);
201✔
2220
            CPLSetTLS(CTLS_CONFIGOPTIONS, nullptr, FALSE);
201✔
2221
        }
2222
    }
2223
    CPLDestroyMutex(hConfigMutex);
1,342✔
2224
    hConfigMutex = nullptr;
1,342✔
2225
}
1,342✔
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,744✔
2279
{
2280
    VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
2,744✔
2281
    if (fp == nullptr)
2,744✔
2282
        return;
2,735✔
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,369✔
2475
{
2476
    const char *pszFile = CPLGetConfigOption("GDAL_CONFIG_FILE", nullptr);
1,369✔
2477
    if (pszFile != nullptr)
1,369✔
2478
    {
2479
        CPLLoadConfigOptionsFromFile(pszFile, false);
2✔
2480
    }
2481
    else
2482
    {
2483
#ifdef SYSCONFDIR
2484
        CPLLoadConfigOptionsFromFile(
1,367✔
2485
            CPLFormFilenameSafe(
2,734✔
2486
                CPLFormFilenameSafe(SYSCONFDIR, "gdal", nullptr).c_str(),
2,734✔
2487
                "gdalrc", nullptr)
2488
                .c_str(),
2489
            false);
2490
#endif
2491

2492
#ifdef _WIN32
2493
        const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
2494
#else
2495
        const char *pszHome = CPLGetConfigOption("HOME", nullptr);
1,367✔
2496
#endif
2497
        if (pszHome != nullptr)
1,367✔
2498
        {
2499
            CPLLoadConfigOptionsFromFile(
1,367✔
2500
                CPLFormFilenameSafe(
2,734✔
2501
                    CPLFormFilenameSafe(pszHome, ".gdal", nullptr).c_str(),
2,734✔
2502
                    "gdalrc", nullptr)
2503
                    .c_str(),
2504
                false);
2505
        }
2506
    }
2507
}
1,369✔
2508

2509
/************************************************************************/
2510
/*                              CPLStat()                               */
2511
/************************************************************************/
2512

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

2515
int CPLStat(const char *pszPath, VSIStatBuf *psStatBuf)
×
2516

2517
{
2518
    if (strlen(pszPath) == 2 && pszPath[1] == ':')
×
2519
    {
2520
        char szAltPath[4] = {pszPath[0], pszPath[1], '\\', '\0'};
×
2521
        return VSIStat(szAltPath, psStatBuf);
×
2522
    }
2523

2524
    return VSIStat(pszPath, psStatBuf);
×
2525
}
2526

2527
/************************************************************************/
2528
/*                            proj_strtod()                             */
2529
/************************************************************************/
2530
static double proj_strtod(char *nptr, char **endptr)
18✔
2531

2532
{
2533
    char c = '\0';
18✔
2534
    char *cp = nptr;
18✔
2535

2536
    // Scan for characters which cause problems with VC++ strtod().
2537
    while ((c = *cp) != '\0')
84✔
2538
    {
2539
        if (c == 'd' || c == 'D')
72✔
2540
        {
2541
            // Found one, so NUL it out, call strtod(),
2542
            // then restore it and return.
2543
            *cp = '\0';
6✔
2544
            const double result = CPLStrtod(nptr, endptr);
6✔
2545
            *cp = c;
6✔
2546
            return result;
6✔
2547
        }
2548
        ++cp;
66✔
2549
    }
2550

2551
    // No offending characters, just handle normally.
2552

2553
    return CPLStrtod(nptr, endptr);
12✔
2554
}
2555

2556
/************************************************************************/
2557
/*                            CPLDMSToDec()                             */
2558
/************************************************************************/
2559

2560
static const char *sym = "NnEeSsWw";
2561
constexpr double vm[] = {1.0, 0.0166666666667, 0.00027777778};
2562

2563
/** CPLDMSToDec */
2564
double CPLDMSToDec(const char *is)
6✔
2565

2566
{
2567
    // Copy string into work space.
2568
    while (isspace(static_cast<unsigned char>(*is)))
6✔
2569
        ++is;
×
2570

2571
    const char *p = is;
6✔
2572
    char work[64] = {};
6✔
2573
    char *s = work;
6✔
2574
    int n = sizeof(work);
6✔
2575
    for (; isgraph(*p) && --n;)
60✔
2576
        *s++ = *p++;
54✔
2577
    *s = '\0';
6✔
2578
    // It is possible that a really odd input (like lots of leading
2579
    // zeros) could be truncated in copying into work.  But...
2580
    s = work;
6✔
2581
    int sign = *s;
6✔
2582

2583
    if (sign == '+' || sign == '-')
6✔
2584
        s++;
×
2585
    else
2586
        sign = '+';
6✔
2587

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

2640
    return v;
6✔
2641
}
2642

2643
/************************************************************************/
2644
/*                            CPLDecToDMS()                             */
2645
/************************************************************************/
2646

2647
/** Translate a decimal degrees value to a DMS string with hemisphere. */
2648

2649
const char *CPLDecToDMS(double dfAngle, const char *pszAxis, int nPrecision)
550✔
2650

2651
{
2652
    VALIDATE_POINTER1(pszAxis, "CPLDecToDMS", "");
550✔
2653

2654
    if (std::isnan(dfAngle))
550✔
2655
        return "Invalid angle";
×
2656

2657
    const double dfEpsilon = (0.5 / 3600.0) * pow(0.1, nPrecision);
550✔
2658
    const double dfABSAngle = std::abs(dfAngle) + dfEpsilon;
550✔
2659
    if (dfABSAngle > 361.0)
550✔
2660
    {
2661
        return "Invalid angle";
×
2662
    }
2663

2664
    const int nDegrees = static_cast<int>(dfABSAngle);
550✔
2665
    const int nMinutes = static_cast<int>((dfABSAngle - nDegrees) * 60);
550✔
2666
    double dfSeconds = dfABSAngle * 3600 - nDegrees * 3600 - nMinutes * 60;
550✔
2667

2668
    if (dfSeconds > dfEpsilon * 3600.0)
550✔
2669
        dfSeconds -= dfEpsilon * 3600.0;
544✔
2670

2671
    const char *pszHemisphere = nullptr;
550✔
2672
    if (EQUAL(pszAxis, "Long") && dfAngle < 0.0)
550✔
2673
        pszHemisphere = "W";
238✔
2674
    else if (EQUAL(pszAxis, "Long"))
312✔
2675
        pszHemisphere = "E";
37✔
2676
    else if (dfAngle < 0.0)
275✔
2677
        pszHemisphere = "S";
22✔
2678
    else
2679
        pszHemisphere = "N";
253✔
2680

2681
    char szFormat[30] = {};
550✔
2682
    CPLsnprintf(szFormat, sizeof(szFormat), "%%3dd%%2d\'%%%d.%df\"%s",
550✔
2683
                nPrecision + 3, nPrecision, pszHemisphere);
2684

2685
    static CPL_THREADLOCAL char szBuffer[50] = {};
2686
    CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, nDegrees, nMinutes,
550✔
2687
                dfSeconds);
2688

2689
    return szBuffer;
550✔
2690
}
2691

2692
/************************************************************************/
2693
/*                         CPLPackedDMSToDec()                          */
2694
/************************************************************************/
2695

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

2739
double CPLPackedDMSToDec(double dfPacked)
34✔
2740
{
2741
    const double dfSign = dfPacked < 0.0 ? -1 : 1;
34✔
2742

2743
    double dfSeconds = std::abs(dfPacked);
34✔
2744
    double dfDegrees = floor(dfSeconds / 1000000.0);
34✔
2745
    dfSeconds -= dfDegrees * 1000000.0;
34✔
2746
    const double dfMinutes = floor(dfSeconds / 1000.0);
34✔
2747
    dfSeconds -= dfMinutes * 1000.0;
34✔
2748
    dfSeconds = dfSign * (dfDegrees * 3600.0 + dfMinutes * 60.0 + dfSeconds);
34✔
2749
    dfDegrees = dfSeconds / 3600.0;
34✔
2750

2751
    return dfDegrees;
34✔
2752
}
2753

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

2773
double CPLDecToPackedDMS(double dfDec)
8✔
2774
{
2775
    const double dfSign = dfDec < 0.0 ? -1 : 1;
8✔
2776

2777
    dfDec = std::abs(dfDec);
8✔
2778
    const double dfDegrees = floor(dfDec);
8✔
2779
    const double dfMinutes = floor((dfDec - dfDegrees) * 60.0);
8✔
2780
    const double dfSeconds = (dfDec - dfDegrees) * 3600.0 - dfMinutes * 60.0;
8✔
2781

2782
    return dfSign * (dfDegrees * 1000000.0 + dfMinutes * 1000.0 + dfSeconds);
8✔
2783
}
2784

2785
/************************************************************************/
2786
/*                         CPLStringToComplex()                         */
2787
/************************************************************************/
2788

2789
/** Fetch the real and imaginary part of a serialized complex number */
2790
void CPL_DLL CPLStringToComplex(const char *pszString, double *pdfReal,
2,199✔
2791
                                double *pdfImag)
2792

2793
{
2794
    while (*pszString == ' ')
2,199✔
2795
        pszString++;
×
2796

2797
    *pdfReal = CPLAtof(pszString);
2,199✔
2798
    *pdfImag = 0.0;
2,199✔
2799

2800
    int iPlus = -1;
2,199✔
2801
    int iImagEnd = -1;
2,199✔
2802

2803
    for (int i = 0; i < 100 && pszString[i] != '\0' && pszString[i] != ' '; i++)
4,648✔
2804
    {
2805
        if (pszString[i] == '+' && i > 0)
2,449✔
2806
            iPlus = i;
×
2807
        if (pszString[i] == '-' && i > 0)
2,449✔
2808
            iPlus = i;
×
2809
        if (pszString[i] == 'i')
2,449✔
2810
            iImagEnd = i;
2✔
2811
    }
2812

2813
    if (iPlus > -1 && iImagEnd > -1 && iPlus < iImagEnd)
2,199✔
2814
    {
2815
        *pdfImag = CPLAtof(pszString + iPlus);
×
2816
    }
2817

2818
    return;
2,199✔
2819
}
2820

2821
/************************************************************************/
2822
/*                           CPLOpenShared()                            */
2823
/************************************************************************/
2824

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

2850
FILE *CPLOpenShared(const char *pszFilename, const char *pszAccess,
35✔
2851
                    int bLargeIn)
2852

2853
{
2854
    const bool bLarge = CPL_TO_BOOL(bLargeIn);
35✔
2855
    CPLMutexHolderD(&hSharedFileMutex);
70✔
2856
    const GIntBig nPID = CPLGetPID();
35✔
2857

2858
    /* -------------------------------------------------------------------- */
2859
    /*      Is there an existing file we can use?                           */
2860
    /* -------------------------------------------------------------------- */
2861
    const bool bReuse = EQUAL(pszAccess, "rb") || EQUAL(pszAccess, "rb+");
35✔
2862

2863
    for (int i = 0; bReuse && i < nSharedFileCount; i++)
39✔
2864
    {
2865
        if (strcmp(pasSharedFileList[i].pszFilename, pszFilename) == 0 &&
20✔
2866
            !bLarge == !pasSharedFileList[i].bLarge &&
4✔
2867
            EQUAL(pasSharedFileList[i].pszAccess, pszAccess) &&
16✔
2868
            nPID == pasSharedFileListExtra[i].nPID)
4✔
2869
        {
2870
            pasSharedFileList[i].nRefCount++;
4✔
2871
            return pasSharedFileList[i].fp;
4✔
2872
        }
2873
    }
2874

2875
    /* -------------------------------------------------------------------- */
2876
    /*      Open the file.                                                  */
2877
    /* -------------------------------------------------------------------- */
2878
    FILE *fp = bLarge
2879
                   ? reinterpret_cast<FILE *>(VSIFOpenL(pszFilename, pszAccess))
31✔
2880
                   : VSIFOpen(pszFilename, pszAccess);
×
2881

2882
    if (fp == nullptr)
31✔
2883
        return nullptr;
8✔
2884

2885
    /* -------------------------------------------------------------------- */
2886
    /*      Add an entry to the list.                                       */
2887
    /* -------------------------------------------------------------------- */
2888
    nSharedFileCount++;
23✔
2889

2890
    pasSharedFileList = static_cast<CPLSharedFileInfo *>(
23✔
2891
        CPLRealloc(const_cast<CPLSharedFileInfo *>(pasSharedFileList),
46✔
2892
                   sizeof(CPLSharedFileInfo) * nSharedFileCount));
23✔
2893
    pasSharedFileListExtra = static_cast<CPLSharedFileInfoExtra *>(
23✔
2894
        CPLRealloc(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra),
46✔
2895
                   sizeof(CPLSharedFileInfoExtra) * nSharedFileCount));
23✔
2896

2897
    pasSharedFileList[nSharedFileCount - 1].fp = fp;
23✔
2898
    pasSharedFileList[nSharedFileCount - 1].nRefCount = 1;
23✔
2899
    pasSharedFileList[nSharedFileCount - 1].bLarge = bLarge;
23✔
2900
    pasSharedFileList[nSharedFileCount - 1].pszFilename =
46✔
2901
        CPLStrdup(pszFilename);
23✔
2902
    pasSharedFileList[nSharedFileCount - 1].pszAccess = CPLStrdup(pszAccess);
23✔
2903
    pasSharedFileListExtra[nSharedFileCount - 1].nPID = nPID;
23✔
2904

2905
    return fp;
23✔
2906
}
2907

2908
/************************************************************************/
2909
/*                           CPLCloseShared()                           */
2910
/************************************************************************/
2911

2912
/**
2913
 * Close shared file.
2914
 *
2915
 * Dereferences the indicated file handle, and closes it if the reference
2916
 * count has dropped to zero.  A CPLError() is issued if the file is not
2917
 * in the shared file list.
2918
 *
2919
 * @param fp file handle from CPLOpenShared() to deaccess.
2920
 */
2921

2922
void CPLCloseShared(FILE *fp)
27✔
2923

2924
{
2925
    CPLMutexHolderD(&hSharedFileMutex);
27✔
2926

2927
    /* -------------------------------------------------------------------- */
2928
    /*      Search for matching information.                                */
2929
    /* -------------------------------------------------------------------- */
2930
    int i = 0;
27✔
2931
    for (; i < nSharedFileCount && fp != pasSharedFileList[i].fp; i++)
29✔
2932
    {
2933
    }
2934

2935
    if (i == nSharedFileCount)
27✔
2936
    {
2937
        CPLError(CE_Failure, CPLE_AppDefined,
×
2938
                 "Unable to find file handle %p in CPLCloseShared().", fp);
2939
        return;
×
2940
    }
2941

2942
    /* -------------------------------------------------------------------- */
2943
    /*      Dereference and return if there are still some references.      */
2944
    /* -------------------------------------------------------------------- */
2945
    if (--pasSharedFileList[i].nRefCount > 0)
27✔
2946
        return;
4✔
2947

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

2965
    CPLFree(pasSharedFileList[i].pszFilename);
23✔
2966
    CPLFree(pasSharedFileList[i].pszAccess);
23✔
2967

2968
    nSharedFileCount--;
23✔
2969
    memmove(
23✔
2970
        const_cast<CPLSharedFileInfo *>(pasSharedFileList + i),
23✔
2971
        const_cast<CPLSharedFileInfo *>(pasSharedFileList + nSharedFileCount),
23✔
2972
        sizeof(CPLSharedFileInfo));
2973
    memmove(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra + i),
23✔
2974
            const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra +
23✔
2975
                                                 nSharedFileCount),
23✔
2976
            sizeof(CPLSharedFileInfoExtra));
2977

2978
    if (nSharedFileCount == 0)
23✔
2979
    {
2980
        CPLFree(const_cast<CPLSharedFileInfo *>(pasSharedFileList));
20✔
2981
        pasSharedFileList = nullptr;
20✔
2982
        CPLFree(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra));
20✔
2983
        pasSharedFileListExtra = nullptr;
20✔
2984
    }
2985
}
2986

2987
/************************************************************************/
2988
/*                   CPLCleanupSharedFileMutex()                        */
2989
/************************************************************************/
2990

2991
void CPLCleanupSharedFileMutex()
922✔
2992
{
2993
    if (hSharedFileMutex != nullptr)
922✔
2994
    {
2995
        CPLDestroyMutex(hSharedFileMutex);
×
2996
        hSharedFileMutex = nullptr;
×
2997
    }
2998
}
922✔
2999

3000
/************************************************************************/
3001
/*                          CPLGetSharedList()                          */
3002
/************************************************************************/
3003

3004
/**
3005
 * Fetch list of open shared files.
3006
 *
3007
 * @param pnCount place to put the count of entries.
3008
 *
3009
 * @return the pointer to the first in the array of shared file info
3010
 * structures.
3011
 */
3012

3013
CPLSharedFileInfo *CPLGetSharedList(int *pnCount)
×
3014

3015
{
3016
    if (pnCount != nullptr)
×
3017
        *pnCount = nSharedFileCount;
×
3018

3019
    return const_cast<CPLSharedFileInfo *>(pasSharedFileList);
×
3020
}
3021

3022
/************************************************************************/
3023
/*                         CPLDumpSharedList()                          */
3024
/************************************************************************/
3025

3026
/**
3027
 * Report open shared files.
3028
 *
3029
 * Dumps all open shared files to the indicated file handle.  If the
3030
 * file handle is NULL information is sent via the CPLDebug() call.
3031
 *
3032
 * @param fp File handle to write to.
3033
 */
3034

3035
void CPLDumpSharedList(FILE *fp)
103✔
3036

3037
{
3038
    if (nSharedFileCount > 0)
103✔
3039
    {
3040
        if (fp == nullptr)
×
3041
            CPLDebug("CPL", "%d Shared files open.", nSharedFileCount);
×
3042
        else
3043
            fprintf(fp, "%d Shared files open.", nSharedFileCount);
×
3044
    }
3045

3046
    for (int i = 0; i < nSharedFileCount; i++)
103✔
3047
    {
3048
        if (fp == nullptr)
×
3049
            CPLDebug("CPL", "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
×
3050
                     pasSharedFileList[i].bLarge,
×
3051
                     pasSharedFileList[i].pszAccess,
×
3052
                     pasSharedFileList[i].pszFilename);
×
3053
        else
3054
            fprintf(fp, "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
×
3055
                    pasSharedFileList[i].bLarge, pasSharedFileList[i].pszAccess,
×
3056
                    pasSharedFileList[i].pszFilename);
×
3057
    }
3058
}
103✔
3059

3060
/************************************************************************/
3061
/*                           CPLUnlinkTree()                            */
3062
/************************************************************************/
3063

3064
/** Recursively unlink a directory.
3065
 *
3066
 * @return 0 on successful completion, -1 if function fails.
3067
 */
3068

3069
int CPLUnlinkTree(const char *pszPath)
52✔
3070

3071
{
3072
    /* -------------------------------------------------------------------- */
3073
    /*      First, ensure there is such a file.                             */
3074
    /* -------------------------------------------------------------------- */
3075
    VSIStatBufL sStatBuf;
3076

3077
    if (VSIStatL(pszPath, &sStatBuf) != 0)
52✔
3078
    {
3079
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
3080
                 "It seems no file system object called '%s' exists.", pszPath);
3081

3082
        return -1;
2✔
3083
    }
3084

3085
    /* -------------------------------------------------------------------- */
3086
    /*      If it is a simple file, just delete it.                         */
3087
    /* -------------------------------------------------------------------- */
3088
    if (VSI_ISREG(sStatBuf.st_mode))
50✔
3089
    {
3090
        if (VSIUnlink(pszPath) != 0)
34✔
3091
        {
3092
            CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
×
3093
                     pszPath);
3094

3095
            return -1;
×
3096
        }
3097

3098
        return 0;
34✔
3099
    }
3100

3101
    /* -------------------------------------------------------------------- */
3102
    /*      If it is a directory recurse then unlink the directory.         */
3103
    /* -------------------------------------------------------------------- */
3104
    else if (VSI_ISDIR(sStatBuf.st_mode))
16✔
3105
    {
3106
        char **papszItems = VSIReadDir(pszPath);
16✔
3107

3108
        for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
32✔
3109
        {
3110
            if (papszItems[i][0] == '\0' || EQUAL(papszItems[i], ".") ||
16✔
3111
                EQUAL(papszItems[i], ".."))
16✔
3112
                continue;
×
3113

3114
            const std::string osSubPath =
3115
                CPLFormFilenameSafe(pszPath, papszItems[i], nullptr);
16✔
3116

3117
            const int nErr = CPLUnlinkTree(osSubPath.c_str());
16✔
3118

3119
            if (nErr != 0)
16✔
3120
            {
3121
                CSLDestroy(papszItems);
×
3122
                return nErr;
×
3123
            }
3124
        }
3125

3126
        CSLDestroy(papszItems);
16✔
3127

3128
        if (VSIRmdir(pszPath) != 0)
16✔
3129
        {
3130
            CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
×
3131
                     pszPath);
3132

3133
            return -1;
×
3134
        }
3135

3136
        return 0;
16✔
3137
    }
3138

3139
    /* -------------------------------------------------------------------- */
3140
    /*      otherwise report an error.                                      */
3141
    /* -------------------------------------------------------------------- */
3142
    CPLError(CE_Failure, CPLE_AppDefined,
×
3143
             "Failed to unlink %s.\nUnrecognised filesystem object.", pszPath);
3144
    return 1000;
×
3145
}
3146

3147
/************************************************************************/
3148
/*                            CPLCopyFile()                             */
3149
/************************************************************************/
3150

3151
/** Copy a file */
3152
int CPLCopyFile(const char *pszNewPath, const char *pszOldPath)
2,231✔
3153

3154
{
3155
    return VSICopyFile(pszOldPath, pszNewPath, nullptr,
2,231✔
3156
                       static_cast<vsi_l_offset>(-1), nullptr, nullptr,
3157
                       nullptr);
2,231✔
3158
}
3159

3160
/************************************************************************/
3161
/*                            CPLCopyTree()                             */
3162
/************************************************************************/
3163

3164
/** Recursively copy a tree */
3165
int CPLCopyTree(const char *pszNewPath, const char *pszOldPath)
4✔
3166

3167
{
3168
    VSIStatBufL sStatBuf;
3169
    if (VSIStatL(pszNewPath, &sStatBuf) == 0)
4✔
3170
    {
3171
        CPLError(
1✔
3172
            CE_Failure, CPLE_AppDefined,
3173
            "It seems that a file system object called '%s' already exists.",
3174
            pszNewPath);
3175

3176
        return -1;
1✔
3177
    }
3178

3179
    if (VSIStatL(pszOldPath, &sStatBuf) != 0)
3✔
3180
    {
3181
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
3182
                 "It seems no file system object called '%s' exists.",
3183
                 pszOldPath);
3184

3185
        return -1;
1✔
3186
    }
3187

3188
    if (VSI_ISDIR(sStatBuf.st_mode))
2✔
3189
    {
3190
        if (VSIMkdir(pszNewPath, 0755) != 0)
1✔
3191
        {
3192
            CPLError(CE_Failure, CPLE_AppDefined,
×
3193
                     "Cannot create directory '%s'.", pszNewPath);
3194

3195
            return -1;
×
3196
        }
3197

3198
        char **papszItems = VSIReadDir(pszOldPath);
1✔
3199

3200
        for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
4✔
3201
        {
3202
            if (EQUAL(papszItems[i], ".") || EQUAL(papszItems[i], ".."))
3✔
3203
                continue;
2✔
3204

3205
            const std::string osNewSubPath =
3206
                CPLFormFilenameSafe(pszNewPath, papszItems[i], nullptr);
1✔
3207
            const std::string osOldSubPath =
3208
                CPLFormFilenameSafe(pszOldPath, papszItems[i], nullptr);
1✔
3209

3210
            const int nErr =
3211
                CPLCopyTree(osNewSubPath.c_str(), osOldSubPath.c_str());
1✔
3212

3213
            if (nErr != 0)
1✔
3214
            {
3215
                CSLDestroy(papszItems);
×
3216
                return nErr;
×
3217
            }
3218
        }
3219
        CSLDestroy(papszItems);
1✔
3220

3221
        return 0;
1✔
3222
    }
3223
    else if (VSI_ISREG(sStatBuf.st_mode))
1✔
3224
    {
3225
        return CPLCopyFile(pszNewPath, pszOldPath);
1✔
3226
    }
3227
    else
3228
    {
3229
        CPLError(CE_Failure, CPLE_AppDefined,
×
3230
                 "Unrecognized filesystem object : '%s'.", pszOldPath);
3231
        return -1;
×
3232
    }
3233
}
3234

3235
/************************************************************************/
3236
/*                            CPLMoveFile()                             */
3237
/************************************************************************/
3238

3239
/** Move a file */
3240
int CPLMoveFile(const char *pszNewPath, const char *pszOldPath)
184✔
3241

3242
{
3243
    if (VSIRename(pszOldPath, pszNewPath) == 0)
184✔
3244
        return 0;
181✔
3245

3246
    const int nRet = CPLCopyFile(pszNewPath, pszOldPath);
3✔
3247

3248
    if (nRet == 0)
3✔
3249
        VSIUnlink(pszOldPath);
3✔
3250
    return nRet;
3✔
3251
}
3252

3253
/************************************************************************/
3254
/*                             CPLSymlink()                             */
3255
/************************************************************************/
3256

3257
/** Create a symbolic link */
3258
#ifdef _WIN32
3259
int CPLSymlink(const char *, const char *, CSLConstList)
3260
{
3261
    return -1;
3262
}
3263
#else
3264
int CPLSymlink(const char *pszOldPath, const char *pszNewPath,
×
3265
               CSLConstList /* papszOptions */)
3266
{
3267
    return symlink(pszOldPath, pszNewPath);
×
3268
}
3269
#endif
3270

3271
/************************************************************************/
3272
/* ==================================================================== */
3273
/*                              CPLLocaleC                              */
3274
/* ==================================================================== */
3275
/************************************************************************/
3276

3277
//! @cond Doxygen_Suppress
3278
/************************************************************************/
3279
/*                             CPLLocaleC()                             */
3280
/************************************************************************/
3281

3282
CPLLocaleC::CPLLocaleC() : pszOldLocale(nullptr)
129✔
3283
{
3284
    if (CPLTestBool(CPLGetConfigOption("GDAL_DISABLE_CPLLOCALEC", "NO")))
129✔
3285
        return;
×
3286

3287
    pszOldLocale = CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr));
129✔
3288
    if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
129✔
3289
        CPLsetlocale(LC_NUMERIC, "C") == nullptr)
×
3290
    {
3291
        CPLFree(pszOldLocale);
129✔
3292
        pszOldLocale = nullptr;
129✔
3293
    }
3294
}
3295

3296
/************************************************************************/
3297
/*                            ~CPLLocaleC()                             */
3298
/************************************************************************/
3299

3300
CPLLocaleC::~CPLLocaleC()
×
3301

3302
{
3303
    if (pszOldLocale == nullptr)
129✔
3304
        return;
129✔
3305

3306
    CPLsetlocale(LC_NUMERIC, pszOldLocale);
×
3307
    CPLFree(pszOldLocale);
×
3308
}
129✔
3309

3310
/************************************************************************/
3311
/*                        CPLThreadLocaleCPrivate                       */
3312
/************************************************************************/
3313

3314
#ifdef HAVE_USELOCALE
3315

3316
class CPLThreadLocaleCPrivate
3317
{
3318
    locale_t nNewLocale;
3319
    locale_t nOldLocale;
3320

3321
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3322

3323
  public:
3324
    CPLThreadLocaleCPrivate();
3325
    ~CPLThreadLocaleCPrivate();
3326
};
3327

3328
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
×
3329
    : nNewLocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)),
×
3330
      nOldLocale(uselocale(nNewLocale))
×
3331
{
3332
}
×
3333

3334
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
×
3335
{
3336
    uselocale(nOldLocale);
×
3337
    freelocale(nNewLocale);
×
3338
}
×
3339

3340
#elif defined(_MSC_VER)
3341

3342
class CPLThreadLocaleCPrivate
3343
{
3344
    int nOldValConfigThreadLocale;
3345
    char *pszOldLocale;
3346

3347
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3348

3349
  public:
3350
    CPLThreadLocaleCPrivate();
3351
    ~CPLThreadLocaleCPrivate();
3352
};
3353

3354
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3355
{
3356
    nOldValConfigThreadLocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
3357
    pszOldLocale = setlocale(LC_NUMERIC, "C");
3358
    if (pszOldLocale)
3359
        pszOldLocale = CPLStrdup(pszOldLocale);
3360
}
3361

3362
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3363
{
3364
    if (pszOldLocale != nullptr)
3365
    {
3366
        setlocale(LC_NUMERIC, pszOldLocale);
3367
        CPLFree(pszOldLocale);
3368
    }
3369
    _configthreadlocale(nOldValConfigThreadLocale);
3370
}
3371

3372
#else
3373

3374
class CPLThreadLocaleCPrivate
3375
{
3376
    char *pszOldLocale;
3377

3378
    CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3379

3380
  public:
3381
    CPLThreadLocaleCPrivate();
3382
    ~CPLThreadLocaleCPrivate();
3383
};
3384

3385
CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3386
    : pszOldLocale(CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr)))
3387
{
3388
    if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
3389
        CPLsetlocale(LC_NUMERIC, "C") == nullptr)
3390
    {
3391
        CPLFree(pszOldLocale);
3392
        pszOldLocale = nullptr;
3393
    }
3394
}
3395

3396
CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3397
{
3398
    if (pszOldLocale != nullptr)
3399
    {
3400
        CPLsetlocale(LC_NUMERIC, pszOldLocale);
3401
        CPLFree(pszOldLocale);
3402
    }
3403
}
3404

3405
#endif
3406

3407
/************************************************************************/
3408
/*                        CPLThreadLocaleC()                            */
3409
/************************************************************************/
3410

3411
CPLThreadLocaleC::CPLThreadLocaleC() : m_private(new CPLThreadLocaleCPrivate)
×
3412
{
3413
}
×
3414

3415
/************************************************************************/
3416
/*                       ~CPLThreadLocaleC()                            */
3417
/************************************************************************/
3418

3419
CPLThreadLocaleC::~CPLThreadLocaleC()
×
3420

3421
{
3422
    delete m_private;
×
3423
}
×
3424

3425
//! @endcond
3426

3427
/************************************************************************/
3428
/*                          CPLsetlocale()                              */
3429
/************************************************************************/
3430

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

3453
    // Make it thread-locale storage.
3454
    return const_cast<char *>(CPLSPrintf("%s", pszRet));
131✔
3455
}
3456

3457
/************************************************************************/
3458
/*                       CPLCleanupSetlocaleMutex()                     */
3459
/************************************************************************/
3460

3461
void CPLCleanupSetlocaleMutex(void)
922✔
3462
{
3463
    if (hSetLocaleMutex != nullptr)
922✔
3464
        CPLDestroyMutex(hSetLocaleMutex);
5✔
3465
    hSetLocaleMutex = nullptr;
922✔
3466
}
922✔
3467

3468
/************************************************************************/
3469
/*                            IsPowerOfTwo()                            */
3470
/************************************************************************/
3471

3472
int CPLIsPowerOfTwo(unsigned int i)
140✔
3473
{
3474
    if (i == 0)
140✔
3475
        return FALSE;
×
3476
    return (i & (i - 1)) == 0 ? TRUE : FALSE;
140✔
3477
}
3478

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

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

3507
int CPLCheckForFile(char *pszFilename, char **papszSiblingFiles)
157,817✔
3508

3509
{
3510
    /* -------------------------------------------------------------------- */
3511
    /*      Fallback case if we don't have a sibling file list.             */
3512
    /* -------------------------------------------------------------------- */
3513
    if (papszSiblingFiles == nullptr)
157,817✔
3514
    {
3515
        VSIStatBufL sStatBuf;
3516

3517
        return VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
10,678✔
3518
    }
3519

3520
    /* -------------------------------------------------------------------- */
3521
    /*      We have sibling files, compare the non-path filename portion    */
3522
    /*      of pszFilename too all entries.                                 */
3523
    /* -------------------------------------------------------------------- */
3524
    const CPLString osFileOnly = CPLGetFilename(pszFilename);
294,277✔
3525

3526
    for (int i = 0; papszSiblingFiles[i] != nullptr; i++)
13,779,400✔
3527
    {
3528
        if (EQUAL(papszSiblingFiles[i], osFileOnly))
13,632,500✔
3529
        {
3530
            strcpy(pszFilename + strlen(pszFilename) - osFileOnly.size(),
316✔
3531
                   papszSiblingFiles[i]);
159✔
3532
            return TRUE;
157✔
3533
        }
3534
    }
3535

3536
    return FALSE;
146,981✔
3537
}
3538

3539
/************************************************************************/
3540
/*      Stub implementation of zip services if we don't have libz.      */
3541
/************************************************************************/
3542

3543
#if !defined(HAVE_LIBZ)
3544

3545
void *CPLCreateZip(const char *, char **)
3546

3547
{
3548
    CPLError(CE_Failure, CPLE_NotSupported,
3549
             "This GDAL/OGR build does not include zlib and zip services.");
3550
    return nullptr;
3551
}
3552

3553
CPLErr CPLCreateFileInZip(void *, const char *, char **)
3554
{
3555
    return CE_Failure;
3556
}
3557

3558
CPLErr CPLWriteFileInZip(void *, const void *, int)
3559
{
3560
    return CE_Failure;
3561
}
3562

3563
CPLErr CPLCloseFileInZip(void *)
3564
{
3565
    return CE_Failure;
3566
}
3567

3568
CPLErr CPLCloseZip(void *)
3569
{
3570
    return CE_Failure;
3571
}
3572

3573
void *CPLZLibDeflate(const void *, size_t, int, void *, size_t,
3574
                     size_t *pnOutBytes)
3575
{
3576
    if (pnOutBytes != nullptr)
3577
        *pnOutBytes = 0;
3578
    return nullptr;
3579
}
3580

3581
void *CPLZLibInflate(const void *, size_t, void *, size_t, size_t *pnOutBytes)
3582
{
3583
    if (pnOutBytes != nullptr)
3584
        *pnOutBytes = 0;
3585
    return nullptr;
3586
}
3587

3588
#endif /* !defined(HAVE_LIBZ) */
3589

3590
/************************************************************************/
3591
/* ==================================================================== */
3592
/*                          CPLConfigOptionSetter                       */
3593
/* ==================================================================== */
3594
/************************************************************************/
3595

3596
//! @cond Doxygen_Suppress
3597
/************************************************************************/
3598
/*                         CPLConfigOptionSetter()                      */
3599
/************************************************************************/
3600

3601
CPLConfigOptionSetter::CPLConfigOptionSetter(const char *pszKey,
23,409✔
3602
                                             const char *pszValue,
3603
                                             bool bSetOnlyIfUndefined)
23,409✔
3604
    : m_pszKey(CPLStrdup(pszKey)), m_pszOldValue(nullptr),
23,409✔
3605
      m_bRestoreOldValue(false)
23,398✔
3606
{
3607
    const char *pszOldValue = CPLGetThreadLocalConfigOption(pszKey, nullptr);
23,398✔
3608
    if ((bSetOnlyIfUndefined &&
37,173✔
3609
         CPLGetConfigOption(pszKey, nullptr) == nullptr) ||
33,039✔
3610
        !bSetOnlyIfUndefined)
9,651✔
3611
    {
3612
        m_bRestoreOldValue = true;
23,397✔
3613
        if (pszOldValue)
23,397✔
3614
            m_pszOldValue = CPLStrdup(pszOldValue);
645✔
3615
        CPLSetThreadLocalConfigOption(pszKey, pszValue);
23,397✔
3616
    }
3617
}
23,211✔
3618

3619
/************************************************************************/
3620
/*                        ~CPLConfigOptionSetter()                      */
3621
/************************************************************************/
3622

3623
CPLConfigOptionSetter::~CPLConfigOptionSetter()
46,733✔
3624
{
3625
    if (m_bRestoreOldValue)
23,410✔
3626
    {
3627
        CPLSetThreadLocalConfigOption(m_pszKey, m_pszOldValue);
23,399✔
3628
        CPLFree(m_pszOldValue);
23,214✔
3629
    }
3630
    CPLFree(m_pszKey);
23,238✔
3631
}
23,323✔
3632

3633
//! @endcond
3634

3635
/************************************************************************/
3636
/*                          CPLIsInteractive()                          */
3637
/************************************************************************/
3638

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