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 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "cpl_string.h"
32 :
33 : #include <cctype>
34 : #include <cstdarg>
35 : #include <cstddef>
36 : #include <cstring>
37 : #include <string>
38 :
39 : #include "cpl_config.h"
40 : #include "cpl_conv.h"
41 :
42 : #if !defined(va_copy) && defined(__va_copy)
43 : #define va_copy __va_copy
44 : #endif
45 :
46 : /*
47 : * The CPLString class is derived from std::string, so the vast majority
48 : * of the implementation comes from that. This module is just the extensions
49 : * we add.
50 : */
51 :
52 : /************************************************************************/
53 : /* Printf() */
54 : /************************************************************************/
55 :
56 : /** Assign the content of the string using sprintf() */
57 377327 : CPLString &CPLString::Printf(CPL_FORMAT_STRING(const char *pszFormat), ...)
58 :
59 : {
60 : va_list args;
61 :
62 377327 : va_start(args, pszFormat);
63 377327 : vPrintf(pszFormat, args);
64 377291 : va_end(args);
65 :
66 377291 : return *this;
67 : }
68 :
69 : /************************************************************************/
70 : /* vPrintf() */
71 : /************************************************************************/
72 :
73 : /** Assign the content of the string using vsprintf() */
74 499650 : CPLString &CPLString::vPrintf(CPL_FORMAT_STRING(const char *pszFormat),
75 : va_list args)
76 :
77 : {
78 : /* -------------------------------------------------------------------- */
79 : /* This implementation for platforms without vsnprintf() will */
80 : /* just plain fail if the formatted contents are too large. */
81 : /* -------------------------------------------------------------------- */
82 :
83 : #if !defined(HAVE_VSNPRINTF)
84 : char *pszBuffer = static_cast<char *>(CPLMalloc(30000));
85 : if (CPLvsnprintf(pszBuffer, 30000, pszFormat, args) > 29998)
86 : {
87 : CPLError(CE_Fatal, CPLE_AppDefined,
88 : "CPLString::vPrintf() ... buffer overrun.");
89 : }
90 : *this = pszBuffer;
91 : CPLFree(pszBuffer);
92 :
93 : /* -------------------------------------------------------------------- */
94 : /* This should grow a big enough buffer to hold any formatted */
95 : /* result. */
96 : /* -------------------------------------------------------------------- */
97 : #else
98 : va_list wrk_args;
99 :
100 : #ifdef va_copy
101 499650 : va_copy(wrk_args, args);
102 : #else
103 : wrk_args = args;
104 : #endif
105 :
106 499650 : char szModestBuffer[500] = {};
107 499650 : szModestBuffer[0] = '\0';
108 499650 : int nPR = CPLvsnprintf(szModestBuffer, sizeof(szModestBuffer), pszFormat,
109 : wrk_args);
110 499623 : if (nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer)) - 1)
111 : {
112 1269 : int nWorkBufferSize = 2000;
113 1269 : char *pszWorkBuffer = static_cast<char *>(CPLMalloc(nWorkBufferSize));
114 :
115 : #ifdef va_copy
116 1285 : va_end(wrk_args);
117 1285 : va_copy(wrk_args, args);
118 : #else
119 : wrk_args = args;
120 : #endif
121 1472 : while ((nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
122 1472 : wrk_args)) >= nWorkBufferSize - 1 ||
123 : nPR == -1)
124 : {
125 187 : nWorkBufferSize *= 4;
126 : pszWorkBuffer =
127 187 : static_cast<char *>(CPLRealloc(pszWorkBuffer, nWorkBufferSize));
128 : #ifdef va_copy
129 187 : va_end(wrk_args);
130 187 : va_copy(wrk_args, args);
131 : #else
132 : wrk_args = args;
133 : #endif
134 : }
135 1285 : *this = pszWorkBuffer;
136 1285 : CPLFree(pszWorkBuffer);
137 : }
138 : else
139 : {
140 498354 : *this = szModestBuffer;
141 : }
142 : #ifdef va_copy
143 499633 : va_end(wrk_args);
144 : #endif
145 :
146 : #endif /* !defined(HAVE_VSNPRINTF) */
147 :
148 499633 : return *this;
149 : }
150 :
151 : /************************************************************************/
152 : /* FormatC() */
153 : /************************************************************************/
154 :
155 : /**
156 : * Format double in C locale.
157 : *
158 : * The passed value is formatted using the C locale (period as decimal
159 : * separator) and appended to the target CPLString.
160 : *
161 : * @param dfValue the value to format.
162 : * @param pszFormat the sprintf() style format to use or omit for default.
163 : * Note that this format string should only include one substitution argument
164 : * and it must be for a double (%f or %g).
165 : *
166 : * @return a reference to the CPLString.
167 : */
168 :
169 4026 : CPLString &CPLString::FormatC(double dfValue, const char *pszFormat)
170 :
171 : {
172 4026 : if (pszFormat == nullptr)
173 3412 : pszFormat = "%g";
174 :
175 : // presumably long enough for any number.
176 4026 : const size_t buf_size = 512;
177 4026 : char szWork[buf_size] = {};
178 :
179 4026 : CPLsnprintf(szWork, buf_size, pszFormat, dfValue);
180 :
181 4026 : *this += szWork;
182 :
183 4026 : return *this;
184 : }
185 :
186 : /************************************************************************/
187 : /* Trim() */
188 : /************************************************************************/
189 :
190 : /**
191 : * Trim white space.
192 : *
193 : * Trims white space off the let and right of the string. White space
194 : * is any of a space, a tab, a newline ('\\n') or a carriage control ('\\r').
195 : *
196 : * @return a reference to the CPLString.
197 : */
198 :
199 244870 : CPLString &CPLString::Trim()
200 :
201 : {
202 244870 : constexpr char szWhitespace[] = " \t\r\n";
203 :
204 244870 : const size_t iLeft = find_first_not_of(szWhitespace);
205 244870 : const size_t iRight = find_last_not_of(szWhitespace);
206 :
207 244870 : if (iLeft == std::string::npos)
208 : {
209 12642 : erase();
210 12642 : return *this;
211 : }
212 :
213 232228 : assign(substr(iLeft, iRight - iLeft + 1));
214 :
215 232228 : return *this;
216 : }
217 :
218 : /************************************************************************/
219 : /* Recode() */
220 : /************************************************************************/
221 :
222 : /** Recode the string */
223 3229 : CPLString &CPLString::Recode(const char *pszSrcEncoding,
224 : const char *pszDstEncoding)
225 :
226 : {
227 3229 : if (pszSrcEncoding == nullptr)
228 0 : pszSrcEncoding = CPL_ENC_UTF8;
229 3229 : if (pszDstEncoding == nullptr)
230 0 : pszDstEncoding = CPL_ENC_UTF8;
231 :
232 3229 : if (strcmp(pszSrcEncoding, pszDstEncoding) == 0)
233 0 : return *this;
234 :
235 3229 : char *pszRecoded = CPLRecode(c_str(), pszSrcEncoding, pszDstEncoding);
236 :
237 3229 : if (pszRecoded == nullptr)
238 0 : return *this;
239 :
240 3229 : assign(pszRecoded);
241 3229 : CPLFree(pszRecoded);
242 :
243 3229 : return *this;
244 : }
245 :
246 : /************************************************************************/
247 : /* ifind() */
248 : /************************************************************************/
249 :
250 : /**
251 : * Case insensitive find() alternative.
252 : *
253 : * @param str substring to find.
254 : * @param pos offset in the string at which the search starts.
255 : * @return the position of substring in the string or std::string::npos if not
256 : * found.
257 : * @since GDAL 1.9.0
258 : */
259 :
260 7483 : size_t CPLString::ifind(const std::string &str, size_t pos) const
261 :
262 : {
263 7483 : return ifind(str.c_str(), pos);
264 : }
265 :
266 : /**
267 : * Case insensitive find() alternative.
268 : *
269 : * @param s substring to find.
270 : * @param nPos offset in the string at which the search starts.
271 : * @return the position of the substring in the string or std::string::npos if
272 : * not found.
273 : * @since GDAL 1.9.0
274 : */
275 :
276 202682 : size_t CPLString::ifind(const char *s, size_t nPos) const
277 :
278 : {
279 202682 : const char *pszHaystack = c_str();
280 : const char chFirst =
281 202667 : static_cast<char>(CPLTolower(static_cast<unsigned char>(s[0])));
282 202674 : const size_t nTargetLen = strlen(s);
283 :
284 202674 : if (nPos > size())
285 0 : nPos = size();
286 :
287 202671 : pszHaystack += nPos;
288 :
289 7293080 : while (*pszHaystack != '\0')
290 : {
291 7099570 : if (chFirst == CPLTolower(static_cast<unsigned char>(*pszHaystack)))
292 : {
293 313963 : if (EQUALN(pszHaystack, s, nTargetLen))
294 9157 : return nPos;
295 : }
296 :
297 7090410 : nPos++;
298 7090410 : pszHaystack++;
299 : }
300 :
301 193509 : return std::string::npos;
302 : }
303 :
304 : /************************************************************************/
305 : /* toupper() */
306 : /************************************************************************/
307 :
308 : /**
309 : * Convert to upper case in place.
310 : */
311 :
312 2557470 : CPLString &CPLString::toupper()
313 :
314 : {
315 22982200 : for (size_t i = 0; i < size(); i++)
316 20424700 : (*this)[i] = static_cast<char>(CPLToupper((*this)[i]));
317 :
318 2557470 : return *this;
319 : }
320 :
321 : /************************************************************************/
322 : /* tolower() */
323 : /************************************************************************/
324 :
325 : /**
326 : * Convert to lower case in place.
327 : */
328 :
329 80800 : CPLString &CPLString::tolower()
330 :
331 : {
332 1419060 : for (size_t i = 0; i < size(); i++)
333 1338260 : (*this)[i] = static_cast<char>(CPLTolower((*this)[i]));
334 :
335 80800 : return *this;
336 : }
337 :
338 : /************************************************************************/
339 : /* replaceAll() */
340 : /************************************************************************/
341 :
342 : /**
343 : * Replace all occurrences of osBefore with osAfter.
344 : */
345 790557 : CPLString &CPLString::replaceAll(const std::string &osBefore,
346 : const std::string &osAfter)
347 : {
348 790557 : const size_t nBeforeSize = osBefore.size();
349 790553 : const size_t nAfterSize = osAfter.size();
350 790549 : if (nBeforeSize)
351 : {
352 790545 : size_t nStartPos = 0;
353 794541 : while ((nStartPos = find(osBefore, nStartPos)) != std::string::npos)
354 : {
355 4006 : replace(nStartPos, nBeforeSize, osAfter);
356 3996 : nStartPos += nAfterSize;
357 : }
358 : }
359 790543 : return *this;
360 : }
361 :
362 : /**
363 : * Replace all occurrences of chBefore with osAfter.
364 : */
365 4536 : CPLString &CPLString::replaceAll(char chBefore, const std::string &osAfter)
366 : {
367 4536 : return replaceAll(std::string(&chBefore, 1), osAfter);
368 : }
369 :
370 : /**
371 : * Replace all occurrences of osBefore with chAfter.
372 : */
373 775836 : CPLString &CPLString::replaceAll(const std::string &osBefore, char chAfter)
374 : {
375 775836 : return replaceAll(osBefore, std::string(&chAfter, 1));
376 : }
377 :
378 : /**
379 : * Replace all occurrences of chBefore with chAfter.
380 : */
381 3411 : CPLString &CPLString::replaceAll(char chBefore, char chAfter)
382 : {
383 3411 : return replaceAll(std::string(&chBefore, 1), std::string(&chAfter, 1));
384 : }
385 :
386 : /************************************************************************/
387 : /* endsWith() */
388 : /************************************************************************/
389 :
390 : /**
391 : * Returns whether the string ends with another string
392 : * @param osStr other string.
393 : * @return true if the string ends with osStr.
394 : */
395 18748 : bool CPLString::endsWith(const std::string &osStr) const
396 : {
397 18748 : if (size() < osStr.size())
398 107 : return false;
399 18641 : return substr(size() - osStr.size()) == osStr;
400 : }
401 :
402 : /************************************************************************/
403 : /* CPLURLGetValue() */
404 : /************************************************************************/
405 :
406 : /**
407 : * Return the value matching a key from a key=value pair in a URL.
408 : *
409 : * @param pszURL the URL.
410 : * @param pszKey the key to find.
411 : * @return the value of empty string if not found.
412 : * @since GDAL 1.9.0
413 : */
414 1611 : CPLString CPLURLGetValue(const char *pszURL, const char *pszKey)
415 : {
416 3222 : CPLString osKey(pszKey);
417 1611 : osKey += "=";
418 1611 : size_t nKeyPos = CPLString(pszURL).ifind(osKey);
419 1611 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
420 327 : (pszURL[nKeyPos - 1] == '?' || pszURL[nKeyPos - 1] == '&'))
421 : {
422 654 : CPLString osValue(pszURL + nKeyPos + osKey.size());
423 327 : const char *pszValue = osValue.c_str();
424 327 : const char *pszSep = strchr(pszValue, '&');
425 327 : if (pszSep)
426 : {
427 94 : osValue.resize(pszSep - pszValue);
428 : }
429 327 : return osValue;
430 : }
431 1284 : return "";
432 : }
433 :
434 : /************************************************************************/
435 : /* CPLURLAddKVP() */
436 : /************************************************************************/
437 :
438 : /**
439 : * Return a new URL with a new key=value pair.
440 : *
441 : * @param pszURL the URL.
442 : * @param pszKey the key to find.
443 : * @param pszValue the value of the key (may be NULL to unset an existing KVP).
444 : * @return the modified URL.
445 : * @since GDAL 1.9.0
446 : */
447 5045 : CPLString CPLURLAddKVP(const char *pszURL, const char *pszKey,
448 : const char *pszValue)
449 : {
450 5045 : const CPLString osURL(strchr(pszURL, '?') == nullptr
451 14491 : ? CPLString(pszURL).append("?")
452 10090 : : pszURL);
453 :
454 10090 : CPLString osKey(pszKey);
455 5045 : osKey += "=";
456 5045 : size_t nKeyPos = osURL.ifind(osKey);
457 5124 : if (nKeyPos != std::string::npos && nKeyPos > 0 &&
458 79 : (osURL[nKeyPos - 1] == '?' || osURL[nKeyPos - 1] == '&'))
459 : {
460 150 : CPLString osNewURL(osURL);
461 75 : osNewURL.resize(nKeyPos);
462 75 : if (pszValue)
463 : {
464 44 : osNewURL += osKey;
465 44 : osNewURL += pszValue;
466 : }
467 75 : const char *pszNext = strchr(osURL.c_str() + nKeyPos, '&');
468 75 : if (pszNext)
469 : {
470 72 : if (osNewURL.back() == '&' || osNewURL.back() == '?')
471 30 : osNewURL += pszNext + 1;
472 : else
473 42 : osNewURL += pszNext;
474 : }
475 75 : return osNewURL;
476 : }
477 : else
478 : {
479 9940 : CPLString osNewURL(osURL);
480 4970 : if (pszValue)
481 : {
482 3246 : if (osNewURL.back() != '&' && osNewURL.back() != '?')
483 2374 : osNewURL += '&';
484 3246 : osNewURL += osKey;
485 3246 : osNewURL += pszValue;
486 : }
487 4970 : return osNewURL;
488 : }
489 : }
490 :
491 : /************************************************************************/
492 : /* CPLOPrintf() */
493 : /************************************************************************/
494 :
495 : /** Return a CPLString with the content of sprintf() */
496 13153 : CPLString CPLOPrintf(CPL_FORMAT_STRING(const char *pszFormat), ...)
497 :
498 : {
499 : va_list args;
500 13153 : va_start(args, pszFormat);
501 :
502 13153 : CPLString osTarget;
503 13153 : osTarget.vPrintf(pszFormat, args);
504 :
505 13153 : va_end(args);
506 :
507 26306 : return osTarget;
508 : }
509 :
510 : /************************************************************************/
511 : /* CPLOvPrintf() */
512 : /************************************************************************/
513 :
514 : /** Return a CPLString with the content of vsprintf() */
515 0 : CPLString CPLOvPrintf(CPL_FORMAT_STRING(const char *pszFormat), va_list args)
516 :
517 : {
518 0 : CPLString osTarget;
519 0 : osTarget.vPrintf(pszFormat, args);
520 0 : return osTarget;
521 : }
522 :
523 : /************************************************************************/
524 : /* CPLQuotedSQLIdentifer() */
525 : /************************************************************************/
526 :
527 : /** Return a CPLString of the SQL quoted identifier */
528 6 : CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
529 :
530 : {
531 6 : CPLString osIdent;
532 :
533 6 : if (pszIdent)
534 : {
535 6 : char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
536 6 : osIdent.Printf("\"%s\"", pszQuotedIdent);
537 6 : CPLFree(pszQuotedIdent);
538 : }
539 :
540 6 : return osIdent;
541 : }
|