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