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