Coverage Report

Created: 2025-06-13 06:29

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