Coverage Report

Created: 2026-09-26 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cplstringlist.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  CPLStringList implementation.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2011, 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 <cstddef>
18
#include <cstdio>
19
#include <cstdlib>
20
#include <cstring>
21
22
#include <algorithm>
23
#include <limits>
24
#include <string>
25
26
#include "cpl_conv.h"
27
#include "cpl_error.h"
28
29
static int CPLCompareKeyValueString(const char *pszKVa, const char *pszKVb);
30
31
/************************************************************************/
32
/*                           CPLStringList()                            */
33
/************************************************************************/
34
35
570M
CPLStringList::CPLStringList() = default;
36
37
/************************************************************************/
38
/*                           CPLStringList()                            */
39
/************************************************************************/
40
41
/**
42
 * CPLStringList constructor.
43
 *
44
 * @param papszListIn the NULL terminated list of strings to consume.
45
 * @param bTakeOwnership TRUE if the CPLStringList should take ownership
46
 * of the list of strings which implies responsibility to free them.
47
 */
48
49
CPLStringList::CPLStringList(char **papszListIn, int bTakeOwnership)
50
89.6M
    : CPLStringList()
51
52
89.6M
{
53
89.6M
    Assign(papszListIn, bTakeOwnership);
54
89.6M
}
55
56
/************************************************************************/
57
/*                           CPLStringList()                            */
58
/************************************************************************/
59
60
/**
61
 * CPLStringList constructor.
62
 *
63
 * The input list is copied.
64
 *
65
 * @param papszListIn the NULL terminated list of strings to ingest.
66
 */
67
68
189k
CPLStringList::CPLStringList(CSLConstList papszListIn) : CPLStringList()
69
70
189k
{
71
189k
    Assign(CSLDuplicate(papszListIn));
72
189k
}
73
74
/************************************************************************/
75
/*                           CPLStringList()                            */
76
/************************************************************************/
77
78
/**
79
 * CPLStringList constructor.
80
 *
81
 * The input list is copied.
82
 *
83
 * @param aosList input list.
84
 *
85
 * @since GDAL 3.9
86
 */
87
CPLStringList::CPLStringList(const std::vector<std::string> &aosList)
88
499
{
89
499
    if (!aosList.empty())
90
0
    {
91
0
        bOwnList = true;
92
0
        papszList = static_cast<char **>(
93
0
            VSI_CALLOC_VERBOSE(aosList.size() + 1, sizeof(char *)));
94
0
        nCount = static_cast<int>(aosList.size());
95
0
        for (int i = 0; i < nCount; ++i)
96
0
        {
97
0
            papszList[i] = VSI_STRDUP_VERBOSE(aosList[i].c_str());
98
0
        }
99
0
    }
100
499
}
101
102
/************************************************************************/
103
/*                           CPLStringList()                            */
104
/************************************************************************/
105
106
/**
107
 * CPLStringList constructor.
108
 *
109
 * The input list is copied.
110
 *
111
 * @param oInitList input list.
112
 *
113
 * @since GDAL 3.9
114
 */
115
CPLStringList::CPLStringList(std::initializer_list<const char *> oInitList)
116
0
{
117
0
    for (const char *pszStr : oInitList)
118
0
    {
119
0
        AddString(pszStr);
120
0
    }
121
0
}
122
123
/************************************************************************/
124
/*                           CPLStringList()                            */
125
/************************************************************************/
126
127
//! Copy constructor
128
365k
CPLStringList::CPLStringList(const CPLStringList &oOther) : CPLStringList()
129
130
365k
{
131
365k
    operator=(oOther);
132
365k
}
133
134
/************************************************************************/
135
/*                           CPLStringList()                            */
136
/************************************************************************/
137
138
//! Move constructor
139
120M
CPLStringList::CPLStringList(CPLStringList &&oOther) : CPLStringList()
140
141
120M
{
142
120M
    operator=(std::move(oOther));
143
120M
}
144
145
/************************************************************************/
146
/*                          BoundToConstList()                          */
147
/************************************************************************/
148
149
/**
150
 * Return a CPLStringList that wraps the passed list.
151
 *
152
 * The input list is *NOT* copied and must be kept alive while the
153
 * return CPLStringList is used.
154
 *
155
 * @param papszListIn a NULL terminated list of strings to wrap into the CPLStringList
156
 * @since GDAL 3.9
157
 */
158
159
/* static */
160
const CPLStringList CPLStringList::BoundToConstList(CSLConstList papszListIn)
161
0
{
162
0
    return CPLStringList(const_cast<char **>(papszListIn),
163
0
                         /* bTakeOwnership= */ false);
164
0
}
165
166
/************************************************************************/
167
/*                             operator=()                              */
168
/************************************************************************/
169
170
CPLStringList &CPLStringList::operator=(const CPLStringList &oOther)
171
812k
{
172
812k
    if (this != &oOther)
173
812k
    {
174
812k
        char **l_papszList = CSLDuplicate(oOther.papszList);
175
812k
        if (l_papszList)
176
35.3k
        {
177
35.3k
            Assign(l_papszList, TRUE);
178
35.3k
            nAllocation = oOther.nCount > 0 ? oOther.nCount + 1 : 0;
179
35.3k
            nCount = oOther.nCount;
180
35.3k
            bIsSorted = oOther.bIsSorted;
181
35.3k
        }
182
812k
    }
183
184
812k
    return *this;
185
812k
}
186
187
/************************************************************************/
188
/*                             operator=()                              */
189
/************************************************************************/
190
191
CPLStringList &CPLStringList::operator=(CPLStringList &&oOther)
192
120M
{
193
120M
    if (this != &oOther)
194
120M
    {
195
120M
        Clear();
196
120M
        papszList = oOther.papszList;
197
120M
        oOther.papszList = nullptr;
198
120M
        nCount = oOther.nCount;
199
120M
        oOther.nCount = 0;
200
120M
        nAllocation = oOther.nAllocation;
201
120M
        oOther.nAllocation = 0;
202
120M
        bOwnList = oOther.bOwnList;
203
120M
        oOther.bOwnList = false;
204
120M
        bIsSorted = oOther.bIsSorted;
205
120M
        oOther.bIsSorted = true;
206
120M
    }
207
208
120M
    return *this;
209
120M
}
210
211
/************************************************************************/
212
/*                             operator=()                              */
213
/************************************************************************/
214
215
CPLStringList &CPLStringList::operator=(CSLConstList papszListIn)
216
716k
{
217
716k
    if (papszListIn != papszList)
218
165k
    {
219
165k
        Assign(CSLDuplicate(papszListIn));
220
165k
        bIsSorted = false;
221
165k
    }
222
223
716k
    return *this;
224
716k
}
225
226
/************************************************************************/
227
/*                           ~CPLStringList()                           */
228
/************************************************************************/
229
230
CPLStringList::~CPLStringList()
231
232
570M
{
233
570M
    Clear();
234
570M
}
235
236
/************************************************************************/
237
/*                               Clear()                                */
238
/************************************************************************/
239
240
/**
241
 * Clear the string list.
242
 */
243
CPLStringList &CPLStringList::Clear()
244
245
821M
{
246
821M
    if (bOwnList)
247
150M
    {
248
150M
        CSLDestroy(papszList);
249
150M
        papszList = nullptr;
250
251
150M
        bOwnList = FALSE;
252
150M
        nAllocation = 0;
253
150M
        nCount = 0;
254
150M
    }
255
256
821M
    return *this;
257
821M
}
258
259
/************************************************************************/
260
/*                               Assign()                               */
261
/************************************************************************/
262
263
/**
264
 * Assign a list of strings.
265
 *
266
 *
267
 * @param papszListIn the NULL terminated list of strings to consume.
268
 * @param bTakeOwnership TRUE if the CPLStringList should take ownership
269
 * of the list of strings which implies responsibility to free them.
270
 *
271
 * @return a reference to the CPLStringList on which it was invoked.
272
 */
273
274
CPLStringList &CPLStringList::Assign(char **papszListIn, int bTakeOwnership)
275
276
118M
{
277
118M
    Clear();
278
279
118M
    papszList = papszListIn;
280
118M
    bOwnList = CPL_TO_BOOL(bTakeOwnership);
281
282
118M
    if (papszList == nullptr || *papszList == nullptr)
283
36.7M
        nCount = 0;
284
81.3M
    else
285
81.3M
        nCount = -1;  // unknown
286
287
118M
    nAllocation = 0;
288
118M
    bIsSorted = FALSE;
289
290
118M
    return *this;
291
118M
}
292
293
/************************************************************************/
294
/*                               Count()                                */
295
/************************************************************************/
296
297
/**
298
 * @return count of strings in the list, zero if empty.
299
 */
300
301
int CPLStringList::Count() const
302
303
145M
{
304
145M
    if (nCount == -1)
305
21.3M
    {
306
21.3M
        if (papszList == nullptr)
307
0
        {
308
0
            nCount = 0;
309
0
            nAllocation = 0;
310
0
        }
311
21.3M
        else
312
21.3M
        {
313
21.3M
            nCount = CSLCount(papszList);
314
21.3M
            nAllocation = std::max(nCount + 1, nAllocation);
315
21.3M
        }
316
21.3M
    }
317
318
145M
    return nCount;
319
145M
}
320
321
/************************************************************************/
322
/*                           MakeOurOwnCopy()                           */
323
/*                                                                      */
324
/*      If we don't own the list, a copy is made which we own.          */
325
/*      Necessary if we are going to modify the list.                   */
326
/************************************************************************/
327
328
bool CPLStringList::MakeOurOwnCopy()
329
330
277M
{
331
277M
    if (bOwnList)
332
8.56M
        return true;
333
334
268M
    if (papszList == nullptr)
335
268M
        return true;
336
337
3
    Count();
338
3
    char **papszListNew = CSLDuplicate(papszList);
339
3
    if (papszListNew == nullptr)
340
0
    {
341
0
        return false;
342
0
    }
343
3
    papszList = papszListNew;
344
3
    bOwnList = true;
345
3
    nAllocation = nCount + 1;
346
3
    return true;
347
3
}
348
349
/************************************************************************/
350
/*                          EnsureAllocation()                          */
351
/*                                                                      */
352
/*      Ensure we have enough room allocated for at least the           */
353
/*      requested number of strings (so nAllocation will be at least    */
354
/*      one more than the target)                                       */
355
/************************************************************************/
356
357
bool CPLStringList::EnsureAllocation(int nMaxList)
358
359
444M
{
360
444M
    if (!bOwnList)
361
207M
    {
362
207M
        if (!MakeOurOwnCopy())
363
0
            return false;
364
207M
    }
365
366
444M
    if (papszList == nullptr || nAllocation <= nMaxList)
367
208M
    {
368
        // we need to be able to store nMaxList+1 as an int,
369
        // and allocate (nMaxList+1) * sizeof(char*) bytes
370
208M
        if (nMaxList < 0 || nMaxList > std::numeric_limits<int>::max() - 1 ||
371
208M
            static_cast<size_t>(nMaxList) >
372
208M
                std::numeric_limits<size_t>::max() / sizeof(char *) - 1)
373
0
        {
374
0
            return false;
375
0
        }
376
208M
        int nNewAllocation = nMaxList + 1;
377
208M
        if (nNewAllocation <= (std::numeric_limits<int>::max() - 20) / 2 /
378
208M
                                  static_cast<int>(sizeof(char *)))
379
208M
            nNewAllocation = std::max(nNewAllocation * 2 + 20, nMaxList + 1);
380
208M
        if (papszList == nullptr)
381
207M
        {
382
207M
            papszList = static_cast<char **>(
383
207M
                VSI_CALLOC_VERBOSE(nNewAllocation, sizeof(char *)));
384
207M
            bOwnList = true;
385
207M
            nCount = 0;
386
207M
            if (papszList == nullptr)
387
0
                return false;
388
207M
        }
389
1.02M
        else
390
1.02M
        {
391
1.02M
            char **papszListNew = static_cast<char **>(VSI_REALLOC_VERBOSE(
392
1.02M
                papszList, nNewAllocation * sizeof(char *)));
393
1.02M
            if (papszListNew == nullptr)
394
0
                return false;
395
1.02M
            papszList = papszListNew;
396
1.02M
        }
397
208M
        nAllocation = nNewAllocation;
398
208M
    }
399
444M
    return true;
400
444M
}
401
402
/************************************************************************/
403
/*                         AddStringDirectly()                          */
404
/************************************************************************/
405
406
/**
407
 * Add a string to the list.
408
 *
409
 * This method is similar to AddString(), but ownership of the
410
 * pszNewString is transferred to the CPLStringList class.
411
 *
412
 * @param pszNewString the string to add to the list.
413
 */
414
415
CPLStringList &CPLStringList::AddStringDirectly(char *pszNewString)
416
417
444M
{
418
444M
    if (nCount == -1)
419
23.3k
        Count();
420
421
444M
    if (!EnsureAllocation(nCount + 1))
422
0
    {
423
0
        VSIFree(pszNewString);
424
0
        return *this;
425
0
    }
426
427
444M
    papszList[nCount++] = pszNewString;
428
444M
    papszList[nCount] = nullptr;
429
430
444M
    bIsSorted = false;
431
432
444M
    return *this;
433
444M
}
434
435
/************************************************************************/
436
/*                             AddString()                              */
437
/************************************************************************/
438
439
/**
440
 * Add a string to the list.
441
 *
442
 * A copy of the passed in string is made and inserted in the list.
443
 *
444
 * @param pszNewString the string to add to the list.
445
 */
446
447
CPLStringList &CPLStringList::AddString(const char *pszNewString)
448
449
202M
{
450
202M
    char *pszDupString = VSI_STRDUP_VERBOSE(pszNewString);
451
202M
    if (pszDupString == nullptr)
452
0
        return *this;
453
202M
    return AddStringDirectly(pszDupString);
454
202M
}
455
456
/************************************************************************/
457
/*                             AddString()                              */
458
/************************************************************************/
459
/**
460
 * Add a string to the list.
461
 *
462
 * A copy of the passed in string is made and inserted in the list.
463
 *
464
 * @param newString the string to add to the list.
465
 * @return a reference to the CPLStringList on which it was invoked.
466
 */
467
468
CPLStringList &CPLStringList::AddString(const std::string &newString)
469
1.46M
{
470
1.46M
    return AddString(newString.c_str());
471
1.46M
}
472
473
/************************************************************************/
474
/*                             AddString()                              */
475
/************************************************************************/
476
477
/**
478
 * Create a new string from a number and add it to the list.
479
 *
480
 * @param dfNumber the number to convert to a string.
481
 * @return a reference to the CPLStringList on which it was invoked.
482
 */
483
484
CPLStringList &CPLStringList::AddString(double dfNumber)
485
0
{
486
0
    return AddString(CPLSPrintf("%.17g", dfNumber));
487
0
}
488
489
/************************************************************************/
490
/*                             AddString()                              */
491
/************************************************************************/
492
/**
493
 * Add a string to the list.
494
 *
495
 * A copy of the passed in string_view is made and inserted in the list.
496
 *
497
 * @param newString the string to add to the list.
498
 * @return a reference to the CPLStringList on which it was invoked.
499
 */
500
501
CPLStringList &CPLStringList::AddString(std::string_view newString)
502
166M
{
503
166M
    char *pszDupString =
504
166M
        static_cast<char *>(VSI_MALLOC_VERBOSE(newString.size() + 1));
505
166M
    if (pszDupString == nullptr)
506
0
    {
507
0
        return *this;
508
0
    }
509
166M
    std::memcpy(pszDupString, newString.data(), newString.size());
510
166M
    pszDupString[newString.size()] = '\0';
511
512
166M
    return AddStringDirectly(pszDupString);
513
166M
}
514
515
/************************************************************************/
516
/*                             push_back()                              */
517
/************************************************************************/
518
519
/**
520
 * Add a string to the list.
521
 *
522
 * A copy of the passed in string is made and inserted in the list.
523
 *
524
 * @param svStr the string to add to the list.
525
 *
526
 * @since 3.13
527
 */
528
529
void CPLStringList::push_back(std::string_view svStr)
530
531
0
{
532
0
    char *pszDupString =
533
0
        static_cast<char *>(VSI_MALLOC_VERBOSE(svStr.size() + 1));
534
0
    if (pszDupString == nullptr)
535
0
        return;
536
0
    memcpy(pszDupString, svStr.data(), svStr.size());
537
0
    pszDupString[svStr.size()] = 0;
538
0
    CPL_IGNORE_RET_VAL(AddStringDirectly(pszDupString));
539
0
}
540
541
/************************************************************************/
542
/*                            AddNameValue()                            */
543
/************************************************************************/
544
545
/**
546
 * Add a name=value entry to the list.
547
 *
548
 * A key=value string is prepared and appended to the list.  There is no
549
 * check for other values for the same key in the list.
550
 *
551
 * @param pszKey the key name to add.
552
 * @param pszValue the key value to add.
553
 */
554
555
CPLStringList &CPLStringList::AddNameValue(const char *pszKey,
556
                                           const char *pszValue)
557
558
68.4M
{
559
68.4M
    if (pszKey == nullptr || pszValue == nullptr)
560
114k
        return *this;
561
562
68.3M
    if (!MakeOurOwnCopy())
563
0
        return *this;
564
565
    /* -------------------------------------------------------------------- */
566
    /*      Format the line.                                                */
567
    /* -------------------------------------------------------------------- */
568
68.3M
    if (strlen(pszKey) >
569
68.3M
            std::numeric_limits<size_t>::max() - strlen(pszValue) ||
570
68.3M
        strlen(pszKey) + strlen(pszValue) >
571
68.3M
            std::numeric_limits<size_t>::max() - 2)
572
0
    {
573
0
        CPLError(CE_Failure, CPLE_OutOfMemory,
574
0
                 "Too big strings in AddNameValue()");
575
0
        return *this;
576
0
    }
577
68.3M
    const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
578
68.3M
    char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
579
68.3M
    if (pszLine == nullptr)
580
0
        return *this;
581
68.3M
    snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
582
583
    /* -------------------------------------------------------------------- */
584
    /*      If we don't need to keep the sort order things are pretty       */
585
    /*      straight forward.                                               */
586
    /* -------------------------------------------------------------------- */
587
68.3M
    if (!IsSorted())
588
68.0M
        return AddStringDirectly(pszLine);
589
590
    /* -------------------------------------------------------------------- */
591
    /*      Find the proper insertion point.                                */
592
    /* -------------------------------------------------------------------- */
593
321k
    CPLAssert(IsSorted());
594
321k
    const int iKey = FindSortedInsertionPoint(pszLine);
595
321k
    InsertStringDirectly(iKey, pszLine);
596
321k
    bIsSorted = true;  // We have actually preserved sort order.
597
598
321k
    return *this;
599
68.3M
}
600
601
/************************************************************************/
602
/*                            SetNameValue()                            */
603
/************************************************************************/
604
605
/**
606
 * Set name=value entry in the list.
607
 *
608
 * Similar to AddNameValue(), except if there is already a value for
609
 * the key in the list it is replaced instead of adding a new entry to
610
 * the list.  If pszValue is NULL any existing key entry is removed.
611
 *
612
 * @param pszKey the key name to add.
613
 * @param pszValue the key value to add.
614
 */
615
616
CPLStringList &CPLStringList::SetNameValue(const char *pszKey,
617
                                           const char *pszValue)
618
619
67.1M
{
620
67.1M
    int iKey = FindName(pszKey);
621
622
67.1M
    if (iKey == -1)
623
66.1M
        return AddNameValue(pszKey, pszValue);
624
625
1.01M
    Count();
626
1.01M
    if (!MakeOurOwnCopy())
627
0
        return *this;
628
629
1.01M
    CPLFree(papszList[iKey]);
630
1.01M
    if (pszValue == nullptr)  // delete entry
631
1.14k
    {
632
633
        // shift everything down by one.
634
1.14k
        do
635
1.37k
        {
636
1.37k
            papszList[iKey] = papszList[iKey + 1];
637
1.37k
        } while (papszList[iKey++] != nullptr);
638
639
1.14k
        nCount--;
640
1.14k
    }
641
1.01M
    else
642
1.01M
    {
643
1.01M
        if (strlen(pszKey) >
644
1.01M
                std::numeric_limits<size_t>::max() - strlen(pszValue) ||
645
1.01M
            strlen(pszKey) + strlen(pszValue) >
646
1.01M
                std::numeric_limits<size_t>::max() - 2)
647
0
        {
648
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
649
0
                     "Too big strings in AddNameValue()");
650
0
            return *this;
651
0
        }
652
1.01M
        const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
653
1.01M
        char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
654
1.01M
        if (pszLine == nullptr)
655
0
            return *this;
656
1.01M
        snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
657
658
1.01M
        papszList[iKey] = pszLine;
659
1.01M
    }
660
661
1.01M
    return *this;
662
1.01M
}
663
664
/************************************************************************/
665
/*                             SetString()                              */
666
/************************************************************************/
667
668
/**
669
 * Replace a string within the list.
670
 *
671
 * @param pos 0-index position of the string to replace
672
 * @param pszString value to be used (will be copied)
673
 * @return a reference to the CPLStringList on which it was invoked.
674
 * @since 3.13
675
 */
676
CPLStringList &CPLStringList::SetString(int pos, const char *pszString)
677
0
{
678
0
    return SetStringDirectly(pos, VSI_STRDUP_VERBOSE(pszString));
679
0
}
680
681
/**
682
 * Replace a string within the list.
683
 *
684
 * @param pos 0-index position of the string to replace
685
 * @param osString value to be used (will be copied)
686
 * @return a reference to the CPLStringList on which it was invoked.
687
 * @since 3.13
688
 */
689
CPLStringList &CPLStringList::SetString(int pos, const std::string &osString)
690
0
{
691
0
    return SetString(pos, osString.c_str());
692
0
}
693
694
/**
695
 * Replace a string within the list.
696
 *
697
 * @param pos 0-index position of the string to replace
698
 * @param pszString value to be used (ownership is taken)
699
 * @return a reference to the CPLStringList on which it was invoked.
700
 * @since 3.13
701
 */
702
CPLStringList &CPLStringList::SetStringDirectly(int pos, char *pszString)
703
0
{
704
0
    if (!MakeOurOwnCopy())
705
0
        return *this;
706
707
0
    CPLFree(papszList[pos]);
708
0
    papszList[pos] = pszString;
709
710
0
    if (bIsSorted)
711
0
    {
712
0
        if (pos > 0 &&
713
0
            CPLCompareKeyValueString(papszList[pos], papszList[pos - 1]) == -1)
714
0
        {
715
0
            bIsSorted = false;
716
0
        }
717
0
        if (pos < Count() - 1 &&
718
0
            CPLCompareKeyValueString(papszList[pos], papszList[pos + 1]) == 1)
719
0
        {
720
0
            bIsSorted = false;
721
0
        }
722
0
    }
723
724
0
    return *this;
725
0
}
726
727
/************************************************************************/
728
/*                              operator[]                              */
729
/************************************************************************/
730
731
/**
732
 * Fetch entry "i".
733
 *
734
 * Fetches the requested item in the list.  Note that the returned string
735
 * remains owned by the CPLStringList.  If "i" is out of range NULL is
736
 * returned.
737
 *
738
 * @param i the index of the list item to return.
739
 * @return selected entry in the list.
740
 */
741
char *CPLStringList::operator[](int i)
742
743
21.7M
{
744
21.7M
    if (nCount == -1)
745
0
        Count();
746
747
21.7M
    if (i < 0 || i >= nCount)
748
1.01k
        return nullptr;
749
750
21.7M
    return papszList[i];
751
21.7M
}
752
753
const char *CPLStringList::operator[](int i) const
754
755
27.8M
{
756
27.8M
    if (nCount == -1)
757
1.85k
        Count();
758
759
27.8M
    if (i < 0 || i >= nCount)
760
0
        return nullptr;
761
762
27.8M
    return papszList[i];
763
27.8M
}
764
765
/************************************************************************/
766
/*                             StealList()                              */
767
/************************************************************************/
768
769
/**
770
 * Seize ownership of underlying string array.
771
 *
772
 * This method is similar to List(), except that the returned list is
773
 * now owned by the caller and the CPLStringList is emptied.
774
 *
775
 * @return the C style string list.
776
 */
777
char **CPLStringList::StealList()
778
779
175M
{
780
175M
    char **papszRetList = papszList;
781
782
175M
    bOwnList = false;
783
175M
    papszList = nullptr;
784
175M
    nCount = 0;
785
175M
    nAllocation = 0;
786
787
175M
    return papszRetList;
788
175M
}
789
790
/* Case insensitive comparison function */
791
static int CPLCompareKeyValueString(const char *pszKVa, const char *pszKVb)
792
19.8M
{
793
19.8M
    const char *pszItera = pszKVa;
794
19.8M
    const char *pszIterb = pszKVb;
795
210M
    while (true)
796
210M
    {
797
210M
        char cha = *pszItera;
798
210M
        char chb = *pszIterb;
799
210M
        if (cha == '=' || cha == '\0')
800
872k
        {
801
872k
            if (chb == '=' || chb == '\0')
802
769k
                return 0;
803
103k
            else
804
103k
                return -1;
805
872k
        }
806
209M
        if (chb == '=' || chb == '\0')
807
77.4k
        {
808
77.4k
            return 1;
809
77.4k
        }
810
209M
        if (cha >= 'a' && cha <= 'z')
811
13.3M
            cha -= ('a' - 'A');
812
209M
        if (chb >= 'a' && chb <= 'z')
813
13.2M
            chb -= ('a' - 'A');
814
209M
        if (cha < chb)
815
9.91M
            return -1;
816
199M
        else if (cha > chb)
817
8.94M
            return 1;
818
190M
        pszItera++;
819
190M
        pszIterb++;
820
190M
    }
821
19.8M
}
822
823
/************************************************************************/
824
/*                                Sort()                                */
825
/************************************************************************/
826
827
/**
828
 * Sort the entries in the list and mark list sorted.
829
 *
830
 * Note that once put into "sorted" mode, the CPLStringList will attempt to
831
 * keep things in sorted order through calls to AddString(),
832
 * AddStringDirectly(), AddNameValue(), SetNameValue(). Complete list
833
 * assignments (via Assign() and operator= will clear the sorting state.
834
 * When in sorted order FindName(), FetchNameValue() and FetchNameValueDef()
835
 * will do a binary search to find the key, substantially improve lookup
836
 * performance in large lists.
837
 */
838
839
CPLStringList &CPLStringList::Sort()
840
841
585k
{
842
585k
    Count();
843
585k
    if (!MakeOurOwnCopy())
844
0
        return *this;
845
846
585k
    if (nCount > 1)
847
105k
    {
848
105k
        std::sort(papszList, papszList + nCount,
849
105k
                  [](const char *a, const char *b)
850
17.7M
                  { return CPLCompareKeyValueString(a, b) < 0; });
851
105k
    }
852
585k
    bIsSorted = true;
853
854
585k
    return *this;
855
585k
}
856
857
/************************************************************************/
858
/*                              FindName()                              */
859
/************************************************************************/
860
861
/**
862
 * Get index of given name/value keyword.
863
 *
864
 * Note that this search is for a line in the form name=value or name:value.
865
 * Use FindString() or PartialFindString() for searches not based on name=value
866
 * pairs.
867
 *
868
 * @param pszKey the name to search for.
869
 *
870
 * @return the string list index of this name, or -1 on failure.
871
 */
872
873
int CPLStringList::FindName(const char *pszKey) const
874
875
333M
{
876
333M
    if (!IsSorted())
877
332M
        return CSLFindName(papszList, pszKey);
878
879
    // If we are sorted, we can do an optimized binary search.
880
722k
    int iStart = 0;
881
722k
    int iEnd = nCount - 1;
882
722k
    size_t nKeyLen = strlen(pszKey);
883
884
2.17M
    while (iStart <= iEnd)
885
1.64M
    {
886
1.64M
        const int iMiddle = (iEnd + iStart) / 2;
887
1.64M
        const char *pszMiddle = papszList[iMiddle];
888
889
1.64M
        if (EQUALN(pszMiddle, pszKey, nKeyLen) &&
890
198k
            (pszMiddle[nKeyLen] == '=' || pszMiddle[nKeyLen] == ':'))
891
190k
            return iMiddle;
892
893
1.45M
        if (CPLCompareKeyValueString(pszKey, pszMiddle) < 0)
894
284k
            iEnd = iMiddle - 1;
895
1.17M
        else
896
1.17M
            iStart = iMiddle + 1;
897
1.45M
    }
898
899
531k
    return -1;
900
722k
}
901
902
/************************************************************************/
903
/*                             FetchBool()                              */
904
/************************************************************************/
905
/**
906
 *
907
 * Check for boolean key value.
908
 *
909
 * In a CPLStringList of "Name=Value" pairs, look to see if there is a key
910
 * with the given name, and if it can be interpreted as being TRUE.  If
911
 * the key appears without any "=Value" portion it will be considered true.
912
 * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
913
 * if the key appears in the list it will be considered TRUE.  If the key
914
 * doesn't appear at all, the indicated default value will be returned.
915
 *
916
 * @param pszKey the key value to look for (case insensitive).
917
 * @param bDefault the value to return if the key isn't found at all.
918
 *
919
 * @return true or false
920
 */
921
922
bool CPLStringList::FetchBool(const char *pszKey, bool bDefault) const
923
924
159k
{
925
159k
    const char *pszValue = FetchNameValue(pszKey);
926
927
159k
    if (pszValue == nullptr)
928
159k
        return bDefault;
929
930
164
    return CPLTestBool(pszValue);
931
159k
}
932
933
/************************************************************************/
934
/*                            FetchBoolean()                            */
935
/************************************************************************/
936
/**
937
 *
938
 * DEPRECATED: Check for boolean key value.
939
 *
940
 * In a CPLStringList of "Name=Value" pairs, look to see if there is a key
941
 * with the given name, and if it can be interpreted as being TRUE.  If
942
 * the key appears without any "=Value" portion it will be considered true.
943
 * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
944
 * if the key appears in the list it will be considered TRUE.  If the key
945
 * doesn't appear at all, the indicated default value will be returned.
946
 *
947
 * @param pszKey the key value to look for (case insensitive).
948
 * @param bDefault the value to return if the key isn't found at all.
949
 *
950
 * @return TRUE or FALSE
951
 */
952
953
int CPLStringList::FetchBoolean(const char *pszKey, int bDefault) const
954
955
154k
{
956
154k
    return FetchBool(pszKey, CPL_TO_BOOL(bDefault)) ? TRUE : FALSE;
957
154k
}
958
959
/************************************************************************/
960
/*                           FetchNameValue()                           */
961
/************************************************************************/
962
963
/**
964
 * Fetch value associated with this key name.
965
 *
966
 * If this list sorted, a fast binary search is done, otherwise a linear
967
 * scan is done.  Name lookup is case insensitive.
968
 *
969
 * @param pszName the key name to search for.
970
 *
971
 * @return the corresponding value or NULL if not found.  The returned string
972
 * should not be modified and points into internal object state that may
973
 * change on future calls.
974
 */
975
976
const char *CPLStringList::FetchNameValue(const char *pszName) const
977
978
266M
{
979
266M
    const int iKey = FindName(pszName);
980
981
266M
    if (iKey == -1)
982
62.3M
        return nullptr;
983
984
204M
    CPLAssert(papszList[iKey][strlen(pszName)] == '=' ||
985
204M
              papszList[iKey][strlen(pszName)] == ':');
986
987
204M
    return papszList[iKey] + strlen(pszName) + 1;
988
266M
}
989
990
/************************************************************************/
991
/*                         FetchNameValueDef()                          */
992
/************************************************************************/
993
994
/**
995
 * Fetch value associated with this key name.
996
 *
997
 * If this list sorted, a fast binary search is done, otherwise a linear
998
 * scan is done.  Name lookup is case insensitive.
999
 *
1000
 * @param pszName the key name to search for.
1001
 * @param pszDefault the default value returned if the named entry isn't found.
1002
 *
1003
 * @return the corresponding value or the passed default if not found.
1004
 */
1005
1006
const char *CPLStringList::FetchNameValueDef(const char *pszName,
1007
                                             const char *pszDefault) const
1008
1009
1.16M
{
1010
1.16M
    const char *pszValue = FetchNameValue(pszName);
1011
1.16M
    if (pszValue == nullptr)
1012
938k
        return pszDefault;
1013
1014
229k
    return pszValue;
1015
1.16M
}
1016
1017
/************************************************************************/
1018
/*                            InsertString()                            */
1019
/************************************************************************/
1020
1021
/**
1022
 * \fn CPLStringList *CPLStringList::InsertString( int nInsertAtLineNo,
1023
 *                                                 const char *pszNewLine );
1024
 *
1025
 * \brief Insert into the list at identified location.
1026
 *
1027
 * This method will insert a string into the list at the identified
1028
 * location.  The insertion point must be within or at the end of the list.
1029
 * The following entries are pushed down to make space.
1030
 *
1031
 * @param nInsertAtLineNo the line to insert at, zero to insert at front.
1032
 * @param pszNewLine to the line to insert.  This string will be copied.
1033
 */
1034
1035
/************************************************************************/
1036
/*                        InsertStringDirectly()                        */
1037
/************************************************************************/
1038
1039
/**
1040
 * Insert into the list at identified location.
1041
 *
1042
 * This method will insert a string into the list at the identified
1043
 * location.  The insertion point must be within or at the end of the list.
1044
 * The following entries are pushed down to make space.
1045
 *
1046
 * @param nInsertAtLineNo the line to insert at, zero to insert at front.
1047
 * @param pszNewLine to the line to insert, the ownership of this string
1048
 * will be taken over by the object.  It must have been allocated on the
1049
 * heap.
1050
 */
1051
1052
CPLStringList &CPLStringList::InsertStringDirectly(int nInsertAtLineNo,
1053
                                                   char *pszNewLine)
1054
1055
322k
{
1056
322k
    if (nCount == -1)
1057
0
        Count();
1058
1059
322k
    if (!EnsureAllocation(nCount + 1))
1060
0
    {
1061
0
        VSIFree(pszNewLine);
1062
0
        return *this;
1063
0
    }
1064
1065
322k
    if (nInsertAtLineNo < 0 || nInsertAtLineNo > nCount)
1066
0
    {
1067
0
        CPLError(CE_Failure, CPLE_AppDefined,
1068
0
                 "CPLStringList::InsertString() requested beyond list end.");
1069
0
        return *this;
1070
0
    }
1071
1072
322k
    bIsSorted = false;
1073
1074
830k
    for (int i = nCount; i > nInsertAtLineNo; i--)
1075
508k
        papszList[i] = papszList[i - 1];
1076
1077
322k
    papszList[nInsertAtLineNo] = pszNewLine;
1078
322k
    papszList[++nCount] = nullptr;
1079
1080
322k
    return *this;
1081
322k
}
1082
1083
/************************************************************************/
1084
/*                           RemoveStrings()                            */
1085
/************************************************************************/
1086
1087
/**
1088
 * Remove strings inside a CPLStringList.
1089
 *
1090
 * @param nFirstLineToDelete the 0-based index of the first string to
1091
 * remove. If this value is -1 or is larger than the actual
1092
 * number of strings in list then the nNumToRemove last strings are
1093
 * removed.
1094
 * @param nNumToRemove the number of strings to remove
1095
 *
1096
 * @return a reference to the CPLStringList on which it was invoked.
1097
 * @since 3.13
1098
 */
1099
CPLStringList &CPLStringList::RemoveStrings(int nFirstLineToDelete,
1100
                                            int nNumToRemove)
1101
0
{
1102
0
    if (!MakeOurOwnCopy())
1103
0
        return *this;
1104
1105
0
    papszList =
1106
0
        CSLRemoveStrings(papszList, nFirstLineToDelete, nNumToRemove, nullptr);
1107
0
    nCount = -1;
1108
0
    return *this;
1109
0
}
1110
1111
/************************************************************************/
1112
/*                      FindSortedInsertionPoint()                      */
1113
/*                                                                      */
1114
/*      Find the location at which the indicated line should be         */
1115
/*      inserted in order to keep things in sorted order.               */
1116
/************************************************************************/
1117
1118
int CPLStringList::FindSortedInsertionPoint(const char *pszLine)
1119
1120
321k
{
1121
321k
    CPLAssert(IsSorted());
1122
1123
321k
    int iStart = 0;
1124
321k
    int iEnd = nCount - 1;
1125
1126
944k
    while (iStart <= iEnd)
1127
623k
    {
1128
623k
        const int iMiddle = (iEnd + iStart) / 2;
1129
623k
        const char *pszMiddle = papszList[iMiddle];
1130
1131
623k
        if (CPLCompareKeyValueString(pszLine, pszMiddle) < 0)
1132
95.3k
            iEnd = iMiddle - 1;
1133
528k
        else
1134
528k
            iStart = iMiddle + 1;
1135
623k
    }
1136
1137
321k
    iEnd++;
1138
321k
    CPLAssert(iEnd >= 0 && iEnd <= nCount);
1139
321k
    CPLAssert(iEnd == 0 ||
1140
321k
              CPLCompareKeyValueString(pszLine, papszList[iEnd - 1]) >= 0);
1141
321k
    CPLAssert(iEnd == nCount ||
1142
321k
              CPLCompareKeyValueString(pszLine, papszList[iEnd]) <= 0);
1143
1144
321k
    return iEnd;
1145
321k
}
1146
1147
namespace cpl
1148
{
1149
1150
/************************************************************************/
1151
/*          CSLIterator::operator==(const CSLIterator &other)           */
1152
/************************************************************************/
1153
1154
/*! @cond Doxygen_Suppress */
1155
bool CSLIterator::operator==(const CSLIterator &other) const
1156
69.3M
{
1157
69.3M
    if (!m_bAtEnd && other.m_bAtEnd)
1158
69.3M
    {
1159
69.3M
        return m_papszList == nullptr || *m_papszList == nullptr;
1160
69.3M
    }
1161
0
    if (!m_bAtEnd && !other.m_bAtEnd)
1162
0
    {
1163
0
        return m_papszList == other.m_papszList;
1164
0
    }
1165
0
    if (m_bAtEnd && other.m_bAtEnd)
1166
0
    {
1167
0
        return true;
1168
0
    }
1169
0
    return false;
1170
0
}
1171
1172
/*! @endcond */
1173
1174
/************************************************************************/
1175
/*                  CSLNameValueIterator::operator*()                   */
1176
/************************************************************************/
1177
1178
/*! @cond Doxygen_Suppress */
1179
CSLNameValueIterator::value_type CSLNameValueIterator::operator*()
1180
43.8k
{
1181
43.8k
    if (m_papszList)
1182
43.8k
    {
1183
56.7k
        while (*m_papszList)
1184
56.7k
        {
1185
56.7k
            char *pszKey = nullptr;
1186
56.7k
            const char *pszValue = CPLParseNameValue(*m_papszList, &pszKey);
1187
56.7k
            if (pszKey)
1188
43.8k
            {
1189
43.8k
                m_osKey = pszKey;
1190
43.8k
                CPLFree(pszKey);
1191
43.8k
                return {m_osKey.c_str(), pszValue};
1192
43.8k
            }
1193
12.9k
            else if (m_bReturnNullKeyIfNotNameValue)
1194
8
            {
1195
8
                return {nullptr, *m_papszList};
1196
8
            }
1197
            // Skip entries that are not name=value pairs.
1198
12.8k
            ++m_papszList;
1199
12.8k
        }
1200
43.8k
    }
1201
    // Should not happen
1202
0
    CPLAssert(false);
1203
0
    return {"", ""};
1204
43.8k
}
1205
1206
/*! @endcond */
1207
1208
/************************************************************************/
1209
/*                  CSLNameValueIteratorWrapper::end()                  */
1210
/************************************************************************/
1211
1212
/*! @cond Doxygen_Suppress */
1213
CSLNameValueIterator CSLNameValueIteratorWrapper::end() const
1214
12.7k
{
1215
12.7k
    int nCount = CSLCount(m_papszList);
1216
12.7k
    if (!m_bReturnNullKeyIfNotNameValue)
1217
12.7k
    {
1218
21.2k
        while (nCount > 0 && strchr(m_papszList[nCount - 1], '=') == nullptr)
1219
8.44k
            --nCount;
1220
12.7k
    }
1221
12.7k
    return CSLNameValueIterator{m_papszList + nCount,
1222
12.7k
                                m_bReturnNullKeyIfNotNameValue};
1223
12.7k
}
1224
1225
/*! @endcond */
1226
1227
}  // namespace cpl