/src/gdal/port/cpl_string.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * Name: cpl_string.cpp |
4 | | * Project: CPL - Common Portability Library |
5 | | * Purpose: String and Stringlist manipulation functions. |
6 | | * Author: Daniel Morissette, danmo@videotron.ca |
7 | | * |
8 | | ********************************************************************** |
9 | | * Copyright (c) 1998, Daniel Morissette |
10 | | * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com> |
11 | | * |
12 | | * SPDX-License-Identifier: MIT |
13 | | ********************************************************************** |
14 | | * |
15 | | * Independent Security Audit 2003/04/04 Andrey Kiselev: |
16 | | * Completed audit of this module. All functions may be used without buffer |
17 | | * overflows and stack corruptions with any kind of input data strings with |
18 | | * except of CPLSPrintf() and CSLAppendPrintf() (see note below). |
19 | | * |
20 | | * Security Audit 2003/03/28 warmerda: |
21 | | * Completed security audit. I believe that this module may be safely used |
22 | | * to parse tokenize arbitrary input strings, assemble arbitrary sets of |
23 | | * names values into string lists, unescape and escape text even if provided |
24 | | * by a potentially hostile source. |
25 | | * |
26 | | * CPLSPrintf() and CSLAppendPrintf() may not be safely invoked on |
27 | | * arbitrary length inputs since it has a fixed size output buffer on system |
28 | | * without vsnprintf(). |
29 | | * |
30 | | **********************************************************************/ |
31 | | |
32 | | #undef WARN_STANDARD_PRINTF |
33 | | |
34 | | #include "cpl_port.h" |
35 | | #include "cpl_string.h" |
36 | | |
37 | | #include <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 | | char **CSLAddString(char **papszStrList, const char *pszNewString) |
70 | 11.8M | { |
71 | 11.8M | char **papszRet = CSLAddStringMayFail(papszStrList, pszNewString); |
72 | 11.8M | if (papszRet == nullptr && pszNewString != nullptr) |
73 | 0 | abort(); |
74 | 11.8M | return papszRet; |
75 | 11.8M | } |
76 | | |
77 | | /** Same as CSLAddString() but may return NULL in case of (memory) failure */ |
78 | | char **CSLAddStringMayFail(char **papszStrList, const char *pszNewString) |
79 | 12.5M | { |
80 | 12.5M | if (pszNewString == nullptr) |
81 | 0 | return papszStrList; // Nothing to do! |
82 | | |
83 | 12.5M | char *pszDup = VSI_STRDUP_VERBOSE(pszNewString); |
84 | 12.5M | if (pszDup == nullptr) |
85 | 0 | return nullptr; |
86 | | |
87 | | // Allocate room for the new string. |
88 | 12.5M | char **papszStrListNew = nullptr; |
89 | 12.5M | int nItems = 0; |
90 | | |
91 | 12.5M | if (papszStrList == nullptr) |
92 | 1.41M | papszStrListNew = |
93 | 1.41M | static_cast<char **>(VSI_CALLOC_VERBOSE(2, sizeof(char *))); |
94 | 11.1M | else |
95 | 11.1M | { |
96 | 11.1M | nItems = CSLCount(papszStrList); |
97 | 11.1M | papszStrListNew = static_cast<char **>( |
98 | 11.1M | VSI_REALLOC_VERBOSE(papszStrList, (nItems + 2) * sizeof(char *))); |
99 | 11.1M | } |
100 | 12.5M | if (papszStrListNew == nullptr) |
101 | 0 | { |
102 | 0 | VSIFree(pszDup); |
103 | 0 | return nullptr; |
104 | 0 | } |
105 | | |
106 | | // Copy the string in the list. |
107 | 12.5M | papszStrListNew[nItems] = pszDup; |
108 | 12.5M | papszStrListNew[nItems + 1] = nullptr; |
109 | | |
110 | 12.5M | return papszStrListNew; |
111 | 12.5M | } |
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 | | int CSLCount(CSLConstList papszStrList) |
134 | 102M | { |
135 | 102M | if (!papszStrList) |
136 | 4.17M | return 0; |
137 | | |
138 | 98.1M | int nItems = 0; |
139 | | |
140 | 3.79G | while (*papszStrList != nullptr) |
141 | 3.69G | { |
142 | 3.69G | ++nItems; |
143 | 3.69G | ++papszStrList; |
144 | 3.69G | } |
145 | | |
146 | 98.1M | return nItems; |
147 | 102M | } |
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 | | const char *CSLGetField(CSLConstList papszStrList, int iField) |
160 | | |
161 | 3.71M | { |
162 | 3.71M | if (papszStrList == nullptr || iField < 0) |
163 | 0 | return (""); |
164 | | |
165 | 7.51M | for (int i = 0; i < iField + 1; i++) |
166 | 3.80M | { |
167 | 3.80M | if (papszStrList[i] == nullptr) |
168 | 1.17k | return ""; |
169 | 3.80M | } |
170 | | |
171 | 3.71M | return (papszStrList[iField]); |
172 | 3.71M | } |
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 | | void CPL_STDCALL CSLDestroy(char **papszStrList) |
187 | 427M | { |
188 | 427M | if (!papszStrList) |
189 | 194M | return; |
190 | | |
191 | 699M | for (char **papszPtr = papszStrList; *papszPtr != nullptr; ++papszPtr) |
192 | 466M | { |
193 | 466M | CPLFree(*papszPtr); |
194 | 466M | } |
195 | | |
196 | 233M | CPLFree(papszStrList); |
197 | 233M | } |
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 | | char **CSLDuplicate(CSLConstList papszStrList) |
215 | 4.10M | { |
216 | 4.10M | const int nLines = CSLCount(papszStrList); |
217 | | |
218 | 4.10M | if (nLines == 0) |
219 | 3.74M | return nullptr; |
220 | | |
221 | 361k | CSLConstList papszSrc = papszStrList; |
222 | | |
223 | 361k | char **papszNewList = |
224 | 361k | static_cast<char **>(VSI_MALLOC2_VERBOSE(nLines + 1, sizeof(char *))); |
225 | | |
226 | 361k | char **papszDst = papszNewList; |
227 | | |
228 | 11.4M | for (; *papszSrc != nullptr; ++papszSrc, ++papszDst) |
229 | 11.1M | { |
230 | 11.1M | *papszDst = VSI_STRDUP_VERBOSE(*papszSrc); |
231 | 11.1M | if (*papszDst == nullptr) |
232 | 0 | { |
233 | 0 | CSLDestroy(papszNewList); |
234 | 0 | return nullptr; |
235 | 0 | } |
236 | 11.1M | } |
237 | 361k | *papszDst = nullptr; |
238 | | |
239 | 361k | return papszNewList; |
240 | 361k | } |
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 | | char **CSLMerge(char **papszOrig, CSLConstList papszOverride) |
260 | | |
261 | 58.8k | { |
262 | 58.8k | if (papszOrig == nullptr && papszOverride != nullptr) |
263 | 2.74k | return CSLDuplicate(papszOverride); |
264 | | |
265 | 56.0k | if (papszOverride == nullptr) |
266 | 46.2k | return papszOrig; |
267 | | |
268 | 73.9k | for (int i = 0; papszOverride[i] != nullptr; ++i) |
269 | 64.1k | { |
270 | 64.1k | char *pszKey = nullptr; |
271 | 64.1k | const char *pszValue = CPLParseNameValue(papszOverride[i], &pszKey); |
272 | | |
273 | 64.1k | papszOrig = CSLSetNameValue(papszOrig, pszKey, pszValue); |
274 | 64.1k | CPLFree(pszKey); |
275 | 64.1k | } |
276 | | |
277 | 9.82k | return papszOrig; |
278 | 56.0k | } |
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 | | char **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, |
307 | | CSLConstList papszOptions) |
308 | 53.1k | { |
309 | 53.1k | VSILFILE *fp = VSIFOpenL(pszFname, "rb"); |
310 | | |
311 | 53.1k | if (!fp) |
312 | 489 | { |
313 | 489 | if (CPLFetchBool(papszOptions, "EMIT_ERROR_IF_CANNOT_OPEN_FILE", true)) |
314 | 55 | { |
315 | | // Unable to open file. |
316 | 55 | CPLError(CE_Failure, CPLE_OpenFailed, |
317 | 55 | "CSLLoad2(\"%s\") failed: unable to open file.", pszFname); |
318 | 55 | } |
319 | 489 | return nullptr; |
320 | 489 | } |
321 | | |
322 | 52.6k | char **papszStrList = nullptr; |
323 | 52.6k | int nLines = 0; |
324 | 52.6k | int nAllocatedLines = 0; |
325 | | |
326 | 22.4M | while (!VSIFEofL(fp) && (nMaxLines == -1 || nLines < nMaxLines)) |
327 | 22.4M | { |
328 | 22.4M | const char *pszLine = CPLReadLine2L(fp, nMaxCols, papszOptions); |
329 | 22.4M | if (pszLine == nullptr) |
330 | 33.4k | break; |
331 | | |
332 | 22.4M | if (nLines + 1 >= nAllocatedLines) |
333 | 152k | { |
334 | 152k | nAllocatedLines = 16 + nAllocatedLines * 2; |
335 | 152k | char **papszStrListNew = static_cast<char **>( |
336 | 152k | VSIRealloc(papszStrList, nAllocatedLines * sizeof(char *))); |
337 | 152k | if (papszStrListNew == nullptr) |
338 | 0 | { |
339 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
340 | 0 | CPLReadLineL(nullptr); |
341 | 0 | CPLError(CE_Failure, CPLE_OutOfMemory, |
342 | 0 | "CSLLoad2(\"%s\") " |
343 | 0 | "failed: not enough memory to allocate lines.", |
344 | 0 | pszFname); |
345 | 0 | return papszStrList; |
346 | 0 | } |
347 | 152k | papszStrList = papszStrListNew; |
348 | 152k | } |
349 | 22.4M | papszStrList[nLines] = CPLStrdup(pszLine); |
350 | 22.4M | papszStrList[nLines + 1] = nullptr; |
351 | 22.4M | ++nLines; |
352 | 22.4M | } |
353 | | |
354 | 52.6k | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
355 | | |
356 | | // Free the internal thread local line buffer. |
357 | 52.6k | CPLReadLineL(nullptr); |
358 | | |
359 | 52.6k | return papszStrList; |
360 | 52.6k | } |
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 | | char **CSLLoad(const char *pszFname) |
383 | 35.4k | { |
384 | 35.4k | return CSLLoad2(pszFname, -1, -1, nullptr); |
385 | 35.4k | } |
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 | | int CSLSave(CSLConstList papszStrList, const char *pszFname) |
398 | 211 | { |
399 | 211 | if (papszStrList == nullptr) |
400 | 0 | return 0; |
401 | | |
402 | 211 | VSILFILE *fp = VSIFOpenL(pszFname, "wt"); |
403 | 211 | if (fp == nullptr) |
404 | 0 | { |
405 | | // Unable to open file. |
406 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
407 | 0 | "CSLSave(\"%s\") failed: unable to open output file.", |
408 | 0 | pszFname); |
409 | 0 | return 0; |
410 | 0 | } |
411 | | |
412 | 211 | int nLines = 0; |
413 | 36.6k | while (*papszStrList != nullptr) |
414 | 36.4k | { |
415 | 36.4k | if (VSIFPrintfL(fp, "%s\n", *papszStrList) < 1) |
416 | 0 | { |
417 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
418 | 0 | "CSLSave(\"%s\") failed: unable to write to output file.", |
419 | 0 | pszFname); |
420 | 0 | break; // A Problem happened... abort. |
421 | 0 | } |
422 | | |
423 | 36.4k | ++nLines; |
424 | 36.4k | ++papszStrList; |
425 | 36.4k | } |
426 | | |
427 | 211 | if (VSIFCloseL(fp) != 0) |
428 | 0 | { |
429 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
430 | 0 | "CSLSave(\"%s\") failed: unable to write to output file.", |
431 | 0 | pszFname); |
432 | 0 | } |
433 | | |
434 | 211 | return nLines; |
435 | 211 | } |
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 | | int CSLPrint(CSLConstList papszStrList, FILE *fpOut) |
447 | 0 | { |
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 | 0 | { |
458 | 0 | if (VSIFPrintf(fpOut, "%s\n", *papszStrList) < 0) |
459 | 0 | return nLines; |
460 | 0 | ++nLines; |
461 | 0 | ++papszStrList; |
462 | 0 | } |
463 | | |
464 | 0 | return nLines; |
465 | 0 | } |
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 | | char **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo, |
483 | | CSLConstList papszNewLines) |
484 | 61.9k | { |
485 | 61.9k | if (papszNewLines == nullptr) |
486 | 19 | return papszStrList; // Nothing to do! |
487 | | |
488 | 61.9k | const int nToInsert = CSLCount(papszNewLines); |
489 | 61.9k | if (nToInsert == 0) |
490 | 0 | return papszStrList; // Nothing to do! |
491 | | |
492 | 61.9k | const int nSrcLines = CSLCount(papszStrList); |
493 | 61.9k | const int nDstLines = nSrcLines + nToInsert; |
494 | | |
495 | | // Allocate room for the new strings. |
496 | 61.9k | papszStrList = static_cast<char **>( |
497 | 61.9k | 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 | 61.9k | 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 | 61.9k | if (nInsertAtLineNo == -1 || nInsertAtLineNo > nSrcLines) |
507 | 59.6k | nInsertAtLineNo = nSrcLines; |
508 | | |
509 | 61.9k | { |
510 | 61.9k | char **ppszSrc = papszStrList + nSrcLines; |
511 | 61.9k | char **ppszDst = papszStrList + nDstLines; |
512 | | |
513 | 228k | for (int i = nSrcLines; i >= nInsertAtLineNo; --i) |
514 | 166k | { |
515 | 166k | *ppszDst = *ppszSrc; |
516 | 166k | --ppszDst; |
517 | 166k | --ppszSrc; |
518 | 166k | } |
519 | 61.9k | } |
520 | | |
521 | | // Copy the strings to the list. |
522 | 61.9k | CSLConstList ppszSrc = papszNewLines; |
523 | 61.9k | char **ppszDst = papszStrList + nInsertAtLineNo; |
524 | | |
525 | 274k | for (; *ppszSrc != nullptr; ++ppszSrc, ++ppszDst) |
526 | 212k | { |
527 | 212k | *ppszDst = CPLStrdup(*ppszSrc); |
528 | 212k | } |
529 | | |
530 | 61.9k | return papszStrList; |
531 | 61.9k | } |
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 | | char **CSLInsertString(char **papszStrList, int nInsertAtLineNo, |
548 | | const char *pszNewLine) |
549 | 1.67k | { |
550 | 1.67k | char *apszList[2] = {const_cast<char *>(pszNewLine), nullptr}; |
551 | | |
552 | 1.67k | return CSLInsertStrings(papszStrList, nInsertAtLineNo, apszList); |
553 | 1.67k | } |
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 | | char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete, |
574 | | int nNumToRemove, char ***ppapszRetStrings) |
575 | 1.12k | { |
576 | 1.12k | const int nSrcLines = CSLCount(papszStrList); |
577 | | |
578 | 1.12k | 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 | 1.12k | const int nDstLines = nSrcLines - nNumToRemove; |
584 | 1.12k | if (nDstLines < 1) |
585 | 277 | { |
586 | 277 | CSLDestroy(papszStrList); |
587 | 277 | return nullptr; |
588 | 277 | } |
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 | 844 | char **ppszDst = papszStrList + nFirstLineToDelete; |
594 | | |
595 | 844 | if (ppapszRetStrings == nullptr) |
596 | 844 | { |
597 | | // free() all the strings that will be removed. |
598 | 1.68k | for (int i = 0; i < nNumToRemove; ++i) |
599 | 844 | { |
600 | 844 | CPLFree(*ppszDst); |
601 | 844 | *ppszDst = nullptr; |
602 | 844 | } |
603 | 844 | } |
604 | 0 | else |
605 | 0 | { |
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 | 0 | { |
612 | 0 | (*ppapszRetStrings)[i] = *ppszDst; |
613 | 0 | *ppszDst = nullptr; |
614 | 0 | ++ppszDst; |
615 | 0 | } |
616 | 0 | } |
617 | | |
618 | | // Shift down all the lines that follow the lines to remove. |
619 | 844 | if (nFirstLineToDelete == -1 || nFirstLineToDelete > nSrcLines) |
620 | 0 | nFirstLineToDelete = nDstLines; |
621 | | |
622 | 844 | char **ppszSrc = papszStrList + nFirstLineToDelete + nNumToRemove; |
623 | 844 | ppszDst = papszStrList + nFirstLineToDelete; |
624 | | |
625 | 1.48k | for (; *ppszSrc != nullptr; ++ppszSrc, ++ppszDst) |
626 | 641 | { |
627 | 641 | *ppszDst = *ppszSrc; |
628 | 641 | } |
629 | | // Move the NULL pointer at the end of the StringList. |
630 | 844 | *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 | 844 | return papszStrList; |
636 | 1.12k | } |
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 | | int CSLFindString(CSLConstList papszList, const char *pszTarget) |
656 | | |
657 | 10.9M | { |
658 | 10.9M | if (papszList == nullptr) |
659 | 2.93M | return -1; |
660 | | |
661 | 36.2M | for (int i = 0; papszList[i] != nullptr; ++i) |
662 | 29.6M | { |
663 | 29.6M | if (EQUAL(papszList[i], pszTarget)) |
664 | 1.38M | return i; |
665 | 29.6M | } |
666 | | |
667 | 6.61M | return -1; |
668 | 7.99M | } |
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 | | int CSLFindStringCaseSensitive(CSLConstList papszList, const char *pszTarget) |
689 | | |
690 | 11.2k | { |
691 | 11.2k | if (papszList == nullptr) |
692 | 5.10k | return -1; |
693 | | |
694 | 32.1k | for (int i = 0; papszList[i] != nullptr; ++i) |
695 | 26.0k | { |
696 | 26.0k | if (strcmp(papszList[i], pszTarget) == 0) |
697 | 20 | return i; |
698 | 26.0k | } |
699 | | |
700 | 6.08k | return -1; |
701 | 6.10k | } |
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 | | int CSLPartialFindString(CSLConstList papszHaystack, const char *pszNeedle) |
721 | 133k | { |
722 | 133k | if (papszHaystack == nullptr || pszNeedle == nullptr) |
723 | 131k | return -1; |
724 | | |
725 | 89.2k | for (int i = 0; papszHaystack[i] != nullptr; ++i) |
726 | 88.3k | { |
727 | 88.3k | if (strstr(papszHaystack[i], pszNeedle)) |
728 | 1.12k | return i; |
729 | 88.3k | } |
730 | | |
731 | 903 | return -1; |
732 | 2.02k | } |
733 | | |
734 | | /********************************************************************** |
735 | | * CSLTokenizeString() |
736 | | **********************************************************************/ |
737 | | |
738 | | /** Tokenizes a string and returns a StringList with one string for |
739 | | * each token. |
740 | | */ |
741 | | char **CSLTokenizeString(const char *pszString) |
742 | 21.0M | { |
743 | 21.0M | return CSLTokenizeString2(pszString, " ", CSLT_HONOURSTRINGS); |
744 | 21.0M | } |
745 | | |
746 | | /************************************************************************/ |
747 | | /* CSLTokenizeStringComplex() */ |
748 | | /************************************************************************/ |
749 | | |
750 | | /** Obsolete tokenizing api. Use CSLTokenizeString2() */ |
751 | | char **CSLTokenizeStringComplex(const char *pszString, |
752 | | const char *pszDelimiters, int bHonourStrings, |
753 | | int bAllowEmptyTokens) |
754 | 31.0M | { |
755 | 31.0M | int nFlags = 0; |
756 | | |
757 | 31.0M | if (bHonourStrings) |
758 | 24.7M | nFlags |= CSLT_HONOURSTRINGS; |
759 | 31.0M | if (bAllowEmptyTokens) |
760 | 838k | nFlags |= CSLT_ALLOWEMPTYTOKENS; |
761 | | |
762 | 31.0M | return CSLTokenizeString2(pszString, pszDelimiters, nFlags); |
763 | 31.0M | } |
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 | | char **CSLTokenizeString2(const char *pszString, const char *pszDelimiters, |
821 | | int nCSLTFlags) |
822 | 77.4M | { |
823 | 77.4M | if (pszString == nullptr) |
824 | 423k | return static_cast<char **>(CPLCalloc(sizeof(char *), 1)); |
825 | | |
826 | 77.0M | return cpl::tokenize_string(pszString, pszDelimiters, nCSLTFlags) |
827 | 77.0M | .StealList(); |
828 | 77.4M | } |
829 | | |
830 | | namespace cpl |
831 | | { |
832 | | CPLStringList tokenize_string(std::string_view str, std::string_view delimiters, |
833 | | int nCSLTFlags) |
834 | 77.0M | { |
835 | 77.0M | CPLStringList oRetList; |
836 | 77.0M | const bool bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS) != 0; |
837 | 77.0M | const bool bHonourStringsSingleQuotes = |
838 | 77.0M | (nCSLTFlags & CSLT_HONOURSINGLEQUOTES) != 0; |
839 | 77.0M | const bool bAllowEmptyTokens = (nCSLTFlags & CSLT_ALLOWEMPTYTOKENS) != 0; |
840 | 77.0M | const bool bStripLeadSpaces = (nCSLTFlags & CSLT_STRIPLEADSPACES) != 0; |
841 | 77.0M | const bool bStripEndSpaces = (nCSLTFlags & CSLT_STRIPENDSPACES) != 0; |
842 | | |
843 | 77.0M | size_t pos = 0; |
844 | 77.0M | std::string token; |
845 | 281M | while (pos < str.size()) |
846 | 204M | { |
847 | 204M | token.clear(); |
848 | 204M | bool bInString = false; |
849 | 204M | bool bInStringSingleQuote = false; |
850 | | |
851 | | // Try to find the next delimiter, marking end of token. |
852 | 3.60G | while (pos < str.size()) |
853 | 3.54G | { |
854 | | // End if this is a delimiter skip it and break. |
855 | 3.54G | if (!bInString && !bInStringSingleQuote && |
856 | 3.39G | delimiters.find(str[pos]) != std::string_view::npos) |
857 | 146M | { |
858 | 146M | pos++; |
859 | 146M | break; |
860 | 146M | } |
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 | 3.40G | if (bHonourStrings && !bInStringSingleQuote && str[pos] == '"') |
866 | 12.8M | { |
867 | 12.8M | if (nCSLTFlags & CSLT_PRESERVEQUOTES) |
868 | 2.68M | { |
869 | 2.68M | token.push_back(str[pos]); |
870 | 2.68M | } |
871 | | |
872 | 12.8M | bInString = !bInString; |
873 | 12.8M | pos++; |
874 | 12.8M | continue; |
875 | 12.8M | } |
876 | 3.39G | else if (bHonourStringsSingleQuotes && !bHonourStrings && |
877 | 0 | str[pos] == '\'') |
878 | 0 | { |
879 | 0 | if (nCSLTFlags & CSLT_PRESERVEQUOTES) |
880 | 0 | { |
881 | 0 | token.push_back(str[pos]); |
882 | 0 | } |
883 | |
|
884 | 0 | bInStringSingleQuote = !bInStringSingleQuote; |
885 | 0 | pos++; |
886 | 0 | continue; |
887 | 0 | } |
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 | 3.39G | if (bInString && str[pos] == '\\') |
895 | 568k | { |
896 | 568k | if (pos + 1 < str.size() && |
897 | 559k | (str[pos + 1] == '"' || str[pos + 1] == '\\')) |
898 | 315k | { |
899 | 315k | if (nCSLTFlags & CSLT_PRESERVEESCAPES) |
900 | 72 | { |
901 | 72 | token.push_back(str[pos]); |
902 | 72 | } |
903 | | |
904 | 315k | ++pos; |
905 | 315k | } |
906 | 568k | } |
907 | 3.38G | else if (bInStringSingleQuote && str[pos] == '\\') |
908 | 0 | { |
909 | 0 | if (pos + 1 < str.size() && |
910 | 0 | (str[pos + 1] == '\'' || str[pos + 1] == '\\')) |
911 | 0 | { |
912 | 0 | if (nCSLTFlags & CSLT_PRESERVEESCAPES) |
913 | 0 | { |
914 | 0 | token.push_back(str[pos]); |
915 | 0 | } |
916 | |
|
917 | 0 | ++pos; |
918 | 0 | } |
919 | 0 | } |
920 | | |
921 | 3.39G | token.push_back(str[pos]); |
922 | 3.39G | pos++; |
923 | 3.39G | } |
924 | | |
925 | | // Add the token. |
926 | 204M | std::string_view token_view(token); |
927 | 204M | if (bStripLeadSpaces) |
928 | 5.82M | { |
929 | 5.82M | token_view = ltrim(token_view); |
930 | 5.82M | } |
931 | 204M | if (bStripEndSpaces) |
932 | 5.82M | { |
933 | 5.82M | token_view = rtrim(token_view); |
934 | 5.82M | } |
935 | | |
936 | 204M | if (!token_view.empty() || bAllowEmptyTokens) |
937 | 149M | oRetList.AddString(token_view); |
938 | 204M | } |
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 | 77.0M | if (!str.empty() && pos == str.size() && bAllowEmptyTokens && |
945 | 1.59M | oRetList.Count() > 0 && |
946 | 1.59M | delimiters.find(str[pos - 1]) != std::string_view::npos) |
947 | 390k | { |
948 | 390k | oRetList.AddString(""); |
949 | 390k | } |
950 | | |
951 | 77.0M | if (oRetList.List() == nullptr) |
952 | 16.7M | { |
953 | | // Prefer to return empty lists as a pointer to |
954 | | // a null pointer since some client code might depend on this. |
955 | 16.7M | oRetList.Assign(static_cast<char **>(CPLCalloc(sizeof(char *), 1))); |
956 | 16.7M | } |
957 | | |
958 | 77.0M | return CPLStringList(oRetList.StealList()); |
959 | 77.0M | } |
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 | | const char *CPLSPrintf(CPL_FORMAT_STRING(const char *fmt), ...) |
980 | 407M | { |
981 | 407M | va_list args; |
982 | | |
983 | | /* -------------------------------------------------------------------- */ |
984 | | /* Get the thread local buffer ring data. */ |
985 | | /* -------------------------------------------------------------------- */ |
986 | 407M | char *pachBufRingInfo = static_cast<char *>(CPLGetTLS(CTLS_CPLSPRINTF)); |
987 | | |
988 | 407M | if (pachBufRingInfo == nullptr) |
989 | 195 | { |
990 | 195 | pachBufRingInfo = static_cast<char *>(CPLCalloc( |
991 | 195 | 1, sizeof(int) + CPLSPrintf_BUF_Count * CPLSPrintf_BUF_SIZE)); |
992 | 195 | CPLSetTLS(CTLS_CPLSPRINTF, pachBufRingInfo, TRUE); |
993 | 195 | } |
994 | | |
995 | | /* -------------------------------------------------------------------- */ |
996 | | /* Work out which string in the "ring" we want to use this */ |
997 | | /* time. */ |
998 | | /* -------------------------------------------------------------------- */ |
999 | 407M | int *pnBufIndex = reinterpret_cast<int *>(pachBufRingInfo); |
1000 | 407M | const size_t nOffset = sizeof(int) + *pnBufIndex * CPLSPrintf_BUF_SIZE; |
1001 | 407M | char *pachBuffer = pachBufRingInfo + nOffset; |
1002 | | |
1003 | 407M | *pnBufIndex = (*pnBufIndex + 1) % CPLSPrintf_BUF_Count; |
1004 | | |
1005 | | /* -------------------------------------------------------------------- */ |
1006 | | /* Format the result. */ |
1007 | | /* -------------------------------------------------------------------- */ |
1008 | | |
1009 | 407M | va_start(args, fmt); |
1010 | | |
1011 | 407M | const int ret = |
1012 | 407M | CPLvsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE - 1, fmt, args); |
1013 | 407M | if (ret < 0 || ret >= CPLSPrintf_BUF_SIZE - 1) |
1014 | 1.72k | { |
1015 | 1.72k | CPLError(CE_Failure, CPLE_AppDefined, |
1016 | 1.72k | "CPLSPrintf() called with too " |
1017 | 1.72k | "big string. Output will be truncated !"); |
1018 | 1.72k | } |
1019 | | |
1020 | 407M | va_end(args); |
1021 | | |
1022 | 407M | return pachBuffer; |
1023 | 407M | } |
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 | | char **CSLAppendPrintf(char **papszStrList, CPL_FORMAT_STRING(const char *fmt), |
1033 | | ...) |
1034 | 12.2k | { |
1035 | 12.2k | va_list args; |
1036 | | |
1037 | 12.2k | va_start(args, fmt); |
1038 | 12.2k | CPLString osWork; |
1039 | 12.2k | osWork.vPrintf(fmt, args); |
1040 | 12.2k | va_end(args); |
1041 | | |
1042 | 12.2k | return CSLAddString(papszStrList, osWork); |
1043 | 12.2k | } |
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 | | int CPLVASPrintf(char **buf, CPL_FORMAT_STRING(const char *fmt), va_list ap) |
1052 | | |
1053 | 0 | { |
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 | 0 | } |
1063 | | |
1064 | | /************************************************************************/ |
1065 | | /* CPLvsnprintf_get_end_of_formatting() */ |
1066 | | /************************************************************************/ |
1067 | | |
1068 | | static const char *CPLvsnprintf_get_end_of_formatting(const char *fmt) |
1069 | 1.06G | { |
1070 | 1.06G | char ch = '\0'; |
1071 | | // Flag. |
1072 | 1.42G | for (; (ch = *fmt) != '\0'; ++fmt) |
1073 | 1.42G | { |
1074 | 1.42G | if (ch == '\'') |
1075 | 0 | continue; // Bad idea as this is locale specific. |
1076 | 1.42G | if (ch == '-' || ch == '+' || ch == ' ' || ch == '#' || ch == '0') |
1077 | 363M | continue; |
1078 | 1.06G | break; |
1079 | 1.42G | } |
1080 | | |
1081 | | // Field width. |
1082 | 1.42G | for (; (ch = *fmt) != '\0'; ++fmt) |
1083 | 1.42G | { |
1084 | 1.42G | if (ch == '$') |
1085 | 0 | return nullptr; // Do not support this. |
1086 | 1.42G | if (*fmt >= '0' && *fmt <= '9') |
1087 | 363M | continue; |
1088 | 1.06G | break; |
1089 | 1.42G | } |
1090 | | |
1091 | | // Precision. |
1092 | 1.06G | if (ch == '.') |
1093 | 11.8M | { |
1094 | 11.8M | ++fmt; |
1095 | 30.3M | for (; (ch = *fmt) != '\0'; ++fmt) |
1096 | 30.3M | { |
1097 | 30.3M | if (ch == '$') |
1098 | 0 | return nullptr; // Do not support this. |
1099 | 30.3M | if (*fmt >= '0' && *fmt <= '9') |
1100 | 18.4M | continue; |
1101 | 11.8M | break; |
1102 | 30.3M | } |
1103 | 11.8M | } |
1104 | | |
1105 | | // Length modifier. |
1106 | 1.12G | for (; (ch = *fmt) != '\0'; ++fmt) |
1107 | 1.12G | { |
1108 | 1.12G | if (ch == 'h' || ch == 'l' || ch == 'j' || ch == 'z' || ch == 't' || |
1109 | 1.06G | ch == 'L') |
1110 | 67.4M | continue; |
1111 | 1.06G | else if (ch == 'I' && fmt[1] == '6' && fmt[2] == '4') |
1112 | 0 | fmt += 2; |
1113 | 1.06G | else |
1114 | 1.06G | return fmt; |
1115 | 1.12G | } |
1116 | | |
1117 | 0 | return nullptr; |
1118 | 1.06G | } |
1119 | | |
1120 | | /************************************************************************/ |
1121 | | /* CPLvsnprintf() */ |
1122 | | /************************************************************************/ |
1123 | | |
1124 | | #define call_native_snprintf(type) \ |
1125 | 575M | local_ret = snprintf(str + offset_out, size - offset_out, localfmt, \ |
1126 | 575M | 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 | | int CPLvsnprintf(char *str, size_t size, CPL_FORMAT_STRING(const char *fmt), |
1143 | | va_list args) |
1144 | 642M | { |
1145 | 642M | if (size == 0) |
1146 | 0 | return vsnprintf(str, size, fmt, args); |
1147 | | |
1148 | 642M | va_list wrk_args; |
1149 | | |
1150 | 642M | #ifdef va_copy |
1151 | 642M | va_copy(wrk_args, args); |
1152 | | #else |
1153 | | wrk_args = args; |
1154 | | #endif |
1155 | | |
1156 | 642M | const char *fmt_ori = fmt; |
1157 | 642M | size_t offset_out = 0; |
1158 | 642M | char ch = '\0'; |
1159 | 642M | bool bFormatUnknown = false; |
1160 | | |
1161 | 7.39G | for (; (ch = *fmt) != '\0'; ++fmt) |
1162 | 6.75G | { |
1163 | 6.75G | if (ch == '%') |
1164 | 1.06G | { |
1165 | 1.06G | if (strncmp(fmt, "%.*f", 4) == 0) |
1166 | 28.1k | { |
1167 | 28.1k | const int precision = va_arg(wrk_args, int); |
1168 | 28.1k | const double val = va_arg(wrk_args, double); |
1169 | 28.1k | const int local_ret = |
1170 | 28.1k | snprintf(str + offset_out, size - offset_out, "%.*f", |
1171 | 28.1k | precision, val); |
1172 | | // MSVC vsnprintf() returns -1. |
1173 | 28.1k | if (local_ret < 0 || offset_out + local_ret >= size) |
1174 | 0 | break; |
1175 | 820k | for (int j = 0; j < local_ret; ++j) |
1176 | 792k | { |
1177 | 792k | if (str[offset_out + j] == ',') |
1178 | 0 | { |
1179 | 0 | str[offset_out + j] = '.'; |
1180 | 0 | break; |
1181 | 0 | } |
1182 | 792k | } |
1183 | 28.1k | offset_out += local_ret; |
1184 | 28.1k | fmt += strlen("%.*f") - 1; |
1185 | 28.1k | continue; |
1186 | 28.1k | } |
1187 | | |
1188 | 1.06G | const char *ptrend = CPLvsnprintf_get_end_of_formatting(fmt + 1); |
1189 | 1.06G | if (ptrend == nullptr || ptrend - fmt >= 20) |
1190 | 0 | { |
1191 | 0 | bFormatUnknown = true; |
1192 | 0 | break; |
1193 | 0 | } |
1194 | 1.06G | char end = *ptrend; |
1195 | 1.06G | char end_m1 = ptrend[-1]; |
1196 | | |
1197 | 1.06G | char localfmt[22] = {}; |
1198 | 1.06G | memcpy(localfmt, fmt, ptrend - fmt + 1); |
1199 | 1.06G | localfmt[ptrend - fmt + 1] = '\0'; |
1200 | | |
1201 | 1.06G | int local_ret = 0; |
1202 | 1.06G | if (end == '%') |
1203 | 344M | { |
1204 | 344M | if (offset_out == size - 1) |
1205 | 0 | break; |
1206 | 344M | local_ret = 1; |
1207 | 344M | str[offset_out] = '%'; |
1208 | 344M | } |
1209 | 717M | else if (end == 'd' || end == 'i' || end == 'c') |
1210 | 118M | { |
1211 | 118M | if (end_m1 == 'h') |
1212 | 0 | call_native_snprintf(int); |
1213 | 118M | else if (end_m1 == 'l' && ptrend[-2] != 'l') |
1214 | 362k | call_native_snprintf(long); |
1215 | 118M | else if (end_m1 == 'l' && ptrend[-2] == 'l') |
1216 | 3.77M | call_native_snprintf(GIntBig); |
1217 | 114M | else if (end_m1 == '4' && ptrend[-2] == '6' && |
1218 | 0 | ptrend[-3] == 'I') |
1219 | | // Microsoft I64 modifier. |
1220 | 0 | call_native_snprintf(GIntBig); |
1221 | 114M | else if (end_m1 == 'z') |
1222 | 0 | call_native_snprintf(size_t); |
1223 | 114M | else if ((end_m1 >= 'a' && end_m1 <= 'z') || |
1224 | 114M | (end_m1 >= 'A' && end_m1 <= 'Z')) |
1225 | 0 | { |
1226 | 0 | bFormatUnknown = true; |
1227 | 0 | break; |
1228 | 0 | } |
1229 | 114M | else |
1230 | 114M | call_native_snprintf(int); |
1231 | 118M | } |
1232 | 599M | else if (end == 'o' || end == 'u' || end == 'x' || end == 'X') |
1233 | 439M | { |
1234 | 439M | if (end_m1 == 'h') |
1235 | 31 | call_native_snprintf(unsigned int); |
1236 | 439M | else if (end_m1 == 'l' && ptrend[-2] != 'l') |
1237 | 12.0M | call_native_snprintf(unsigned long); |
1238 | 427M | else if (end_m1 == 'l' && ptrend[-2] == 'l') |
1239 | 23.7M | call_native_snprintf(GUIntBig); |
1240 | 403M | else if (end_m1 == '4' && ptrend[-2] == '6' && |
1241 | 0 | ptrend[-3] == 'I') |
1242 | | // Microsoft I64 modifier. |
1243 | 0 | call_native_snprintf(GUIntBig); |
1244 | 403M | else if (end_m1 == 'z') |
1245 | 0 | call_native_snprintf(size_t); |
1246 | 403M | else if ((end_m1 >= 'a' && end_m1 <= 'z') || |
1247 | 403M | (end_m1 >= 'A' && end_m1 <= 'Z')) |
1248 | 0 | { |
1249 | 0 | bFormatUnknown = true; |
1250 | 0 | break; |
1251 | 0 | } |
1252 | 403M | else |
1253 | 403M | call_native_snprintf(unsigned int); |
1254 | 439M | } |
1255 | 159M | else if (end == 'e' || end == 'E' || end == 'f' || end == 'F' || |
1256 | 144M | end == 'g' || end == 'G' || end == 'a' || end == 'A') |
1257 | 17.5M | { |
1258 | 17.5M | if (end_m1 == 'L') |
1259 | 0 | call_native_snprintf(long double); |
1260 | 17.5M | else |
1261 | 17.5M | call_native_snprintf(double); |
1262 | | // MSVC vsnprintf() returns -1. |
1263 | 17.5M | if (local_ret < 0 || offset_out + local_ret >= size) |
1264 | 352 | break; |
1265 | 202M | for (int j = 0; j < local_ret; ++j) |
1266 | 184M | { |
1267 | 184M | if (str[offset_out + j] == ',') |
1268 | 0 | { |
1269 | 0 | str[offset_out + j] = '.'; |
1270 | 0 | break; |
1271 | 0 | } |
1272 | 184M | } |
1273 | 17.4M | } |
1274 | 142M | else if (end == 's') |
1275 | 141M | { |
1276 | 141M | const char *pszPtr = va_arg(wrk_args, const char *); |
1277 | 141M | CPLAssert(pszPtr); |
1278 | 141M | local_ret = snprintf(str + offset_out, size - offset_out, |
1279 | 141M | localfmt, pszPtr); |
1280 | 141M | } |
1281 | 941k | else if (end == 'p') |
1282 | 359k | { |
1283 | 359k | call_native_snprintf(void *); |
1284 | 359k | } |
1285 | 582k | else |
1286 | 582k | { |
1287 | 582k | bFormatUnknown = true; |
1288 | 582k | break; |
1289 | 582k | } |
1290 | | // MSVC vsnprintf() returns -1. |
1291 | 1.06G | if (local_ret < 0 || offset_out + local_ret >= size) |
1292 | 2.35M | break; |
1293 | 1.05G | offset_out += local_ret; |
1294 | 1.05G | fmt = ptrend; |
1295 | 1.05G | } |
1296 | 5.69G | else |
1297 | 5.69G | { |
1298 | 5.69G | if (offset_out == size - 1) |
1299 | 35.5k | break; |
1300 | 5.69G | str[offset_out++] = *fmt; |
1301 | 5.69G | } |
1302 | 6.75G | } |
1303 | 642M | if (ch == '\0' && offset_out < size) |
1304 | 639M | str[offset_out] = '\0'; |
1305 | 2.96M | else |
1306 | 2.96M | { |
1307 | 2.96M | if (bFormatUnknown) |
1308 | 582k | { |
1309 | 582k | CPLDebug("CPL", |
1310 | 582k | "CPLvsnprintf() called with unsupported " |
1311 | 582k | "formatting string: %s", |
1312 | 582k | fmt_ori); |
1313 | 582k | } |
1314 | 2.96M | #ifdef va_copy |
1315 | 2.96M | va_end(wrk_args); |
1316 | 2.96M | va_copy(wrk_args, args); |
1317 | | #else |
1318 | | wrk_args = args; |
1319 | | #endif |
1320 | 2.96M | #if defined(HAVE_VSNPRINTF) |
1321 | 2.96M | offset_out = vsnprintf(str, size, fmt_ori, wrk_args); |
1322 | | #else |
1323 | | offset_out = vsprintf(str, fmt_ori, wrk_args); |
1324 | | #endif |
1325 | 2.96M | } |
1326 | | |
1327 | 642M | #ifdef va_copy |
1328 | 642M | va_end(wrk_args); |
1329 | 642M | #endif |
1330 | | |
1331 | 642M | return static_cast<int>(offset_out); |
1332 | 642M | } |
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 | | int CPLsnprintf(char *str, size_t size, CPL_FORMAT_STRING(const char *fmt), ...) |
1362 | 8.39M | { |
1363 | 8.39M | va_list args; |
1364 | | |
1365 | 8.39M | va_start(args, fmt); |
1366 | 8.39M | const int ret = CPLvsnprintf(str, size, fmt, args); |
1367 | 8.39M | va_end(args); |
1368 | 8.39M | return ret; |
1369 | 8.39M | } |
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 | | int CPLsprintf(char *str, CPL_FORMAT_STRING(const char *fmt), ...) |
1390 | 29.4k | { |
1391 | 29.4k | va_list args; |
1392 | | |
1393 | 29.4k | va_start(args, fmt); |
1394 | 29.4k | const int ret = CPLvsnprintf(str, INT_MAX, fmt, args); |
1395 | 29.4k | va_end(args); |
1396 | 29.4k | return ret; |
1397 | 29.4k | } |
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 | | int CPLprintf(CPL_FORMAT_STRING(const char *fmt), ...) |
1415 | 0 | { |
1416 | 0 | va_list wrk_args, args; |
1417 | |
|
1418 | 0 | va_start(args, fmt); |
1419 | |
|
1420 | 0 | #ifdef va_copy |
1421 | 0 | va_copy(wrk_args, args); |
1422 | | #else |
1423 | | wrk_args = args; |
1424 | | #endif |
1425 | |
|
1426 | 0 | char szBuffer[4096] = {}; |
1427 | | // Quiet coverity by staring off nul terminated. |
1428 | 0 | int ret = CPLvsnprintf(szBuffer, sizeof(szBuffer), fmt, wrk_args); |
1429 | |
|
1430 | 0 | #ifdef va_copy |
1431 | 0 | va_end(wrk_args); |
1432 | 0 | #endif |
1433 | |
|
1434 | 0 | if (ret < int(sizeof(szBuffer)) - 1) |
1435 | 0 | ret = printf("%s", szBuffer); /*ok*/ |
1436 | 0 | else |
1437 | 0 | { |
1438 | 0 | #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 | 0 | #ifdef va_copy |
1447 | 0 | va_end(wrk_args); |
1448 | 0 | #endif |
1449 | 0 | } |
1450 | |
|
1451 | 0 | va_end(args); |
1452 | |
|
1453 | 0 | return ret; |
1454 | 0 | } |
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 | | int CPLsscanf(const char *str, CPL_SCANF_FORMAT_STRING(const char *fmt), ...) |
1478 | | #endif |
1479 | 676k | { |
1480 | 676k | bool error = false; |
1481 | 676k | int ret = 0; |
1482 | 676k | const char *fmt_ori = fmt; |
1483 | 676k | va_list args; |
1484 | | |
1485 | 676k | va_start(args, fmt); |
1486 | 1.83M | for (; *fmt != '\0' && *str != '\0'; ++fmt) |
1487 | 1.67M | { |
1488 | 1.67M | if (*fmt == '%') |
1489 | 1.16M | { |
1490 | 1.16M | if (fmt[1] == 'l' && fmt[2] == 'f') |
1491 | 1.16M | { |
1492 | 1.16M | fmt += 2; |
1493 | 1.16M | char *end; |
1494 | 1.16M | *(va_arg(args, double *)) = CPLStrtod(str, &end); |
1495 | 1.16M | if (end > str) |
1496 | 655k | { |
1497 | 655k | ++ret; |
1498 | 655k | str = end; |
1499 | 655k | } |
1500 | 512k | else |
1501 | 512k | break; |
1502 | 1.16M | } |
1503 | 0 | else |
1504 | 0 | { |
1505 | 0 | error = true; |
1506 | 0 | break; |
1507 | 0 | } |
1508 | 1.16M | } |
1509 | 505k | else if (isspace(static_cast<unsigned char>(*fmt))) |
1510 | 501k | { |
1511 | 743k | while (*str != '\0' && isspace(static_cast<unsigned char>(*str))) |
1512 | 241k | ++str; |
1513 | 501k | } |
1514 | 3.85k | else if (*str != *fmt) |
1515 | 77 | break; |
1516 | 3.78k | else |
1517 | 3.78k | ++str; |
1518 | 1.67M | } |
1519 | 676k | va_end(args); |
1520 | | |
1521 | 676k | if (error) |
1522 | 0 | { |
1523 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1524 | 0 | "Format %s not supported by CPLsscanf()", fmt_ori); |
1525 | 0 | } |
1526 | | |
1527 | 676k | return ret; |
1528 | 676k | } |
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 | | bool CPLTestBool(const char *pszValue) |
1550 | 108M | { |
1551 | 108M | return !(EQUAL(pszValue, "NO") || EQUAL(pszValue, "FALSE") || |
1552 | 8.99M | EQUAL(pszValue, "OFF") || EQUAL(pszValue, "0")); |
1553 | 108M | } |
1554 | | |
1555 | | /************************************************************************/ |
1556 | | /* CSLTestBoolean() */ |
1557 | | /************************************************************************/ |
1558 | | |
1559 | | /** |
1560 | | * Test what boolean value contained in the string. |
1561 | | * |
1562 | | * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE. |
1563 | | * Otherwise, TRUE will be returned. |
1564 | | * |
1565 | | * Deprecated. Removed in GDAL 3.x. |
1566 | | * |
1567 | | * Use CPLTestBoolean() for C and CPLTestBool() for C++. |
1568 | | * |
1569 | | * @param pszValue the string should be tested. |
1570 | | * |
1571 | | * @return TRUE or FALSE. |
1572 | | */ |
1573 | | |
1574 | | int CSLTestBoolean(const char *pszValue) |
1575 | 46 | { |
1576 | 46 | return CPLTestBool(pszValue) ? TRUE : FALSE; |
1577 | 46 | } |
1578 | | |
1579 | | /************************************************************************/ |
1580 | | /* CPLTestBoolean() */ |
1581 | | /************************************************************************/ |
1582 | | |
1583 | | /** |
1584 | | * Test what boolean value contained in the string. |
1585 | | * |
1586 | | * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE. |
1587 | | * Otherwise, TRUE will be returned. |
1588 | | * |
1589 | | * Use this only in C code. In C++, prefer CPLTestBool(). |
1590 | | * |
1591 | | * @param pszValue the string should be tested. |
1592 | | * |
1593 | | * @return TRUE or FALSE. |
1594 | | */ |
1595 | | |
1596 | | int CPLTestBoolean(const char *pszValue) |
1597 | 849 | { |
1598 | 849 | return CPLTestBool(pszValue) ? TRUE : FALSE; |
1599 | 849 | } |
1600 | | |
1601 | | /********************************************************************** |
1602 | | * CPLFetchBool() |
1603 | | **********************************************************************/ |
1604 | | |
1605 | | /** Check for boolean key value. |
1606 | | * |
1607 | | * In a StringList of "Name=Value" pairs, look to see if there is a key |
1608 | | * with the given name, and if it can be interpreted as being TRUE. If |
1609 | | * the key appears without any "=Value" portion it will be considered true. |
1610 | | * If the value is NO, FALSE or 0 it will be considered FALSE otherwise |
1611 | | * if the key appears in the list it will be considered TRUE. If the key |
1612 | | * doesn't appear at all, the indicated default value will be returned. |
1613 | | * |
1614 | | * @param papszStrList the string list to search. |
1615 | | * @param pszKey the key value to look for (case insensitive). |
1616 | | * @param bDefault the value to return if the key isn't found at all. |
1617 | | * |
1618 | | * @return true or false |
1619 | | */ |
1620 | | |
1621 | | bool CPLFetchBool(CSLConstList papszStrList, const char *pszKey, bool bDefault) |
1622 | | |
1623 | 6.11M | { |
1624 | 6.11M | if (CSLFindString(papszStrList, pszKey) != -1) |
1625 | 62 | return true; |
1626 | | |
1627 | 6.11M | const char *const pszValue = CSLFetchNameValue(papszStrList, pszKey); |
1628 | 6.11M | if (pszValue == nullptr) |
1629 | 6.02M | return bDefault; |
1630 | | |
1631 | 83.6k | return CPLTestBool(pszValue); |
1632 | 6.11M | } |
1633 | | |
1634 | | /********************************************************************** |
1635 | | * CSLFetchBoolean() |
1636 | | **********************************************************************/ |
1637 | | |
1638 | | /** DEPRECATED. Check for boolean key value. |
1639 | | * |
1640 | | * In a StringList of "Name=Value" pairs, look to see if there is a key |
1641 | | * with the given name, and if it can be interpreted as being TRUE. If |
1642 | | * the key appears without any "=Value" portion it will be considered true. |
1643 | | * If the value is NO, FALSE or 0 it will be considered FALSE otherwise |
1644 | | * if the key appears in the list it will be considered TRUE. If the key |
1645 | | * doesn't appear at all, the indicated default value will be returned. |
1646 | | * |
1647 | | * @param papszStrList the string list to search. |
1648 | | * @param pszKey the key value to look for (case insensitive). |
1649 | | * @param bDefault the value to return if the key isn't found at all. |
1650 | | * |
1651 | | * @return TRUE or FALSE |
1652 | | */ |
1653 | | |
1654 | | int CSLFetchBoolean(CSLConstList papszStrList, const char *pszKey, int bDefault) |
1655 | | |
1656 | 10.1k | { |
1657 | 10.1k | return CPLFetchBool(papszStrList, pszKey, CPL_TO_BOOL(bDefault)); |
1658 | 10.1k | } |
1659 | | |
1660 | | /************************************************************************/ |
1661 | | /* CSLFetchNameValueDefaulted() */ |
1662 | | /************************************************************************/ |
1663 | | |
1664 | | /** Same as CSLFetchNameValue() but return pszDefault in case of no match */ |
1665 | | const char *CSLFetchNameValueDef(CSLConstList papszStrList, const char *pszName, |
1666 | | const char *pszDefault) |
1667 | | |
1668 | 10.2M | { |
1669 | 10.2M | const char *pszResult = CSLFetchNameValue(papszStrList, pszName); |
1670 | 10.2M | if (pszResult != nullptr) |
1671 | 572k | return pszResult; |
1672 | | |
1673 | 9.70M | return pszDefault; |
1674 | 10.2M | } |
1675 | | |
1676 | | /********************************************************************** |
1677 | | * CSLFetchNameValue() |
1678 | | **********************************************************************/ |
1679 | | |
1680 | | /** In a StringList of "Name=Value" pairs, look for the |
1681 | | * first value associated with the specified name. The search is not |
1682 | | * case sensitive. |
1683 | | * ("Name:Value" pairs are also supported for backward compatibility |
1684 | | * with older stuff.) |
1685 | | * |
1686 | | * Returns a reference to the value in the StringList that the caller |
1687 | | * should not attempt to free. |
1688 | | * |
1689 | | * Returns NULL if the name is not found. |
1690 | | */ |
1691 | | |
1692 | | const char *CSLFetchNameValue(CSLConstList papszStrList, const char *pszName) |
1693 | 716M | { |
1694 | 716M | if (papszStrList == nullptr || pszName == nullptr) |
1695 | 105M | return nullptr; |
1696 | | |
1697 | 610M | const size_t nLen = strlen(pszName); |
1698 | 3.99G | while (*papszStrList != nullptr) |
1699 | 3.39G | { |
1700 | 3.39G | if (EQUALN(*papszStrList, pszName, nLen) && |
1701 | 7.79M | ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':')) |
1702 | 7.75M | { |
1703 | 7.75M | return (*papszStrList) + nLen + 1; |
1704 | 7.75M | } |
1705 | 3.38G | ++papszStrList; |
1706 | 3.38G | } |
1707 | 602M | return nullptr; |
1708 | 610M | } |
1709 | | |
1710 | | /************************************************************************/ |
1711 | | /* CSLFindName() */ |
1712 | | /************************************************************************/ |
1713 | | |
1714 | | /** |
1715 | | * Find StringList entry with given key name. |
1716 | | * |
1717 | | * @param papszStrList the string list to search. |
1718 | | * @param pszName the key value to look for (case insensitive). |
1719 | | * |
1720 | | * @return -1 on failure or the list index of the first occurrence |
1721 | | * matching the given key. |
1722 | | */ |
1723 | | |
1724 | | int CSLFindName(CSLConstList papszStrList, const char *pszName) |
1725 | 315M | { |
1726 | 315M | if (papszStrList == nullptr || pszName == nullptr) |
1727 | 57.0M | return -1; |
1728 | | |
1729 | 258M | const size_t nLen = strlen(pszName); |
1730 | 258M | int iIndex = 0; |
1731 | 2.12G | while (*papszStrList != nullptr) |
1732 | 2.06G | { |
1733 | 2.06G | if (EQUALN(*papszStrList, pszName, nLen) && |
1734 | 195M | ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':')) |
1735 | 194M | { |
1736 | 194M | return iIndex; |
1737 | 194M | } |
1738 | 1.86G | ++iIndex; |
1739 | 1.86G | ++papszStrList; |
1740 | 1.86G | } |
1741 | 63.6M | return -1; |
1742 | 258M | } |
1743 | | |
1744 | | /************************************************************************/ |
1745 | | /* CPLParseMemorySize() */ |
1746 | | /************************************************************************/ |
1747 | | |
1748 | | /** Parse a memory size from a string. |
1749 | | * |
1750 | | * The string may indicate the units of the memory (e.g., "230k", "500 MB"), |
1751 | | * using the prefixes "k", "m", or "g" in either lower or upper-case, |
1752 | | * optionally followed by a "b" or "B". The string may alternatively specify |
1753 | | * memory as a fraction of the usable RAM (e.g., "25%"). Spaces before the |
1754 | | * number, between the number and the units, or after the units are ignored, |
1755 | | * but other characters will cause a parsing failure. If the string cannot |
1756 | | * be understood, the function will return CE_Failure. |
1757 | | * |
1758 | | * @param pszValue the string to parse |
1759 | | * @param[out] pnValue the parsed size, converted to bytes (if unit was specified) |
1760 | | * @param[out] pbUnitSpecified whether the string indicated the units |
1761 | | * |
1762 | | * @return CE_None on success, CE_Failure otherwise |
1763 | | * @since 3.10 |
1764 | | */ |
1765 | | CPLErr CPLParseMemorySize(const char *pszValue, GIntBig *pnValue, |
1766 | | bool *pbUnitSpecified) |
1767 | 1.16M | { |
1768 | 1.16M | const char *start = pszValue; |
1769 | 1.16M | char *end = nullptr; |
1770 | | |
1771 | | // trim leading whitespace |
1772 | 1.16M | while (*start == ' ') |
1773 | 0 | { |
1774 | 0 | start++; |
1775 | 0 | } |
1776 | | |
1777 | 1.16M | auto len = CPLStrnlen(start, 100); |
1778 | 1.16M | double value = CPLStrtodM(start, &end); |
1779 | 1.16M | const char *unit = nullptr; |
1780 | 1.16M | bool unitIsNotPercent = false; |
1781 | | |
1782 | 1.16M | if (end == start) |
1783 | 0 | { |
1784 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, "Received non-numeric value: %s", |
1785 | 0 | pszValue); |
1786 | 0 | return CE_Failure; |
1787 | 0 | } |
1788 | | |
1789 | 1.16M | if (value < 0 || !std::isfinite(value)) |
1790 | 0 | { |
1791 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1792 | 0 | "Memory size must be a positive number or zero."); |
1793 | 0 | return CE_Failure; |
1794 | 0 | } |
1795 | | |
1796 | 3.50M | for (const char *c = end; c < start + len; c++) |
1797 | 2.33M | { |
1798 | 2.33M | if (unit == nullptr) |
1799 | 1.16M | { |
1800 | | // check various suffixes and convert number into bytes |
1801 | 1.16M | if (*c == '%') |
1802 | 0 | { |
1803 | 0 | if (value < 0 || value > 100) |
1804 | 0 | { |
1805 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1806 | 0 | "Memory percentage must be between 0 and 100."); |
1807 | 0 | return CE_Failure; |
1808 | 0 | } |
1809 | 0 | auto bytes = CPLGetUsablePhysicalRAM(); |
1810 | 0 | if (bytes == 0) |
1811 | 0 | { |
1812 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1813 | 0 | "Cannot determine usable physical RAM"); |
1814 | 0 | return CE_Failure; |
1815 | 0 | } |
1816 | 0 | value *= static_cast<double>(bytes / 100); |
1817 | 0 | unit = c; |
1818 | 0 | } |
1819 | 1.16M | else |
1820 | 1.16M | { |
1821 | 1.16M | switch (*c) |
1822 | 1.16M | { |
1823 | 0 | case 'G': |
1824 | 0 | case 'g': |
1825 | 0 | value *= 1024; |
1826 | 0 | [[fallthrough]]; |
1827 | 1.16M | case 'M': |
1828 | 1.16M | case 'm': |
1829 | 1.16M | value *= 1024; |
1830 | 1.16M | [[fallthrough]]; |
1831 | 1.16M | case 'K': |
1832 | 1.16M | case 'k': |
1833 | 1.16M | value *= 1024; |
1834 | 1.16M | unit = c; |
1835 | 1.16M | unitIsNotPercent = true; |
1836 | 1.16M | break; |
1837 | 0 | case ' ': |
1838 | 0 | break; |
1839 | 0 | default: |
1840 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1841 | 0 | "Failed to parse memory size: %s", pszValue); |
1842 | 0 | return CE_Failure; |
1843 | 1.16M | } |
1844 | 1.16M | } |
1845 | 1.16M | } |
1846 | 1.16M | else if (unitIsNotPercent && c == unit + 1 && (*c == 'b' || *c == 'B')) |
1847 | 1.16M | { |
1848 | | // ignore 'B' or 'b' as part of unit |
1849 | 1.16M | continue; |
1850 | 1.16M | } |
1851 | 0 | else if (*c != ' ') |
1852 | 0 | { |
1853 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1854 | 0 | "Failed to parse memory size: %s", pszValue); |
1855 | 0 | return CE_Failure; |
1856 | 0 | } |
1857 | 2.33M | } |
1858 | | |
1859 | 1.16M | if (value > static_cast<double>(std::numeric_limits<GIntBig>::max()) || |
1860 | 1.16M | value > static_cast<double>(std::numeric_limits<size_t>::max())) |
1861 | 0 | { |
1862 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, "Memory size is too large: %s", |
1863 | 0 | pszValue); |
1864 | 0 | return CE_Failure; |
1865 | 0 | } |
1866 | | |
1867 | 1.16M | *pnValue = static_cast<GIntBig>(value); |
1868 | 1.16M | if (pbUnitSpecified) |
1869 | 199 | { |
1870 | 199 | *pbUnitSpecified = (unit != nullptr); |
1871 | 199 | } |
1872 | 1.16M | return CE_None; |
1873 | 1.16M | } |
1874 | | |
1875 | | /********************************************************************** |
1876 | | * CPLParseNameValue() |
1877 | | **********************************************************************/ |
1878 | | |
1879 | | /** |
1880 | | * Parse NAME=VALUE string into name and value components. |
1881 | | * |
1882 | | * Note that if ppszKey is non-NULL, the key (or name) portion will be |
1883 | | * allocated using CPLMalloc() and returned in that pointer. It is the |
1884 | | * application's responsibility to free this string, but the application should |
1885 | | * not modify or free the returned value portion. |
1886 | | * |
1887 | | * This function also supports "NAME:VALUE" strings and will strip white |
1888 | | * space from around the delimiter when forming name and value strings. |
1889 | | * |
1890 | | * Eventually CSLFetchNameValue() and friends may be modified to use |
1891 | | * CPLParseNameValue(). |
1892 | | * |
1893 | | * @param pszNameValue string in "NAME=VALUE" format. |
1894 | | * @param ppszKey optional pointer though which to return the name |
1895 | | * portion. |
1896 | | * |
1897 | | * @return the value portion (pointing into the original string). |
1898 | | */ |
1899 | | |
1900 | | const char *CPLParseNameValue(const char *pszNameValue, char **ppszKey) |
1901 | 15.7M | { |
1902 | 354M | for (int i = 0; pszNameValue[i] != '\0'; ++i) |
1903 | 348M | { |
1904 | 348M | if (pszNameValue[i] == '=' || pszNameValue[i] == ':') |
1905 | 9.02M | { |
1906 | 9.02M | const char *pszValue = pszNameValue + i + 1; |
1907 | 12.0M | while (*pszValue == ' ' || *pszValue == '\t') |
1908 | 2.99M | ++pszValue; |
1909 | | |
1910 | 9.02M | if (ppszKey != nullptr) |
1911 | 9.02M | { |
1912 | 9.02M | *ppszKey = static_cast<char *>(CPLMalloc(i + 1)); |
1913 | 9.02M | memcpy(*ppszKey, pszNameValue, i); |
1914 | 9.02M | (*ppszKey)[i] = '\0'; |
1915 | 9.39M | while (i > 0 && |
1916 | 9.08M | ((*ppszKey)[i - 1] == ' ' || (*ppszKey)[i - 1] == '\t')) |
1917 | 365k | { |
1918 | 365k | (*ppszKey)[i - 1] = '\0'; |
1919 | 365k | i--; |
1920 | 365k | } |
1921 | 9.02M | } |
1922 | | |
1923 | 9.02M | return pszValue; |
1924 | 9.02M | } |
1925 | 348M | } |
1926 | | |
1927 | 6.68M | return nullptr; |
1928 | 15.7M | } |
1929 | | |
1930 | | namespace cpl |
1931 | | { |
1932 | | std::pair<std::string_view, std::string_view> |
1933 | | parse_name_value(std::string_view svNameValue) |
1934 | 0 | { |
1935 | 0 | for (size_t i = 0; i < svNameValue.size(); ++i) |
1936 | 0 | { |
1937 | 0 | if (svNameValue[i] == '=' || svNameValue[i] == ':') |
1938 | 0 | { |
1939 | 0 | auto parsed = std::make_pair(trim(svNameValue.substr(0, i)), |
1940 | 0 | trim(svNameValue.substr(i + 1))); |
1941 | |
|
1942 | 0 | if (!parsed.first.empty()) |
1943 | 0 | { |
1944 | 0 | return parsed; |
1945 | 0 | } |
1946 | 0 | else |
1947 | 0 | { |
1948 | 0 | return std::make_pair(std::string_view(), std::string_view()); |
1949 | 0 | } |
1950 | 0 | } |
1951 | 0 | } |
1952 | | |
1953 | 0 | return std::make_pair(std::string_view(), std::string_view()); |
1954 | 0 | } |
1955 | | |
1956 | | std::pair<std::string_view, std::string_view> |
1957 | | parse_name_value(const char *pszNameValue) |
1958 | 0 | { |
1959 | 0 | return parse_name_value(std::string_view(pszNameValue)); |
1960 | 0 | } |
1961 | | |
1962 | | } // namespace cpl |
1963 | | |
1964 | | /********************************************************************** |
1965 | | * CPLParseNameValueSep() |
1966 | | **********************************************************************/ |
1967 | | /** |
1968 | | * Parse NAME<Sep>VALUE string into name and value components. |
1969 | | * |
1970 | | * This is derived directly from CPLParseNameValue() which will separate |
1971 | | * on '=' OR ':', here chSep is required for specifying the separator |
1972 | | * explicitly. |
1973 | | * |
1974 | | * @param pszNameValue string in "NAME=VALUE" format. |
1975 | | * @param ppszKey optional pointer though which to return the name |
1976 | | * portion. |
1977 | | * @param chSep required single char separator |
1978 | | * @return the value portion (pointing into original string). |
1979 | | */ |
1980 | | |
1981 | | const char *CPLParseNameValueSep(const char *pszNameValue, char **ppszKey, |
1982 | | char chSep) |
1983 | 37 | { |
1984 | 1.14k | for (int i = 0; pszNameValue[i] != '\0'; ++i) |
1985 | 1.11k | { |
1986 | 1.11k | if (pszNameValue[i] == chSep) |
1987 | 7 | { |
1988 | 7 | const char *pszValue = pszNameValue + i + 1; |
1989 | 7 | while (*pszValue == ' ' || *pszValue == '\t') |
1990 | 0 | ++pszValue; |
1991 | | |
1992 | 7 | if (ppszKey != nullptr) |
1993 | 7 | { |
1994 | 7 | *ppszKey = static_cast<char *>(CPLMalloc(i + 1)); |
1995 | 7 | memcpy(*ppszKey, pszNameValue, i); |
1996 | 7 | (*ppszKey)[i] = '\0'; |
1997 | 7 | while (i > 0 && |
1998 | 7 | ((*ppszKey)[i - 1] == ' ' || (*ppszKey)[i - 1] == '\t')) |
1999 | 0 | { |
2000 | 0 | (*ppszKey)[i - 1] = '\0'; |
2001 | 0 | i--; |
2002 | 0 | } |
2003 | 7 | } |
2004 | | |
2005 | 7 | return pszValue; |
2006 | 7 | } |
2007 | 1.11k | } |
2008 | | |
2009 | 30 | return nullptr; |
2010 | 37 | } |
2011 | | |
2012 | | /********************************************************************** |
2013 | | * CSLFetchNameValueMultiple() |
2014 | | **********************************************************************/ |
2015 | | |
2016 | | /** In a StringList of "Name=Value" pairs, look for all the |
2017 | | * values with the specified name. The search is not case |
2018 | | * sensitive. |
2019 | | * ("Name:Value" pairs are also supported for backward compatibility |
2020 | | * with older stuff.) |
2021 | | * |
2022 | | * Returns StringList with one entry for each occurrence of the |
2023 | | * specified name. The StringList should eventually be destroyed |
2024 | | * by calling CSLDestroy(). |
2025 | | * |
2026 | | * Returns NULL if the name is not found. |
2027 | | */ |
2028 | | |
2029 | | char **CSLFetchNameValueMultiple(CSLConstList papszStrList, const char *pszName) |
2030 | 66.4k | { |
2031 | 66.4k | if (papszStrList == nullptr || pszName == nullptr) |
2032 | 65.7k | return nullptr; |
2033 | | |
2034 | 690 | const size_t nLen = strlen(pszName); |
2035 | 690 | char **papszValues = nullptr; |
2036 | 43.2k | while (*papszStrList != nullptr) |
2037 | 42.5k | { |
2038 | 42.5k | if (EQUALN(*papszStrList, pszName, nLen) && |
2039 | 0 | ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':')) |
2040 | 0 | { |
2041 | 0 | papszValues = CSLAddString(papszValues, (*papszStrList) + nLen + 1); |
2042 | 0 | } |
2043 | 42.5k | ++papszStrList; |
2044 | 42.5k | } |
2045 | | |
2046 | 690 | return papszValues; |
2047 | 66.4k | } |
2048 | | |
2049 | | /********************************************************************** |
2050 | | * CSLAddNameValue() |
2051 | | **********************************************************************/ |
2052 | | |
2053 | | /** Add a new entry to a StringList of "Name=Value" pairs, |
2054 | | * ("Name:Value" pairs are also supported for backward compatibility |
2055 | | * with older stuff.) |
2056 | | * |
2057 | | * This function does not check if a "Name=Value" pair already exists |
2058 | | * for that name and can generate multiple entries for the same name. |
2059 | | * Use CSLSetNameValue() if you want each name to have only one value. |
2060 | | * |
2061 | | * Returns the modified StringList. |
2062 | | */ |
2063 | | |
2064 | | char **CSLAddNameValue(char **papszStrList, const char *pszName, |
2065 | | const char *pszValue) |
2066 | 5.55M | { |
2067 | 5.55M | if (pszName == nullptr || pszValue == nullptr) |
2068 | 0 | return papszStrList; |
2069 | | |
2070 | 5.55M | const size_t nLen = strlen(pszName) + strlen(pszValue) + 2; |
2071 | 5.55M | char *pszLine = static_cast<char *>(CPLMalloc(nLen)); |
2072 | 5.55M | snprintf(pszLine, nLen, "%s=%s", pszName, pszValue); |
2073 | 5.55M | papszStrList = CSLAddString(papszStrList, pszLine); |
2074 | 5.55M | CPLFree(pszLine); |
2075 | | |
2076 | 5.55M | return papszStrList; |
2077 | 5.55M | } |
2078 | | |
2079 | | /************************************************************************/ |
2080 | | /* CSLSetNameValue() */ |
2081 | | /************************************************************************/ |
2082 | | |
2083 | | /** |
2084 | | * Assign value to name in StringList. |
2085 | | * |
2086 | | * Set the value for a given name in a StringList of "Name=Value" pairs |
2087 | | * ("Name:Value" pairs are also supported for backward compatibility |
2088 | | * with older stuff.) |
2089 | | * |
2090 | | * If there is already a value for that name in the list then the value |
2091 | | * is changed, otherwise a new "Name=Value" pair is added. |
2092 | | * |
2093 | | * @param papszList the original list, the modified version is returned. |
2094 | | * @param pszName the name to be assigned a value. This should be a well |
2095 | | * formed token (no spaces or very special characters). |
2096 | | * @param pszValue the value to assign to the name. This should not contain |
2097 | | * any newlines (CR or LF) but is otherwise pretty much unconstrained. If |
2098 | | * NULL any corresponding value will be removed. |
2099 | | * |
2100 | | * @return modified StringList. |
2101 | | */ |
2102 | | |
2103 | | char **CSLSetNameValue(char **papszList, const char *pszName, |
2104 | | const char *pszValue) |
2105 | 10.0M | { |
2106 | 10.0M | if (pszName == nullptr) |
2107 | 0 | return papszList; |
2108 | | |
2109 | 10.0M | size_t nLen = strlen(pszName); |
2110 | 10.0M | while (nLen > 0 && pszName[nLen - 1] == ' ') |
2111 | 73 | nLen--; |
2112 | 10.0M | char **papszPtr = papszList; |
2113 | 268M | while (papszPtr && *papszPtr != nullptr) |
2114 | 263M | { |
2115 | 263M | if (EQUALN(*papszPtr, pszName, nLen)) |
2116 | 9.40M | { |
2117 | 9.40M | size_t i; |
2118 | 10.5M | for (i = nLen; (*papszPtr)[i] == ' '; ++i) |
2119 | 1.13M | { |
2120 | 1.13M | } |
2121 | 9.40M | if ((*papszPtr)[i] == '=' || (*papszPtr)[i] == ':') |
2122 | 4.38M | { |
2123 | | // Found it. |
2124 | | // Change the value... make sure to keep the ':' or '='. |
2125 | 4.38M | const char cSep = (*papszPtr)[i]; |
2126 | | |
2127 | 4.38M | CPLFree(*papszPtr); |
2128 | | |
2129 | | // If the value is NULL, remove this entry completely. |
2130 | 4.38M | if (pszValue == nullptr) |
2131 | 398k | { |
2132 | 398k | while (papszPtr[1] != nullptr) |
2133 | 0 | { |
2134 | 0 | *papszPtr = papszPtr[1]; |
2135 | 0 | ++papszPtr; |
2136 | 0 | } |
2137 | 398k | *papszPtr = nullptr; |
2138 | 398k | } |
2139 | | |
2140 | | // Otherwise replace with new value. |
2141 | 3.98M | else |
2142 | 3.98M | { |
2143 | 3.98M | const size_t nLen2 = strlen(pszName) + strlen(pszValue) + 2; |
2144 | 3.98M | *papszPtr = static_cast<char *>(CPLMalloc(nLen2)); |
2145 | 3.98M | snprintf(*papszPtr, nLen2, "%s%c%s", pszName, cSep, |
2146 | 3.98M | pszValue); |
2147 | 3.98M | } |
2148 | 4.38M | return papszList; |
2149 | 4.38M | } |
2150 | 9.40M | } |
2151 | 258M | ++papszPtr; |
2152 | 258M | } |
2153 | | |
2154 | 5.70M | if (pszValue == nullptr) |
2155 | 551k | return papszList; |
2156 | | |
2157 | | // The name does not exist yet. Create a new entry. |
2158 | 5.15M | return CSLAddNameValue(papszList, pszName, pszValue); |
2159 | 5.70M | } |
2160 | | |
2161 | | /************************************************************************/ |
2162 | | /* CSLSetNameValueSeparator() */ |
2163 | | /************************************************************************/ |
2164 | | |
2165 | | /** |
2166 | | * Replace the default separator (":" or "=") with the passed separator |
2167 | | * in the given name/value list. |
2168 | | * |
2169 | | * Note that if a separator other than ":" or "=" is used, the resulting |
2170 | | * list will not be manipulable by the CSL name/value functions any more. |
2171 | | * |
2172 | | * The CPLParseNameValue() function is used to break the existing lines, |
2173 | | * and it also strips white space from around the existing delimiter, thus |
2174 | | * the old separator, and any white space will be replaced by the new |
2175 | | * separator. For formatting purposes it may be desirable to include some |
2176 | | * white space in the new separator. e.g. ": " or " = ". |
2177 | | * |
2178 | | * @param papszList the list to update. Component strings may be freed |
2179 | | * but the list array will remain at the same location. |
2180 | | * |
2181 | | * @param pszSeparator the new separator string to insert. |
2182 | | */ |
2183 | | |
2184 | | void CSLSetNameValueSeparator(char **papszList, const char *pszSeparator) |
2185 | | |
2186 | 0 | { |
2187 | 0 | const int nLines = CSLCount(papszList); |
2188 | |
|
2189 | 0 | for (int iLine = 0; iLine < nLines; ++iLine) |
2190 | 0 | { |
2191 | 0 | char *pszKey = nullptr; |
2192 | 0 | const char *pszValue = CPLParseNameValue(papszList[iLine], &pszKey); |
2193 | 0 | if (pszValue == nullptr || pszKey == nullptr) |
2194 | 0 | { |
2195 | 0 | CPLFree(pszKey); |
2196 | 0 | continue; |
2197 | 0 | } |
2198 | | |
2199 | 0 | char *pszNewLine = static_cast<char *>(CPLMalloc( |
2200 | 0 | strlen(pszValue) + strlen(pszKey) + strlen(pszSeparator) + 1)); |
2201 | 0 | strcpy(pszNewLine, pszKey); |
2202 | 0 | strcat(pszNewLine, pszSeparator); |
2203 | 0 | strcat(pszNewLine, pszValue); |
2204 | 0 | CPLFree(papszList[iLine]); |
2205 | 0 | papszList[iLine] = pszNewLine; |
2206 | 0 | CPLFree(pszKey); |
2207 | 0 | } |
2208 | 0 | } |
2209 | | |
2210 | | /************************************************************************/ |
2211 | | /* CPLEscapeString() */ |
2212 | | /************************************************************************/ |
2213 | | |
2214 | | /** |
2215 | | * Apply escaping to string to preserve special characters. |
2216 | | * |
2217 | | * This function will "escape" a variety of special characters |
2218 | | * to make the string suitable to embed within a string constant |
2219 | | * or to write within a text stream but in a form that can be |
2220 | | * reconstituted to its original form. The escaping will even preserve |
2221 | | * zero bytes allowing preservation of raw binary data. |
2222 | | * |
2223 | | * CPLES_BackslashQuotable(0): This scheme turns a binary string into |
2224 | | * a form suitable to be placed within double quotes as a string constant. |
2225 | | * The backslash, quote, '\\0' and newline characters are all escaped in |
2226 | | * the usual C style. |
2227 | | * |
2228 | | * CPLES_XML(1): This scheme converts the '<', '>', '"' and '&' characters into |
2229 | | * their XML/HTML equivalent (<, >, " and &) making a string safe |
2230 | | * to embed as CDATA within an XML element. The '\\0' is not escaped and |
2231 | | * should not be included in the input. |
2232 | | * |
2233 | | * CPLES_URL(2): Everything except alphanumerics and the characters |
2234 | | * '$', '-', '_', '.', '+', '!', '*', ''', '(', ')' and ',' (see RFC1738) are |
2235 | | * converted to a percent followed by a two digit hex encoding of the character |
2236 | | * (leading zero supplied if needed). This is the mechanism used for encoding |
2237 | | * values to be passed in URLs. Note that this is different from what |
2238 | | * CPLString::URLEncode() does. |
2239 | | * |
2240 | | * CPLES_SQL(3): All single quotes are replaced with two single quotes. |
2241 | | * Suitable for use when constructing literal values for SQL commands where |
2242 | | * the literal will be enclosed in single quotes. |
2243 | | * |
2244 | | * CPLES_CSV(4): If the values contains commas, semicolons, tabs, double quotes, |
2245 | | * or newlines it placed in double quotes, and double quotes in the value are |
2246 | | * doubled. Suitable for use when constructing field values for .csv files. |
2247 | | * Note that CPLUnescapeString() currently does not support this format, only |
2248 | | * CPLEscapeString(). See cpl_csv.cpp for CSV parsing support. |
2249 | | * |
2250 | | * CPLES_SQLI(7): All double quotes are replaced with two double quotes. |
2251 | | * Suitable for use when constructing identifiers for SQL commands where |
2252 | | * the literal will be enclosed in double quotes. |
2253 | | * |
2254 | | * @param pszInput the string to escape. |
2255 | | * @param nLength The number of bytes of data to preserve. If this is -1 |
2256 | | * the strlen(pszString) function will be used to compute the length. |
2257 | | * @param nScheme the encoding scheme to use. |
2258 | | * |
2259 | | * @return an escaped, zero terminated string that should be freed with |
2260 | | * CPLFree() when no longer needed. |
2261 | | */ |
2262 | | |
2263 | | char *CPLEscapeString(const char *pszInput, int nLength, int nScheme) |
2264 | 33.2M | { |
2265 | 33.2M | const size_t szLength = |
2266 | 33.2M | (nLength < 0) ? strlen(pszInput) : static_cast<size_t>(nLength); |
2267 | 33.2M | #define nLength no_longer_use_me |
2268 | | |
2269 | 33.2M | size_t nSizeAlloc = 1; |
2270 | | #if SIZEOF_VOIDP < 8 |
2271 | | bool bWrapAround = false; |
2272 | | const auto IncSizeAlloc = [&nSizeAlloc, &bWrapAround](size_t inc) |
2273 | | { |
2274 | | constexpr size_t SZ_MAX = std::numeric_limits<size_t>::max(); |
2275 | | if (nSizeAlloc > SZ_MAX - inc) |
2276 | | { |
2277 | | bWrapAround = true; |
2278 | | nSizeAlloc = 0; |
2279 | | } |
2280 | | nSizeAlloc += inc; |
2281 | | }; |
2282 | | #else |
2283 | 1.03G | const auto IncSizeAlloc = [&nSizeAlloc](size_t inc) { nSizeAlloc += inc; }; |
2284 | 33.2M | #endif |
2285 | | |
2286 | 33.2M | if (nScheme == CPLES_BackslashQuotable) |
2287 | 1.62k | { |
2288 | 245k | for (size_t iIn = 0; iIn < szLength; iIn++) |
2289 | 244k | { |
2290 | 244k | if (pszInput[iIn] == '\0' || pszInput[iIn] == '\n' || |
2291 | 236k | pszInput[iIn] == '"' || pszInput[iIn] == '\\') |
2292 | 9.23k | IncSizeAlloc(2); |
2293 | 234k | else |
2294 | 234k | IncSizeAlloc(1); |
2295 | 244k | } |
2296 | 1.62k | } |
2297 | 33.2M | else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES) |
2298 | 31.9M | { |
2299 | 998M | for (size_t iIn = 0; iIn < szLength; ++iIn) |
2300 | 966M | { |
2301 | 966M | if (pszInput[iIn] == '<') |
2302 | 12.7M | { |
2303 | 12.7M | IncSizeAlloc(4); |
2304 | 12.7M | } |
2305 | 953M | else if (pszInput[iIn] == '>') |
2306 | 49.2M | { |
2307 | 49.2M | IncSizeAlloc(4); |
2308 | 49.2M | } |
2309 | 904M | else if (pszInput[iIn] == '&') |
2310 | 1.52M | { |
2311 | 1.52M | IncSizeAlloc(5); |
2312 | 1.52M | } |
2313 | 902M | else if (pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES) |
2314 | 3.08M | { |
2315 | 3.08M | IncSizeAlloc(6); |
2316 | 3.08M | } |
2317 | | // Python 2 does not display the UTF-8 character corresponding |
2318 | | // to the byte-order mark (BOM), so escape it. |
2319 | 899M | else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] == |
2320 | 899M | 0xEF && |
2321 | 272k | (reinterpret_cast<const unsigned char *>( |
2322 | 272k | pszInput))[iIn + 1] == 0xBB && |
2323 | 106k | (reinterpret_cast<const unsigned char *>( |
2324 | 106k | pszInput))[iIn + 2] == 0xBF) |
2325 | 89.1k | { |
2326 | 89.1k | IncSizeAlloc(8); |
2327 | 89.1k | iIn += 2; |
2328 | 89.1k | } |
2329 | 899M | else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] < |
2330 | 899M | 0x20 && |
2331 | 8.49M | pszInput[iIn] != 0x9 && pszInput[iIn] != 0xA && |
2332 | 4.70M | pszInput[iIn] != 0xD) |
2333 | 4.43M | { |
2334 | | // These control characters are unrepresentable in XML format, |
2335 | | // so we just drop them. #4117 |
2336 | 4.43M | } |
2337 | 895M | else |
2338 | 895M | { |
2339 | 895M | IncSizeAlloc(1); |
2340 | 895M | } |
2341 | 966M | } |
2342 | 31.9M | } |
2343 | 1.33M | else if (nScheme == CPLES_URL) // Untested at implementation. |
2344 | 176k | { |
2345 | 68.1M | for (size_t iIn = 0; iIn < szLength; ++iIn) |
2346 | 68.0M | { |
2347 | 68.0M | if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') || |
2348 | 40.2M | (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') || |
2349 | 35.2M | (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') || |
2350 | 30.7M | pszInput[iIn] == '$' || pszInput[iIn] == '-' || |
2351 | 30.6M | pszInput[iIn] == '_' || pszInput[iIn] == '.' || |
2352 | 29.2M | pszInput[iIn] == '+' || pszInput[iIn] == '!' || |
2353 | 27.9M | pszInput[iIn] == '*' || pszInput[iIn] == '\'' || |
2354 | 27.7M | pszInput[iIn] == '(' || pszInput[iIn] == ')' || |
2355 | 27.7M | pszInput[iIn] == ',') |
2356 | 40.5M | { |
2357 | 40.5M | IncSizeAlloc(1); |
2358 | 40.5M | } |
2359 | 27.4M | else |
2360 | 27.4M | { |
2361 | 27.4M | IncSizeAlloc(3); |
2362 | 27.4M | } |
2363 | 68.0M | } |
2364 | 176k | } |
2365 | 1.16M | else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI) |
2366 | 28 | { |
2367 | 28 | const char chQuote = nScheme == CPLES_SQL ? '\'' : '\"'; |
2368 | 519 | for (size_t iIn = 0; iIn < szLength; ++iIn) |
2369 | 491 | { |
2370 | 491 | if (pszInput[iIn] == chQuote) |
2371 | 0 | { |
2372 | 0 | IncSizeAlloc(2); |
2373 | 0 | } |
2374 | 491 | else |
2375 | 491 | { |
2376 | 491 | IncSizeAlloc(1); |
2377 | 491 | } |
2378 | 491 | } |
2379 | 28 | } |
2380 | 1.16M | else if (nScheme == CPLES_CSV || nScheme == CPLES_CSV_FORCE_QUOTING) |
2381 | 1.16M | { |
2382 | 1.16M | if (nScheme == CPLES_CSV && strcspn(pszInput, "\",;\t\n\r") == szLength) |
2383 | 1.12M | { |
2384 | 1.12M | char *pszOutput = |
2385 | 1.12M | static_cast<char *>(VSI_MALLOC_VERBOSE(szLength + 1)); |
2386 | 1.12M | if (pszOutput == nullptr) |
2387 | 0 | return nullptr; |
2388 | 1.12M | memcpy(pszOutput, pszInput, szLength + 1); |
2389 | 1.12M | return pszOutput; |
2390 | 1.12M | } |
2391 | 34.8k | else |
2392 | 34.8k | { |
2393 | 34.8k | IncSizeAlloc(1); |
2394 | 4.45M | for (size_t iIn = 0; iIn < szLength; ++iIn) |
2395 | 4.41M | { |
2396 | 4.41M | if (pszInput[iIn] == '\"') |
2397 | 68.1k | { |
2398 | 68.1k | IncSizeAlloc(2); |
2399 | 68.1k | } |
2400 | 4.34M | else |
2401 | 4.34M | IncSizeAlloc(1); |
2402 | 4.41M | } |
2403 | 34.8k | IncSizeAlloc(1); |
2404 | 34.8k | } |
2405 | 1.16M | } |
2406 | 0 | else |
2407 | 0 | { |
2408 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2409 | 0 | "Undefined escaping scheme (%d) in CPLEscapeString()", |
2410 | 0 | nScheme); |
2411 | 0 | return CPLStrdup(""); |
2412 | 0 | } |
2413 | | |
2414 | | #if SIZEOF_VOIDP < 8 |
2415 | | if (bWrapAround) |
2416 | | { |
2417 | | CPLError(CE_Failure, CPLE_OutOfMemory, |
2418 | | "Out of memory in CPLEscapeString()"); |
2419 | | return nullptr; |
2420 | | } |
2421 | | #endif |
2422 | | |
2423 | 32.1M | char *pszOutput = static_cast<char *>(VSI_MALLOC_VERBOSE(nSizeAlloc)); |
2424 | 32.1M | if (pszOutput == nullptr) |
2425 | 0 | return nullptr; |
2426 | | |
2427 | 32.1M | size_t iOut = 0; |
2428 | | |
2429 | 32.1M | if (nScheme == CPLES_BackslashQuotable) |
2430 | 1.62k | { |
2431 | 245k | for (size_t iIn = 0; iIn < szLength; iIn++) |
2432 | 244k | { |
2433 | 244k | if (pszInput[iIn] == '\0') |
2434 | 0 | { |
2435 | 0 | pszOutput[iOut++] = '\\'; |
2436 | 0 | pszOutput[iOut++] = '0'; |
2437 | 0 | } |
2438 | 244k | else if (pszInput[iIn] == '\n') |
2439 | 7.64k | { |
2440 | 7.64k | pszOutput[iOut++] = '\\'; |
2441 | 7.64k | pszOutput[iOut++] = 'n'; |
2442 | 7.64k | } |
2443 | 236k | else if (pszInput[iIn] == '"') |
2444 | 585 | { |
2445 | 585 | pszOutput[iOut++] = '\\'; |
2446 | 585 | pszOutput[iOut++] = '\"'; |
2447 | 585 | } |
2448 | 235k | else if (pszInput[iIn] == '\\') |
2449 | 1.00k | { |
2450 | 1.00k | pszOutput[iOut++] = '\\'; |
2451 | 1.00k | pszOutput[iOut++] = '\\'; |
2452 | 1.00k | } |
2453 | 234k | else |
2454 | 234k | pszOutput[iOut++] = pszInput[iIn]; |
2455 | 244k | } |
2456 | 1.62k | pszOutput[iOut++] = '\0'; |
2457 | 1.62k | } |
2458 | 32.1M | else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES) |
2459 | 31.9M | { |
2460 | 998M | for (size_t iIn = 0; iIn < szLength; ++iIn) |
2461 | 966M | { |
2462 | 966M | if (pszInput[iIn] == '<') |
2463 | 12.7M | { |
2464 | 12.7M | pszOutput[iOut++] = '&'; |
2465 | 12.7M | pszOutput[iOut++] = 'l'; |
2466 | 12.7M | pszOutput[iOut++] = 't'; |
2467 | 12.7M | pszOutput[iOut++] = ';'; |
2468 | 12.7M | } |
2469 | 953M | else if (pszInput[iIn] == '>') |
2470 | 49.2M | { |
2471 | 49.2M | pszOutput[iOut++] = '&'; |
2472 | 49.2M | pszOutput[iOut++] = 'g'; |
2473 | 49.2M | pszOutput[iOut++] = 't'; |
2474 | 49.2M | pszOutput[iOut++] = ';'; |
2475 | 49.2M | } |
2476 | 904M | else if (pszInput[iIn] == '&') |
2477 | 1.52M | { |
2478 | 1.52M | pszOutput[iOut++] = '&'; |
2479 | 1.52M | pszOutput[iOut++] = 'a'; |
2480 | 1.52M | pszOutput[iOut++] = 'm'; |
2481 | 1.52M | pszOutput[iOut++] = 'p'; |
2482 | 1.52M | pszOutput[iOut++] = ';'; |
2483 | 1.52M | } |
2484 | 902M | else if (pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES) |
2485 | 3.08M | { |
2486 | 3.08M | pszOutput[iOut++] = '&'; |
2487 | 3.08M | pszOutput[iOut++] = 'q'; |
2488 | 3.08M | pszOutput[iOut++] = 'u'; |
2489 | 3.08M | pszOutput[iOut++] = 'o'; |
2490 | 3.08M | pszOutput[iOut++] = 't'; |
2491 | 3.08M | pszOutput[iOut++] = ';'; |
2492 | 3.08M | } |
2493 | | // Python 2 does not display the UTF-8 character corresponding |
2494 | | // to the byte-order mark (BOM), so escape it. |
2495 | 899M | else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] == |
2496 | 899M | 0xEF && |
2497 | 272k | (reinterpret_cast<const unsigned char *>( |
2498 | 272k | pszInput))[iIn + 1] == 0xBB && |
2499 | 106k | (reinterpret_cast<const unsigned char *>( |
2500 | 106k | pszInput))[iIn + 2] == 0xBF) |
2501 | 89.1k | { |
2502 | 89.1k | pszOutput[iOut++] = '&'; |
2503 | 89.1k | pszOutput[iOut++] = '#'; |
2504 | 89.1k | pszOutput[iOut++] = 'x'; |
2505 | 89.1k | pszOutput[iOut++] = 'F'; |
2506 | 89.1k | pszOutput[iOut++] = 'E'; |
2507 | 89.1k | pszOutput[iOut++] = 'F'; |
2508 | 89.1k | pszOutput[iOut++] = 'F'; |
2509 | 89.1k | pszOutput[iOut++] = ';'; |
2510 | 89.1k | iIn += 2; |
2511 | 89.1k | } |
2512 | 899M | else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] < |
2513 | 899M | 0x20 && |
2514 | 8.49M | pszInput[iIn] != 0x9 && pszInput[iIn] != 0xA && |
2515 | 4.70M | pszInput[iIn] != 0xD) |
2516 | 4.43M | { |
2517 | | // These control characters are unrepresentable in XML format, |
2518 | | // so we just drop them. #4117 |
2519 | 4.43M | } |
2520 | 895M | else |
2521 | 895M | { |
2522 | 895M | pszOutput[iOut++] = pszInput[iIn]; |
2523 | 895M | } |
2524 | 966M | } |
2525 | 31.9M | pszOutput[iOut++] = '\0'; |
2526 | 31.9M | } |
2527 | 211k | else if (nScheme == CPLES_URL) // Untested at implementation. |
2528 | 176k | { |
2529 | 68.1M | for (size_t iIn = 0; iIn < szLength; ++iIn) |
2530 | 68.0M | { |
2531 | 68.0M | if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') || |
2532 | 40.2M | (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') || |
2533 | 35.2M | (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') || |
2534 | 30.7M | pszInput[iIn] == '$' || pszInput[iIn] == '-' || |
2535 | 30.6M | pszInput[iIn] == '_' || pszInput[iIn] == '.' || |
2536 | 29.2M | pszInput[iIn] == '+' || pszInput[iIn] == '!' || |
2537 | 27.9M | pszInput[iIn] == '*' || pszInput[iIn] == '\'' || |
2538 | 27.7M | pszInput[iIn] == '(' || pszInput[iIn] == ')' || |
2539 | 27.7M | pszInput[iIn] == ',') |
2540 | 40.5M | { |
2541 | 40.5M | pszOutput[iOut++] = pszInput[iIn]; |
2542 | 40.5M | } |
2543 | 27.4M | else |
2544 | 27.4M | { |
2545 | 27.4M | snprintf(pszOutput + iOut, nSizeAlloc - iOut, "%%%02X", |
2546 | 27.4M | static_cast<unsigned char>(pszInput[iIn])); |
2547 | 27.4M | iOut += 3; |
2548 | 27.4M | } |
2549 | 68.0M | } |
2550 | 176k | pszOutput[iOut++] = '\0'; |
2551 | 176k | } |
2552 | 34.8k | else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI) |
2553 | 28 | { |
2554 | 28 | const char chQuote = nScheme == CPLES_SQL ? '\'' : '\"'; |
2555 | 519 | for (size_t iIn = 0; iIn < szLength; ++iIn) |
2556 | 491 | { |
2557 | 491 | if (pszInput[iIn] == chQuote) |
2558 | 0 | { |
2559 | 0 | pszOutput[iOut++] = chQuote; |
2560 | 0 | pszOutput[iOut++] = chQuote; |
2561 | 0 | } |
2562 | 491 | else |
2563 | 491 | { |
2564 | 491 | pszOutput[iOut++] = pszInput[iIn]; |
2565 | 491 | } |
2566 | 491 | } |
2567 | 28 | pszOutput[iOut++] = '\0'; |
2568 | 28 | } |
2569 | 34.8k | else if (nScheme == CPLES_CSV || nScheme == CPLES_CSV_FORCE_QUOTING) |
2570 | 34.8k | { |
2571 | 34.8k | pszOutput[iOut++] = '\"'; |
2572 | | |
2573 | 4.45M | for (size_t iIn = 0; iIn < szLength; ++iIn) |
2574 | 4.41M | { |
2575 | 4.41M | if (pszInput[iIn] == '\"') |
2576 | 68.1k | { |
2577 | 68.1k | pszOutput[iOut++] = '\"'; |
2578 | 68.1k | pszOutput[iOut++] = '\"'; |
2579 | 68.1k | } |
2580 | 4.34M | else |
2581 | 4.34M | pszOutput[iOut++] = pszInput[iIn]; |
2582 | 4.41M | } |
2583 | 34.8k | pszOutput[iOut++] = '\"'; |
2584 | 34.8k | pszOutput[iOut++] = '\0'; |
2585 | 34.8k | } |
2586 | | |
2587 | 32.1M | return pszOutput; |
2588 | 32.1M | #undef nLength |
2589 | 32.1M | } |
2590 | | |
2591 | | /************************************************************************/ |
2592 | | /* CPLUnescapeString() */ |
2593 | | /************************************************************************/ |
2594 | | |
2595 | | /** |
2596 | | * Unescape a string. |
2597 | | * |
2598 | | * This function does the opposite of CPLEscapeString(). Given a string |
2599 | | * with special values escaped according to some scheme, it will return a |
2600 | | * new copy of the string returned to its original form. |
2601 | | * |
2602 | | * @param pszInput the input string. This is a zero terminated string. |
2603 | | * @param pnLength location to return the length of the unescaped string, |
2604 | | * which may in some cases include embedded '\\0' characters. |
2605 | | * @param nScheme the escaped scheme to undo (see CPLEscapeString() for a |
2606 | | * list). Does not yet support CSV. |
2607 | | * |
2608 | | * @return a copy of the unescaped string that should be freed by the |
2609 | | * application using CPLFree() when no longer needed. |
2610 | | */ |
2611 | | |
2612 | | CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW |
2613 | | char *CPLUnescapeString(const char *pszInput, int *pnLength, int nScheme) |
2614 | | |
2615 | 3.97M | { |
2616 | 3.97M | int iOut = 0; |
2617 | | |
2618 | | // TODO: Why times 4? |
2619 | 3.97M | char *pszOutput = static_cast<char *>(CPLMalloc(4 * strlen(pszInput) + 1)); |
2620 | 3.97M | pszOutput[0] = '\0'; |
2621 | | |
2622 | 3.97M | if (nScheme == CPLES_BackslashQuotable) |
2623 | 1.27M | { |
2624 | 33.9M | for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn) |
2625 | 32.6M | { |
2626 | 32.6M | if (pszInput[iIn] == '\\') |
2627 | 98.2k | { |
2628 | 98.2k | ++iIn; |
2629 | 98.2k | if (pszInput[iIn] == '\0') |
2630 | 3.40k | break; |
2631 | 94.8k | if (pszInput[iIn] == 'n') |
2632 | 6.09k | pszOutput[iOut++] = '\n'; |
2633 | 88.7k | else if (pszInput[iIn] == '0') |
2634 | 447 | pszOutput[iOut++] = '\0'; |
2635 | 88.3k | else |
2636 | 88.3k | pszOutput[iOut++] = pszInput[iIn]; |
2637 | 94.8k | } |
2638 | 32.5M | else |
2639 | 32.5M | { |
2640 | 32.5M | pszOutput[iOut++] = pszInput[iIn]; |
2641 | 32.5M | } |
2642 | 32.6M | } |
2643 | 1.27M | } |
2644 | 2.70M | else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES) |
2645 | 930k | { |
2646 | 930k | char ch = '\0'; |
2647 | 2.08G | for (int iIn = 0; (ch = pszInput[iIn]) != '\0'; ++iIn) |
2648 | 2.08G | { |
2649 | 2.08G | if (ch != '&') |
2650 | 2.04G | { |
2651 | 2.04G | pszOutput[iOut++] = ch; |
2652 | 2.04G | } |
2653 | 36.9M | else if (STARTS_WITH_CI(pszInput + iIn, "<")) |
2654 | 17.2M | { |
2655 | 17.2M | pszOutput[iOut++] = '<'; |
2656 | 17.2M | iIn += 3; |
2657 | 17.2M | } |
2658 | 19.6M | else if (STARTS_WITH_CI(pszInput + iIn, ">")) |
2659 | 16.6M | { |
2660 | 16.6M | pszOutput[iOut++] = '>'; |
2661 | 16.6M | iIn += 3; |
2662 | 16.6M | } |
2663 | 2.98M | else if (STARTS_WITH_CI(pszInput + iIn, "&")) |
2664 | 749k | { |
2665 | 749k | pszOutput[iOut++] = '&'; |
2666 | 749k | iIn += 4; |
2667 | 749k | } |
2668 | 2.23M | else if (STARTS_WITH_CI(pszInput + iIn, "'")) |
2669 | 24.9k | { |
2670 | 24.9k | pszOutput[iOut++] = '\''; |
2671 | 24.9k | iIn += 5; |
2672 | 24.9k | } |
2673 | 2.21M | else if (STARTS_WITH_CI(pszInput + iIn, """)) |
2674 | 1.33M | { |
2675 | 1.33M | pszOutput[iOut++] = '"'; |
2676 | 1.33M | iIn += 5; |
2677 | 1.33M | } |
2678 | 872k | else if (STARTS_WITH_CI(pszInput + iIn, "&#x")) |
2679 | 128k | { |
2680 | 128k | wchar_t anVal[2] = {0, 0}; |
2681 | 128k | iIn += 3; |
2682 | | |
2683 | 128k | unsigned int nVal = 0; |
2684 | 745k | while (true) |
2685 | 745k | { |
2686 | 745k | ch = pszInput[iIn++]; |
2687 | 745k | if (ch >= 'a' && ch <= 'f') |
2688 | 117k | nVal = nVal * 16U + |
2689 | 117k | static_cast<unsigned int>(ch - 'a' + 10); |
2690 | 628k | else if (ch >= 'A' && ch <= 'F') |
2691 | 194k | nVal = nVal * 16U + |
2692 | 194k | static_cast<unsigned int>(ch - 'A' + 10); |
2693 | 433k | else if (ch >= '0' && ch <= '9') |
2694 | 304k | nVal = nVal * 16U + static_cast<unsigned int>(ch - '0'); |
2695 | 128k | else |
2696 | 128k | break; |
2697 | 745k | } |
2698 | 128k | anVal[0] = static_cast<wchar_t>(nVal); |
2699 | 128k | if (ch != ';') |
2700 | 49.6k | break; |
2701 | 79.1k | iIn--; |
2702 | | |
2703 | 79.1k | char *pszUTF8 = |
2704 | 79.1k | CPLRecodeFromWChar(anVal, "WCHAR_T", CPL_ENC_UTF8); |
2705 | 79.1k | int nLen = static_cast<int>(strlen(pszUTF8)); |
2706 | 79.1k | memcpy(pszOutput + iOut, pszUTF8, nLen); |
2707 | 79.1k | CPLFree(pszUTF8); |
2708 | 79.1k | iOut += nLen; |
2709 | 79.1k | } |
2710 | 743k | else if (STARTS_WITH_CI(pszInput + iIn, "&#")) |
2711 | 240k | { |
2712 | 240k | wchar_t anVal[2] = {0, 0}; |
2713 | 240k | iIn += 2; |
2714 | | |
2715 | 240k | unsigned int nVal = 0; |
2716 | 1.14M | while (true) |
2717 | 1.14M | { |
2718 | 1.14M | ch = pszInput[iIn++]; |
2719 | 1.14M | if (ch >= '0' && ch <= '9') |
2720 | 901k | nVal = nVal * 10U + static_cast<unsigned int>(ch - '0'); |
2721 | 240k | else |
2722 | 240k | break; |
2723 | 1.14M | } |
2724 | 240k | anVal[0] = static_cast<wchar_t>(nVal); |
2725 | 240k | if (ch != ';') |
2726 | 48.9k | break; |
2727 | 191k | iIn--; |
2728 | | |
2729 | 191k | char *pszUTF8 = |
2730 | 191k | CPLRecodeFromWChar(anVal, "WCHAR_T", CPL_ENC_UTF8); |
2731 | 191k | const int nLen = static_cast<int>(strlen(pszUTF8)); |
2732 | 191k | memcpy(pszOutput + iOut, pszUTF8, nLen); |
2733 | 191k | CPLFree(pszUTF8); |
2734 | 191k | iOut += nLen; |
2735 | 191k | } |
2736 | 503k | else |
2737 | 503k | { |
2738 | | // Illegal escape sequence. |
2739 | 503k | CPLDebug("CPL", |
2740 | 503k | "Error unescaping CPLES_XML text, '&' character " |
2741 | 503k | "followed by unhandled escape sequence."); |
2742 | 503k | break; |
2743 | 503k | } |
2744 | 2.08G | } |
2745 | 930k | } |
2746 | 1.77M | else if (nScheme == CPLES_URL) |
2747 | 1.77M | { |
2748 | 1.14G | for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn) |
2749 | 1.13G | { |
2750 | 1.13G | if (pszInput[iIn] == '%' && pszInput[iIn + 1] != '\0' && |
2751 | 8.28M | pszInput[iIn + 2] != '\0') |
2752 | 8.25M | { |
2753 | 8.25M | int nHexChar = 0; |
2754 | | |
2755 | 8.25M | if (pszInput[iIn + 1] >= 'A' && pszInput[iIn + 1] <= 'F') |
2756 | 1.01M | nHexChar += 16 * (pszInput[iIn + 1] - 'A' + 10); |
2757 | 7.23M | else if (pszInput[iIn + 1] >= 'a' && pszInput[iIn + 1] <= 'f') |
2758 | 158k | nHexChar += 16 * (pszInput[iIn + 1] - 'a' + 10); |
2759 | 7.07M | else if (pszInput[iIn + 1] >= '0' && pszInput[iIn + 1] <= '9') |
2760 | 932k | nHexChar += 16 * (pszInput[iIn + 1] - '0'); |
2761 | 6.14M | else |
2762 | 6.14M | CPLDebug("CPL", |
2763 | 6.14M | "Error unescaping CPLES_URL text, percent not " |
2764 | 6.14M | "followed by two hex digits."); |
2765 | | |
2766 | 8.25M | if (pszInput[iIn + 2] >= 'A' && pszInput[iIn + 2] <= 'F') |
2767 | 854k | nHexChar += pszInput[iIn + 2] - 'A' + 10; |
2768 | 7.39M | else if (pszInput[iIn + 2] >= 'a' && pszInput[iIn + 2] <= 'f') |
2769 | 123k | nHexChar += pszInput[iIn + 2] - 'a' + 10; |
2770 | 7.27M | else if (pszInput[iIn + 2] >= '0' && pszInput[iIn + 2] <= '9') |
2771 | 1.13M | nHexChar += pszInput[iIn + 2] - '0'; |
2772 | 6.13M | else |
2773 | 6.13M | CPLDebug("CPL", |
2774 | 6.13M | "Error unescaping CPLES_URL text, percent not " |
2775 | 6.13M | "followed by two hex digits."); |
2776 | | |
2777 | 8.25M | pszOutput[iOut++] = static_cast<char>(nHexChar); |
2778 | 8.25M | iIn += 2; |
2779 | 8.25M | } |
2780 | 1.13G | else if (pszInput[iIn] == '+') |
2781 | 657k | { |
2782 | 657k | pszOutput[iOut++] = ' '; |
2783 | 657k | } |
2784 | 1.12G | else |
2785 | 1.12G | { |
2786 | 1.12G | pszOutput[iOut++] = pszInput[iIn]; |
2787 | 1.12G | } |
2788 | 1.13G | } |
2789 | 1.77M | } |
2790 | 0 | else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI) |
2791 | 0 | { |
2792 | 0 | char szQuote = nScheme == CPLES_SQL ? '\'' : '\"'; |
2793 | 0 | for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn) |
2794 | 0 | { |
2795 | 0 | if (pszInput[iIn] == szQuote && pszInput[iIn + 1] == szQuote) |
2796 | 0 | { |
2797 | 0 | ++iIn; |
2798 | 0 | pszOutput[iOut++] = pszInput[iIn]; |
2799 | 0 | } |
2800 | 0 | else |
2801 | 0 | { |
2802 | 0 | pszOutput[iOut++] = pszInput[iIn]; |
2803 | 0 | } |
2804 | 0 | } |
2805 | 0 | } |
2806 | 0 | else if (nScheme == CPLES_CSV) |
2807 | 0 | { |
2808 | 0 | CPLError(CE_Fatal, CPLE_NotSupported, |
2809 | 0 | "CSV Unescaping not yet implemented."); |
2810 | 0 | } |
2811 | 0 | else |
2812 | 0 | { |
2813 | 0 | CPLError(CE_Fatal, CPLE_NotSupported, "Unknown escaping style."); |
2814 | 0 | } |
2815 | | |
2816 | 3.97M | pszOutput[iOut] = '\0'; |
2817 | | |
2818 | 3.97M | if (pnLength != nullptr) |
2819 | 838k | *pnLength = iOut; |
2820 | | |
2821 | 3.97M | return pszOutput; |
2822 | 3.97M | } |
2823 | | |
2824 | | /************************************************************************/ |
2825 | | /* CPLBinaryToHex() */ |
2826 | | /************************************************************************/ |
2827 | | |
2828 | | /** |
2829 | | * Binary to hexadecimal translation. |
2830 | | * |
2831 | | * @param nBytes number of bytes of binary data in pabyData. |
2832 | | * @param pabyData array of data bytes to translate. |
2833 | | * |
2834 | | * @return hexadecimal translation, zero terminated. Free with CPLFree(). |
2835 | | */ |
2836 | | |
2837 | | char *CPLBinaryToHex(int nBytes, const GByte *pabyData) |
2838 | | |
2839 | 29.0k | { |
2840 | 29.0k | CPLAssert(nBytes >= 0); |
2841 | 29.0k | char *pszHex = static_cast<char *>( |
2842 | 29.0k | VSI_MALLOC_VERBOSE(static_cast<size_t>(nBytes) * 2 + 1)); |
2843 | 29.0k | if (!pszHex) |
2844 | 0 | { |
2845 | 0 | pszHex = CPLStrdup(""); |
2846 | 0 | return pszHex; |
2847 | 0 | } |
2848 | 29.0k | pszHex[nBytes * 2] = '\0'; |
2849 | | |
2850 | 29.0k | constexpr char achHex[] = "0123456789ABCDEF"; |
2851 | | |
2852 | 241k | for (size_t i = 0; i < static_cast<size_t>(nBytes); ++i) |
2853 | 212k | { |
2854 | 212k | const int nLow = pabyData[i] & 0x0f; |
2855 | 212k | const int nHigh = (pabyData[i] & 0xf0) >> 4; |
2856 | | |
2857 | 212k | pszHex[i * 2] = achHex[nHigh]; |
2858 | 212k | pszHex[i * 2 + 1] = achHex[nLow]; |
2859 | 212k | } |
2860 | | |
2861 | 29.0k | return pszHex; |
2862 | 29.0k | } |
2863 | | |
2864 | | /************************************************************************/ |
2865 | | /* CPLHexToBinary() */ |
2866 | | /************************************************************************/ |
2867 | | |
2868 | | constexpr unsigned char hex2char[256] = { |
2869 | | // Not Hex characters. |
2870 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2871 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2872 | | // 0-9 |
2873 | | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, |
2874 | | // A-F |
2875 | | 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2876 | | // Not Hex characters. |
2877 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2878 | | // a-f |
2879 | | 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2880 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2881 | | // Not Hex characters (upper 128 characters). |
2882 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2883 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2884 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2885 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2886 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
2887 | | 0, 0, 0}; |
2888 | | |
2889 | | /** |
2890 | | * Hexadecimal to binary translation |
2891 | | * |
2892 | | * @param pszHex the input hex encoded string. |
2893 | | * @param pnBytes the returned count of decoded bytes placed here. |
2894 | | * |
2895 | | * @return returns binary buffer of data - free with CPLFree(). |
2896 | | */ |
2897 | | |
2898 | | GByte *CPLHexToBinary(const char *pszHex, int *pnBytes) |
2899 | 2.89M | { |
2900 | 2.89M | const GByte *pabyHex = reinterpret_cast<const GByte *>(pszHex); |
2901 | 2.89M | const size_t nHexLen = strlen(pszHex); |
2902 | | |
2903 | 2.89M | GByte *pabyWKB = static_cast<GByte *>(CPLMalloc(nHexLen / 2 + 2)); |
2904 | | |
2905 | 77.0M | for (size_t i = 0; i < nHexLen / 2; ++i) |
2906 | 74.1M | { |
2907 | 74.1M | const unsigned char h1 = hex2char[pabyHex[2 * i]]; |
2908 | 74.1M | const unsigned char h2 = hex2char[pabyHex[2 * i + 1]]; |
2909 | | |
2910 | | // First character is high bits, second is low bits. |
2911 | 74.1M | pabyWKB[i] = static_cast<GByte>((h1 << 4) | h2); |
2912 | 74.1M | } |
2913 | 2.89M | pabyWKB[nHexLen / 2] = 0; |
2914 | 2.89M | *pnBytes = static_cast<int>(nHexLen / 2); |
2915 | | |
2916 | 2.89M | return pabyWKB; |
2917 | 2.89M | } |
2918 | | |
2919 | | /************************************************************************/ |
2920 | | /* CPLGetValueType() */ |
2921 | | /************************************************************************/ |
2922 | | |
2923 | | /** |
2924 | | * Detect the type of the value contained in a string, whether it is |
2925 | | * a real, an integer or a string |
2926 | | * Leading and trailing spaces are skipped in the analysis. |
2927 | | * |
2928 | | * Note: in the context of this function, integer must be understood in a |
2929 | | * broad sense. It does not mean that the value can fit into a 32 bit integer |
2930 | | * for example. It might be larger. |
2931 | | * |
2932 | | * @param pszValue the string to analyze |
2933 | | * |
2934 | | * @return returns the type of the value contained in the string. |
2935 | | */ |
2936 | | |
2937 | | CPLValueType CPLGetValueType(const char *pszValue) |
2938 | 19.8M | { |
2939 | | // Doubles : "+25.e+3", "-25.e-3", "25.e3", "25e3", " 25e3 " |
2940 | | // Not doubles: "25e 3", "25e.3", "-2-5e3", "2-5e3", "25.25.3", "-3d", "d1" |
2941 | | // "XXeYYYYYYYYYYYYYYYYYYY" that evaluates to infinity |
2942 | | |
2943 | 19.8M | if (pszValue == nullptr) |
2944 | 0 | return CPL_VALUE_STRING; |
2945 | | |
2946 | 19.8M | const char *pszValueInit = pszValue; |
2947 | | |
2948 | | // Skip leading spaces. |
2949 | 19.9M | while (isspace(static_cast<unsigned char>(*pszValue))) |
2950 | 163k | ++pszValue; |
2951 | | |
2952 | 19.8M | if (*pszValue == '\0') |
2953 | 12.0M | return CPL_VALUE_STRING; |
2954 | | |
2955 | | // Skip leading + or -. |
2956 | 7.76M | if (*pszValue == '+' || *pszValue == '-') |
2957 | 1.01M | ++pszValue; |
2958 | | |
2959 | 7.76M | constexpr char DIGIT_ZERO = '0'; |
2960 | 7.76M | if (pszValue[0] == DIGIT_ZERO && pszValue[1] != '\0' && pszValue[1] != '.') |
2961 | 228k | return CPL_VALUE_STRING; |
2962 | | |
2963 | 7.53M | bool bFoundDot = false; |
2964 | 7.53M | bool bFoundExponent = false; |
2965 | 7.53M | bool bIsLastCharExponent = false; |
2966 | 7.53M | bool bIsReal = false; |
2967 | 7.53M | const char *pszAfterExponent = nullptr; |
2968 | 7.53M | bool bFoundMantissa = false; |
2969 | | |
2970 | 67.6M | for (; *pszValue != '\0'; ++pszValue) |
2971 | 63.3M | { |
2972 | 63.3M | if (isdigit(static_cast<unsigned char>(*pszValue))) |
2973 | 57.6M | { |
2974 | 57.6M | bIsLastCharExponent = false; |
2975 | 57.6M | bFoundMantissa = true; |
2976 | 57.6M | } |
2977 | 5.70M | else if (isspace(static_cast<unsigned char>(*pszValue))) |
2978 | 96.2k | { |
2979 | 96.2k | const char *pszTmp = pszValue; |
2980 | 10.1M | while (isspace(static_cast<unsigned char>(*pszTmp))) |
2981 | 10.0M | ++pszTmp; |
2982 | 96.2k | if (*pszTmp == 0) |
2983 | 43.8k | break; |
2984 | 52.4k | else |
2985 | 52.4k | return CPL_VALUE_STRING; |
2986 | 96.2k | } |
2987 | 5.60M | else if (*pszValue == '-' || *pszValue == '+') |
2988 | 131k | { |
2989 | 131k | if (bIsLastCharExponent) |
2990 | 79.9k | { |
2991 | | // Do nothing. |
2992 | 79.9k | } |
2993 | 51.4k | else |
2994 | 51.4k | { |
2995 | 51.4k | return CPL_VALUE_STRING; |
2996 | 51.4k | } |
2997 | 79.9k | bIsLastCharExponent = false; |
2998 | 79.9k | } |
2999 | 5.47M | else if (*pszValue == '.') |
3000 | 2.08M | { |
3001 | 2.08M | bIsReal = true; |
3002 | 2.08M | if (!bFoundDot && !bIsLastCharExponent) |
3003 | 2.01M | bFoundDot = true; |
3004 | 65.2k | else |
3005 | 65.2k | return CPL_VALUE_STRING; |
3006 | 2.01M | bIsLastCharExponent = false; |
3007 | 2.01M | } |
3008 | 3.39M | else if (*pszValue == 'D' || *pszValue == 'd' || *pszValue == 'E' || |
3009 | 2.95M | *pszValue == 'e') |
3010 | 504k | { |
3011 | 504k | if (!bFoundMantissa) |
3012 | 162k | return CPL_VALUE_STRING; |
3013 | 342k | if (!(pszValue[1] == '+' || pszValue[1] == '-' || |
3014 | 258k | isdigit(static_cast<unsigned char>(pszValue[1])))) |
3015 | 37.1k | return CPL_VALUE_STRING; |
3016 | | |
3017 | 305k | bIsReal = true; |
3018 | 305k | if (!bFoundExponent) |
3019 | 288k | bFoundExponent = true; |
3020 | 17.1k | else |
3021 | 17.1k | return CPL_VALUE_STRING; |
3022 | 288k | pszAfterExponent = pszValue + 1; |
3023 | 288k | bIsLastCharExponent = true; |
3024 | 288k | } |
3025 | 2.88M | else |
3026 | 2.88M | { |
3027 | 2.88M | return CPL_VALUE_STRING; |
3028 | 2.88M | } |
3029 | 63.3M | } |
3030 | | |
3031 | 4.26M | if (bIsReal && pszAfterExponent && strlen(pszAfterExponent) > 3) |
3032 | 69.3k | { |
3033 | | // cppcheck-suppress unreadVariable |
3034 | 69.3k | const double dfVal = CPLAtof(pszValueInit); |
3035 | 69.3k | if (std::isinf(dfVal)) |
3036 | 20.5k | return CPL_VALUE_STRING; |
3037 | 69.3k | } |
3038 | | |
3039 | 4.24M | return bIsReal ? CPL_VALUE_REAL : CPL_VALUE_INTEGER; |
3040 | 4.26M | } |
3041 | | |
3042 | | /************************************************************************/ |
3043 | | /* CPLStrlcpy() */ |
3044 | | /************************************************************************/ |
3045 | | |
3046 | | /** |
3047 | | * Copy source string to a destination buffer. |
3048 | | * |
3049 | | * This function ensures that the destination buffer is always NUL terminated |
3050 | | * (provided that its length is at least 1). |
3051 | | * |
3052 | | * This function is designed to be a safer, more consistent, and less error |
3053 | | * prone replacement for strncpy. Its contract is identical to libbsd's strlcpy. |
3054 | | * |
3055 | | * Truncation can be detected by testing if the return value of CPLStrlcpy |
3056 | | * is greater or equal to nDestSize. |
3057 | | |
3058 | | \verbatim |
3059 | | char szDest[5] = {}; |
3060 | | if( CPLStrlcpy(szDest, "abcde", sizeof(szDest)) >= sizeof(szDest) ) |
3061 | | fprintf(stderr, "truncation occurred !\n"); |
3062 | | \endverbatim |
3063 | | |
3064 | | * @param pszDest destination buffer |
3065 | | * @param pszSrc source string. Must be NUL terminated |
3066 | | * @param nDestSize size of destination buffer (including space for the NUL |
3067 | | * terminator character) |
3068 | | * |
3069 | | * @return the length of the source string (=strlen(pszSrc)) |
3070 | | * |
3071 | | */ |
3072 | | size_t CPLStrlcpy(char *pszDest, const char *pszSrc, size_t nDestSize) |
3073 | 3.49M | { |
3074 | 3.49M | if (nDestSize == 0) |
3075 | 0 | return strlen(pszSrc); |
3076 | | |
3077 | 3.49M | char *pszDestIter = pszDest; |
3078 | 3.49M | const char *pszSrcIter = pszSrc; |
3079 | | |
3080 | 3.49M | --nDestSize; |
3081 | 35.3M | while (nDestSize != 0 && *pszSrcIter != '\0') |
3082 | 31.8M | { |
3083 | 31.8M | *pszDestIter = *pszSrcIter; |
3084 | 31.8M | ++pszDestIter; |
3085 | 31.8M | ++pszSrcIter; |
3086 | 31.8M | --nDestSize; |
3087 | 31.8M | } |
3088 | 3.49M | *pszDestIter = '\0'; |
3089 | 3.49M | return pszSrcIter - pszSrc + strlen(pszSrcIter); |
3090 | 3.49M | } |
3091 | | |
3092 | | /************************************************************************/ |
3093 | | /* CPLStrlcat() */ |
3094 | | /************************************************************************/ |
3095 | | |
3096 | | /** |
3097 | | * Appends a source string to a destination buffer. |
3098 | | * |
3099 | | * This function ensures that the destination buffer is always NUL terminated |
3100 | | * (provided that its length is at least 1 and that there is at least one byte |
3101 | | * free in pszDest, that is to say strlen(pszDest_before) < nDestSize) |
3102 | | * |
3103 | | * This function is designed to be a safer, more consistent, and less error |
3104 | | * prone replacement for strncat. Its contract is identical to libbsd's strlcat. |
3105 | | * |
3106 | | * Truncation can be detected by testing if the return value of CPLStrlcat |
3107 | | * is greater or equal to nDestSize. |
3108 | | |
3109 | | \verbatim |
3110 | | char szDest[5] = {}; |
3111 | | CPLStrlcpy(szDest, "ab", sizeof(szDest)); |
3112 | | if( CPLStrlcat(szDest, "cde", sizeof(szDest)) >= sizeof(szDest) ) |
3113 | | fprintf(stderr, "truncation occurred !\n"); |
3114 | | \endverbatim |
3115 | | |
3116 | | * @param pszDest destination buffer. Must be NUL terminated before |
3117 | | * running CPLStrlcat |
3118 | | * @param pszSrc source string. Must be NUL terminated |
3119 | | * @param nDestSize size of destination buffer (including space for the |
3120 | | * NUL terminator character) |
3121 | | * |
3122 | | * @return the theoretical length of the destination string after concatenation |
3123 | | * (=strlen(pszDest_before) + strlen(pszSrc)). |
3124 | | * If strlen(pszDest_before) >= nDestSize, then it returns |
3125 | | * nDestSize + strlen(pszSrc) |
3126 | | * |
3127 | | */ |
3128 | | size_t CPLStrlcat(char *pszDest, const char *pszSrc, size_t nDestSize) |
3129 | 2.56k | { |
3130 | 2.56k | char *pszDestIter = pszDest; |
3131 | | |
3132 | 2.56k | while (nDestSize != 0 && *pszDestIter != '\0') |
3133 | 0 | { |
3134 | 0 | ++pszDestIter; |
3135 | 0 | --nDestSize; |
3136 | 0 | } |
3137 | | |
3138 | 2.56k | return pszDestIter - pszDest + CPLStrlcpy(pszDestIter, pszSrc, nDestSize); |
3139 | 2.56k | } |
3140 | | |
3141 | | /************************************************************************/ |
3142 | | /* CPLStrnlen() */ |
3143 | | /************************************************************************/ |
3144 | | |
3145 | | /** |
3146 | | * Returns the length of a NUL terminated string by reading at most |
3147 | | * the specified number of bytes. |
3148 | | * |
3149 | | * The CPLStrnlen() function returns min(strlen(pszStr), nMaxLen). |
3150 | | * Only the first nMaxLen bytes of the string will be read. Useful to |
3151 | | * test if a string contains at least nMaxLen characters without reading |
3152 | | * the full string up to the NUL terminating character. |
3153 | | * |
3154 | | * @param pszStr a NUL terminated string |
3155 | | * @param nMaxLen maximum number of bytes to read in pszStr |
3156 | | * |
3157 | | * @return strlen(pszStr) if the length is lesser than nMaxLen, otherwise |
3158 | | * nMaxLen if the NUL character has not been found in the first nMaxLen bytes. |
3159 | | * |
3160 | | */ |
3161 | | |
3162 | | size_t CPLStrnlen(const char *pszStr, size_t nMaxLen) |
3163 | 34.3M | { |
3164 | 34.3M | size_t nLen = 0; |
3165 | 1.37G | while (nLen < nMaxLen && *pszStr != '\0') |
3166 | 1.33G | { |
3167 | 1.33G | ++nLen; |
3168 | 1.33G | ++pszStr; |
3169 | 1.33G | } |
3170 | 34.3M | return nLen; |
3171 | 34.3M | } |
3172 | | |
3173 | | /************************************************************************/ |
3174 | | /* CSLParseCommandLine() */ |
3175 | | /************************************************************************/ |
3176 | | |
3177 | | /** |
3178 | | * Tokenize command line arguments in a list of strings. |
3179 | | * |
3180 | | * @param pszCommandLine command line |
3181 | | * |
3182 | | * @return NULL terminated list of strings to free with CSLDestroy() |
3183 | | * |
3184 | | */ |
3185 | | char **CSLParseCommandLine(const char *pszCommandLine) |
3186 | 0 | { |
3187 | 0 | return CSLTokenizeString(pszCommandLine); |
3188 | 0 | } |
3189 | | |
3190 | | /************************************************************************/ |
3191 | | /* CPLToupper() */ |
3192 | | /************************************************************************/ |
3193 | | |
3194 | | /** Converts a (ASCII) lowercase character to uppercase. |
3195 | | * |
3196 | | * Same as standard toupper(), except that it is not locale sensitive. |
3197 | | * |
3198 | | * @since GDAL 3.9 |
3199 | | */ |
3200 | | int CPLToupper(int c) |
3201 | 156M | { |
3202 | 156M | return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c; |
3203 | 156M | } |
3204 | | |
3205 | | /************************************************************************/ |
3206 | | /* CPLTolower() */ |
3207 | | /************************************************************************/ |
3208 | | |
3209 | | /** Converts a (ASCII) uppercase character to lowercase. |
3210 | | * |
3211 | | * Same as standard tolower(), except that it is not locale sensitive. |
3212 | | * |
3213 | | * @since GDAL 3.9 |
3214 | | */ |
3215 | | int CPLTolower(int c) |
3216 | 445M | { |
3217 | 445M | return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; |
3218 | 445M | } |
3219 | | |
3220 | | /************************************************************************/ |
3221 | | /* CPLRemoveSQLComments() */ |
3222 | | /************************************************************************/ |
3223 | | |
3224 | | /** Remove SQL comments from a string |
3225 | | * |
3226 | | * @param osInput Input string. |
3227 | | * @since GDAL 3.11 |
3228 | | */ |
3229 | | std::string CPLRemoveSQLComments(const std::string &osInput) |
3230 | 57 | { |
3231 | 57 | const CPLStringList aosLines( |
3232 | 57 | CSLTokenizeStringComplex(osInput.c_str(), "\r\n", FALSE, FALSE)); |
3233 | 57 | std::string osSQL; |
3234 | 57 | for (const char *pszLine : aosLines) |
3235 | 0 | { |
3236 | 0 | char chQuote = 0; |
3237 | 0 | int i = 0; |
3238 | 0 | for (; pszLine[i] != '\0'; ++i) |
3239 | 0 | { |
3240 | 0 | if (chQuote) |
3241 | 0 | { |
3242 | 0 | if (pszLine[i] == chQuote) |
3243 | 0 | { |
3244 | | // Deal with escaped quote character which is repeated, |
3245 | | // so 'foo''bar' or "foo""bar" |
3246 | 0 | if (pszLine[i + 1] == chQuote) |
3247 | 0 | { |
3248 | 0 | i++; |
3249 | 0 | } |
3250 | 0 | else |
3251 | 0 | { |
3252 | 0 | chQuote = 0; |
3253 | 0 | } |
3254 | 0 | } |
3255 | 0 | } |
3256 | 0 | else if (pszLine[i] == '\'' || pszLine[i] == '"') |
3257 | 0 | { |
3258 | 0 | chQuote = pszLine[i]; |
3259 | 0 | } |
3260 | 0 | else if (pszLine[i] == '-' && pszLine[i + 1] == '-') |
3261 | 0 | { |
3262 | 0 | break; |
3263 | 0 | } |
3264 | 0 | } |
3265 | 0 | if (i > 0) |
3266 | 0 | { |
3267 | 0 | if (!osSQL.empty()) |
3268 | 0 | osSQL += ' '; |
3269 | 0 | osSQL.append(pszLine, i); |
3270 | 0 | } |
3271 | 0 | } |
3272 | 57 | return osSQL; |
3273 | 57 | } |
3274 | | |
3275 | | namespace cpl |
3276 | | { |
3277 | | |
3278 | | static bool CaseInsensitiveCompare(unsigned char c1, unsigned char c2) |
3279 | 7.08G | { |
3280 | 7.08G | return toupper(c1) == toupper(c2); |
3281 | 7.08G | } |
3282 | | |
3283 | | /** Check whether the start of one string is equivalent to another string, |
3284 | | * considering case. |
3285 | | * |
3286 | | * @param str string to test |
3287 | | * @param prefix expected prefix |
3288 | | * @return true if the string starts with the prefix |
3289 | | * |
3290 | | * @since GDAL 3.11 |
3291 | | */ |
3292 | | bool starts_with(std::string_view str, std::string_view prefix) |
3293 | 31.8M | { |
3294 | 31.8M | return str.size() >= prefix.size() && |
3295 | 17.9M | str.compare(0, prefix.size(), prefix) == 0; |
3296 | 31.8M | } |
3297 | | |
3298 | | /** Check whether the start of one string is equivalent to another string, |
3299 | | * not considering case. |
3300 | | * |
3301 | | * @param str string to test |
3302 | | * @param prefix expected prefix |
3303 | | * @return true if the string starts with the prefix |
3304 | | * |
3305 | | * @since GDAL 3.14 |
3306 | | */ |
3307 | | bool starts_with_ci(std::string_view str, std::string_view prefix) |
3308 | 128M | { |
3309 | 128M | return str.size() >= prefix.size() && |
3310 | 100M | std::search(str.begin(), str.end(), prefix.begin(), prefix.end(), |
3311 | 100M | CaseInsensitiveCompare) != str.end(); |
3312 | 128M | } |
3313 | | |
3314 | | /** Check whether the end of one string is equivalent to another string, |
3315 | | * considering case. |
3316 | | * |
3317 | | * @param str string to test |
3318 | | * @param suffix expected suffix |
3319 | | * @return true if the string ends with the suffix |
3320 | | * |
3321 | | * @since GDAL 3.11 |
3322 | | */ |
3323 | | bool ends_with(std::string_view str, std::string_view suffix) |
3324 | 1.98M | { |
3325 | 1.98M | return str.size() >= suffix.size() && |
3326 | 1.92M | (suffix.empty() || str.compare(str.size() - suffix.size(), |
3327 | 1.92M | suffix.size(), suffix) == 0); |
3328 | 1.98M | } |
3329 | | |
3330 | | /** Check whether the end of one string is equivalent to another string, |
3331 | | * not considering case. |
3332 | | * |
3333 | | * @param str string to test |
3334 | | * @param suffix expected suffix |
3335 | | * @return true if the string ends with the suffix |
3336 | | * |
3337 | | * @since GDAL 3.14 |
3338 | | */ |
3339 | | bool ends_with_ci(std::string_view str, std::string_view suffix) |
3340 | 0 | { |
3341 | 0 | return str.size() >= suffix.size() && |
3342 | 0 | (suffix.empty() || |
3343 | 0 | std::search(str.end() - suffix.size(), str.end(), suffix.begin(), |
3344 | 0 | suffix.end(), CaseInsensitiveCompare) != str.end()); |
3345 | 0 | } |
3346 | | |
3347 | | /** Check whether two strings are equal, considering case. |
3348 | | * |
3349 | | * @param str1 first string to test |
3350 | | * @param str2 second string to test |
3351 | | * @return true if the strings are considered equal |
3352 | | * |
3353 | | * @since GDAL 3.14 |
3354 | | */ |
3355 | | bool equals(std::string_view str1, std::string_view str2) |
3356 | 0 | { |
3357 | 0 | return str1 == str2; |
3358 | 0 | } |
3359 | | |
3360 | | /** Check whether two strings are equal, not considering case. |
3361 | | * |
3362 | | * @param str1 first string to test |
3363 | | * @param str2 second string to test |
3364 | | * @return true if the strings are considered equal |
3365 | | * |
3366 | | * @since GDAL 3.14 |
3367 | | */ |
3368 | | bool equals_ci(std::string_view str1, std::string_view str2) |
3369 | 0 | { |
3370 | 0 | return str1.size() == str2.size() && |
3371 | 0 | std::equal(str1.begin(), str1.end(), str2.begin(), |
3372 | 0 | CaseInsensitiveCompare); |
3373 | 0 | } |
3374 | | |
3375 | | /** Remove leading and trailing whitespace from a string. |
3376 | | * The returned string view will be a reference into the input. |
3377 | | * |
3378 | | * @param str string to trim |
3379 | | * @return trimmed string |
3380 | | * |
3381 | | * @since GDAL 3.14 |
3382 | | */ |
3383 | | std::string_view trim(std::string_view str) |
3384 | 72.5M | { |
3385 | 72.5M | if (str.empty()) |
3386 | 4.07M | { |
3387 | 4.07M | return str; |
3388 | 4.07M | } |
3389 | | |
3390 | 68.5M | size_t start = 0; |
3391 | 94.6M | while (start < str.size() && |
3392 | 93.9M | isspace(static_cast<unsigned char>(str[start]))) |
3393 | 26.1M | { |
3394 | 26.1M | start++; |
3395 | 26.1M | } |
3396 | | |
3397 | 68.5M | if (start == str.size()) |
3398 | 783k | { |
3399 | 783k | return str.substr(start, 0); |
3400 | 783k | } |
3401 | | |
3402 | 67.7M | size_t stop = str.size(); |
3403 | 76.1M | while (stop > start && isspace(static_cast<unsigned char>(str[stop - 1]))) |
3404 | 8.45M | { |
3405 | 8.45M | stop--; |
3406 | 8.45M | } |
3407 | | |
3408 | 67.7M | return str.substr(start, stop - start); |
3409 | 68.5M | } |
3410 | | |
3411 | | std::string_view trim(const char *pszStr) |
3412 | 0 | { |
3413 | 0 | return trim(std::string_view(pszStr)); |
3414 | 0 | } |
3415 | | |
3416 | | /** Remove leading whitespace from a string. |
3417 | | * The returned string view will be a reference into the input. |
3418 | | * |
3419 | | * @param str string to trim |
3420 | | * @return trimmed string |
3421 | | * |
3422 | | * @since GDAL 3.14 |
3423 | | */ |
3424 | | std::string_view ltrim(std::string_view str) |
3425 | 5.82M | { |
3426 | 5.82M | if (str.empty()) |
3427 | 871k | { |
3428 | 871k | return str; |
3429 | 871k | } |
3430 | | |
3431 | 4.95M | size_t start = 0; |
3432 | 5.23M | while (start < str.size() && |
3433 | 5.22M | isspace(static_cast<unsigned char>(str[start]))) |
3434 | 275k | { |
3435 | 275k | start++; |
3436 | 275k | } |
3437 | | |
3438 | 4.95M | return str.substr(start); |
3439 | 5.82M | } |
3440 | | |
3441 | | std::string_view ltrim(const char *pszStr) |
3442 | 0 | { |
3443 | 0 | return ltrim(std::string_view(pszStr)); |
3444 | 0 | } |
3445 | | |
3446 | | /** Remove trailing whitespace from a string. |
3447 | | * The returned string view will be a reference into the input. |
3448 | | * |
3449 | | * @param str string to trim |
3450 | | * @return trimmed string |
3451 | | * |
3452 | | * @since GDAL 3.14 |
3453 | | */ |
3454 | | std::string_view rtrim(std::string_view str) |
3455 | 5.82M | { |
3456 | 5.82M | if (str.empty()) |
3457 | 876k | { |
3458 | 876k | return str; |
3459 | 876k | } |
3460 | | |
3461 | 4.95M | size_t stop = str.size(); |
3462 | 5.15M | while (stop > 0 && isspace(static_cast<unsigned char>(str[stop - 1]))) |
3463 | 202k | { |
3464 | 202k | stop--; |
3465 | 202k | } |
3466 | | |
3467 | 4.95M | return str.substr(0, stop); |
3468 | 5.82M | } |
3469 | | |
3470 | | std::string_view rtrim(const char *pszStr) |
3471 | 0 | { |
3472 | 0 | return rtrim(std::string_view(pszStr)); |
3473 | 0 | } |
3474 | | |
3475 | | } // namespace cpl |