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 404058 : char **CSLAddString(char **papszStrList, const char *pszNewString)
70 : {
71 404058 : char **papszRet = CSLAddStringMayFail(papszStrList, pszNewString);
72 404030 : if (papszRet == nullptr && pszNewString != nullptr)
73 0 : abort();
74 404030 : return papszRet;
75 : }
76 :
77 : /** Same as CSLAddString() but may return NULL in case of (memory) failure */
78 466226 : char **CSLAddStringMayFail(char **papszStrList, const char *pszNewString)
79 : {
80 466226 : if (pszNewString == nullptr)
81 131 : return papszStrList; // Nothing to do!
82 :
83 466095 : char *pszDup = VSI_STRDUP_VERBOSE(pszNewString);
84 466073 : if (pszDup == nullptr)
85 0 : return nullptr;
86 :
87 : // Allocate room for the new string.
88 466073 : char **papszStrListNew = nullptr;
89 466073 : int nItems = 0;
90 :
91 466073 : if (papszStrList == nullptr)
92 : papszStrListNew =
93 82016 : static_cast<char **>(VSI_CALLOC_VERBOSE(2, sizeof(char *)));
94 : else
95 : {
96 384057 : nItems = CSLCount(papszStrList);
97 : papszStrListNew = static_cast<char **>(
98 384066 : VSI_REALLOC_VERBOSE(papszStrList, (nItems + 2) * sizeof(char *)));
99 : }
100 466066 : if (papszStrListNew == nullptr)
101 : {
102 0 : VSIFree(pszDup);
103 0 : return nullptr;
104 : }
105 :
106 : // Copy the string in the list.
107 466066 : papszStrListNew[nItems] = pszDup;
108 466066 : papszStrListNew[nItems + 1] = nullptr;
109 :
110 466066 : 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 5253630 : int CSLCount(CSLConstList papszStrList)
134 : {
135 5253630 : if (!papszStrList)
136 3660590 : return 0;
137 :
138 1593040 : int nItems = 0;
139 :
140 12122000 : while (*papszStrList != nullptr)
141 : {
142 10529000 : ++nItems;
143 10529000 : ++papszStrList;
144 : }
145 :
146 1593040 : 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 15723800 : void CPL_STDCALL CSLDestroy(char **papszStrList)
187 : {
188 15723800 : if (!papszStrList)
189 12166800 : return;
190 :
191 16906100 : for (char **papszPtr = papszStrList; *papszPtr != nullptr; ++papszPtr)
192 : {
193 13349100 : CPLFree(*papszPtr);
194 : }
195 :
196 3557000 : 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 3697100 : char **CSLDuplicate(CSLConstList papszStrList)
215 : {
216 3697100 : const int nLines = CSLCount(papszStrList);
217 :
218 3689040 : if (nLines == 0)
219 3612560 : return nullptr;
220 :
221 76478 : CSLConstList papszSrc = papszStrList;
222 :
223 : char **papszNewList =
224 76478 : static_cast<char **>(VSI_MALLOC2_VERBOSE(nLines + 1, sizeof(char *)));
225 :
226 83159 : char **papszDst = papszNewList;
227 :
228 535100 : for (; *papszSrc != nullptr; ++papszSrc, ++papszDst)
229 : {
230 451943 : *papszDst = VSI_STRDUP_VERBOSE(*papszSrc);
231 451944 : if (*papszDst == nullptr)
232 : {
233 3 : CSLDestroy(papszNewList);
234 0 : return nullptr;
235 : }
236 : }
237 83157 : *papszDst = nullptr;
238 :
239 83157 : 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 756175 : char **CSLMerge(char **papszOrig, CSLConstList papszOverride)
260 :
261 : {
262 756175 : if (papszOrig == nullptr && papszOverride != nullptr)
263 686 : return CSLDuplicate(papszOverride);
264 :
265 755489 : if (papszOverride == nullptr)
266 753640 : 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 18162 : char **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo,
483 : CSLConstList papszNewLines)
484 : {
485 18162 : if (papszNewLines == nullptr)
486 36 : return papszStrList; // Nothing to do!
487 :
488 18126 : const int nToInsert = CSLCount(papszNewLines);
489 18126 : if (nToInsert == 0)
490 1243 : return papszStrList; // Nothing to do!
491 :
492 16883 : const int nSrcLines = CSLCount(papszStrList);
493 16883 : const int nDstLines = nSrcLines + nToInsert;
494 :
495 : // Allocate room for the new strings.
496 : papszStrList = static_cast<char **>(
497 16883 : 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 16883 : 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 16883 : if (nInsertAtLineNo == -1 || nInsertAtLineNo > nSrcLines)
507 16075 : nInsertAtLineNo = nSrcLines;
508 :
509 : {
510 16883 : char **ppszSrc = papszStrList + nSrcLines;
511 16883 : char **ppszDst = papszStrList + nDstLines;
512 :
513 35076 : for (int i = nSrcLines; i >= nInsertAtLineNo; --i)
514 : {
515 18193 : *ppszDst = *ppszSrc;
516 18193 : --ppszDst;
517 18193 : --ppszSrc;
518 : }
519 : }
520 :
521 : // Copy the strings to the list.
522 16883 : CSLConstList ppszSrc = papszNewLines;
523 16883 : char **ppszDst = papszStrList + nInsertAtLineNo;
524 :
525 148361 : for (; *ppszSrc != nullptr; ++ppszSrc, ++ppszDst)
526 : {
527 131478 : *ppszDst = CPLStrdup(*ppszSrc);
528 : }
529 :
530 16883 : 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 7011 : char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete,
574 : int nNumToRemove, char ***ppapszRetStrings)
575 : {
576 7011 : const int nSrcLines = CSLCount(papszStrList);
577 :
578 7011 : 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 7011 : const int nDstLines = nSrcLines - nNumToRemove;
584 7011 : if (nDstLines < 1)
585 : {
586 1163 : CSLDestroy(papszStrList);
587 1163 : 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 5848 : char **ppszDst = papszStrList + nFirstLineToDelete;
594 :
595 5848 : if (ppapszRetStrings == nullptr)
596 : {
597 : // free() all the strings that will be removed.
598 11696 : for (int i = 0; i < nNumToRemove; ++i)
599 : {
600 5848 : CPLFree(*ppszDst);
601 5848 : *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 5848 : if (nFirstLineToDelete == -1 || nFirstLineToDelete > nSrcLines)
620 0 : nFirstLineToDelete = nDstLines;
621 :
622 5848 : char **ppszSrc = papszStrList + nFirstLineToDelete + nNumToRemove;
623 5848 : ppszDst = papszStrList + nFirstLineToDelete;
624 :
625 12292 : for (; *ppszSrc != nullptr; ++ppszSrc, ++ppszDst)
626 : {
627 6444 : *ppszDst = *ppszSrc;
628 : }
629 : // Move the NULL pointer at the end of the StringList.
630 5848 : *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 5848 : 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 825982 : int CSLFindString(CSLConstList papszList, const char *pszTarget)
656 :
657 : {
658 825982 : if (papszList == nullptr)
659 303291 : return -1;
660 :
661 19802900 : for (int i = 0; papszList[i] != nullptr; ++i)
662 : {
663 19393000 : if (EQUAL(papszList[i], pszTarget))
664 112792 : return i;
665 : }
666 :
667 409899 : 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 3114 : int CSLFindStringCaseSensitive(CSLConstList papszList, const char *pszTarget)
689 :
690 : {
691 3114 : if (papszList == nullptr)
692 742 : return -1;
693 :
694 14675 : for (int i = 0; papszList[i] != nullptr; ++i)
695 : {
696 12317 : if (strcmp(papszList[i], pszTarget) == 0)
697 14 : return i;
698 : }
699 :
700 2358 : 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 26073 : int CSLPartialFindString(CSLConstList papszHaystack, const char *pszNeedle)
721 : {
722 26073 : if (papszHaystack == nullptr || pszNeedle == nullptr)
723 7138 : return -1;
724 :
725 148298 : for (int i = 0; papszHaystack[i] != nullptr; ++i)
726 : {
727 138041 : if (strstr(papszHaystack[i], pszNeedle))
728 8678 : return i;
729 : }
730 :
731 10257 : 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 205472 : char **CSLTokenizeString(const char *pszString)
742 : {
743 205472 : return CSLTokenizeString2(pszString, " ", CSLT_HONOURSTRINGS);
744 : }
745 :
746 : /************************************************************************/
747 : /* CSLTokenizeStringComplex() */
748 : /************************************************************************/
749 :
750 : /** Obsolete tokenizing api. Use CSLTokenizeString2() */
751 691652 : char **CSLTokenizeStringComplex(const char *pszString,
752 : const char *pszDelimiters, int bHonourStrings,
753 : int bAllowEmptyTokens)
754 : {
755 691652 : int nFlags = 0;
756 :
757 691652 : if (bHonourStrings)
758 130807 : nFlags |= CSLT_HONOURSTRINGS;
759 691652 : if (bAllowEmptyTokens)
760 17825 : nFlags |= CSLT_ALLOWEMPTYTOKENS;
761 :
762 691652 : 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 1568800 : char **CSLTokenizeString2(const char *pszString, const char *pszDelimiters,
821 : int nCSLTFlags)
822 : {
823 1568800 : if (pszString == nullptr)
824 4541 : return static_cast<char **>(CPLCalloc(sizeof(char *), 1));
825 :
826 3128490 : return cpl::tokenize_string(pszString, pszDelimiters, nCSLTFlags)
827 1564240 : .StealList();
828 : }
829 :
830 : namespace cpl
831 : {
832 1564240 : CPLStringList tokenize_string(std::string_view str, std::string_view delimiters,
833 : int nCSLTFlags)
834 : {
835 3128490 : CPLStringList oRetList;
836 1564240 : const bool bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS) != 0;
837 1564240 : const bool bHonourStringsSingleQuotes =
838 1564240 : (nCSLTFlags & CSLT_HONOURSINGLEQUOTES) != 0;
839 1564240 : const bool bAllowEmptyTokens = (nCSLTFlags & CSLT_ALLOWEMPTYTOKENS) != 0;
840 1564240 : const bool bStripLeadSpaces = (nCSLTFlags & CSLT_STRIPLEADSPACES) != 0;
841 1564240 : const bool bStripEndSpaces = (nCSLTFlags & CSLT_STRIPENDSPACES) != 0;
842 :
843 1564240 : size_t pos = 0;
844 3128460 : std::string token;
845 4913610 : while (pos < str.size())
846 : {
847 3349380 : token.clear();
848 3349380 : bool bInString = false;
849 3349380 : bool bInStringSingleQuote = false;
850 :
851 : // Try to find the next delimiter, marking end of token.
852 42671900 : while (pos < str.size())
853 : {
854 : // End if this is a delimiter skip it and break.
855 81086200 : if (!bInString && !bInStringSingleQuote &&
856 39919000 : delimiters.find(str[pos]) != std::string_view::npos)
857 : {
858 1844670 : pos++;
859 1844670 : 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 39322500 : if (bHonourStrings && !bInStringSingleQuote && str[pos] == '"')
866 : {
867 76866 : if (nCSLTFlags & CSLT_PRESERVEQUOTES)
868 : {
869 5543 : token.push_back(str[pos]);
870 : }
871 :
872 76866 : bInString = !bInString;
873 76866 : pos++;
874 76866 : continue;
875 : }
876 39245700 : 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 39245700 : if (bInString && str[pos] == '\\')
895 : {
896 244 : if (pos + 1 < str.size() &&
897 122 : (str[pos + 1] == '"' || str[pos + 1] == '\\'))
898 : {
899 48 : if (nCSLTFlags & CSLT_PRESERVEESCAPES)
900 : {
901 6 : token.push_back(str[pos]);
902 : }
903 :
904 48 : ++pos;
905 : }
906 : }
907 39245500 : 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 39245700 : token.push_back(str[pos]);
922 39245700 : pos++;
923 : }
924 :
925 : // Add the token.
926 3349360 : std::string_view token_view(token);
927 3349390 : if (bStripLeadSpaces)
928 : {
929 34809 : token_view = ltrim(token_view);
930 : }
931 3349390 : if (bStripEndSpaces)
932 : {
933 34756 : token_view = rtrim(token_view);
934 : }
935 :
936 3349390 : if (!token_view.empty() || bAllowEmptyTokens)
937 3219000 : 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 3100570 : if (!str.empty() && pos == str.size() && bAllowEmptyTokens &&
945 3128630 : oRetList.Count() > 0 &&
946 28103 : delimiters.find(str[pos - 1]) != std::string_view::npos)
947 : {
948 1410 : oRetList.AddString("");
949 : }
950 :
951 1564210 : 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 28028 : oRetList.Assign(static_cast<char **>(CPLCalloc(sizeof(char *), 1)));
956 : }
957 :
958 3128450 : 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 1791930 : 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 1791930 : char *pachBufRingInfo = static_cast<char *>(CPLGetTLS(CTLS_CPLSPRINTF));
987 :
988 1791910 : if (pachBufRingInfo == nullptr)
989 : {
990 7147 : pachBufRingInfo = static_cast<char *>(CPLCalloc(
991 : 1, sizeof(int) + CPLSPrintf_BUF_Count * CPLSPrintf_BUF_SIZE));
992 7148 : 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 1791920 : int *pnBufIndex = reinterpret_cast<int *>(pachBufRingInfo);
1000 1791920 : const size_t nOffset = sizeof(int) + *pnBufIndex * CPLSPrintf_BUF_SIZE;
1001 1791920 : char *pachBuffer = pachBufRingInfo + nOffset;
1002 :
1003 1791920 : *pnBufIndex = (*pnBufIndex + 1) % CPLSPrintf_BUF_Count;
1004 :
1005 : /* -------------------------------------------------------------------- */
1006 : /* Format the result. */
1007 : /* -------------------------------------------------------------------- */
1008 :
1009 1791920 : va_start(args, fmt);
1010 :
1011 : const int ret =
1012 1791920 : CPLvsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE - 1, fmt, args);
1013 1791910 : if (ret < 0 || ret >= CPLSPrintf_BUF_SIZE - 1)
1014 : {
1015 12 : CPLError(CE_Failure, CPLE_AppDefined,
1016 : "CPLSPrintf() called with too "
1017 : "big string. Output will be truncated !");
1018 : }
1019 :
1020 1791900 : va_end(args);
1021 :
1022 1791900 : 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 4702370 : static const char *CPLvsnprintf_get_end_of_formatting(const char *fmt)
1069 : {
1070 4702370 : char ch = '\0';
1071 : // Flag.
1072 5899050 : for (; (ch = *fmt) != '\0'; ++fmt)
1073 : {
1074 5899030 : if (ch == '\'')
1075 0 : continue; // Bad idea as this is locale specific.
1076 5899030 : if (ch == '-' || ch == '+' || ch == ' ' || ch == '#' || ch == '0')
1077 1196680 : continue;
1078 4702350 : break;
1079 : }
1080 :
1081 : // Field width.
1082 6061210 : for (; (ch = *fmt) != '\0'; ++fmt)
1083 : {
1084 6061200 : if (ch == '$')
1085 0 : return nullptr; // Do not support this.
1086 6061200 : if (*fmt >= '0' && *fmt <= '9')
1087 1358840 : continue;
1088 4702360 : break;
1089 : }
1090 :
1091 : // Precision.
1092 4702370 : if (ch == '.')
1093 : {
1094 716287 : ++fmt;
1095 2029640 : for (; (ch = *fmt) != '\0'; ++fmt)
1096 : {
1097 2029640 : if (ch == '$')
1098 0 : return nullptr; // Do not support this.
1099 2029640 : if (*fmt >= '0' && *fmt <= '9')
1100 1313350 : continue;
1101 716287 : break;
1102 : }
1103 : }
1104 :
1105 : // Length modifier.
1106 4827400 : for (; (ch = *fmt) != '\0'; ++fmt)
1107 : {
1108 4827400 : if (ch == 'h' || ch == 'l' || ch == 'j' || ch == 'z' || ch == 't' ||
1109 : ch == 'L')
1110 125068 : continue;
1111 4702330 : else if (ch == 'I' && fmt[1] == '6' && fmt[2] == '4')
1112 0 : fmt += 2;
1113 : else
1114 4702370 : 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 2790670 : int CPLvsnprintf(char *str, size_t size, CPL_FORMAT_STRING(const char *fmt),
1143 : va_list args)
1144 : {
1145 2790670 : if (size == 0)
1146 0 : return vsnprintf(str, size, fmt, args);
1147 :
1148 : va_list wrk_args;
1149 :
1150 : #ifdef va_copy
1151 2790670 : va_copy(wrk_args, args);
1152 : #else
1153 : wrk_args = args;
1154 : #endif
1155 :
1156 2790670 : const char *fmt_ori = fmt;
1157 2790670 : size_t offset_out = 0;
1158 2790670 : char ch = '\0';
1159 2790670 : bool bFormatUnknown = false;
1160 :
1161 38522000 : for (; (ch = *fmt) != '\0'; ++fmt)
1162 : {
1163 35733300 : if (ch == '%')
1164 : {
1165 4703040 : 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 684 : snprintf(str + offset_out, size - offset_out, "%.*f",
1171 : precision, val);
1172 : // MSVC vsnprintf() returns -1.
1173 684 : 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 4702370 : const char *ptrend = CPLvsnprintf_get_end_of_formatting(fmt + 1);
1189 4702370 : if (ptrend == nullptr || ptrend - fmt >= 20)
1190 : {
1191 14 : bFormatUnknown = true;
1192 14 : break;
1193 : }
1194 4702350 : char end = *ptrend;
1195 4702350 : char end_m1 = ptrend[-1];
1196 :
1197 4702350 : char localfmt[22] = {};
1198 4702350 : memcpy(localfmt, fmt, ptrend - fmt + 1);
1199 4702350 : localfmt[ptrend - fmt + 1] = '\0';
1200 :
1201 4702350 : int local_ret = 0;
1202 4702350 : 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 4686820 : else if (end == 'd' || end == 'i' || end == 'c')
1210 : {
1211 1558930 : if (end_m1 == 'h')
1212 0 : call_native_snprintf(int);
1213 1558930 : else if (end_m1 == 'l' && ptrend[-2] != 'l')
1214 4373 : call_native_snprintf(long);
1215 1554560 : else if (end_m1 == 'l' && ptrend[-2] == 'l')
1216 32452 : call_native_snprintf(GIntBig);
1217 1522100 : else if (end_m1 == '4' && ptrend[-2] == '6' &&
1218 0 : ptrend[-3] == 'I')
1219 : // Microsoft I64 modifier.
1220 0 : call_native_snprintf(GIntBig);
1221 1522100 : else if (end_m1 == 'z')
1222 0 : call_native_snprintf(size_t);
1223 1522100 : 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 1522100 : call_native_snprintf(int);
1231 : }
1232 3127890 : else if (end == 'o' || end == 'u' || end == 'x' || end == 'X')
1233 : {
1234 1228340 : if (end_m1 == 'h')
1235 0 : call_native_snprintf(unsigned int);
1236 1228340 : else if (end_m1 == 'l' && ptrend[-2] != 'l')
1237 14142 : call_native_snprintf(unsigned long);
1238 1214200 : else if (end_m1 == 'l' && ptrend[-2] == 'l')
1239 17703 : call_native_snprintf(GUIntBig);
1240 1196490 : else if (end_m1 == '4' && ptrend[-2] == '6' &&
1241 0 : ptrend[-3] == 'I')
1242 : // Microsoft I64 modifier.
1243 0 : call_native_snprintf(GUIntBig);
1244 1196490 : else if (end_m1 == 'z')
1245 0 : call_native_snprintf(size_t);
1246 1196490 : 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 1196490 : call_native_snprintf(unsigned int);
1254 : }
1255 1899550 : else if (end == 'e' || end == 'E' || end == 'f' || end == 'F' ||
1256 1157020 : end == 'g' || end == 'G' || end == 'a' || end == 'A')
1257 : {
1258 742544 : if (end_m1 == 'L')
1259 0 : call_native_snprintf(long double);
1260 : else
1261 742544 : call_native_snprintf(double);
1262 : // MSVC vsnprintf() returns -1.
1263 742563 : if (local_ret < 0 || offset_out + local_ret >= size)
1264 : break;
1265 10304500 : for (int j = 0; j < local_ret; ++j)
1266 : {
1267 9562010 : if (str[offset_out + j] == ',')
1268 : {
1269 0 : str[offset_out + j] = '.';
1270 0 : break;
1271 : }
1272 742475 : }
1273 : }
1274 1157000 : else if (end == 's')
1275 : {
1276 1150510 : const char *pszPtr = va_arg(wrk_args, const char *);
1277 1150470 : CPLAssert(pszPtr);
1278 1150460 : local_ret = snprintf(str + offset_out, size - offset_out,
1279 : localfmt, pszPtr);
1280 : }
1281 6490 : else if (end == 'p')
1282 : {
1283 6172 : call_native_snprintf(void *);
1284 : }
1285 : else
1286 : {
1287 318 : bFormatUnknown = true;
1288 318 : break;
1289 : }
1290 : // MSVC vsnprintf() returns -1.
1291 4701880 : if (local_ret < 0 || offset_out + local_ret >= size)
1292 : break;
1293 4701010 : offset_out += local_ret;
1294 4701010 : fmt = ptrend;
1295 : }
1296 : else
1297 : {
1298 31030200 : if (offset_out == size - 1)
1299 597 : break;
1300 31029600 : str[offset_out++] = *fmt;
1301 : }
1302 : }
1303 2790610 : if (ch == '\0' && offset_out < size)
1304 2788660 : str[offset_out] = '\0';
1305 : else
1306 : {
1307 1950 : 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 2790570 : va_end(wrk_args);
1329 : #endif
1330 :
1331 2790570 : 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 178383 : int CPLsnprintf(char *str, size_t size, CPL_FORMAT_STRING(const char *fmt), ...)
1362 : {
1363 : va_list args;
1364 :
1365 178383 : va_start(args, fmt);
1366 178383 : const int ret = CPLvsnprintf(str, size, fmt, args);
1367 178382 : va_end(args);
1368 178382 : 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 3828790 : bool CPLTestBool(const char *pszValue)
1550 : {
1551 4939270 : return !(EQUAL(pszValue, "NO") || EQUAL(pszValue, "FALSE") ||
1552 4939270 : EQUAL(pszValue, "OFF") || EQUAL(pszValue, "0"));
1553 : }
1554 :
1555 : /// Return true if the config option's value represents a boolean true.
1556 : /// \param configVal String name of config value.
1557 : /// \return Whether the config option's value represents true.
1558 4417 : bool CPLTestConfigOption(const char *configVal)
1559 : {
1560 4417 : return CPLTestBool(CPLGetConfigOption(configVal, "NO"));
1561 : }
1562 :
1563 : /************************************************************************/
1564 : /* CSLTestBoolean() */
1565 : /************************************************************************/
1566 :
1567 : /**
1568 : * Test what boolean value contained in the string.
1569 : *
1570 : * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE.
1571 : * Otherwise, TRUE will be returned.
1572 : *
1573 : * Deprecated. Removed in GDAL 3.x.
1574 : *
1575 : * Use CPLTestBoolean() for C and CPLTestBool() for C++.
1576 : *
1577 : * @param pszValue the string should be tested.
1578 : *
1579 : * @return TRUE or FALSE.
1580 : */
1581 :
1582 760 : int CSLTestBoolean(const char *pszValue)
1583 : {
1584 760 : return CPLTestBool(pszValue) ? TRUE : FALSE;
1585 : }
1586 :
1587 : /************************************************************************/
1588 : /* CPLTestBoolean() */
1589 : /************************************************************************/
1590 :
1591 : /**
1592 : * Test what boolean value contained in the string.
1593 : *
1594 : * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE.
1595 : * Otherwise, TRUE will be returned.
1596 : *
1597 : * Use this only in C code. In C++, prefer CPLTestBool().
1598 : *
1599 : * @param pszValue the string should be tested.
1600 : *
1601 : * @return TRUE or FALSE.
1602 : */
1603 :
1604 162 : int CPLTestBoolean(const char *pszValue)
1605 : {
1606 162 : return CPLTestBool(pszValue) ? TRUE : FALSE;
1607 : }
1608 :
1609 : /**********************************************************************
1610 : * CPLFetchBool()
1611 : **********************************************************************/
1612 :
1613 : /** Check for boolean key value.
1614 : *
1615 : * In a StringList of "Name=Value" pairs, look to see if there is a key
1616 : * with the given name, and if it can be interpreted as being TRUE. If
1617 : * the key appears without any "=Value" portion it will be considered true.
1618 : * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
1619 : * if the key appears in the list it will be considered TRUE. If the key
1620 : * doesn't appear at all, the indicated default value will be returned.
1621 : *
1622 : * @param papszStrList the string list to search.
1623 : * @param pszKey the key value to look for (case insensitive).
1624 : * @param bDefault the value to return if the key isn't found at all.
1625 : *
1626 : * @return true or false
1627 : */
1628 :
1629 379630 : bool CPLFetchBool(CSLConstList papszStrList, const char *pszKey, bool bDefault)
1630 :
1631 : {
1632 379630 : if (CSLFindString(papszStrList, pszKey) != -1)
1633 2 : return true;
1634 :
1635 379456 : const char *const pszValue = CSLFetchNameValue(papszStrList, pszKey);
1636 379510 : if (pszValue == nullptr)
1637 359925 : return bDefault;
1638 :
1639 19585 : return CPLTestBool(pszValue);
1640 : }
1641 :
1642 : /**********************************************************************
1643 : * CSLFetchBoolean()
1644 : **********************************************************************/
1645 :
1646 : /** DEPRECATED. Check for boolean key value.
1647 : *
1648 : * In a StringList of "Name=Value" pairs, look to see if there is a key
1649 : * with the given name, and if it can be interpreted as being TRUE. If
1650 : * the key appears without any "=Value" portion it will be considered true.
1651 : * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
1652 : * if the key appears in the list it will be considered TRUE. If the key
1653 : * doesn't appear at all, the indicated default value will be returned.
1654 : *
1655 : * @param papszStrList the string list to search.
1656 : * @param pszKey the key value to look for (case insensitive).
1657 : * @param bDefault the value to return if the key isn't found at all.
1658 : *
1659 : * @return TRUE or FALSE
1660 : */
1661 :
1662 1026 : int CSLFetchBoolean(CSLConstList papszStrList, const char *pszKey, int bDefault)
1663 :
1664 : {
1665 1026 : return CPLFetchBool(papszStrList, pszKey, CPL_TO_BOOL(bDefault));
1666 : }
1667 :
1668 : /************************************************************************/
1669 : /* CSLFetchNameValueDefaulted() */
1670 : /************************************************************************/
1671 :
1672 : /** Same as CSLFetchNameValue() but return pszDefault in case of no match */
1673 975503 : const char *CSLFetchNameValueDef(CSLConstList papszStrList, const char *pszName,
1674 : const char *pszDefault)
1675 :
1676 : {
1677 975503 : const char *pszResult = CSLFetchNameValue(papszStrList, pszName);
1678 975444 : if (pszResult != nullptr)
1679 192265 : return pszResult;
1680 :
1681 783179 : return pszDefault;
1682 : }
1683 :
1684 : /**********************************************************************
1685 : * CSLFetchNameValue()
1686 : **********************************************************************/
1687 :
1688 : /** In a StringList of "Name=Value" pairs, look for the
1689 : * first value associated with the specified name. The search is not
1690 : * case sensitive.
1691 : * ("Name:Value" pairs are also supported for backward compatibility
1692 : * with older stuff.)
1693 : *
1694 : * Returns a reference to the value in the StringList that the caller
1695 : * should not attempt to free.
1696 : *
1697 : * Returns NULL if the name is not found.
1698 : */
1699 :
1700 20339400 : const char *CSLFetchNameValue(CSLConstList papszStrList, const char *pszName)
1701 : {
1702 20339400 : if (papszStrList == nullptr || pszName == nullptr)
1703 5712750 : return nullptr;
1704 :
1705 14626600 : const size_t nLen = strlen(pszName);
1706 24230300 : while (*papszStrList != nullptr)
1707 : {
1708 9979000 : if (EQUALN(*papszStrList, pszName, nLen) &&
1709 386366 : ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
1710 : {
1711 375357 : return (*papszStrList) + nLen + 1;
1712 : }
1713 9603640 : ++papszStrList;
1714 : }
1715 14251300 : return nullptr;
1716 : }
1717 :
1718 : /************************************************************************/
1719 : /* CSLFindName() */
1720 : /************************************************************************/
1721 :
1722 : /**
1723 : * Find StringList entry with given key name.
1724 : *
1725 : * @param papszStrList the string list to search.
1726 : * @param pszName the key value to look for (case insensitive).
1727 : *
1728 : * @return -1 on failure or the list index of the first occurrence
1729 : * matching the given key.
1730 : */
1731 :
1732 18317000 : int CSLFindName(CSLConstList papszStrList, const char *pszName)
1733 : {
1734 18317000 : if (papszStrList == nullptr || pszName == nullptr)
1735 933530 : return -1;
1736 :
1737 17383400 : const size_t nLen = strlen(pszName);
1738 17383400 : int iIndex = 0;
1739 157565000 : while (*papszStrList != nullptr)
1740 : {
1741 147996000 : if (EQUALN(*papszStrList, pszName, nLen) &&
1742 8700850 : ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
1743 : {
1744 7814860 : return iIndex;
1745 : }
1746 140181000 : ++iIndex;
1747 140181000 : ++papszStrList;
1748 : }
1749 9568580 : return -1;
1750 : }
1751 :
1752 : /************************************************************************/
1753 : /* CPLParseMemorySize() */
1754 : /************************************************************************/
1755 :
1756 : /** Parse a memory size from a string.
1757 : *
1758 : * The string may indicate the units of the memory (e.g., "230k", "500 MB"),
1759 : * using the prefixes "k", "m", or "g" in either lower or upper-case,
1760 : * optionally followed by a "b" or "B". The string may alternatively specify
1761 : * memory as a fraction of the usable RAM (e.g., "25%"). Spaces before the
1762 : * number, between the number and the units, or after the units are ignored,
1763 : * but other characters will cause a parsing failure. If the string cannot
1764 : * be understood, the function will return CE_Failure.
1765 : *
1766 : * @param pszValue the string to parse
1767 : * @param[out] pnValue the parsed size, converted to bytes (if unit was specified)
1768 : * @param[out] pbUnitSpecified whether the string indicated the units
1769 : *
1770 : * @return CE_None on success, CE_Failure otherwise
1771 : * @since 3.10
1772 : */
1773 8731 : CPLErr CPLParseMemorySize(const char *pszValue, GIntBig *pnValue,
1774 : bool *pbUnitSpecified)
1775 : {
1776 8731 : const char *start = pszValue;
1777 8731 : char *end = nullptr;
1778 :
1779 : // trim leading whitespace
1780 8735 : while (*start == ' ')
1781 : {
1782 4 : start++;
1783 : }
1784 :
1785 8731 : auto len = CPLStrnlen(start, 100);
1786 8731 : double value = CPLStrtodM(start, &end);
1787 8731 : const char *unit = nullptr;
1788 8731 : bool unitIsNotPercent = false;
1789 :
1790 8731 : if (end == start)
1791 : {
1792 3 : CPLError(CE_Failure, CPLE_IllegalArg, "Received non-numeric value: %s",
1793 : pszValue);
1794 3 : return CE_Failure;
1795 : }
1796 :
1797 8728 : if (value < 0 || !std::isfinite(value))
1798 : {
1799 3 : CPLError(CE_Failure, CPLE_IllegalArg,
1800 : "Memory size must be a positive number or zero.");
1801 3 : return CE_Failure;
1802 : }
1803 :
1804 25272 : for (const char *c = end; c < start + len; c++)
1805 : {
1806 16553 : if (unit == nullptr)
1807 : {
1808 : // check various suffixes and convert number into bytes
1809 8576 : if (*c == '%')
1810 : {
1811 546 : if (value < 0 || value > 100)
1812 : {
1813 2 : CPLError(CE_Failure, CPLE_IllegalArg,
1814 : "Memory percentage must be between 0 and 100.");
1815 2 : return CE_Failure;
1816 : }
1817 544 : auto bytes = CPLGetUsablePhysicalRAM();
1818 544 : if (bytes == 0)
1819 : {
1820 0 : CPLError(CE_Failure, CPLE_NotSupported,
1821 : "Cannot determine usable physical RAM");
1822 0 : return CE_Failure;
1823 : }
1824 544 : value *= static_cast<double>(bytes / 100);
1825 544 : unit = c;
1826 : }
1827 : else
1828 : {
1829 8030 : switch (*c)
1830 : {
1831 35 : case 'G':
1832 : case 'g':
1833 35 : value *= 1024;
1834 : [[fallthrough]];
1835 7973 : case 'M':
1836 : case 'm':
1837 7973 : value *= 1024;
1838 : [[fallthrough]];
1839 8019 : case 'K':
1840 : case 'k':
1841 8019 : value *= 1024;
1842 8019 : unit = c;
1843 8019 : unitIsNotPercent = true;
1844 8019 : break;
1845 9 : case ' ':
1846 9 : break;
1847 2 : default:
1848 2 : CPLError(CE_Failure, CPLE_IllegalArg,
1849 : "Failed to parse memory size: %s", pszValue);
1850 2 : return CE_Failure;
1851 : }
1852 : }
1853 : }
1854 7977 : else if (unitIsNotPercent && c == unit + 1 && (*c == 'b' || *c == 'B'))
1855 : {
1856 : // ignore 'B' or 'b' as part of unit
1857 7975 : continue;
1858 : }
1859 2 : else if (*c != ' ')
1860 : {
1861 2 : CPLError(CE_Failure, CPLE_IllegalArg,
1862 : "Failed to parse memory size: %s", pszValue);
1863 2 : return CE_Failure;
1864 : }
1865 : }
1866 :
1867 17437 : if (value > static_cast<double>(std::numeric_limits<GIntBig>::max()) ||
1868 8718 : value > static_cast<double>(std::numeric_limits<size_t>::max()))
1869 : {
1870 1 : CPLError(CE_Failure, CPLE_IllegalArg, "Memory size is too large: %s",
1871 : pszValue);
1872 1 : return CE_Failure;
1873 : }
1874 :
1875 8718 : *pnValue = static_cast<GIntBig>(value);
1876 8718 : if (pbUnitSpecified)
1877 : {
1878 719 : *pbUnitSpecified = (unit != nullptr);
1879 : }
1880 8718 : return CE_None;
1881 : }
1882 :
1883 : /**********************************************************************
1884 : * CPLParseNameValue()
1885 : **********************************************************************/
1886 :
1887 : /**
1888 : * Parse NAME=VALUE string into name and value components.
1889 : *
1890 : * Note that if ppszKey is non-NULL, the key (or name) portion will be
1891 : * allocated using CPLMalloc() and returned in that pointer. It is the
1892 : * application's responsibility to free this string, but the application should
1893 : * not modify or free the returned value portion.
1894 : *
1895 : * This function also supports "NAME:VALUE" strings and will strip white
1896 : * space from around the delimiter when forming name and value strings.
1897 : *
1898 : * Eventually CSLFetchNameValue() and friends may be modified to use
1899 : * CPLParseNameValue().
1900 : *
1901 : * @param pszNameValue string in "NAME=VALUE" format.
1902 : * @param ppszKey optional pointer though which to return the name
1903 : * portion.
1904 : *
1905 : * @return the value portion (pointing into the original string).
1906 : */
1907 :
1908 91273 : const char *CPLParseNameValue(const char *pszNameValue, char **ppszKey)
1909 : {
1910 1337780 : for (int i = 0; pszNameValue[i] != '\0'; ++i)
1911 : {
1912 1334820 : if (pszNameValue[i] == '=' || pszNameValue[i] == ':')
1913 : {
1914 88310 : const char *pszValue = pszNameValue + i + 1;
1915 95244 : while (*pszValue == ' ' || *pszValue == '\t')
1916 6934 : ++pszValue;
1917 :
1918 88310 : if (ppszKey != nullptr)
1919 : {
1920 88286 : *ppszKey = static_cast<char *>(CPLMalloc(i + 1));
1921 88286 : memcpy(*ppszKey, pszNameValue, i);
1922 88286 : (*ppszKey)[i] = '\0';
1923 88647 : while (i > 0 &&
1924 88647 : ((*ppszKey)[i - 1] == ' ' || (*ppszKey)[i - 1] == '\t'))
1925 : {
1926 361 : (*ppszKey)[i - 1] = '\0';
1927 361 : i--;
1928 : }
1929 : }
1930 :
1931 88310 : return pszValue;
1932 : }
1933 : }
1934 :
1935 2963 : return nullptr;
1936 : }
1937 :
1938 : namespace cpl
1939 : {
1940 : std::pair<std::string_view, std::string_view>
1941 6 : parse_name_value(std::string_view svNameValue)
1942 : {
1943 40 : for (size_t i = 0; i < svNameValue.size(); ++i)
1944 : {
1945 38 : if (svNameValue[i] == '=' || svNameValue[i] == ':')
1946 : {
1947 4 : auto parsed = std::make_pair(trim(svNameValue.substr(0, i)),
1948 8 : trim(svNameValue.substr(i + 1)));
1949 :
1950 4 : if (!parsed.first.empty())
1951 : {
1952 3 : return parsed;
1953 : }
1954 : else
1955 : {
1956 2 : return std::make_pair(std::string_view(), std::string_view());
1957 : }
1958 : }
1959 : }
1960 :
1961 4 : return std::make_pair(std::string_view(), std::string_view());
1962 : }
1963 :
1964 : std::pair<std::string_view, std::string_view>
1965 2 : parse_name_value(const char *pszNameValue)
1966 : {
1967 2 : return parse_name_value(std::string_view(pszNameValue));
1968 : }
1969 :
1970 : } // namespace cpl
1971 :
1972 : /**********************************************************************
1973 : * CPLParseNameValueSep()
1974 : **********************************************************************/
1975 : /**
1976 : * Parse NAME<Sep>VALUE string into name and value components.
1977 : *
1978 : * This is derived directly from CPLParseNameValue() which will separate
1979 : * on '=' OR ':', here chSep is required for specifying the separator
1980 : * explicitly.
1981 : *
1982 : * @param pszNameValue string in "NAME=VALUE" format.
1983 : * @param ppszKey optional pointer though which to return the name
1984 : * portion.
1985 : * @param chSep required single char separator
1986 : * @return the value portion (pointing into original string).
1987 : */
1988 :
1989 17 : const char *CPLParseNameValueSep(const char *pszNameValue, char **ppszKey,
1990 : char chSep)
1991 : {
1992 140 : for (int i = 0; pszNameValue[i] != '\0'; ++i)
1993 : {
1994 138 : if (pszNameValue[i] == chSep)
1995 : {
1996 15 : const char *pszValue = pszNameValue + i + 1;
1997 15 : while (*pszValue == ' ' || *pszValue == '\t')
1998 0 : ++pszValue;
1999 :
2000 15 : if (ppszKey != nullptr)
2001 : {
2002 15 : *ppszKey = static_cast<char *>(CPLMalloc(i + 1));
2003 15 : memcpy(*ppszKey, pszNameValue, i);
2004 15 : (*ppszKey)[i] = '\0';
2005 15 : while (i > 0 &&
2006 15 : ((*ppszKey)[i - 1] == ' ' || (*ppszKey)[i - 1] == '\t'))
2007 : {
2008 0 : (*ppszKey)[i - 1] = '\0';
2009 0 : i--;
2010 : }
2011 : }
2012 :
2013 15 : return pszValue;
2014 : }
2015 : }
2016 :
2017 2 : return nullptr;
2018 : }
2019 :
2020 : /**********************************************************************
2021 : * CSLFetchNameValueMultiple()
2022 : **********************************************************************/
2023 :
2024 : /** In a StringList of "Name=Value" pairs, look for all the
2025 : * values with the specified name. The search is not case
2026 : * sensitive.
2027 : * ("Name:Value" pairs are also supported for backward compatibility
2028 : * with older stuff.)
2029 : *
2030 : * Returns StringList with one entry for each occurrence of the
2031 : * specified name. The StringList should eventually be destroyed
2032 : * by calling CSLDestroy().
2033 : *
2034 : * Returns NULL if the name is not found.
2035 : */
2036 :
2037 15190 : char **CSLFetchNameValueMultiple(CSLConstList papszStrList, const char *pszName)
2038 : {
2039 15190 : if (papszStrList == nullptr || pszName == nullptr)
2040 6690 : return nullptr;
2041 :
2042 8500 : const size_t nLen = strlen(pszName);
2043 8500 : char **papszValues = nullptr;
2044 23850 : while (*papszStrList != nullptr)
2045 : {
2046 15350 : if (EQUALN(*papszStrList, pszName, nLen) &&
2047 65 : ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
2048 : {
2049 65 : papszValues = CSLAddString(papszValues, (*papszStrList) + nLen + 1);
2050 : }
2051 15350 : ++papszStrList;
2052 : }
2053 :
2054 8500 : return papszValues;
2055 : }
2056 :
2057 : /**********************************************************************
2058 : * CSLAddNameValue()
2059 : **********************************************************************/
2060 :
2061 : /** Add a new entry to a StringList of "Name=Value" pairs,
2062 : * ("Name:Value" pairs are also supported for backward compatibility
2063 : * with older stuff.)
2064 : *
2065 : * This function does not check if a "Name=Value" pair already exists
2066 : * for that name and can generate multiple entries for the same name.
2067 : * Use CSLSetNameValue() if you want each name to have only one value.
2068 : *
2069 : * Returns the modified StringList.
2070 : */
2071 :
2072 371304 : char **CSLAddNameValue(char **papszStrList, const char *pszName,
2073 : const char *pszValue)
2074 : {
2075 371304 : if (pszName == nullptr || pszValue == nullptr)
2076 15 : return papszStrList;
2077 :
2078 371289 : const size_t nLen = strlen(pszName) + strlen(pszValue) + 2;
2079 371289 : char *pszLine = static_cast<char *>(CPLMalloc(nLen));
2080 371292 : snprintf(pszLine, nLen, "%s=%s", pszName, pszValue);
2081 371292 : papszStrList = CSLAddString(papszStrList, pszLine);
2082 371274 : CPLFree(pszLine);
2083 :
2084 371285 : return papszStrList;
2085 : }
2086 :
2087 : /************************************************************************/
2088 : /* CSLSetNameValue() */
2089 : /************************************************************************/
2090 :
2091 : /**
2092 : * Assign value to name in StringList.
2093 : *
2094 : * Set the value for a given name in a StringList of "Name=Value" pairs
2095 : * ("Name:Value" pairs are also supported for backward compatibility
2096 : * with older stuff.)
2097 : *
2098 : * If there is already a value for that name in the list then the value
2099 : * is changed, otherwise a new "Name=Value" pair is added.
2100 : *
2101 : * @param papszList the original list, the modified version is returned.
2102 : * @param pszName the name to be assigned a value. This should be a well
2103 : * formed token (no spaces or very special characters).
2104 : * @param pszValue the value to assign to the name. This should not contain
2105 : * any newlines (CR or LF) but is otherwise pretty much unconstrained. If
2106 : * NULL any corresponding value will be removed.
2107 : *
2108 : * @return modified StringList.
2109 : */
2110 :
2111 403183 : char **CSLSetNameValue(char **papszList, const char *pszName,
2112 : const char *pszValue)
2113 : {
2114 403183 : if (pszName == nullptr)
2115 38 : return papszList;
2116 :
2117 403145 : size_t nLen = strlen(pszName);
2118 403819 : while (nLen > 0 && pszName[nLen - 1] == ' ')
2119 674 : nLen--;
2120 403145 : char **papszPtr = papszList;
2121 4583350 : while (papszPtr && *papszPtr != nullptr)
2122 : {
2123 4223900 : if (EQUALN(*papszPtr, pszName, nLen))
2124 : {
2125 : size_t i;
2126 46059 : for (i = nLen; (*papszPtr)[i] == ' '; ++i)
2127 : {
2128 : }
2129 45385 : if ((*papszPtr)[i] == '=' || (*papszPtr)[i] == ':')
2130 : {
2131 : // Found it.
2132 : // Change the value... make sure to keep the ':' or '='.
2133 43689 : const char cSep = (*papszPtr)[i];
2134 :
2135 43689 : CPLFree(*papszPtr);
2136 :
2137 : // If the value is NULL, remove this entry completely.
2138 43686 : if (pszValue == nullptr)
2139 : {
2140 48456 : while (papszPtr[1] != nullptr)
2141 : {
2142 12720 : *papszPtr = papszPtr[1];
2143 12720 : ++papszPtr;
2144 : }
2145 35736 : *papszPtr = nullptr;
2146 : }
2147 :
2148 : // Otherwise replace with new value.
2149 : else
2150 : {
2151 7950 : const size_t nLen2 = strlen(pszName) + strlen(pszValue) + 2;
2152 7950 : *papszPtr = static_cast<char *>(CPLMalloc(nLen2));
2153 7945 : snprintf(*papszPtr, nLen2, "%s%c%s", pszName, cSep,
2154 : pszValue);
2155 : }
2156 43681 : return papszList;
2157 : }
2158 : }
2159 4180210 : ++papszPtr;
2160 : }
2161 :
2162 359456 : if (pszValue == nullptr)
2163 3028 : return papszList;
2164 :
2165 : // The name does not exist yet. Create a new entry.
2166 356428 : return CSLAddNameValue(papszList, pszName, pszValue);
2167 : }
2168 :
2169 : /************************************************************************/
2170 : /* CSLSetNameValueSeparator() */
2171 : /************************************************************************/
2172 :
2173 : /**
2174 : * Replace the default separator (":" or "=") with the passed separator
2175 : * in the given name/value list.
2176 : *
2177 : * Note that if a separator other than ":" or "=" is used, the resulting
2178 : * list will not be manipulable by the CSL name/value functions any more.
2179 : *
2180 : * The CPLParseNameValue() function is used to break the existing lines,
2181 : * and it also strips white space from around the existing delimiter, thus
2182 : * the old separator, and any white space will be replaced by the new
2183 : * separator. For formatting purposes it may be desirable to include some
2184 : * white space in the new separator. e.g. ": " or " = ".
2185 : *
2186 : * @param papszList the list to update. Component strings may be freed
2187 : * but the list array will remain at the same location.
2188 : *
2189 : * @param pszSeparator the new separator string to insert.
2190 : */
2191 :
2192 68 : void CSLSetNameValueSeparator(char **papszList, const char *pszSeparator)
2193 :
2194 : {
2195 68 : const int nLines = CSLCount(papszList);
2196 :
2197 583 : for (int iLine = 0; iLine < nLines; ++iLine)
2198 : {
2199 515 : char *pszKey = nullptr;
2200 515 : const char *pszValue = CPLParseNameValue(papszList[iLine], &pszKey);
2201 515 : if (pszValue == nullptr || pszKey == nullptr)
2202 : {
2203 0 : CPLFree(pszKey);
2204 0 : continue;
2205 : }
2206 :
2207 1030 : char *pszNewLine = static_cast<char *>(CPLMalloc(
2208 515 : strlen(pszValue) + strlen(pszKey) + strlen(pszSeparator) + 1));
2209 515 : strcpy(pszNewLine, pszKey);
2210 515 : strcat(pszNewLine, pszSeparator);
2211 515 : strcat(pszNewLine, pszValue);
2212 515 : CPLFree(papszList[iLine]);
2213 515 : papszList[iLine] = pszNewLine;
2214 515 : CPLFree(pszKey);
2215 : }
2216 68 : }
2217 :
2218 : /************************************************************************/
2219 : /* CPLEscapeString() */
2220 : /************************************************************************/
2221 :
2222 : /**
2223 : * Apply escaping to string to preserve special characters.
2224 : *
2225 : * This function will "escape" a variety of special characters
2226 : * to make the string suitable to embed within a string constant
2227 : * or to write within a text stream but in a form that can be
2228 : * reconstituted to its original form. The escaping will even preserve
2229 : * zero bytes allowing preservation of raw binary data.
2230 : *
2231 : * CPLES_BackslashQuotable(0): This scheme turns a binary string into
2232 : * a form suitable to be placed within double quotes as a string constant.
2233 : * The backslash, quote, '\\0' and newline characters are all escaped in
2234 : * the usual C style.
2235 : *
2236 : * CPLES_XML(1): This scheme converts the '<', '>', '"' and '&' characters into
2237 : * their XML/HTML equivalent (<, >, " and &) making a string safe
2238 : * to embed as CDATA within an XML element. The '\\0' is not escaped and
2239 : * should not be included in the input.
2240 : *
2241 : * CPLES_URL(2): Everything except alphanumerics and the characters
2242 : * '$', '-', '_', '.', '+', '!', '*', ''', '(', ')' and ',' (see RFC1738) are
2243 : * converted to a percent followed by a two digit hex encoding of the character
2244 : * (leading zero supplied if needed). This is the mechanism used for encoding
2245 : * values to be passed in URLs. Note that this is different from what
2246 : * CPLString::URLEncode() does.
2247 : *
2248 : * CPLES_SQL(3): All single quotes are replaced with two single quotes.
2249 : * Suitable for use when constructing literal values for SQL commands where
2250 : * the literal will be enclosed in single quotes.
2251 : *
2252 : * CPLES_CSV(4): If the values contains commas, semicolons, tabs, double quotes,
2253 : * or newlines it placed in double quotes, and double quotes in the value are
2254 : * doubled. Suitable for use when constructing field values for .csv files.
2255 : * Note that CPLUnescapeString() currently does not support this format, only
2256 : * CPLEscapeString(). See cpl_csv.cpp for CSV parsing support.
2257 : *
2258 : * CPLES_SQLI(7): All double quotes are replaced with two double quotes.
2259 : * Suitable for use when constructing identifiers for SQL commands where
2260 : * the literal will be enclosed in double quotes.
2261 : *
2262 : * @param pszInput the string to escape.
2263 : * @param nLength The number of bytes of data to preserve. If this is -1
2264 : * the strlen(pszString) function will be used to compute the length.
2265 : * @param nScheme the encoding scheme to use.
2266 : *
2267 : * @return an escaped, zero terminated string that should be freed with
2268 : * CPLFree() when no longer needed.
2269 : */
2270 :
2271 717282 : char *CPLEscapeString(const char *pszInput, int nLength, int nScheme)
2272 : {
2273 717282 : const size_t szLength =
2274 717282 : (nLength < 0) ? strlen(pszInput) : static_cast<size_t>(nLength);
2275 : #define nLength no_longer_use_me
2276 :
2277 717282 : size_t nSizeAlloc = 1;
2278 : #if SIZEOF_VOIDP < 8
2279 : bool bWrapAround = false;
2280 : const auto IncSizeAlloc = [&nSizeAlloc, &bWrapAround](size_t inc)
2281 : {
2282 : constexpr size_t SZ_MAX = std::numeric_limits<size_t>::max();
2283 : if (nSizeAlloc > SZ_MAX - inc)
2284 : {
2285 : bWrapAround = true;
2286 : nSizeAlloc = 0;
2287 : }
2288 : nSizeAlloc += inc;
2289 : };
2290 : #else
2291 43351500 : const auto IncSizeAlloc = [&nSizeAlloc](size_t inc) { nSizeAlloc += inc; };
2292 : #endif
2293 :
2294 717282 : if (nScheme == CPLES_BackslashQuotable)
2295 : {
2296 67426 : for (size_t iIn = 0; iIn < szLength; iIn++)
2297 : {
2298 67233 : if (pszInput[iIn] == '\0' || pszInput[iIn] == '\n' ||
2299 55586 : pszInput[iIn] == '"' || pszInput[iIn] == '\\')
2300 11814 : IncSizeAlloc(2);
2301 : else
2302 55419 : IncSizeAlloc(1);
2303 : }
2304 : }
2305 717089 : else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES)
2306 : {
2307 43240800 : for (size_t iIn = 0; iIn < szLength; ++iIn)
2308 : {
2309 42527100 : if (pszInput[iIn] == '<')
2310 : {
2311 1408 : IncSizeAlloc(4);
2312 : }
2313 42525700 : else if (pszInput[iIn] == '>')
2314 : {
2315 1534 : IncSizeAlloc(4);
2316 : }
2317 42524200 : else if (pszInput[iIn] == '&')
2318 : {
2319 1653 : IncSizeAlloc(5);
2320 : }
2321 42522500 : else if (pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES)
2322 : {
2323 2700 : IncSizeAlloc(6);
2324 : }
2325 : // Python 2 does not display the UTF-8 character corresponding
2326 : // to the byte-order mark (BOM), so escape it.
2327 42519800 : else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] ==
2328 2 : 0xEF &&
2329 : (reinterpret_cast<const unsigned char *>(
2330 2 : pszInput))[iIn + 1] == 0xBB &&
2331 : (reinterpret_cast<const unsigned char *>(
2332 2 : pszInput))[iIn + 2] == 0xBF)
2333 : {
2334 2 : IncSizeAlloc(8);
2335 2 : iIn += 2;
2336 : }
2337 42519800 : else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] <
2338 21952 : 0x20 &&
2339 21952 : pszInput[iIn] != 0x9 && pszInput[iIn] != 0xA &&
2340 146 : pszInput[iIn] != 0xD)
2341 : {
2342 : // These control characters are unrepresentable in XML format,
2343 : // so we just drop them. #4117
2344 : }
2345 : else
2346 : {
2347 42519800 : IncSizeAlloc(1);
2348 : }
2349 713680 : }
2350 : }
2351 3409 : else if (nScheme == CPLES_URL) // Untested at implementation.
2352 : {
2353 15538 : for (size_t iIn = 0; iIn < szLength; ++iIn)
2354 : {
2355 14889 : if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') ||
2356 8063 : (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') ||
2357 3073 : (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') ||
2358 1804 : pszInput[iIn] == '$' || pszInput[iIn] == '-' ||
2359 1716 : pszInput[iIn] == '_' || pszInput[iIn] == '.' ||
2360 702 : pszInput[iIn] == '+' || pszInput[iIn] == '!' ||
2361 684 : pszInput[iIn] == '*' || pszInput[iIn] == '\'' ||
2362 682 : pszInput[iIn] == '(' || pszInput[iIn] == ')' ||
2363 672 : pszInput[iIn] == ',')
2364 : {
2365 14223 : IncSizeAlloc(1);
2366 : }
2367 : else
2368 : {
2369 666 : IncSizeAlloc(3);
2370 : }
2371 : }
2372 : }
2373 2760 : else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI)
2374 : {
2375 855 : const char chQuote = nScheme == CPLES_SQL ? '\'' : '\"';
2376 12084 : for (size_t iIn = 0; iIn < szLength; ++iIn)
2377 : {
2378 11229 : if (pszInput[iIn] == chQuote)
2379 : {
2380 5 : IncSizeAlloc(2);
2381 : }
2382 : else
2383 : {
2384 11224 : IncSizeAlloc(1);
2385 : }
2386 855 : }
2387 : }
2388 1905 : else if (nScheme == CPLES_CSV || nScheme == CPLES_CSV_FORCE_QUOTING)
2389 : {
2390 1905 : if (nScheme == CPLES_CSV && strcspn(pszInput, "\",;\t\n\r") == szLength)
2391 : {
2392 : char *pszOutput =
2393 1627 : static_cast<char *>(VSI_MALLOC_VERBOSE(szLength + 1));
2394 1627 : if (pszOutput == nullptr)
2395 0 : return nullptr;
2396 1627 : memcpy(pszOutput, pszInput, szLength + 1);
2397 1627 : return pszOutput;
2398 : }
2399 : else
2400 : {
2401 278 : IncSizeAlloc(1);
2402 13461 : for (size_t iIn = 0; iIn < szLength; ++iIn)
2403 : {
2404 13183 : if (pszInput[iIn] == '\"')
2405 : {
2406 169 : IncSizeAlloc(2);
2407 : }
2408 : else
2409 13014 : IncSizeAlloc(1);
2410 : }
2411 278 : IncSizeAlloc(1);
2412 278 : }
2413 : }
2414 : else
2415 : {
2416 0 : CPLError(CE_Failure, CPLE_AppDefined,
2417 : "Undefined escaping scheme (%d) in CPLEscapeString()",
2418 : nScheme);
2419 0 : return CPLStrdup("");
2420 : }
2421 :
2422 : #if SIZEOF_VOIDP < 8
2423 : if (bWrapAround)
2424 : {
2425 : CPLError(CE_Failure, CPLE_OutOfMemory,
2426 : "Out of memory in CPLEscapeString()");
2427 : return nullptr;
2428 : }
2429 : #endif
2430 :
2431 715655 : char *pszOutput = static_cast<char *>(VSI_MALLOC_VERBOSE(nSizeAlloc));
2432 715655 : if (pszOutput == nullptr)
2433 0 : return nullptr;
2434 :
2435 715655 : size_t iOut = 0;
2436 :
2437 715655 : if (nScheme == CPLES_BackslashQuotable)
2438 : {
2439 67426 : for (size_t iIn = 0; iIn < szLength; iIn++)
2440 : {
2441 67233 : if (pszInput[iIn] == '\0')
2442 : {
2443 11469 : pszOutput[iOut++] = '\\';
2444 11469 : pszOutput[iOut++] = '0';
2445 : }
2446 55764 : else if (pszInput[iIn] == '\n')
2447 : {
2448 178 : pszOutput[iOut++] = '\\';
2449 178 : pszOutput[iOut++] = 'n';
2450 : }
2451 55586 : else if (pszInput[iIn] == '"')
2452 : {
2453 128 : pszOutput[iOut++] = '\\';
2454 128 : pszOutput[iOut++] = '\"';
2455 : }
2456 55458 : else if (pszInput[iIn] == '\\')
2457 : {
2458 39 : pszOutput[iOut++] = '\\';
2459 39 : pszOutput[iOut++] = '\\';
2460 : }
2461 : else
2462 55419 : pszOutput[iOut++] = pszInput[iIn];
2463 : }
2464 193 : pszOutput[iOut++] = '\0';
2465 : }
2466 715462 : else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES)
2467 : {
2468 43240800 : for (size_t iIn = 0; iIn < szLength; ++iIn)
2469 : {
2470 42527100 : if (pszInput[iIn] == '<')
2471 : {
2472 1408 : pszOutput[iOut++] = '&';
2473 1408 : pszOutput[iOut++] = 'l';
2474 1408 : pszOutput[iOut++] = 't';
2475 1408 : pszOutput[iOut++] = ';';
2476 : }
2477 42525700 : else if (pszInput[iIn] == '>')
2478 : {
2479 1534 : pszOutput[iOut++] = '&';
2480 1534 : pszOutput[iOut++] = 'g';
2481 1534 : pszOutput[iOut++] = 't';
2482 1534 : pszOutput[iOut++] = ';';
2483 : }
2484 42524200 : else if (pszInput[iIn] == '&')
2485 : {
2486 1653 : pszOutput[iOut++] = '&';
2487 1653 : pszOutput[iOut++] = 'a';
2488 1653 : pszOutput[iOut++] = 'm';
2489 1653 : pszOutput[iOut++] = 'p';
2490 1653 : pszOutput[iOut++] = ';';
2491 : }
2492 42522500 : else if (pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES)
2493 : {
2494 2700 : pszOutput[iOut++] = '&';
2495 2700 : pszOutput[iOut++] = 'q';
2496 2700 : pszOutput[iOut++] = 'u';
2497 2700 : pszOutput[iOut++] = 'o';
2498 2700 : pszOutput[iOut++] = 't';
2499 2700 : pszOutput[iOut++] = ';';
2500 : }
2501 : // Python 2 does not display the UTF-8 character corresponding
2502 : // to the byte-order mark (BOM), so escape it.
2503 42519800 : else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] ==
2504 2 : 0xEF &&
2505 : (reinterpret_cast<const unsigned char *>(
2506 2 : pszInput))[iIn + 1] == 0xBB &&
2507 : (reinterpret_cast<const unsigned char *>(
2508 2 : pszInput))[iIn + 2] == 0xBF)
2509 : {
2510 2 : pszOutput[iOut++] = '&';
2511 2 : pszOutput[iOut++] = '#';
2512 2 : pszOutput[iOut++] = 'x';
2513 2 : pszOutput[iOut++] = 'F';
2514 2 : pszOutput[iOut++] = 'E';
2515 2 : pszOutput[iOut++] = 'F';
2516 2 : pszOutput[iOut++] = 'F';
2517 2 : pszOutput[iOut++] = ';';
2518 2 : iIn += 2;
2519 : }
2520 42519800 : else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] <
2521 21952 : 0x20 &&
2522 21952 : pszInput[iIn] != 0x9 && pszInput[iIn] != 0xA &&
2523 146 : pszInput[iIn] != 0xD)
2524 : {
2525 : // These control characters are unrepresentable in XML format,
2526 : // so we just drop them. #4117
2527 : }
2528 : else
2529 : {
2530 42519800 : pszOutput[iOut++] = pszInput[iIn];
2531 : }
2532 : }
2533 713680 : pszOutput[iOut++] = '\0';
2534 : }
2535 1782 : else if (nScheme == CPLES_URL) // Untested at implementation.
2536 : {
2537 15538 : for (size_t iIn = 0; iIn < szLength; ++iIn)
2538 : {
2539 14889 : if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') ||
2540 8063 : (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') ||
2541 3073 : (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') ||
2542 1804 : pszInput[iIn] == '$' || pszInput[iIn] == '-' ||
2543 1716 : pszInput[iIn] == '_' || pszInput[iIn] == '.' ||
2544 702 : pszInput[iIn] == '+' || pszInput[iIn] == '!' ||
2545 684 : pszInput[iIn] == '*' || pszInput[iIn] == '\'' ||
2546 682 : pszInput[iIn] == '(' || pszInput[iIn] == ')' ||
2547 672 : pszInput[iIn] == ',')
2548 : {
2549 14223 : pszOutput[iOut++] = pszInput[iIn];
2550 : }
2551 : else
2552 : {
2553 666 : snprintf(pszOutput + iOut, nSizeAlloc - iOut, "%%%02X",
2554 666 : static_cast<unsigned char>(pszInput[iIn]));
2555 666 : iOut += 3;
2556 : }
2557 : }
2558 649 : pszOutput[iOut++] = '\0';
2559 : }
2560 1133 : else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI)
2561 : {
2562 855 : const char chQuote = nScheme == CPLES_SQL ? '\'' : '\"';
2563 12084 : for (size_t iIn = 0; iIn < szLength; ++iIn)
2564 : {
2565 11229 : if (pszInput[iIn] == chQuote)
2566 : {
2567 5 : pszOutput[iOut++] = chQuote;
2568 5 : pszOutput[iOut++] = chQuote;
2569 : }
2570 : else
2571 : {
2572 11224 : pszOutput[iOut++] = pszInput[iIn];
2573 : }
2574 : }
2575 855 : pszOutput[iOut++] = '\0';
2576 : }
2577 278 : else if (nScheme == CPLES_CSV || nScheme == CPLES_CSV_FORCE_QUOTING)
2578 : {
2579 278 : pszOutput[iOut++] = '\"';
2580 :
2581 13461 : for (size_t iIn = 0; iIn < szLength; ++iIn)
2582 : {
2583 13183 : if (pszInput[iIn] == '\"')
2584 : {
2585 169 : pszOutput[iOut++] = '\"';
2586 169 : pszOutput[iOut++] = '\"';
2587 : }
2588 : else
2589 13014 : pszOutput[iOut++] = pszInput[iIn];
2590 : }
2591 278 : pszOutput[iOut++] = '\"';
2592 278 : pszOutput[iOut++] = '\0';
2593 : }
2594 :
2595 715655 : return pszOutput;
2596 : #undef nLength
2597 : }
2598 :
2599 : /************************************************************************/
2600 : /* CPLUnescapeString() */
2601 : /************************************************************************/
2602 :
2603 : /**
2604 : * Unescape a string.
2605 : *
2606 : * This function does the opposite of CPLEscapeString(). Given a string
2607 : * with special values escaped according to some scheme, it will return a
2608 : * new copy of the string returned to its original form.
2609 : *
2610 : * @param pszInput the input string. This is a zero terminated string.
2611 : * @param pnLength location to return the length of the unescaped string,
2612 : * which may in some cases include embedded '\\0' characters.
2613 : * @param nScheme the escaped scheme to undo (see CPLEscapeString() for a
2614 : * list). Does not yet support CSV.
2615 : *
2616 : * @return a copy of the unescaped string that should be freed by the
2617 : * application using CPLFree() when no longer needed.
2618 : */
2619 :
2620 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
2621 40406 : char *CPLUnescapeString(const char *pszInput, int *pnLength, int nScheme)
2622 :
2623 : {
2624 40406 : int iOut = 0;
2625 :
2626 : // TODO: Why times 4?
2627 40406 : char *pszOutput = static_cast<char *>(CPLMalloc(4 * strlen(pszInput) + 1));
2628 40401 : pszOutput[0] = '\0';
2629 :
2630 40401 : if (nScheme == CPLES_BackslashQuotable)
2631 : {
2632 58939 : for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
2633 : {
2634 58378 : if (pszInput[iIn] == '\\')
2635 : {
2636 975 : ++iIn;
2637 975 : if (pszInput[iIn] == '\0')
2638 0 : break;
2639 975 : if (pszInput[iIn] == 'n')
2640 6 : pszOutput[iOut++] = '\n';
2641 969 : else if (pszInput[iIn] == '0')
2642 881 : pszOutput[iOut++] = '\0';
2643 : else
2644 88 : pszOutput[iOut++] = pszInput[iIn];
2645 : }
2646 : else
2647 : {
2648 57403 : pszOutput[iOut++] = pszInput[iIn];
2649 : }
2650 : }
2651 : }
2652 39840 : else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES)
2653 : {
2654 38980 : char ch = '\0';
2655 33785400 : for (int iIn = 0; (ch = pszInput[iIn]) != '\0'; ++iIn)
2656 : {
2657 33746400 : if (ch != '&')
2658 : {
2659 33380400 : pszOutput[iOut++] = ch;
2660 : }
2661 366042 : else if (STARTS_WITH_CI(pszInput + iIn, "<"))
2662 : {
2663 5048 : pszOutput[iOut++] = '<';
2664 5048 : iIn += 3;
2665 : }
2666 360994 : else if (STARTS_WITH_CI(pszInput + iIn, ">"))
2667 : {
2668 5176 : pszOutput[iOut++] = '>';
2669 5176 : iIn += 3;
2670 : }
2671 355818 : else if (STARTS_WITH_CI(pszInput + iIn, "&"))
2672 : {
2673 257595 : pszOutput[iOut++] = '&';
2674 257595 : iIn += 4;
2675 : }
2676 98223 : else if (STARTS_WITH_CI(pszInput + iIn, "'"))
2677 : {
2678 686 : pszOutput[iOut++] = '\'';
2679 686 : iIn += 5;
2680 : }
2681 97537 : else if (STARTS_WITH_CI(pszInput + iIn, """))
2682 : {
2683 97369 : pszOutput[iOut++] = '"';
2684 97369 : iIn += 5;
2685 : }
2686 168 : else if (STARTS_WITH_CI(pszInput + iIn, "&#x"))
2687 : {
2688 4 : wchar_t anVal[2] = {0, 0};
2689 4 : iIn += 3;
2690 :
2691 4 : unsigned int nVal = 0;
2692 : while (true)
2693 : {
2694 10 : ch = pszInput[iIn++];
2695 10 : if (ch >= 'a' && ch <= 'f')
2696 1 : nVal = nVal * 16U +
2697 : static_cast<unsigned int>(ch - 'a' + 10);
2698 9 : else if (ch >= 'A' && ch <= 'F')
2699 1 : nVal = nVal * 16U +
2700 : static_cast<unsigned int>(ch - 'A' + 10);
2701 8 : else if (ch >= '0' && ch <= '9')
2702 4 : nVal = nVal * 16U + static_cast<unsigned int>(ch - '0');
2703 : else
2704 : break;
2705 : }
2706 4 : anVal[0] = static_cast<wchar_t>(nVal);
2707 4 : if (ch != ';')
2708 1 : break;
2709 3 : iIn--;
2710 :
2711 : char *pszUTF8 =
2712 3 : CPLRecodeFromWChar(anVal, "WCHAR_T", CPL_ENC_UTF8);
2713 3 : int nLen = static_cast<int>(strlen(pszUTF8));
2714 3 : memcpy(pszOutput + iOut, pszUTF8, nLen);
2715 3 : CPLFree(pszUTF8);
2716 3 : iOut += nLen;
2717 : }
2718 164 : else if (STARTS_WITH_CI(pszInput + iIn, "&#"))
2719 : {
2720 159 : wchar_t anVal[2] = {0, 0};
2721 159 : iIn += 2;
2722 :
2723 159 : unsigned int nVal = 0;
2724 : while (true)
2725 : {
2726 646 : ch = pszInput[iIn++];
2727 646 : if (ch >= '0' && ch <= '9')
2728 487 : nVal = nVal * 10U + static_cast<unsigned int>(ch - '0');
2729 : else
2730 : break;
2731 : }
2732 159 : anVal[0] = static_cast<wchar_t>(nVal);
2733 159 : if (ch != ';')
2734 1 : break;
2735 158 : iIn--;
2736 :
2737 : char *pszUTF8 =
2738 158 : CPLRecodeFromWChar(anVal, "WCHAR_T", CPL_ENC_UTF8);
2739 158 : const int nLen = static_cast<int>(strlen(pszUTF8));
2740 158 : memcpy(pszOutput + iOut, pszUTF8, nLen);
2741 158 : CPLFree(pszUTF8);
2742 158 : iOut += nLen;
2743 : }
2744 : else
2745 : {
2746 : // Illegal escape sequence.
2747 5 : CPLDebug("CPL",
2748 : "Error unescaping CPLES_XML text, '&' character "
2749 : "followed by unhandled escape sequence.");
2750 2 : break;
2751 : }
2752 38977 : }
2753 : }
2754 860 : else if (nScheme == CPLES_URL)
2755 : {
2756 41323 : for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
2757 : {
2758 40508 : if (pszInput[iIn] == '%' && pszInput[iIn + 1] != '\0' &&
2759 1144 : pszInput[iIn + 2] != '\0')
2760 : {
2761 1144 : int nHexChar = 0;
2762 :
2763 1144 : if (pszInput[iIn + 1] >= 'A' && pszInput[iIn + 1] <= 'F')
2764 0 : nHexChar += 16 * (pszInput[iIn + 1] - 'A' + 10);
2765 1144 : else if (pszInput[iIn + 1] >= 'a' && pszInput[iIn + 1] <= 'f')
2766 0 : nHexChar += 16 * (pszInput[iIn + 1] - 'a' + 10);
2767 1144 : else if (pszInput[iIn + 1] >= '0' && pszInput[iIn + 1] <= '9')
2768 1144 : nHexChar += 16 * (pszInput[iIn + 1] - '0');
2769 : else
2770 0 : CPLDebug("CPL",
2771 : "Error unescaping CPLES_URL text, percent not "
2772 : "followed by two hex digits.");
2773 :
2774 1144 : if (pszInput[iIn + 2] >= 'A' && pszInput[iIn + 2] <= 'F')
2775 1120 : nHexChar += pszInput[iIn + 2] - 'A' + 10;
2776 24 : else if (pszInput[iIn + 2] >= 'a' && pszInput[iIn + 2] <= 'f')
2777 0 : nHexChar += pszInput[iIn + 2] - 'a' + 10;
2778 24 : else if (pszInput[iIn + 2] >= '0' && pszInput[iIn + 2] <= '9')
2779 24 : nHexChar += pszInput[iIn + 2] - '0';
2780 : else
2781 0 : CPLDebug("CPL",
2782 : "Error unescaping CPLES_URL text, percent not "
2783 : "followed by two hex digits.");
2784 :
2785 1144 : pszOutput[iOut++] = static_cast<char>(nHexChar);
2786 1144 : iIn += 2;
2787 : }
2788 39364 : else if (pszInput[iIn] == '+')
2789 : {
2790 0 : pszOutput[iOut++] = ' ';
2791 : }
2792 : else
2793 : {
2794 39364 : pszOutput[iOut++] = pszInput[iIn];
2795 : }
2796 : }
2797 : }
2798 45 : else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI)
2799 : {
2800 45 : char szQuote = nScheme == CPLES_SQL ? '\'' : '\"';
2801 565 : for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
2802 : {
2803 520 : if (pszInput[iIn] == szQuote && pszInput[iIn + 1] == szQuote)
2804 : {
2805 3 : ++iIn;
2806 3 : pszOutput[iOut++] = pszInput[iIn];
2807 : }
2808 : else
2809 : {
2810 517 : pszOutput[iOut++] = pszInput[iIn];
2811 : }
2812 45 : }
2813 : }
2814 0 : else if (nScheme == CPLES_CSV)
2815 : {
2816 0 : CPLError(CE_Fatal, CPLE_NotSupported,
2817 : "CSV Unescaping not yet implemented.");
2818 : }
2819 : else
2820 : {
2821 0 : CPLError(CE_Fatal, CPLE_NotSupported, "Unknown escaping style.");
2822 : }
2823 :
2824 40405 : pszOutput[iOut] = '\0';
2825 :
2826 40405 : if (pnLength != nullptr)
2827 24138 : *pnLength = iOut;
2828 :
2829 40405 : return pszOutput;
2830 : }
2831 :
2832 : /************************************************************************/
2833 : /* CPLBinaryToHex() */
2834 : /************************************************************************/
2835 :
2836 : /**
2837 : * Binary to hexadecimal translation.
2838 : *
2839 : * @param nBytes number of bytes of binary data in pabyData.
2840 : * @param pabyData array of data bytes to translate.
2841 : *
2842 : * @return hexadecimal translation, zero terminated. Free with CPLFree().
2843 : */
2844 :
2845 4313 : char *CPLBinaryToHex(int nBytes, const GByte *pabyData)
2846 :
2847 : {
2848 4313 : CPLAssert(nBytes >= 0);
2849 : char *pszHex = static_cast<char *>(
2850 4313 : VSI_MALLOC_VERBOSE(static_cast<size_t>(nBytes) * 2 + 1));
2851 4313 : if (!pszHex)
2852 : {
2853 0 : pszHex = CPLStrdup("");
2854 0 : return pszHex;
2855 : }
2856 4313 : pszHex[nBytes * 2] = '\0';
2857 :
2858 4313 : constexpr char achHex[] = "0123456789ABCDEF";
2859 :
2860 261289 : for (size_t i = 0; i < static_cast<size_t>(nBytes); ++i)
2861 : {
2862 256976 : const int nLow = pabyData[i] & 0x0f;
2863 256976 : const int nHigh = (pabyData[i] & 0xf0) >> 4;
2864 :
2865 256976 : pszHex[i * 2] = achHex[nHigh];
2866 256976 : pszHex[i * 2 + 1] = achHex[nLow];
2867 : }
2868 :
2869 4313 : return pszHex;
2870 : }
2871 :
2872 : /************************************************************************/
2873 : /* CPLHexToBinary() */
2874 : /************************************************************************/
2875 :
2876 : constexpr unsigned char hex2char[256] = {
2877 : // Not Hex characters.
2878 : 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,
2879 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2880 : // 0-9
2881 : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2882 : // A-F
2883 : 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2884 : // Not Hex characters.
2885 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2886 : // a-f
2887 : 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2888 : 0, 0, 0, 0, 0, 0, 0, 0, 0,
2889 : // Not Hex characters (upper 128 characters).
2890 : 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,
2891 : 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,
2892 : 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,
2893 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2894 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2895 : 0, 0, 0};
2896 :
2897 : /**
2898 : * Hexadecimal to binary translation
2899 : *
2900 : * @param pszHex the input hex encoded string.
2901 : * @param pnBytes the returned count of decoded bytes placed here.
2902 : *
2903 : * @return returns binary buffer of data - free with CPLFree().
2904 : */
2905 :
2906 3990 : GByte *CPLHexToBinary(const char *pszHex, int *pnBytes)
2907 : {
2908 3990 : const GByte *pabyHex = reinterpret_cast<const GByte *>(pszHex);
2909 3990 : const size_t nHexLen = strlen(pszHex);
2910 :
2911 3990 : GByte *pabyWKB = static_cast<GByte *>(CPLMalloc(nHexLen / 2 + 2));
2912 :
2913 1051740 : for (size_t i = 0; i < nHexLen / 2; ++i)
2914 : {
2915 1047750 : const unsigned char h1 = hex2char[pabyHex[2 * i]];
2916 1047750 : const unsigned char h2 = hex2char[pabyHex[2 * i + 1]];
2917 :
2918 : // First character is high bits, second is low bits.
2919 1047750 : pabyWKB[i] = static_cast<GByte>((h1 << 4) | h2);
2920 : }
2921 3990 : pabyWKB[nHexLen / 2] = 0;
2922 3990 : *pnBytes = static_cast<int>(nHexLen / 2);
2923 :
2924 3990 : return pabyWKB;
2925 : }
2926 :
2927 : /************************************************************************/
2928 : /* CPLGetValueType() */
2929 : /************************************************************************/
2930 :
2931 : /**
2932 : * Detect the type of the value contained in a string, whether it is
2933 : * a real, an integer or a string
2934 : * Leading and trailing spaces are skipped in the analysis.
2935 : *
2936 : * Note: in the context of this function, integer must be understood in a
2937 : * broad sense. It does not mean that the value can fit into a 32 bit integer
2938 : * for example. It might be larger.
2939 : *
2940 : * @param pszValue the string to analyze
2941 : *
2942 : * @return returns the type of the value contained in the string.
2943 : */
2944 :
2945 167180 : CPLValueType CPLGetValueType(const char *pszValue)
2946 : {
2947 : // Doubles : "+25.e+3", "-25.e-3", "25.e3", "25e3", " 25e3 "
2948 : // Not doubles: "25e 3", "25e.3", "-2-5e3", "2-5e3", "25.25.3", "-3d", "d1"
2949 : // "XXeYYYYYYYYYYYYYYYYYYY" that evaluates to infinity
2950 :
2951 167180 : if (pszValue == nullptr)
2952 0 : return CPL_VALUE_STRING;
2953 :
2954 167180 : const char *pszValueInit = pszValue;
2955 :
2956 : // Skip leading spaces.
2957 167231 : while (isspace(static_cast<unsigned char>(*pszValue)))
2958 51 : ++pszValue;
2959 :
2960 167180 : if (*pszValue == '\0')
2961 392 : return CPL_VALUE_STRING;
2962 :
2963 : // Skip leading + or -.
2964 166788 : if (*pszValue == '+' || *pszValue == '-')
2965 11355 : ++pszValue;
2966 :
2967 166788 : constexpr char DIGIT_ZERO = '0';
2968 166788 : if (pszValue[0] == DIGIT_ZERO && pszValue[1] != '\0' && pszValue[1] != '.')
2969 1122 : return CPL_VALUE_STRING;
2970 :
2971 165666 : bool bFoundDot = false;
2972 165666 : bool bFoundExponent = false;
2973 165666 : bool bIsLastCharExponent = false;
2974 165666 : bool bIsReal = false;
2975 165666 : const char *pszAfterExponent = nullptr;
2976 165666 : bool bFoundMantissa = false;
2977 :
2978 575430 : for (; *pszValue != '\0'; ++pszValue)
2979 : {
2980 462839 : if (isdigit(static_cast<unsigned char>(*pszValue)))
2981 : {
2982 393667 : bIsLastCharExponent = false;
2983 393667 : bFoundMantissa = true;
2984 : }
2985 69172 : else if (isspace(static_cast<unsigned char>(*pszValue)))
2986 : {
2987 831 : const char *pszTmp = pszValue;
2988 1666 : while (isspace(static_cast<unsigned char>(*pszTmp)))
2989 835 : ++pszTmp;
2990 831 : if (*pszTmp == 0)
2991 24 : break;
2992 : else
2993 807 : return CPL_VALUE_STRING;
2994 : }
2995 68341 : else if (*pszValue == '-' || *pszValue == '+')
2996 : {
2997 625 : if (bIsLastCharExponent)
2998 : {
2999 : // Do nothing.
3000 : }
3001 : else
3002 : {
3003 365 : return CPL_VALUE_STRING;
3004 : }
3005 260 : bIsLastCharExponent = false;
3006 : }
3007 67716 : else if (*pszValue == '.')
3008 : {
3009 15585 : bIsReal = true;
3010 15585 : if (!bFoundDot && !bIsLastCharExponent)
3011 15567 : bFoundDot = true;
3012 : else
3013 18 : return CPL_VALUE_STRING;
3014 15567 : bIsLastCharExponent = false;
3015 : }
3016 52131 : else if (*pszValue == 'D' || *pszValue == 'd' || *pszValue == 'E' ||
3017 47041 : *pszValue == 'e')
3018 : {
3019 5367 : if (!bFoundMantissa)
3020 5094 : return CPL_VALUE_STRING;
3021 273 : if (!(pszValue[1] == '+' || pszValue[1] == '-' ||
3022 10 : isdigit(static_cast<unsigned char>(pszValue[1]))))
3023 2 : return CPL_VALUE_STRING;
3024 :
3025 271 : bIsReal = true;
3026 271 : if (!bFoundExponent)
3027 270 : bFoundExponent = true;
3028 : else
3029 1 : return CPL_VALUE_STRING;
3030 270 : pszAfterExponent = pszValue + 1;
3031 270 : bIsLastCharExponent = true;
3032 : }
3033 : else
3034 : {
3035 46764 : return CPL_VALUE_STRING;
3036 : }
3037 : }
3038 :
3039 112615 : if (bIsReal && pszAfterExponent && strlen(pszAfterExponent) > 3)
3040 : {
3041 : // cppcheck-suppress unreadVariable
3042 15 : const double dfVal = CPLAtof(pszValueInit);
3043 15 : if (std::isinf(dfVal))
3044 1 : return CPL_VALUE_STRING;
3045 : }
3046 :
3047 112595 : return bIsReal ? CPL_VALUE_REAL : CPL_VALUE_INTEGER;
3048 : }
3049 :
3050 : /************************************************************************/
3051 : /* CPLStrlcpy() */
3052 : /************************************************************************/
3053 :
3054 : /**
3055 : * Copy source string to a destination buffer.
3056 : *
3057 : * This function ensures that the destination buffer is always NUL terminated
3058 : * (provided that its length is at least 1).
3059 : *
3060 : * This function is designed to be a safer, more consistent, and less error
3061 : * prone replacement for strncpy. Its contract is identical to libbsd's strlcpy.
3062 : *
3063 : * Truncation can be detected by testing if the return value of CPLStrlcpy
3064 : * is greater or equal to nDestSize.
3065 :
3066 : \verbatim
3067 : char szDest[5] = {};
3068 : if( CPLStrlcpy(szDest, "abcde", sizeof(szDest)) >= sizeof(szDest) )
3069 : fprintf(stderr, "truncation occurred !\n");
3070 : \endverbatim
3071 :
3072 : * @param pszDest destination buffer
3073 : * @param pszSrc source string. Must be NUL terminated
3074 : * @param nDestSize size of destination buffer (including space for the NUL
3075 : * terminator character)
3076 : *
3077 : * @return the length of the source string (=strlen(pszSrc))
3078 : *
3079 : */
3080 90254 : size_t CPLStrlcpy(char *pszDest, const char *pszSrc, size_t nDestSize)
3081 : {
3082 90254 : if (nDestSize == 0)
3083 0 : return strlen(pszSrc);
3084 :
3085 90254 : char *pszDestIter = pszDest;
3086 90254 : const char *pszSrcIter = pszSrc;
3087 :
3088 90254 : --nDestSize;
3089 883658 : while (nDestSize != 0 && *pszSrcIter != '\0')
3090 : {
3091 793404 : *pszDestIter = *pszSrcIter;
3092 793404 : ++pszDestIter;
3093 793404 : ++pszSrcIter;
3094 793404 : --nDestSize;
3095 : }
3096 90254 : *pszDestIter = '\0';
3097 90254 : return pszSrcIter - pszSrc + strlen(pszSrcIter);
3098 : }
3099 :
3100 : /************************************************************************/
3101 : /* CPLStrlcat() */
3102 : /************************************************************************/
3103 :
3104 : /**
3105 : * Appends a source string to a destination buffer.
3106 : *
3107 : * This function ensures that the destination buffer is always NUL terminated
3108 : * (provided that its length is at least 1 and that there is at least one byte
3109 : * free in pszDest, that is to say strlen(pszDest_before) < nDestSize)
3110 : *
3111 : * This function is designed to be a safer, more consistent, and less error
3112 : * prone replacement for strncat. Its contract is identical to libbsd's strlcat.
3113 : *
3114 : * Truncation can be detected by testing if the return value of CPLStrlcat
3115 : * is greater or equal to nDestSize.
3116 :
3117 : \verbatim
3118 : char szDest[5] = {};
3119 : CPLStrlcpy(szDest, "ab", sizeof(szDest));
3120 : if( CPLStrlcat(szDest, "cde", sizeof(szDest)) >= sizeof(szDest) )
3121 : fprintf(stderr, "truncation occurred !\n");
3122 : \endverbatim
3123 :
3124 : * @param pszDest destination buffer. Must be NUL terminated before
3125 : * running CPLStrlcat
3126 : * @param pszSrc source string. Must be NUL terminated
3127 : * @param nDestSize size of destination buffer (including space for the
3128 : * NUL terminator character)
3129 : *
3130 : * @return the theoretical length of the destination string after concatenation
3131 : * (=strlen(pszDest_before) + strlen(pszSrc)).
3132 : * If strlen(pszDest_before) >= nDestSize, then it returns
3133 : * nDestSize + strlen(pszSrc)
3134 : *
3135 : */
3136 753 : size_t CPLStrlcat(char *pszDest, const char *pszSrc, size_t nDestSize)
3137 : {
3138 753 : char *pszDestIter = pszDest;
3139 :
3140 55921 : while (nDestSize != 0 && *pszDestIter != '\0')
3141 : {
3142 55168 : ++pszDestIter;
3143 55168 : --nDestSize;
3144 : }
3145 :
3146 753 : return pszDestIter - pszDest + CPLStrlcpy(pszDestIter, pszSrc, nDestSize);
3147 : }
3148 :
3149 : /************************************************************************/
3150 : /* CPLStrnlen() */
3151 : /************************************************************************/
3152 :
3153 : /**
3154 : * Returns the length of a NUL terminated string by reading at most
3155 : * the specified number of bytes.
3156 : *
3157 : * The CPLStrnlen() function returns min(strlen(pszStr), nMaxLen).
3158 : * Only the first nMaxLen bytes of the string will be read. Useful to
3159 : * test if a string contains at least nMaxLen characters without reading
3160 : * the full string up to the NUL terminating character.
3161 : *
3162 : * @param pszStr a NUL terminated string
3163 : * @param nMaxLen maximum number of bytes to read in pszStr
3164 : *
3165 : * @return strlen(pszStr) if the length is lesser than nMaxLen, otherwise
3166 : * nMaxLen if the NUL character has not been found in the first nMaxLen bytes.
3167 : *
3168 : */
3169 :
3170 525635 : size_t CPLStrnlen(const char *pszStr, size_t nMaxLen)
3171 : {
3172 525635 : size_t nLen = 0;
3173 29243700 : while (nLen < nMaxLen && *pszStr != '\0')
3174 : {
3175 28718100 : ++nLen;
3176 28718100 : ++pszStr;
3177 : }
3178 525635 : return nLen;
3179 : }
3180 :
3181 : /************************************************************************/
3182 : /* CSLParseCommandLine() */
3183 : /************************************************************************/
3184 :
3185 : /**
3186 : * Tokenize command line arguments in a list of strings.
3187 : *
3188 : * @param pszCommandLine command line
3189 : *
3190 : * @return NULL terminated list of strings to free with CSLDestroy()
3191 : *
3192 : */
3193 1018 : char **CSLParseCommandLine(const char *pszCommandLine)
3194 : {
3195 1018 : return CSLTokenizeString(pszCommandLine);
3196 : }
3197 :
3198 : /************************************************************************/
3199 : /* CPLToupper() */
3200 : /************************************************************************/
3201 :
3202 : /** Converts a (ASCII) lowercase character to uppercase.
3203 : *
3204 : * Same as standard toupper(), except that it is not locale sensitive.
3205 : *
3206 : * @since GDAL 3.9
3207 : */
3208 29479100 : int CPLToupper(int c)
3209 : {
3210 29479100 : return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
3211 : }
3212 :
3213 : /************************************************************************/
3214 : /* CPLTolower() */
3215 : /************************************************************************/
3216 :
3217 : /** Converts a (ASCII) uppercase character to lowercase.
3218 : *
3219 : * Same as standard tolower(), except that it is not locale sensitive.
3220 : *
3221 : * @since GDAL 3.9
3222 : */
3223 21940700 : int CPLTolower(int c)
3224 : {
3225 21940700 : return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c;
3226 : }
3227 :
3228 : /************************************************************************/
3229 : /* CPLRemoveSQLComments() */
3230 : /************************************************************************/
3231 :
3232 : /** Remove SQL comments from a string
3233 : *
3234 : * @param osInput Input string.
3235 : * @since GDAL 3.11
3236 : */
3237 55 : std::string CPLRemoveSQLComments(const std::string &osInput)
3238 : {
3239 : const CPLStringList aosLines(
3240 110 : CSLTokenizeStringComplex(osInput.c_str(), "\r\n", FALSE, FALSE));
3241 55 : std::string osSQL;
3242 121 : for (const char *pszLine : aosLines)
3243 : {
3244 66 : char chQuote = 0;
3245 66 : int i = 0;
3246 1135 : for (; pszLine[i] != '\0'; ++i)
3247 : {
3248 1078 : if (chQuote)
3249 : {
3250 24 : if (pszLine[i] == chQuote)
3251 : {
3252 : // Deal with escaped quote character which is repeated,
3253 : // so 'foo''bar' or "foo""bar"
3254 7 : if (pszLine[i + 1] == chQuote)
3255 : {
3256 2 : i++;
3257 : }
3258 : else
3259 : {
3260 5 : chQuote = 0;
3261 : }
3262 : }
3263 : }
3264 1054 : else if (pszLine[i] == '\'' || pszLine[i] == '"')
3265 : {
3266 5 : chQuote = pszLine[i];
3267 : }
3268 1049 : else if (pszLine[i] == '-' && pszLine[i + 1] == '-')
3269 : {
3270 9 : break;
3271 : }
3272 : }
3273 66 : if (i > 0)
3274 : {
3275 59 : if (!osSQL.empty())
3276 4 : osSQL += ' ';
3277 59 : osSQL.append(pszLine, i);
3278 : }
3279 : }
3280 110 : return osSQL;
3281 : }
3282 :
3283 : namespace cpl
3284 : {
3285 :
3286 83933300 : static bool CaseInsensitiveCompare(unsigned char c1, unsigned char c2)
3287 : {
3288 83933300 : return toupper(c1) == toupper(c2);
3289 : }
3290 :
3291 : /** Check whether the start of one string is equivalent to another string,
3292 : * considering case.
3293 : *
3294 : * @param str string to test
3295 : * @param prefix expected prefix
3296 : * @return true if the string starts with the prefix
3297 : *
3298 : * @since GDAL 3.11
3299 : */
3300 17491400 : bool starts_with(std::string_view str, std::string_view prefix)
3301 : {
3302 25823400 : return str.size() >= prefix.size() &&
3303 25823300 : str.compare(0, prefix.size(), prefix) == 0;
3304 : }
3305 :
3306 : /** Check whether the start of one string is equivalent to another string,
3307 : * not considering case.
3308 : *
3309 : * @param str string to test
3310 : * @param prefix expected prefix
3311 : * @return true if the string starts with the prefix
3312 : *
3313 : * @since GDAL 3.14
3314 : */
3315 8761520 : bool starts_with_ci(std::string_view str, std::string_view prefix)
3316 : {
3317 13665700 : return str.size() >= prefix.size() &&
3318 4904180 : std::search(str.begin(), str.end(), prefix.begin(), prefix.end(),
3319 13665700 : CaseInsensitiveCompare) != str.end();
3320 : }
3321 :
3322 : /** Check whether the end of one string is equivalent to another string,
3323 : * considering case.
3324 : *
3325 : * @param str string to test
3326 : * @param suffix expected suffix
3327 : * @return true if the string ends with the suffix
3328 : *
3329 : * @since GDAL 3.11
3330 : */
3331 255651 : bool ends_with(std::string_view str, std::string_view suffix)
3332 : {
3333 757536 : return str.size() >= suffix.size() &&
3334 501887 : (suffix.empty() || str.compare(str.size() - suffix.size(),
3335 255647 : suffix.size(), suffix) == 0);
3336 : }
3337 :
3338 : /** Check whether the end of one string is equivalent to another string,
3339 : * not considering case.
3340 : *
3341 : * @param str string to test
3342 : * @param suffix expected suffix
3343 : * @return true if the string ends with the suffix
3344 : *
3345 : * @since GDAL 3.14
3346 : */
3347 3 : bool ends_with_ci(std::string_view str, std::string_view suffix)
3348 : {
3349 8 : return str.size() >= suffix.size() &&
3350 5 : (suffix.empty() ||
3351 2 : std::search(str.end() - suffix.size(), str.end(), suffix.begin(),
3352 5 : suffix.end(), CaseInsensitiveCompare) != str.end());
3353 : }
3354 :
3355 : /** Check whether two strings are equal, considering case.
3356 : *
3357 : * @param str1 first string to test
3358 : * @param str2 second string to test
3359 : * @return true if the strings are considered equal
3360 : *
3361 : * @since GDAL 3.14
3362 : */
3363 6 : bool equals(std::string_view str1, std::string_view str2)
3364 : {
3365 6 : return str1 == str2;
3366 : }
3367 :
3368 : /** Check whether two strings are equal, not considering case.
3369 : *
3370 : * @param str1 first string to test
3371 : * @param str2 second string to test
3372 : * @return true if the strings are considered equal
3373 : *
3374 : * @since GDAL 3.14
3375 : */
3376 2 : bool equals_ci(std::string_view str1, std::string_view str2)
3377 : {
3378 3 : return str1.size() == str2.size() &&
3379 1 : std::equal(str1.begin(), str1.end(), str2.begin(),
3380 2 : CaseInsensitiveCompare);
3381 : }
3382 :
3383 : /** Remove leading and trailing whitespace from a string.
3384 : * The returned string view will be a reference into the input.
3385 : *
3386 : * @param str string to trim
3387 : * @return trimmed string
3388 : *
3389 : * @since GDAL 3.14
3390 : */
3391 15102700 : std::string_view trim(std::string_view str)
3392 : {
3393 15102700 : if (str.empty())
3394 : {
3395 11174 : return str;
3396 : }
3397 :
3398 15091500 : size_t start = 0;
3399 30343300 : while (start < str.size() &&
3400 15170000 : isspace(static_cast<unsigned char>(str[start])))
3401 : {
3402 81779 : start++;
3403 : }
3404 :
3405 15091500 : if (start == str.size())
3406 : {
3407 3249 : return str.substr(start, 0);
3408 : }
3409 :
3410 15088200 : size_t stop = str.size();
3411 15102400 : while (stop > start && isspace(static_cast<unsigned char>(str[stop - 1])))
3412 : {
3413 14202 : stop--;
3414 : }
3415 :
3416 15088200 : return str.substr(start, stop - start);
3417 : }
3418 :
3419 1 : std::string_view trim(const char *pszStr)
3420 : {
3421 1 : return trim(std::string_view(pszStr));
3422 : }
3423 :
3424 : /** Remove leading whitespace from a string.
3425 : * The returned string view will be a reference into the input.
3426 : *
3427 : * @param str string to trim
3428 : * @return trimmed string
3429 : *
3430 : * @since GDAL 3.14
3431 : */
3432 34812 : std::string_view ltrim(std::string_view str)
3433 : {
3434 34812 : if (str.empty())
3435 : {
3436 5282 : return str;
3437 : }
3438 :
3439 29530 : size_t start = 0;
3440 68473 : while (start < str.size() &&
3441 34235 : isspace(static_cast<unsigned char>(str[start])))
3442 : {
3443 4708 : start++;
3444 : }
3445 :
3446 29530 : return str.substr(start);
3447 : }
3448 :
3449 1 : std::string_view ltrim(const char *pszStr)
3450 : {
3451 1 : return ltrim(std::string_view(pszStr));
3452 : }
3453 :
3454 : /** Remove trailing whitespace from a string.
3455 : * The returned string view will be a reference into the input.
3456 : *
3457 : * @param str string to trim
3458 : * @return trimmed string
3459 : *
3460 : * @since GDAL 3.14
3461 : */
3462 34759 : std::string_view rtrim(std::string_view str)
3463 : {
3464 34759 : if (str.empty())
3465 : {
3466 5284 : return str;
3467 : }
3468 :
3469 29475 : size_t stop = str.size();
3470 29526 : while (stop > 0 && isspace(static_cast<unsigned char>(str[stop - 1])))
3471 : {
3472 51 : stop--;
3473 : }
3474 :
3475 29475 : return str.substr(0, stop);
3476 : }
3477 :
3478 1 : std::string_view rtrim(const char *pszStr)
3479 : {
3480 1 : return rtrim(std::string_view(pszStr));
3481 : }
3482 :
3483 : } // namespace cpl
|