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