Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cpl_string.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * Name:     cpl_string.cpp
4
 * Project:  CPL - Common Portability Library
5
 * Purpose:  String and Stringlist manipulation functions.
6
 * Author:   Daniel Morissette, danmo@videotron.ca
7
 *
8
 **********************************************************************
9
 * Copyright (c) 1998, Daniel Morissette
10
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 **********************************************************************
14
 *
15
 * Independent Security Audit 2003/04/04 Andrey Kiselev:
16
 *   Completed audit of this module. All functions may be used without buffer
17
 *   overflows and stack corruptions with any kind of input data strings with
18
 *   except of CPLSPrintf() and CSLAppendPrintf() (see note below).
19
 *
20
 * Security Audit 2003/03/28 warmerda:
21
 *   Completed security audit.  I believe that this module may be safely used
22
 *   to parse tokenize arbitrary input strings, assemble arbitrary sets of
23
 *   names values into string lists, unescape and escape text even if provided
24
 *   by a potentially hostile source.
25
 *
26
 *   CPLSPrintf() and CSLAppendPrintf() may not be safely invoked on
27
 *   arbitrary length inputs since it has a fixed size output buffer on system
28
 *   without vsnprintf().
29
 *
30
 **********************************************************************/
31
32
#undef WARN_STANDARD_PRINTF
33
34
#include "cpl_port.h"
35
#include "cpl_string.h"
36
37
#include <algorithm>
38
#include <cctype>
39
#include <climits>
40
#include <cmath>
41
#include <cstdlib>
42
#include <cstring>
43
44
#include <limits>
45
46
#include "cpl_config.h"
47
#include "cpl_multiproc.h"
48
#include "cpl_vsi.h"
49
50
#if !defined(va_copy) && defined(__va_copy)
51
#define va_copy __va_copy
52
#endif
53
54
/*=====================================================================
55
                    StringList manipulation functions.
56
 =====================================================================*/
57
58
/**********************************************************************
59
 *                       CSLAddString()
60
 **********************************************************************/
61
62
/** Append a string to a StringList and return a pointer to the modified
63
 * StringList.
64
 *
65
 * If the input StringList is NULL, then a new StringList is created.
66
 * Note that CSLAddString performance when building a list is in O(n^2)
67
 * which can cause noticeable slow down when n > 10000.
68
 */
69
char **CSLAddString(char **papszStrList, const char *pszNewString)
70
11.8M
{
71
11.8M
    char **papszRet = CSLAddStringMayFail(papszStrList, pszNewString);
72
11.8M
    if (papszRet == nullptr && pszNewString != nullptr)
73
0
        abort();
74
11.8M
    return papszRet;
75
11.8M
}
76
77
/** Same as CSLAddString() but may return NULL in case of (memory) failure */
78
char **CSLAddStringMayFail(char **papszStrList, const char *pszNewString)
79
12.5M
{
80
12.5M
    if (pszNewString == nullptr)
81
0
        return papszStrList;  // Nothing to do!
82
83
12.5M
    char *pszDup = VSI_STRDUP_VERBOSE(pszNewString);
84
12.5M
    if (pszDup == nullptr)
85
0
        return nullptr;
86
87
    // Allocate room for the new string.
88
12.5M
    char **papszStrListNew = nullptr;
89
12.5M
    int nItems = 0;
90
91
12.5M
    if (papszStrList == nullptr)
92
1.41M
        papszStrListNew =
93
1.41M
            static_cast<char **>(VSI_CALLOC_VERBOSE(2, sizeof(char *)));
94
11.1M
    else
95
11.1M
    {
96
11.1M
        nItems = CSLCount(papszStrList);
97
11.1M
        papszStrListNew = static_cast<char **>(
98
11.1M
            VSI_REALLOC_VERBOSE(papszStrList, (nItems + 2) * sizeof(char *)));
99
11.1M
    }
100
12.5M
    if (papszStrListNew == nullptr)
101
0
    {
102
0
        VSIFree(pszDup);
103
0
        return nullptr;
104
0
    }
105
106
    // Copy the string in the list.
107
12.5M
    papszStrListNew[nItems] = pszDup;
108
12.5M
    papszStrListNew[nItems + 1] = nullptr;
109
110
12.5M
    return papszStrListNew;
111
12.5M
}
112
113
/************************************************************************/
114
/*                              CSLCount()                              */
115
/************************************************************************/
116
117
/**
118
 * Return number of items in a string list.
119
 *
120
 * Returns the number of items in a string list, not counting the
121
 * terminating NULL.  Passing in NULL is safe, and will result in a count
122
 * of zero.
123
 *
124
 * Lists are counted by iterating through them so long lists will
125
 * take more time than short lists.  Care should be taken to avoid using
126
 * CSLCount() as an end condition for loops as it will result in O(n^2)
127
 * behavior.
128
 *
129
 * @param papszStrList the string list to count.
130
 *
131
 * @return the number of entries.
132
 */
133
int CSLCount(CSLConstList papszStrList)
134
102M
{
135
102M
    if (!papszStrList)
136
4.17M
        return 0;
137
138
98.1M
    int nItems = 0;
139
140
3.79G
    while (*papszStrList != nullptr)
141
3.69G
    {
142
3.69G
        ++nItems;
143
3.69G
        ++papszStrList;
144
3.69G
    }
145
146
98.1M
    return nItems;
147
102M
}
148
149
/************************************************************************/
150
/*                            CSLGetField()                             */
151
/************************************************************************/
152
153
/**
154
 * Fetches the indicated field, being careful not to crash if the field
155
 * doesn't exist within this string list.
156
 *
157
 * The returned pointer should not be freed, and doesn't necessarily last long.
158
 */
159
const char *CSLGetField(CSLConstList papszStrList, int iField)
160
161
3.71M
{
162
3.71M
    if (papszStrList == nullptr || iField < 0)
163
0
        return ("");
164
165
7.51M
    for (int i = 0; i < iField + 1; i++)
166
3.80M
    {
167
3.80M
        if (papszStrList[i] == nullptr)
168
1.17k
            return "";
169
3.80M
    }
170
171
3.71M
    return (papszStrList[iField]);
172
3.71M
}
173
174
/************************************************************************/
175
/*                             CSLDestroy()                             */
176
/************************************************************************/
177
178
/**
179
 * Free string list.
180
 *
181
 * Frees the passed string list (null terminated array of strings).
182
 * It is safe to pass NULL.
183
 *
184
 * @param papszStrList the list to free.
185
 */
186
void CPL_STDCALL CSLDestroy(char **papszStrList)
187
427M
{
188
427M
    if (!papszStrList)
189
194M
        return;
190
191
699M
    for (char **papszPtr = papszStrList; *papszPtr != nullptr; ++papszPtr)
192
466M
    {
193
466M
        CPLFree(*papszPtr);
194
466M
    }
195
196
233M
    CPLFree(papszStrList);
197
233M
}
198
199
/************************************************************************/
200
/*                            CSLDuplicate()                            */
201
/************************************************************************/
202
203
/**
204
 * Clone a string list.
205
 *
206
 * Efficiently allocates a copy of a string list.  The returned list is
207
 * owned by the caller and should be freed with CSLDestroy().
208
 *
209
 * @param papszStrList the input string list.
210
 *
211
 * @return newly allocated copy.
212
 */
213
214
char **CSLDuplicate(CSLConstList papszStrList)
215
4.10M
{
216
4.10M
    const int nLines = CSLCount(papszStrList);
217
218
4.10M
    if (nLines == 0)
219
3.74M
        return nullptr;
220
221
361k
    CSLConstList papszSrc = papszStrList;
222
223
361k
    char **papszNewList =
224
361k
        static_cast<char **>(VSI_MALLOC2_VERBOSE(nLines + 1, sizeof(char *)));
225
226
361k
    char **papszDst = papszNewList;
227
228
11.4M
    for (; *papszSrc != nullptr; ++papszSrc, ++papszDst)
229
11.1M
    {
230
11.1M
        *papszDst = VSI_STRDUP_VERBOSE(*papszSrc);
231
11.1M
        if (*papszDst == nullptr)
232
0
        {
233
0
            CSLDestroy(papszNewList);
234
0
            return nullptr;
235
0
        }
236
11.1M
    }
237
361k
    *papszDst = nullptr;
238
239
361k
    return papszNewList;
240
361k
}
241
242
/************************************************************************/
243
/*                               CSLMerge                               */
244
/************************************************************************/
245
246
/**
247
 * \brief Merge two lists.
248
 *
249
 * The two lists are merged, ensuring that if any keys appear in both
250
 * that the value from the second (papszOverride) list take precedence.
251
 *
252
 * @param papszOrig the original list, being modified.
253
 * @param papszOverride the list of items being merged in.  This list
254
 * is unaltered and remains owned by the caller.
255
 *
256
 * @return updated list.
257
 */
258
259
char **CSLMerge(char **papszOrig, CSLConstList papszOverride)
260
261
58.8k
{
262
58.8k
    if (papszOrig == nullptr && papszOverride != nullptr)
263
2.74k
        return CSLDuplicate(papszOverride);
264
265
56.0k
    if (papszOverride == nullptr)
266
46.2k
        return papszOrig;
267
268
73.9k
    for (int i = 0; papszOverride[i] != nullptr; ++i)
269
64.1k
    {
270
64.1k
        char *pszKey = nullptr;
271
64.1k
        const char *pszValue = CPLParseNameValue(papszOverride[i], &pszKey);
272
273
64.1k
        papszOrig = CSLSetNameValue(papszOrig, pszKey, pszValue);
274
64.1k
        CPLFree(pszKey);
275
64.1k
    }
276
277
9.82k
    return papszOrig;
278
56.0k
}
279
280
/************************************************************************/
281
/*                              CSLLoad2()                              */
282
/************************************************************************/
283
284
/**
285
 * Load a text file into a string list.
286
 *
287
 * The VSI*L API is used, so VSIFOpenL() supported objects that aren't
288
 * physical files can also be accessed.  Files are returned as a string list,
289
 * with one item in the string list per line.  End of line markers are
290
 * stripped (by CPLReadLineL()).
291
 *
292
 * If reading the file fails a CPLError() will be issued and NULL returned.
293
 *
294
 * @param pszFname the name of the file to read.
295
 * @param nMaxLines maximum number of lines to read before stopping, or -1 for
296
 * no limit.
297
 * @param nMaxCols maximum number of characters in a line before stopping, or -1
298
 * for no limit.
299
 * @param papszOptions NULL-terminated array of options. Unused for now.
300
 *
301
 * @return a string list with the files lines, now owned by caller. To be freed
302
 * with CSLDestroy()
303
 *
304
 */
305
306
char **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols,
307
                CSLConstList papszOptions)
308
53.1k
{
309
53.1k
    VSILFILE *fp = VSIFOpenL(pszFname, "rb");
310
311
53.1k
    if (!fp)
312
489
    {
313
489
        if (CPLFetchBool(papszOptions, "EMIT_ERROR_IF_CANNOT_OPEN_FILE", true))
314
55
        {
315
            // Unable to open file.
316
55
            CPLError(CE_Failure, CPLE_OpenFailed,
317
55
                     "CSLLoad2(\"%s\") failed: unable to open file.", pszFname);
318
55
        }
319
489
        return nullptr;
320
489
    }
321
322
52.6k
    char **papszStrList = nullptr;
323
52.6k
    int nLines = 0;
324
52.6k
    int nAllocatedLines = 0;
325
326
22.4M
    while (!VSIFEofL(fp) && (nMaxLines == -1 || nLines < nMaxLines))
327
22.4M
    {
328
22.4M
        const char *pszLine = CPLReadLine2L(fp, nMaxCols, papszOptions);
329
22.4M
        if (pszLine == nullptr)
330
33.4k
            break;
331
332
22.4M
        if (nLines + 1 >= nAllocatedLines)
333
152k
        {
334
152k
            nAllocatedLines = 16 + nAllocatedLines * 2;
335
152k
            char **papszStrListNew = static_cast<char **>(
336
152k
                VSIRealloc(papszStrList, nAllocatedLines * sizeof(char *)));
337
152k
            if (papszStrListNew == nullptr)
338
0
            {
339
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
340
0
                CPLReadLineL(nullptr);
341
0
                CPLError(CE_Failure, CPLE_OutOfMemory,
342
0
                         "CSLLoad2(\"%s\") "
343
0
                         "failed: not enough memory to allocate lines.",
344
0
                         pszFname);
345
0
                return papszStrList;
346
0
            }
347
152k
            papszStrList = papszStrListNew;
348
152k
        }
349
22.4M
        papszStrList[nLines] = CPLStrdup(pszLine);
350
22.4M
        papszStrList[nLines + 1] = nullptr;
351
22.4M
        ++nLines;
352
22.4M
    }
353
354
52.6k
    CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
355
356
    // Free the internal thread local line buffer.
357
52.6k
    CPLReadLineL(nullptr);
358
359
52.6k
    return papszStrList;
360
52.6k
}
361
362
/************************************************************************/
363
/*                              CSLLoad()                               */
364
/************************************************************************/
365
366
/**
367
 * Load a text file into a string list.
368
 *
369
 * The VSI*L API is used, so VSIFOpenL() supported objects that aren't
370
 * physical files can also be accessed.  Files are returned as a string list,
371
 * with one item in the string list per line.  End of line markers are
372
 * stripped (by CPLReadLineL()).
373
 *
374
 * If reading the file fails a CPLError() will be issued and NULL returned.
375
 *
376
 * @param pszFname the name of the file to read.
377
 *
378
 * @return a string list with the files lines, now owned by caller. To be freed
379
 * with CSLDestroy()
380
 */
381
382
char **CSLLoad(const char *pszFname)
383
35.4k
{
384
35.4k
    return CSLLoad2(pszFname, -1, -1, nullptr);
385
35.4k
}
386
387
/**********************************************************************
388
 *                       CSLSave()
389
 **********************************************************************/
390
391
/** Write a StringList to a text file.
392
 *
393
 * Returns the number of lines written, or 0 if the file could not
394
 * be written.
395
 */
396
397
int CSLSave(CSLConstList papszStrList, const char *pszFname)
398
211
{
399
211
    if (papszStrList == nullptr)
400
0
        return 0;
401
402
211
    VSILFILE *fp = VSIFOpenL(pszFname, "wt");
403
211
    if (fp == nullptr)
404
0
    {
405
        // Unable to open file.
406
0
        CPLError(CE_Failure, CPLE_OpenFailed,
407
0
                 "CSLSave(\"%s\") failed: unable to open output file.",
408
0
                 pszFname);
409
0
        return 0;
410
0
    }
411
412
211
    int nLines = 0;
413
36.6k
    while (*papszStrList != nullptr)
414
36.4k
    {
415
36.4k
        if (VSIFPrintfL(fp, "%s\n", *papszStrList) < 1)
416
0
        {
417
0
            CPLError(CE_Failure, CPLE_FileIO,
418
0
                     "CSLSave(\"%s\") failed: unable to write to output file.",
419
0
                     pszFname);
420
0
            break;  // A Problem happened... abort.
421
0
        }
422
423
36.4k
        ++nLines;
424
36.4k
        ++papszStrList;
425
36.4k
    }
426
427
211
    if (VSIFCloseL(fp) != 0)
428
0
    {
429
0
        CPLError(CE_Failure, CPLE_FileIO,
430
0
                 "CSLSave(\"%s\") failed: unable to write to output file.",
431
0
                 pszFname);
432
0
    }
433
434
211
    return nLines;
435
211
}
436
437
/**********************************************************************
438
 *                       CSLPrint()
439
 **********************************************************************/
440
441
/** Print a StringList to fpOut.  If fpOut==NULL, then output is sent
442
 * to stdout.
443
 *
444
 * Returns the number of lines printed.
445
 */
446
int CSLPrint(CSLConstList papszStrList, FILE *fpOut)
447
0
{
448
0
    if (!papszStrList)
449
0
        return 0;
450
451
0
    if (fpOut == nullptr)
452
0
        fpOut = stdout;
453
454
0
    int nLines = 0;
455
456
0
    while (*papszStrList != nullptr)
457
0
    {
458
0
        if (VSIFPrintf(fpOut, "%s\n", *papszStrList) < 0)
459
0
            return nLines;
460
0
        ++nLines;
461
0
        ++papszStrList;
462
0
    }
463
464
0
    return nLines;
465
0
}
466
467
/**********************************************************************
468
 *                       CSLInsertStrings()
469
 **********************************************************************/
470
471
/** Copies the contents of a StringList inside another StringList
472
 * before the specified line.
473
 *
474
 * nInsertAtLineNo is a 0-based line index before which the new strings
475
 * should be inserted.  If this value is -1 or is larger than the actual
476
 * number of strings in the list then the strings are added at the end
477
 * of the source StringList.
478
 *
479
 * Returns the modified StringList.
480
 */
481
482
char **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo,
483
                        CSLConstList papszNewLines)
484
61.9k
{
485
61.9k
    if (papszNewLines == nullptr)
486
19
        return papszStrList;  // Nothing to do!
487
488
61.9k
    const int nToInsert = CSLCount(papszNewLines);
489
61.9k
    if (nToInsert == 0)
490
0
        return papszStrList;  // Nothing to do!
491
492
61.9k
    const int nSrcLines = CSLCount(papszStrList);
493
61.9k
    const int nDstLines = nSrcLines + nToInsert;
494
495
    // Allocate room for the new strings.
496
61.9k
    papszStrList = static_cast<char **>(
497
61.9k
        CPLRealloc(papszStrList, (nDstLines + 1) * sizeof(char *)));
498
499
    // Make sure the array is NULL-terminated.  It may not be if
500
    // papszStrList was NULL before Realloc().
501
61.9k
    papszStrList[nSrcLines] = nullptr;
502
503
    // Make some room in the original list at the specified location.
504
    // Note that we also have to move the NULL pointer at the end of
505
    // the source StringList.
506
61.9k
    if (nInsertAtLineNo == -1 || nInsertAtLineNo > nSrcLines)
507
59.6k
        nInsertAtLineNo = nSrcLines;
508
509
61.9k
    {
510
61.9k
        char **ppszSrc = papszStrList + nSrcLines;
511
61.9k
        char **ppszDst = papszStrList + nDstLines;
512
513
228k
        for (int i = nSrcLines; i >= nInsertAtLineNo; --i)
514
166k
        {
515
166k
            *ppszDst = *ppszSrc;
516
166k
            --ppszDst;
517
166k
            --ppszSrc;
518
166k
        }
519
61.9k
    }
520
521
    // Copy the strings to the list.
522
61.9k
    CSLConstList ppszSrc = papszNewLines;
523
61.9k
    char **ppszDst = papszStrList + nInsertAtLineNo;
524
525
274k
    for (; *ppszSrc != nullptr; ++ppszSrc, ++ppszDst)
526
212k
    {
527
212k
        *ppszDst = CPLStrdup(*ppszSrc);
528
212k
    }
529
530
61.9k
    return papszStrList;
531
61.9k
}
532
533
/**********************************************************************
534
 *                       CSLInsertString()
535
 **********************************************************************/
536
537
/** Insert a string at a given line number inside a StringList
538
 *
539
 * nInsertAtLineNo is a 0-based line index before which the new string
540
 * should be inserted.  If this value is -1 or is larger than the actual
541
 * number of strings in the list then the string is added at the end
542
 * of the source StringList.
543
 *
544
 * Returns the modified StringList.
545
 */
546
547
char **CSLInsertString(char **papszStrList, int nInsertAtLineNo,
548
                       const char *pszNewLine)
549
1.67k
{
550
1.67k
    char *apszList[2] = {const_cast<char *>(pszNewLine), nullptr};
551
552
1.67k
    return CSLInsertStrings(papszStrList, nInsertAtLineNo, apszList);
553
1.67k
}
554
555
/**********************************************************************
556
 *                       CSLRemoveStrings()
557
 **********************************************************************/
558
559
/** Remove strings inside a StringList
560
 *
561
 * nFirstLineToDelete is the 0-based line index of the first line to
562
 * remove. If this value is -1 or is larger than the actual
563
 * number of strings in list then the nNumToRemove last strings are
564
 * removed.
565
 *
566
 * If ppapszRetStrings != NULL then the deleted strings won't be
567
 * free'd, they will be stored in a new StringList and the pointer to
568
 * this new list will be returned in *ppapszRetStrings.
569
 *
570
 * Returns the modified StringList.
571
 */
572
573
char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete,
574
                        int nNumToRemove, char ***ppapszRetStrings)
575
1.12k
{
576
1.12k
    const int nSrcLines = CSLCount(papszStrList);
577
578
1.12k
    if (nNumToRemove < 1 || nSrcLines == 0)
579
0
        return papszStrList;  // Nothing to do!
580
581
    // If operation will result in an empty StringList, don't waste
582
    // time here.
583
1.12k
    const int nDstLines = nSrcLines - nNumToRemove;
584
1.12k
    if (nDstLines < 1)
585
277
    {
586
277
        CSLDestroy(papszStrList);
587
277
        return nullptr;
588
277
    }
589
590
    // Remove lines from the source StringList.
591
    // Either free() each line or store them to a new StringList depending on
592
    // the caller's choice.
593
844
    char **ppszDst = papszStrList + nFirstLineToDelete;
594
595
844
    if (ppapszRetStrings == nullptr)
596
844
    {
597
        // free() all the strings that will be removed.
598
1.68k
        for (int i = 0; i < nNumToRemove; ++i)
599
844
        {
600
844
            CPLFree(*ppszDst);
601
844
            *ppszDst = nullptr;
602
844
        }
603
844
    }
604
0
    else
605
0
    {
606
        // Store the strings to remove in a new StringList.
607
0
        *ppapszRetStrings =
608
0
            static_cast<char **>(CPLCalloc(nNumToRemove + 1, sizeof(char *)));
609
610
0
        for (int i = 0; i < nNumToRemove; ++i)
611
0
        {
612
0
            (*ppapszRetStrings)[i] = *ppszDst;
613
0
            *ppszDst = nullptr;
614
0
            ++ppszDst;
615
0
        }
616
0
    }
617
618
    // Shift down all the lines that follow the lines to remove.
619
844
    if (nFirstLineToDelete == -1 || nFirstLineToDelete > nSrcLines)
620
0
        nFirstLineToDelete = nDstLines;
621
622
844
    char **ppszSrc = papszStrList + nFirstLineToDelete + nNumToRemove;
623
844
    ppszDst = papszStrList + nFirstLineToDelete;
624
625
1.48k
    for (; *ppszSrc != nullptr; ++ppszSrc, ++ppszDst)
626
641
    {
627
641
        *ppszDst = *ppszSrc;
628
641
    }
629
    // Move the NULL pointer at the end of the StringList.
630
844
    *ppszDst = *ppszSrc;
631
632
    // At this point, we could realloc() papszStrList to a smaller size, but
633
    // since this array will likely grow again in further operations on the
634
    // StringList we'll leave it as it is.
635
844
    return papszStrList;
636
1.12k
}
637
638
/************************************************************************/
639
/*                           CSLFindString()                            */
640
/************************************************************************/
641
642
/**
643
 * Find a string within a string list (case insensitive).
644
 *
645
 * Returns the index of the entry in the string list that contains the
646
 * target string.  The string in the string list must be a full match for
647
 * the target, but the search is case insensitive.
648
 *
649
 * @param papszList the string list to be searched.
650
 * @param pszTarget the string to be searched for.
651
 *
652
 * @return the index of the string within the list or -1 on failure.
653
 */
654
655
int CSLFindString(CSLConstList papszList, const char *pszTarget)
656
657
10.9M
{
658
10.9M
    if (papszList == nullptr)
659
2.93M
        return -1;
660
661
36.2M
    for (int i = 0; papszList[i] != nullptr; ++i)
662
29.6M
    {
663
29.6M
        if (EQUAL(papszList[i], pszTarget))
664
1.38M
            return i;
665
29.6M
    }
666
667
6.61M
    return -1;
668
7.99M
}
669
670
/************************************************************************/
671
/*                     CSLFindStringCaseSensitive()                     */
672
/************************************************************************/
673
674
/**
675
 * Find a string within a string list(case sensitive)
676
 *
677
 * Returns the index of the entry in the string list that contains the
678
 * target string.  The string in the string list must be a full match for
679
 * the target.
680
 *
681
 * @param papszList the string list to be searched.
682
 * @param pszTarget the string to be searched for.
683
 *
684
 * @return the index of the string within the list or -1 on failure.
685
 *
686
 */
687
688
int CSLFindStringCaseSensitive(CSLConstList papszList, const char *pszTarget)
689
690
11.2k
{
691
11.2k
    if (papszList == nullptr)
692
5.10k
        return -1;
693
694
32.1k
    for (int i = 0; papszList[i] != nullptr; ++i)
695
26.0k
    {
696
26.0k
        if (strcmp(papszList[i], pszTarget) == 0)
697
20
            return i;
698
26.0k
    }
699
700
6.08k
    return -1;
701
6.10k
}
702
703
/************************************************************************/
704
/*                        CSLPartialFindString()                        */
705
/************************************************************************/
706
707
/**
708
 * Find a substring within a string list.
709
 *
710
 * Returns the index of the entry in the string list that contains the
711
 * target string as a substring.  The search is case sensitive (unlike
712
 * CSLFindString()).
713
 *
714
 * @param papszHaystack the string list to be searched.
715
 * @param pszNeedle the substring to be searched for.
716
 *
717
 * @return the index of the string within the list or -1 on failure.
718
 */
719
720
int CSLPartialFindString(CSLConstList papszHaystack, const char *pszNeedle)
721
133k
{
722
133k
    if (papszHaystack == nullptr || pszNeedle == nullptr)
723
131k
        return -1;
724
725
89.2k
    for (int i = 0; papszHaystack[i] != nullptr; ++i)
726
88.3k
    {
727
88.3k
        if (strstr(papszHaystack[i], pszNeedle))
728
1.12k
            return i;
729
88.3k
    }
730
731
903
    return -1;
732
2.02k
}
733
734
/**********************************************************************
735
 *                       CSLTokenizeString()
736
 **********************************************************************/
737
738
/** Tokenizes a string and returns a StringList with one string for
739
 * each token.
740
 */
741
char **CSLTokenizeString(const char *pszString)
742
21.0M
{
743
21.0M
    return CSLTokenizeString2(pszString, " ", CSLT_HONOURSTRINGS);
744
21.0M
}
745
746
/************************************************************************/
747
/*                      CSLTokenizeStringComplex()                      */
748
/************************************************************************/
749
750
/** Obsolete tokenizing api. Use CSLTokenizeString2() */
751
char **CSLTokenizeStringComplex(const char *pszString,
752
                                const char *pszDelimiters, int bHonourStrings,
753
                                int bAllowEmptyTokens)
754
31.0M
{
755
31.0M
    int nFlags = 0;
756
757
31.0M
    if (bHonourStrings)
758
24.7M
        nFlags |= CSLT_HONOURSTRINGS;
759
31.0M
    if (bAllowEmptyTokens)
760
838k
        nFlags |= CSLT_ALLOWEMPTYTOKENS;
761
762
31.0M
    return CSLTokenizeString2(pszString, pszDelimiters, nFlags);
763
31.0M
}
764
765
/************************************************************************/
766
/*                         CSLTokenizeString2()                         */
767
/************************************************************************/
768
769
/**
770
 * Tokenize a string.
771
 *
772
 * This function will split a string into tokens based on specified
773
 * delimiter(s) with a variety of options.  The returned result is a
774
 * string list that should be freed with CSLDestroy() when no longer
775
 * needed.
776
 *
777
 * The available parsing options are:
778
 *
779
 * - CSLT_ALLOWEMPTYTOKENS: allow the return of empty tokens when two
780
 * delimiters in a row occur with no other text between them.  If not set,
781
 * empty tokens will be discarded;
782
 * - CSLT_STRIPLEADSPACES: strip leading space characters from the token (as
783
 * reported by isspace());
784
 * - CSLT_STRIPENDSPACES: strip ending space characters from the token (as
785
 * reported by isspace());
786
 * - CSLT_HONOURSTRINGS: double quotes can be used to hold values that should
787
 * not be broken into multiple tokens;
788
 * - CSLT_HONOURSINGLEQUOTES: single quotes can be used to hold values that should
789
 * not be broken into multiple tokens;
790
 * - CSLT_PRESERVEQUOTES: string quotes are carried into the tokens when this
791
 * is set, otherwise they are removed;
792
 * - CSLT_PRESERVEESCAPES: if set backslash escapes (for backslash itself,
793
 * and for literal single/double quotes) will be preserved in the tokens, otherwise
794
 * the backslashes will be removed in processing.
795
 *
796
 * \b Example:
797
 *
798
 * Parse a string into tokens based on various white space (space, newline,
799
 * tab) and then print out results and cleanup.  Quotes may be used to hold
800
 * white space in tokens.
801
802
\code
803
    char **papszTokens =
804
        CSLTokenizeString2( pszCommand, " \t\n",
805
                            CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS );
806
807
    for( int i = 0; papszTokens != NULL && papszTokens[i] != NULL; ++i )
808
        printf( "arg %d: '%s'", papszTokens[i] );  // ok
809
810
    CSLDestroy( papszTokens );
811
\endcode
812
813
 * @param pszString the string to be split into tokens.
814
 * @param pszDelimiters one or more characters to be used as token delimiters.
815
 * @param nCSLTFlags an ORing of one or more of the CSLT_ flag values.
816
 *
817
 * @return a string list of tokens owned by the caller.
818
 */
819
820
char **CSLTokenizeString2(const char *pszString, const char *pszDelimiters,
821
                          int nCSLTFlags)
822
77.4M
{
823
77.4M
    if (pszString == nullptr)
824
423k
        return static_cast<char **>(CPLCalloc(sizeof(char *), 1));
825
826
77.0M
    return cpl::tokenize_string(pszString, pszDelimiters, nCSLTFlags)
827
77.0M
        .StealList();
828
77.4M
}
829
830
namespace cpl
831
{
832
CPLStringList tokenize_string(std::string_view str, std::string_view delimiters,
833
                              int nCSLTFlags)
834
77.0M
{
835
77.0M
    CPLStringList oRetList;
836
77.0M
    const bool bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS) != 0;
837
77.0M
    const bool bHonourStringsSingleQuotes =
838
77.0M
        (nCSLTFlags & CSLT_HONOURSINGLEQUOTES) != 0;
839
77.0M
    const bool bAllowEmptyTokens = (nCSLTFlags & CSLT_ALLOWEMPTYTOKENS) != 0;
840
77.0M
    const bool bStripLeadSpaces = (nCSLTFlags & CSLT_STRIPLEADSPACES) != 0;
841
77.0M
    const bool bStripEndSpaces = (nCSLTFlags & CSLT_STRIPENDSPACES) != 0;
842
843
77.0M
    size_t pos = 0;
844
77.0M
    std::string token;
845
281M
    while (pos < str.size())
846
204M
    {
847
204M
        token.clear();
848
204M
        bool bInString = false;
849
204M
        bool bInStringSingleQuote = false;
850
851
        // Try to find the next delimiter, marking end of token.
852
3.60G
        while (pos < str.size())
853
3.54G
        {
854
            // End if this is a delimiter skip it and break.
855
3.54G
            if (!bInString && !bInStringSingleQuote &&
856
3.39G
                delimiters.find(str[pos]) != std::string_view::npos)
857
146M
            {
858
146M
                pos++;
859
146M
                break;
860
146M
            }
861
862
            // If this is a quote, and we are honouring constant
863
            // strings, then process the constant strings, with out delim
864
            // but don't copy over the quotes.
865
3.40G
            if (bHonourStrings && !bInStringSingleQuote && str[pos] == '"')
866
12.8M
            {
867
12.8M
                if (nCSLTFlags & CSLT_PRESERVEQUOTES)
868
2.68M
                {
869
2.68M
                    token.push_back(str[pos]);
870
2.68M
                }
871
872
12.8M
                bInString = !bInString;
873
12.8M
                pos++;
874
12.8M
                continue;
875
12.8M
            }
876
3.39G
            else if (bHonourStringsSingleQuotes && !bHonourStrings &&
877
0
                     str[pos] == '\'')
878
0
            {
879
0
                if (nCSLTFlags & CSLT_PRESERVEQUOTES)
880
0
                {
881
0
                    token.push_back(str[pos]);
882
0
                }
883
884
0
                bInStringSingleQuote = !bInStringSingleQuote;
885
0
                pos++;
886
0
                continue;
887
0
            }
888
889
            /*
890
             * Within string constants we allow for escaped quotes, but in
891
             * processing them we will unescape the quotes and \\ sequence
892
             * reduces to \
893
             */
894
3.39G
            if (bInString && str[pos] == '\\')
895
568k
            {
896
568k
                if (pos + 1 < str.size() &&
897
559k
                    (str[pos + 1] == '"' || str[pos + 1] == '\\'))
898
315k
                {
899
315k
                    if (nCSLTFlags & CSLT_PRESERVEESCAPES)
900
72
                    {
901
72
                        token.push_back(str[pos]);
902
72
                    }
903
904
315k
                    ++pos;
905
315k
                }
906
568k
            }
907
3.38G
            else if (bInStringSingleQuote && str[pos] == '\\')
908
0
            {
909
0
                if (pos + 1 < str.size() &&
910
0
                    (str[pos + 1] == '\'' || str[pos + 1] == '\\'))
911
0
                {
912
0
                    if (nCSLTFlags & CSLT_PRESERVEESCAPES)
913
0
                    {
914
0
                        token.push_back(str[pos]);
915
0
                    }
916
917
0
                    ++pos;
918
0
                }
919
0
            }
920
921
3.39G
            token.push_back(str[pos]);
922
3.39G
            pos++;
923
3.39G
        }
924
925
        // Add the token.
926
204M
        std::string_view token_view(token);
927
204M
        if (bStripLeadSpaces)
928
5.82M
        {
929
5.82M
            token_view = ltrim(token_view);
930
5.82M
        }
931
204M
        if (bStripEndSpaces)
932
5.82M
        {
933
5.82M
            token_view = rtrim(token_view);
934
5.82M
        }
935
936
204M
        if (!token_view.empty() || bAllowEmptyTokens)
937
149M
            oRetList.AddString(token_view);
938
204M
    }
939
940
    /*
941
     * If the last token was empty, then we need to capture
942
     * it now, as the loop would skip it.
943
     */
944
77.0M
    if (!str.empty() && pos == str.size() && bAllowEmptyTokens &&
945
1.59M
        oRetList.Count() > 0 &&
946
1.59M
        delimiters.find(str[pos - 1]) != std::string_view::npos)
947
390k
    {
948
390k
        oRetList.AddString("");
949
390k
    }
950
951
77.0M
    if (oRetList.List() == nullptr)
952
16.7M
    {
953
        // Prefer to return empty lists as a pointer to
954
        // a null pointer since some client code might depend on this.
955
16.7M
        oRetList.Assign(static_cast<char **>(CPLCalloc(sizeof(char *), 1)));
956
16.7M
    }
957
958
77.0M
    return CPLStringList(oRetList.StealList());
959
77.0M
}
960
961
}  // namespace cpl
962
963
/**********************************************************************
964
 *                       CPLSPrintf()
965
 *
966
 * NOTE: This function should move to cpl_conv.cpp.
967
 **********************************************************************/
968
969
// For now, assume that a 8000 chars buffer will be enough.
970
constexpr int CPLSPrintf_BUF_SIZE = 8000;
971
constexpr int CPLSPrintf_BUF_Count = 10;
972
973
/** CPLSPrintf() that works with 10 static buffer.
974
 *
975
 * It returns a ref. to a static buffer that should not be freed and
976
 * is valid only until the next call to CPLSPrintf().
977
 */
978
979
const char *CPLSPrintf(CPL_FORMAT_STRING(const char *fmt), ...)
980
407M
{
981
407M
    va_list args;
982
983
    /* -------------------------------------------------------------------- */
984
    /*      Get the thread local buffer ring data.                          */
985
    /* -------------------------------------------------------------------- */
986
407M
    char *pachBufRingInfo = static_cast<char *>(CPLGetTLS(CTLS_CPLSPRINTF));
987
988
407M
    if (pachBufRingInfo == nullptr)
989
195
    {
990
195
        pachBufRingInfo = static_cast<char *>(CPLCalloc(
991
195
            1, sizeof(int) + CPLSPrintf_BUF_Count * CPLSPrintf_BUF_SIZE));
992
195
        CPLSetTLS(CTLS_CPLSPRINTF, pachBufRingInfo, TRUE);
993
195
    }
994
995
    /* -------------------------------------------------------------------- */
996
    /*      Work out which string in the "ring" we want to use this         */
997
    /*      time.                                                           */
998
    /* -------------------------------------------------------------------- */
999
407M
    int *pnBufIndex = reinterpret_cast<int *>(pachBufRingInfo);
1000
407M
    const size_t nOffset = sizeof(int) + *pnBufIndex * CPLSPrintf_BUF_SIZE;
1001
407M
    char *pachBuffer = pachBufRingInfo + nOffset;
1002
1003
407M
    *pnBufIndex = (*pnBufIndex + 1) % CPLSPrintf_BUF_Count;
1004
1005
    /* -------------------------------------------------------------------- */
1006
    /*      Format the result.                                              */
1007
    /* -------------------------------------------------------------------- */
1008
1009
407M
    va_start(args, fmt);
1010
1011
407M
    const int ret =
1012
407M
        CPLvsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE - 1, fmt, args);
1013
407M
    if (ret < 0 || ret >= CPLSPrintf_BUF_SIZE - 1)
1014
1.72k
    {
1015
1.72k
        CPLError(CE_Failure, CPLE_AppDefined,
1016
1.72k
                 "CPLSPrintf() called with too "
1017
1.72k
                 "big string. Output will be truncated !");
1018
1.72k
    }
1019
1020
407M
    va_end(args);
1021
1022
407M
    return pachBuffer;
1023
407M
}
1024
1025
/**********************************************************************
1026
 *                       CSLAppendPrintf()
1027
 **********************************************************************/
1028
1029
/** Use CPLSPrintf() to append a new line at the end of a StringList.
1030
 * Returns the modified StringList.
1031
 */
1032
char **CSLAppendPrintf(char **papszStrList, CPL_FORMAT_STRING(const char *fmt),
1033
                       ...)
1034
12.2k
{
1035
12.2k
    va_list args;
1036
1037
12.2k
    va_start(args, fmt);
1038
12.2k
    CPLString osWork;
1039
12.2k
    osWork.vPrintf(fmt, args);
1040
12.2k
    va_end(args);
1041
1042
12.2k
    return CSLAddString(papszStrList, osWork);
1043
12.2k
}
1044
1045
/************************************************************************/
1046
/*                            CPLVASPrintf()                            */
1047
/************************************************************************/
1048
1049
/** This is intended to serve as an easy to use C callable vasprintf()
1050
 * alternative.  Used in the GeoJSON library for instance */
1051
int CPLVASPrintf(char **buf, CPL_FORMAT_STRING(const char *fmt), va_list ap)
1052
1053
0
{
1054
0
    CPLString osWork;
1055
1056
0
    osWork.vPrintf(fmt, ap);
1057
1058
0
    if (buf)
1059
0
        *buf = CPLStrdup(osWork.c_str());
1060
1061
0
    return static_cast<int>(osWork.size());
1062
0
}
1063
1064
/************************************************************************/
1065
/*                 CPLvsnprintf_get_end_of_formatting()                 */
1066
/************************************************************************/
1067
1068
static const char *CPLvsnprintf_get_end_of_formatting(const char *fmt)
1069
1.06G
{
1070
1.06G
    char ch = '\0';
1071
    // Flag.
1072
1.42G
    for (; (ch = *fmt) != '\0'; ++fmt)
1073
1.42G
    {
1074
1.42G
        if (ch == '\'')
1075
0
            continue;  // Bad idea as this is locale specific.
1076
1.42G
        if (ch == '-' || ch == '+' || ch == ' ' || ch == '#' || ch == '0')
1077
363M
            continue;
1078
1.06G
        break;
1079
1.42G
    }
1080
1081
    // Field width.
1082
1.42G
    for (; (ch = *fmt) != '\0'; ++fmt)
1083
1.42G
    {
1084
1.42G
        if (ch == '$')
1085
0
            return nullptr;  // Do not support this.
1086
1.42G
        if (*fmt >= '0' && *fmt <= '9')
1087
363M
            continue;
1088
1.06G
        break;
1089
1.42G
    }
1090
1091
    // Precision.
1092
1.06G
    if (ch == '.')
1093
11.8M
    {
1094
11.8M
        ++fmt;
1095
30.3M
        for (; (ch = *fmt) != '\0'; ++fmt)
1096
30.3M
        {
1097
30.3M
            if (ch == '$')
1098
0
                return nullptr;  // Do not support this.
1099
30.3M
            if (*fmt >= '0' && *fmt <= '9')
1100
18.4M
                continue;
1101
11.8M
            break;
1102
30.3M
        }
1103
11.8M
    }
1104
1105
    // Length modifier.
1106
1.12G
    for (; (ch = *fmt) != '\0'; ++fmt)
1107
1.12G
    {
1108
1.12G
        if (ch == 'h' || ch == 'l' || ch == 'j' || ch == 'z' || ch == 't' ||
1109
1.06G
            ch == 'L')
1110
67.4M
            continue;
1111
1.06G
        else if (ch == 'I' && fmt[1] == '6' && fmt[2] == '4')
1112
0
            fmt += 2;
1113
1.06G
        else
1114
1.06G
            return fmt;
1115
1.12G
    }
1116
1117
0
    return nullptr;
1118
1.06G
}
1119
1120
/************************************************************************/
1121
/*                            CPLvsnprintf()                            */
1122
/************************************************************************/
1123
1124
#define call_native_snprintf(type)                                             \
1125
575M
    local_ret = snprintf(str + offset_out, size - offset_out, localfmt,        \
1126
575M
                         va_arg(wrk_args, type))
1127
1128
/** vsnprintf() wrapper that is not sensitive to LC_NUMERIC settings.
1129
 *
1130
 * This function has the same contract as standard vsnprintf(), except that
1131
 * formatting of floating-point numbers will use decimal point, whatever the
1132
 * current locale is set.
1133
 *
1134
 * @param str output buffer
1135
 * @param size size of the output buffer (including space for terminating nul)
1136
 * @param fmt formatting string
1137
 * @param args arguments
1138
 * @return the number of characters (excluding terminating nul) that would be
1139
 * written if size is big enough. Or potentially -1 with Microsoft C runtime
1140
 * for Visual Studio < 2015.
1141
 */
1142
int CPLvsnprintf(char *str, size_t size, CPL_FORMAT_STRING(const char *fmt),
1143
                 va_list args)
1144
642M
{
1145
642M
    if (size == 0)
1146
0
        return vsnprintf(str, size, fmt, args);
1147
1148
642M
    va_list wrk_args;
1149
1150
642M
#ifdef va_copy
1151
642M
    va_copy(wrk_args, args);
1152
#else
1153
    wrk_args = args;
1154
#endif
1155
1156
642M
    const char *fmt_ori = fmt;
1157
642M
    size_t offset_out = 0;
1158
642M
    char ch = '\0';
1159
642M
    bool bFormatUnknown = false;
1160
1161
7.39G
    for (; (ch = *fmt) != '\0'; ++fmt)
1162
6.75G
    {
1163
6.75G
        if (ch == '%')
1164
1.06G
        {
1165
1.06G
            if (strncmp(fmt, "%.*f", 4) == 0)
1166
28.1k
            {
1167
28.1k
                const int precision = va_arg(wrk_args, int);
1168
28.1k
                const double val = va_arg(wrk_args, double);
1169
28.1k
                const int local_ret =
1170
28.1k
                    snprintf(str + offset_out, size - offset_out, "%.*f",
1171
28.1k
                             precision, val);
1172
                // MSVC vsnprintf() returns -1.
1173
28.1k
                if (local_ret < 0 || offset_out + local_ret >= size)
1174
0
                    break;
1175
820k
                for (int j = 0; j < local_ret; ++j)
1176
792k
                {
1177
792k
                    if (str[offset_out + j] == ',')
1178
0
                    {
1179
0
                        str[offset_out + j] = '.';
1180
0
                        break;
1181
0
                    }
1182
792k
                }
1183
28.1k
                offset_out += local_ret;
1184
28.1k
                fmt += strlen("%.*f") - 1;
1185
28.1k
                continue;
1186
28.1k
            }
1187
1188
1.06G
            const char *ptrend = CPLvsnprintf_get_end_of_formatting(fmt + 1);
1189
1.06G
            if (ptrend == nullptr || ptrend - fmt >= 20)
1190
0
            {
1191
0
                bFormatUnknown = true;
1192
0
                break;
1193
0
            }
1194
1.06G
            char end = *ptrend;
1195
1.06G
            char end_m1 = ptrend[-1];
1196
1197
1.06G
            char localfmt[22] = {};
1198
1.06G
            memcpy(localfmt, fmt, ptrend - fmt + 1);
1199
1.06G
            localfmt[ptrend - fmt + 1] = '\0';
1200
1201
1.06G
            int local_ret = 0;
1202
1.06G
            if (end == '%')
1203
344M
            {
1204
344M
                if (offset_out == size - 1)
1205
0
                    break;
1206
344M
                local_ret = 1;
1207
344M
                str[offset_out] = '%';
1208
344M
            }
1209
717M
            else if (end == 'd' || end == 'i' || end == 'c')
1210
118M
            {
1211
118M
                if (end_m1 == 'h')
1212
0
                    call_native_snprintf(int);
1213
118M
                else if (end_m1 == 'l' && ptrend[-2] != 'l')
1214
362k
                    call_native_snprintf(long);
1215
118M
                else if (end_m1 == 'l' && ptrend[-2] == 'l')
1216
3.77M
                    call_native_snprintf(GIntBig);
1217
114M
                else if (end_m1 == '4' && ptrend[-2] == '6' &&
1218
0
                         ptrend[-3] == 'I')
1219
                    // Microsoft I64 modifier.
1220
0
                    call_native_snprintf(GIntBig);
1221
114M
                else if (end_m1 == 'z')
1222
0
                    call_native_snprintf(size_t);
1223
114M
                else if ((end_m1 >= 'a' && end_m1 <= 'z') ||
1224
114M
                         (end_m1 >= 'A' && end_m1 <= 'Z'))
1225
0
                {
1226
0
                    bFormatUnknown = true;
1227
0
                    break;
1228
0
                }
1229
114M
                else
1230
114M
                    call_native_snprintf(int);
1231
118M
            }
1232
599M
            else if (end == 'o' || end == 'u' || end == 'x' || end == 'X')
1233
439M
            {
1234
439M
                if (end_m1 == 'h')
1235
31
                    call_native_snprintf(unsigned int);
1236
439M
                else if (end_m1 == 'l' && ptrend[-2] != 'l')
1237
12.0M
                    call_native_snprintf(unsigned long);
1238
427M
                else if (end_m1 == 'l' && ptrend[-2] == 'l')
1239
23.7M
                    call_native_snprintf(GUIntBig);
1240
403M
                else if (end_m1 == '4' && ptrend[-2] == '6' &&
1241
0
                         ptrend[-3] == 'I')
1242
                    // Microsoft I64 modifier.
1243
0
                    call_native_snprintf(GUIntBig);
1244
403M
                else if (end_m1 == 'z')
1245
0
                    call_native_snprintf(size_t);
1246
403M
                else if ((end_m1 >= 'a' && end_m1 <= 'z') ||
1247
403M
                         (end_m1 >= 'A' && end_m1 <= 'Z'))
1248
0
                {
1249
0
                    bFormatUnknown = true;
1250
0
                    break;
1251
0
                }
1252
403M
                else
1253
403M
                    call_native_snprintf(unsigned int);
1254
439M
            }
1255
159M
            else if (end == 'e' || end == 'E' || end == 'f' || end == 'F' ||
1256
144M
                     end == 'g' || end == 'G' || end == 'a' || end == 'A')
1257
17.5M
            {
1258
17.5M
                if (end_m1 == 'L')
1259
0
                    call_native_snprintf(long double);
1260
17.5M
                else
1261
17.5M
                    call_native_snprintf(double);
1262
                // MSVC vsnprintf() returns -1.
1263
17.5M
                if (local_ret < 0 || offset_out + local_ret >= size)
1264
352
                    break;
1265
202M
                for (int j = 0; j < local_ret; ++j)
1266
184M
                {
1267
184M
                    if (str[offset_out + j] == ',')
1268
0
                    {
1269
0
                        str[offset_out + j] = '.';
1270
0
                        break;
1271
0
                    }
1272
184M
                }
1273
17.4M
            }
1274
142M
            else if (end == 's')
1275
141M
            {
1276
141M
                const char *pszPtr = va_arg(wrk_args, const char *);
1277
141M
                CPLAssert(pszPtr);
1278
141M
                local_ret = snprintf(str + offset_out, size - offset_out,
1279
141M
                                     localfmt, pszPtr);
1280
141M
            }
1281
941k
            else if (end == 'p')
1282
359k
            {
1283
359k
                call_native_snprintf(void *);
1284
359k
            }
1285
582k
            else
1286
582k
            {
1287
582k
                bFormatUnknown = true;
1288
582k
                break;
1289
582k
            }
1290
            // MSVC vsnprintf() returns -1.
1291
1.06G
            if (local_ret < 0 || offset_out + local_ret >= size)
1292
2.35M
                break;
1293
1.05G
            offset_out += local_ret;
1294
1.05G
            fmt = ptrend;
1295
1.05G
        }
1296
5.69G
        else
1297
5.69G
        {
1298
5.69G
            if (offset_out == size - 1)
1299
35.5k
                break;
1300
5.69G
            str[offset_out++] = *fmt;
1301
5.69G
        }
1302
6.75G
    }
1303
642M
    if (ch == '\0' && offset_out < size)
1304
639M
        str[offset_out] = '\0';
1305
2.96M
    else
1306
2.96M
    {
1307
2.96M
        if (bFormatUnknown)
1308
582k
        {
1309
582k
            CPLDebug("CPL",
1310
582k
                     "CPLvsnprintf() called with unsupported "
1311
582k
                     "formatting string: %s",
1312
582k
                     fmt_ori);
1313
582k
        }
1314
2.96M
#ifdef va_copy
1315
2.96M
        va_end(wrk_args);
1316
2.96M
        va_copy(wrk_args, args);
1317
#else
1318
        wrk_args = args;
1319
#endif
1320
2.96M
#if defined(HAVE_VSNPRINTF)
1321
2.96M
        offset_out = vsnprintf(str, size, fmt_ori, wrk_args);
1322
#else
1323
        offset_out = vsprintf(str, fmt_ori, wrk_args);
1324
#endif
1325
2.96M
    }
1326
1327
642M
#ifdef va_copy
1328
642M
    va_end(wrk_args);
1329
642M
#endif
1330
1331
642M
    return static_cast<int>(offset_out);
1332
642M
}
1333
1334
/************************************************************************/
1335
/*                            CPLsnprintf()                             */
1336
/************************************************************************/
1337
1338
#if !defined(ALIAS_CPLSNPRINTF_AS_SNPRINTF)
1339
1340
#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 2
1341
#pragma clang diagnostic push
1342
#pragma clang diagnostic ignored "-Wunknown-pragmas"
1343
#pragma clang diagnostic ignored "-Wdocumentation"
1344
#endif
1345
1346
/** snprintf() wrapper that is not sensitive to LC_NUMERIC settings.
1347
 *
1348
 * This function has the same contract as standard snprintf(), except that
1349
 * formatting of floating-point numbers will use decimal point, whatever the
1350
 * current locale is set.
1351
 *
1352
 * @param str output buffer
1353
 * @param size size of the output buffer (including space for terminating nul)
1354
 * @param fmt formatting string
1355
 * @param ... arguments
1356
 * @return the number of characters (excluding terminating nul) that would be
1357
 * written if size is big enough. Or potentially -1 with Microsoft C runtime
1358
 * for Visual Studio < 2015.
1359
 */
1360
1361
int CPLsnprintf(char *str, size_t size, CPL_FORMAT_STRING(const char *fmt), ...)
1362
8.39M
{
1363
8.39M
    va_list args;
1364
1365
8.39M
    va_start(args, fmt);
1366
8.39M
    const int ret = CPLvsnprintf(str, size, fmt, args);
1367
8.39M
    va_end(args);
1368
8.39M
    return ret;
1369
8.39M
}
1370
1371
#endif  //  !defined(ALIAS_CPLSNPRINTF_AS_SNPRINTF)
1372
1373
/************************************************************************/
1374
/*                             CPLsprintf()                             */
1375
/************************************************************************/
1376
1377
/** sprintf() wrapper that is not sensitive to LC_NUMERIC settings.
1378
  *
1379
  * This function has the same contract as standard sprintf(), except that
1380
  * formatting of floating-point numbers will use decimal point, whatever the
1381
  * current locale is set.
1382
  *
1383
  * @param str output buffer (must be large enough to hold the result)
1384
  * @param fmt formatting string
1385
  * @param ... arguments
1386
  * @return the number of characters (excluding terminating nul) written in
1387
` * output buffer.
1388
  */
1389
int CPLsprintf(char *str, CPL_FORMAT_STRING(const char *fmt), ...)
1390
29.4k
{
1391
29.4k
    va_list args;
1392
1393
29.4k
    va_start(args, fmt);
1394
29.4k
    const int ret = CPLvsnprintf(str, INT_MAX, fmt, args);
1395
29.4k
    va_end(args);
1396
29.4k
    return ret;
1397
29.4k
}
1398
1399
/************************************************************************/
1400
/*                             CPLprintf()                              */
1401
/************************************************************************/
1402
1403
/** printf() wrapper that is not sensitive to LC_NUMERIC settings.
1404
 *
1405
 * This function has the same contract as standard printf(), except that
1406
 * formatting of floating-point numbers will use decimal point, whatever the
1407
 * current locale is set.
1408
 *
1409
 * @param fmt formatting string
1410
 * @param ... arguments
1411
 * @return the number of characters (excluding terminating nul) written in
1412
 * output buffer.
1413
 */
1414
int CPLprintf(CPL_FORMAT_STRING(const char *fmt), ...)
1415
0
{
1416
0
    va_list wrk_args, args;
1417
1418
0
    va_start(args, fmt);
1419
1420
0
#ifdef va_copy
1421
0
    va_copy(wrk_args, args);
1422
#else
1423
    wrk_args = args;
1424
#endif
1425
1426
0
    char szBuffer[4096] = {};
1427
    // Quiet coverity by staring off nul terminated.
1428
0
    int ret = CPLvsnprintf(szBuffer, sizeof(szBuffer), fmt, wrk_args);
1429
1430
0
#ifdef va_copy
1431
0
    va_end(wrk_args);
1432
0
#endif
1433
1434
0
    if (ret < int(sizeof(szBuffer)) - 1)
1435
0
        ret = printf("%s", szBuffer); /*ok*/
1436
0
    else
1437
0
    {
1438
0
#ifdef va_copy
1439
0
        va_copy(wrk_args, args);
1440
#else
1441
        wrk_args = args;
1442
#endif
1443
1444
0
        ret = vfprintf(stdout, fmt, wrk_args);
1445
1446
0
#ifdef va_copy
1447
0
        va_end(wrk_args);
1448
0
#endif
1449
0
    }
1450
1451
0
    va_end(args);
1452
1453
0
    return ret;
1454
0
}
1455
1456
/************************************************************************/
1457
/*                             CPLsscanf()                              */
1458
/************************************************************************/
1459
1460
/** \brief sscanf() wrapper that is not sensitive to LC_NUMERIC settings.
1461
 *
1462
 * This function has the same contract as standard sscanf(), except that
1463
 * formatting of floating-point numbers will use decimal point, whatever the
1464
 * current locale is set.
1465
 *
1466
 * CAUTION: only works with a very limited number of formatting strings,
1467
 * consisting only of "%lf" and regular characters.
1468
 *
1469
 * @param str input string
1470
 * @param fmt formatting string
1471
 * @param ... arguments
1472
 * @return the number of matched patterns;
1473
 */
1474
#ifdef DOXYGEN_XML
1475
int CPLsscanf(const char *str, const char *fmt, ...)
1476
#else
1477
int CPLsscanf(const char *str, CPL_SCANF_FORMAT_STRING(const char *fmt), ...)
1478
#endif
1479
676k
{
1480
676k
    bool error = false;
1481
676k
    int ret = 0;
1482
676k
    const char *fmt_ori = fmt;
1483
676k
    va_list args;
1484
1485
676k
    va_start(args, fmt);
1486
1.83M
    for (; *fmt != '\0' && *str != '\0'; ++fmt)
1487
1.67M
    {
1488
1.67M
        if (*fmt == '%')
1489
1.16M
        {
1490
1.16M
            if (fmt[1] == 'l' && fmt[2] == 'f')
1491
1.16M
            {
1492
1.16M
                fmt += 2;
1493
1.16M
                char *end;
1494
1.16M
                *(va_arg(args, double *)) = CPLStrtod(str, &end);
1495
1.16M
                if (end > str)
1496
655k
                {
1497
655k
                    ++ret;
1498
655k
                    str = end;
1499
655k
                }
1500
512k
                else
1501
512k
                    break;
1502
1.16M
            }
1503
0
            else
1504
0
            {
1505
0
                error = true;
1506
0
                break;
1507
0
            }
1508
1.16M
        }
1509
505k
        else if (isspace(static_cast<unsigned char>(*fmt)))
1510
501k
        {
1511
743k
            while (*str != '\0' && isspace(static_cast<unsigned char>(*str)))
1512
241k
                ++str;
1513
501k
        }
1514
3.85k
        else if (*str != *fmt)
1515
77
            break;
1516
3.78k
        else
1517
3.78k
            ++str;
1518
1.67M
    }
1519
676k
    va_end(args);
1520
1521
676k
    if (error)
1522
0
    {
1523
0
        CPLError(CE_Failure, CPLE_NotSupported,
1524
0
                 "Format %s not supported by CPLsscanf()", fmt_ori);
1525
0
    }
1526
1527
676k
    return ret;
1528
676k
}
1529
1530
#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 2
1531
#pragma clang diagnostic pop
1532
#endif
1533
1534
/************************************************************************/
1535
/*                            CPLTestBool()                             */
1536
/************************************************************************/
1537
1538
/**
1539
 * Test what boolean value contained in the string.
1540
 *
1541
 * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned false.
1542
 * Otherwise, true will be returned.
1543
 *
1544
 * @param pszValue the string should be tested.
1545
 *
1546
 * @return true or false.
1547
 */
1548
1549
bool CPLTestBool(const char *pszValue)
1550
108M
{
1551
108M
    return !(EQUAL(pszValue, "NO") || EQUAL(pszValue, "FALSE") ||
1552
8.99M
             EQUAL(pszValue, "OFF") || EQUAL(pszValue, "0"));
1553
108M
}
1554
1555
/************************************************************************/
1556
/*                           CSLTestBoolean()                           */
1557
/************************************************************************/
1558
1559
/**
1560
 * Test what boolean value contained in the string.
1561
 *
1562
 * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE.
1563
 * Otherwise, TRUE will be returned.
1564
 *
1565
 * Deprecated.  Removed in GDAL 3.x.
1566
 *
1567
 * Use CPLTestBoolean() for C and CPLTestBool() for C++.
1568
 *
1569
 * @param pszValue the string should be tested.
1570
 *
1571
 * @return TRUE or FALSE.
1572
 */
1573
1574
int CSLTestBoolean(const char *pszValue)
1575
46
{
1576
46
    return CPLTestBool(pszValue) ? TRUE : FALSE;
1577
46
}
1578
1579
/************************************************************************/
1580
/*                           CPLTestBoolean()                           */
1581
/************************************************************************/
1582
1583
/**
1584
 * Test what boolean value contained in the string.
1585
 *
1586
 * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE.
1587
 * Otherwise, TRUE will be returned.
1588
 *
1589
 * Use this only in C code.  In C++, prefer CPLTestBool().
1590
 *
1591
 * @param pszValue the string should be tested.
1592
 *
1593
 * @return TRUE or FALSE.
1594
 */
1595
1596
int CPLTestBoolean(const char *pszValue)
1597
849
{
1598
849
    return CPLTestBool(pszValue) ? TRUE : FALSE;
1599
849
}
1600
1601
/**********************************************************************
1602
 *                       CPLFetchBool()
1603
 **********************************************************************/
1604
1605
/** Check for boolean key value.
1606
 *
1607
 * In a StringList of "Name=Value" pairs, look to see if there is a key
1608
 * with the given name, and if it can be interpreted as being TRUE.  If
1609
 * the key appears without any "=Value" portion it will be considered true.
1610
 * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
1611
 * if the key appears in the list it will be considered TRUE.  If the key
1612
 * doesn't appear at all, the indicated default value will be returned.
1613
 *
1614
 * @param papszStrList the string list to search.
1615
 * @param pszKey the key value to look for (case insensitive).
1616
 * @param bDefault the value to return if the key isn't found at all.
1617
 *
1618
 * @return true or false
1619
 */
1620
1621
bool CPLFetchBool(CSLConstList papszStrList, const char *pszKey, bool bDefault)
1622
1623
6.11M
{
1624
6.11M
    if (CSLFindString(papszStrList, pszKey) != -1)
1625
62
        return true;
1626
1627
6.11M
    const char *const pszValue = CSLFetchNameValue(papszStrList, pszKey);
1628
6.11M
    if (pszValue == nullptr)
1629
6.02M
        return bDefault;
1630
1631
83.6k
    return CPLTestBool(pszValue);
1632
6.11M
}
1633
1634
/**********************************************************************
1635
 *                       CSLFetchBoolean()
1636
 **********************************************************************/
1637
1638
/** DEPRECATED.  Check for boolean key value.
1639
 *
1640
 * In a StringList of "Name=Value" pairs, look to see if there is a key
1641
 * with the given name, and if it can be interpreted as being TRUE.  If
1642
 * the key appears without any "=Value" portion it will be considered true.
1643
 * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
1644
 * if the key appears in the list it will be considered TRUE.  If the key
1645
 * doesn't appear at all, the indicated default value will be returned.
1646
 *
1647
 * @param papszStrList the string list to search.
1648
 * @param pszKey the key value to look for (case insensitive).
1649
 * @param bDefault the value to return if the key isn't found at all.
1650
 *
1651
 * @return TRUE or FALSE
1652
 */
1653
1654
int CSLFetchBoolean(CSLConstList papszStrList, const char *pszKey, int bDefault)
1655
1656
10.1k
{
1657
10.1k
    return CPLFetchBool(papszStrList, pszKey, CPL_TO_BOOL(bDefault));
1658
10.1k
}
1659
1660
/************************************************************************/
1661
/*                     CSLFetchNameValueDefaulted()                     */
1662
/************************************************************************/
1663
1664
/** Same as CSLFetchNameValue() but return pszDefault in case of no match */
1665
const char *CSLFetchNameValueDef(CSLConstList papszStrList, const char *pszName,
1666
                                 const char *pszDefault)
1667
1668
10.2M
{
1669
10.2M
    const char *pszResult = CSLFetchNameValue(papszStrList, pszName);
1670
10.2M
    if (pszResult != nullptr)
1671
572k
        return pszResult;
1672
1673
9.70M
    return pszDefault;
1674
10.2M
}
1675
1676
/**********************************************************************
1677
 *                       CSLFetchNameValue()
1678
 **********************************************************************/
1679
1680
/** In a StringList of "Name=Value" pairs, look for the
1681
 * first value associated with the specified name.  The search is not
1682
 * case sensitive.
1683
 * ("Name:Value" pairs are also supported for backward compatibility
1684
 * with older stuff.)
1685
 *
1686
 * Returns a reference to the value in the StringList that the caller
1687
 * should not attempt to free.
1688
 *
1689
 * Returns NULL if the name is not found.
1690
 */
1691
1692
const char *CSLFetchNameValue(CSLConstList papszStrList, const char *pszName)
1693
716M
{
1694
716M
    if (papszStrList == nullptr || pszName == nullptr)
1695
105M
        return nullptr;
1696
1697
610M
    const size_t nLen = strlen(pszName);
1698
3.99G
    while (*papszStrList != nullptr)
1699
3.39G
    {
1700
3.39G
        if (EQUALN(*papszStrList, pszName, nLen) &&
1701
7.79M
            ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
1702
7.75M
        {
1703
7.75M
            return (*papszStrList) + nLen + 1;
1704
7.75M
        }
1705
3.38G
        ++papszStrList;
1706
3.38G
    }
1707
602M
    return nullptr;
1708
610M
}
1709
1710
/************************************************************************/
1711
/*                            CSLFindName()                             */
1712
/************************************************************************/
1713
1714
/**
1715
 * Find StringList entry with given key name.
1716
 *
1717
 * @param papszStrList the string list to search.
1718
 * @param pszName the key value to look for (case insensitive).
1719
 *
1720
 * @return -1 on failure or the list index of the first occurrence
1721
 * matching the given key.
1722
 */
1723
1724
int CSLFindName(CSLConstList papszStrList, const char *pszName)
1725
315M
{
1726
315M
    if (papszStrList == nullptr || pszName == nullptr)
1727
57.0M
        return -1;
1728
1729
258M
    const size_t nLen = strlen(pszName);
1730
258M
    int iIndex = 0;
1731
2.12G
    while (*papszStrList != nullptr)
1732
2.06G
    {
1733
2.06G
        if (EQUALN(*papszStrList, pszName, nLen) &&
1734
195M
            ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
1735
194M
        {
1736
194M
            return iIndex;
1737
194M
        }
1738
1.86G
        ++iIndex;
1739
1.86G
        ++papszStrList;
1740
1.86G
    }
1741
63.6M
    return -1;
1742
258M
}
1743
1744
/************************************************************************/
1745
/*                         CPLParseMemorySize()                         */
1746
/************************************************************************/
1747
1748
/** Parse a memory size from a string.
1749
 *
1750
 * The string may indicate the units of the memory (e.g., "230k", "500 MB"),
1751
 * using the prefixes "k", "m", or "g" in either lower or upper-case,
1752
 * optionally followed by a "b" or "B". The string may alternatively specify
1753
 * memory as a fraction of the usable RAM (e.g., "25%"). Spaces before the
1754
 * number, between the number and the units, or after the units are ignored,
1755
 * but other characters will cause a parsing failure. If the string cannot
1756
 * be understood, the function will return CE_Failure.
1757
 *
1758
 * @param pszValue the string to parse
1759
 * @param[out] pnValue the parsed size, converted to bytes (if unit was specified)
1760
 * @param[out] pbUnitSpecified whether the string indicated the units
1761
 *
1762
 * @return CE_None on success, CE_Failure otherwise
1763
 * @since 3.10
1764
 */
1765
CPLErr CPLParseMemorySize(const char *pszValue, GIntBig *pnValue,
1766
                          bool *pbUnitSpecified)
1767
1.16M
{
1768
1.16M
    const char *start = pszValue;
1769
1.16M
    char *end = nullptr;
1770
1771
    // trim leading whitespace
1772
1.16M
    while (*start == ' ')
1773
0
    {
1774
0
        start++;
1775
0
    }
1776
1777
1.16M
    auto len = CPLStrnlen(start, 100);
1778
1.16M
    double value = CPLStrtodM(start, &end);
1779
1.16M
    const char *unit = nullptr;
1780
1.16M
    bool unitIsNotPercent = false;
1781
1782
1.16M
    if (end == start)
1783
0
    {
1784
0
        CPLError(CE_Failure, CPLE_IllegalArg, "Received non-numeric value: %s",
1785
0
                 pszValue);
1786
0
        return CE_Failure;
1787
0
    }
1788
1789
1.16M
    if (value < 0 || !std::isfinite(value))
1790
0
    {
1791
0
        CPLError(CE_Failure, CPLE_IllegalArg,
1792
0
                 "Memory size must be a positive number or zero.");
1793
0
        return CE_Failure;
1794
0
    }
1795
1796
3.50M
    for (const char *c = end; c < start + len; c++)
1797
2.33M
    {
1798
2.33M
        if (unit == nullptr)
1799
1.16M
        {
1800
            // check various suffixes and convert number into bytes
1801
1.16M
            if (*c == '%')
1802
0
            {
1803
0
                if (value < 0 || value > 100)
1804
0
                {
1805
0
                    CPLError(CE_Failure, CPLE_IllegalArg,
1806
0
                             "Memory percentage must be between 0 and 100.");
1807
0
                    return CE_Failure;
1808
0
                }
1809
0
                auto bytes = CPLGetUsablePhysicalRAM();
1810
0
                if (bytes == 0)
1811
0
                {
1812
0
                    CPLError(CE_Failure, CPLE_NotSupported,
1813
0
                             "Cannot determine usable physical RAM");
1814
0
                    return CE_Failure;
1815
0
                }
1816
0
                value *= static_cast<double>(bytes / 100);
1817
0
                unit = c;
1818
0
            }
1819
1.16M
            else
1820
1.16M
            {
1821
1.16M
                switch (*c)
1822
1.16M
                {
1823
0
                    case 'G':
1824
0
                    case 'g':
1825
0
                        value *= 1024;
1826
0
                        [[fallthrough]];
1827
1.16M
                    case 'M':
1828
1.16M
                    case 'm':
1829
1.16M
                        value *= 1024;
1830
1.16M
                        [[fallthrough]];
1831
1.16M
                    case 'K':
1832
1.16M
                    case 'k':
1833
1.16M
                        value *= 1024;
1834
1.16M
                        unit = c;
1835
1.16M
                        unitIsNotPercent = true;
1836
1.16M
                        break;
1837
0
                    case ' ':
1838
0
                        break;
1839
0
                    default:
1840
0
                        CPLError(CE_Failure, CPLE_IllegalArg,
1841
0
                                 "Failed to parse memory size: %s", pszValue);
1842
0
                        return CE_Failure;
1843
1.16M
                }
1844
1.16M
            }
1845
1.16M
        }
1846
1.16M
        else if (unitIsNotPercent && c == unit + 1 && (*c == 'b' || *c == 'B'))
1847
1.16M
        {
1848
            // ignore 'B' or 'b' as part of unit
1849
1.16M
            continue;
1850
1.16M
        }
1851
0
        else if (*c != ' ')
1852
0
        {
1853
0
            CPLError(CE_Failure, CPLE_IllegalArg,
1854
0
                     "Failed to parse memory size: %s", pszValue);
1855
0
            return CE_Failure;
1856
0
        }
1857
2.33M
    }
1858
1859
1.16M
    if (value > static_cast<double>(std::numeric_limits<GIntBig>::max()) ||
1860
1.16M
        value > static_cast<double>(std::numeric_limits<size_t>::max()))
1861
0
    {
1862
0
        CPLError(CE_Failure, CPLE_IllegalArg, "Memory size is too large: %s",
1863
0
                 pszValue);
1864
0
        return CE_Failure;
1865
0
    }
1866
1867
1.16M
    *pnValue = static_cast<GIntBig>(value);
1868
1.16M
    if (pbUnitSpecified)
1869
199
    {
1870
199
        *pbUnitSpecified = (unit != nullptr);
1871
199
    }
1872
1.16M
    return CE_None;
1873
1.16M
}
1874
1875
/**********************************************************************
1876
 *                       CPLParseNameValue()
1877
 **********************************************************************/
1878
1879
/**
1880
 * Parse NAME=VALUE string into name and value components.
1881
 *
1882
 * Note that if ppszKey is non-NULL, the key (or name) portion will be
1883
 * allocated using CPLMalloc() and returned in that pointer.  It is the
1884
 * application's responsibility to free this string, but the application should
1885
 * not modify or free the returned value portion.
1886
 *
1887
 * This function also supports "NAME:VALUE" strings and will strip white
1888
 * space from around the delimiter when forming name and value strings.
1889
 *
1890
 * Eventually CSLFetchNameValue() and friends may be modified to use
1891
 * CPLParseNameValue().
1892
 *
1893
 * @param pszNameValue string in "NAME=VALUE" format.
1894
 * @param ppszKey optional pointer though which to return the name
1895
 * portion.
1896
 *
1897
 * @return the value portion (pointing into the original string).
1898
 */
1899
1900
const char *CPLParseNameValue(const char *pszNameValue, char **ppszKey)
1901
15.7M
{
1902
354M
    for (int i = 0; pszNameValue[i] != '\0'; ++i)
1903
348M
    {
1904
348M
        if (pszNameValue[i] == '=' || pszNameValue[i] == ':')
1905
9.02M
        {
1906
9.02M
            const char *pszValue = pszNameValue + i + 1;
1907
12.0M
            while (*pszValue == ' ' || *pszValue == '\t')
1908
2.99M
                ++pszValue;
1909
1910
9.02M
            if (ppszKey != nullptr)
1911
9.02M
            {
1912
9.02M
                *ppszKey = static_cast<char *>(CPLMalloc(i + 1));
1913
9.02M
                memcpy(*ppszKey, pszNameValue, i);
1914
9.02M
                (*ppszKey)[i] = '\0';
1915
9.39M
                while (i > 0 &&
1916
9.08M
                       ((*ppszKey)[i - 1] == ' ' || (*ppszKey)[i - 1] == '\t'))
1917
365k
                {
1918
365k
                    (*ppszKey)[i - 1] = '\0';
1919
365k
                    i--;
1920
365k
                }
1921
9.02M
            }
1922
1923
9.02M
            return pszValue;
1924
9.02M
        }
1925
348M
    }
1926
1927
6.68M
    return nullptr;
1928
15.7M
}
1929
1930
namespace cpl
1931
{
1932
std::pair<std::string_view, std::string_view>
1933
parse_name_value(std::string_view svNameValue)
1934
0
{
1935
0
    for (size_t i = 0; i < svNameValue.size(); ++i)
1936
0
    {
1937
0
        if (svNameValue[i] == '=' || svNameValue[i] == ':')
1938
0
        {
1939
0
            auto parsed = std::make_pair(trim(svNameValue.substr(0, i)),
1940
0
                                         trim(svNameValue.substr(i + 1)));
1941
1942
0
            if (!parsed.first.empty())
1943
0
            {
1944
0
                return parsed;
1945
0
            }
1946
0
            else
1947
0
            {
1948
0
                return std::make_pair(std::string_view(), std::string_view());
1949
0
            }
1950
0
        }
1951
0
    }
1952
1953
0
    return std::make_pair(std::string_view(), std::string_view());
1954
0
}
1955
1956
std::pair<std::string_view, std::string_view>
1957
parse_name_value(const char *pszNameValue)
1958
0
{
1959
0
    return parse_name_value(std::string_view(pszNameValue));
1960
0
}
1961
1962
}  // namespace cpl
1963
1964
/**********************************************************************
1965
 *                       CPLParseNameValueSep()
1966
 **********************************************************************/
1967
/**
1968
 * Parse NAME<Sep>VALUE string into name and value components.
1969
 *
1970
 * This is derived directly from CPLParseNameValue() which will separate
1971
 * on '=' OR ':', here chSep is required for specifying the separator
1972
 * explicitly.
1973
 *
1974
 * @param pszNameValue string in "NAME=VALUE" format.
1975
 * @param ppszKey optional pointer though which to return the name
1976
 * portion.
1977
 * @param chSep required single char separator
1978
 * @return the value portion (pointing into original string).
1979
 */
1980
1981
const char *CPLParseNameValueSep(const char *pszNameValue, char **ppszKey,
1982
                                 char chSep)
1983
37
{
1984
1.14k
    for (int i = 0; pszNameValue[i] != '\0'; ++i)
1985
1.11k
    {
1986
1.11k
        if (pszNameValue[i] == chSep)
1987
7
        {
1988
7
            const char *pszValue = pszNameValue + i + 1;
1989
7
            while (*pszValue == ' ' || *pszValue == '\t')
1990
0
                ++pszValue;
1991
1992
7
            if (ppszKey != nullptr)
1993
7
            {
1994
7
                *ppszKey = static_cast<char *>(CPLMalloc(i + 1));
1995
7
                memcpy(*ppszKey, pszNameValue, i);
1996
7
                (*ppszKey)[i] = '\0';
1997
7
                while (i > 0 &&
1998
7
                       ((*ppszKey)[i - 1] == ' ' || (*ppszKey)[i - 1] == '\t'))
1999
0
                {
2000
0
                    (*ppszKey)[i - 1] = '\0';
2001
0
                    i--;
2002
0
                }
2003
7
            }
2004
2005
7
            return pszValue;
2006
7
        }
2007
1.11k
    }
2008
2009
30
    return nullptr;
2010
37
}
2011
2012
/**********************************************************************
2013
 *                       CSLFetchNameValueMultiple()
2014
 **********************************************************************/
2015
2016
/** In a StringList of "Name=Value" pairs, look for all the
2017
 * values with the specified name.  The search is not case
2018
 * sensitive.
2019
 * ("Name:Value" pairs are also supported for backward compatibility
2020
 * with older stuff.)
2021
 *
2022
 * Returns StringList with one entry for each occurrence of the
2023
 * specified name.  The StringList should eventually be destroyed
2024
 * by calling CSLDestroy().
2025
 *
2026
 * Returns NULL if the name is not found.
2027
 */
2028
2029
char **CSLFetchNameValueMultiple(CSLConstList papszStrList, const char *pszName)
2030
66.4k
{
2031
66.4k
    if (papszStrList == nullptr || pszName == nullptr)
2032
65.7k
        return nullptr;
2033
2034
690
    const size_t nLen = strlen(pszName);
2035
690
    char **papszValues = nullptr;
2036
43.2k
    while (*papszStrList != nullptr)
2037
42.5k
    {
2038
42.5k
        if (EQUALN(*papszStrList, pszName, nLen) &&
2039
0
            ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
2040
0
        {
2041
0
            papszValues = CSLAddString(papszValues, (*papszStrList) + nLen + 1);
2042
0
        }
2043
42.5k
        ++papszStrList;
2044
42.5k
    }
2045
2046
690
    return papszValues;
2047
66.4k
}
2048
2049
/**********************************************************************
2050
 *                       CSLAddNameValue()
2051
 **********************************************************************/
2052
2053
/** Add a new entry to a StringList of "Name=Value" pairs,
2054
 * ("Name:Value" pairs are also supported for backward compatibility
2055
 * with older stuff.)
2056
 *
2057
 * This function does not check if a "Name=Value" pair already exists
2058
 * for that name and can generate multiple entries for the same name.
2059
 * Use CSLSetNameValue() if you want each name to have only one value.
2060
 *
2061
 * Returns the modified StringList.
2062
 */
2063
2064
char **CSLAddNameValue(char **papszStrList, const char *pszName,
2065
                       const char *pszValue)
2066
5.55M
{
2067
5.55M
    if (pszName == nullptr || pszValue == nullptr)
2068
0
        return papszStrList;
2069
2070
5.55M
    const size_t nLen = strlen(pszName) + strlen(pszValue) + 2;
2071
5.55M
    char *pszLine = static_cast<char *>(CPLMalloc(nLen));
2072
5.55M
    snprintf(pszLine, nLen, "%s=%s", pszName, pszValue);
2073
5.55M
    papszStrList = CSLAddString(papszStrList, pszLine);
2074
5.55M
    CPLFree(pszLine);
2075
2076
5.55M
    return papszStrList;
2077
5.55M
}
2078
2079
/************************************************************************/
2080
/*                          CSLSetNameValue()                           */
2081
/************************************************************************/
2082
2083
/**
2084
 * Assign value to name in StringList.
2085
 *
2086
 * Set the value for a given name in a StringList of "Name=Value" pairs
2087
 * ("Name:Value" pairs are also supported for backward compatibility
2088
 * with older stuff.)
2089
 *
2090
 * If there is already a value for that name in the list then the value
2091
 * is changed, otherwise a new "Name=Value" pair is added.
2092
 *
2093
 * @param papszList the original list, the modified version is returned.
2094
 * @param pszName the name to be assigned a value.  This should be a well
2095
 * formed token (no spaces or very special characters).
2096
 * @param pszValue the value to assign to the name.  This should not contain
2097
 * any newlines (CR or LF) but is otherwise pretty much unconstrained.  If
2098
 * NULL any corresponding value will be removed.
2099
 *
2100
 * @return modified StringList.
2101
 */
2102
2103
char **CSLSetNameValue(char **papszList, const char *pszName,
2104
                       const char *pszValue)
2105
10.0M
{
2106
10.0M
    if (pszName == nullptr)
2107
0
        return papszList;
2108
2109
10.0M
    size_t nLen = strlen(pszName);
2110
10.0M
    while (nLen > 0 && pszName[nLen - 1] == ' ')
2111
73
        nLen--;
2112
10.0M
    char **papszPtr = papszList;
2113
268M
    while (papszPtr && *papszPtr != nullptr)
2114
263M
    {
2115
263M
        if (EQUALN(*papszPtr, pszName, nLen))
2116
9.40M
        {
2117
9.40M
            size_t i;
2118
10.5M
            for (i = nLen; (*papszPtr)[i] == ' '; ++i)
2119
1.13M
            {
2120
1.13M
            }
2121
9.40M
            if ((*papszPtr)[i] == '=' || (*papszPtr)[i] == ':')
2122
4.38M
            {
2123
                // Found it.
2124
                // Change the value... make sure to keep the ':' or '='.
2125
4.38M
                const char cSep = (*papszPtr)[i];
2126
2127
4.38M
                CPLFree(*papszPtr);
2128
2129
                // If the value is NULL, remove this entry completely.
2130
4.38M
                if (pszValue == nullptr)
2131
398k
                {
2132
398k
                    while (papszPtr[1] != nullptr)
2133
0
                    {
2134
0
                        *papszPtr = papszPtr[1];
2135
0
                        ++papszPtr;
2136
0
                    }
2137
398k
                    *papszPtr = nullptr;
2138
398k
                }
2139
2140
                // Otherwise replace with new value.
2141
3.98M
                else
2142
3.98M
                {
2143
3.98M
                    const size_t nLen2 = strlen(pszName) + strlen(pszValue) + 2;
2144
3.98M
                    *papszPtr = static_cast<char *>(CPLMalloc(nLen2));
2145
3.98M
                    snprintf(*papszPtr, nLen2, "%s%c%s", pszName, cSep,
2146
3.98M
                             pszValue);
2147
3.98M
                }
2148
4.38M
                return papszList;
2149
4.38M
            }
2150
9.40M
        }
2151
258M
        ++papszPtr;
2152
258M
    }
2153
2154
5.70M
    if (pszValue == nullptr)
2155
551k
        return papszList;
2156
2157
    // The name does not exist yet.  Create a new entry.
2158
5.15M
    return CSLAddNameValue(papszList, pszName, pszValue);
2159
5.70M
}
2160
2161
/************************************************************************/
2162
/*                      CSLSetNameValueSeparator()                      */
2163
/************************************************************************/
2164
2165
/**
2166
 * Replace the default separator (":" or "=") with the passed separator
2167
 * in the given name/value list.
2168
 *
2169
 * Note that if a separator other than ":" or "=" is used, the resulting
2170
 * list will not be manipulable by the CSL name/value functions any more.
2171
 *
2172
 * The CPLParseNameValue() function is used to break the existing lines,
2173
 * and it also strips white space from around the existing delimiter, thus
2174
 * the old separator, and any white space will be replaced by the new
2175
 * separator.  For formatting purposes it may be desirable to include some
2176
 * white space in the new separator.  e.g. ": " or " = ".
2177
 *
2178
 * @param papszList the list to update.  Component strings may be freed
2179
 * but the list array will remain at the same location.
2180
 *
2181
 * @param pszSeparator the new separator string to insert.
2182
 */
2183
2184
void CSLSetNameValueSeparator(char **papszList, const char *pszSeparator)
2185
2186
0
{
2187
0
    const int nLines = CSLCount(papszList);
2188
2189
0
    for (int iLine = 0; iLine < nLines; ++iLine)
2190
0
    {
2191
0
        char *pszKey = nullptr;
2192
0
        const char *pszValue = CPLParseNameValue(papszList[iLine], &pszKey);
2193
0
        if (pszValue == nullptr || pszKey == nullptr)
2194
0
        {
2195
0
            CPLFree(pszKey);
2196
0
            continue;
2197
0
        }
2198
2199
0
        char *pszNewLine = static_cast<char *>(CPLMalloc(
2200
0
            strlen(pszValue) + strlen(pszKey) + strlen(pszSeparator) + 1));
2201
0
        strcpy(pszNewLine, pszKey);
2202
0
        strcat(pszNewLine, pszSeparator);
2203
0
        strcat(pszNewLine, pszValue);
2204
0
        CPLFree(papszList[iLine]);
2205
0
        papszList[iLine] = pszNewLine;
2206
0
        CPLFree(pszKey);
2207
0
    }
2208
0
}
2209
2210
/************************************************************************/
2211
/*                          CPLEscapeString()                           */
2212
/************************************************************************/
2213
2214
/**
2215
 * Apply escaping to string to preserve special characters.
2216
 *
2217
 * This function will "escape" a variety of special characters
2218
 * to make the string suitable to embed within a string constant
2219
 * or to write within a text stream but in a form that can be
2220
 * reconstituted to its original form.  The escaping will even preserve
2221
 * zero bytes allowing preservation of raw binary data.
2222
 *
2223
 * CPLES_BackslashQuotable(0): This scheme turns a binary string into
2224
 * a form suitable to be placed within double quotes as a string constant.
2225
 * The backslash, quote, '\\0' and newline characters are all escaped in
2226
 * the usual C style.
2227
 *
2228
 * CPLES_XML(1): This scheme converts the '<', '>', '"' and '&' characters into
2229
 * their XML/HTML equivalent (&lt;, &gt;, &quot; and &amp;) making a string safe
2230
 * to embed as CDATA within an XML element.  The '\\0' is not escaped and
2231
 * should not be included in the input.
2232
 *
2233
 * CPLES_URL(2): Everything except alphanumerics and the characters
2234
 * '$', '-', '_', '.', '+', '!', '*', ''', '(', ')' and ',' (see RFC1738) are
2235
 * converted to a percent followed by a two digit hex encoding of the character
2236
 * (leading zero supplied if needed).  This is the mechanism used for encoding
2237
 * values to be passed in URLs. Note that this is different from what
2238
 * CPLString::URLEncode() does.
2239
 *
2240
 * CPLES_SQL(3): All single quotes are replaced with two single quotes.
2241
 * Suitable for use when constructing literal values for SQL commands where
2242
 * the literal will be enclosed in single quotes.
2243
 *
2244
 * CPLES_CSV(4): If the values contains commas, semicolons, tabs, double quotes,
2245
 * or newlines it placed in double quotes, and double quotes in the value are
2246
 * doubled. Suitable for use when constructing field values for .csv files.
2247
 * Note that CPLUnescapeString() currently does not support this format, only
2248
 * CPLEscapeString().  See cpl_csv.cpp for CSV parsing support.
2249
 *
2250
 * CPLES_SQLI(7): All double quotes are replaced with two double quotes.
2251
 * Suitable for use when constructing identifiers for SQL commands where
2252
 * the literal will be enclosed in double quotes.
2253
 *
2254
 * @param pszInput the string to escape.
2255
 * @param nLength The number of bytes of data to preserve.  If this is -1
2256
 * the strlen(pszString) function will be used to compute the length.
2257
 * @param nScheme the encoding scheme to use.
2258
 *
2259
 * @return an escaped, zero terminated string that should be freed with
2260
 * CPLFree() when no longer needed.
2261
 */
2262
2263
char *CPLEscapeString(const char *pszInput, int nLength, int nScheme)
2264
33.2M
{
2265
33.2M
    const size_t szLength =
2266
33.2M
        (nLength < 0) ? strlen(pszInput) : static_cast<size_t>(nLength);
2267
33.2M
#define nLength no_longer_use_me
2268
2269
33.2M
    size_t nSizeAlloc = 1;
2270
#if SIZEOF_VOIDP < 8
2271
    bool bWrapAround = false;
2272
    const auto IncSizeAlloc = [&nSizeAlloc, &bWrapAround](size_t inc)
2273
    {
2274
        constexpr size_t SZ_MAX = std::numeric_limits<size_t>::max();
2275
        if (nSizeAlloc > SZ_MAX - inc)
2276
        {
2277
            bWrapAround = true;
2278
            nSizeAlloc = 0;
2279
        }
2280
        nSizeAlloc += inc;
2281
    };
2282
#else
2283
1.03G
    const auto IncSizeAlloc = [&nSizeAlloc](size_t inc) { nSizeAlloc += inc; };
2284
33.2M
#endif
2285
2286
33.2M
    if (nScheme == CPLES_BackslashQuotable)
2287
1.62k
    {
2288
245k
        for (size_t iIn = 0; iIn < szLength; iIn++)
2289
244k
        {
2290
244k
            if (pszInput[iIn] == '\0' || pszInput[iIn] == '\n' ||
2291
236k
                pszInput[iIn] == '"' || pszInput[iIn] == '\\')
2292
9.23k
                IncSizeAlloc(2);
2293
234k
            else
2294
234k
                IncSizeAlloc(1);
2295
244k
        }
2296
1.62k
    }
2297
33.2M
    else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES)
2298
31.9M
    {
2299
998M
        for (size_t iIn = 0; iIn < szLength; ++iIn)
2300
966M
        {
2301
966M
            if (pszInput[iIn] == '<')
2302
12.7M
            {
2303
12.7M
                IncSizeAlloc(4);
2304
12.7M
            }
2305
953M
            else if (pszInput[iIn] == '>')
2306
49.2M
            {
2307
49.2M
                IncSizeAlloc(4);
2308
49.2M
            }
2309
904M
            else if (pszInput[iIn] == '&')
2310
1.52M
            {
2311
1.52M
                IncSizeAlloc(5);
2312
1.52M
            }
2313
902M
            else if (pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES)
2314
3.08M
            {
2315
3.08M
                IncSizeAlloc(6);
2316
3.08M
            }
2317
            // Python 2 does not display the UTF-8 character corresponding
2318
            // to the byte-order mark (BOM), so escape it.
2319
899M
            else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] ==
2320
899M
                         0xEF &&
2321
272k
                     (reinterpret_cast<const unsigned char *>(
2322
272k
                         pszInput))[iIn + 1] == 0xBB &&
2323
106k
                     (reinterpret_cast<const unsigned char *>(
2324
106k
                         pszInput))[iIn + 2] == 0xBF)
2325
89.1k
            {
2326
89.1k
                IncSizeAlloc(8);
2327
89.1k
                iIn += 2;
2328
89.1k
            }
2329
899M
            else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] <
2330
899M
                         0x20 &&
2331
8.49M
                     pszInput[iIn] != 0x9 && pszInput[iIn] != 0xA &&
2332
4.70M
                     pszInput[iIn] != 0xD)
2333
4.43M
            {
2334
                // These control characters are unrepresentable in XML format,
2335
                // so we just drop them.  #4117
2336
4.43M
            }
2337
895M
            else
2338
895M
            {
2339
895M
                IncSizeAlloc(1);
2340
895M
            }
2341
966M
        }
2342
31.9M
    }
2343
1.33M
    else if (nScheme == CPLES_URL)  // Untested at implementation.
2344
176k
    {
2345
68.1M
        for (size_t iIn = 0; iIn < szLength; ++iIn)
2346
68.0M
        {
2347
68.0M
            if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') ||
2348
40.2M
                (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') ||
2349
35.2M
                (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') ||
2350
30.7M
                pszInput[iIn] == '$' || pszInput[iIn] == '-' ||
2351
30.6M
                pszInput[iIn] == '_' || pszInput[iIn] == '.' ||
2352
29.2M
                pszInput[iIn] == '+' || pszInput[iIn] == '!' ||
2353
27.9M
                pszInput[iIn] == '*' || pszInput[iIn] == '\'' ||
2354
27.7M
                pszInput[iIn] == '(' || pszInput[iIn] == ')' ||
2355
27.7M
                pszInput[iIn] == ',')
2356
40.5M
            {
2357
40.5M
                IncSizeAlloc(1);
2358
40.5M
            }
2359
27.4M
            else
2360
27.4M
            {
2361
27.4M
                IncSizeAlloc(3);
2362
27.4M
            }
2363
68.0M
        }
2364
176k
    }
2365
1.16M
    else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI)
2366
28
    {
2367
28
        const char chQuote = nScheme == CPLES_SQL ? '\'' : '\"';
2368
519
        for (size_t iIn = 0; iIn < szLength; ++iIn)
2369
491
        {
2370
491
            if (pszInput[iIn] == chQuote)
2371
0
            {
2372
0
                IncSizeAlloc(2);
2373
0
            }
2374
491
            else
2375
491
            {
2376
491
                IncSizeAlloc(1);
2377
491
            }
2378
491
        }
2379
28
    }
2380
1.16M
    else if (nScheme == CPLES_CSV || nScheme == CPLES_CSV_FORCE_QUOTING)
2381
1.16M
    {
2382
1.16M
        if (nScheme == CPLES_CSV && strcspn(pszInput, "\",;\t\n\r") == szLength)
2383
1.12M
        {
2384
1.12M
            char *pszOutput =
2385
1.12M
                static_cast<char *>(VSI_MALLOC_VERBOSE(szLength + 1));
2386
1.12M
            if (pszOutput == nullptr)
2387
0
                return nullptr;
2388
1.12M
            memcpy(pszOutput, pszInput, szLength + 1);
2389
1.12M
            return pszOutput;
2390
1.12M
        }
2391
34.8k
        else
2392
34.8k
        {
2393
34.8k
            IncSizeAlloc(1);
2394
4.45M
            for (size_t iIn = 0; iIn < szLength; ++iIn)
2395
4.41M
            {
2396
4.41M
                if (pszInput[iIn] == '\"')
2397
68.1k
                {
2398
68.1k
                    IncSizeAlloc(2);
2399
68.1k
                }
2400
4.34M
                else
2401
4.34M
                    IncSizeAlloc(1);
2402
4.41M
            }
2403
34.8k
            IncSizeAlloc(1);
2404
34.8k
        }
2405
1.16M
    }
2406
0
    else
2407
0
    {
2408
0
        CPLError(CE_Failure, CPLE_AppDefined,
2409
0
                 "Undefined escaping scheme (%d) in CPLEscapeString()",
2410
0
                 nScheme);
2411
0
        return CPLStrdup("");
2412
0
    }
2413
2414
#if SIZEOF_VOIDP < 8
2415
    if (bWrapAround)
2416
    {
2417
        CPLError(CE_Failure, CPLE_OutOfMemory,
2418
                 "Out of memory in CPLEscapeString()");
2419
        return nullptr;
2420
    }
2421
#endif
2422
2423
32.1M
    char *pszOutput = static_cast<char *>(VSI_MALLOC_VERBOSE(nSizeAlloc));
2424
32.1M
    if (pszOutput == nullptr)
2425
0
        return nullptr;
2426
2427
32.1M
    size_t iOut = 0;
2428
2429
32.1M
    if (nScheme == CPLES_BackslashQuotable)
2430
1.62k
    {
2431
245k
        for (size_t iIn = 0; iIn < szLength; iIn++)
2432
244k
        {
2433
244k
            if (pszInput[iIn] == '\0')
2434
0
            {
2435
0
                pszOutput[iOut++] = '\\';
2436
0
                pszOutput[iOut++] = '0';
2437
0
            }
2438
244k
            else if (pszInput[iIn] == '\n')
2439
7.64k
            {
2440
7.64k
                pszOutput[iOut++] = '\\';
2441
7.64k
                pszOutput[iOut++] = 'n';
2442
7.64k
            }
2443
236k
            else if (pszInput[iIn] == '"')
2444
585
            {
2445
585
                pszOutput[iOut++] = '\\';
2446
585
                pszOutput[iOut++] = '\"';
2447
585
            }
2448
235k
            else if (pszInput[iIn] == '\\')
2449
1.00k
            {
2450
1.00k
                pszOutput[iOut++] = '\\';
2451
1.00k
                pszOutput[iOut++] = '\\';
2452
1.00k
            }
2453
234k
            else
2454
234k
                pszOutput[iOut++] = pszInput[iIn];
2455
244k
        }
2456
1.62k
        pszOutput[iOut++] = '\0';
2457
1.62k
    }
2458
32.1M
    else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES)
2459
31.9M
    {
2460
998M
        for (size_t iIn = 0; iIn < szLength; ++iIn)
2461
966M
        {
2462
966M
            if (pszInput[iIn] == '<')
2463
12.7M
            {
2464
12.7M
                pszOutput[iOut++] = '&';
2465
12.7M
                pszOutput[iOut++] = 'l';
2466
12.7M
                pszOutput[iOut++] = 't';
2467
12.7M
                pszOutput[iOut++] = ';';
2468
12.7M
            }
2469
953M
            else if (pszInput[iIn] == '>')
2470
49.2M
            {
2471
49.2M
                pszOutput[iOut++] = '&';
2472
49.2M
                pszOutput[iOut++] = 'g';
2473
49.2M
                pszOutput[iOut++] = 't';
2474
49.2M
                pszOutput[iOut++] = ';';
2475
49.2M
            }
2476
904M
            else if (pszInput[iIn] == '&')
2477
1.52M
            {
2478
1.52M
                pszOutput[iOut++] = '&';
2479
1.52M
                pszOutput[iOut++] = 'a';
2480
1.52M
                pszOutput[iOut++] = 'm';
2481
1.52M
                pszOutput[iOut++] = 'p';
2482
1.52M
                pszOutput[iOut++] = ';';
2483
1.52M
            }
2484
902M
            else if (pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES)
2485
3.08M
            {
2486
3.08M
                pszOutput[iOut++] = '&';
2487
3.08M
                pszOutput[iOut++] = 'q';
2488
3.08M
                pszOutput[iOut++] = 'u';
2489
3.08M
                pszOutput[iOut++] = 'o';
2490
3.08M
                pszOutput[iOut++] = 't';
2491
3.08M
                pszOutput[iOut++] = ';';
2492
3.08M
            }
2493
            // Python 2 does not display the UTF-8 character corresponding
2494
            // to the byte-order mark (BOM), so escape it.
2495
899M
            else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] ==
2496
899M
                         0xEF &&
2497
272k
                     (reinterpret_cast<const unsigned char *>(
2498
272k
                         pszInput))[iIn + 1] == 0xBB &&
2499
106k
                     (reinterpret_cast<const unsigned char *>(
2500
106k
                         pszInput))[iIn + 2] == 0xBF)
2501
89.1k
            {
2502
89.1k
                pszOutput[iOut++] = '&';
2503
89.1k
                pszOutput[iOut++] = '#';
2504
89.1k
                pszOutput[iOut++] = 'x';
2505
89.1k
                pszOutput[iOut++] = 'F';
2506
89.1k
                pszOutput[iOut++] = 'E';
2507
89.1k
                pszOutput[iOut++] = 'F';
2508
89.1k
                pszOutput[iOut++] = 'F';
2509
89.1k
                pszOutput[iOut++] = ';';
2510
89.1k
                iIn += 2;
2511
89.1k
            }
2512
899M
            else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] <
2513
899M
                         0x20 &&
2514
8.49M
                     pszInput[iIn] != 0x9 && pszInput[iIn] != 0xA &&
2515
4.70M
                     pszInput[iIn] != 0xD)
2516
4.43M
            {
2517
                // These control characters are unrepresentable in XML format,
2518
                // so we just drop them.  #4117
2519
4.43M
            }
2520
895M
            else
2521
895M
            {
2522
895M
                pszOutput[iOut++] = pszInput[iIn];
2523
895M
            }
2524
966M
        }
2525
31.9M
        pszOutput[iOut++] = '\0';
2526
31.9M
    }
2527
211k
    else if (nScheme == CPLES_URL)  // Untested at implementation.
2528
176k
    {
2529
68.1M
        for (size_t iIn = 0; iIn < szLength; ++iIn)
2530
68.0M
        {
2531
68.0M
            if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') ||
2532
40.2M
                (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') ||
2533
35.2M
                (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') ||
2534
30.7M
                pszInput[iIn] == '$' || pszInput[iIn] == '-' ||
2535
30.6M
                pszInput[iIn] == '_' || pszInput[iIn] == '.' ||
2536
29.2M
                pszInput[iIn] == '+' || pszInput[iIn] == '!' ||
2537
27.9M
                pszInput[iIn] == '*' || pszInput[iIn] == '\'' ||
2538
27.7M
                pszInput[iIn] == '(' || pszInput[iIn] == ')' ||
2539
27.7M
                pszInput[iIn] == ',')
2540
40.5M
            {
2541
40.5M
                pszOutput[iOut++] = pszInput[iIn];
2542
40.5M
            }
2543
27.4M
            else
2544
27.4M
            {
2545
27.4M
                snprintf(pszOutput + iOut, nSizeAlloc - iOut, "%%%02X",
2546
27.4M
                         static_cast<unsigned char>(pszInput[iIn]));
2547
27.4M
                iOut += 3;
2548
27.4M
            }
2549
68.0M
        }
2550
176k
        pszOutput[iOut++] = '\0';
2551
176k
    }
2552
34.8k
    else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI)
2553
28
    {
2554
28
        const char chQuote = nScheme == CPLES_SQL ? '\'' : '\"';
2555
519
        for (size_t iIn = 0; iIn < szLength; ++iIn)
2556
491
        {
2557
491
            if (pszInput[iIn] == chQuote)
2558
0
            {
2559
0
                pszOutput[iOut++] = chQuote;
2560
0
                pszOutput[iOut++] = chQuote;
2561
0
            }
2562
491
            else
2563
491
            {
2564
491
                pszOutput[iOut++] = pszInput[iIn];
2565
491
            }
2566
491
        }
2567
28
        pszOutput[iOut++] = '\0';
2568
28
    }
2569
34.8k
    else if (nScheme == CPLES_CSV || nScheme == CPLES_CSV_FORCE_QUOTING)
2570
34.8k
    {
2571
34.8k
        pszOutput[iOut++] = '\"';
2572
2573
4.45M
        for (size_t iIn = 0; iIn < szLength; ++iIn)
2574
4.41M
        {
2575
4.41M
            if (pszInput[iIn] == '\"')
2576
68.1k
            {
2577
68.1k
                pszOutput[iOut++] = '\"';
2578
68.1k
                pszOutput[iOut++] = '\"';
2579
68.1k
            }
2580
4.34M
            else
2581
4.34M
                pszOutput[iOut++] = pszInput[iIn];
2582
4.41M
        }
2583
34.8k
        pszOutput[iOut++] = '\"';
2584
34.8k
        pszOutput[iOut++] = '\0';
2585
34.8k
    }
2586
2587
32.1M
    return pszOutput;
2588
32.1M
#undef nLength
2589
32.1M
}
2590
2591
/************************************************************************/
2592
/*                         CPLUnescapeString()                          */
2593
/************************************************************************/
2594
2595
/**
2596
 * Unescape a string.
2597
 *
2598
 * This function does the opposite of CPLEscapeString().  Given a string
2599
 * with special values escaped according to some scheme, it will return a
2600
 * new copy of the string returned to its original form.
2601
 *
2602
 * @param pszInput the input string.  This is a zero terminated string.
2603
 * @param pnLength location to return the length of the unescaped string,
2604
 * which may in some cases include embedded '\\0' characters.
2605
 * @param nScheme the escaped scheme to undo (see CPLEscapeString() for a
2606
 * list).  Does not yet support CSV.
2607
 *
2608
 * @return a copy of the unescaped string that should be freed by the
2609
 * application using CPLFree() when no longer needed.
2610
 */
2611
2612
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
2613
char *CPLUnescapeString(const char *pszInput, int *pnLength, int nScheme)
2614
2615
3.97M
{
2616
3.97M
    int iOut = 0;
2617
2618
    // TODO: Why times 4?
2619
3.97M
    char *pszOutput = static_cast<char *>(CPLMalloc(4 * strlen(pszInput) + 1));
2620
3.97M
    pszOutput[0] = '\0';
2621
2622
3.97M
    if (nScheme == CPLES_BackslashQuotable)
2623
1.27M
    {
2624
33.9M
        for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
2625
32.6M
        {
2626
32.6M
            if (pszInput[iIn] == '\\')
2627
98.2k
            {
2628
98.2k
                ++iIn;
2629
98.2k
                if (pszInput[iIn] == '\0')
2630
3.40k
                    break;
2631
94.8k
                if (pszInput[iIn] == 'n')
2632
6.09k
                    pszOutput[iOut++] = '\n';
2633
88.7k
                else if (pszInput[iIn] == '0')
2634
447
                    pszOutput[iOut++] = '\0';
2635
88.3k
                else
2636
88.3k
                    pszOutput[iOut++] = pszInput[iIn];
2637
94.8k
            }
2638
32.5M
            else
2639
32.5M
            {
2640
32.5M
                pszOutput[iOut++] = pszInput[iIn];
2641
32.5M
            }
2642
32.6M
        }
2643
1.27M
    }
2644
2.70M
    else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES)
2645
930k
    {
2646
930k
        char ch = '\0';
2647
2.08G
        for (int iIn = 0; (ch = pszInput[iIn]) != '\0'; ++iIn)
2648
2.08G
        {
2649
2.08G
            if (ch != '&')
2650
2.04G
            {
2651
2.04G
                pszOutput[iOut++] = ch;
2652
2.04G
            }
2653
36.9M
            else if (STARTS_WITH_CI(pszInput + iIn, "&lt;"))
2654
17.2M
            {
2655
17.2M
                pszOutput[iOut++] = '<';
2656
17.2M
                iIn += 3;
2657
17.2M
            }
2658
19.6M
            else if (STARTS_WITH_CI(pszInput + iIn, "&gt;"))
2659
16.6M
            {
2660
16.6M
                pszOutput[iOut++] = '>';
2661
16.6M
                iIn += 3;
2662
16.6M
            }
2663
2.98M
            else if (STARTS_WITH_CI(pszInput + iIn, "&amp;"))
2664
749k
            {
2665
749k
                pszOutput[iOut++] = '&';
2666
749k
                iIn += 4;
2667
749k
            }
2668
2.23M
            else if (STARTS_WITH_CI(pszInput + iIn, "&apos;"))
2669
24.9k
            {
2670
24.9k
                pszOutput[iOut++] = '\'';
2671
24.9k
                iIn += 5;
2672
24.9k
            }
2673
2.21M
            else if (STARTS_WITH_CI(pszInput + iIn, "&quot;"))
2674
1.33M
            {
2675
1.33M
                pszOutput[iOut++] = '"';
2676
1.33M
                iIn += 5;
2677
1.33M
            }
2678
872k
            else if (STARTS_WITH_CI(pszInput + iIn, "&#x"))
2679
128k
            {
2680
128k
                wchar_t anVal[2] = {0, 0};
2681
128k
                iIn += 3;
2682
2683
128k
                unsigned int nVal = 0;
2684
745k
                while (true)
2685
745k
                {
2686
745k
                    ch = pszInput[iIn++];
2687
745k
                    if (ch >= 'a' && ch <= 'f')
2688
117k
                        nVal = nVal * 16U +
2689
117k
                               static_cast<unsigned int>(ch - 'a' + 10);
2690
628k
                    else if (ch >= 'A' && ch <= 'F')
2691
194k
                        nVal = nVal * 16U +
2692
194k
                               static_cast<unsigned int>(ch - 'A' + 10);
2693
433k
                    else if (ch >= '0' && ch <= '9')
2694
304k
                        nVal = nVal * 16U + static_cast<unsigned int>(ch - '0');
2695
128k
                    else
2696
128k
                        break;
2697
745k
                }
2698
128k
                anVal[0] = static_cast<wchar_t>(nVal);
2699
128k
                if (ch != ';')
2700
49.6k
                    break;
2701
79.1k
                iIn--;
2702
2703
79.1k
                char *pszUTF8 =
2704
79.1k
                    CPLRecodeFromWChar(anVal, "WCHAR_T", CPL_ENC_UTF8);
2705
79.1k
                int nLen = static_cast<int>(strlen(pszUTF8));
2706
79.1k
                memcpy(pszOutput + iOut, pszUTF8, nLen);
2707
79.1k
                CPLFree(pszUTF8);
2708
79.1k
                iOut += nLen;
2709
79.1k
            }
2710
743k
            else if (STARTS_WITH_CI(pszInput + iIn, "&#"))
2711
240k
            {
2712
240k
                wchar_t anVal[2] = {0, 0};
2713
240k
                iIn += 2;
2714
2715
240k
                unsigned int nVal = 0;
2716
1.14M
                while (true)
2717
1.14M
                {
2718
1.14M
                    ch = pszInput[iIn++];
2719
1.14M
                    if (ch >= '0' && ch <= '9')
2720
901k
                        nVal = nVal * 10U + static_cast<unsigned int>(ch - '0');
2721
240k
                    else
2722
240k
                        break;
2723
1.14M
                }
2724
240k
                anVal[0] = static_cast<wchar_t>(nVal);
2725
240k
                if (ch != ';')
2726
48.9k
                    break;
2727
191k
                iIn--;
2728
2729
191k
                char *pszUTF8 =
2730
191k
                    CPLRecodeFromWChar(anVal, "WCHAR_T", CPL_ENC_UTF8);
2731
191k
                const int nLen = static_cast<int>(strlen(pszUTF8));
2732
191k
                memcpy(pszOutput + iOut, pszUTF8, nLen);
2733
191k
                CPLFree(pszUTF8);
2734
191k
                iOut += nLen;
2735
191k
            }
2736
503k
            else
2737
503k
            {
2738
                // Illegal escape sequence.
2739
503k
                CPLDebug("CPL",
2740
503k
                         "Error unescaping CPLES_XML text, '&' character "
2741
503k
                         "followed by unhandled escape sequence.");
2742
503k
                break;
2743
503k
            }
2744
2.08G
        }
2745
930k
    }
2746
1.77M
    else if (nScheme == CPLES_URL)
2747
1.77M
    {
2748
1.14G
        for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
2749
1.13G
        {
2750
1.13G
            if (pszInput[iIn] == '%' && pszInput[iIn + 1] != '\0' &&
2751
8.28M
                pszInput[iIn + 2] != '\0')
2752
8.25M
            {
2753
8.25M
                int nHexChar = 0;
2754
2755
8.25M
                if (pszInput[iIn + 1] >= 'A' && pszInput[iIn + 1] <= 'F')
2756
1.01M
                    nHexChar += 16 * (pszInput[iIn + 1] - 'A' + 10);
2757
7.23M
                else if (pszInput[iIn + 1] >= 'a' && pszInput[iIn + 1] <= 'f')
2758
158k
                    nHexChar += 16 * (pszInput[iIn + 1] - 'a' + 10);
2759
7.07M
                else if (pszInput[iIn + 1] >= '0' && pszInput[iIn + 1] <= '9')
2760
932k
                    nHexChar += 16 * (pszInput[iIn + 1] - '0');
2761
6.14M
                else
2762
6.14M
                    CPLDebug("CPL",
2763
6.14M
                             "Error unescaping CPLES_URL text, percent not "
2764
6.14M
                             "followed by two hex digits.");
2765
2766
8.25M
                if (pszInput[iIn + 2] >= 'A' && pszInput[iIn + 2] <= 'F')
2767
854k
                    nHexChar += pszInput[iIn + 2] - 'A' + 10;
2768
7.39M
                else if (pszInput[iIn + 2] >= 'a' && pszInput[iIn + 2] <= 'f')
2769
123k
                    nHexChar += pszInput[iIn + 2] - 'a' + 10;
2770
7.27M
                else if (pszInput[iIn + 2] >= '0' && pszInput[iIn + 2] <= '9')
2771
1.13M
                    nHexChar += pszInput[iIn + 2] - '0';
2772
6.13M
                else
2773
6.13M
                    CPLDebug("CPL",
2774
6.13M
                             "Error unescaping CPLES_URL text, percent not "
2775
6.13M
                             "followed by two hex digits.");
2776
2777
8.25M
                pszOutput[iOut++] = static_cast<char>(nHexChar);
2778
8.25M
                iIn += 2;
2779
8.25M
            }
2780
1.13G
            else if (pszInput[iIn] == '+')
2781
657k
            {
2782
657k
                pszOutput[iOut++] = ' ';
2783
657k
            }
2784
1.12G
            else
2785
1.12G
            {
2786
1.12G
                pszOutput[iOut++] = pszInput[iIn];
2787
1.12G
            }
2788
1.13G
        }
2789
1.77M
    }
2790
0
    else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI)
2791
0
    {
2792
0
        char szQuote = nScheme == CPLES_SQL ? '\'' : '\"';
2793
0
        for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
2794
0
        {
2795
0
            if (pszInput[iIn] == szQuote && pszInput[iIn + 1] == szQuote)
2796
0
            {
2797
0
                ++iIn;
2798
0
                pszOutput[iOut++] = pszInput[iIn];
2799
0
            }
2800
0
            else
2801
0
            {
2802
0
                pszOutput[iOut++] = pszInput[iIn];
2803
0
            }
2804
0
        }
2805
0
    }
2806
0
    else if (nScheme == CPLES_CSV)
2807
0
    {
2808
0
        CPLError(CE_Fatal, CPLE_NotSupported,
2809
0
                 "CSV Unescaping not yet implemented.");
2810
0
    }
2811
0
    else
2812
0
    {
2813
0
        CPLError(CE_Fatal, CPLE_NotSupported, "Unknown escaping style.");
2814
0
    }
2815
2816
3.97M
    pszOutput[iOut] = '\0';
2817
2818
3.97M
    if (pnLength != nullptr)
2819
838k
        *pnLength = iOut;
2820
2821
3.97M
    return pszOutput;
2822
3.97M
}
2823
2824
/************************************************************************/
2825
/*                           CPLBinaryToHex()                           */
2826
/************************************************************************/
2827
2828
/**
2829
 * Binary to hexadecimal translation.
2830
 *
2831
 * @param nBytes number of bytes of binary data in pabyData.
2832
 * @param pabyData array of data bytes to translate.
2833
 *
2834
 * @return hexadecimal translation, zero terminated.  Free with CPLFree().
2835
 */
2836
2837
char *CPLBinaryToHex(int nBytes, const GByte *pabyData)
2838
2839
29.0k
{
2840
29.0k
    CPLAssert(nBytes >= 0);
2841
29.0k
    char *pszHex = static_cast<char *>(
2842
29.0k
        VSI_MALLOC_VERBOSE(static_cast<size_t>(nBytes) * 2 + 1));
2843
29.0k
    if (!pszHex)
2844
0
    {
2845
0
        pszHex = CPLStrdup("");
2846
0
        return pszHex;
2847
0
    }
2848
29.0k
    pszHex[nBytes * 2] = '\0';
2849
2850
29.0k
    constexpr char achHex[] = "0123456789ABCDEF";
2851
2852
241k
    for (size_t i = 0; i < static_cast<size_t>(nBytes); ++i)
2853
212k
    {
2854
212k
        const int nLow = pabyData[i] & 0x0f;
2855
212k
        const int nHigh = (pabyData[i] & 0xf0) >> 4;
2856
2857
212k
        pszHex[i * 2] = achHex[nHigh];
2858
212k
        pszHex[i * 2 + 1] = achHex[nLow];
2859
212k
    }
2860
2861
29.0k
    return pszHex;
2862
29.0k
}
2863
2864
/************************************************************************/
2865
/*                           CPLHexToBinary()                           */
2866
/************************************************************************/
2867
2868
constexpr unsigned char hex2char[256] = {
2869
    // Not Hex characters.
2870
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2871
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2872
    // 0-9
2873
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2874
    // A-F
2875
    0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2876
    // Not Hex characters.
2877
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2878
    // a-f
2879
    0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2880
    0, 0, 0, 0, 0, 0, 0, 0, 0,
2881
    // Not Hex characters (upper 128 characters).
2882
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2883
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2884
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2885
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2886
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2887
    0, 0, 0};
2888
2889
/**
2890
 * Hexadecimal to binary translation
2891
 *
2892
 * @param pszHex the input hex encoded string.
2893
 * @param pnBytes the returned count of decoded bytes placed here.
2894
 *
2895
 * @return returns binary buffer of data - free with CPLFree().
2896
 */
2897
2898
GByte *CPLHexToBinary(const char *pszHex, int *pnBytes)
2899
2.89M
{
2900
2.89M
    const GByte *pabyHex = reinterpret_cast<const GByte *>(pszHex);
2901
2.89M
    const size_t nHexLen = strlen(pszHex);
2902
2903
2.89M
    GByte *pabyWKB = static_cast<GByte *>(CPLMalloc(nHexLen / 2 + 2));
2904
2905
77.0M
    for (size_t i = 0; i < nHexLen / 2; ++i)
2906
74.1M
    {
2907
74.1M
        const unsigned char h1 = hex2char[pabyHex[2 * i]];
2908
74.1M
        const unsigned char h2 = hex2char[pabyHex[2 * i + 1]];
2909
2910
        // First character is high bits, second is low bits.
2911
74.1M
        pabyWKB[i] = static_cast<GByte>((h1 << 4) | h2);
2912
74.1M
    }
2913
2.89M
    pabyWKB[nHexLen / 2] = 0;
2914
2.89M
    *pnBytes = static_cast<int>(nHexLen / 2);
2915
2916
2.89M
    return pabyWKB;
2917
2.89M
}
2918
2919
/************************************************************************/
2920
/*                          CPLGetValueType()                           */
2921
/************************************************************************/
2922
2923
/**
2924
 * Detect the type of the value contained in a string, whether it is
2925
 * a real, an integer or a string
2926
 * Leading and trailing spaces are skipped in the analysis.
2927
 *
2928
 * Note: in the context of this function, integer must be understood in a
2929
 * broad sense. It does not mean that the value can fit into a 32 bit integer
2930
 * for example. It might be larger.
2931
 *
2932
 * @param pszValue the string to analyze
2933
 *
2934
 * @return returns the type of the value contained in the string.
2935
 */
2936
2937
CPLValueType CPLGetValueType(const char *pszValue)
2938
19.8M
{
2939
    // Doubles : "+25.e+3", "-25.e-3", "25.e3", "25e3", " 25e3 "
2940
    // Not doubles: "25e 3", "25e.3", "-2-5e3", "2-5e3", "25.25.3", "-3d", "d1"
2941
    //              "XXeYYYYYYYYYYYYYYYYYYY" that evaluates to infinity
2942
2943
19.8M
    if (pszValue == nullptr)
2944
0
        return CPL_VALUE_STRING;
2945
2946
19.8M
    const char *pszValueInit = pszValue;
2947
2948
    // Skip leading spaces.
2949
19.9M
    while (isspace(static_cast<unsigned char>(*pszValue)))
2950
163k
        ++pszValue;
2951
2952
19.8M
    if (*pszValue == '\0')
2953
12.0M
        return CPL_VALUE_STRING;
2954
2955
    // Skip leading + or -.
2956
7.76M
    if (*pszValue == '+' || *pszValue == '-')
2957
1.01M
        ++pszValue;
2958
2959
7.76M
    constexpr char DIGIT_ZERO = '0';
2960
7.76M
    if (pszValue[0] == DIGIT_ZERO && pszValue[1] != '\0' && pszValue[1] != '.')
2961
228k
        return CPL_VALUE_STRING;
2962
2963
7.53M
    bool bFoundDot = false;
2964
7.53M
    bool bFoundExponent = false;
2965
7.53M
    bool bIsLastCharExponent = false;
2966
7.53M
    bool bIsReal = false;
2967
7.53M
    const char *pszAfterExponent = nullptr;
2968
7.53M
    bool bFoundMantissa = false;
2969
2970
67.6M
    for (; *pszValue != '\0'; ++pszValue)
2971
63.3M
    {
2972
63.3M
        if (isdigit(static_cast<unsigned char>(*pszValue)))
2973
57.6M
        {
2974
57.6M
            bIsLastCharExponent = false;
2975
57.6M
            bFoundMantissa = true;
2976
57.6M
        }
2977
5.70M
        else if (isspace(static_cast<unsigned char>(*pszValue)))
2978
96.2k
        {
2979
96.2k
            const char *pszTmp = pszValue;
2980
10.1M
            while (isspace(static_cast<unsigned char>(*pszTmp)))
2981
10.0M
                ++pszTmp;
2982
96.2k
            if (*pszTmp == 0)
2983
43.8k
                break;
2984
52.4k
            else
2985
52.4k
                return CPL_VALUE_STRING;
2986
96.2k
        }
2987
5.60M
        else if (*pszValue == '-' || *pszValue == '+')
2988
131k
        {
2989
131k
            if (bIsLastCharExponent)
2990
79.9k
            {
2991
                // Do nothing.
2992
79.9k
            }
2993
51.4k
            else
2994
51.4k
            {
2995
51.4k
                return CPL_VALUE_STRING;
2996
51.4k
            }
2997
79.9k
            bIsLastCharExponent = false;
2998
79.9k
        }
2999
5.47M
        else if (*pszValue == '.')
3000
2.08M
        {
3001
2.08M
            bIsReal = true;
3002
2.08M
            if (!bFoundDot && !bIsLastCharExponent)
3003
2.01M
                bFoundDot = true;
3004
65.2k
            else
3005
65.2k
                return CPL_VALUE_STRING;
3006
2.01M
            bIsLastCharExponent = false;
3007
2.01M
        }
3008
3.39M
        else if (*pszValue == 'D' || *pszValue == 'd' || *pszValue == 'E' ||
3009
2.95M
                 *pszValue == 'e')
3010
504k
        {
3011
504k
            if (!bFoundMantissa)
3012
162k
                return CPL_VALUE_STRING;
3013
342k
            if (!(pszValue[1] == '+' || pszValue[1] == '-' ||
3014
258k
                  isdigit(static_cast<unsigned char>(pszValue[1]))))
3015
37.1k
                return CPL_VALUE_STRING;
3016
3017
305k
            bIsReal = true;
3018
305k
            if (!bFoundExponent)
3019
288k
                bFoundExponent = true;
3020
17.1k
            else
3021
17.1k
                return CPL_VALUE_STRING;
3022
288k
            pszAfterExponent = pszValue + 1;
3023
288k
            bIsLastCharExponent = true;
3024
288k
        }
3025
2.88M
        else
3026
2.88M
        {
3027
2.88M
            return CPL_VALUE_STRING;
3028
2.88M
        }
3029
63.3M
    }
3030
3031
4.26M
    if (bIsReal && pszAfterExponent && strlen(pszAfterExponent) > 3)
3032
69.3k
    {
3033
        // cppcheck-suppress unreadVariable
3034
69.3k
        const double dfVal = CPLAtof(pszValueInit);
3035
69.3k
        if (std::isinf(dfVal))
3036
20.5k
            return CPL_VALUE_STRING;
3037
69.3k
    }
3038
3039
4.24M
    return bIsReal ? CPL_VALUE_REAL : CPL_VALUE_INTEGER;
3040
4.26M
}
3041
3042
/************************************************************************/
3043
/*                             CPLStrlcpy()                             */
3044
/************************************************************************/
3045
3046
/**
3047
 * Copy source string to a destination buffer.
3048
 *
3049
 * This function ensures that the destination buffer is always NUL terminated
3050
 * (provided that its length is at least 1).
3051
 *
3052
 * This function is designed to be a safer, more consistent, and less error
3053
 * prone replacement for strncpy. Its contract is identical to libbsd's strlcpy.
3054
 *
3055
 * Truncation can be detected by testing if the return value of CPLStrlcpy
3056
 * is greater or equal to nDestSize.
3057
3058
\verbatim
3059
char szDest[5] = {};
3060
if( CPLStrlcpy(szDest, "abcde", sizeof(szDest)) >= sizeof(szDest) )
3061
    fprintf(stderr, "truncation occurred !\n");
3062
\endverbatim
3063
3064
 * @param pszDest   destination buffer
3065
 * @param pszSrc    source string. Must be NUL terminated
3066
 * @param nDestSize size of destination buffer (including space for the NUL
3067
 *     terminator character)
3068
 *
3069
 * @return the length of the source string (=strlen(pszSrc))
3070
 *
3071
 */
3072
size_t CPLStrlcpy(char *pszDest, const char *pszSrc, size_t nDestSize)
3073
3.49M
{
3074
3.49M
    if (nDestSize == 0)
3075
0
        return strlen(pszSrc);
3076
3077
3.49M
    char *pszDestIter = pszDest;
3078
3.49M
    const char *pszSrcIter = pszSrc;
3079
3080
3.49M
    --nDestSize;
3081
35.3M
    while (nDestSize != 0 && *pszSrcIter != '\0')
3082
31.8M
    {
3083
31.8M
        *pszDestIter = *pszSrcIter;
3084
31.8M
        ++pszDestIter;
3085
31.8M
        ++pszSrcIter;
3086
31.8M
        --nDestSize;
3087
31.8M
    }
3088
3.49M
    *pszDestIter = '\0';
3089
3.49M
    return pszSrcIter - pszSrc + strlen(pszSrcIter);
3090
3.49M
}
3091
3092
/************************************************************************/
3093
/*                             CPLStrlcat()                             */
3094
/************************************************************************/
3095
3096
/**
3097
 * Appends a source string to a destination buffer.
3098
 *
3099
 * This function ensures that the destination buffer is always NUL terminated
3100
 * (provided that its length is at least 1 and that there is at least one byte
3101
 * free in pszDest, that is to say strlen(pszDest_before) < nDestSize)
3102
 *
3103
 * This function is designed to be a safer, more consistent, and less error
3104
 * prone replacement for strncat. Its contract is identical to libbsd's strlcat.
3105
 *
3106
 * Truncation can be detected by testing if the return value of CPLStrlcat
3107
 * is greater or equal to nDestSize.
3108
3109
\verbatim
3110
char szDest[5] = {};
3111
CPLStrlcpy(szDest, "ab", sizeof(szDest));
3112
if( CPLStrlcat(szDest, "cde", sizeof(szDest)) >= sizeof(szDest) )
3113
    fprintf(stderr, "truncation occurred !\n");
3114
\endverbatim
3115
3116
 * @param pszDest   destination buffer. Must be NUL terminated before
3117
 *         running CPLStrlcat
3118
 * @param pszSrc    source string. Must be NUL terminated
3119
 * @param nDestSize size of destination buffer (including space for the
3120
 *         NUL terminator character)
3121
 *
3122
 * @return the theoretical length of the destination string after concatenation
3123
 *         (=strlen(pszDest_before) + strlen(pszSrc)).
3124
 *         If strlen(pszDest_before) >= nDestSize, then it returns
3125
 *         nDestSize + strlen(pszSrc)
3126
 *
3127
 */
3128
size_t CPLStrlcat(char *pszDest, const char *pszSrc, size_t nDestSize)
3129
2.56k
{
3130
2.56k
    char *pszDestIter = pszDest;
3131
3132
2.56k
    while (nDestSize != 0 && *pszDestIter != '\0')
3133
0
    {
3134
0
        ++pszDestIter;
3135
0
        --nDestSize;
3136
0
    }
3137
3138
2.56k
    return pszDestIter - pszDest + CPLStrlcpy(pszDestIter, pszSrc, nDestSize);
3139
2.56k
}
3140
3141
/************************************************************************/
3142
/*                             CPLStrnlen()                             */
3143
/************************************************************************/
3144
3145
/**
3146
 * Returns the length of a NUL terminated string by reading at most
3147
 * the specified number of bytes.
3148
 *
3149
 * The CPLStrnlen() function returns min(strlen(pszStr), nMaxLen).
3150
 * Only the first nMaxLen bytes of the string will be read. Useful to
3151
 * test if a string contains at least nMaxLen characters without reading
3152
 * the full string up to the NUL terminating character.
3153
 *
3154
 * @param pszStr    a NUL terminated string
3155
 * @param nMaxLen   maximum number of bytes to read in pszStr
3156
 *
3157
 * @return strlen(pszStr) if the length is lesser than nMaxLen, otherwise
3158
 * nMaxLen if the NUL character has not been found in the first nMaxLen bytes.
3159
 *
3160
 */
3161
3162
size_t CPLStrnlen(const char *pszStr, size_t nMaxLen)
3163
34.3M
{
3164
34.3M
    size_t nLen = 0;
3165
1.37G
    while (nLen < nMaxLen && *pszStr != '\0')
3166
1.33G
    {
3167
1.33G
        ++nLen;
3168
1.33G
        ++pszStr;
3169
1.33G
    }
3170
34.3M
    return nLen;
3171
34.3M
}
3172
3173
/************************************************************************/
3174
/*                        CSLParseCommandLine()                         */
3175
/************************************************************************/
3176
3177
/**
3178
 * Tokenize command line arguments in a list of strings.
3179
 *
3180
 * @param pszCommandLine  command line
3181
 *
3182
 * @return NULL terminated list of strings to free with CSLDestroy()
3183
 *
3184
 */
3185
char **CSLParseCommandLine(const char *pszCommandLine)
3186
0
{
3187
0
    return CSLTokenizeString(pszCommandLine);
3188
0
}
3189
3190
/************************************************************************/
3191
/*                             CPLToupper()                             */
3192
/************************************************************************/
3193
3194
/** Converts a (ASCII) lowercase character to uppercase.
3195
 *
3196
 * Same as standard toupper(), except that it is not locale sensitive.
3197
 *
3198
 * @since GDAL 3.9
3199
 */
3200
int CPLToupper(int c)
3201
156M
{
3202
156M
    return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
3203
156M
}
3204
3205
/************************************************************************/
3206
/*                             CPLTolower()                             */
3207
/************************************************************************/
3208
3209
/** Converts a (ASCII) uppercase character to lowercase.
3210
 *
3211
 * Same as standard tolower(), except that it is not locale sensitive.
3212
 *
3213
 * @since GDAL 3.9
3214
 */
3215
int CPLTolower(int c)
3216
445M
{
3217
445M
    return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c;
3218
445M
}
3219
3220
/************************************************************************/
3221
/*                        CPLRemoveSQLComments()                        */
3222
/************************************************************************/
3223
3224
/** Remove SQL comments from a string
3225
 *
3226
 * @param osInput Input string.
3227
 * @since GDAL 3.11
3228
 */
3229
std::string CPLRemoveSQLComments(const std::string &osInput)
3230
57
{
3231
57
    const CPLStringList aosLines(
3232
57
        CSLTokenizeStringComplex(osInput.c_str(), "\r\n", FALSE, FALSE));
3233
57
    std::string osSQL;
3234
57
    for (const char *pszLine : aosLines)
3235
0
    {
3236
0
        char chQuote = 0;
3237
0
        int i = 0;
3238
0
        for (; pszLine[i] != '\0'; ++i)
3239
0
        {
3240
0
            if (chQuote)
3241
0
            {
3242
0
                if (pszLine[i] == chQuote)
3243
0
                {
3244
                    // Deal with escaped quote character which is repeated,
3245
                    // so 'foo''bar' or "foo""bar"
3246
0
                    if (pszLine[i + 1] == chQuote)
3247
0
                    {
3248
0
                        i++;
3249
0
                    }
3250
0
                    else
3251
0
                    {
3252
0
                        chQuote = 0;
3253
0
                    }
3254
0
                }
3255
0
            }
3256
0
            else if (pszLine[i] == '\'' || pszLine[i] == '"')
3257
0
            {
3258
0
                chQuote = pszLine[i];
3259
0
            }
3260
0
            else if (pszLine[i] == '-' && pszLine[i + 1] == '-')
3261
0
            {
3262
0
                break;
3263
0
            }
3264
0
        }
3265
0
        if (i > 0)
3266
0
        {
3267
0
            if (!osSQL.empty())
3268
0
                osSQL += ' ';
3269
0
            osSQL.append(pszLine, i);
3270
0
        }
3271
0
    }
3272
57
    return osSQL;
3273
57
}
3274
3275
namespace cpl
3276
{
3277
3278
static bool CaseInsensitiveCompare(unsigned char c1, unsigned char c2)
3279
7.08G
{
3280
7.08G
    return toupper(c1) == toupper(c2);
3281
7.08G
}
3282
3283
/** Check whether the start of one string is equivalent to another string,
3284
 *  considering case.
3285
 *
3286
 * @param str string to test
3287
 * @param prefix expected prefix
3288
 * @return true if the string starts with the prefix
3289
 *
3290
 * @since GDAL 3.11
3291
 */
3292
bool starts_with(std::string_view str, std::string_view prefix)
3293
31.8M
{
3294
31.8M
    return str.size() >= prefix.size() &&
3295
17.9M
           str.compare(0, prefix.size(), prefix) == 0;
3296
31.8M
}
3297
3298
/** Check whether the start of one string is equivalent to another string,
3299
 *  not considering case.
3300
 *
3301
 * @param str string to test
3302
 * @param prefix expected prefix
3303
 * @return true if the string starts with the prefix
3304
 *
3305
 * @since GDAL 3.14
3306
 */
3307
bool starts_with_ci(std::string_view str, std::string_view prefix)
3308
128M
{
3309
128M
    return str.size() >= prefix.size() &&
3310
100M
           std::search(str.begin(), str.end(), prefix.begin(), prefix.end(),
3311
100M
                       CaseInsensitiveCompare) != str.end();
3312
128M
}
3313
3314
/** Check whether the end of one string is equivalent to another string,
3315
 *  considering case.
3316
 *
3317
 * @param str string to test
3318
 * @param suffix expected suffix
3319
 * @return true if the string ends with the suffix
3320
 *
3321
 * @since GDAL 3.11
3322
 */
3323
bool ends_with(std::string_view str, std::string_view suffix)
3324
1.98M
{
3325
1.98M
    return str.size() >= suffix.size() &&
3326
1.92M
           (suffix.empty() || str.compare(str.size() - suffix.size(),
3327
1.92M
                                          suffix.size(), suffix) == 0);
3328
1.98M
}
3329
3330
/** Check whether the end of one string is equivalent to another string,
3331
 *  not considering case.
3332
 *
3333
 * @param str string to test
3334
 * @param suffix expected suffix
3335
 * @return true if the string ends with the suffix
3336
 *
3337
 * @since GDAL 3.14
3338
 */
3339
bool ends_with_ci(std::string_view str, std::string_view suffix)
3340
0
{
3341
0
    return str.size() >= suffix.size() &&
3342
0
           (suffix.empty() ||
3343
0
            std::search(str.end() - suffix.size(), str.end(), suffix.begin(),
3344
0
                        suffix.end(), CaseInsensitiveCompare) != str.end());
3345
0
}
3346
3347
/** Check whether two strings are equal, considering case.
3348
 *
3349
 * @param str1 first string to test
3350
 * @param str2 second string to test
3351
 * @return true if the strings are considered equal
3352
 *
3353
 * @since GDAL 3.14
3354
 */
3355
bool equals(std::string_view str1, std::string_view str2)
3356
0
{
3357
0
    return str1 == str2;
3358
0
}
3359
3360
/** Check whether two strings are equal, not considering case.
3361
 *
3362
 * @param str1 first string to test
3363
 * @param str2 second string to test
3364
 * @return true if the strings are considered equal
3365
 *
3366
 * @since GDAL 3.14
3367
 */
3368
bool equals_ci(std::string_view str1, std::string_view str2)
3369
0
{
3370
0
    return str1.size() == str2.size() &&
3371
0
           std::equal(str1.begin(), str1.end(), str2.begin(),
3372
0
                      CaseInsensitiveCompare);
3373
0
}
3374
3375
/** Remove leading and trailing whitespace from a string.
3376
 *  The returned string view will be a reference into the input.
3377
 *
3378
 * @param str string to trim
3379
 * @return trimmed string
3380
 *
3381
 * @since GDAL 3.14
3382
 */
3383
std::string_view trim(std::string_view str)
3384
72.5M
{
3385
72.5M
    if (str.empty())
3386
4.07M
    {
3387
4.07M
        return str;
3388
4.07M
    }
3389
3390
68.5M
    size_t start = 0;
3391
94.6M
    while (start < str.size() &&
3392
93.9M
           isspace(static_cast<unsigned char>(str[start])))
3393
26.1M
    {
3394
26.1M
        start++;
3395
26.1M
    }
3396
3397
68.5M
    if (start == str.size())
3398
783k
    {
3399
783k
        return str.substr(start, 0);
3400
783k
    }
3401
3402
67.7M
    size_t stop = str.size();
3403
76.1M
    while (stop > start && isspace(static_cast<unsigned char>(str[stop - 1])))
3404
8.45M
    {
3405
8.45M
        stop--;
3406
8.45M
    }
3407
3408
67.7M
    return str.substr(start, stop - start);
3409
68.5M
}
3410
3411
std::string_view trim(const char *pszStr)
3412
0
{
3413
0
    return trim(std::string_view(pszStr));
3414
0
}
3415
3416
/** Remove leading whitespace from a string.
3417
 *  The returned string view will be a reference into the input.
3418
 *
3419
 * @param str string to trim
3420
 * @return trimmed string
3421
 *
3422
 * @since GDAL 3.14
3423
 */
3424
std::string_view ltrim(std::string_view str)
3425
5.82M
{
3426
5.82M
    if (str.empty())
3427
871k
    {
3428
871k
        return str;
3429
871k
    }
3430
3431
4.95M
    size_t start = 0;
3432
5.23M
    while (start < str.size() &&
3433
5.22M
           isspace(static_cast<unsigned char>(str[start])))
3434
275k
    {
3435
275k
        start++;
3436
275k
    }
3437
3438
4.95M
    return str.substr(start);
3439
5.82M
}
3440
3441
std::string_view ltrim(const char *pszStr)
3442
0
{
3443
0
    return ltrim(std::string_view(pszStr));
3444
0
}
3445
3446
/** Remove trailing whitespace from a string.
3447
 *  The returned string view will be a reference into the input.
3448
 *
3449
 * @param str string to trim
3450
 * @return trimmed string
3451
 *
3452
 * @since GDAL 3.14
3453
 */
3454
std::string_view rtrim(std::string_view str)
3455
5.82M
{
3456
5.82M
    if (str.empty())
3457
876k
    {
3458
876k
        return str;
3459
876k
    }
3460
3461
4.95M
    size_t stop = str.size();
3462
5.15M
    while (stop > 0 && isspace(static_cast<unsigned char>(str[stop - 1])))
3463
202k
    {
3464
202k
        stop--;
3465
202k
    }
3466
3467
4.95M
    return str.substr(0, stop);
3468
5.82M
}
3469
3470
std::string_view rtrim(const char *pszStr)
3471
0
{
3472
0
    return rtrim(std::string_view(pszStr));
3473
0
}
3474
3475
}  // namespace cpl