1 /**********************************************************************
2 * $Id: cpl_string.c,v 1.3 2005/06/03 03:49:59 daniel Exp $
3 *
4 * Name: cpl_string.cpp
5 * Project: CPL - Common Portability Library
6 * Purpose: String and Stringlist manipulation functions.
7 * Author: Daniel Morissette, dmorissette@dmsolutions.ca
8 *
9 **********************************************************************
10 * Copyright (c) 1998, Daniel Morissette
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 **********************************************************************
30 *
31 * $Log: cpl_string.c,v $
32 * Revision 1.3 2005/06/03 03:49:59 daniel
33 * Update email address, website url, and copyright dates
34 *
35 * Revision 1.2 1999/10/29 06:10:29 daniel
36 * Updated cpl_string.*
37 *
38 * Revision 1.10 1999/06/26 14:05:10 warmerda
39 * Added CSLFindString().
40 *
41 * Revision 1.9 1999/04/28 02:33:02 danmo
42 * CSLInsertStrings(): make sure papszStrList is NULL-terminated properly
43 *
44 * Revision 1.8 1999/03/12 21:19:49 danmo
45 * Fixed TokenizeStringComplex() vs strings ending with empty token,
46 * and fixed a problem with CSLAdd/SetNameValue() vs empty string list.
47 *
48 * Revision 1.7 1999/03/09 21:29:57 warmerda
49 * Added backslash escaping within string constants for tokenize function.
50 *
51 * Revision 1.6 1999/02/25 04:40:46 danmo
52 * Modif. CSLLoad() to use CPLReadLine() (better handling of newlines)
53 *
54 * Revision 1.5 1999/02/17 01:41:58 warmerda
55 * Added CSLGetField
56 *
57 * Revision 1.4 1998/12/15 19:01:40 warmerda
58 * *** empty log message ***
59 *
60 * Revision 1.3 1998/12/05 23:04:21 warmerda
61 * Use EQUALN() instead of strincmp() which doesn't exist on Linux.
62 *
63 * Revision 1.2 1998/12/04 21:40:42 danmo
64 * Added more Name=Value manipulation fuctions
65 *
66 * Revision 1.1 1998/12/03 18:26:02 warmerda
67 * New
68 *
69 **********************************************************************/
70
71 #include "cpl_string.h"
72 #include "cpl_vsi.h"
73
74 /*=====================================================================
75 StringList manipulation functions.
76 =====================================================================*/
77
78 /**********************************************************************
79 * CSLAddString()
80 *
81 * Append a string to a StringList and return a pointer to the modified
82 * StringList.
83 * If the input StringList is NULL, then a new StringList is created.
84 **********************************************************************/
CSLAddString(char ** papszStrList,const char * pszNewString)85 char **CSLAddString(char **papszStrList, const char *pszNewString)
86 {
87 int nItems=0;
88
89 if (pszNewString == NULL)
90 return papszStrList; /* Nothing to do!*/
91
92 /* Allocate room for the new string */
93 if (papszStrList == NULL)
94 papszStrList = (char**) CPLCalloc(2,sizeof(char*));
95 else
96 {
97 nItems = CSLCount(papszStrList);
98 papszStrList = (char**)CPLRealloc(papszStrList,
99 (nItems+2)*sizeof(char*));
100 }
101
102 /* Copy the string in the list */
103 papszStrList[nItems] = CPLStrdup(pszNewString);
104 papszStrList[nItems+1] = NULL;
105
106 return papszStrList;
107 }
108
109 /**********************************************************************
110 * CSLCount()
111 *
112 * Return the number of lines in a Stringlist.
113 **********************************************************************/
CSLCount(char ** papszStrList)114 int CSLCount(char **papszStrList)
115 {
116 int nItems=0;
117
118 if (papszStrList)
119 {
120 while(*papszStrList != NULL)
121 {
122 nItems++;
123 papszStrList++;
124 }
125 }
126
127 return nItems;
128 }
129
130
131 /************************************************************************/
132 /* CSLGetField() */
133 /* */
134 /* Fetches the indicated field, being careful not to crash if */
135 /* the field doesn't exist within this string list. The */
136 /* returned pointer should not be freed, and doesn't */
137 /* necessarily last long. */
138 /************************************************************************/
139
CSLGetField(char ** papszStrList,int iField)140 const char * CSLGetField( char ** papszStrList, int iField )
141
142 {
143 int i;
144
145 if( papszStrList == NULL || iField < 0 )
146 return( "" );
147
148 for( i = 0; i < iField+1; i++ )
149 {
150 if( papszStrList[i] == NULL )
151 return "";
152 }
153
154 return( papszStrList[iField] );
155 }
156
157 /**********************************************************************
158 * CSLDestroy()
159 *
160 * Free all memory used by a StringList.
161 **********************************************************************/
CSLDestroy(char ** papszStrList)162 void CSLDestroy(char **papszStrList)
163 {
164 char **papszPtr;
165
166 if (papszStrList)
167 {
168 papszPtr = papszStrList;
169 while(*papszPtr != NULL)
170 {
171 CPLFree(*papszPtr);
172 papszPtr++;
173 }
174
175 CPLFree(papszStrList);
176 }
177 }
178
179
180 /**********************************************************************
181 * CSLDuplicate()
182 *
183 * Allocate and return a copy of a StringList.
184 **********************************************************************/
CSLDuplicate(char ** papszStrList)185 char **CSLDuplicate(char **papszStrList)
186 {
187 char **papszNewList, **papszSrc, **papszDst;
188 int nLines;
189
190 nLines = CSLCount(papszStrList);
191
192 if (nLines == 0)
193 return NULL;
194
195 papszNewList = (char **)CPLMalloc((nLines+1)*sizeof(char*));
196 papszSrc = papszStrList;
197 papszDst = papszNewList;
198
199 while(*papszSrc != NULL)
200 {
201 *papszDst = CPLStrdup(*papszSrc);
202
203 papszSrc++;
204 papszDst++;
205 }
206 *papszDst = NULL;
207
208 return papszNewList;
209 }
210
211 /**********************************************************************
212 * CSLLoad()
213 *
214 * Load a test file into a stringlist.
215 *
216 * Lines are limited in length by the size fo the CPLReadLine() buffer.
217 **********************************************************************/
CSLLoad(const char * pszFname)218 char **CSLLoad(const char *pszFname)
219 {
220 FILE *fp;
221 const char *pszLine;
222 char **papszStrList=NULL;
223
224 fp = VSIFOpen(pszFname, "rt");
225
226 if (fp)
227 {
228 while(!VSIFEof(fp))
229 {
230 if ( (pszLine = CPLReadLine(fp)) != NULL )
231 {
232 papszStrList = CSLAddString(papszStrList, pszLine);
233 }
234 }
235
236 VSIFClose(fp);
237 }
238 else
239 {
240 /* Unable to open file */
241 CPLError(CE_Failure, CPLE_OpenFailed,
242 "CSLLoad(%s): %s", pszFname, strerror(errno));
243 }
244
245 return papszStrList;
246 }
247
248 /**********************************************************************
249 * CSLSave()
250 *
251 * Write a stringlist to a text file.
252 *
253 * Returns the number of lines written, or 0 if the file could not
254 * be written.
255 **********************************************************************/
CSLSave(char ** papszStrList,const char * pszFname)256 int CSLSave(char **papszStrList, const char *pszFname)
257 {
258 FILE *fp;
259 int nLines=0;
260
261 if (papszStrList)
262 {
263 if ((fp = VSIFOpen(pszFname, "wt")) != NULL)
264 {
265 while(*papszStrList != NULL)
266 {
267 if (VSIFPuts(*papszStrList, fp) == EOF ||
268 VSIFPutc('\n', fp) == EOF)
269 {
270 CPLError(CE_Failure, CPLE_FileIO,
271 "CSLSave(%s): %s", pszFname,
272 strerror(errno));
273 break; /* A Problem happened... abort */
274 }
275
276 nLines++;
277 papszStrList++;
278 }
279
280 VSIFClose(fp);
281 }
282 else
283 {
284 /* Unable to open file */
285 CPLError(CE_Failure, CPLE_OpenFailed,
286 "CSLSave(%s): %s", pszFname, strerror(errno));
287 }
288 }
289
290 return nLines;
291 }
292
293 /**********************************************************************
294 * CSLPrint()
295 *
296 * Print a StringList to fpOut. If fpOut==NULL, then output is sent
297 * to stdout.
298 *
299 * Returns the number of lines printed.
300 **********************************************************************/
CSLPrint(char ** papszStrList,FILE * fpOut)301 int CSLPrint(char **papszStrList, FILE *fpOut)
302 {
303 int nLines=0;
304
305 if (fpOut == NULL)
306 fpOut = stdout;
307
308 if (papszStrList)
309 {
310 while(*papszStrList != NULL)
311 {
312 VSIFPrintf(fpOut, "%s\n", *papszStrList);
313 nLines++;
314 papszStrList++;
315 }
316 }
317
318 return nLines;
319 }
320
321
322 /**********************************************************************
323 * CSLInsertStrings()
324 *
325 * Copies the contents of a StringList inside another StringList
326 * before the specified line.
327 *
328 * nInsertAtLineNo is a 0-based line index before which the new strings
329 * should be inserted. If this value is -1 or is larger than the actual
330 * number of strings in the list then the strings are added at the end
331 * of the source StringList.
332 *
333 * Returns the modified StringList.
334 **********************************************************************/
CSLInsertStrings(char ** papszStrList,int nInsertAtLineNo,char ** papszNewLines)335 char **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo,
336 char **papszNewLines)
337 {
338 int i, nSrcLines, nDstLines, nToInsert;
339 char **ppszSrc, **ppszDst;
340
341 if (papszNewLines == NULL ||
342 ( nToInsert = CSLCount(papszNewLines) ) == 0)
343 return papszStrList; /* Nothing to do!*/
344
345 nSrcLines = CSLCount(papszStrList);
346 nDstLines = nSrcLines + nToInsert;
347
348 /* Allocate room for the new strings */
349 papszStrList = (char**)CPLRealloc(papszStrList,
350 (nDstLines+1)*sizeof(char*));
351
352 /* Make sure the array is NULL-terminated... it may not be if
353 * papszStrList was NULL before Realloc()
354 */
355 papszStrList[nSrcLines] = NULL;
356
357 /* Make some room in the original list at the specified location
358 * Note that we also have to move the NULL pointer at the end of
359 * the source StringList.
360 */
361 if (nInsertAtLineNo == -1 || nInsertAtLineNo > nSrcLines)
362 nInsertAtLineNo = nSrcLines;
363
364 ppszSrc = papszStrList + nSrcLines;
365 ppszDst = papszStrList + nDstLines;
366
367 for (i=nSrcLines; i>=nInsertAtLineNo; i--)
368 {
369 *ppszDst = *ppszSrc;
370 ppszDst--;
371 ppszSrc--;
372 }
373
374 /* Copy the strings to the list */
375 ppszSrc = papszNewLines;
376 ppszDst = papszStrList + nInsertAtLineNo;
377
378 for (; *ppszSrc != NULL; ppszSrc++, ppszDst++)
379 {
380 *ppszDst = CPLStrdup(*ppszSrc);
381 }
382
383 return papszStrList;
384 }
385
386 /**********************************************************************
387 * CSLInsertString()
388 *
389 * Insert a string at a given line number inside a StringList
390 *
391 * nInsertAtLineNo is a 0-based line index before which the new string
392 * should be inserted. If this value is -1 or is larger than the actual
393 * number of strings in the list then the string is added at the end
394 * of the source StringList.
395 *
396 * Returns the modified StringList.
397 **********************************************************************/
CSLInsertString(char ** papszStrList,int nInsertAtLineNo,char * pszNewLine)398 char **CSLInsertString(char **papszStrList, int nInsertAtLineNo,
399 char *pszNewLine)
400 {
401 char *apszList[2];
402
403 /* Create a temporary StringList and call CSLInsertStrings()
404 */
405 apszList[0] = pszNewLine;
406 apszList[1] = NULL;
407
408 return CSLInsertStrings(papszStrList, nInsertAtLineNo, apszList);
409 }
410
411
412 /**********************************************************************
413 * CSLRemoveStrings()
414 *
415 * Remove strings inside a StringList
416 *
417 * nFirstLineToDelete is the 0-based line index of the first line to
418 * remove. If this value is -1 or is larger than the actual
419 * number of strings in list then the nNumToRemove last strings are
420 * removed.
421 *
422 * If ppapszRetStrings != NULL then the deleted strings won't be
423 * free'd, they will be stored in a new StringList and the pointer to
424 * this new list will be returned in *ppapszRetStrings.
425 *
426 * Returns the modified StringList.
427 **********************************************************************/
CSLRemoveStrings(char ** papszStrList,int nFirstLineToDelete,int nNumToRemove,char *** ppapszRetStrings)428 char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete,
429 int nNumToRemove, char ***ppapszRetStrings)
430 {
431 int i, nSrcLines, nDstLines;
432 char **ppszSrc, **ppszDst;
433
434 nSrcLines = CSLCount(papszStrList);
435 nDstLines = nSrcLines - nNumToRemove;
436
437 if (nNumToRemove < 1 || nSrcLines == 0)
438 return papszStrList; /* Nothing to do!*/
439
440 /* If operation will result in an empty StringList then don't waste
441 * time here!
442 */
443 if (nDstLines < 1)
444 {
445 CSLDestroy(papszStrList);
446 return NULL;
447 }
448
449
450 /* Remove lines from the source StringList...
451 * Either free() each line or store them to a new StringList depending on
452 * the caller's choice.
453 */
454 ppszDst = papszStrList + nFirstLineToDelete;
455
456 if (ppapszRetStrings == NULL)
457 {
458 /* free() all the strings that will be removed.
459 */
460 for (i=0; i < nNumToRemove; i++)
461 {
462 CPLFree(*ppszDst);
463 *ppszDst = NULL;
464 }
465 }
466 else
467 {
468 /* Store the strings to remove in a new StringList
469 */
470 *ppapszRetStrings = (char **)CPLCalloc(nNumToRemove+1, sizeof(char*));
471
472 for (i=0; i < nNumToRemove; i++)
473 {
474 (*ppapszRetStrings)[i] = *ppszDst;
475 *ppszDst = NULL;
476 ppszDst++;
477 }
478 }
479
480
481 /* Shift down all the lines that follow the lines to remove.
482 */
483 if (nFirstLineToDelete == -1 || nFirstLineToDelete > nSrcLines)
484 nFirstLineToDelete = nDstLines;
485
486 ppszSrc = papszStrList + nFirstLineToDelete + nNumToRemove;
487 ppszDst = papszStrList + nFirstLineToDelete;
488
489 for ( ; *ppszSrc != NULL; ppszSrc++, ppszDst++)
490 {
491 *ppszDst = *ppszSrc;
492 }
493 /* Move the NULL pointer at the end of the StringList */
494 *ppszDst = *ppszSrc;
495
496 /* At this point, we could realloc() papszStrList to a smaller size, but
497 * since this array will likely grow again in further operations on the
498 * StringList we'll leave it as it is.
499 */
500
501 return papszStrList;
502 }
503
504 /************************************************************************/
505 /* CSLFindString() */
506 /* */
507 /* Find a string within a string list. The string must match */
508 /* the full length, but the comparison is case insensitive. */
509 /* Return -1 on failure. */
510 /************************************************************************/
511
CSLFindString(char ** papszList,const char * pszTarget)512 int CSLFindString( char ** papszList, const char * pszTarget )
513
514 {
515 int i;
516
517 if( papszList == NULL )
518 return -1;
519
520 for( i = 0; papszList[i] != NULL; i++ )
521 {
522 if( EQUAL(papszList[i],pszTarget) )
523 return i;
524 }
525
526 return -1;
527 }
528
529 /**********************************************************************
530 * CSLTokenizeString()
531 *
532 * Tokenizes a string and returns a StringList with one string for
533 * each token.
534 **********************************************************************/
CSLTokenizeString(const char * pszString)535 char **CSLTokenizeString( const char *pszString )
536 {
537 return CSLTokenizeStringComplex( pszString, " ", TRUE, FALSE );
538 }
539
540 /************************************************************************/
541 /* CSLTokenizeStringComplex() */
542 /* */
543 /* The ultimate tokenizer? */
544 /************************************************************************/
545
CSLTokenizeStringComplex(const char * pszString,const char * pszDelimiters,int bHonourStrings,int bAllowEmptyTokens)546 char ** CSLTokenizeStringComplex( const char * pszString,
547 const char * pszDelimiters,
548 int bHonourStrings, int bAllowEmptyTokens )
549
550 {
551 char **papszRetList = NULL;
552 char *pszToken;
553 int nTokenMax, nTokenLen;
554
555 pszToken = (char *) CPLCalloc(10,1);
556 nTokenMax = 10;
557
558 while( pszString != NULL && *pszString != '\0' )
559 {
560 int bInString = FALSE;
561
562 nTokenLen = 0;
563
564 /* Try to find the next delimeter, marking end of token */
565 for( ; *pszString != '\0'; pszString++ )
566 {
567
568 /* End if this is a delimeter skip it and break. */
569 if( !bInString && strchr(pszDelimiters, *pszString) != NULL )
570 {
571 pszString++;
572 break;
573 }
574
575 /* If this is a quote, and we are honouring constant
576 strings, then process the constant strings, with out delim
577 but don't copy over the quotes */
578 if( bHonourStrings && *pszString == '"' )
579 {
580 if( bInString )
581 {
582 bInString = FALSE;
583 continue;
584 }
585 else
586 {
587 bInString = TRUE;
588 continue;
589 }
590 }
591
592 /* Within string constants we allow for escaped quotes, but
593 in processing them we will unescape the quotes */
594 if( bInString && pszString[0] == '\\' && pszString[1] == '"' )
595 {
596 pszString++;
597 }
598
599 /* Within string constants a \\ sequence reduces to \ */
600 else if( bInString
601 && pszString[0] == '\\' && pszString[1] == '\\' )
602 {
603 pszString++;
604 }
605
606 if( nTokenLen >= nTokenMax-1 )
607 {
608 nTokenMax = nTokenMax * 2 + 10;
609 pszToken = (char *) CPLRealloc( pszToken, nTokenMax );
610 }
611
612 pszToken[nTokenLen] = *pszString;
613 nTokenLen++;
614 }
615
616 pszToken[nTokenLen] = '\0';
617
618 if( pszToken[0] != '\0' || bAllowEmptyTokens )
619 {
620 papszRetList = CSLAddString( papszRetList, pszToken );
621 }
622
623 /* If the last token is an empty token, then we have to catch
624 * it now, otherwise we won't reenter the loop and it will be lost.
625 */
626 if ( *pszString == '\0' && bAllowEmptyTokens &&
627 strchr(pszDelimiters, *(pszString-1)) )
628 {
629 papszRetList = CSLAddString( papszRetList, "" );
630 }
631 }
632
633 if( papszRetList == NULL )
634 papszRetList = (char **) CPLCalloc(sizeof(char *),1);
635
636 CPLFree( pszToken );
637
638 return papszRetList;
639 }
640
641 /**********************************************************************
642 * CPLSPrintf()
643 *
644 * My own version of CPLSPrintf() that works with a static buffer.
645 *
646 * It returns a ref. to a static buffer that should not be freed and
647 * is valid only until the next call to CPLSPrintf().
648 *
649 * NOTE: This function should move to cpl_conv.cpp.
650 **********************************************************************/
651 /* For now, assume that a 8000 chars buffer will be enough.
652 */
653 #define CPLSPrintf_BUF_SIZE 8000
654 static char gszCPLSPrintfBuffer[CPLSPrintf_BUF_SIZE];
655
CPLSPrintf(char * fmt,...)656 const char *CPLSPrintf(char *fmt, ...)
657 {
658 va_list args;
659
660 va_start(args, fmt);
661 vsprintf(gszCPLSPrintfBuffer, fmt, args);
662 va_end(args);
663
664 return gszCPLSPrintfBuffer;
665 }
666
667 /**********************************************************************
668 * CSLAppendPrintf()
669 *
670 * Use CPLSPrintf() to append a new line at the end of a StringList.
671 *
672 * Returns the modified StringList.
673 **********************************************************************/
CSLAppendPrintf(char ** papszStrList,char * fmt,...)674 char **CSLAppendPrintf(char **papszStrList, char *fmt, ...)
675 {
676 va_list args;
677
678 va_start(args, fmt);
679 vsprintf(gszCPLSPrintfBuffer, fmt, args);
680 va_end(args);
681
682 return CSLAddString(papszStrList, gszCPLSPrintfBuffer);
683 }
684
685
686 /**********************************************************************
687 * CSLFetchNameValue()
688 *
689 * In a StringList of "Name=Value" pairs, look for the
690 * first value associated with the specified name. The search is not
691 * case sensitive.
692 * ("Name:Value" pairs are also supported for backward compatibility
693 * with older stuff.)
694 *
695 * Returns a reference to the value in the StringList that the caller
696 * should not attempt to free.
697 *
698 * Returns NULL if the name is not found.
699 **********************************************************************/
CSLFetchNameValue(char ** papszStrList,const char * pszName)700 const char *CSLFetchNameValue(char **papszStrList, const char *pszName)
701 {
702 int nLen;
703
704 if (papszStrList == NULL || pszName == NULL)
705 return NULL;
706
707 nLen = strlen(pszName);
708 while(*papszStrList != NULL)
709 {
710 if (EQUALN(*papszStrList, pszName, nLen)
711 && ( (*papszStrList)[nLen] == '=' ||
712 (*papszStrList)[nLen] == ':' ) )
713 {
714 return (*papszStrList)+nLen+1;
715 }
716 papszStrList++;
717 }
718 return NULL;
719 }
720
721 /**********************************************************************
722 * CSLFetchNameValueMultiple()
723 *
724 * In a StringList of "Name=Value" pairs, look for all the
725 * values with the specified name. The search is not case
726 * sensitive.
727 * ("Name:Value" pairs are also supported for backward compatibility
728 * with older stuff.)
729 *
730 * Returns stringlist with one entry for each occurence of the
731 * specified name. The stringlist should eventually be destroyed
732 * by calling CSLDestroy().
733 *
734 * Returns NULL if the name is not found.
735 **********************************************************************/
CSLFetchNameValueMultiple(char ** papszStrList,const char * pszName)736 char **CSLFetchNameValueMultiple(char **papszStrList, const char *pszName)
737 {
738 int nLen;
739 char **papszValues = NULL;
740
741 if (papszStrList == NULL || pszName == NULL)
742 return NULL;
743
744 nLen = strlen(pszName);
745 while(*papszStrList != NULL)
746 {
747 if (EQUALN(*papszStrList, pszName, nLen)
748 && ( (*papszStrList)[nLen] == '=' ||
749 (*papszStrList)[nLen] == ':' ) )
750 {
751 papszValues = CSLAddString(papszValues,
752 (*papszStrList)+nLen+1);
753 }
754 papszStrList++;
755 }
756
757 return papszValues;
758 }
759
760
761 /**********************************************************************
762 * CSLAddNameValue()
763 *
764 * Add a new entry to a StringList of "Name=Value" pairs,
765 * ("Name:Value" pairs are also supported for backward compatibility
766 * with older stuff.)
767 *
768 * This function does not check if a "Name=Value" pair already exists
769 * for that name and can generate multiple entryes for the same name.
770 * Use CSLSetNameValue() if you want each name to have only one value.
771 *
772 * Returns the modified stringlist.
773 **********************************************************************/
CSLAddNameValue(char ** papszStrList,const char * pszName,const char * pszValue)774 char **CSLAddNameValue(char **papszStrList,
775 const char *pszName, const char *pszValue)
776 {
777 const char *pszLine;
778
779 if (pszName == NULL || pszValue==NULL)
780 return papszStrList;
781
782 pszLine = CPLSPrintf("%s=%s", pszName, pszValue);
783
784 return CSLAddString(papszStrList, pszLine);
785 }
786
787 /**********************************************************************
788 * CSLSetNameValue()
789 *
790 * Set the value for a given name in a StringList of "Name=Value" pairs
791 * ("Name:Value" pairs are also supported for backward compatibility
792 * with older stuff.)
793 *
794 * If there is already a value for that name in the list then the value
795 * is changed, otherwise a new "Name=Value" pair is added.
796 *
797 * Returns the modified stringlist.
798 **********************************************************************/
CSLSetNameValue(char ** papszList,const char * pszName,const char * pszValue)799 char **CSLSetNameValue(char **papszList,
800 const char *pszName, const char *pszValue)
801 {
802 char **papszPtr;
803 int nLen;
804
805 if (pszName == NULL || pszValue==NULL)
806 return papszList;
807
808 nLen = strlen(pszName);
809 papszPtr = papszList;
810 while(papszPtr && *papszPtr != NULL)
811 {
812 if (EQUALN(*papszPtr, pszName, nLen)
813 && ( (*papszPtr)[nLen] == '=' ||
814 (*papszPtr)[nLen] == ':' ) )
815 {
816 /* Found it!
817 * Change the value... make sure to keep the ':' or '='
818 */
819 char cSep;
820 cSep = (*papszPtr)[nLen];
821
822 free(*papszPtr);
823 *papszPtr = CPLStrdup(CPLSPrintf("%s%c%s", pszName,
824 cSep, pszValue));
825
826 return papszList;
827 }
828 papszPtr++;
829 }
830
831 /* The name does not exist yet... create a new entry
832 */
833 return CSLAddString(papszList,
834 CPLSPrintf("%s=%s", pszName, pszValue));
835 }
836