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