Coverage Report

Created: 2025-12-31 06:48

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