Coverage Report

Created: 2026-04-01 06:20

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
2.88k
{
70
2.88k
    char **papszRet = CSLAddStringMayFail(papszStrList, pszNewString);
71
2.88k
    if (papszRet == nullptr && pszNewString != nullptr)
72
0
        abort();
73
2.88k
    return papszRet;
74
2.88k
}
75
76
/** Same as CSLAddString() but may return NULL in case of (memory) failure */
77
char **CSLAddStringMayFail(char **papszStrList, const char *pszNewString)
78
2.88k
{
79
2.88k
    if (pszNewString == nullptr)
80
0
        return papszStrList;  // Nothing to do!
81
82
2.88k
    char *pszDup = VSI_STRDUP_VERBOSE(pszNewString);
83
2.88k
    if (pszDup == nullptr)
84
0
        return nullptr;
85
86
    // Allocate room for the new string.
87
2.88k
    char **papszStrListNew = nullptr;
88
2.88k
    int nItems = 0;
89
90
2.88k
    if (papszStrList == nullptr)
91
16
        papszStrListNew =
92
16
            static_cast<char **>(VSI_CALLOC_VERBOSE(2, sizeof(char *)));
93
2.87k
    else
94
2.87k
    {
95
2.87k
        nItems = CSLCount(papszStrList);
96
2.87k
        papszStrListNew = static_cast<char **>(
97
2.87k
            VSI_REALLOC_VERBOSE(papszStrList, (nItems + 2) * sizeof(char *)));
98
2.87k
    }
99
2.88k
    if (papszStrListNew == nullptr)
100
0
    {
101
0
        VSIFree(pszDup);
102
0
        return nullptr;
103
0
    }
104
105
    // Copy the string in the list.
106
2.88k
    papszStrListNew[nItems] = pszDup;
107
2.88k
    papszStrListNew[nItems + 1] = nullptr;
108
109
2.88k
    return papszStrListNew;
110
2.88k
}
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
8.79k
{
134
8.79k
    if (!papszStrList)
135
0
        return 0;
136
137
8.79k
    int nItems = 0;
138
139
2.30M
    while (*papszStrList != nullptr)
140
2.29M
    {
141
2.29M
        ++nItems;
142
2.29M
        ++papszStrList;
143
2.29M
    }
144
145
8.79k
    return nItems;
146
8.79k
}
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
52.7k
{
187
52.7k
    if (!papszStrList)
188
39.0k
        return;
189
190
67.5k
    for (char **papszPtr = papszStrList; *papszPtr != nullptr; ++papszPtr)
191
53.7k
    {
192
53.7k
        CPLFree(*papszPtr);
193
53.7k
    }
194
195
13.7k
    CPLFree(papszStrList);
196
13.7k
}
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.39k
{
215
1.39k
    const int nLines = CSLCount(papszStrList);
216
217
1.39k
    if (nLines == 0)
218
177
        return nullptr;
219
220
1.21k
    CSLConstList papszSrc = papszStrList;
221
222
1.21k
    char **papszNewList =
223
1.21k
        static_cast<char **>(VSI_MALLOC2_VERBOSE(nLines + 1, sizeof(char *)));
224
225
1.21k
    char **papszDst = papszNewList;
226
227
7.82k
    for (; *papszSrc != nullptr; ++papszSrc, ++papszDst)
228
6.60k
    {
229
6.60k
        *papszDst = VSI_STRDUP_VERBOSE(*papszSrc);
230
6.60k
        if (*papszDst == nullptr)
231
0
        {
232
0
            CSLDestroy(papszNewList);
233
0
            return nullptr;
234
0
        }
235
6.60k
    }
236
1.21k
    *papszDst = nullptr;
237
238
1.21k
    return papszNewList;
239
1.21k
}
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
12.4k
{
820
12.4k
    if (pszString == nullptr)
821
0
        return static_cast<char **>(CPLCalloc(sizeof(char *), 1));
822
823
12.4k
    CPLStringList oRetList;
824
12.4k
    const bool bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS) != 0;
825
12.4k
    const bool bAllowEmptyTokens = (nCSLTFlags & CSLT_ALLOWEMPTYTOKENS) != 0;
826
12.4k
    const bool bStripLeadSpaces = (nCSLTFlags & CSLT_STRIPLEADSPACES) != 0;
827
12.4k
    const bool bStripEndSpaces = (nCSLTFlags & CSLT_STRIPENDSPACES) != 0;
828
829
12.4k
    char *pszToken = static_cast<char *>(CPLCalloc(10, 1));
830
12.4k
    size_t nTokenMax = 10;
831
832
67.8k
    while (*pszString != '\0')
833
55.3k
    {
834
55.3k
        bool bInString = false;
835
55.3k
        bool bStartString = true;
836
55.3k
        size_t nTokenLen = 0;
837
838
        // Try to find the next delimiter, marking end of token.
839
1.31M
        for (; *pszString != '\0'; ++pszString)
840
1.30M
        {
841
            // Extend token buffer if we are running close to its end.
842
1.30M
            if (nTokenLen >= nTokenMax - 3)
843
26.1k
            {
844
26.1k
                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
26.1k
                nTokenMax = nTokenMax * 2;
850
26.1k
                char *pszNewToken = static_cast<char *>(
851
26.1k
                    VSI_REALLOC_VERBOSE(pszToken, nTokenMax));
852
26.1k
                if (pszNewToken == nullptr)
853
0
                {
854
0
                    CPLFree(pszToken);
855
0
                    return static_cast<char **>(CPLCalloc(sizeof(char *), 1));
856
0
                }
857
26.1k
                pszToken = pszNewToken;
858
26.1k
            }
859
860
            // End if this is a delimiter skip it and break.
861
1.30M
            if (!bInString && strchr(pszDelimiters, *pszString) != nullptr)
862
44.9k
            {
863
44.9k
                ++pszString;
864
44.9k
                break;
865
44.9k
            }
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
1.26M
            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
1.26M
            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
1.26M
            if (!bInString && bStripLeadSpaces && bStartString &&
903
0
                isspace(static_cast<unsigned char>(*pszString)))
904
0
                continue;
905
906
1.26M
            bStartString = false;
907
908
1.26M
            pszToken[nTokenLen] = *pszString;
909
1.26M
            ++nTokenLen;
910
1.26M
        }
911
912
        // Strip spaces at the token end if requested.
913
55.3k
        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
55.3k
        pszToken[nTokenLen] = '\0';
921
922
        // Add the token.
923
55.3k
        if (pszToken[0] != '\0' || bAllowEmptyTokens)
924
47.1k
            oRetList.AddString(pszToken);
925
55.3k
    }
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
12.4k
    if (*pszString == '\0' && bAllowEmptyTokens && oRetList.Count() > 0 &&
932
0
        strchr(pszDelimiters, *(pszString - 1)) != nullptr)
933
0
    {
934
0
        oRetList.AddString("");
935
0
    }
936
937
12.4k
    CPLFree(pszToken);
938
939
12.4k
    if (oRetList.List() == nullptr)
940
1.97k
    {
941
        // Prefer to return empty lists as a pointer to
942
        // a null pointer since some client code might depend on this.
943
1.97k
        oRetList.Assign(static_cast<char **>(CPLCalloc(sizeof(char *), 1)));
944
1.97k
    }
945
946
12.4k
    return oRetList.StealList();
947
12.4k
}
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
17.6k
{
967
17.6k
    va_list args;
968
969
    /* -------------------------------------------------------------------- */
970
    /*      Get the thread local buffer ring data.                          */
971
    /* -------------------------------------------------------------------- */
972
17.6k
    char *pachBufRingInfo = static_cast<char *>(CPLGetTLS(CTLS_CPLSPRINTF));
973
974
17.6k
    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
17.6k
    int *pnBufIndex = reinterpret_cast<int *>(pachBufRingInfo);
986
17.6k
    const size_t nOffset = sizeof(int) + *pnBufIndex * CPLSPrintf_BUF_SIZE;
987
17.6k
    char *pachBuffer = pachBufRingInfo + nOffset;
988
989
17.6k
    *pnBufIndex = (*pnBufIndex + 1) % CPLSPrintf_BUF_Count;
990
991
    /* -------------------------------------------------------------------- */
992
    /*      Format the result.                                              */
993
    /* -------------------------------------------------------------------- */
994
995
17.6k
    va_start(args, fmt);
996
997
17.6k
    const int ret =
998
17.6k
        CPLvsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE - 1, fmt, args);
999
17.6k
    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
17.6k
    va_end(args);
1007
1008
17.6k
    return pachBuffer;
1009
17.6k
}
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
87.1k
{
1056
87.1k
    char ch = '\0';
1057
    // Flag.
1058
87.5k
    for (; (ch = *fmt) != '\0'; ++fmt)
1059
87.5k
    {
1060
87.5k
        if (ch == '\'')
1061
0
            continue;  // Bad idea as this is locale specific.
1062
87.5k
        if (ch == '-' || ch == '+' || ch == ' ' || ch == '#' || ch == '0')
1063
406
            continue;
1064
87.1k
        break;
1065
87.5k
    }
1066
1067
    // Field width.
1068
87.5k
    for (; (ch = *fmt) != '\0'; ++fmt)
1069
87.5k
    {
1070
87.5k
        if (ch == '$')
1071
0
            return nullptr;  // Do not support this.
1072
87.5k
        if (*fmt >= '0' && *fmt <= '9')
1073
406
            continue;
1074
87.1k
        break;
1075
87.5k
    }
1076
1077
    // Precision.
1078
87.1k
    if (ch == '.')
1079
203
    {
1080
203
        ++fmt;
1081
609
        for (; (ch = *fmt) != '\0'; ++fmt)
1082
609
        {
1083
609
            if (ch == '$')
1084
0
                return nullptr;  // Do not support this.
1085
609
            if (*fmt >= '0' && *fmt <= '9')
1086
406
                continue;
1087
203
            break;
1088
609
        }
1089
203
    }
1090
1091
    // Length modifier.
1092
87.8k
    for (; (ch = *fmt) != '\0'; ++fmt)
1093
87.8k
    {
1094
87.8k
        if (ch == 'h' || ch == 'l' || ch == 'j' || ch == 'z' || ch == 't' ||
1095
87.1k
            ch == 'L')
1096
664
            continue;
1097
87.1k
        else if (ch == 'I' && fmt[1] == '6' && fmt[2] == '4')
1098
0
            fmt += 2;
1099
87.1k
        else
1100
87.1k
            return fmt;
1101
87.8k
    }
1102
1103
0
    return nullptr;
1104
87.1k
}
1105
1106
/************************************************************************/
1107
/*                            CPLvsnprintf()                            */
1108
/************************************************************************/
1109
1110
#define call_native_snprintf(type)                                             \
1111
35.6k
    local_ret = snprintf(str + offset_out, size - offset_out, localfmt,        \
1112
35.6k
                         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
53.2k
{
1131
53.2k
    if (size == 0)
1132
0
        return vsnprintf(str, size, fmt, args);
1133
1134
53.2k
    va_list wrk_args;
1135
1136
53.2k
#ifdef va_copy
1137
53.2k
    va_copy(wrk_args, args);
1138
#else
1139
    wrk_args = args;
1140
#endif
1141
1142
53.2k
    const char *fmt_ori = fmt;
1143
53.2k
    size_t offset_out = 0;
1144
53.2k
    char ch = '\0';
1145
53.2k
    bool bFormatUnknown = false;
1146
1147
700k
    for (; (ch = *fmt) != '\0'; ++fmt)
1148
647k
    {
1149
647k
        if (ch == '%')
1150
87.1k
        {
1151
87.1k
            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
87.1k
            const char *ptrend = CPLvsnprintf_get_end_of_formatting(fmt + 1);
1175
87.1k
            if (ptrend == nullptr || ptrend - fmt >= 20)
1176
0
            {
1177
0
                bFormatUnknown = true;
1178
0
                break;
1179
0
            }
1180
87.1k
            char end = *ptrend;
1181
87.1k
            char end_m1 = ptrend[-1];
1182
1183
87.1k
            char localfmt[22] = {};
1184
87.1k
            memcpy(localfmt, fmt, ptrend - fmt + 1);
1185
87.1k
            localfmt[ptrend - fmt + 1] = '\0';
1186
1187
87.1k
            int local_ret = 0;
1188
87.1k
            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
87.1k
            else if (end == 'd' || end == 'i' || end == 'c')
1196
35.0k
            {
1197
35.0k
                if (end_m1 == 'h')
1198
0
                    call_native_snprintf(int);
1199
35.0k
                else if (end_m1 == 'l' && ptrend[-2] != 'l')
1200
0
                    call_native_snprintf(long);
1201
35.0k
                else if (end_m1 == 'l' && ptrend[-2] == 'l')
1202
0
                    call_native_snprintf(GIntBig);
1203
35.0k
                else if (end_m1 == '4' && ptrend[-2] == '6' &&
1204
0
                         ptrend[-3] == 'I')
1205
                    // Microsoft I64 modifier.
1206
0
                    call_native_snprintf(GIntBig);
1207
35.0k
                else if (end_m1 == 'z')
1208
0
                    call_native_snprintf(size_t);
1209
35.0k
                else if ((end_m1 >= 'a' && end_m1 <= 'z') ||
1210
35.0k
                         (end_m1 >= 'A' && end_m1 <= 'Z'))
1211
0
                {
1212
0
                    bFormatUnknown = true;
1213
0
                    break;
1214
0
                }
1215
35.0k
                else
1216
35.0k
                    call_native_snprintf(int);
1217
35.0k
            }
1218
52.0k
            else if (end == 'o' || end == 'u' || end == 'x' || end == 'X')
1219
332
            {
1220
332
                if (end_m1 == 'h')
1221
0
                    call_native_snprintf(unsigned int);
1222
332
                else if (end_m1 == 'l' && ptrend[-2] != 'l')
1223
0
                    call_native_snprintf(unsigned long);
1224
332
                else if (end_m1 == 'l' && ptrend[-2] == 'l')
1225
332
                    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
332
            }
1241
51.7k
            else if (end == 'e' || end == 'E' || end == 'f' || end == 'F' ||
1242
51.5k
                     end == 'g' || end == 'G' || end == 'a' || end == 'A')
1243
203
            {
1244
203
                if (end_m1 == 'L')
1245
0
                    call_native_snprintf(long double);
1246
203
                else
1247
203
                    call_native_snprintf(double);
1248
                // MSVC vsnprintf() returns -1.
1249
203
                if (local_ret < 0 || offset_out + local_ret >= size)
1250
0
                    break;
1251
1.42k
                for (int j = 0; j < local_ret; ++j)
1252
1.21k
                {
1253
1.21k
                    if (str[offset_out + j] == ',')
1254
0
                    {
1255
0
                        str[offset_out + j] = '.';
1256
0
                        break;
1257
0
                    }
1258
1.21k
                }
1259
203
            }
1260
51.5k
            else if (end == 's')
1261
51.5k
            {
1262
51.5k
                const char *pszPtr = va_arg(wrk_args, const char *);
1263
51.5k
                CPLAssert(pszPtr);
1264
51.5k
                local_ret = snprintf(str + offset_out, size - offset_out,
1265
51.5k
                                     localfmt, pszPtr);
1266
51.5k
            }
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
87.1k
            if (local_ret < 0 || offset_out + local_ret >= size)
1278
3
                break;
1279
87.1k
            offset_out += local_ret;
1280
87.1k
            fmt = ptrend;
1281
87.1k
        }
1282
560k
        else
1283
560k
        {
1284
560k
            if (offset_out == size - 1)
1285
0
                break;
1286
560k
            str[offset_out++] = *fmt;
1287
560k
        }
1288
647k
    }
1289
53.2k
    if (ch == '\0' && offset_out < size)
1290
53.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
53.2k
#ifdef va_copy
1314
53.2k
    va_end(wrk_args);
1315
53.2k
#endif
1316
1317
53.2k
    return static_cast<int>(offset_out);
1318
53.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
203
{
1349
203
    va_list args;
1350
1351
203
    va_start(args, fmt);
1352
203
    const int ret = CPLvsnprintf(str, size, fmt, args);
1353
203
    va_end(args);
1354
203
    return ret;
1355
203
}
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
9.03k
{
1537
9.03k
    return !(EQUAL(pszValue, "NO") || EQUAL(pszValue, "FALSE") ||
1538
7.39k
             EQUAL(pszValue, "OFF") || EQUAL(pszValue, "0"));
1539
9.03k
}
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
158k
{
1680
158k
    if (papszStrList == nullptr || pszName == nullptr)
1681
1.00k
        return nullptr;
1682
1683
157k
    const size_t nLen = strlen(pszName);
1684
53.1M
    while (*papszStrList != nullptr)
1685
53.0M
    {
1686
53.0M
        if (EQUALN(*papszStrList, pszName, nLen) &&
1687
79.3k
            ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
1688
40.5k
        {
1689
40.5k
            return (*papszStrList) + nLen + 1;
1690
40.5k
        }
1691
53.0M
        ++papszStrList;
1692
53.0M
    }
1693
116k
    return nullptr;
1694
157k
}
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
1.20k
{
1754
1.20k
    const char *start = pszValue;
1755
1.20k
    char *end = nullptr;
1756
1757
    // trim leading whitespace
1758
1.20k
    while (*start == ' ')
1759
0
    {
1760
0
        start++;
1761
0
    }
1762
1763
1.20k
    auto len = CPLStrnlen(start, 100);
1764
1.20k
    double value = CPLStrtodM(start, &end);
1765
1.20k
    const char *unit = nullptr;
1766
1.20k
    bool unitIsNotPercent = false;
1767
1768
1.20k
    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
1.20k
    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
1.20k
    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
1.20k
    if (value > static_cast<double>(std::numeric_limits<GIntBig>::max()) ||
1846
1.20k
        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
1.20k
    *pnValue = static_cast<GIntBig>(value);
1854
1.20k
    if (pbUnitSpecified)
1855
1.20k
    {
1856
1.20k
        *pbUnitSpecified = (unit != nullptr);
1857
1.20k
    }
1858
1.20k
    return CE_None;
1859
1.20k
}
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
38.4k
{
1888
637k
    for (int i = 0; pszNameValue[i] != '\0'; ++i)
1889
614k
    {
1890
614k
        if (pszNameValue[i] == '=' || pszNameValue[i] == ':')
1891
15.6k
        {
1892
15.6k
            const char *pszValue = pszNameValue + i + 1;
1893
17.6k
            while (*pszValue == ' ' || *pszValue == '\t')
1894
2.02k
                ++pszValue;
1895
1896
15.6k
            if (ppszKey != nullptr)
1897
15.6k
            {
1898
15.6k
                *ppszKey = static_cast<char *>(CPLMalloc(i + 1));
1899
15.6k
                memcpy(*ppszKey, pszNameValue, i);
1900
15.6k
                (*ppszKey)[i] = '\0';
1901
16.6k
                while (i > 0 &&
1902
15.7k
                       ((*ppszKey)[i - 1] == ' ' || (*ppszKey)[i - 1] == '\t'))
1903
1.07k
                {
1904
1.07k
                    (*ppszKey)[i - 1] = '\0';
1905
1.07k
                    i--;
1906
1.07k
                }
1907
15.6k
            }
1908
1909
15.6k
            return pszValue;
1910
15.6k
        }
1911
614k
    }
1912
1913
22.8k
    return nullptr;
1914
38.4k
}
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
2.87k
{
2019
2.87k
    if (pszName == nullptr || pszValue == nullptr)
2020
0
        return papszStrList;
2021
2022
2.87k
    const size_t nLen = strlen(pszName) + strlen(pszValue) + 2;
2023
2.87k
    char *pszLine = static_cast<char *>(CPLMalloc(nLen));
2024
2.87k
    snprintf(pszLine, nLen, "%s=%s", pszName, pszValue);
2025
2.87k
    papszStrList = CSLAddString(papszStrList, pszLine);
2026
2.87k
    CPLFree(pszLine);
2027
2028
2.87k
    return papszStrList;
2029
2.87k
}
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
9.43k
{
2058
9.43k
    if (pszName == nullptr)
2059
0
        return papszList;
2060
2061
9.43k
    size_t nLen = strlen(pszName);
2062
11.9k
    while (nLen > 0 && pszName[nLen - 1] == ' ')
2063
2.50k
        nLen--;
2064
9.43k
    char **papszPtr = papszList;
2065
4.68M
    while (papszPtr && *papszPtr != nullptr)
2066
4.68M
    {
2067
4.68M
        if (EQUALN(*papszPtr, pszName, nLen))
2068
19.2k
        {
2069
19.2k
            size_t i;
2070
21.1k
            for (i = nLen; (*papszPtr)[i] == ' '; ++i)
2071
1.87k
            {
2072
1.87k
            }
2073
19.2k
            if ((*papszPtr)[i] == '=' || (*papszPtr)[i] == ':')
2074
6.56k
            {
2075
                // Found it.
2076
                // Change the value... make sure to keep the ':' or '='.
2077
6.56k
                const char cSep = (*papszPtr)[i];
2078
2079
6.56k
                CPLFree(*papszPtr);
2080
2081
                // If the value is NULL, remove this entry completely.
2082
6.56k
                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
6.56k
                else
2094
6.56k
                {
2095
6.56k
                    const size_t nLen2 = strlen(pszName) + strlen(pszValue) + 2;
2096
6.56k
                    *papszPtr = static_cast<char *>(CPLMalloc(nLen2));
2097
6.56k
                    snprintf(*papszPtr, nLen2, "%s%c%s", pszName, cSep,
2098
6.56k
                             pszValue);
2099
6.56k
                }
2100
6.56k
                return papszList;
2101
6.56k
            }
2102
19.2k
        }
2103
4.67M
        ++papszPtr;
2104
4.67M
    }
2105
2106
2.87k
    if (pszValue == nullptr)
2107
0
        return papszList;
2108
2109
    // The name does not exist yet.  Create a new entry.
2110
2.87k
    return CSLAddNameValue(papszList, pszName, pszValue);
2111
2.87k
}
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
38.4k
{
2568
38.4k
    int iOut = 0;
2569
2570
    // TODO: Why times 4?
2571
38.4k
    char *pszOutput = static_cast<char *>(CPLMalloc(4 * strlen(pszInput) + 1));
2572
38.4k
    pszOutput[0] = '\0';
2573
2574
38.4k
    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
38.4k
    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
38.4k
    else if (nScheme == CPLES_URL)
2699
38.4k
    {
2700
1.13M
        for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
2701
1.09M
        {
2702
1.09M
            if (pszInput[iIn] == '%' && pszInput[iIn + 1] != '\0' &&
2703
20.2k
                pszInput[iIn + 2] != '\0')
2704
19.3k
            {
2705
19.3k
                int nHexChar = 0;
2706
2707
19.3k
                if (pszInput[iIn + 1] >= 'A' && pszInput[iIn + 1] <= 'F')
2708
980
                    nHexChar += 16 * (pszInput[iIn + 1] - 'A' + 10);
2709
18.4k
                else if (pszInput[iIn + 1] >= 'a' && pszInput[iIn + 1] <= 'f')
2710
2.04k
                    nHexChar += 16 * (pszInput[iIn + 1] - 'a' + 10);
2711
16.3k
                else if (pszInput[iIn + 1] >= '0' && pszInput[iIn + 1] <= '9')
2712
2.65k
                    nHexChar += 16 * (pszInput[iIn + 1] - '0');
2713
13.7k
                else
2714
13.7k
                    CPLDebug("CPL",
2715
13.7k
                             "Error unescaping CPLES_URL text, percent not "
2716
13.7k
                             "followed by two hex digits.");
2717
2718
19.3k
                if (pszInput[iIn + 2] >= 'A' && pszInput[iIn + 2] <= 'F')
2719
1.66k
                    nHexChar += pszInput[iIn + 2] - 'A' + 10;
2720
17.7k
                else if (pszInput[iIn + 2] >= 'a' && pszInput[iIn + 2] <= 'f')
2721
3.08k
                    nHexChar += pszInput[iIn + 2] - 'a' + 10;
2722
14.6k
                else if (pszInput[iIn + 2] >= '0' && pszInput[iIn + 2] <= '9')
2723
1.42k
                    nHexChar += pszInput[iIn + 2] - '0';
2724
13.2k
                else
2725
13.2k
                    CPLDebug("CPL",
2726
13.2k
                             "Error unescaping CPLES_URL text, percent not "
2727
13.2k
                             "followed by two hex digits.");
2728
2729
19.3k
                pszOutput[iOut++] = static_cast<char>(nHexChar);
2730
19.3k
                iIn += 2;
2731
19.3k
            }
2732
1.08M
            else if (pszInput[iIn] == '+')
2733
9.30k
            {
2734
9.30k
                pszOutput[iOut++] = ' ';
2735
9.30k
            }
2736
1.07M
            else
2737
1.07M
            {
2738
1.07M
                pszOutput[iOut++] = pszInput[iIn];
2739
1.07M
            }
2740
1.09M
        }
2741
38.4k
    }
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
38.4k
    pszOutput[iOut] = '\0';
2769
2770
38.4k
    if (pnLength != nullptr)
2771
0
        *pnLength = iOut;
2772
2773
38.4k
    return pszOutput;
2774
38.4k
}
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
13.2k
{
3116
13.2k
    size_t nLen = 0;
3117
2.15M
    while (nLen < nMaxLen && *pszStr != '\0')
3118
2.14M
    {
3119
2.14M
        ++nLen;
3120
2.14M
        ++pszStr;
3121
2.14M
    }
3122
13.2k
    return nLen;
3123
13.2k
}
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
194k
{
3154
194k
    return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
3155
194k
}
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
}