Coverage Report

Created: 2026-10-28 07:09

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