Coverage Report

Created: 2025-11-16 06:25

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