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 CPL_CVSID("$Id: cplstring.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
47
48 /*
49 * The CPLString class is derived from std::string, so the vast majority
50 * of the implementation comes from that. This module is just the extensions
51 * we add.
52 */
53
54 /************************************************************************/
55 /* Printf() */
56 /************************************************************************/
57
58 /** Assign the content of the string using sprintf() */
Printf(CPL_FORMAT_STRING (const char * pszFormat),...)59 CPLString &CPLString::Printf( CPL_FORMAT_STRING(const char *pszFormat), ... )
60
61 {
62 va_list args;
63
64 va_start( args, pszFormat );
65 vPrintf( pszFormat, args );
66 va_end( args );
67
68 return *this;
69 }
70
71 /************************************************************************/
72 /* vPrintf() */
73 /************************************************************************/
74
75 /** Assign the content of the string using vsprintf() */
vPrintf(CPL_FORMAT_STRING (const char * pszFormat),va_list args)76 CPLString &CPLString::vPrintf( CPL_FORMAT_STRING(const char *pszFormat),
77 va_list args )
78
79 {
80 /* -------------------------------------------------------------------- */
81 /* This implementation for platforms without vsnprintf() will */
82 /* just plain fail if the formatted contents are too large. */
83 /* -------------------------------------------------------------------- */
84
85 #if !defined(HAVE_VSNPRINTF)
86 char *pszBuffer = static_cast<char *>(CPLMalloc(30000));
87 if( CPLvsnprintf( pszBuffer, 30000, pszFormat, args) > 29998 )
88 {
89 CPLError( CE_Fatal, CPLE_AppDefined,
90 "CPLString::vPrintf() ... buffer overrun." );
91 }
92 *this = pszBuffer;
93 CPLFree( pszBuffer );
94
95 /* -------------------------------------------------------------------- */
96 /* This should grow a big enough buffer to hold any formatted */
97 /* result. */
98 /* -------------------------------------------------------------------- */
99 #else
100 va_list wrk_args;
101
102 #ifdef va_copy
103 va_copy( wrk_args, args );
104 #else
105 wrk_args = args;
106 #endif
107
108 char szModestBuffer[500] = {};
109 szModestBuffer[0] = '\0';
110 int nPR = CPLvsnprintf( szModestBuffer, sizeof(szModestBuffer), pszFormat,
111 wrk_args );
112 if( nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer))-1 )
113 {
114 int nWorkBufferSize = 2000;
115 char *pszWorkBuffer = static_cast<char *>(
116 CPLMalloc(nWorkBufferSize));
117
118 #ifdef va_copy
119 va_end( wrk_args );
120 va_copy( wrk_args, args );
121 #else
122 wrk_args = args;
123 #endif
124 while( (nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
125 wrk_args))
126 >= nWorkBufferSize-1
127 || nPR == -1 )
128 {
129 nWorkBufferSize *= 4;
130 pszWorkBuffer = static_cast<char *>(
131 CPLRealloc(pszWorkBuffer, nWorkBufferSize));
132 #ifdef va_copy
133 va_end( wrk_args );
134 va_copy( wrk_args, args );
135 #else
136 wrk_args = args;
137 #endif
138 }
139 *this = pszWorkBuffer;
140 CPLFree( pszWorkBuffer );
141 }
142 else
143 {
144 *this = szModestBuffer;
145 }
146 #ifdef va_copy
147 va_end( wrk_args );
148 #endif
149
150 #endif /* !defined(HAVE_VSNPRINTF) */
151
152 return *this;
153 }
154
155 /************************************************************************/
156 /* FormatC() */
157 /************************************************************************/
158
159 /**
160 * Format double in C locale.
161 *
162 * The passed value is formatted using the C locale (period as decimal
163 * separator) and appended to the target CPLString.
164 *
165 * @param dfValue the value to format.
166 * @param pszFormat the sprintf() style format to use or omit for default.
167 * Note that this format string should only include one substitution argument
168 * and it must be for a double (%f or %g).
169 *
170 * @return a reference to the CPLString.
171 */
172
FormatC(double dfValue,const char * pszFormat)173 CPLString &CPLString::FormatC( double dfValue, const char *pszFormat )
174
175 {
176 if( pszFormat == nullptr )
177 pszFormat = "%g";
178
179 // presumably long enough for any number.
180 const size_t buf_size = 512;
181 char szWork[buf_size] = {};
182
183 CPLsnprintf( szWork, buf_size, pszFormat, dfValue );
184
185 *this += szWork;
186
187 return *this;
188 }
189
190 /************************************************************************/
191 /* Trim() */
192 /************************************************************************/
193
194 /**
195 * Trim white space.
196 *
197 * Trims white space off the let and right of the string. White space
198 * is any of a space, a tab, a newline ('\\n') or a carriage control ('\\r').
199 *
200 * @return a reference to the CPLString.
201 */
202
Trim()203 CPLString &CPLString::Trim()
204
205 {
206 constexpr char szWhitespace[] = " \t\r\n";
207
208 const size_t iLeft = find_first_not_of( szWhitespace );
209 const size_t iRight = find_last_not_of( szWhitespace );
210
211 if( iLeft == std::string::npos )
212 {
213 erase();
214 return *this;
215 }
216
217 assign( substr( iLeft, iRight - iLeft + 1 ) );
218
219 return *this;
220 }
221
222 /************************************************************************/
223 /* Recode() */
224 /************************************************************************/
225
226 /** Recode the string */
Recode(const char * pszSrcEncoding,const char * pszDstEncoding)227 CPLString &CPLString::Recode( const char *pszSrcEncoding,
228 const char *pszDstEncoding )
229
230 {
231 if( pszSrcEncoding == nullptr )
232 pszSrcEncoding = CPL_ENC_UTF8;
233 if( pszDstEncoding == nullptr )
234 pszDstEncoding = CPL_ENC_UTF8;
235
236 if( strcmp(pszSrcEncoding, pszDstEncoding) == 0 )
237 return *this;
238
239 char *pszRecoded = CPLRecode( c_str(),
240 pszSrcEncoding,
241 pszDstEncoding );
242
243 if( pszRecoded == nullptr )
244 return *this;
245
246 assign( pszRecoded );
247 CPLFree( pszRecoded );
248
249 return *this;
250 }
251
252 /************************************************************************/
253 /* ifind() */
254 /************************************************************************/
255
256 /**
257 * Case insensitive find() alternative.
258 *
259 * @param str substring to find.
260 * @param pos offset in the string at which the search starts.
261 * @return the position of substring in the string or std::string::npos if not found.
262 * @since GDAL 1.9.0
263 */
264
ifind(const std::string & str,size_t pos) const265 size_t CPLString::ifind( const std::string & str, size_t pos ) const
266
267 {
268 return ifind( str.c_str(), pos );
269 }
270
271 /**
272 * Case insensitive find() alternative.
273 *
274 * @param s substring to find.
275 * @param nPos offset in the string at which the search starts.
276 * @return the position of the substring in the string or std::string::npos if
277 * not found.
278 * @since GDAL 1.9.0
279 */
280
ifind(const char * s,size_t nPos) const281 size_t CPLString::ifind( const char *s, size_t nPos ) const
282
283 {
284 const char *pszHaystack = c_str();
285 const char chFirst = static_cast<char>(::tolower(s[0]));
286 const size_t nTargetLen = strlen(s);
287
288 if( nPos > size() )
289 nPos = size();
290
291 pszHaystack += nPos;
292
293 while( *pszHaystack != '\0' )
294 {
295 if( chFirst == ::tolower(*pszHaystack) )
296 {
297 if( EQUALN(pszHaystack, s, nTargetLen) )
298 return nPos;
299 }
300
301 nPos++;
302 pszHaystack++;
303 }
304
305 return std::string::npos;
306 }
307
308 /************************************************************************/
309 /* toupper() */
310 /************************************************************************/
311
312 /**
313 * Convert to upper case in place.
314 */
315
toupper()316 CPLString &CPLString::toupper()
317
318 {
319 for( size_t i = 0; i < size(); i++ )
320 (*this)[i] = static_cast<char>(::toupper((*this)[i]));
321
322 return *this;
323 }
324
325 /************************************************************************/
326 /* tolower() */
327 /************************************************************************/
328
329 /**
330 * Convert to lower case in place.
331 */
332
tolower()333 CPLString &CPLString::tolower()
334
335 {
336 for( size_t i = 0; i < size(); i++ )
337 (*this)[i] = static_cast<char>(::tolower((*this)[i]));
338
339 return *this;
340 }
341
342 /************************************************************************/
343 /* replaceAll() */
344 /************************************************************************/
345
346 /**
347 * Replace all occurrences of osBefore with osAfter.
348 */
replaceAll(const std::string & osBefore,const std::string & osAfter)349 CPLString &CPLString::replaceAll( const std::string &osBefore,
350 const std::string &osAfter )
351 {
352 const size_t nBeforeSize = osBefore.size();
353 const size_t nAfterSize = osAfter.size();
354 if( nBeforeSize )
355 {
356 size_t nStartPos = 0;
357 while( (nStartPos = find(osBefore, nStartPos)) != std::string::npos )
358 {
359 replace(nStartPos, nBeforeSize, osAfter);
360 nStartPos += nAfterSize;
361 }
362 }
363 return *this;
364 }
365
366 /**
367 * Replace all occurrences of chBefore with osAfter.
368 */
replaceAll(char chBefore,const std::string & osAfter)369 CPLString &CPLString::replaceAll( char chBefore,
370 const std::string &osAfter )
371 {
372 return replaceAll(std::string(&chBefore, 1), osAfter);
373 }
374
375 /**
376 * Replace all occurrences of osBefore with chAfter.
377 */
replaceAll(const std::string & osBefore,char chAfter)378 CPLString &CPLString::replaceAll( const std::string &osBefore,
379 char chAfter )
380 {
381 return replaceAll(osBefore, std::string(&chAfter, 1));
382 }
383
384 /**
385 * Replace all occurrences of chBefore with chAfter.
386 */
replaceAll(char chBefore,char chAfter)387 CPLString &CPLString::replaceAll( char chBefore,
388 char chAfter )
389 {
390 return replaceAll(std::string(&chBefore, 1), std::string(&chAfter, 1));
391 }
392
393
394 /************************************************************************/
395 /* endsWith() */
396 /************************************************************************/
397
398 /**
399 * Returns whether the string ends with another string
400 * @param osStr other string.
401 * @return true if the string ends wit osStr.
402 */
endsWith(const std::string & osStr) const403 bool CPLString::endsWith( const std::string& osStr ) const
404 {
405 if( size() < osStr.size() )
406 return false;
407 return substr(size() - osStr.size()) == osStr;
408 }
409
410 /************************************************************************/
411 /* CPLURLGetValue() */
412 /************************************************************************/
413
414 /**
415 * Return the value matching a key from a key=value pair in a URL.
416 *
417 * @param pszURL the URL.
418 * @param pszKey the key to find.
419 * @return the value of empty string if not found.
420 * @since GDAL 1.9.0
421 */
CPLURLGetValue(const char * pszURL,const char * pszKey)422 CPLString CPLURLGetValue(const char* pszURL, const char* pszKey)
423 {
424 CPLString osKey(pszKey);
425 osKey += "=";
426 size_t nKeyPos = CPLString(pszURL).ifind(osKey);
427 if( nKeyPos != std::string::npos && nKeyPos > 0 &&
428 (pszURL[nKeyPos-1] == '?' || pszURL[nKeyPos-1] == '&') )
429 {
430 CPLString osValue(pszURL + nKeyPos + osKey.size());
431 const char* pszValue = osValue.c_str();
432 const char* pszSep = strchr(pszValue, '&');
433 if( pszSep )
434 {
435 osValue.resize(pszSep - pszValue);
436 }
437 return osValue;
438 }
439 return "";
440 }
441
442 /************************************************************************/
443 /* CPLURLAddKVP() */
444 /************************************************************************/
445
446 /**
447 * Return a new URL with a new key=value pair.
448 *
449 * @param pszURL the URL.
450 * @param pszKey the key to find.
451 * @param pszValue the value of the key (may be NULL to unset an existing KVP).
452 * @return the modified URL.
453 * @since GDAL 1.9.0
454 */
CPLURLAddKVP(const char * pszURL,const char * pszKey,const char * pszValue)455 CPLString CPLURLAddKVP(const char* pszURL, const char* pszKey,
456 const char* pszValue)
457 {
458 CPLString osURL(pszURL);
459 if( strchr(osURL, '?') == nullptr )
460 osURL += "?";
461 pszURL = osURL.c_str();
462
463 CPLString osKey(pszKey);
464 osKey += "=";
465 size_t nKeyPos = osURL.ifind(osKey);
466 if( nKeyPos != std::string::npos && nKeyPos > 0 &&
467 (pszURL[nKeyPos-1] == '?' || pszURL[nKeyPos-1] == '&') )
468 {
469 CPLString osNewURL(osURL);
470 osNewURL.resize(nKeyPos);
471 if( pszValue )
472 {
473 osNewURL += osKey;
474 osNewURL += pszValue;
475 }
476 const char* pszNext = strchr(pszURL + nKeyPos, '&');
477 if( pszNext )
478 {
479 if( osNewURL.back() == '&'
480 || osNewURL.back() == '?' )
481 osNewURL += pszNext + 1;
482 else
483 osNewURL += pszNext;
484 }
485 return osNewURL;
486 }
487 else
488 {
489 if( pszValue )
490 {
491 if( osURL.back() != '&' && osURL.back() != '?' )
492 osURL += '&';
493 osURL += osKey;
494 osURL += pszValue;
495 }
496 return osURL;
497 }
498 }
499
500 /************************************************************************/
501 /* CPLOPrintf() */
502 /************************************************************************/
503
504 /** Return a CPLString with the content of sprintf() */
CPLOPrintf(CPL_FORMAT_STRING (const char * pszFormat),...)505 CPLString CPLOPrintf( CPL_FORMAT_STRING(const char *pszFormat), ... )
506
507 {
508 va_list args;
509 va_start( args, pszFormat );
510
511 CPLString osTarget;
512 osTarget.vPrintf( pszFormat, args );
513
514 va_end( args );
515
516 return osTarget;
517 }
518
519 /************************************************************************/
520 /* CPLOvPrintf() */
521 /************************************************************************/
522
523 /** Return a CPLString with the content of vsprintf() */
CPLOvPrintf(CPL_FORMAT_STRING (const char * pszFormat),va_list args)524 CPLString CPLOvPrintf( CPL_FORMAT_STRING(const char *pszFormat), va_list args )
525
526 {
527 CPLString osTarget;
528 osTarget.vPrintf( pszFormat, args);
529 return osTarget;
530 }
531
532 /************************************************************************/
533 /* CPLQuotedSQLIdentifer() */
534 /************************************************************************/
535
536 /** Return a CPLString of the SQL quoted identifier */
CPLQuotedSQLIdentifier(const char * pszIdent)537 CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
538
539 {
540 CPLString osIdent;
541
542 if(pszIdent)
543 {
544 char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
545 osIdent.Printf("\"%s\"", pszQuotedIdent);
546 CPLFree(pszQuotedIdent);
547 }
548
549 return osIdent;
550 }
551