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