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