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 503119 : CPLString &CPLString::Printf(CPL_FORMAT_STRING(const char *pszFormat), ...)
42 :
43 : {
44 : va_list args;
45 :
46 503119 : va_start(args, pszFormat);
47 503119 : vPrintf(pszFormat, args);
48 503074 : va_end(args);
49 :
50 503074 : return *this;
51 : }
52 :
53 : /************************************************************************/
54 : /* vPrintf() */
55 : /************************************************************************/
56 :
57 : /** Assign the content of the string using vsprintf() */
58 620151 : 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 620151 : va_copy(wrk_args, args);
86 : #else
87 : wrk_args = args;
88 : #endif
89 :
90 620151 : char szModestBuffer[500] = {};
91 620151 : szModestBuffer[0] = '\0';
92 620151 : int nPR = CPLvsnprintf(szModestBuffer, sizeof(szModestBuffer), pszFormat,
93 : wrk_args);
94 620102 : if (nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer)) - 1)
95 : {
96 1254 : int nWorkBufferSize = 2000;
97 1254 : char *pszWorkBuffer = static_cast<char *>(CPLMalloc(nWorkBufferSize));
98 :
99 : #ifdef va_copy
100 1250 : va_end(wrk_args);
101 1250 : va_copy(wrk_args, args);
102 : #else
103 : wrk_args = args;
104 : #endif
105 1457 : while ((nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
106 1457 : wrk_args)) >= nWorkBufferSize - 1 ||
107 : nPR == -1)
108 : {
109 207 : nWorkBufferSize *= 4;
110 : pszWorkBuffer =
111 207 : static_cast<char *>(CPLRealloc(pszWorkBuffer, nWorkBufferSize));
112 : #ifdef va_copy
113 207 : va_end(wrk_args);
114 207 : va_copy(wrk_args, args);
115 : #else
116 : wrk_args = args;
117 : #endif
118 : }
119 1250 : *this = pszWorkBuffer;
120 1250 : CPLFree(pszWorkBuffer);
121 : }
122 : else
123 : {
124 618848 : *this = szModestBuffer;
125 : }
126 : #ifdef va_copy
127 620104 : va_end(wrk_args);
128 : #endif
129 :
130 : #endif /* !defined(HAVE_VSNPRINTF) */
131 :
132 620104 : 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 849359 : CPLString &CPLString::Trim()
184 :
185 : {
186 849359 : constexpr char szWhitespace[] = " \t\r\n";
187 :
188 849359 : const size_t iLeft = find_first_not_of(szWhitespace);
189 849359 : const size_t iRight = find_last_not_of(szWhitespace);
190 :
191 849359 : if (iLeft == std::string::npos)
192 : {
193 92158 : erase();
194 92158 : return *this;
195 : }
196 :
197 757201 : assign(substr(iLeft, iRight - iLeft + 1));
198 :
199 757201 : return *this;
200 : }
201 :
202 : /************************************************************************/
203 : /* Recode() */
204 : /************************************************************************/
205 :
206 : /** Recode the string */
207 467425 : CPLString &CPLString::Recode(const char *pszSrcEncoding,
208 : const char *pszDstEncoding)
209 :
210 : {
211 467425 : if (pszSrcEncoding == nullptr)
212 0 : pszSrcEncoding = CPL_ENC_UTF8;
213 467425 : if (pszDstEncoding == nullptr)
214 0 : pszDstEncoding = CPL_ENC_UTF8;
215 :
216 467425 : if (strcmp(pszSrcEncoding, pszDstEncoding) == 0)
217 50 : return *this;
218 :
219 467375 : char *pszRecoded = CPLRecode(c_str(), pszSrcEncoding, pszDstEncoding);
220 :
221 467375 : if (pszRecoded == nullptr)
222 0 : return *this;
223 :
224 467375 : assign(pszRecoded);
225 467375 : CPLFree(pszRecoded);
226 :
227 467375 : 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 : */
242 :
243 7353 : size_t CPLString::ifind(const std::string &str, size_t pos) const
244 :
245 : {
246 7353 : return ifind(str.c_str(), pos);
247 : }
248 :
249 : /**
250 : * Case insensitive find() alternative.
251 : *
252 : * @param s substring to find.
253 : * @param nPos offset in the string at which the search starts.
254 : * @return the position of the substring in the string or std::string::npos if
255 : * not found.
256 : */
257 :
258 247071 : size_t CPLString::ifind(const char *s, size_t nPos) const
259 :
260 : {
261 247071 : const char *pszHaystack = c_str();
262 : const char chFirst =
263 247067 : static_cast<char>(CPLTolower(static_cast<unsigned char>(s[0])));
264 247065 : const size_t nTargetLen = strlen(s);
265 :
266 247065 : if (nPos > size())
267 0 : nPos = size();
268 :
269 247066 : pszHaystack += nPos;
270 :
271 9353210 : while (*pszHaystack != '\0')
272 : {
273 9115060 : if (chFirst == CPLTolower(static_cast<unsigned char>(*pszHaystack)))
274 : {
275 413232 : if (EQUALN(pszHaystack, s, nTargetLen))
276 8915 : return nPos;
277 : }
278 :
279 9106140 : nPos++;
280 9106140 : pszHaystack++;
281 : }
282 :
283 238149 : return std::string::npos;
284 : }
285 :
286 : /************************************************************************/
287 : /* toupper() */
288 : /************************************************************************/
289 :
290 : /**
291 : * Convert to upper case in place.
292 : */
293 :
294 3527490 : CPLString &CPLString::toupper()
295 :
296 : {
297 29706300 : for (size_t i = 0; i < size(); i++)
298 26178800 : (*this)[i] = static_cast<char>(CPLToupper((*this)[i]));
299 :
300 3527490 : return *this;
301 : }
302 :
303 : /************************************************************************/
304 : /* tolower() */
305 : /************************************************************************/
306 :
307 : /**
308 : * Convert to lower case in place.
309 : */
310 :
311 126437 : CPLString &CPLString::tolower()
312 :
313 : {
314 1808360 : for (size_t i = 0; i < size(); i++)
315 1681940 : (*this)[i] = static_cast<char>(CPLTolower((*this)[i]));
316 :
317 126439 : return *this;
318 : }
319 :
320 : /************************************************************************/
321 : /* replaceAll() */
322 : /************************************************************************/
323 :
324 : /**
325 : * Replace all occurrences of osBefore with osAfter.
326 : */
327 1215340 : CPLString &CPLString::replaceAll(const std::string &osBefore,
328 : const std::string &osAfter)
329 : {
330 1215340 : const size_t nBeforeSize = osBefore.size();
331 1215320 : const size_t nAfterSize = osAfter.size();
332 1215320 : if (nBeforeSize)
333 : {
334 1215310 : size_t nStartPos = 0;
335 1316960 : while ((nStartPos = find(osBefore, nStartPos)) != std::string::npos)
336 : {
337 101676 : replace(nStartPos, nBeforeSize, osAfter);
338 101655 : nStartPos += nAfterSize;
339 : }
340 : }
341 1215300 : return *this;
342 : }
343 :
344 : /**
345 : * Replace all occurrences of chBefore with osAfter.
346 : */
347 4401 : CPLString &CPLString::replaceAll(char chBefore, const std::string &osAfter)
348 : {
349 4401 : return replaceAll(std::string(&chBefore, 1), osAfter);
350 : }
351 :
352 : /**
353 : * Replace all occurrences of osBefore with chAfter.
354 : */
355 1149400 : CPLString &CPLString::replaceAll(const std::string &osBefore, char chAfter)
356 : {
357 1149400 : return replaceAll(osBefore, std::string(&chAfter, 1));
358 : }
359 :
360 : /**
361 : * Replace all occurrences of chBefore with chAfter.
362 : */
363 19055 : CPLString &CPLString::replaceAll(char chBefore, char chAfter)
364 : {
365 19055 : return replaceAll(std::string(&chBefore, 1), std::string(&chAfter, 1));
366 : }
367 :
368 : /************************************************************************/
369 : /* endsWith() */
370 : /************************************************************************/
371 :
372 : /**
373 : * Returns whether the string ends with another string
374 : * @param osStr other string.
375 : * @return true if the string ends with osStr.
376 : */
377 20565 : bool CPLString::endsWith(const std::string &osStr) const
378 : {
379 20565 : if (size() < osStr.size())
380 150 : return false;
381 20412 : return substr(size() - osStr.size()) == osStr;
382 : }
383 :
384 : /************************************************************************/
385 : /* URLEncode() */
386 : /************************************************************************/
387 :
388 : /**
389 : * Return a string that *can* be a valid URL.
390 : *
391 : * Said otherwise if URLEncode() != *this was not a valid URL according to
392 : * https://datatracker.ietf.org/doc/html/rfc3986.html.
393 : *
394 : * This replaces all characters that are not reserved (:/?#[]\@!$&'()*+,;=),
395 : * unreserved (a-z, A-Z, 0-9 and -.-~) or already percent-encoded by their
396 : * percent-encoding.
397 : *
398 : * Note that when composing a URL, and typically query-parameters, it might
399 : * be needed to use CPLEscape(,,CPLES_URL) to also substitute reserved
400 : * characters.
401 : *
402 : * @return a URL encoded string
403 : * @since 3.12
404 : */
405 55 : CPLString CPLString::URLEncode() const
406 : {
407 : // Helper to check if a substring is a valid percent-encoding
408 21 : auto isPercentEncoded = [](const char *str) -> bool
409 : {
410 42 : return str[0] == '%' &&
411 42 : std::isxdigit(static_cast<unsigned char>(str[1])) &&
412 42 : std::isxdigit(static_cast<unsigned char>(str[2]));
413 : };
414 :
415 : // Cf https://datatracker.ietf.org/doc/html/rfc3986.html
416 55 : const char *unreserved =
417 : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
418 55 : const char *reserved = ":/?#[]@!$&'()*+,;=";
419 55 : CPLString osEncoded;
420 55 : osEncoded.reserve(size());
421 4498 : for (size_t i = 0; i < size(); ++i)
422 : {
423 4443 : char ch = (*this)[i];
424 : // If already percent-encoded, copy as is
425 4443 : if (ch == '%' && i + 2 < size() && isPercentEncoded(c_str() + i))
426 : {
427 21 : osEncoded += ch;
428 21 : osEncoded += (*this)[i + 1];
429 21 : osEncoded += (*this)[i + 2];
430 21 : i += 2;
431 : }
432 4422 : else if (strchr(unreserved, ch) || strchr(reserved, ch))
433 : {
434 4409 : osEncoded += ch;
435 : }
436 : else
437 : {
438 : char buf[4];
439 13 : snprintf(buf, sizeof(buf), "%%%02X",
440 13 : static_cast<unsigned char>(ch));
441 13 : osEncoded += buf;
442 : }
443 : }
444 110 : return osEncoded;
445 : }
446 :
447 : /************************************************************************/
448 : /* SQLQuotedIdentifier() */
449 : /************************************************************************/
450 :
451 : /** Returns a string between double quotes and with all double quotes
452 : * inside the string are escaped by being doubled.
453 : *
454 : * Aimed at being used for SQL identifiers (table names, column names, etc.).
455 : *
456 : * @since 3.13
457 : */
458 16 : CPLString CPLString::SQLQuotedIdentifier() const
459 : {
460 16 : CPLString ret("\"");
461 32 : CPLString tmp(*this);
462 16 : tmp.replaceAll('"', "\"\"");
463 16 : ret += tmp;
464 16 : ret += '"';
465 32 : return ret;
466 : }
467 :
468 : /************************************************************************/
469 : /* SQLQuotedLiteral() */
470 : /************************************************************************/
471 :
472 : /** Returns a string between single quotes and with all single quotes
473 : * inside the string are escaped by being doubled.
474 : *
475 : * Aimed at being used for SQL literal strings.
476 : *
477 : * @since 3.13
478 : */
479 4 : CPLString CPLString::SQLQuotedLiteral() const
480 : {
481 4 : CPLString ret("'");
482 8 : CPLString tmp(*this);
483 4 : tmp.replaceAll('\'', "''");
484 4 : ret += tmp;
485 4 : ret += '\'';
486 8 : return ret;
487 : }
488 :
489 : /************************************************************************/
490 : /* CPLURLGetValue() */
491 : /************************************************************************/
492 :
493 : /**
494 : * Return the value matching a key from a key=value pair in a URL.
495 : *
496 : * @param pszURL the URL.
497 : * @param pszKey the key to find.
498 : * @return the value of empty string if not found.
499 : */
500 1669 : CPLString CPLURLGetValue(const char *pszURL, const char *pszKey)
501 : {
502 3338 : CPLString osKey(pszKey);
503 1669 : osKey += "=";
504 1669 : size_t nKeyPos = CPLString(pszURL).ifind(osKey);
505 1669 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
506 331 : (pszURL[nKeyPos - 1] == '?' || pszURL[nKeyPos - 1] == '&'))
507 : {
508 662 : CPLString osValue(pszURL + nKeyPos + osKey.size());
509 331 : const char *pszValue = osValue.c_str();
510 331 : const char *pszSep = strchr(pszValue, '&');
511 331 : if (pszSep)
512 : {
513 95 : osValue.resize(pszSep - pszValue);
514 : }
515 331 : return osValue;
516 : }
517 1338 : return "";
518 : }
519 :
520 : /************************************************************************/
521 : /* CPLURLAddKVP() */
522 : /************************************************************************/
523 :
524 : /**
525 : * Return a new URL with a new key=value pair.
526 : *
527 : * @param pszURL the URL.
528 : * @param pszKey the key to find.
529 : * @param pszValue the value of the key (may be NULL to unset an existing KVP).
530 : * @return the modified URL.
531 : */
532 5301 : CPLString CPLURLAddKVP(const char *pszURL, const char *pszKey,
533 : const char *pszValue)
534 : {
535 5301 : CPLString osURL(strchr(pszURL, '?') == nullptr
536 15222 : ? CPLString(pszURL).append("?")
537 10602 : : pszURL);
538 :
539 10602 : CPLString osKey(pszKey);
540 5301 : osKey += "=";
541 5301 : size_t nKeyPos = osURL.ifind(osKey);
542 5380 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
543 79 : (osURL[nKeyPos - 1] == '?' || osURL[nKeyPos - 1] == '&'))
544 : {
545 150 : CPLString osNewURL(osURL);
546 75 : osNewURL.resize(nKeyPos);
547 75 : if (pszValue)
548 : {
549 44 : osNewURL += osKey;
550 44 : osNewURL += pszValue;
551 : }
552 75 : const char *pszNext = strchr(osURL.c_str() + nKeyPos, '&');
553 75 : if (pszNext)
554 : {
555 72 : if (osNewURL.back() == '&' || osNewURL.back() == '?')
556 30 : osNewURL += pszNext + 1;
557 : else
558 42 : osNewURL += pszNext;
559 : }
560 75 : return osNewURL;
561 : }
562 : else
563 : {
564 10452 : CPLString osNewURL(std::move(osURL));
565 5226 : if (pszValue)
566 : {
567 3403 : if (osNewURL.back() != '&' && osNewURL.back() != '?')
568 2489 : osNewURL += '&';
569 3403 : osNewURL += osKey;
570 3403 : osNewURL += pszValue;
571 : }
572 5226 : return osNewURL;
573 : }
574 : }
575 :
576 : /************************************************************************/
577 : /* CPLOPrintf() */
578 : /************************************************************************/
579 :
580 : /** Return a CPLString with the content of sprintf() */
581 17590 : CPLString CPLOPrintf(CPL_FORMAT_STRING(const char *pszFormat), ...)
582 :
583 : {
584 : va_list args;
585 17590 : va_start(args, pszFormat);
586 :
587 17590 : CPLString osTarget;
588 17590 : osTarget.vPrintf(pszFormat, args);
589 :
590 17590 : va_end(args);
591 :
592 35180 : return osTarget;
593 : }
594 :
595 : /************************************************************************/
596 : /* CPLOvPrintf() */
597 : /************************************************************************/
598 :
599 : /** Return a CPLString with the content of vsprintf() */
600 0 : CPLString CPLOvPrintf(CPL_FORMAT_STRING(const char *pszFormat), va_list args)
601 :
602 : {
603 0 : CPLString osTarget;
604 0 : osTarget.vPrintf(pszFormat, args);
605 0 : return osTarget;
606 : }
607 :
608 : /************************************************************************/
609 : /* CPLQuotedSQLIdentifer() */
610 : /************************************************************************/
611 :
612 : /** Return a CPLString of the SQL quoted identifier */
613 6 : CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
614 :
615 : {
616 6 : CPLString osIdent;
617 :
618 6 : if (pszIdent)
619 : {
620 6 : char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
621 6 : osIdent.Printf("\"%s\"", pszQuotedIdent);
622 6 : CPLFree(pszQuotedIdent);
623 : }
624 :
625 6 : return osIdent;
626 : }
|