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