Line data Source code
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 : 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 1935290 : CPLStringList::CPLStringList(char **papszListIn, int bTakeOwnership)
48 1935290 : : CPLStringList()
49 :
50 : {
51 1935150 : Assign(papszListIn, bTakeOwnership);
52 1934550 : }
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 4323 : CPLStringList::CPLStringList(CSLConstList papszListIn) : CPLStringList()
67 :
68 : {
69 4323 : Assign(CSLDuplicate(papszListIn));
70 4323 : }
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 2670 : CPLStringList::CPLStringList(const std::vector<std::string> &aosList)
86 : {
87 2670 : if (!aosList.empty())
88 : {
89 545 : bOwnList = true;
90 545 : papszList = static_cast<char **>(
91 545 : VSI_CALLOC_VERBOSE(aosList.size() + 1, sizeof(char *)));
92 545 : nCount = static_cast<int>(aosList.size());
93 2932 : for (int i = 0; i < nCount; ++i)
94 : {
95 2387 : papszList[i] = VSI_STRDUP_VERBOSE(aosList[i].c_str());
96 : }
97 : }
98 2670 : }
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 3 : CPLStringList::CPLStringList(std::initializer_list<const char *> oInitList)
114 : {
115 9 : for (const char *pszStr : oInitList)
116 : {
117 6 : AddString(pszStr);
118 : }
119 3 : }
120 :
121 : /************************************************************************/
122 : /* CPLStringList() */
123 : /************************************************************************/
124 :
125 : //! Copy constructor
126 24864 : CPLStringList::CPLStringList(const CPLStringList &oOther) : CPLStringList()
127 :
128 : {
129 24864 : operator=(oOther);
130 24865 : }
131 :
132 : /************************************************************************/
133 : /* CPLStringList() */
134 : /************************************************************************/
135 :
136 : //! Move constructor
137 3546480 : CPLStringList::CPLStringList(CPLStringList &&oOther) : CPLStringList()
138 :
139 : {
140 3541330 : operator=(std::move(oOther));
141 3543400 : }
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 94 : const CPLStringList CPLStringList::BoundToConstList(CSLConstList papszListIn)
159 : {
160 : return CPLStringList(const_cast<char **>(papszListIn),
161 94 : /* bTakeOwnership= */ false);
162 : }
163 :
164 : /************************************************************************/
165 : /* operator=() */
166 : /************************************************************************/
167 :
168 275820 : CPLStringList &CPLStringList::operator=(const CPLStringList &oOther)
169 : {
170 275820 : if (this != &oOther)
171 : {
172 275819 : char **l_papszList = CSLDuplicate(oOther.papszList);
173 275820 : if (l_papszList)
174 : {
175 6248 : Assign(l_papszList, TRUE);
176 6249 : nAllocation = oOther.nCount > 0 ? oOther.nCount + 1 : 0;
177 6249 : nCount = oOther.nCount;
178 6249 : bIsSorted = oOther.bIsSorted;
179 : }
180 : }
181 :
182 275822 : return *this;
183 : }
184 :
185 : /************************************************************************/
186 : /* operator=() */
187 : /************************************************************************/
188 :
189 3549450 : CPLStringList &CPLStringList::operator=(CPLStringList &&oOther)
190 : {
191 3549450 : if (this != &oOther)
192 : {
193 3550180 : Clear();
194 3549520 : papszList = oOther.papszList;
195 3549520 : oOther.papszList = nullptr;
196 3549520 : nCount = oOther.nCount;
197 3549520 : oOther.nCount = 0;
198 3549520 : nAllocation = oOther.nAllocation;
199 3549520 : oOther.nAllocation = 0;
200 3549520 : bOwnList = oOther.bOwnList;
201 3549520 : oOther.bOwnList = false;
202 3549520 : bIsSorted = oOther.bIsSorted;
203 3549520 : oOther.bIsSorted = true;
204 : }
205 :
206 3548790 : return *this;
207 : }
208 :
209 : /************************************************************************/
210 : /* operator=() */
211 : /************************************************************************/
212 :
213 99885 : CPLStringList &CPLStringList::operator=(CSLConstList papszListIn)
214 : {
215 99885 : if (papszListIn != papszList)
216 : {
217 15411 : Assign(CSLDuplicate(papszListIn));
218 15411 : bIsSorted = false;
219 : }
220 :
221 99885 : return *this;
222 : }
223 :
224 : /************************************************************************/
225 : /* ~CPLStringList() */
226 : /************************************************************************/
227 :
228 24162000 : CPLStringList::~CPLStringList()
229 :
230 : {
231 12091400 : Clear();
232 12070600 : }
233 :
234 : /************************************************************************/
235 : /* Clear() */
236 : /************************************************************************/
237 :
238 : /**
239 : * Clear the string list.
240 : */
241 18080800 : CPLStringList &CPLStringList::Clear()
242 :
243 : {
244 18080800 : if (bOwnList)
245 : {
246 3200780 : CSLDestroy(papszList);
247 3197690 : papszList = nullptr;
248 :
249 3197690 : bOwnList = FALSE;
250 3197690 : nAllocation = 0;
251 3197690 : nCount = 0;
252 : }
253 :
254 18077800 : return *this;
255 : }
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 2344420 : CPLStringList &CPLStringList::Assign(char **papszListIn, int bTakeOwnership)
273 :
274 : {
275 2344420 : Clear();
276 :
277 2344220 : papszList = papszListIn;
278 2344220 : bOwnList = CPL_TO_BOOL(bTakeOwnership);
279 :
280 2344260 : if (papszList == nullptr || *papszList == nullptr)
281 1879940 : nCount = 0;
282 : else
283 464317 : nCount = -1; // unknown
284 :
285 2344260 : nAllocation = 0;
286 2344260 : bIsSorted = FALSE;
287 :
288 2344260 : return *this;
289 : }
290 :
291 : /************************************************************************/
292 : /* Count() */
293 : /************************************************************************/
294 :
295 : /**
296 : * @return count of strings in the list, zero if empty.
297 : */
298 :
299 3112320 : int CPLStringList::Count() const
300 :
301 : {
302 3112320 : if (nCount == -1)
303 : {
304 450484 : if (papszList == nullptr)
305 : {
306 0 : nCount = 0;
307 0 : nAllocation = 0;
308 : }
309 : else
310 : {
311 450484 : nCount = CSLCount(papszList);
312 450488 : nAllocation = std::max(nCount + 1, nAllocation);
313 : }
314 : }
315 :
316 3112370 : return nCount;
317 : }
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 7453900 : bool CPLStringList::MakeOurOwnCopy()
327 :
328 : {
329 7453900 : if (bOwnList)
330 4147630 : return true;
331 :
332 3306270 : if (papszList == nullptr)
333 3306160 : return true;
334 :
335 108 : Count();
336 86 : char **papszListNew = CSLDuplicate(papszList);
337 86 : if (papszListNew == nullptr)
338 : {
339 0 : return false;
340 : }
341 86 : papszList = papszListNew;
342 86 : bOwnList = true;
343 86 : nAllocation = nCount + 1;
344 86 : return true;
345 : }
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 10545600 : bool CPLStringList::EnsureAllocation(int nMaxList)
356 :
357 : {
358 10545600 : if (!bOwnList)
359 : {
360 2598490 : if (!MakeOurOwnCopy())
361 0 : return false;
362 : }
363 :
364 10545300 : if (papszList == nullptr || nAllocation <= nMaxList)
365 : {
366 : // we need to be able to store nMaxList+1 as an int,
367 : // and allocate (nMaxList+1) * sizeof(char*) bytes
368 5352570 : if (nMaxList < 0 || nMaxList > std::numeric_limits<int>::max() - 1 ||
369 2676700 : static_cast<size_t>(nMaxList) >
370 2676700 : std::numeric_limits<size_t>::max() / sizeof(char *) - 1)
371 : {
372 0 : return false;
373 : }
374 2676020 : int nNewAllocation = nMaxList + 1;
375 2676020 : if (nNewAllocation <= (std::numeric_limits<int>::max() - 20) / 2 /
376 : static_cast<int>(sizeof(char *)))
377 2676120 : nNewAllocation = std::max(nNewAllocation * 2 + 20, nMaxList + 1);
378 2676310 : if (papszList == nullptr)
379 : {
380 2600790 : papszList = static_cast<char **>(
381 2600690 : VSI_CALLOC_VERBOSE(nNewAllocation, sizeof(char *)));
382 2600790 : bOwnList = true;
383 2600790 : nCount = 0;
384 2600790 : if (papszList == nullptr)
385 0 : return false;
386 : }
387 : else
388 : {
389 75622 : char **papszListNew = static_cast<char **>(VSI_REALLOC_VERBOSE(
390 : papszList, nNewAllocation * sizeof(char *)));
391 75623 : if (papszListNew == nullptr)
392 0 : return false;
393 75623 : papszList = papszListNew;
394 : }
395 2676410 : nAllocation = nNewAllocation;
396 : }
397 10545200 : return true;
398 : }
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 10538200 : CPLStringList &CPLStringList::AddStringDirectly(char *pszNewString)
414 :
415 : {
416 10538200 : if (nCount == -1)
417 289 : Count();
418 :
419 10538200 : if (!EnsureAllocation(nCount + 1))
420 : {
421 111 : VSIFree(pszNewString);
422 0 : return *this;
423 : }
424 :
425 10538000 : papszList[nCount++] = pszNewString;
426 10538000 : papszList[nCount] = nullptr;
427 :
428 10538000 : bIsSorted = false;
429 :
430 10538000 : return *this;
431 : }
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 5887030 : CPLStringList &CPLStringList::AddString(const char *pszNewString)
446 :
447 : {
448 5887030 : char *pszDupString = VSI_STRDUP_VERBOSE(pszNewString);
449 5886780 : if (pszDupString == nullptr)
450 0 : return *this;
451 5886780 : return AddStringDirectly(pszDupString);
452 : }
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 25023 : CPLStringList &CPLStringList::AddString(const std::string &newString)
467 : {
468 25023 : return AddString(newString.c_str());
469 : }
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 4799500 : CPLStringList &CPLStringList::AddNameValue(const char *pszKey,
486 : const char *pszValue)
487 :
488 : {
489 4799500 : if (pszKey == nullptr || pszValue == nullptr)
490 142144 : return *this;
491 :
492 4657360 : if (!MakeOurOwnCopy())
493 0 : return *this;
494 :
495 : /* -------------------------------------------------------------------- */
496 : /* Format the line. */
497 : /* -------------------------------------------------------------------- */
498 9315160 : if (strlen(pszKey) >
499 9315430 : std::numeric_limits<size_t>::max() - strlen(pszValue) ||
500 4657980 : strlen(pszKey) + strlen(pszValue) >
501 4657980 : std::numeric_limits<size_t>::max() - 2)
502 : {
503 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
504 : "Too big strings in AddNameValue()");
505 0 : return *this;
506 : }
507 4657530 : const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
508 4657530 : char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
509 4657800 : if (pszLine == nullptr)
510 0 : return *this;
511 4657800 : 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 4657800 : if (!IsSorted())
518 4650880 : return AddStringDirectly(pszLine);
519 :
520 : /* -------------------------------------------------------------------- */
521 : /* Find the proper insertion point. */
522 : /* -------------------------------------------------------------------- */
523 6753 : CPLAssert(IsSorted());
524 6732 : const int iKey = FindSortedInsertionPoint(pszLine);
525 6732 : InsertStringDirectly(iKey, pszLine);
526 6732 : bIsSorted = true; // We have actually preserved sort order.
527 :
528 6732 : return *this;
529 : }
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 4865320 : CPLStringList &CPLStringList::SetNameValue(const char *pszKey,
547 : const char *pszValue)
548 :
549 : {
550 4865320 : int iKey = FindName(pszKey);
551 :
552 4865080 : if (iKey == -1)
553 4764440 : return AddNameValue(pszKey, pszValue);
554 :
555 100636 : Count();
556 100312 : if (!MakeOurOwnCopy())
557 0 : return *this;
558 :
559 100313 : CPLFree(papszList[iKey]);
560 100313 : if (pszValue == nullptr) // delete entry
561 : {
562 :
563 : // shift everything down by one.
564 281 : do
565 : {
566 2338 : papszList[iKey] = papszList[iKey + 1];
567 2338 : } while (papszList[iKey++] != nullptr);
568 :
569 2057 : nCount--;
570 : }
571 : else
572 : {
573 196512 : if (strlen(pszKey) >
574 196512 : std::numeric_limits<size_t>::max() - strlen(pszValue) ||
575 98256 : strlen(pszKey) + strlen(pszValue) >
576 98256 : std::numeric_limits<size_t>::max() - 2)
577 : {
578 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
579 : "Too big strings in AddNameValue()");
580 0 : return *this;
581 : }
582 98256 : const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
583 98256 : char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
584 98256 : if (pszLine == nullptr)
585 0 : return *this;
586 98256 : snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
587 :
588 98256 : papszList[iKey] = pszLine;
589 : }
590 :
591 100313 : return *this;
592 : }
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 1342440 : char *CPLStringList::operator[](int i)
609 :
610 : {
611 1342440 : if (nCount == -1)
612 165 : Count();
613 :
614 1342440 : if (i < 0 || i >= nCount)
615 52 : return nullptr;
616 :
617 1342380 : return papszList[i];
618 : }
619 :
620 454449 : const char *CPLStringList::operator[](int i) const
621 :
622 : {
623 454449 : if (nCount == -1)
624 162 : Count();
625 :
626 454449 : if (i < 0 || i >= nCount)
627 2 : return nullptr;
628 :
629 454447 : return papszList[i];
630 : }
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 1436980 : char **CPLStringList::StealList()
645 :
646 : {
647 1436980 : char **papszRetList = papszList;
648 :
649 1436980 : bOwnList = false;
650 1436980 : papszList = nullptr;
651 1436980 : nCount = 0;
652 1436980 : nAllocation = 0;
653 :
654 1436980 : return papszRetList;
655 : }
656 :
657 : /* Case insensitive comparison function */
658 758360 : static int CPLCompareKeyValueString(const char *pszKVa, const char *pszKVb)
659 : {
660 758360 : const char *pszItera = pszKVa;
661 758360 : const char *pszIterb = pszKVb;
662 : while (true)
663 : {
664 5138320 : char cha = *pszItera;
665 5138320 : char chb = *pszIterb;
666 5138320 : if (cha == '=' || cha == '\0')
667 : {
668 4047 : if (chb == '=' || chb == '\0')
669 2 : return 0;
670 : else
671 4045 : return -1;
672 : }
673 5134270 : if (chb == '=' || chb == '\0')
674 : {
675 6372 : return 1;
676 : }
677 5127900 : if (cha >= 'a' && cha <= 'z')
678 498135 : cha -= ('a' - 'A');
679 5127900 : if (chb >= 'a' && chb <= 'z')
680 498913 : chb -= ('a' - 'A');
681 5127900 : if (cha < chb)
682 450173 : return -1;
683 4677730 : else if (cha > chb)
684 297768 : return 1;
685 4379960 : pszItera++;
686 4379960 : pszIterb++;
687 4379960 : }
688 : }
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 97744 : CPLStringList &CPLStringList::Sort()
707 :
708 : {
709 97744 : Count();
710 97744 : if (!MakeOurOwnCopy())
711 0 : return *this;
712 :
713 97744 : if (nCount > 1)
714 : {
715 5563 : std::sort(papszList, papszList + nCount,
716 708020 : [](const char *a, const char *b)
717 708020 : { return CPLCompareKeyValueString(a, b) < 0; });
718 : }
719 97744 : bIsSorted = true;
720 :
721 97744 : return *this;
722 : }
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 16351200 : int CPLStringList::FindName(const char *pszKey) const
741 :
742 : {
743 16351200 : if (!IsSorted())
744 16319600 : return CSLFindName(papszList, pszKey);
745 :
746 : // If we are sorted, we can do an optimized binary search.
747 20870 : int iStart = 0;
748 20870 : int iEnd = nCount - 1;
749 20870 : size_t nKeyLen = strlen(pszKey);
750 :
751 52616 : while (iStart <= iEnd)
752 : {
753 39008 : const int iMiddle = (iEnd + iStart) / 2;
754 39008 : const char *pszMiddle = papszList[iMiddle];
755 :
756 39008 : if (EQUALN(pszMiddle, pszKey, nKeyLen) &&
757 7639 : (pszMiddle[nKeyLen] == '=' || pszMiddle[nKeyLen] == ':'))
758 7262 : return iMiddle;
759 :
760 31746 : if (CPLCompareKeyValueString(pszKey, pszMiddle) < 0)
761 8879 : iEnd = iMiddle - 1;
762 : else
763 22867 : iStart = iMiddle + 1;
764 : }
765 :
766 13608 : return -1;
767 : }
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 13344 : bool CPLStringList::FetchBool(const char *pszKey, bool bDefault) const
790 :
791 : {
792 13344 : const char *pszValue = FetchNameValue(pszKey);
793 :
794 13343 : if (pszValue == nullptr)
795 13109 : return bDefault;
796 :
797 234 : return CPLTestBool(pszValue);
798 : }
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 3201 : int CPLStringList::FetchBoolean(const char *pszKey, int bDefault) const
821 :
822 : {
823 3201 : return FetchBool(pszKey, CPL_TO_BOOL(bDefault)) ? TRUE : FALSE;
824 : }
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 11468800 : const char *CPLStringList::FetchNameValue(const char *pszName) const
844 :
845 : {
846 11468800 : const int iKey = FindName(pszName);
847 :
848 11482200 : if (iKey == -1)
849 4002880 : return nullptr;
850 :
851 7479330 : CPLAssert(papszList[iKey][strlen(pszName)] == '=' ||
852 : papszList[iKey][strlen(pszName)] == ':');
853 :
854 7479330 : return papszList[iKey] + strlen(pszName) + 1;
855 : }
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 32047 : const char *CPLStringList::FetchNameValueDef(const char *pszName,
874 : const char *pszDefault) const
875 :
876 : {
877 32047 : const char *pszValue = FetchNameValue(pszName);
878 32047 : if (pszValue == nullptr)
879 24103 : return pszDefault;
880 :
881 7944 : return pszValue;
882 : }
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 7090 : CPLStringList &CPLStringList::InsertStringDirectly(int nInsertAtLineNo,
920 : char *pszNewLine)
921 :
922 : {
923 7090 : if (nCount == -1)
924 27 : Count();
925 :
926 7090 : if (!EnsureAllocation(nCount + 1))
927 : {
928 0 : VSIFree(pszNewLine);
929 0 : return *this;
930 : }
931 :
932 7090 : if (nInsertAtLineNo < 0 || nInsertAtLineNo > nCount)
933 : {
934 0 : CPLError(CE_Failure, CPLE_AppDefined,
935 : "CPLStringList::InsertString() requested beyond list end.");
936 0 : return *this;
937 : }
938 :
939 7090 : bIsSorted = false;
940 :
941 24611 : for (int i = nCount; i > nInsertAtLineNo; i--)
942 17521 : papszList[i] = papszList[i - 1];
943 :
944 7090 : papszList[nInsertAtLineNo] = pszNewLine;
945 7090 : papszList[++nCount] = nullptr;
946 :
947 7090 : return *this;
948 : }
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 6732 : int CPLStringList::FindSortedInsertionPoint(const char *pszLine)
958 :
959 : {
960 6732 : CPLAssert(IsSorted());
961 :
962 6732 : int iStart = 0;
963 6732 : int iEnd = nCount - 1;
964 :
965 20049 : while (iStart <= iEnd)
966 : {
967 13317 : const int iMiddle = (iEnd + iStart) / 2;
968 13317 : const char *pszMiddle = papszList[iMiddle];
969 :
970 13317 : if (CPLCompareKeyValueString(pszLine, pszMiddle) < 0)
971 2294 : iEnd = iMiddle - 1;
972 : else
973 11023 : iStart = iMiddle + 1;
974 : }
975 :
976 6732 : iEnd++;
977 6732 : CPLAssert(iEnd >= 0 && iEnd <= nCount);
978 6732 : CPLAssert(iEnd == 0 ||
979 : CPLCompareKeyValueString(pszLine, papszList[iEnd - 1]) >= 0);
980 6732 : CPLAssert(iEnd == nCount ||
981 : CPLCompareKeyValueString(pszLine, papszList[iEnd]) <= 0);
982 :
983 6732 : return iEnd;
984 : }
985 :
986 : namespace cpl
987 : {
988 :
989 : /************************************************************************/
990 : /* CSLIterator::operator==(const CSLIterator &other) */
991 : /************************************************************************/
992 :
993 : /*! @cond Doxygen_Suppress */
994 270021 : bool CSLIterator::operator==(const CSLIterator &other) const
995 : {
996 270021 : if (!m_bAtEnd && other.m_bAtEnd)
997 : {
998 270020 : return m_papszList == nullptr || *m_papszList == nullptr;
999 : }
1000 1 : if (!m_bAtEnd && !other.m_bAtEnd)
1001 : {
1002 0 : return m_papszList == other.m_papszList;
1003 : }
1004 1 : if (m_bAtEnd && other.m_bAtEnd)
1005 : {
1006 0 : return true;
1007 : }
1008 1 : return false;
1009 : }
1010 :
1011 : /*! @endcond */
1012 :
1013 : /************************************************************************/
1014 : /* CSLNameValueIterator::operator*() */
1015 : /************************************************************************/
1016 :
1017 : /*! @cond Doxygen_Suppress */
1018 3596 : CSLNameValueIterator::value_type CSLNameValueIterator::operator*()
1019 : {
1020 3596 : if (m_papszList)
1021 : {
1022 3597 : while (*m_papszList)
1023 : {
1024 3597 : char *pszKey = nullptr;
1025 3597 : const char *pszValue = CPLParseNameValue(*m_papszList, &pszKey);
1026 3597 : if (pszKey)
1027 : {
1028 3594 : m_osKey = pszKey;
1029 3594 : CPLFree(pszKey);
1030 3594 : return {m_osKey.c_str(), pszValue};
1031 : }
1032 3 : else if (m_bReturnNullKeyIfNotNameValue)
1033 : {
1034 2 : return {nullptr, *m_papszList};
1035 : }
1036 : // Skip entries that are not name=value pairs.
1037 1 : ++m_papszList;
1038 : }
1039 : }
1040 : // Should not happen
1041 0 : CPLAssert(false);
1042 : return {"", ""};
1043 : }
1044 :
1045 : /*! @endcond */
1046 :
1047 : /************************************************************************/
1048 : /* CSLNameValueIteratorWrapper::end() */
1049 : /************************************************************************/
1050 :
1051 : /*! @cond Doxygen_Suppress */
1052 5359 : CSLNameValueIterator CSLNameValueIteratorWrapper::end() const
1053 : {
1054 5359 : int nCount = CSLCount(m_papszList);
1055 5359 : if (!m_bReturnNullKeyIfNotNameValue)
1056 : {
1057 5287 : while (nCount > 0 && strchr(m_papszList[nCount - 1], '=') == nullptr)
1058 12 : --nCount;
1059 : }
1060 5359 : return CSLNameValueIterator{m_papszList + nCount,
1061 5359 : m_bReturnNullKeyIfNotNameValue};
1062 : }
1063 :
1064 : /*! @endcond */
1065 :
1066 : } // namespace cpl
|