Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: CPLString implementation.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "cpl_string.h"
16 :
17 : #include <cctype>
18 : #include <cstdarg>
19 : #include <cstddef>
20 : #include <cstring>
21 : #include <string>
22 :
23 : #include "cpl_config.h"
24 : #include "cpl_conv.h"
25 :
26 : #if !defined(va_copy) && defined(__va_copy)
27 : #define va_copy __va_copy
28 : #endif
29 :
30 : /*
31 : * The CPLString class is derived from std::string, so the vast majority
32 : * of the implementation comes from that. This module is just the extensions
33 : * we add.
34 : */
35 :
36 : /************************************************************************/
37 : /* Printf() */
38 : /************************************************************************/
39 :
40 : /** Assign the content of the string using sprintf() */
41 508258 : CPLString &CPLString::Printf(CPL_FORMAT_STRING(const char *pszFormat), ...)
42 :
43 : {
44 : va_list args;
45 :
46 508258 : va_start(args, pszFormat);
47 508258 : vPrintf(pszFormat, args);
48 508164 : va_end(args);
49 :
50 508164 : return *this;
51 : }
52 :
53 : /************************************************************************/
54 : /* vPrintf() */
55 : /************************************************************************/
56 :
57 : /** Assign the content of the string using vsprintf() */
58 624549 : CPLString &CPLString::vPrintf(CPL_FORMAT_STRING(const char *pszFormat),
59 : va_list args)
60 :
61 : {
62 : /* -------------------------------------------------------------------- */
63 : /* This implementation for platforms without vsnprintf() will */
64 : /* just plain fail if the formatted contents are too large. */
65 : /* -------------------------------------------------------------------- */
66 :
67 : #if !defined(HAVE_VSNPRINTF)
68 : char *pszBuffer = static_cast<char *>(CPLMalloc(30000));
69 : if (CPLvsnprintf(pszBuffer, 30000, pszFormat, args) > 29998)
70 : {
71 : CPLError(CE_Fatal, CPLE_AppDefined,
72 : "CPLString::vPrintf() ... buffer overrun.");
73 : }
74 : *this = pszBuffer;
75 : CPLFree(pszBuffer);
76 :
77 : /* -------------------------------------------------------------------- */
78 : /* This should grow a big enough buffer to hold any formatted */
79 : /* result. */
80 : /* -------------------------------------------------------------------- */
81 : #else
82 : va_list wrk_args;
83 :
84 : #ifdef va_copy
85 624549 : va_copy(wrk_args, args);
86 : #else
87 : wrk_args = args;
88 : #endif
89 :
90 624549 : char szModestBuffer[500] = {};
91 624549 : szModestBuffer[0] = '\0';
92 624549 : int nPR = CPLvsnprintf(szModestBuffer, sizeof(szModestBuffer), pszFormat,
93 : wrk_args);
94 624488 : if (nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer)) - 1)
95 : {
96 1251 : int nWorkBufferSize = 2000;
97 1251 : char *pszWorkBuffer = static_cast<char *>(CPLMalloc(nWorkBufferSize));
98 :
99 : #ifdef va_copy
100 1215 : va_end(wrk_args);
101 1215 : va_copy(wrk_args, args);
102 : #else
103 : wrk_args = args;
104 : #endif
105 1404 : while ((nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
106 1404 : wrk_args)) >= nWorkBufferSize - 1 ||
107 : nPR == -1)
108 : {
109 189 : nWorkBufferSize *= 4;
110 : pszWorkBuffer =
111 189 : static_cast<char *>(CPLRealloc(pszWorkBuffer, nWorkBufferSize));
112 : #ifdef va_copy
113 189 : va_end(wrk_args);
114 189 : va_copy(wrk_args, args);
115 : #else
116 : wrk_args = args;
117 : #endif
118 : }
119 1215 : *this = pszWorkBuffer;
120 1215 : CPLFree(pszWorkBuffer);
121 : }
122 : else
123 : {
124 623237 : *this = szModestBuffer;
125 : }
126 : #ifdef va_copy
127 624477 : va_end(wrk_args);
128 : #endif
129 :
130 : #endif /* !defined(HAVE_VSNPRINTF) */
131 :
132 624477 : return *this;
133 : }
134 :
135 : /************************************************************************/
136 : /* FormatC() */
137 : /************************************************************************/
138 :
139 : /**
140 : * Format double in C locale.
141 : *
142 : * The passed value is formatted using the C locale (period as decimal
143 : * separator) and appended to the target CPLString.
144 : *
145 : * @param dfValue the value to format.
146 : * @param pszFormat the sprintf() style format to use or omit for default.
147 : * Note that this format string should only include one substitution argument
148 : * and it must be for a double (%f or %g).
149 : *
150 : * @return a reference to the CPLString.
151 : */
152 :
153 3546 : CPLString &CPLString::FormatC(double dfValue, const char *pszFormat)
154 :
155 : {
156 3546 : if (pszFormat == nullptr)
157 3528 : pszFormat = "%g";
158 :
159 : // presumably long enough for any number.
160 3546 : const size_t buf_size = 512;
161 3546 : char szWork[buf_size] = {};
162 :
163 3546 : CPLsnprintf(szWork, buf_size, pszFormat, dfValue);
164 :
165 3546 : *this += szWork;
166 :
167 3546 : return *this;
168 : }
169 :
170 : /************************************************************************/
171 : /* Trim() */
172 : /************************************************************************/
173 :
174 : /**
175 : * Trim white space.
176 : *
177 : * Trims white space off the let and right of the string. White space
178 : * is any of a space, a tab, a newline ('\\n') or a carriage control ('\\r').
179 : *
180 : * @return a reference to the CPLString.
181 : */
182 :
183 818144 : CPLString &CPLString::Trim()
184 :
185 : {
186 818144 : constexpr char szWhitespace[] = " \t\r\n";
187 :
188 818144 : const size_t iLeft = find_first_not_of(szWhitespace);
189 818144 : const size_t iRight = find_last_not_of(szWhitespace);
190 :
191 818144 : if (iLeft == std::string::npos)
192 : {
193 87722 : erase();
194 87722 : return *this;
195 : }
196 :
197 730422 : assign(substr(iLeft, iRight - iLeft + 1));
198 :
199 730422 : return *this;
200 : }
201 :
202 : /************************************************************************/
203 : /* Recode() */
204 : /************************************************************************/
205 :
206 : /** Recode the string */
207 441680 : CPLString &CPLString::Recode(const char *pszSrcEncoding,
208 : const char *pszDstEncoding)
209 :
210 : {
211 441680 : if (pszSrcEncoding == nullptr)
212 0 : pszSrcEncoding = CPL_ENC_UTF8;
213 441680 : if (pszDstEncoding == nullptr)
214 0 : pszDstEncoding = CPL_ENC_UTF8;
215 :
216 441680 : if (strcmp(pszSrcEncoding, pszDstEncoding) == 0)
217 40 : return *this;
218 :
219 441640 : char *pszRecoded = CPLRecode(c_str(), pszSrcEncoding, pszDstEncoding);
220 :
221 441640 : if (pszRecoded == nullptr)
222 0 : return *this;
223 :
224 441640 : assign(pszRecoded);
225 441640 : CPLFree(pszRecoded);
226 :
227 441640 : return *this;
228 : }
229 :
230 : /************************************************************************/
231 : /* ifind() */
232 : /************************************************************************/
233 :
234 : /**
235 : * Case insensitive find() alternative.
236 : *
237 : * @param str substring to find.
238 : * @param pos offset in the string at which the search starts.
239 : * @return the position of substring in the string or std::string::npos if not
240 : * found.
241 : * @since GDAL 1.9.0
242 : */
243 :
244 7313 : size_t CPLString::ifind(const std::string &str, size_t pos) const
245 :
246 : {
247 7313 : return ifind(str.c_str(), pos);
248 : }
249 :
250 : /**
251 : * Case insensitive find() alternative.
252 : *
253 : * @param s substring to find.
254 : * @param nPos offset in the string at which the search starts.
255 : * @return the position of the substring in the string or std::string::npos if
256 : * not found.
257 : * @since GDAL 1.9.0
258 : */
259 :
260 231738 : size_t CPLString::ifind(const char *s, size_t nPos) const
261 :
262 : {
263 231738 : const char *pszHaystack = c_str();
264 : const char chFirst =
265 231726 : static_cast<char>(CPLTolower(static_cast<unsigned char>(s[0])));
266 231723 : const size_t nTargetLen = strlen(s);
267 :
268 231723 : if (nPos > size())
269 0 : nPos = size();
270 :
271 231720 : pszHaystack += nPos;
272 :
273 8285390 : while (*pszHaystack != '\0')
274 : {
275 8062560 : if (chFirst == CPLTolower(static_cast<unsigned char>(*pszHaystack)))
276 : {
277 357555 : if (EQUALN(pszHaystack, s, nTargetLen))
278 8891 : return nPos;
279 : }
280 :
281 8053670 : nPos++;
282 8053670 : pszHaystack++;
283 : }
284 :
285 222828 : return std::string::npos;
286 : }
287 :
288 : /************************************************************************/
289 : /* toupper() */
290 : /************************************************************************/
291 :
292 : /**
293 : * Convert to upper case in place.
294 : */
295 :
296 3461380 : CPLString &CPLString::toupper()
297 :
298 : {
299 29273900 : for (size_t i = 0; i < size(); i++)
300 25812500 : (*this)[i] = static_cast<char>(CPLToupper((*this)[i]));
301 :
302 3461380 : return *this;
303 : }
304 :
305 : /************************************************************************/
306 : /* tolower() */
307 : /************************************************************************/
308 :
309 : /**
310 : * Convert to lower case in place.
311 : */
312 :
313 104764 : CPLString &CPLString::tolower()
314 :
315 : {
316 1711940 : for (size_t i = 0; i < size(); i++)
317 1607170 : (*this)[i] = static_cast<char>(CPLTolower((*this)[i]));
318 :
319 104765 : return *this;
320 : }
321 :
322 : /************************************************************************/
323 : /* replaceAll() */
324 : /************************************************************************/
325 :
326 : /**
327 : * Replace all occurrences of osBefore with osAfter.
328 : */
329 1164650 : CPLString &CPLString::replaceAll(const std::string &osBefore,
330 : const std::string &osAfter)
331 : {
332 1164650 : const size_t nBeforeSize = osBefore.size();
333 1164630 : const size_t nAfterSize = osAfter.size();
334 1164620 : if (nBeforeSize)
335 : {
336 1164610 : size_t nStartPos = 0;
337 1258510 : while ((nStartPos = find(osBefore, nStartPos)) != std::string::npos)
338 : {
339 93913 : replace(nStartPos, nBeforeSize, osAfter);
340 93900 : nStartPos += nAfterSize;
341 : }
342 : }
343 1164640 : return *this;
344 : }
345 :
346 : /**
347 : * Replace all occurrences of chBefore with osAfter.
348 : */
349 4352 : CPLString &CPLString::replaceAll(char chBefore, const std::string &osAfter)
350 : {
351 4352 : return replaceAll(std::string(&chBefore, 1), osAfter);
352 : }
353 :
354 : /**
355 : * Replace all occurrences of osBefore with chAfter.
356 : */
357 1111020 : CPLString &CPLString::replaceAll(const std::string &osBefore, char chAfter)
358 : {
359 1111020 : return replaceAll(osBefore, std::string(&chAfter, 1));
360 : }
361 :
362 : /**
363 : * Replace all occurrences of chBefore with chAfter.
364 : */
365 7025 : CPLString &CPLString::replaceAll(char chBefore, char chAfter)
366 : {
367 7025 : return replaceAll(std::string(&chBefore, 1), std::string(&chAfter, 1));
368 : }
369 :
370 : /************************************************************************/
371 : /* endsWith() */
372 : /************************************************************************/
373 :
374 : /**
375 : * Returns whether the string ends with another string
376 : * @param osStr other string.
377 : * @return true if the string ends with osStr.
378 : */
379 20567 : bool CPLString::endsWith(const std::string &osStr) const
380 : {
381 20567 : if (size() < osStr.size())
382 150 : return false;
383 20417 : return substr(size() - osStr.size()) == osStr;
384 : }
385 :
386 : /************************************************************************/
387 : /* URLEncode() */
388 : /************************************************************************/
389 :
390 : /**
391 : * Return a string that *can* be a valid URL.
392 : *
393 : * Said otherwise if URLEncode() != *this was not a valid URL according to
394 : * https://datatracker.ietf.org/doc/html/rfc3986.html.
395 : *
396 : * This replaces all characters that are not reserved (:/?#[]\@!$&'()*+,;=),
397 : * unreserved (a-z, A-Z, 0-9 and -.-~) or already percent-encoded by their
398 : * percent-encoding.
399 : *
400 : * Note that when composing a URL, and typically query-parameters, it might
401 : * be needed to use CPLEscape(,,CPLES_URL) to also substitute reserved
402 : * characters.
403 : *
404 : * @return a URL encoded string
405 : * @since 3.12
406 : */
407 55 : CPLString CPLString::URLEncode() const
408 : {
409 : // Helper to check if a substring is a valid percent-encoding
410 21 : auto isPercentEncoded = [](const char *str) -> bool
411 : {
412 42 : return str[0] == '%' &&
413 42 : std::isxdigit(static_cast<unsigned char>(str[1])) &&
414 42 : std::isxdigit(static_cast<unsigned char>(str[2]));
415 : };
416 :
417 : // Cf https://datatracker.ietf.org/doc/html/rfc3986.html
418 55 : const char *unreserved =
419 : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
420 55 : const char *reserved = ":/?#[]@!$&'()*+,;=";
421 55 : CPLString osEncoded;
422 55 : osEncoded.reserve(size());
423 4498 : for (size_t i = 0; i < size(); ++i)
424 : {
425 4443 : char ch = (*this)[i];
426 : // If already percent-encoded, copy as is
427 4443 : if (ch == '%' && i + 2 < size() && isPercentEncoded(c_str() + i))
428 : {
429 21 : osEncoded += ch;
430 21 : osEncoded += (*this)[i + 1];
431 21 : osEncoded += (*this)[i + 2];
432 21 : i += 2;
433 : }
434 4422 : else if (strchr(unreserved, ch) || strchr(reserved, ch))
435 : {
436 4409 : osEncoded += ch;
437 : }
438 : else
439 : {
440 : char buf[4];
441 13 : snprintf(buf, sizeof(buf), "%%%02X",
442 13 : static_cast<unsigned char>(ch));
443 13 : osEncoded += buf;
444 : }
445 : }
446 110 : return osEncoded;
447 : }
448 :
449 : /************************************************************************/
450 : /* CPLURLGetValue() */
451 : /************************************************************************/
452 :
453 : /**
454 : * Return the value matching a key from a key=value pair in a URL.
455 : *
456 : * @param pszURL the URL.
457 : * @param pszKey the key to find.
458 : * @return the value of empty string if not found.
459 : * @since GDAL 1.9.0
460 : */
461 1656 : CPLString CPLURLGetValue(const char *pszURL, const char *pszKey)
462 : {
463 3312 : CPLString osKey(pszKey);
464 1656 : osKey += "=";
465 1656 : size_t nKeyPos = CPLString(pszURL).ifind(osKey);
466 1656 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
467 329 : (pszURL[nKeyPos - 1] == '?' || pszURL[nKeyPos - 1] == '&'))
468 : {
469 658 : CPLString osValue(pszURL + nKeyPos + osKey.size());
470 329 : const char *pszValue = osValue.c_str();
471 329 : const char *pszSep = strchr(pszValue, '&');
472 329 : if (pszSep)
473 : {
474 95 : osValue.resize(pszSep - pszValue);
475 : }
476 329 : return osValue;
477 : }
478 1327 : return "";
479 : }
480 :
481 : /************************************************************************/
482 : /* CPLURLAddKVP() */
483 : /************************************************************************/
484 :
485 : /**
486 : * Return a new URL with a new key=value pair.
487 : *
488 : * @param pszURL the URL.
489 : * @param pszKey the key to find.
490 : * @param pszValue the value of the key (may be NULL to unset an existing KVP).
491 : * @return the modified URL.
492 : * @since GDAL 1.9.0
493 : */
494 5274 : CPLString CPLURLAddKVP(const char *pszURL, const char *pszKey,
495 : const char *pszValue)
496 : {
497 5274 : CPLString osURL(strchr(pszURL, '?') == nullptr
498 15145 : ? CPLString(pszURL).append("?")
499 10548 : : pszURL);
500 :
501 10548 : CPLString osKey(pszKey);
502 5274 : osKey += "=";
503 5274 : size_t nKeyPos = osURL.ifind(osKey);
504 5353 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
505 79 : (osURL[nKeyPos - 1] == '?' || osURL[nKeyPos - 1] == '&'))
506 : {
507 150 : CPLString osNewURL(osURL);
508 75 : osNewURL.resize(nKeyPos);
509 75 : if (pszValue)
510 : {
511 44 : osNewURL += osKey;
512 44 : osNewURL += pszValue;
513 : }
514 75 : const char *pszNext = strchr(osURL.c_str() + nKeyPos, '&');
515 75 : if (pszNext)
516 : {
517 72 : if (osNewURL.back() == '&' || osNewURL.back() == '?')
518 30 : osNewURL += pszNext + 1;
519 : else
520 42 : osNewURL += pszNext;
521 : }
522 75 : return osNewURL;
523 : }
524 : else
525 : {
526 10398 : CPLString osNewURL(std::move(osURL));
527 5199 : if (pszValue)
528 : {
529 3387 : if (osNewURL.back() != '&' && osNewURL.back() != '?')
530 2477 : osNewURL += '&';
531 3387 : osNewURL += osKey;
532 3387 : osNewURL += pszValue;
533 : }
534 5199 : return osNewURL;
535 : }
536 : }
537 :
538 : /************************************************************************/
539 : /* CPLOPrintf() */
540 : /************************************************************************/
541 :
542 : /** Return a CPLString with the content of sprintf() */
543 17511 : CPLString CPLOPrintf(CPL_FORMAT_STRING(const char *pszFormat), ...)
544 :
545 : {
546 : va_list args;
547 17511 : va_start(args, pszFormat);
548 :
549 17511 : CPLString osTarget;
550 17511 : osTarget.vPrintf(pszFormat, args);
551 :
552 17511 : va_end(args);
553 :
554 35022 : return osTarget;
555 : }
556 :
557 : /************************************************************************/
558 : /* CPLOvPrintf() */
559 : /************************************************************************/
560 :
561 : /** Return a CPLString with the content of vsprintf() */
562 0 : CPLString CPLOvPrintf(CPL_FORMAT_STRING(const char *pszFormat), va_list args)
563 :
564 : {
565 0 : CPLString osTarget;
566 0 : osTarget.vPrintf(pszFormat, args);
567 0 : return osTarget;
568 : }
569 :
570 : /************************************************************************/
571 : /* CPLQuotedSQLIdentifer() */
572 : /************************************************************************/
573 :
574 : /** Return a CPLString of the SQL quoted identifier */
575 6 : CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
576 :
577 : {
578 6 : CPLString osIdent;
579 :
580 6 : if (pszIdent)
581 : {
582 6 : char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
583 6 : osIdent.Printf("\"%s\"", pszQuotedIdent);
584 6 : CPLFree(pszQuotedIdent);
585 : }
586 :
587 6 : return osIdent;
588 : }
|