Coverage Report

Created: 2025-06-13 06:18

/src/gdal/port/cplstring.cpp
Line
Count
Source (jump to first uncovered line)
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
CPLString &CPLString::Printf(CPL_FORMAT_STRING(const char *pszFormat), ...)
42
43
0
{
44
0
    va_list args;
45
46
0
    va_start(args, pszFormat);
47
0
    vPrintf(pszFormat, args);
48
0
    va_end(args);
49
50
0
    return *this;
51
0
}
52
53
/************************************************************************/
54
/*                              vPrintf()                               */
55
/************************************************************************/
56
57
/** Assign the content of the string using vsprintf() */
58
CPLString &CPLString::vPrintf(CPL_FORMAT_STRING(const char *pszFormat),
59
                              va_list args)
60
61
0
{
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
0
    va_list wrk_args;
83
84
0
#ifdef va_copy
85
0
    va_copy(wrk_args, args);
86
#else
87
    wrk_args = args;
88
#endif
89
90
0
    char szModestBuffer[500] = {};
91
0
    szModestBuffer[0] = '\0';
92
0
    int nPR = CPLvsnprintf(szModestBuffer, sizeof(szModestBuffer), pszFormat,
93
0
                           wrk_args);
94
0
    if (nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer)) - 1)
95
0
    {
96
0
        int nWorkBufferSize = 2000;
97
0
        char *pszWorkBuffer = static_cast<char *>(CPLMalloc(nWorkBufferSize));
98
99
0
#ifdef va_copy
100
0
        va_end(wrk_args);
101
0
        va_copy(wrk_args, args);
102
#else
103
        wrk_args = args;
104
#endif
105
0
        while ((nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
106
0
                                   wrk_args)) >= nWorkBufferSize - 1 ||
107
0
               nPR == -1)
108
0
        {
109
0
            nWorkBufferSize *= 4;
110
0
            pszWorkBuffer =
111
0
                static_cast<char *>(CPLRealloc(pszWorkBuffer, nWorkBufferSize));
112
0
#ifdef va_copy
113
0
            va_end(wrk_args);
114
0
            va_copy(wrk_args, args);
115
#else
116
            wrk_args = args;
117
#endif
118
0
        }
119
0
        *this = pszWorkBuffer;
120
0
        CPLFree(pszWorkBuffer);
121
0
    }
122
0
    else
123
0
    {
124
0
        *this = szModestBuffer;
125
0
    }
126
0
#ifdef va_copy
127
0
    va_end(wrk_args);
128
0
#endif
129
130
0
#endif /* !defined(HAVE_VSNPRINTF) */
131
132
0
    return *this;
133
0
}
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
CPLString &CPLString::FormatC(double dfValue, const char *pszFormat)
154
155
0
{
156
0
    if (pszFormat == nullptr)
157
0
        pszFormat = "%g";
158
159
    // presumably long enough for any number.
160
0
    const size_t buf_size = 512;
161
0
    char szWork[buf_size] = {};
162
163
0
    CPLsnprintf(szWork, buf_size, pszFormat, dfValue);
164
165
0
    *this += szWork;
166
167
0
    return *this;
168
0
}
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
CPLString &CPLString::Trim()
184
185
0
{
186
0
    constexpr char szWhitespace[] = " \t\r\n";
187
188
0
    const size_t iLeft = find_first_not_of(szWhitespace);
189
0
    const size_t iRight = find_last_not_of(szWhitespace);
190
191
0
    if (iLeft == std::string::npos)
192
0
    {
193
0
        erase();
194
0
        return *this;
195
0
    }
196
197
0
    assign(substr(iLeft, iRight - iLeft + 1));
198
199
0
    return *this;
200
0
}
201
202
/************************************************************************/
203
/*                               Recode()                               */
204
/************************************************************************/
205
206
/** Recode the string */
207
CPLString &CPLString::Recode(const char *pszSrcEncoding,
208
                             const char *pszDstEncoding)
209
210
0
{
211
0
    if (pszSrcEncoding == nullptr)
212
0
        pszSrcEncoding = CPL_ENC_UTF8;
213
0
    if (pszDstEncoding == nullptr)
214
0
        pszDstEncoding = CPL_ENC_UTF8;
215
216
0
    if (strcmp(pszSrcEncoding, pszDstEncoding) == 0)
217
0
        return *this;
218
219
0
    char *pszRecoded = CPLRecode(c_str(), pszSrcEncoding, pszDstEncoding);
220
221
0
    if (pszRecoded == nullptr)
222
0
        return *this;
223
224
0
    assign(pszRecoded);
225
0
    CPLFree(pszRecoded);
226
227
0
    return *this;
228
0
}
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
size_t CPLString::ifind(const std::string &str, size_t pos) const
245
246
0
{
247
0
    return ifind(str.c_str(), pos);
248
0
}
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
size_t CPLString::ifind(const char *s, size_t nPos) const
261
262
0
{
263
0
    const char *pszHaystack = c_str();
264
0
    const char chFirst =
265
0
        static_cast<char>(CPLTolower(static_cast<unsigned char>(s[0])));
266
0
    const size_t nTargetLen = strlen(s);
267
268
0
    if (nPos > size())
269
0
        nPos = size();
270
271
0
    pszHaystack += nPos;
272
273
0
    while (*pszHaystack != '\0')
274
0
    {
275
0
        if (chFirst == CPLTolower(static_cast<unsigned char>(*pszHaystack)))
276
0
        {
277
0
            if (EQUALN(pszHaystack, s, nTargetLen))
278
0
                return nPos;
279
0
        }
280
281
0
        nPos++;
282
0
        pszHaystack++;
283
0
    }
284
285
0
    return std::string::npos;
286
0
}
287
288
/************************************************************************/
289
/*                              toupper()                               */
290
/************************************************************************/
291
292
/**
293
 * Convert to upper case in place.
294
 */
295
296
CPLString &CPLString::toupper()
297
298
0
{
299
0
    for (size_t i = 0; i < size(); i++)
300
0
        (*this)[i] = static_cast<char>(CPLToupper((*this)[i]));
301
302
0
    return *this;
303
0
}
304
305
/************************************************************************/
306
/*                              tolower()                               */
307
/************************************************************************/
308
309
/**
310
 * Convert to lower case in place.
311
 */
312
313
CPLString &CPLString::tolower()
314
315
0
{
316
0
    for (size_t i = 0; i < size(); i++)
317
0
        (*this)[i] = static_cast<char>(CPLTolower((*this)[i]));
318
319
0
    return *this;
320
0
}
321
322
/************************************************************************/
323
/*                             replaceAll()                             */
324
/************************************************************************/
325
326
/**
327
 * Replace all occurrences of osBefore with osAfter.
328
 */
329
CPLString &CPLString::replaceAll(const std::string &osBefore,
330
                                 const std::string &osAfter)
331
22.9k
{
332
22.9k
    const size_t nBeforeSize = osBefore.size();
333
22.9k
    const size_t nAfterSize = osAfter.size();
334
22.9k
    if (nBeforeSize)
335
22.9k
    {
336
22.9k
        size_t nStartPos = 0;
337
22.9k
        while ((nStartPos = find(osBefore, nStartPos)) != std::string::npos)
338
0
        {
339
0
            replace(nStartPos, nBeforeSize, osAfter);
340
0
            nStartPos += nAfterSize;
341
0
        }
342
22.9k
    }
343
22.9k
    return *this;
344
22.9k
}
345
346
/**
347
 * Replace all occurrences of chBefore with osAfter.
348
 */
349
CPLString &CPLString::replaceAll(char chBefore, const std::string &osAfter)
350
0
{
351
0
    return replaceAll(std::string(&chBefore, 1), osAfter);
352
0
}
353
354
/**
355
 * Replace all occurrences of osBefore with chAfter.
356
 */
357
CPLString &CPLString::replaceAll(const std::string &osBefore, char chAfter)
358
22.9k
{
359
22.9k
    return replaceAll(osBefore, std::string(&chAfter, 1));
360
22.9k
}
361
362
/**
363
 * Replace all occurrences of chBefore with chAfter.
364
 */
365
CPLString &CPLString::replaceAll(char chBefore, char chAfter)
366
0
{
367
0
    return replaceAll(std::string(&chBefore, 1), std::string(&chAfter, 1));
368
0
}
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
bool CPLString::endsWith(const std::string &osStr) const
380
0
{
381
0
    if (size() < osStr.size())
382
0
        return false;
383
0
    return substr(size() - osStr.size()) == osStr;
384
0
}
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
CPLString CPLURLGetValue(const char *pszURL, const char *pszKey)
399
0
{
400
0
    CPLString osKey(pszKey);
401
0
    osKey += "=";
402
0
    size_t nKeyPos = CPLString(pszURL).ifind(osKey);
403
0
    if (nKeyPos != std::string::npos && nKeyPos > 0 &&
404
0
        (pszURL[nKeyPos - 1] == '?' || pszURL[nKeyPos - 1] == '&'))
405
0
    {
406
0
        CPLString osValue(pszURL + nKeyPos + osKey.size());
407
0
        const char *pszValue = osValue.c_str();
408
0
        const char *pszSep = strchr(pszValue, '&');
409
0
        if (pszSep)
410
0
        {
411
0
            osValue.resize(pszSep - pszValue);
412
0
        }
413
0
        return osValue;
414
0
    }
415
0
    return "";
416
0
}
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
CPLString CPLURLAddKVP(const char *pszURL, const char *pszKey,
432
                       const char *pszValue)
433
0
{
434
0
    CPLString osURL(strchr(pszURL, '?') == nullptr
435
0
                        ? CPLString(pszURL).append("?")
436
0
                        : pszURL);
437
438
0
    CPLString osKey(pszKey);
439
0
    osKey += "=";
440
0
    size_t nKeyPos = osURL.ifind(osKey);
441
0
    if (nKeyPos != std::string::npos && nKeyPos > 0 &&
442
0
        (osURL[nKeyPos - 1] == '?' || osURL[nKeyPos - 1] == '&'))
443
0
    {
444
0
        CPLString osNewURL(osURL);
445
0
        osNewURL.resize(nKeyPos);
446
0
        if (pszValue)
447
0
        {
448
0
            osNewURL += osKey;
449
0
            osNewURL += pszValue;
450
0
        }
451
0
        const char *pszNext = strchr(osURL.c_str() + nKeyPos, '&');
452
0
        if (pszNext)
453
0
        {
454
0
            if (osNewURL.back() == '&' || osNewURL.back() == '?')
455
0
                osNewURL += pszNext + 1;
456
0
            else
457
0
                osNewURL += pszNext;
458
0
        }
459
0
        return osNewURL;
460
0
    }
461
0
    else
462
0
    {
463
0
        CPLString osNewURL(std::move(osURL));
464
0
        if (pszValue)
465
0
        {
466
0
            if (osNewURL.back() != '&' && osNewURL.back() != '?')
467
0
                osNewURL += '&';
468
0
            osNewURL += osKey;
469
0
            osNewURL += pszValue;
470
0
        }
471
0
        return osNewURL;
472
0
    }
473
0
}
474
475
/************************************************************************/
476
/*                            CPLOPrintf()                              */
477
/************************************************************************/
478
479
/** Return a CPLString with the content of sprintf() */
480
CPLString CPLOPrintf(CPL_FORMAT_STRING(const char *pszFormat), ...)
481
482
0
{
483
0
    va_list args;
484
0
    va_start(args, pszFormat);
485
486
0
    CPLString osTarget;
487
0
    osTarget.vPrintf(pszFormat, args);
488
489
0
    va_end(args);
490
491
0
    return osTarget;
492
0
}
493
494
/************************************************************************/
495
/*                            CPLOvPrintf()                             */
496
/************************************************************************/
497
498
/** Return a CPLString with the content of vsprintf() */
499
CPLString CPLOvPrintf(CPL_FORMAT_STRING(const char *pszFormat), va_list args)
500
501
0
{
502
0
    CPLString osTarget;
503
0
    osTarget.vPrintf(pszFormat, args);
504
0
    return osTarget;
505
0
}
506
507
/************************************************************************/
508
/*                            CPLQuotedSQLIdentifer()                   */
509
/************************************************************************/
510
511
/** Return a CPLString of the SQL quoted identifier */
512
CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
513
514
0
{
515
0
    CPLString osIdent;
516
517
0
    if (pszIdent)
518
0
    {
519
0
        char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
520
0
        osIdent.Printf("\"%s\"", pszQuotedIdent);
521
0
        CPLFree(pszQuotedIdent);
522
0
    }
523
524
0
    return osIdent;
525
0
}