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 504228 : CPLString &CPLString::Printf(CPL_FORMAT_STRING(const char *pszFormat), ...)
42 :
43 : {
44 : va_list args;
45 :
46 504228 : va_start(args, pszFormat);
47 504228 : vPrintf(pszFormat, args);
48 504195 : va_end(args);
49 :
50 504195 : return *this;
51 : }
52 :
53 : /************************************************************************/
54 : /* vPrintf() */
55 : /************************************************************************/
56 :
57 : /** Assign the content of the string using vsprintf() */
58 621257 : 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 621257 : va_copy(wrk_args, args);
86 : #else
87 : wrk_args = args;
88 : #endif
89 :
90 621257 : char szModestBuffer[500] = {};
91 621257 : szModestBuffer[0] = '\0';
92 621257 : int nPR = CPLvsnprintf(szModestBuffer, sizeof(szModestBuffer), pszFormat,
93 : wrk_args);
94 621202 : if (nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer)) - 1)
95 : {
96 1236 : int nWorkBufferSize = 2000;
97 1236 : char *pszWorkBuffer = static_cast<char *>(CPLMalloc(nWorkBufferSize));
98 :
99 : #ifdef va_copy
100 1245 : va_end(wrk_args);
101 1245 : va_copy(wrk_args, args);
102 : #else
103 : wrk_args = args;
104 : #endif
105 1449 : while ((nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
106 1449 : wrk_args)) >= nWorkBufferSize - 1 ||
107 : nPR == -1)
108 : {
109 204 : nWorkBufferSize *= 4;
110 : pszWorkBuffer =
111 204 : static_cast<char *>(CPLRealloc(pszWorkBuffer, nWorkBufferSize));
112 : #ifdef va_copy
113 204 : va_end(wrk_args);
114 204 : va_copy(wrk_args, args);
115 : #else
116 : wrk_args = args;
117 : #endif
118 : }
119 1245 : *this = pszWorkBuffer;
120 1245 : CPLFree(pszWorkBuffer);
121 : }
122 : else
123 : {
124 619966 : *this = szModestBuffer;
125 : }
126 : #ifdef va_copy
127 621228 : va_end(wrk_args);
128 : #endif
129 :
130 : #endif /* !defined(HAVE_VSNPRINTF) */
131 :
132 621228 : 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 810333 : CPLString &CPLString::Trim()
184 :
185 : {
186 810333 : constexpr char szWhitespace[] = " \t\r\n";
187 :
188 810333 : const size_t iLeft = find_first_not_of(szWhitespace);
189 810333 : const size_t iRight = find_last_not_of(szWhitespace);
190 :
191 810333 : if (iLeft == std::string::npos)
192 : {
193 86616 : erase();
194 86616 : return *this;
195 : }
196 :
197 723717 : assign(substr(iLeft, iRight - iLeft + 1));
198 :
199 723717 : return *this;
200 : }
201 :
202 : /************************************************************************/
203 : /* Recode() */
204 : /************************************************************************/
205 :
206 : /** Recode the string */
207 435185 : CPLString &CPLString::Recode(const char *pszSrcEncoding,
208 : const char *pszDstEncoding)
209 :
210 : {
211 435185 : if (pszSrcEncoding == nullptr)
212 0 : pszSrcEncoding = CPL_ENC_UTF8;
213 435185 : if (pszDstEncoding == nullptr)
214 0 : pszDstEncoding = CPL_ENC_UTF8;
215 :
216 435185 : if (strcmp(pszSrcEncoding, pszDstEncoding) == 0)
217 50 : return *this;
218 :
219 435135 : char *pszRecoded = CPLRecode(c_str(), pszSrcEncoding, pszDstEncoding);
220 :
221 435135 : if (pszRecoded == nullptr)
222 0 : return *this;
223 :
224 435135 : assign(pszRecoded);
225 435135 : CPLFree(pszRecoded);
226 :
227 435135 : 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 234574 : size_t CPLString::ifind(const char *s, size_t nPos) const
259 :
260 : {
261 234574 : const char *pszHaystack = c_str();
262 : const char chFirst =
263 234569 : static_cast<char>(CPLTolower(static_cast<unsigned char>(s[0])));
264 234566 : const size_t nTargetLen = strlen(s);
265 :
266 234566 : if (nPos > size())
267 0 : nPos = size();
268 :
269 234563 : pszHaystack += nPos;
270 :
271 8361890 : while (*pszHaystack != '\0')
272 : {
273 8136260 : if (chFirst == CPLTolower(static_cast<unsigned char>(*pszHaystack)))
274 : {
275 361232 : if (EQUALN(pszHaystack, s, nTargetLen))
276 8921 : return nPos;
277 : }
278 :
279 8127330 : nPos++;
280 8127330 : pszHaystack++;
281 : }
282 :
283 225633 : return std::string::npos;
284 : }
285 :
286 : /************************************************************************/
287 : /* toupper() */
288 : /************************************************************************/
289 :
290 : /**
291 : * Convert to upper case in place.
292 : */
293 :
294 3492880 : CPLString &CPLString::toupper()
295 :
296 : {
297 29474800 : for (size_t i = 0; i < size(); i++)
298 25982000 : (*this)[i] = static_cast<char>(CPLToupper((*this)[i]));
299 :
300 3492880 : return *this;
301 : }
302 :
303 : /************************************************************************/
304 : /* tolower() */
305 : /************************************************************************/
306 :
307 : /**
308 : * Convert to lower case in place.
309 : */
310 :
311 105540 : CPLString &CPLString::tolower()
312 :
313 : {
314 1719570 : for (size_t i = 0; i < size(); i++)
315 1614030 : (*this)[i] = static_cast<char>(CPLTolower((*this)[i]));
316 :
317 105540 : return *this;
318 : }
319 :
320 : /************************************************************************/
321 : /* replaceAll() */
322 : /************************************************************************/
323 :
324 : /**
325 : * Replace all occurrences of osBefore with osAfter.
326 : */
327 1168140 : CPLString &CPLString::replaceAll(const std::string &osBefore,
328 : const std::string &osAfter)
329 : {
330 1168140 : const size_t nBeforeSize = osBefore.size();
331 1168130 : const size_t nAfterSize = osAfter.size();
332 1168130 : if (nBeforeSize)
333 : {
334 1168130 : size_t nStartPos = 0;
335 1262500 : while ((nStartPos = find(osBefore, nStartPos)) != std::string::npos)
336 : {
337 94374 : replace(nStartPos, nBeforeSize, osAfter);
338 94372 : nStartPos += nAfterSize;
339 : }
340 : }
341 1168120 : return *this;
342 : }
343 :
344 : /**
345 : * Replace all occurrences of chBefore with osAfter.
346 : */
347 4379 : CPLString &CPLString::replaceAll(char chBefore, const std::string &osAfter)
348 : {
349 4379 : return replaceAll(std::string(&chBefore, 1), osAfter);
350 : }
351 :
352 : /**
353 : * Replace all occurrences of osBefore with chAfter.
354 : */
355 1114470 : CPLString &CPLString::replaceAll(const std::string &osBefore, char chAfter)
356 : {
357 1114470 : return replaceAll(osBefore, std::string(&chAfter, 1));
358 : }
359 :
360 : /**
361 : * Replace all occurrences of chBefore with chAfter.
362 : */
363 7027 : CPLString &CPLString::replaceAll(char chBefore, char chAfter)
364 : {
365 7027 : 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 20563 : bool CPLString::endsWith(const std::string &osStr) const
378 : {
379 20563 : if (size() < osStr.size())
380 150 : return false;
381 20413 : 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 : /* CPLURLGetValue() */
449 : /************************************************************************/
450 :
451 : /**
452 : * Return the value matching a key from a key=value pair in a URL.
453 : *
454 : * @param pszURL the URL.
455 : * @param pszKey the key to find.
456 : * @return the value of empty string if not found.
457 : */
458 1669 : CPLString CPLURLGetValue(const char *pszURL, const char *pszKey)
459 : {
460 3338 : CPLString osKey(pszKey);
461 1669 : osKey += "=";
462 1669 : size_t nKeyPos = CPLString(pszURL).ifind(osKey);
463 1669 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
464 331 : (pszURL[nKeyPos - 1] == '?' || pszURL[nKeyPos - 1] == '&'))
465 : {
466 662 : CPLString osValue(pszURL + nKeyPos + osKey.size());
467 331 : const char *pszValue = osValue.c_str();
468 331 : const char *pszSep = strchr(pszValue, '&');
469 331 : if (pszSep)
470 : {
471 95 : osValue.resize(pszSep - pszValue);
472 : }
473 331 : return osValue;
474 : }
475 1338 : return "";
476 : }
477 :
478 : /************************************************************************/
479 : /* CPLURLAddKVP() */
480 : /************************************************************************/
481 :
482 : /**
483 : * Return a new URL with a new key=value pair.
484 : *
485 : * @param pszURL the URL.
486 : * @param pszKey the key to find.
487 : * @param pszValue the value of the key (may be NULL to unset an existing KVP).
488 : * @return the modified URL.
489 : */
490 5301 : CPLString CPLURLAddKVP(const char *pszURL, const char *pszKey,
491 : const char *pszValue)
492 : {
493 5301 : CPLString osURL(strchr(pszURL, '?') == nullptr
494 15222 : ? CPLString(pszURL).append("?")
495 10602 : : pszURL);
496 :
497 10602 : CPLString osKey(pszKey);
498 5301 : osKey += "=";
499 5301 : size_t nKeyPos = osURL.ifind(osKey);
500 5380 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
501 79 : (osURL[nKeyPos - 1] == '?' || osURL[nKeyPos - 1] == '&'))
502 : {
503 150 : CPLString osNewURL(osURL);
504 75 : osNewURL.resize(nKeyPos);
505 75 : if (pszValue)
506 : {
507 44 : osNewURL += osKey;
508 44 : osNewURL += pszValue;
509 : }
510 75 : const char *pszNext = strchr(osURL.c_str() + nKeyPos, '&');
511 75 : if (pszNext)
512 : {
513 72 : if (osNewURL.back() == '&' || osNewURL.back() == '?')
514 30 : osNewURL += pszNext + 1;
515 : else
516 42 : osNewURL += pszNext;
517 : }
518 75 : return osNewURL;
519 : }
520 : else
521 : {
522 10452 : CPLString osNewURL(std::move(osURL));
523 5226 : if (pszValue)
524 : {
525 3403 : if (osNewURL.back() != '&' && osNewURL.back() != '?')
526 2489 : osNewURL += '&';
527 3403 : osNewURL += osKey;
528 3403 : osNewURL += pszValue;
529 : }
530 5226 : return osNewURL;
531 : }
532 : }
533 :
534 : /************************************************************************/
535 : /* CPLOPrintf() */
536 : /************************************************************************/
537 :
538 : /** Return a CPLString with the content of sprintf() */
539 17582 : CPLString CPLOPrintf(CPL_FORMAT_STRING(const char *pszFormat), ...)
540 :
541 : {
542 : va_list args;
543 17582 : va_start(args, pszFormat);
544 :
545 17582 : CPLString osTarget;
546 17582 : osTarget.vPrintf(pszFormat, args);
547 :
548 17582 : va_end(args);
549 :
550 35164 : return osTarget;
551 : }
552 :
553 : /************************************************************************/
554 : /* CPLOvPrintf() */
555 : /************************************************************************/
556 :
557 : /** Return a CPLString with the content of vsprintf() */
558 0 : CPLString CPLOvPrintf(CPL_FORMAT_STRING(const char *pszFormat), va_list args)
559 :
560 : {
561 0 : CPLString osTarget;
562 0 : osTarget.vPrintf(pszFormat, args);
563 0 : return osTarget;
564 : }
565 :
566 : /************************************************************************/
567 : /* CPLQuotedSQLIdentifer() */
568 : /************************************************************************/
569 :
570 : /** Return a CPLString of the SQL quoted identifier */
571 6 : CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
572 :
573 : {
574 6 : CPLString osIdent;
575 :
576 6 : if (pszIdent)
577 : {
578 6 : char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
579 6 : osIdent.Printf("\"%s\"", pszQuotedIdent);
580 6 : CPLFree(pszQuotedIdent);
581 : }
582 :
583 6 : return osIdent;
584 : }
|