Coverage Report

Created: 2026-07-25 06:50

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
766
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
378
    : CPLStringList()
51
52
378
{
53
378
    Assign(papszListIn, bTakeOwnership);
54
378
}
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
0
CPLStringList::CPLStringList(CSLConstList papszListIn) : CPLStringList()
69
70
0
{
71
0
    Assign(CSLDuplicate(papszListIn));
72
0
}
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
0
{
89
0
    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
0
}
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
0
CPLStringList::CPLStringList(const CPLStringList &oOther) : CPLStringList()
129
130
0
{
131
0
    operator=(oOther);
132
0
}
133
134
/************************************************************************/
135
/*                           CPLStringList()                            */
136
/************************************************************************/
137
138
//! Move constructor
139
0
CPLStringList::CPLStringList(CPLStringList &&oOther) : CPLStringList()
140
141
0
{
142
0
    operator=(std::move(oOther));
143
0
}
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
0
{
172
0
    if (this != &oOther)
173
0
    {
174
0
        char **l_papszList = CSLDuplicate(oOther.papszList);
175
0
        if (l_papszList)
176
0
        {
177
0
            Assign(l_papszList, TRUE);
178
0
            nAllocation = oOther.nCount > 0 ? oOther.nCount + 1 : 0;
179
0
            nCount = oOther.nCount;
180
0
            bIsSorted = oOther.bIsSorted;
181
0
        }
182
0
    }
183
184
0
    return *this;
185
0
}
186
187
/************************************************************************/
188
/*                             operator=()                              */
189
/************************************************************************/
190
191
CPLStringList &CPLStringList::operator=(CPLStringList &&oOther)
192
0
{
193
0
    if (this != &oOther)
194
0
    {
195
0
        Clear();
196
0
        papszList = oOther.papszList;
197
0
        oOther.papszList = nullptr;
198
0
        nCount = oOther.nCount;
199
0
        oOther.nCount = 0;
200
0
        nAllocation = oOther.nAllocation;
201
0
        oOther.nAllocation = 0;
202
0
        bOwnList = oOther.bOwnList;
203
0
        oOther.bOwnList = false;
204
0
        bIsSorted = oOther.bIsSorted;
205
0
        oOther.bIsSorted = true;
206
0
    }
207
208
0
    return *this;
209
0
}
210
211
/************************************************************************/
212
/*                             operator=()                              */
213
/************************************************************************/
214
215
CPLStringList &CPLStringList::operator=(CSLConstList papszListIn)
216
0
{
217
0
    if (papszListIn != papszList)
218
0
    {
219
0
        Assign(CSLDuplicate(papszListIn));
220
0
        bIsSorted = false;
221
0
    }
222
223
0
    return *this;
224
0
}
225
226
/************************************************************************/
227
/*                           ~CPLStringList()                           */
228
/************************************************************************/
229
230
CPLStringList::~CPLStringList()
231
232
756
{
233
756
    Clear();
234
756
}
235
236
/************************************************************************/
237
/*                               Clear()                                */
238
/************************************************************************/
239
240
/**
241
 * Clear the string list.
242
 */
243
CPLStringList &CPLStringList::Clear()
244
245
1.91k
{
246
1.91k
    if (bOwnList)
247
754
    {
248
754
        CSLDestroy(papszList);
249
754
        papszList = nullptr;
250
251
754
        bOwnList = FALSE;
252
754
        nAllocation = 0;
253
754
        nCount = 0;
254
754
    }
255
256
1.91k
    return *this;
257
1.91k
}
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
1.16k
{
277
1.16k
    Clear();
278
279
1.16k
    papszList = papszListIn;
280
1.16k
    bOwnList = CPL_TO_BOOL(bTakeOwnership);
281
282
1.16k
    if (papszList == nullptr || *papszList == nullptr)
283
87
        nCount = 0;
284
1.07k
    else
285
1.07k
        nCount = -1;  // unknown
286
287
1.16k
    nAllocation = 0;
288
1.16k
    bIsSorted = FALSE;
289
290
1.16k
    return *this;
291
1.16k
}
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
0
{
304
0
    if (nCount == -1)
305
0
    {
306
0
        if (papszList == nullptr)
307
0
        {
308
0
            nCount = 0;
309
0
            nAllocation = 0;
310
0
        }
311
0
        else
312
0
        {
313
0
            nCount = CSLCount(papszList);
314
0
            nAllocation = std::max(nCount + 1, nAllocation);
315
0
        }
316
0
    }
317
318
0
    return nCount;
319
0
}
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
349
{
331
349
    if (bOwnList)
332
0
        return true;
333
334
349
    if (papszList == nullptr)
335
349
        return true;
336
337
0
    Count();
338
0
    char **papszListNew = CSLDuplicate(papszList);
339
0
    if (papszListNew == nullptr)
340
0
    {
341
0
        return false;
342
0
    }
343
0
    papszList = papszListNew;
344
0
    bOwnList = true;
345
0
    nAllocation = nCount + 1;
346
0
    return true;
347
0
}
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
18.5k
{
360
18.5k
    if (!bOwnList)
361
349
    {
362
349
        if (!MakeOurOwnCopy())
363
0
            return false;
364
349
    }
365
366
18.5k
    if (papszList == nullptr || nAllocation <= nMaxList)
367
442
    {
368
        // we need to be able to store nMaxList+1 as an int,
369
        // and allocate (nMaxList+1) * sizeof(char*) bytes
370
442
        if (nMaxList < 0 || nMaxList > std::numeric_limits<int>::max() - 1 ||
371
442
            static_cast<size_t>(nMaxList) >
372
442
                std::numeric_limits<size_t>::max() / sizeof(char *) - 1)
373
0
        {
374
0
            return false;
375
0
        }
376
442
        int nNewAllocation = nMaxList + 1;
377
442
        if (nNewAllocation <= (std::numeric_limits<int>::max() - 20) / 2 /
378
442
                                  static_cast<int>(sizeof(char *)))
379
442
            nNewAllocation = std::max(nNewAllocation * 2 + 20, nMaxList + 1);
380
442
        if (papszList == nullptr)
381
349
        {
382
349
            papszList = static_cast<char **>(
383
349
                VSI_CALLOC_VERBOSE(nNewAllocation, sizeof(char *)));
384
349
            bOwnList = true;
385
349
            nCount = 0;
386
349
            if (papszList == nullptr)
387
0
                return false;
388
349
        }
389
93
        else
390
93
        {
391
93
            char **papszListNew = static_cast<char **>(VSI_REALLOC_VERBOSE(
392
93
                papszList, nNewAllocation * sizeof(char *)));
393
93
            if (papszListNew == nullptr)
394
0
                return false;
395
93
            papszList = papszListNew;
396
93
        }
397
442
        nAllocation = nNewAllocation;
398
442
    }
399
18.5k
    return true;
400
18.5k
}
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
18.5k
{
418
18.5k
    if (nCount == -1)
419
0
        Count();
420
421
18.5k
    if (!EnsureAllocation(nCount + 1))
422
0
    {
423
0
        VSIFree(pszNewString);
424
0
        return *this;
425
0
    }
426
427
18.5k
    papszList[nCount++] = pszNewString;
428
18.5k
    papszList[nCount] = nullptr;
429
430
18.5k
    bIsSorted = false;
431
432
18.5k
    return *this;
433
18.5k
}
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
0
{
450
0
    char *pszDupString = VSI_STRDUP_VERBOSE(pszNewString);
451
0
    if (pszDupString == nullptr)
452
0
        return *this;
453
0
    return AddStringDirectly(pszDupString);
454
0
}
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
0
{
470
0
    return AddString(newString.c_str());
471
0
}
472
473
/************************************************************************/
474
/*                             AddString()                              */
475
/************************************************************************/
476
/**
477
 * Add a string to the list.
478
 *
479
 * A copy of the passed in string_view is made and inserted in the list.
480
 *
481
 * @param newString the string to add to the list.
482
 * @return a reference to the CPLStringList on which it was invoked.
483
 */
484
485
CPLStringList &CPLStringList::AddString(std::string_view newString)
486
18.5k
{
487
18.5k
    char *pszDupString =
488
18.5k
        static_cast<char *>(VSI_MALLOC_VERBOSE(newString.size() + 1));
489
18.5k
    if (pszDupString == nullptr)
490
0
    {
491
0
        return *this;
492
0
    }
493
18.5k
    std::memcpy(pszDupString, newString.data(), newString.size());
494
18.5k
    pszDupString[newString.size()] = '\0';
495
496
18.5k
    return AddStringDirectly(pszDupString);
497
18.5k
}
498
499
/************************************************************************/
500
/*                             push_back()                              */
501
/************************************************************************/
502
503
/**
504
 * Add a string to the list.
505
 *
506
 * A copy of the passed in string is made and inserted in the list.
507
 *
508
 * @param svStr the string to add to the list.
509
 *
510
 * @since 3.13
511
 */
512
513
void CPLStringList::push_back(std::string_view svStr)
514
515
0
{
516
0
    char *pszDupString =
517
0
        static_cast<char *>(VSI_MALLOC_VERBOSE(svStr.size() + 1));
518
0
    if (pszDupString == nullptr)
519
0
        return;
520
0
    memcpy(pszDupString, svStr.data(), svStr.size());
521
0
    pszDupString[svStr.size()] = 0;
522
0
    CPL_IGNORE_RET_VAL(AddStringDirectly(pszDupString));
523
0
}
524
525
/************************************************************************/
526
/*                            AddNameValue()                            */
527
/************************************************************************/
528
529
/**
530
 * Add a name=value entry to the list.
531
 *
532
 * A key=value string is prepared and appended to the list.  There is no
533
 * check for other values for the same key in the list.
534
 *
535
 * @param pszKey the key name to add.
536
 * @param pszValue the key value to add.
537
 */
538
539
CPLStringList &CPLStringList::AddNameValue(const char *pszKey,
540
                                           const char *pszValue)
541
542
0
{
543
0
    if (pszKey == nullptr || pszValue == nullptr)
544
0
        return *this;
545
546
0
    if (!MakeOurOwnCopy())
547
0
        return *this;
548
549
    /* -------------------------------------------------------------------- */
550
    /*      Format the line.                                                */
551
    /* -------------------------------------------------------------------- */
552
0
    if (strlen(pszKey) >
553
0
            std::numeric_limits<size_t>::max() - strlen(pszValue) ||
554
0
        strlen(pszKey) + strlen(pszValue) >
555
0
            std::numeric_limits<size_t>::max() - 2)
556
0
    {
557
0
        CPLError(CE_Failure, CPLE_OutOfMemory,
558
0
                 "Too big strings in AddNameValue()");
559
0
        return *this;
560
0
    }
561
0
    const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
562
0
    char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
563
0
    if (pszLine == nullptr)
564
0
        return *this;
565
0
    snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
566
567
    /* -------------------------------------------------------------------- */
568
    /*      If we don't need to keep the sort order things are pretty       */
569
    /*      straight forward.                                               */
570
    /* -------------------------------------------------------------------- */
571
0
    if (!IsSorted())
572
0
        return AddStringDirectly(pszLine);
573
574
    /* -------------------------------------------------------------------- */
575
    /*      Find the proper insertion point.                                */
576
    /* -------------------------------------------------------------------- */
577
0
    CPLAssert(IsSorted());
578
0
    const int iKey = FindSortedInsertionPoint(pszLine);
579
0
    InsertStringDirectly(iKey, pszLine);
580
0
    bIsSorted = true;  // We have actually preserved sort order.
581
582
0
    return *this;
583
0
}
584
585
/************************************************************************/
586
/*                            SetNameValue()                            */
587
/************************************************************************/
588
589
/**
590
 * Set name=value entry in the list.
591
 *
592
 * Similar to AddNameValue(), except if there is already a value for
593
 * the key in the list it is replaced instead of adding a new entry to
594
 * the list.  If pszValue is NULL any existing key entry is removed.
595
 *
596
 * @param pszKey the key name to add.
597
 * @param pszValue the key value to add.
598
 */
599
600
CPLStringList &CPLStringList::SetNameValue(const char *pszKey,
601
                                           const char *pszValue)
602
603
0
{
604
0
    int iKey = FindName(pszKey);
605
606
0
    if (iKey == -1)
607
0
        return AddNameValue(pszKey, pszValue);
608
609
0
    Count();
610
0
    if (!MakeOurOwnCopy())
611
0
        return *this;
612
613
0
    CPLFree(papszList[iKey]);
614
0
    if (pszValue == nullptr)  // delete entry
615
0
    {
616
617
        // shift everything down by one.
618
0
        do
619
0
        {
620
0
            papszList[iKey] = papszList[iKey + 1];
621
0
        } while (papszList[iKey++] != nullptr);
622
623
0
        nCount--;
624
0
    }
625
0
    else
626
0
    {
627
0
        if (strlen(pszKey) >
628
0
                std::numeric_limits<size_t>::max() - strlen(pszValue) ||
629
0
            strlen(pszKey) + strlen(pszValue) >
630
0
                std::numeric_limits<size_t>::max() - 2)
631
0
        {
632
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
633
0
                     "Too big strings in AddNameValue()");
634
0
            return *this;
635
0
        }
636
0
        const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
637
0
        char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
638
0
        if (pszLine == nullptr)
639
0
            return *this;
640
0
        snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
641
642
0
        papszList[iKey] = pszLine;
643
0
    }
644
645
0
    return *this;
646
0
}
647
648
/************************************************************************/
649
/*                             SetString()                              */
650
/************************************************************************/
651
652
/**
653
 * Replace a string within the list.
654
 *
655
 * @param pos 0-index position of the string to replace
656
 * @param pszString value to be used (will be copied)
657
 * @return a reference to the CPLStringList on which it was invoked.
658
 * @since 3.13
659
 */
660
CPLStringList &CPLStringList::SetString(int pos, const char *pszString)
661
0
{
662
0
    return SetStringDirectly(pos, VSI_STRDUP_VERBOSE(pszString));
663
0
}
664
665
/**
666
 * Replace a string within the list.
667
 *
668
 * @param pos 0-index position of the string to replace
669
 * @param osString value to be used (will be copied)
670
 * @return a reference to the CPLStringList on which it was invoked.
671
 * @since 3.13
672
 */
673
CPLStringList &CPLStringList::SetString(int pos, const std::string &osString)
674
0
{
675
0
    return SetString(pos, osString.c_str());
676
0
}
677
678
/**
679
 * Replace a string within the list.
680
 *
681
 * @param pos 0-index position of the string to replace
682
 * @param pszString value to be used (ownership is taken)
683
 * @return a reference to the CPLStringList on which it was invoked.
684
 * @since 3.13
685
 */
686
CPLStringList &CPLStringList::SetStringDirectly(int pos, char *pszString)
687
0
{
688
0
    if (!MakeOurOwnCopy())
689
0
        return *this;
690
691
0
    CPLFree(papszList[pos]);
692
0
    papszList[pos] = pszString;
693
694
0
    if (bIsSorted)
695
0
    {
696
0
        if (pos > 0 &&
697
0
            CPLCompareKeyValueString(papszList[pos], papszList[pos - 1]) == -1)
698
0
        {
699
0
            bIsSorted = false;
700
0
        }
701
0
        if (pos < Count() - 1 &&
702
0
            CPLCompareKeyValueString(papszList[pos], papszList[pos + 1]) == 1)
703
0
        {
704
0
            bIsSorted = false;
705
0
        }
706
0
    }
707
708
0
    return *this;
709
0
}
710
711
/************************************************************************/
712
/*                              operator[]                              */
713
/************************************************************************/
714
715
/**
716
 * Fetch entry "i".
717
 *
718
 * Fetches the requested item in the list.  Note that the returned string
719
 * remains owned by the CPLStringList.  If "i" is out of range NULL is
720
 * returned.
721
 *
722
 * @param i the index of the list item to return.
723
 * @return selected entry in the list.
724
 */
725
char *CPLStringList::operator[](int i)
726
727
0
{
728
0
    if (nCount == -1)
729
0
        Count();
730
731
0
    if (i < 0 || i >= nCount)
732
0
        return nullptr;
733
734
0
    return papszList[i];
735
0
}
736
737
const char *CPLStringList::operator[](int i) const
738
739
0
{
740
0
    if (nCount == -1)
741
0
        Count();
742
743
0
    if (i < 0 || i >= nCount)
744
0
        return nullptr;
745
746
0
    return papszList[i];
747
0
}
748
749
/************************************************************************/
750
/*                             StealList()                              */
751
/************************************************************************/
752
753
/**
754
 * Seize ownership of underlying string array.
755
 *
756
 * This method is similar to List(), except that the returned list is
757
 * now owned by the caller and the CPLStringList is emptied.
758
 *
759
 * @return the C style string list.
760
 */
761
char **CPLStringList::StealList()
762
763
756
{
764
756
    char **papszRetList = papszList;
765
766
756
    bOwnList = false;
767
756
    papszList = nullptr;
768
756
    nCount = 0;
769
756
    nAllocation = 0;
770
771
756
    return papszRetList;
772
756
}
773
774
/* Case insensitive comparison function */
775
static int CPLCompareKeyValueString(const char *pszKVa, const char *pszKVb)
776
0
{
777
0
    const char *pszItera = pszKVa;
778
0
    const char *pszIterb = pszKVb;
779
0
    while (true)
780
0
    {
781
0
        char cha = *pszItera;
782
0
        char chb = *pszIterb;
783
0
        if (cha == '=' || cha == '\0')
784
0
        {
785
0
            if (chb == '=' || chb == '\0')
786
0
                return 0;
787
0
            else
788
0
                return -1;
789
0
        }
790
0
        if (chb == '=' || chb == '\0')
791
0
        {
792
0
            return 1;
793
0
        }
794
0
        if (cha >= 'a' && cha <= 'z')
795
0
            cha -= ('a' - 'A');
796
0
        if (chb >= 'a' && chb <= 'z')
797
0
            chb -= ('a' - 'A');
798
0
        if (cha < chb)
799
0
            return -1;
800
0
        else if (cha > chb)
801
0
            return 1;
802
0
        pszItera++;
803
0
        pszIterb++;
804
0
    }
805
0
}
806
807
/************************************************************************/
808
/*                                Sort()                                */
809
/************************************************************************/
810
811
/**
812
 * Sort the entries in the list and mark list sorted.
813
 *
814
 * Note that once put into "sorted" mode, the CPLStringList will attempt to
815
 * keep things in sorted order through calls to AddString(),
816
 * AddStringDirectly(), AddNameValue(), SetNameValue(). Complete list
817
 * assignments (via Assign() and operator= will clear the sorting state.
818
 * When in sorted order FindName(), FetchNameValue() and FetchNameValueDef()
819
 * will do a binary search to find the key, substantially improve lookup
820
 * performance in large lists.
821
 */
822
823
CPLStringList &CPLStringList::Sort()
824
825
0
{
826
0
    Count();
827
0
    if (!MakeOurOwnCopy())
828
0
        return *this;
829
830
0
    if (nCount > 1)
831
0
    {
832
0
        std::sort(papszList, papszList + nCount,
833
0
                  [](const char *a, const char *b)
834
0
                  { return CPLCompareKeyValueString(a, b) < 0; });
835
0
    }
836
0
    bIsSorted = true;
837
838
0
    return *this;
839
0
}
840
841
/************************************************************************/
842
/*                              FindName()                              */
843
/************************************************************************/
844
845
/**
846
 * Get index of given name/value keyword.
847
 *
848
 * Note that this search is for a line in the form name=value or name:value.
849
 * Use FindString() or PartialFindString() for searches not based on name=value
850
 * pairs.
851
 *
852
 * @param pszKey the name to search for.
853
 *
854
 * @return the string list index of this name, or -1 on failure.
855
 */
856
857
int CPLStringList::FindName(const char *pszKey) const
858
859
0
{
860
0
    if (!IsSorted())
861
0
        return CSLFindName(papszList, pszKey);
862
863
    // If we are sorted, we can do an optimized binary search.
864
0
    int iStart = 0;
865
0
    int iEnd = nCount - 1;
866
0
    size_t nKeyLen = strlen(pszKey);
867
868
0
    while (iStart <= iEnd)
869
0
    {
870
0
        const int iMiddle = (iEnd + iStart) / 2;
871
0
        const char *pszMiddle = papszList[iMiddle];
872
873
0
        if (EQUALN(pszMiddle, pszKey, nKeyLen) &&
874
0
            (pszMiddle[nKeyLen] == '=' || pszMiddle[nKeyLen] == ':'))
875
0
            return iMiddle;
876
877
0
        if (CPLCompareKeyValueString(pszKey, pszMiddle) < 0)
878
0
            iEnd = iMiddle - 1;
879
0
        else
880
0
            iStart = iMiddle + 1;
881
0
    }
882
883
0
    return -1;
884
0
}
885
886
/************************************************************************/
887
/*                             FetchBool()                              */
888
/************************************************************************/
889
/**
890
 *
891
 * Check for boolean key value.
892
 *
893
 * In a CPLStringList of "Name=Value" pairs, look to see if there is a key
894
 * with the given name, and if it can be interpreted as being TRUE.  If
895
 * the key appears without any "=Value" portion it will be considered true.
896
 * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
897
 * if the key appears in the list it will be considered TRUE.  If the key
898
 * doesn't appear at all, the indicated default value will be returned.
899
 *
900
 * @param pszKey the key value to look for (case insensitive).
901
 * @param bDefault the value to return if the key isn't found at all.
902
 *
903
 * @return true or false
904
 */
905
906
bool CPLStringList::FetchBool(const char *pszKey, bool bDefault) const
907
908
0
{
909
0
    const char *pszValue = FetchNameValue(pszKey);
910
911
0
    if (pszValue == nullptr)
912
0
        return bDefault;
913
914
0
    return CPLTestBool(pszValue);
915
0
}
916
917
/************************************************************************/
918
/*                            FetchBoolean()                            */
919
/************************************************************************/
920
/**
921
 *
922
 * DEPRECATED: Check for boolean key value.
923
 *
924
 * In a CPLStringList of "Name=Value" pairs, look to see if there is a key
925
 * with the given name, and if it can be interpreted as being TRUE.  If
926
 * the key appears without any "=Value" portion it will be considered true.
927
 * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
928
 * if the key appears in the list it will be considered TRUE.  If the key
929
 * doesn't appear at all, the indicated default value will be returned.
930
 *
931
 * @param pszKey the key value to look for (case insensitive).
932
 * @param bDefault the value to return if the key isn't found at all.
933
 *
934
 * @return TRUE or FALSE
935
 */
936
937
int CPLStringList::FetchBoolean(const char *pszKey, int bDefault) const
938
939
0
{
940
0
    return FetchBool(pszKey, CPL_TO_BOOL(bDefault)) ? TRUE : FALSE;
941
0
}
942
943
/************************************************************************/
944
/*                           FetchNameValue()                           */
945
/************************************************************************/
946
947
/**
948
 * Fetch value associated with this key name.
949
 *
950
 * If this list sorted, a fast binary search is done, otherwise a linear
951
 * scan is done.  Name lookup is case insensitive.
952
 *
953
 * @param pszName the key name to search for.
954
 *
955
 * @return the corresponding value or NULL if not found.  The returned string
956
 * should not be modified and points into internal object state that may
957
 * change on future calls.
958
 */
959
960
const char *CPLStringList::FetchNameValue(const char *pszName) const
961
962
0
{
963
0
    const int iKey = FindName(pszName);
964
965
0
    if (iKey == -1)
966
0
        return nullptr;
967
968
0
    CPLAssert(papszList[iKey][strlen(pszName)] == '=' ||
969
0
              papszList[iKey][strlen(pszName)] == ':');
970
971
0
    return papszList[iKey] + strlen(pszName) + 1;
972
0
}
973
974
/************************************************************************/
975
/*                         FetchNameValueDef()                          */
976
/************************************************************************/
977
978
/**
979
 * Fetch value associated with this key name.
980
 *
981
 * If this list sorted, a fast binary search is done, otherwise a linear
982
 * scan is done.  Name lookup is case insensitive.
983
 *
984
 * @param pszName the key name to search for.
985
 * @param pszDefault the default value returned if the named entry isn't found.
986
 *
987
 * @return the corresponding value or the passed default if not found.
988
 */
989
990
const char *CPLStringList::FetchNameValueDef(const char *pszName,
991
                                             const char *pszDefault) const
992
993
0
{
994
0
    const char *pszValue = FetchNameValue(pszName);
995
0
    if (pszValue == nullptr)
996
0
        return pszDefault;
997
998
0
    return pszValue;
999
0
}
1000
1001
/************************************************************************/
1002
/*                            InsertString()                            */
1003
/************************************************************************/
1004
1005
/**
1006
 * \fn CPLStringList *CPLStringList::InsertString( int nInsertAtLineNo,
1007
 *                                                 const char *pszNewLine );
1008
 *
1009
 * \brief Insert into the list at identified location.
1010
 *
1011
 * This method will insert a string into the list at the identified
1012
 * location.  The insertion point must be within or at the end of the list.
1013
 * The following entries are pushed down to make space.
1014
 *
1015
 * @param nInsertAtLineNo the line to insert at, zero to insert at front.
1016
 * @param pszNewLine to the line to insert.  This string will be copied.
1017
 */
1018
1019
/************************************************************************/
1020
/*                        InsertStringDirectly()                        */
1021
/************************************************************************/
1022
1023
/**
1024
 * Insert into the list at identified location.
1025
 *
1026
 * This method will insert a string into the list at the identified
1027
 * location.  The insertion point must be within or at the end of the list.
1028
 * The following entries are pushed down to make space.
1029
 *
1030
 * @param nInsertAtLineNo the line to insert at, zero to insert at front.
1031
 * @param pszNewLine to the line to insert, the ownership of this string
1032
 * will be taken over the by the object.  It must have been allocated on the
1033
 * heap.
1034
 */
1035
1036
CPLStringList &CPLStringList::InsertStringDirectly(int nInsertAtLineNo,
1037
                                                   char *pszNewLine)
1038
1039
0
{
1040
0
    if (nCount == -1)
1041
0
        Count();
1042
1043
0
    if (!EnsureAllocation(nCount + 1))
1044
0
    {
1045
0
        VSIFree(pszNewLine);
1046
0
        return *this;
1047
0
    }
1048
1049
0
    if (nInsertAtLineNo < 0 || nInsertAtLineNo > nCount)
1050
0
    {
1051
0
        CPLError(CE_Failure, CPLE_AppDefined,
1052
0
                 "CPLStringList::InsertString() requested beyond list end.");
1053
0
        return *this;
1054
0
    }
1055
1056
0
    bIsSorted = false;
1057
1058
0
    for (int i = nCount; i > nInsertAtLineNo; i--)
1059
0
        papszList[i] = papszList[i - 1];
1060
1061
0
    papszList[nInsertAtLineNo] = pszNewLine;
1062
0
    papszList[++nCount] = nullptr;
1063
1064
0
    return *this;
1065
0
}
1066
1067
/************************************************************************/
1068
/*                           RemoveStrings()                            */
1069
/************************************************************************/
1070
1071
/**
1072
 * Remove strings inside a CPLStringList.
1073
 *
1074
 * @param nFirstLineToDelete the 0-based index of the first string to
1075
 * remove. If this value is -1 or is larger than the actual
1076
 * number of strings in list then the nNumToRemove last strings are
1077
 * removed.
1078
 * @param nNumToRemove the number of strings to remove
1079
 *
1080
 * @return a reference to the CPLStringList on which it was invoked.
1081
 * @since 3.13
1082
 */
1083
CPLStringList &CPLStringList::RemoveStrings(int nFirstLineToDelete,
1084
                                            int nNumToRemove)
1085
0
{
1086
0
    if (!MakeOurOwnCopy())
1087
0
        return *this;
1088
1089
0
    papszList =
1090
0
        CSLRemoveStrings(papszList, nFirstLineToDelete, nNumToRemove, nullptr);
1091
0
    nCount = -1;
1092
0
    return *this;
1093
0
}
1094
1095
/************************************************************************/
1096
/*                      FindSortedInsertionPoint()                      */
1097
/*                                                                      */
1098
/*      Find the location at which the indicated line should be         */
1099
/*      inserted in order to keep things in sorted order.               */
1100
/************************************************************************/
1101
1102
int CPLStringList::FindSortedInsertionPoint(const char *pszLine)
1103
1104
0
{
1105
0
    CPLAssert(IsSorted());
1106
1107
0
    int iStart = 0;
1108
0
    int iEnd = nCount - 1;
1109
1110
0
    while (iStart <= iEnd)
1111
0
    {
1112
0
        const int iMiddle = (iEnd + iStart) / 2;
1113
0
        const char *pszMiddle = papszList[iMiddle];
1114
1115
0
        if (CPLCompareKeyValueString(pszLine, pszMiddle) < 0)
1116
0
            iEnd = iMiddle - 1;
1117
0
        else
1118
0
            iStart = iMiddle + 1;
1119
0
    }
1120
1121
0
    iEnd++;
1122
0
    CPLAssert(iEnd >= 0 && iEnd <= nCount);
1123
0
    CPLAssert(iEnd == 0 ||
1124
0
              CPLCompareKeyValueString(pszLine, papszList[iEnd - 1]) >= 0);
1125
0
    CPLAssert(iEnd == nCount ||
1126
0
              CPLCompareKeyValueString(pszLine, papszList[iEnd]) <= 0);
1127
1128
0
    return iEnd;
1129
0
}
1130
1131
namespace cpl
1132
{
1133
1134
/************************************************************************/
1135
/*          CSLIterator::operator==(const CSLIterator &other)           */
1136
/************************************************************************/
1137
1138
/*! @cond Doxygen_Suppress */
1139
bool CSLIterator::operator==(const CSLIterator &other) const
1140
0
{
1141
0
    if (!m_bAtEnd && other.m_bAtEnd)
1142
0
    {
1143
0
        return m_papszList == nullptr || *m_papszList == nullptr;
1144
0
    }
1145
0
    if (!m_bAtEnd && !other.m_bAtEnd)
1146
0
    {
1147
0
        return m_papszList == other.m_papszList;
1148
0
    }
1149
0
    if (m_bAtEnd && other.m_bAtEnd)
1150
0
    {
1151
0
        return true;
1152
0
    }
1153
0
    return false;
1154
0
}
1155
1156
/*! @endcond */
1157
1158
/************************************************************************/
1159
/*                  CSLNameValueIterator::operator*()                   */
1160
/************************************************************************/
1161
1162
/*! @cond Doxygen_Suppress */
1163
CSLNameValueIterator::value_type CSLNameValueIterator::operator*()
1164
0
{
1165
0
    if (m_papszList)
1166
0
    {
1167
0
        while (*m_papszList)
1168
0
        {
1169
0
            char *pszKey = nullptr;
1170
0
            const char *pszValue = CPLParseNameValue(*m_papszList, &pszKey);
1171
0
            if (pszKey)
1172
0
            {
1173
0
                m_osKey = pszKey;
1174
0
                CPLFree(pszKey);
1175
0
                return {m_osKey.c_str(), pszValue};
1176
0
            }
1177
0
            else if (m_bReturnNullKeyIfNotNameValue)
1178
0
            {
1179
0
                return {nullptr, *m_papszList};
1180
0
            }
1181
            // Skip entries that are not name=value pairs.
1182
0
            ++m_papszList;
1183
0
        }
1184
0
    }
1185
    // Should not happen
1186
0
    CPLAssert(false);
1187
0
    return {"", ""};
1188
0
}
1189
1190
/*! @endcond */
1191
1192
/************************************************************************/
1193
/*                  CSLNameValueIteratorWrapper::end()                  */
1194
/************************************************************************/
1195
1196
/*! @cond Doxygen_Suppress */
1197
CSLNameValueIterator CSLNameValueIteratorWrapper::end() const
1198
0
{
1199
0
    int nCount = CSLCount(m_papszList);
1200
0
    if (!m_bReturnNullKeyIfNotNameValue)
1201
0
    {
1202
0
        while (nCount > 0 && strchr(m_papszList[nCount - 1], '=') == nullptr)
1203
0
            --nCount;
1204
0
    }
1205
0
    return CSLNameValueIterator{m_papszList + nCount,
1206
0
                                m_bReturnNullKeyIfNotNameValue};
1207
0
}
1208
1209
/*! @endcond */
1210
1211
}  // namespace cpl