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 1694620 : CPLStringList::CPLStringList(char **papszListIn, int bTakeOwnership)
48 1694620 : : CPLStringList()
49 :
50 : {
51 1632000 : Assign(papszListIn, bTakeOwnership);
52 1614930 : }
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 4035 : CPLStringList::CPLStringList(CSLConstList papszListIn) : CPLStringList()
67 :
68 : {
69 4035 : Assign(CSLDuplicate(papszListIn));
70 4035 : }
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 795 : CPLStringList::CPLStringList(const std::vector<std::string> &aosList)
86 : {
87 795 : if (!aosList.empty())
88 : {
89 274 : bOwnList = true;
90 274 : papszList = static_cast<char **>(
91 274 : VSI_CALLOC_VERBOSE(aosList.size() + 1, sizeof(char *)));
92 274 : nCount = static_cast<int>(aosList.size());
93 1228 : for (int i = 0; i < nCount; ++i)
94 : {
95 954 : papszList[i] = VSI_STRDUP_VERBOSE(aosList[i].c_str());
96 : }
97 : }
98 795 : }
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 21653 : CPLStringList::CPLStringList(const CPLStringList &oOther) : CPLStringList()
127 :
128 : {
129 21655 : operator=(oOther);
130 21652 : }
131 :
132 : /************************************************************************/
133 : /* CPLStringList() */
134 : /************************************************************************/
135 :
136 : //! Move constructor
137 2895250 : CPLStringList::CPLStringList(CPLStringList &&oOther) : CPLStringList()
138 :
139 : {
140 2848920 : operator=(std::move(oOther));
141 2947240 : }
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 37425 : CPLStringList &CPLStringList::operator=(const CPLStringList &oOther)
169 : {
170 37425 : if (this != &oOther)
171 : {
172 37424 : char **l_papszList = CSLDuplicate(oOther.papszList);
173 37420 : if (l_papszList)
174 : {
175 5700 : Assign(l_papszList, TRUE);
176 5698 : nAllocation = oOther.nCount > 0 ? oOther.nCount + 1 : 0;
177 5698 : nCount = oOther.nCount;
178 5698 : bIsSorted = oOther.bIsSorted;
179 : }
180 : }
181 :
182 37419 : return *this;
183 : }
184 :
185 : /************************************************************************/
186 : /* operator=() */
187 : /************************************************************************/
188 :
189 2920680 : CPLStringList &CPLStringList::operator=(CPLStringList &&oOther)
190 : {
191 2920680 : if (this != &oOther)
192 : {
193 2939720 : Clear();
194 2914060 : papszList = oOther.papszList;
195 2914060 : oOther.papszList = nullptr;
196 2914060 : nCount = oOther.nCount;
197 2914060 : oOther.nCount = 0;
198 2914060 : nAllocation = oOther.nAllocation;
199 2914060 : oOther.nAllocation = 0;
200 2914060 : bOwnList = oOther.bOwnList;
201 2914060 : oOther.bOwnList = false;
202 2914060 : bIsSorted = oOther.bIsSorted;
203 2914060 : oOther.bIsSorted = true;
204 : }
205 :
206 2895030 : return *this;
207 : }
208 :
209 : /************************************************************************/
210 : /* operator=() */
211 : /************************************************************************/
212 :
213 31285 : CPLStringList &CPLStringList::operator=(CSLConstList papszListIn)
214 : {
215 31285 : if (papszListIn != papszList)
216 : {
217 13088 : Assign(CSLDuplicate(papszListIn));
218 13088 : bIsSorted = false;
219 : }
220 :
221 31285 : return *this;
222 : }
223 :
224 : /************************************************************************/
225 : /* ~CPLStringList() */
226 : /************************************************************************/
227 :
228 17702400 : CPLStringList::~CPLStringList()
229 :
230 : {
231 8897850 : Clear();
232 8804560 : }
233 :
234 : /************************************************************************/
235 : /* Clear() */
236 : /************************************************************************/
237 :
238 : /**
239 : * Clear the string list.
240 : */
241 13752400 : CPLStringList &CPLStringList::Clear()
242 :
243 : {
244 13752400 : if (bOwnList)
245 : {
246 2408620 : CSLDestroy(papszList);
247 2455460 : papszList = nullptr;
248 :
249 2455460 : bOwnList = FALSE;
250 2455460 : nAllocation = 0;
251 2455460 : nCount = 0;
252 : }
253 :
254 13799300 : 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 1793060 : CPLStringList &CPLStringList::Assign(char **papszListIn, int bTakeOwnership)
273 :
274 : {
275 1793060 : Clear();
276 :
277 1758840 : papszList = papszListIn;
278 1758840 : bOwnList = CPL_TO_BOOL(bTakeOwnership);
279 :
280 1710480 : if (papszList == nullptr || *papszList == nullptr)
281 1342490 : nCount = 0;
282 : else
283 367992 : nCount = -1; // unknown
284 :
285 1710480 : nAllocation = 0;
286 1710480 : bIsSorted = FALSE;
287 :
288 1710480 : return *this;
289 : }
290 :
291 : /************************************************************************/
292 : /* Count() */
293 : /************************************************************************/
294 :
295 : /**
296 : * @return count of strings in the list, zero if empty.
297 : */
298 :
299 2345530 : int CPLStringList::Count() const
300 :
301 : {
302 2345530 : if (nCount == -1)
303 : {
304 355266 : if (papszList == nullptr)
305 : {
306 0 : nCount = 0;
307 0 : nAllocation = 0;
308 : }
309 : else
310 : {
311 355266 : nCount = CSLCount(papszList);
312 355266 : nAllocation = std::max(nCount + 1, nAllocation);
313 : }
314 : }
315 :
316 2345470 : 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 6142610 : bool CPLStringList::MakeOurOwnCopy()
327 :
328 : {
329 6142610 : if (bOwnList)
330 3431140 : return true;
331 :
332 2711470 : if (papszList == nullptr)
333 2710960 : return true;
334 :
335 509 : Count();
336 83 : char **papszListNew = CSLDuplicate(papszList);
337 83 : if (papszListNew == nullptr)
338 : {
339 0 : return false;
340 : }
341 83 : papszList = papszListNew;
342 83 : bOwnList = true;
343 83 : nAllocation = nCount + 1;
344 83 : 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 9036190 : bool CPLStringList::EnsureAllocation(int nMaxList)
356 :
357 : {
358 9036190 : if (!bOwnList)
359 : {
360 2208570 : if (!MakeOurOwnCopy())
361 0 : return false;
362 : }
363 :
364 9036110 : 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 4549310 : if (nMaxList < 0 || nMaxList > std::numeric_limits<int>::max() - 1 ||
369 2274760 : static_cast<size_t>(nMaxList) >
370 2274760 : std::numeric_limits<size_t>::max() / sizeof(char *) - 1)
371 : {
372 0 : return false;
373 : }
374 2274550 : int nNewAllocation = nMaxList + 1;
375 2274550 : if (nNewAllocation <= (std::numeric_limits<int>::max() - 20) / 2 /
376 : static_cast<int>(sizeof(char *)))
377 2274760 : nNewAllocation = std::max(nNewAllocation * 2 + 20, nMaxList + 1);
378 2274670 : if (papszList == nullptr)
379 : {
380 2210870 : papszList = static_cast<char **>(
381 2210820 : VSI_CALLOC_VERBOSE(nNewAllocation, sizeof(char *)));
382 2210870 : bOwnList = true;
383 2210870 : nCount = 0;
384 2210870 : if (papszList == nullptr)
385 0 : return false;
386 : }
387 : else
388 : {
389 63858 : char **papszListNew = static_cast<char **>(VSI_REALLOC_VERBOSE(
390 : papszList, nNewAllocation * sizeof(char *)));
391 63858 : if (papszListNew == nullptr)
392 0 : return false;
393 63858 : papszList = papszListNew;
394 : }
395 2274730 : nAllocation = nNewAllocation;
396 : }
397 9036230 : 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 9029390 : CPLStringList &CPLStringList::AddStringDirectly(char *pszNewString)
414 :
415 : {
416 9029390 : if (nCount == -1)
417 215 : Count();
418 :
419 9029390 : if (!EnsureAllocation(nCount + 1))
420 : {
421 0 : VSIFree(pszNewString);
422 0 : return *this;
423 : }
424 :
425 9029440 : papszList[nCount++] = pszNewString;
426 9029440 : papszList[nCount] = nullptr;
427 :
428 9029440 : bIsSorted = false;
429 :
430 9029440 : 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 5212230 : CPLStringList &CPLStringList::AddString(const char *pszNewString)
446 :
447 : {
448 5212230 : char *pszDupString = VSI_STRDUP_VERBOSE(pszNewString);
449 5212170 : if (pszDupString == nullptr)
450 0 : return *this;
451 5212170 : 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 24955 : CPLStringList &CPLStringList::AddString(const std::string &newString)
467 : {
468 24955 : 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 3826720 : CPLStringList &CPLStringList::AddNameValue(const char *pszKey,
486 : const char *pszValue)
487 :
488 : {
489 3826720 : if (pszKey == nullptr || pszValue == nullptr)
490 3341 : return *this;
491 :
492 3823380 : if (!MakeOurOwnCopy())
493 0 : return *this;
494 :
495 : /* -------------------------------------------------------------------- */
496 : /* Format the line. */
497 : /* -------------------------------------------------------------------- */
498 7647070 : if (strlen(pszKey) >
499 7647010 : std::numeric_limits<size_t>::max() - strlen(pszValue) ||
500 3823390 : strlen(pszKey) + strlen(pszValue) >
501 3823390 : 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 3823600 : const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
508 3823600 : char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
509 3823570 : if (pszLine == nullptr)
510 0 : return *this;
511 3823570 : 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 3823570 : if (!IsSorted())
518 3816810 : return AddStringDirectly(pszLine);
519 :
520 : /* -------------------------------------------------------------------- */
521 : /* Find the proper insertion point. */
522 : /* -------------------------------------------------------------------- */
523 6705 : CPLAssert(IsSorted());
524 6609 : const int iKey = FindSortedInsertionPoint(pszLine);
525 6609 : InsertStringDirectly(iKey, pszLine);
526 6609 : bIsSorted = true; // We have actually preserved sort order.
527 :
528 6609 : 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 3875060 : CPLStringList &CPLStringList::SetNameValue(const char *pszKey,
547 : const char *pszValue)
548 :
549 : {
550 3875060 : int iKey = FindName(pszKey);
551 :
552 3875030 : if (iKey == -1)
553 3793760 : return AddNameValue(pszKey, pszValue);
554 :
555 81273 : Count();
556 81061 : if (!MakeOurOwnCopy())
557 0 : return *this;
558 :
559 81061 : CPLFree(papszList[iKey]);
560 81061 : if (pszValue == nullptr) // delete entry
561 : {
562 :
563 : // shift everything down by one.
564 119 : do
565 : {
566 225 : papszList[iKey] = papszList[iKey + 1];
567 225 : } while (papszList[iKey++] != nullptr);
568 :
569 106 : nCount--;
570 : }
571 : else
572 : {
573 161910 : if (strlen(pszKey) >
574 161910 : std::numeric_limits<size_t>::max() - strlen(pszValue) ||
575 80955 : strlen(pszKey) + strlen(pszValue) >
576 80955 : 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 80955 : const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
583 80955 : char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
584 80955 : if (pszLine == nullptr)
585 0 : return *this;
586 80955 : snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
587 :
588 80955 : papszList[iKey] = pszLine;
589 : }
590 :
591 81061 : 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 1094870 : char *CPLStringList::operator[](int i)
609 :
610 : {
611 1094870 : if (nCount == -1)
612 181 : Count();
613 :
614 1094870 : if (i < 0 || i >= nCount)
615 52 : return nullptr;
616 :
617 1094810 : return papszList[i];
618 : }
619 :
620 295728 : const char *CPLStringList::operator[](int i) const
621 :
622 : {
623 295728 : if (nCount == -1)
624 22 : Count();
625 :
626 295728 : if (i < 0 || i >= nCount)
627 2 : return nullptr;
628 :
629 295726 : 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 1316520 : char **CPLStringList::StealList()
645 :
646 : {
647 1316520 : char **papszRetList = papszList;
648 :
649 1316520 : bOwnList = false;
650 1316520 : papszList = nullptr;
651 1316520 : nCount = 0;
652 1316520 : nAllocation = 0;
653 :
654 1316520 : return papszRetList;
655 : }
656 :
657 : /* Case insensitive comparison function */
658 489828 : static int CPLCompareKeyValueString(const char *pszKVa, const char *pszKVb)
659 : {
660 489828 : const char *pszItera = pszKVa;
661 489828 : const char *pszIterb = pszKVb;
662 : while (true)
663 : {
664 3738960 : char cha = *pszItera;
665 3738960 : char chb = *pszIterb;
666 3738960 : if (cha == '=' || cha == '\0')
667 : {
668 2337 : if (chb == '=' || chb == '\0')
669 2 : return 0;
670 : else
671 2335 : return -1;
672 : }
673 3736620 : if (chb == '=' || chb == '\0')
674 : {
675 2890 : return 1;
676 : }
677 3733730 : if (cha >= 'a' && cha <= 'z')
678 490743 : cha -= ('a' - 'A');
679 3733730 : if (chb >= 'a' && chb <= 'z')
680 491491 : chb -= ('a' - 'A');
681 3733730 : if (cha < chb)
682 286621 : return -1;
683 3447110 : else if (cha > chb)
684 197980 : return 1;
685 3249130 : pszItera++;
686 3249130 : pszIterb++;
687 3249130 : }
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 29211 : CPLStringList &CPLStringList::Sort()
707 :
708 : {
709 29211 : Count();
710 29211 : if (!MakeOurOwnCopy())
711 0 : return *this;
712 :
713 29211 : if (nCount > 1)
714 : {
715 3836 : std::sort(papszList, papszList + nCount,
716 440944 : [](const char *a, const char *b)
717 440944 : { return CPLCompareKeyValueString(a, b) < 0; });
718 : }
719 29211 : bIsSorted = true;
720 :
721 29211 : 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 14750600 : int CPLStringList::FindName(const char *pszKey) const
741 :
742 : {
743 14750600 : if (!IsSorted())
744 14697300 : return CSLFindName(papszList, pszKey);
745 :
746 : // If we are sorted, we can do an optimized binary search.
747 26073 : int iStart = 0;
748 26073 : int iEnd = nCount - 1;
749 26073 : size_t nKeyLen = strlen(pszKey);
750 :
751 56671 : while (iStart <= iEnd)
752 : {
753 37528 : const int iMiddle = (iEnd + iStart) / 2;
754 37528 : const char *pszMiddle = papszList[iMiddle];
755 :
756 37528 : if (EQUALN(pszMiddle, pszKey, nKeyLen) &&
757 7283 : (pszMiddle[nKeyLen] == '=' || pszMiddle[nKeyLen] == ':'))
758 6930 : return iMiddle;
759 :
760 30598 : if (CPLCompareKeyValueString(pszKey, pszMiddle) < 0)
761 8647 : iEnd = iMiddle - 1;
762 : else
763 21951 : iStart = iMiddle + 1;
764 : }
765 :
766 19143 : 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 12814 : bool CPLStringList::FetchBool(const char *pszKey, bool bDefault) const
790 :
791 : {
792 12814 : const char *pszValue = FetchNameValue(pszKey);
793 :
794 12813 : if (pszValue == nullptr)
795 12604 : return bDefault;
796 :
797 209 : 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 3153 : int CPLStringList::FetchBoolean(const char *pszKey, int bDefault) const
821 :
822 : {
823 3153 : 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 10878700 : const char *CPLStringList::FetchNameValue(const char *pszName) const
844 :
845 : {
846 10878700 : const int iKey = FindName(pszName);
847 :
848 10903500 : if (iKey == -1)
849 3779540 : return nullptr;
850 :
851 7123990 : CPLAssert(papszList[iKey][strlen(pszName)] == '=' ||
852 : papszList[iKey][strlen(pszName)] == ':');
853 :
854 7123990 : 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 30777 : const char *CPLStringList::FetchNameValueDef(const char *pszName,
874 : const char *pszDefault) const
875 :
876 : {
877 30777 : const char *pszValue = FetchNameValue(pszName);
878 30777 : if (pszValue == nullptr)
879 22859 : return pszDefault;
880 :
881 7918 : 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 6827 : CPLStringList &CPLStringList::InsertStringDirectly(int nInsertAtLineNo,
920 : char *pszNewLine)
921 :
922 : {
923 6827 : if (nCount == -1)
924 27 : Count();
925 :
926 6827 : if (!EnsureAllocation(nCount + 1))
927 : {
928 0 : VSIFree(pszNewLine);
929 0 : return *this;
930 : }
931 :
932 6827 : 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 6827 : bIsSorted = false;
940 :
941 23252 : for (int i = nCount; i > nInsertAtLineNo; i--)
942 16425 : papszList[i] = papszList[i - 1];
943 :
944 6827 : papszList[nInsertAtLineNo] = pszNewLine;
945 6827 : papszList[++nCount] = nullptr;
946 :
947 6827 : 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 6609 : int CPLStringList::FindSortedInsertionPoint(const char *pszLine)
958 :
959 : {
960 6609 : CPLAssert(IsSorted());
961 :
962 6609 : int iStart = 0;
963 6609 : int iEnd = nCount - 1;
964 :
965 19732 : while (iStart <= iEnd)
966 : {
967 13123 : const int iMiddle = (iEnd + iStart) / 2;
968 13123 : const char *pszMiddle = papszList[iMiddle];
969 :
970 13123 : if (CPLCompareKeyValueString(pszLine, pszMiddle) < 0)
971 2256 : iEnd = iMiddle - 1;
972 : else
973 10867 : iStart = iMiddle + 1;
974 : }
975 :
976 6609 : iEnd++;
977 6609 : CPLAssert(iEnd >= 0 && iEnd <= nCount);
978 6609 : CPLAssert(iEnd == 0 ||
979 : CPLCompareKeyValueString(pszLine, papszList[iEnd - 1]) >= 0);
980 6609 : CPLAssert(iEnd == nCount ||
981 : CPLCompareKeyValueString(pszLine, papszList[iEnd]) <= 0);
982 :
983 6609 : return iEnd;
984 : }
985 :
986 : namespace cpl
987 : {
988 :
989 : /************************************************************************/
990 : /* CSLIterator::operator==(const CSLIterator &other) */
991 : /************************************************************************/
992 :
993 : /*! @cond Doxygen_Suppress */
994 255425 : bool CSLIterator::operator==(const CSLIterator &other) const
995 : {
996 255425 : if (!m_bAtEnd && other.m_bAtEnd)
997 : {
998 255419 : return m_papszList == nullptr || *m_papszList == nullptr;
999 : }
1000 6 : if (!m_bAtEnd && !other.m_bAtEnd)
1001 : {
1002 0 : return m_papszList == other.m_papszList;
1003 : }
1004 6 : if (m_bAtEnd && other.m_bAtEnd)
1005 : {
1006 0 : return true;
1007 : }
1008 6 : return false;
1009 : }
1010 :
1011 : /*! @endcond */
1012 :
1013 : /************************************************************************/
1014 : /* CSLNameValueIterator::operator*() */
1015 : /************************************************************************/
1016 :
1017 : /*! @cond Doxygen_Suppress */
1018 1297 : CSLNameValueIterator::value_type CSLNameValueIterator::operator*()
1019 : {
1020 1297 : if (m_papszList)
1021 : {
1022 1298 : while (*m_papszList)
1023 : {
1024 1298 : char *pszKey = nullptr;
1025 1298 : const char *pszValue = CPLParseNameValue(*m_papszList, &pszKey);
1026 1298 : if (pszKey)
1027 : {
1028 1295 : m_osKey = pszKey;
1029 1295 : CPLFree(pszKey);
1030 1295 : 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 3949 : CSLNameValueIterator CSLNameValueIteratorWrapper::end() const
1053 : {
1054 3949 : int nCount = CSLCount(m_papszList);
1055 3949 : if (!m_bReturnNullKeyIfNotNameValue)
1056 : {
1057 3884 : while (nCount > 0 && strchr(m_papszList[nCount - 1], '=') == nullptr)
1058 12 : --nCount;
1059 : }
1060 3949 : return CSLNameValueIterator{m_papszList + nCount,
1061 3949 : m_bReturnNullKeyIfNotNameValue};
1062 : }
1063 :
1064 : /*! @endcond */
1065 :
1066 : } // namespace cpl
|