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 475851 : CPLString &CPLString::Printf(CPL_FORMAT_STRING(const char *pszFormat), ...)
42 :
43 : {
44 : va_list args;
45 :
46 475851 : va_start(args, pszFormat);
47 475851 : vPrintf(pszFormat, args);
48 475795 : va_end(args);
49 :
50 475795 : return *this;
51 : }
52 :
53 : /************************************************************************/
54 : /* vPrintf() */
55 : /************************************************************************/
56 :
57 : /** Assign the content of the string using vsprintf() */
58 601806 : 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 601806 : va_copy(wrk_args, args);
86 : #else
87 : wrk_args = args;
88 : #endif
89 :
90 601806 : char szModestBuffer[500] = {};
91 601806 : szModestBuffer[0] = '\0';
92 601806 : int nPR = CPLvsnprintf(szModestBuffer, sizeof(szModestBuffer), pszFormat,
93 : wrk_args);
94 601755 : if (nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer)) - 1)
95 : {
96 1224 : int nWorkBufferSize = 2000;
97 1224 : char *pszWorkBuffer = static_cast<char *>(CPLMalloc(nWorkBufferSize));
98 :
99 : #ifdef va_copy
100 1253 : va_end(wrk_args);
101 1253 : va_copy(wrk_args, args);
102 : #else
103 : wrk_args = args;
104 : #endif
105 1411 : while ((nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
106 1411 : wrk_args)) >= nWorkBufferSize - 1 ||
107 : nPR == -1)
108 : {
109 158 : nWorkBufferSize *= 4;
110 : pszWorkBuffer =
111 158 : static_cast<char *>(CPLRealloc(pszWorkBuffer, nWorkBufferSize));
112 : #ifdef va_copy
113 158 : va_end(wrk_args);
114 158 : va_copy(wrk_args, args);
115 : #else
116 : wrk_args = args;
117 : #endif
118 : }
119 1253 : *this = pszWorkBuffer;
120 1253 : CPLFree(pszWorkBuffer);
121 : }
122 : else
123 : {
124 600531 : *this = szModestBuffer;
125 : }
126 : #ifdef va_copy
127 601779 : va_end(wrk_args);
128 : #endif
129 :
130 : #endif /* !defined(HAVE_VSNPRINTF) */
131 :
132 601779 : 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 4122 : CPLString &CPLString::FormatC(double dfValue, const char *pszFormat)
154 :
155 : {
156 4122 : if (pszFormat == nullptr)
157 3508 : pszFormat = "%g";
158 :
159 : // presumably long enough for any number.
160 4122 : const size_t buf_size = 512;
161 4122 : char szWork[buf_size] = {};
162 :
163 4122 : CPLsnprintf(szWork, buf_size, pszFormat, dfValue);
164 :
165 4122 : *this += szWork;
166 :
167 4122 : 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 244841 : CPLString &CPLString::Trim()
184 :
185 : {
186 244841 : constexpr char szWhitespace[] = " \t\r\n";
187 :
188 244841 : const size_t iLeft = find_first_not_of(szWhitespace);
189 244841 : const size_t iRight = find_last_not_of(szWhitespace);
190 :
191 244841 : if (iLeft == std::string::npos)
192 : {
193 12668 : erase();
194 12668 : return *this;
195 : }
196 :
197 232173 : assign(substr(iLeft, iRight - iLeft + 1));
198 :
199 232173 : return *this;
200 : }
201 :
202 : /************************************************************************/
203 : /* Recode() */
204 : /************************************************************************/
205 :
206 : /** Recode the string */
207 3250 : CPLString &CPLString::Recode(const char *pszSrcEncoding,
208 : const char *pszDstEncoding)
209 :
210 : {
211 3250 : if (pszSrcEncoding == nullptr)
212 0 : pszSrcEncoding = CPL_ENC_UTF8;
213 3250 : if (pszDstEncoding == nullptr)
214 0 : pszDstEncoding = CPL_ENC_UTF8;
215 :
216 3250 : if (strcmp(pszSrcEncoding, pszDstEncoding) == 0)
217 40 : return *this;
218 :
219 3210 : char *pszRecoded = CPLRecode(c_str(), pszSrcEncoding, pszDstEncoding);
220 :
221 3210 : if (pszRecoded == nullptr)
222 0 : return *this;
223 :
224 3210 : assign(pszRecoded);
225 3210 : CPLFree(pszRecoded);
226 :
227 3210 : 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 7578 : size_t CPLString::ifind(const std::string &str, size_t pos) const
245 :
246 : {
247 7578 : 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 211853 : size_t CPLString::ifind(const char *s, size_t nPos) const
261 :
262 : {
263 211853 : const char *pszHaystack = c_str();
264 : const char chFirst =
265 211851 : static_cast<char>(CPLTolower(static_cast<unsigned char>(s[0])));
266 211849 : const size_t nTargetLen = strlen(s);
267 :
268 211849 : if (nPos > size())
269 0 : nPos = size();
270 :
271 211840 : pszHaystack += nPos;
272 :
273 7957970 : while (*pszHaystack != '\0')
274 : {
275 7755430 : if (chFirst == CPLTolower(static_cast<unsigned char>(*pszHaystack)))
276 : {
277 337905 : if (EQUALN(pszHaystack, s, nTargetLen))
278 9283 : return nPos;
279 : }
280 :
281 7746130 : nPos++;
282 7746130 : pszHaystack++;
283 : }
284 :
285 202534 : return std::string::npos;
286 : }
287 :
288 : /************************************************************************/
289 : /* toupper() */
290 : /************************************************************************/
291 :
292 : /**
293 : * Convert to upper case in place.
294 : */
295 :
296 2753250 : CPLString &CPLString::toupper()
297 :
298 : {
299 24266500 : for (size_t i = 0; i < size(); i++)
300 21513300 : (*this)[i] = static_cast<char>(CPLToupper((*this)[i]));
301 :
302 2753250 : return *this;
303 : }
304 :
305 : /************************************************************************/
306 : /* tolower() */
307 : /************************************************************************/
308 :
309 : /**
310 : * Convert to lower case in place.
311 : */
312 :
313 84494 : CPLString &CPLString::tolower()
314 :
315 : {
316 1501830 : for (size_t i = 0; i < size(); i++)
317 1417330 : (*this)[i] = static_cast<char>(CPLTolower((*this)[i]));
318 :
319 84494 : return *this;
320 : }
321 :
322 : /************************************************************************/
323 : /* replaceAll() */
324 : /************************************************************************/
325 :
326 : /**
327 : * Replace all occurrences of osBefore with osAfter.
328 : */
329 1016820 : CPLString &CPLString::replaceAll(const std::string &osBefore,
330 : const std::string &osAfter)
331 : {
332 1016820 : const size_t nBeforeSize = osBefore.size();
333 1016810 : const size_t nAfterSize = osAfter.size();
334 1016820 : if (nBeforeSize)
335 : {
336 1016810 : size_t nStartPos = 0;
337 1020950 : while ((nStartPos = find(osBefore, nStartPos)) != std::string::npos)
338 : {
339 4149 : replace(nStartPos, nBeforeSize, osAfter);
340 4142 : nStartPos += nAfterSize;
341 : }
342 : }
343 1016810 : return *this;
344 : }
345 :
346 : /**
347 : * Replace all occurrences of chBefore with osAfter.
348 : */
349 4545 : CPLString &CPLString::replaceAll(char chBefore, const std::string &osAfter)
350 : {
351 4545 : return replaceAll(std::string(&chBefore, 1), osAfter);
352 : }
353 :
354 : /**
355 : * Replace all occurrences of osBefore with chAfter.
356 : */
357 1001650 : CPLString &CPLString::replaceAll(const std::string &osBefore, char chAfter)
358 : {
359 1001650 : return replaceAll(osBefore, std::string(&chAfter, 1));
360 : }
361 :
362 : /**
363 : * Replace all occurrences of chBefore with chAfter.
364 : */
365 3735 : CPLString &CPLString::replaceAll(char chBefore, char chAfter)
366 : {
367 3735 : 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 18911 : bool CPLString::endsWith(const std::string &osStr) const
380 : {
381 18911 : if (size() < osStr.size())
382 150 : return false;
383 18761 : return substr(size() - osStr.size()) == osStr;
384 : }
385 :
386 : /************************************************************************/
387 : /* CPLURLGetValue() */
388 : /************************************************************************/
389 :
390 : /**
391 : * Return the value matching a key from a key=value pair in a URL.
392 : *
393 : * @param pszURL the URL.
394 : * @param pszKey the key to find.
395 : * @return the value of empty string if not found.
396 : * @since GDAL 1.9.0
397 : */
398 1628 : CPLString CPLURLGetValue(const char *pszURL, const char *pszKey)
399 : {
400 3256 : CPLString osKey(pszKey);
401 1628 : osKey += "=";
402 1628 : size_t nKeyPos = CPLString(pszURL).ifind(osKey);
403 1628 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
404 328 : (pszURL[nKeyPos - 1] == '?' || pszURL[nKeyPos - 1] == '&'))
405 : {
406 656 : CPLString osValue(pszURL + nKeyPos + osKey.size());
407 328 : const char *pszValue = osValue.c_str();
408 328 : const char *pszSep = strchr(pszValue, '&');
409 328 : if (pszSep)
410 : {
411 95 : osValue.resize(pszSep - pszValue);
412 : }
413 328 : return osValue;
414 : }
415 1300 : return "";
416 : }
417 :
418 : /************************************************************************/
419 : /* CPLURLAddKVP() */
420 : /************************************************************************/
421 :
422 : /**
423 : * Return a new URL with a new key=value pair.
424 : *
425 : * @param pszURL the URL.
426 : * @param pszKey the key to find.
427 : * @param pszValue the value of the key (may be NULL to unset an existing KVP).
428 : * @return the modified URL.
429 : * @since GDAL 1.9.0
430 : */
431 5112 : CPLString CPLURLAddKVP(const char *pszURL, const char *pszKey,
432 : const char *pszValue)
433 : {
434 5112 : const CPLString osURL(strchr(pszURL, '?') == nullptr
435 14684 : ? CPLString(pszURL).append("?")
436 10224 : : pszURL);
437 :
438 10224 : CPLString osKey(pszKey);
439 5112 : osKey += "=";
440 5112 : size_t nKeyPos = osURL.ifind(osKey);
441 5191 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
442 79 : (osURL[nKeyPos - 1] == '?' || osURL[nKeyPos - 1] == '&'))
443 : {
444 150 : CPLString osNewURL(osURL);
445 75 : osNewURL.resize(nKeyPos);
446 75 : if (pszValue)
447 : {
448 44 : osNewURL += osKey;
449 44 : osNewURL += pszValue;
450 : }
451 75 : const char *pszNext = strchr(osURL.c_str() + nKeyPos, '&');
452 75 : if (pszNext)
453 : {
454 72 : if (osNewURL.back() == '&' || osNewURL.back() == '?')
455 30 : osNewURL += pszNext + 1;
456 : else
457 42 : osNewURL += pszNext;
458 : }
459 75 : return osNewURL;
460 : }
461 : else
462 : {
463 10074 : CPLString osNewURL(osURL);
464 5037 : if (pszValue)
465 : {
466 3293 : if (osNewURL.back() != '&' && osNewURL.back() != '?')
467 2408 : osNewURL += '&';
468 3293 : osNewURL += osKey;
469 3293 : osNewURL += pszValue;
470 : }
471 5037 : return osNewURL;
472 : }
473 : }
474 :
475 : /************************************************************************/
476 : /* CPLOPrintf() */
477 : /************************************************************************/
478 :
479 : /** Return a CPLString with the content of sprintf() */
480 13412 : CPLString CPLOPrintf(CPL_FORMAT_STRING(const char *pszFormat), ...)
481 :
482 : {
483 : va_list args;
484 13412 : va_start(args, pszFormat);
485 :
486 13412 : CPLString osTarget;
487 13412 : osTarget.vPrintf(pszFormat, args);
488 :
489 13412 : va_end(args);
490 :
491 26824 : return osTarget;
492 : }
493 :
494 : /************************************************************************/
495 : /* CPLOvPrintf() */
496 : /************************************************************************/
497 :
498 : /** Return a CPLString with the content of vsprintf() */
499 0 : CPLString CPLOvPrintf(CPL_FORMAT_STRING(const char *pszFormat), va_list args)
500 :
501 : {
502 0 : CPLString osTarget;
503 0 : osTarget.vPrintf(pszFormat, args);
504 0 : return osTarget;
505 : }
506 :
507 : /************************************************************************/
508 : /* CPLQuotedSQLIdentifer() */
509 : /************************************************************************/
510 :
511 : /** Return a CPLString of the SQL quoted identifier */
512 6 : CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
513 :
514 : {
515 6 : CPLString osIdent;
516 :
517 6 : if (pszIdent)
518 : {
519 6 : char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
520 6 : osIdent.Printf("\"%s\"", pszQuotedIdent);
521 6 : CPLFree(pszQuotedIdent);
522 : }
523 :
524 6 : return osIdent;
525 : }
|