1 /**********************************************************************
2  *
3  * Name:     cpl_string.cpp
4  * Project:  CPL - Common Portability Library
5  * Purpose:  String and Stringlist manipulation functions.
6  * Author:   Daniel Morissette, danmo@videotron.ca
7  *
8  **********************************************************************
9  * Copyright (c) 1998, Daniel Morissette
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 OR
22  * 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  * Independent Security Audit 2003/04/04 Andrey Kiselev:
31  *   Completed audit of this module. All functions may be used without buffer
32  *   overflows and stack corruptions with any kind of input data strings with
33  *   except of CPLSPrintf() and CSLAppendPrintf() (see note below).
34  *
35  * Security Audit 2003/03/28 warmerda:
36  *   Completed security audit.  I believe that this module may be safely used
37  *   to parse tokenize arbitrary input strings, assemble arbitrary sets of
38  *   names values into string lists, unescape and escape text even if provided
39  *   by a potentially hostile source.
40  *
41  *   CPLSPrintf() and CSLAppendPrintf() may not be safely invoked on
42  *   arbitrary length inputs since it has a fixed size output buffer on system
43  *   without vsnprintf().
44  *
45  * $Log: cpl_string.cpp,v $
46  * Revision 1.1.1.1  2006/08/21 05:52:20  dsr
47  * Initial import as opencpn, GNU Automake compliant.
48  *
49  * Revision 1.1.1.1  2006/04/19 03:23:29  dsr
50  * Rename/Import to OpenCPN
51  *
52  * Revision 1.37  2003/12/02 15:56:47  warmerda
53  * avoid use of CSLAddString() in tokenize, manage list ourselves
54  *
55  * Revision 1.36  2003/08/29 17:32:27  warmerda
56  * Open file in binary mode for CSLLoad() since CPLReadline() works much
57  * better then.
58  *
59  * Revision 1.35  2003/07/17 10:15:40  dron
60  * CSLTestBoolean() added.
61  *
62  * Revision 1.34  2003/05/28 19:22:38  warmerda
63  * fixed docs
64  *
65  * Revision 1.33  2003/05/21 04:20:30  warmerda
66  * avoid warnings
67  *
68  * Revision 1.32  2003/04/04 14:57:38  dron
69  * _vsnprintf() hack moved to the cpl_config.h.vc.
70  *
71  * Revision 1.31  2003/04/04 14:16:07  dron
72  * Use _vsnprintf() in Windows environment.
73  *
74  * Revision 1.30  2003/03/28 05:29:53  warmerda
75  * Fixed buffer overflow risk in escaping code (for XML method).  Avoid
76  * use of CPLSPrintf() for name/value list assembly to avoid risk with long
77  * key names or values.  Use vsnprintf() in CPLSPrintf() on platforms where it
78  * is available.  Security audit complete.
79  *
80  * Revision 1.29  2003/03/27 21:32:08  warmerda
81  * Fixed bug with escaped spaces.
82  *
83  * Revision 1.28  2003/03/11 21:33:02  warmerda
84  * added URL encode/decode support, untested
85  *
86  * Revision 1.27  2003/01/30 19:15:55  warmerda
87  * added some docs
88  *
89  * Revision 1.26  2003/01/14 14:31:16  warmerda
90  * Added "OFF" as a negative response to CSLFetchBoolean().
91  *
92  * Revision 1.25  2002/10/07 19:35:38  dron
93  * Fixed description for CSLFetchBoolean()
94  *
95  * Revision 1.24  2002/07/12 22:37:05  warmerda
96  * added CSLFetchBoolean
97  *
98  * Revision 1.23  2002/07/09 20:25:25  warmerda
99  * expand tabs
100  *
101  * Revision 1.22  2002/05/28 18:53:43  warmerda
102  * added XML escaping support
103  *
104  * Revision 1.21  2002/04/26 14:55:26  warmerda
105  * Added CPLEscapeString() and CPLUnescapeString() (unescape untested)
106  *
107  * Revision 1.20  2002/03/05 14:26:57  warmerda
108  * expanded tabs
109  *
110  * Revision 1.19  2002/01/16 03:59:27  warmerda
111  * added CPLTokenizeString2
112  *
113  * Revision 1.18  2001/12/11 22:40:26  warmerda
114  * cleanup CPLReadLine buffer in CSLLoad()
115  *
116  * Revision 1.17  2001/11/07 14:31:16  warmerda
117  * doc fix
118  *
119  * Revision 1.16  2001/07/18 04:00:49  warmerda
120  *
121  * Revision 1.15  2001/01/19 21:16:41  warmerda
122  * expanded tabs
123  *
124  * Revision 1.14  2000/10/06 15:19:03  warmerda
125  * added CPLSetNameValueSeparator
126  *
127  * Revision 1.13  2000/08/22 17:47:50  warmerda
128  * Fixed declaration of gnCPLSPrintfBuffer.
129  *
130  * Revision 1.12  2000/08/18 21:20:54  svillene
131  * *** empty log message ***
132  *
133  * Revision 1.11  2000/03/30 05:38:48  warmerda
134  * added CPLParseNameValue
135  *
136  * Revision 1.10  1999/06/26 14:05:10  warmerda
137  * Added CSLFindString().
138  *
139  * Revision 1.9  1999/04/28 02:33:02  danmo
140  * CSLInsertStrings(): make sure papszStrList is NULL-terminated properly
141  *
142  * Revision 1.8  1999/03/12 21:19:49  danmo
143  * Fixed TokenizeStringComplex() vs strings ending with empty token,
144  * and fixed a problem with CSLAdd/SetNameValue() vs empty string list.
145  *
146  * Revision 1.7  1999/03/09 21:29:57  warmerda
147  * Added backslash escaping within string constants for tokenize function.
148  *
149  * Revision 1.6  1999/02/25 04:40:46  danmo
150  * Modif. CSLLoad() to use CPLReadLine() (better handling of newlines)
151  *
152  * Revision 1.5  1999/02/17 01:41:58  warmerda
153  * Added CSLGetField
154  *
155  * Revision 1.4  1998/12/15 19:01:40  warmerda
156  * *** empty log message ***
157  *
158  * Revision 1.3  1998/12/05 23:04:21  warmerda
159  * Use EQUALN() instead of strincmp() which doesn't exist on Linux.
160  *
161  * Revision 1.2  1998/12/04 21:40:42  danmo
162  * Added more Name=Value manipulation fuctions
163  *
164  * Revision 1.1  1998/12/03 18:26:02  warmerda
165  * New
166  *
167  **********************************************************************/
168 
169 #include "cpl_string.h"
170 #include "cpl_vsi.h"
171 
172 /*=====================================================================
173                     StringList manipulation functions.
174  =====================================================================*/
175 
176 /**********************************************************************
177  *                       CSLAddString()
178  *
179  * Append a string to a StringList and return a pointer to the modified
180  * StringList.
181  * If the input StringList is NULL, then a new StringList is created.
182  **********************************************************************/
CSLAddString(char ** papszStrList,const char * pszNewString)183 char **CSLAddString(char **papszStrList, const char *pszNewString)
184 {
185     int nItems=0;
186 
187     if (pszNewString == NULL)
188         return papszStrList;    /* Nothing to do!*/
189 
190     /* Allocate room for the new string */
191     if (papszStrList == NULL)
192         papszStrList = (char**) CPLCalloc(2,sizeof(char*));
193     else
194     {
195         nItems = CSLCount(papszStrList);
196         papszStrList = (char**)CPLRealloc(papszStrList,
197                                           (nItems+2)*sizeof(char*));
198     }
199 
200     /* Copy the string in the list */
201     papszStrList[nItems] = CPLStrdup(pszNewString);
202     papszStrList[nItems+1] = NULL;
203 
204     return papszStrList;
205 }
206 
207 /**********************************************************************
208  *                       CSLCount()
209  *
210  * Return the number of lines in a Stringlist.
211  **********************************************************************/
CSLCount(char ** papszStrList)212 int CSLCount(char **papszStrList)
213 {
214     int nItems=0;
215 
216     if (papszStrList)
217     {
218         while(*papszStrList != NULL)
219         {
220             nItems++;
221             papszStrList++;
222         }
223     }
224 
225     return nItems;
226 }
227 
228 
229 /************************************************************************/
230 /*                            CSLGetField()                             */
231 /*                                                                      */
232 /*      Fetches the indicated field, being careful not to crash if      */
233 /*      the field doesn't exist within this string list.  The           */
234 /*      returned pointer should not be freed, and doesn't               */
235 /*      necessarily last long.                                          */
236 /************************************************************************/
237 
CSLGetField(char ** papszStrList,int iField)238 const char * CSLGetField( char ** papszStrList, int iField )
239 
240 {
241     int         i;
242 
243     if( papszStrList == NULL || iField < 0 )
244         return( "" );
245 
246     for( i = 0; i < iField+1; i++ )
247     {
248         if( papszStrList[i] == NULL )
249             return "";
250     }
251 
252     return( papszStrList[iField] );
253 }
254 
255 /**********************************************************************
256  *                       CSLDestroy()
257  *
258  * Free all memory used by a StringList.
259  **********************************************************************/
CSLDestroy(char ** papszStrList)260 void CSLDestroy(char **papszStrList)
261 {
262     char **papszPtr;
263 
264     if (papszStrList)
265     {
266         papszPtr = papszStrList;
267         while(*papszPtr != NULL)
268         {
269             CPLFree(*papszPtr);
270             papszPtr++;
271         }
272 
273         CPLFree(papszStrList);
274     }
275 }
276 
277 
278 /**********************************************************************
279  *                       CSLDuplicate()
280  *
281  * Allocate and return a copy of a StringList.
282  **********************************************************************/
CSLDuplicate(char ** papszStrList)283 char    **CSLDuplicate(char **papszStrList)
284 {
285     char **papszNewList, **papszSrc, **papszDst;
286     int  nLines;
287 
288     nLines = CSLCount(papszStrList);
289 
290     if (nLines == 0)
291         return NULL;
292 
293     papszNewList = (char **)CPLMalloc((nLines+1)*sizeof(char*));
294     papszSrc = papszStrList;
295     papszDst = papszNewList;
296 
297     while(*papszSrc != NULL)
298     {
299         *papszDst = CPLStrdup(*papszSrc);
300 
301         papszSrc++;
302         papszDst++;
303     }
304     *papszDst = NULL;
305 
306     return papszNewList;
307 }
308 
309 /**********************************************************************
310  *                       CSLLoad()
311  *
312  * Load a test file into a stringlist.
313  *
314  * Lines are limited in length by the size of the CPLReadLine() buffer.
315  **********************************************************************/
CSLLoad(const char * pszFname)316 char **CSLLoad(const char *pszFname)
317 {
318     FILE        *fp;
319     const char  *pszLine;
320     char        **papszStrList=NULL;
321 
322     fp = VSIFOpen(pszFname, "rb");
323 
324     if (fp)
325     {
326         while(!VSIFEof(fp))
327         {
328             if ( (pszLine = CPLReadLine(fp)) != NULL )
329             {
330                 papszStrList = CSLAddString(papszStrList, pszLine);
331             }
332         }
333 
334         VSIFClose(fp);
335 
336         CPLReadLine( NULL );
337     }
338     else
339     {
340         /* Unable to open file */
341         CPLError(CE_Failure, CPLE_OpenFailed,
342                  "CSLLoad(%s): %s", pszFname, strerror(errno));
343     }
344 
345     return papszStrList;
346 }
347 
348 /**********************************************************************
349  *                       CSLSave()
350  *
351  * Write a stringlist to a text file.
352  *
353  * Returns the number of lines written, or 0 if the file could not
354  * be written.
355  **********************************************************************/
CSLSave(char ** papszStrList,const char * pszFname)356 int  CSLSave(char **papszStrList, const char *pszFname)
357 {
358     FILE    *fp;
359     int     nLines = 0;
360 
361     if (papszStrList)
362     {
363         if ((fp = VSIFOpen(pszFname, "wt")) != NULL)
364         {
365             while(*papszStrList != NULL)
366             {
367                 if (VSIFPuts(*papszStrList, fp) == EOF ||
368                     VSIFPutc('\n', fp) == EOF)
369                 {
370                     CPLError(CE_Failure, CPLE_FileIO,
371                              "CSLSave(%s): %s", pszFname,
372                              strerror(errno));
373                     break;  /* A Problem happened... abort */
374                 }
375 
376                 nLines++;
377                 papszStrList++;
378             }
379 
380             VSIFClose(fp);
381         }
382         else
383         {
384             /* Unable to open file */
385             CPLError(CE_Failure, CPLE_OpenFailed,
386                      "CSLSave(%s): %s", pszFname, strerror(errno));
387         }
388     }
389 
390     return nLines;
391 }
392 
393 /**********************************************************************
394  *                       CSLPrint()
395  *
396  * Print a StringList to fpOut.  If fpOut==NULL, then output is sent
397  * to stdout.
398  *
399  * Returns the number of lines printed.
400  **********************************************************************/
CSLPrint(char ** papszStrList,FILE * fpOut)401 int  CSLPrint(char **papszStrList, FILE *fpOut)
402 {
403     int     nLines=0;
404 
405     if (fpOut == NULL)
406         fpOut = stdout;
407 
408     if (papszStrList)
409     {
410         while(*papszStrList != NULL)
411         {
412             VSIFPrintf(fpOut, "%s\n", *papszStrList);
413             nLines++;
414             papszStrList++;
415         }
416     }
417 
418     return nLines;
419 }
420 
421 
422 /**********************************************************************
423  *                       CSLInsertStrings()
424  *
425  * Copies the contents of a StringList inside another StringList
426  * before the specified line.
427  *
428  * nInsertAtLineNo is a 0-based line index before which the new strings
429  * should be inserted.  If this value is -1 or is larger than the actual
430  * number of strings in the list then the strings are added at the end
431  * of the source StringList.
432  *
433  * Returns the modified StringList.
434  **********************************************************************/
CSLInsertStrings(char ** papszStrList,int nInsertAtLineNo,char ** papszNewLines)435 char **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo,
436                         char **papszNewLines)
437 {
438     int     i, nSrcLines, nDstLines, nToInsert;
439     char    **ppszSrc, **ppszDst;
440 
441     if (papszNewLines == NULL ||
442         ( nToInsert = CSLCount(papszNewLines) ) == 0)
443         return papszStrList;    /* Nothing to do!*/
444 
445     nSrcLines = CSLCount(papszStrList);
446     nDstLines = nSrcLines + nToInsert;
447 
448     /* Allocate room for the new strings */
449     papszStrList = (char**)CPLRealloc(papszStrList,
450                                       (nDstLines+1)*sizeof(char*));
451 
452     /* Make sure the array is NULL-terminated... it may not be if
453      * papszStrList was NULL before Realloc()
454      */
455     papszStrList[nSrcLines] = NULL;
456 
457     /* Make some room in the original list at the specified location
458      * Note that we also have to move the NULL pointer at the end of
459      * the source StringList.
460      */
461     if (nInsertAtLineNo == -1 || nInsertAtLineNo > nSrcLines)
462         nInsertAtLineNo = nSrcLines;
463 
464     ppszSrc = papszStrList + nSrcLines;
465     ppszDst = papszStrList + nDstLines;
466 
467     for (i=nSrcLines; i>=nInsertAtLineNo; i--)
468     {
469         *ppszDst = *ppszSrc;
470         ppszDst--;
471         ppszSrc--;
472     }
473 
474     /* Copy the strings to the list */
475     ppszSrc = papszNewLines;
476     ppszDst = papszStrList + nInsertAtLineNo;
477 
478     for (; *ppszSrc != NULL; ppszSrc++, ppszDst++)
479     {
480         *ppszDst = CPLStrdup(*ppszSrc);
481     }
482 
483     return papszStrList;
484 }
485 
486 /**********************************************************************
487  *                       CSLInsertString()
488  *
489  * Insert a string at a given line number inside a StringList
490  *
491  * nInsertAtLineNo is a 0-based line index before which the new string
492  * should be inserted.  If this value is -1 or is larger than the actual
493  * number of strings in the list then the string is added at the end
494  * of the source StringList.
495  *
496  * Returns the modified StringList.
497  **********************************************************************/
CSLInsertString(char ** papszStrList,int nInsertAtLineNo,char * pszNewLine)498 char **CSLInsertString(char **papszStrList, int nInsertAtLineNo,
499                            char *pszNewLine)
500 {
501     char *apszList[2];
502 
503     /* Create a temporary StringList and call CSLInsertStrings()
504      */
505     apszList[0] = pszNewLine;
506     apszList[1] = NULL;
507 
508     return CSLInsertStrings(papszStrList, nInsertAtLineNo, apszList);
509 }
510 
511 
512 /**********************************************************************
513  *                       CSLRemoveStrings()
514  *
515  * Remove strings inside a StringList
516  *
517  * nFirstLineToDelete is the 0-based line index of the first line to
518  * remove. If this value is -1 or is larger than the actual
519  * number of strings in list then the nNumToRemove last strings are
520  * removed.
521  *
522  * If ppapszRetStrings != NULL then the deleted strings won't be
523  * free'd, they will be stored in a new StringList and the pointer to
524  * this new list will be returned in *ppapszRetStrings.
525  *
526  * Returns the modified StringList.
527  **********************************************************************/
CSLRemoveStrings(char ** papszStrList,int nFirstLineToDelete,int nNumToRemove,char *** ppapszRetStrings)528 char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete,
529                         int nNumToRemove, char ***ppapszRetStrings)
530 {
531     int     i, nSrcLines, nDstLines;
532     char    **ppszSrc, **ppszDst;
533 
534     nSrcLines = CSLCount(papszStrList);
535     nDstLines = nSrcLines - nNumToRemove;
536 
537     if (nNumToRemove < 1 || nSrcLines == 0)
538         return papszStrList;    /* Nothing to do!*/
539 
540     /* If operation will result in an empty StringList then don't waste
541      * time here!
542      */
543     if (nDstLines < 1)
544     {
545         CSLDestroy(papszStrList);
546         return NULL;
547     }
548 
549 
550     /* Remove lines from the source StringList...
551      * Either free() each line or store them to a new StringList depending on
552      * the caller's choice.
553      */
554     ppszDst = papszStrList + nFirstLineToDelete;
555 
556     if (ppapszRetStrings == NULL)
557     {
558         /* free() all the strings that will be removed.
559          */
560         for (i=0; i < nNumToRemove; i++)
561         {
562             CPLFree(*ppszDst);
563             *ppszDst = NULL;
564         }
565     }
566     else
567     {
568         /* Store the strings to remove in a new StringList
569          */
570         *ppapszRetStrings = (char **)CPLCalloc(nNumToRemove+1, sizeof(char*));
571 
572         for (i=0; i < nNumToRemove; i++)
573         {
574             (*ppapszRetStrings)[i] = *ppszDst;
575             *ppszDst = NULL;
576             ppszDst++;
577         }
578     }
579 
580 
581     /* Shift down all the lines that follow the lines to remove.
582      */
583     if (nFirstLineToDelete == -1 || nFirstLineToDelete > nSrcLines)
584         nFirstLineToDelete = nDstLines;
585 
586     ppszSrc = papszStrList + nFirstLineToDelete + nNumToRemove;
587     ppszDst = papszStrList + nFirstLineToDelete;
588 
589     for ( ; *ppszSrc != NULL; ppszSrc++, ppszDst++)
590     {
591         *ppszDst = *ppszSrc;
592     }
593     /* Move the NULL pointer at the end of the StringList     */
594     *ppszDst = *ppszSrc;
595 
596     /* At this point, we could realloc() papszStrList to a smaller size, but
597      * since this array will likely grow again in further operations on the
598      * StringList we'll leave it as it is.
599      */
600 
601     return papszStrList;
602 }
603 
604 /************************************************************************/
605 /*                           CSLFindString()                            */
606 /*                                                                      */
607 /*      Find a string within a string list.  The string must match      */
608 /*      the full length, but the comparison is case insensitive.        */
609 /*      Return -1 on failure.                                           */
610 /************************************************************************/
611 
CSLFindString(char ** papszList,const char * pszTarget)612 int CSLFindString( char ** papszList, const char * pszTarget )
613 
614 {
615     int         i;
616 
617     if( papszList == NULL )
618         return -1;
619 
620     for( i = 0; papszList[i] != NULL; i++ )
621     {
622         if( EQUAL(papszList[i],pszTarget) )
623             return i;
624     }
625 
626     return -1;
627 }
628 
629 /**********************************************************************
630  *                       CSLTokenizeString()
631  *
632  * Tokenizes a string and returns a StringList with one string for
633  * each token.
634  **********************************************************************/
CSLTokenizeString(const char * pszString)635 char    **CSLTokenizeString( const char *pszString )
636 {
637     return CSLTokenizeString2( pszString, " ", CSLT_HONOURSTRINGS );
638 }
639 
640 /************************************************************************/
641 /*                      CSLTokenizeStringComplex()                      */
642 /*                                                                      */
643 /*      Obsolete tokenizing api.                                        */
644 /************************************************************************/
645 
CSLTokenizeStringComplex(const char * pszString,const char * pszDelimiters,int bHonourStrings,int bAllowEmptyTokens)646 char ** CSLTokenizeStringComplex( const char * pszString,
647                                   const char * pszDelimiters,
648                                   int bHonourStrings, int bAllowEmptyTokens )
649 
650 {
651     int         nFlags = 0;
652 
653     if( bHonourStrings )
654         nFlags |= CSLT_HONOURSTRINGS;
655     if( bAllowEmptyTokens )
656         nFlags |= CSLT_ALLOWEMPTYTOKENS;
657 
658     return CSLTokenizeString2( pszString, pszDelimiters, nFlags );
659 }
660 
661 /************************************************************************/
662 /*                         CSLTokenizeString2()                         */
663 /*                                                                      */
664 /*      The ultimate tokenizer?                                         */
665 /************************************************************************/
666 
CSLTokenizeString2(const char * pszString,const char * pszDelimiters,int nCSLTFlags)667 char ** CSLTokenizeString2( const char * pszString,
668                             const char * pszDelimiters,
669                             int nCSLTFlags )
670 
671 {
672     char        **papszRetList = NULL;
673     int         nRetMax = 0, nRetLen = 0;
674     char        *pszToken;
675     int         nTokenMax, nTokenLen;
676     int         bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS);
677     int         bAllowEmptyTokens = (nCSLTFlags & CSLT_ALLOWEMPTYTOKENS);
678 
679     pszToken = (char *) CPLCalloc(10,1);
680     nTokenMax = 10;
681 
682     while( pszString != NULL && *pszString != '\0' )
683     {
684         int     bInString = FALSE;
685 
686         nTokenLen = 0;
687 
688         /* Try to find the next delimeter, marking end of token */
689         for( ; *pszString != '\0'; pszString++ )
690         {
691 
692             /* End if this is a delimeter skip it and break. */
693             if( !bInString && strchr(pszDelimiters, *pszString) != NULL )
694             {
695                 pszString++;
696                 break;
697             }
698 
699             /* If this is a quote, and we are honouring constant
700                strings, then process the constant strings, with out delim
701                but don't copy over the quotes */
702             if( bHonourStrings && *pszString == '"' )
703             {
704                 if( nCSLTFlags & CSLT_PRESERVEQUOTES )
705                 {
706                     pszToken[nTokenLen] = *pszString;
707                     nTokenLen++;
708                 }
709 
710                 if( bInString )
711                 {
712                     bInString = FALSE;
713                     continue;
714                 }
715                 else
716                 {
717                     bInString = TRUE;
718                     continue;
719                 }
720             }
721 
722             /* Within string constants we allow for escaped quotes, but
723                in processing them we will unescape the quotes */
724             if( bInString && pszString[0] == '\\' && pszString[1] == '"' )
725             {
726                 if( nCSLTFlags & CSLT_PRESERVEESCAPES )
727                 {
728                     pszToken[nTokenLen] = *pszString;
729                     nTokenLen++;
730                 }
731 
732                 pszString++;
733             }
734 
735             /* Within string constants a \\ sequence reduces to \ */
736             else if( bInString
737                      && pszString[0] == '\\' && pszString[1] == '\\' )
738             {
739                 if( nCSLTFlags & CSLT_PRESERVEESCAPES )
740                 {
741                     pszToken[nTokenLen] = *pszString;
742                     nTokenLen++;
743                 }
744                 pszString++;
745             }
746 
747             if( nTokenLen >= nTokenMax-3 )
748             {
749                 nTokenMax = nTokenMax * 2 + 10;
750                 pszToken = (char *) CPLRealloc( pszToken, nTokenMax );
751             }
752 
753             pszToken[nTokenLen] = *pszString;
754             nTokenLen++;
755         }
756 
757         pszToken[nTokenLen] = '\0';
758 
759         /*
760          * If the last token is an empty token, then we have to catch
761          * it now, otherwise we won't reenter the loop and it will be lost.
762          */
763 
764         if( (pszToken[0] != '\0' || bAllowEmptyTokens)
765             || (*pszString == '\0' && bAllowEmptyTokens
766                 && strchr(pszDelimiters, *(pszString-1)) ) )
767         {
768             if( nRetLen >= nRetMax - 1 )
769             {
770                 nRetMax = nRetMax * 2 + 10;
771                 papszRetList = (char **)
772                     CPLRealloc(papszRetList, sizeof(char*) * nRetMax );
773             }
774 
775             papszRetList[nRetLen++] = CPLStrdup( pszToken );
776             papszRetList[nRetLen] = NULL;
777         }
778     }
779 
780     if( papszRetList == NULL )
781         papszRetList = (char **) CPLCalloc(sizeof(char *),1);
782 
783     CPLFree( pszToken );
784 
785     return papszRetList;
786 }
787 
788 /**********************************************************************
789  *                       CPLSPrintf()
790  *
791  * My own version of CPLSPrintf() that works with 10 static buffer.
792  *
793  * It returns a ref. to a static buffer that should not be freed and
794  * is valid only until the next call to CPLSPrintf().
795  *
796  * NOTE: This function should move to cpl_conv.cpp.
797  **********************************************************************/
798 /* For now, assume that a 8000 chars buffer will be enough.
799  */
800 #define CPLSPrintf_BUF_SIZE 8000
801 #define CPLSPrintf_BUF_Count 10
802 static char gszCPLSPrintfBuffer[CPLSPrintf_BUF_Count][CPLSPrintf_BUF_SIZE];
803 static int gnCPLSPrintfBuffer = 0;
804 
CPLSPrintf(char * fmt,...)805 const char *CPLSPrintf(char *fmt, ...)
806 {
807     va_list args;
808 
809     va_start(args, fmt);
810 #if defined(HAVE_VSNPRINTF)
811     vsnprintf(gszCPLSPrintfBuffer[gnCPLSPrintfBuffer], CPLSPrintf_BUF_SIZE-1,
812               fmt, args);
813 #else
814     vsprintf(gszCPLSPrintfBuffer[gnCPLSPrintfBuffer], fmt, args);
815 #endif
816     va_end(args);
817 
818    int nCurrent = gnCPLSPrintfBuffer;
819 
820     if (++gnCPLSPrintfBuffer == CPLSPrintf_BUF_Count)
821       gnCPLSPrintfBuffer = 0;
822 
823     return gszCPLSPrintfBuffer[nCurrent];
824 }
825 
826 /**********************************************************************
827  *                       CSLAppendPrintf()
828  *
829  * Use CPLSPrintf() to append a new line at the end of a StringList.
830  *
831  * Returns the modified StringList.
832  **********************************************************************/
CSLAppendPrintf(char ** papszStrList,char * fmt,...)833 char **CSLAppendPrintf(char **papszStrList, char *fmt, ...)
834 {
835     va_list args;
836 
837     va_start(args, fmt);
838 #if defined(HAVE_VSNPRINTF)
839     vsnprintf(gszCPLSPrintfBuffer[gnCPLSPrintfBuffer], CPLSPrintf_BUF_SIZE-1,
840               fmt, args);
841 #else
842     vsprintf(gszCPLSPrintfBuffer[gnCPLSPrintfBuffer], fmt, args);
843 #endif
844     va_end(args);
845 
846     int nCurrent = gnCPLSPrintfBuffer;
847 
848     if (++gnCPLSPrintfBuffer == CPLSPrintf_BUF_Count)
849       gnCPLSPrintfBuffer = 0;
850 
851     return CSLAddString(papszStrList, gszCPLSPrintfBuffer[nCurrent]);
852 }
853 
854 /************************************************************************/
855 /*                         CSLTestBoolean()                             */
856 /************************************************************************/
857 
858 /**
859  * Test what boolean value contained in the string.
860  *
861  * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE.
862  * Otherwise, TRUE will be returned.
863  *
864  * @param pszValue the string should be tested.
865  *
866  * @return TRUE or FALSE.
867  */
868 
CSLTestBoolean(const char * pszValue)869 int CSLTestBoolean( const char *pszValue )
870 {
871     if( EQUAL(pszValue,"NO")
872         || EQUAL(pszValue,"FALSE")
873         || EQUAL(pszValue,"OFF")
874         || EQUAL(pszValue,"0") )
875         return FALSE;
876     else
877         return TRUE;
878 }
879 
880 /**********************************************************************
881  *                       CSLFetchBoolean()
882  *
883  * Check for boolean key value.
884  *
885  * In a StringList of "Name=Value" pairs, look to see if there is a key
886  * with the given name, and if it can be interpreted as being TRUE.  If
887  * the key appears without any "=Value" portion it will be considered true.
888  * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
889  * if the key appears in the list it will be considered TRUE.  If the key
890  * doesn't appear at all, the indicated default value will be returned.
891  *
892  * @param papszStrList the string list to search.
893  * @param pszKey the key value to look for (case insensitive).
894  * @param bDefault the value to return if the key isn't found at all.
895  *
896  * @return TRUE or FALSE
897  **********************************************************************/
898 
CSLFetchBoolean(char ** papszStrList,const char * pszKey,int bDefault)899 int CSLFetchBoolean( char **papszStrList, const char *pszKey, int bDefault )
900 
901 {
902     const char *pszValue;
903 
904     if( CSLFindString( papszStrList, pszKey ) != -1 )
905         return TRUE;
906 
907     pszValue = CSLFetchNameValue(papszStrList, pszKey );
908     if( pszValue == NULL )
909         return bDefault;
910     else
911         return CSLTestBoolean( pszValue );
912 }
913 
914 /**********************************************************************
915  *                       CSLFetchNameValue()
916  *
917  * In a StringList of "Name=Value" pairs, look for the
918  * first value associated with the specified name.  The search is not
919  * case sensitive.
920  * ("Name:Value" pairs are also supported for backward compatibility
921  * with older stuff.)
922  *
923  * Returns a reference to the value in the StringList that the caller
924  * should not attempt to free.
925  *
926  * Returns NULL if the name is not found.
927  **********************************************************************/
CSLFetchNameValue(char ** papszStrList,const char * pszName)928 const char *CSLFetchNameValue(char **papszStrList, const char *pszName)
929 {
930     int nLen;
931 
932     if (papszStrList == NULL || pszName == NULL)
933         return NULL;
934 
935     nLen = strlen(pszName);
936     while(*papszStrList != NULL)
937     {
938         if (EQUALN(*papszStrList, pszName, nLen)
939             && ( (*papszStrList)[nLen] == '=' ||
940                  (*papszStrList)[nLen] == ':' ) )
941         {
942             return (*papszStrList)+nLen+1;
943         }
944         papszStrList++;
945     }
946     return NULL;
947 }
948 
949 /**********************************************************************
950  *                       CPLParseNameValue()
951  **********************************************************************/
952 
953 /**
954  * Parse NAME=VALUE string into name and value components.
955  *
956  * Note that if ppszKey is non-NULL, the key (or name) portion will be
957  * allocated using VSIMalloc(), and returned in that pointer.  It is the
958  * applications responsibility to free this string, but the application should
959  * not modify or free the returned value portion.
960  *
961  * This function also support "NAME:VALUE" strings and will strip white
962  * space from around the delimeter when forming name and value strings.
963  *
964  * Eventually CSLFetchNameValue() and friends may be modified to use
965  * CPLParseNameValue().
966  *
967  * @param pszNameValue string in "NAME=VALUE" format.
968  * @param ppszKey optional pointer though which to return the name
969  * portion.
970  * @return the value portion (pointing into original string).
971  */
972 
CPLParseNameValue(const char * pszNameValue,char ** ppszKey)973 const char *CPLParseNameValue(const char *pszNameValue, char **ppszKey )
974 
975 {
976     int  i;
977     const char *pszValue;
978 
979     for( i = 0; pszNameValue[i] != '\0'; i++ )
980     {
981         if( pszNameValue[i] == '=' || pszNameValue[i] == ':' )
982         {
983             pszValue = pszNameValue + i + 1;
984             while( *pszValue == ' ' || *pszValue == '\t' )
985                 pszValue++;
986 
987             if( ppszKey != NULL )
988             {
989                 *ppszKey = (char *) CPLMalloc(i+1);
990                 strncpy( *ppszKey, pszNameValue, i );
991                 (*ppszKey)[i] = '\0';
992                 while( i > 0 &&
993                        ( (*ppszKey)[i] == ' ' || (*ppszKey)[i] == '\t') )
994                 {
995                     (*ppszKey)[i] = '\0';
996                     i--;
997                 }
998             }
999 
1000             return pszValue;
1001         }
1002     }
1003 
1004     return NULL;
1005 }
1006 
1007 /**********************************************************************
1008  *                       CSLFetchNameValueMultiple()
1009  *
1010  * In a StringList of "Name=Value" pairs, look for all the
1011  * values with the specified name.  The search is not case
1012  * sensitive.
1013  * ("Name:Value" pairs are also supported for backward compatibility
1014  * with older stuff.)
1015  *
1016  * Returns stringlist with one entry for each occurence of the
1017  * specified name.  The stringlist should eventually be destroyed
1018  * by calling CSLDestroy().
1019  *
1020  * Returns NULL if the name is not found.
1021  **********************************************************************/
CSLFetchNameValueMultiple(char ** papszStrList,const char * pszName)1022 char **CSLFetchNameValueMultiple(char **papszStrList, const char *pszName)
1023 {
1024     int nLen;
1025     char **papszValues = NULL;
1026 
1027     if (papszStrList == NULL || pszName == NULL)
1028         return NULL;
1029 
1030     nLen = strlen(pszName);
1031     while(*papszStrList != NULL)
1032     {
1033         if (EQUALN(*papszStrList, pszName, nLen)
1034             && ( (*papszStrList)[nLen] == '=' ||
1035                  (*papszStrList)[nLen] == ':' ) )
1036         {
1037             papszValues = CSLAddString(papszValues,
1038                                           (*papszStrList)+nLen+1);
1039         }
1040         papszStrList++;
1041     }
1042 
1043     return papszValues;
1044 }
1045 
1046 
1047 /**********************************************************************
1048  *                       CSLAddNameValue()
1049  *
1050  * Add a new entry to a StringList of "Name=Value" pairs,
1051  * ("Name:Value" pairs are also supported for backward compatibility
1052  * with older stuff.)
1053  *
1054  * This function does not check if a "Name=Value" pair already exists
1055  * for that name and can generate multiple entryes for the same name.
1056  * Use CSLSetNameValue() if you want each name to have only one value.
1057  *
1058  * Returns the modified stringlist.
1059  **********************************************************************/
CSLAddNameValue(char ** papszStrList,const char * pszName,const char * pszValue)1060 char **CSLAddNameValue(char **papszStrList,
1061                     const char *pszName, const char *pszValue)
1062 {
1063     char *pszLine;
1064 
1065     if (pszName == NULL || pszValue==NULL)
1066         return papszStrList;
1067 
1068     pszLine = (char *) CPLMalloc(strlen(pszName)+strlen(pszValue)+2);
1069     sprintf( pszLine, "%s=%s", pszName, pszValue );
1070     papszStrList = CSLAddString(papszStrList, pszLine);
1071     CPLFree( pszLine );
1072 
1073     return papszStrList;
1074 }
1075 
1076 /************************************************************************/
1077 /*                          CSLSetNameValue()                           */
1078 /************************************************************************/
1079 
1080 /**
1081  * Assign value to name in StringList.
1082  *
1083  * Set the value for a given name in a StringList of "Name=Value" pairs
1084  * ("Name:Value" pairs are also supported for backward compatibility
1085  * with older stuff.)
1086  *
1087  * If there is already a value for that name in the list then the value
1088  * is changed, otherwise a new "Name=Value" pair is added.
1089  *
1090  * @param papszList the original list, the modified version is returned.
1091  * @param pszName the name to be assigned a value.  This should be a well
1092  * formed token (no spaces or very special characters).
1093  * @param pszValue the value to assign to the name.  This should not contain
1094  * any newlines (CR or LF) but is otherwise pretty much unconstrained.
1095  *
1096  * @return modified stringlist.
1097  */
1098 
CSLSetNameValue(char ** papszList,const char * pszName,const char * pszValue)1099 char **CSLSetNameValue(char **papszList,
1100                        const char *pszName, const char *pszValue)
1101 {
1102     char **papszPtr;
1103     int nLen;
1104 
1105     if (pszName == NULL || pszValue==NULL)
1106         return papszList;
1107 
1108     nLen = strlen(pszName);
1109     papszPtr = papszList;
1110     while(papszPtr && *papszPtr != NULL)
1111     {
1112         if (EQUALN(*papszPtr, pszName, nLen)
1113             && ( (*papszPtr)[nLen] == '=' ||
1114                  (*papszPtr)[nLen] == ':' ) )
1115         {
1116             /* Found it!
1117              * Change the value... make sure to keep the ':' or '='
1118              */
1119             char cSep;
1120             cSep = (*papszPtr)[nLen];
1121 
1122             CPLFree(*papszPtr);
1123             *papszPtr = (char *) CPLMalloc(strlen(pszName)+strlen(pszValue)+2);
1124             sprintf( *papszPtr, "%s%c%s", pszName, cSep, pszValue );
1125             return papszList;
1126         }
1127         papszPtr++;
1128     }
1129 
1130     /* The name does not exist yet... create a new entry
1131      */
1132     return CSLAddNameValue(papszList, pszName, pszValue);
1133 }
1134 
1135 /************************************************************************/
1136 /*                      CSLSetNameValueSeparator()                      */
1137 /************************************************************************/
1138 
1139 /**
1140  * Replace the default separator (":" or "=") with the passed separator
1141  * in the given name/value list.
1142  *
1143  * Note that if a separator other than ":" or "=" is used, the resulting
1144  * list will not be manipulatable by the CSL name/value functions any more.
1145  *
1146  * The CPLParseNameValue() function is used to break the existing lines,
1147  * and it also strips white space from around the existing delimiter, thus
1148  * the old separator, and any white space will be replaced by the new
1149  * separator.  For formatting purposes it may be desireable to include some
1150  * white space in the new separator.  eg. ": " or " = ".
1151  *
1152  * @param papszList the list to update.  Component strings may be freed
1153  * but the list array will remain at the same location.
1154  *
1155  * @param pszSeparator the new separator string to insert.
1156  *
1157  */
1158 
CSLSetNameValueSeparator(char ** papszList,const char * pszSeparator)1159 void CSLSetNameValueSeparator( char ** papszList, const char *pszSeparator )
1160 
1161 {
1162     int         nLines = CSLCount(papszList), iLine;
1163 
1164     for( iLine = 0; iLine < nLines; iLine++ )
1165     {
1166         char        *pszKey = NULL;
1167         const char  *pszValue;
1168         char        *pszNewLine;
1169 
1170         pszValue = CPLParseNameValue( papszList[iLine], &pszKey );
1171 
1172         pszNewLine = (char *) CPLMalloc( strlen(pszValue) + strlen(pszKey)
1173                                          + strlen(pszSeparator) + 1 );
1174         strcpy( pszNewLine, pszKey );
1175         strcat( pszNewLine, pszSeparator );
1176         strcat( pszNewLine, pszValue );
1177         CPLFree( papszList[iLine] );
1178         papszList[iLine] = pszNewLine;
1179     }
1180 }
1181 
1182 /************************************************************************/
1183 /*                          CPLEscapeString()                           */
1184 /************************************************************************/
1185 
1186 /**
1187  * Apply escaping to string to preserve special characters.
1188  *
1189  * This function will "escape" a variety of special characters
1190  * to make the string suitable to embed within a string constant
1191  * or to write within a text stream but in a form that can be
1192  * reconstitued to it's original form.  The escaping will even preserve
1193  * zero bytes allowing preservation of raw binary data.
1194  *
1195  * CPLES_BackslashQuotable(0): This scheme turns a binary string into
1196  * a form suitable to be placed within double quotes as a string constant.
1197  * The backslash, quote, '\0' and newline characters are all escaped in
1198  * the usual C style.
1199  *
1200  * CPLES_XML(1): This scheme converts the '<', '<' and '&' characters into
1201  * their XML/HTML equivelent (&gt;, &lt; and &amp;) making a string safe
1202  * to embed as CDATA within an XML element.  The '\0' is not escaped and
1203  * should not be included in the input.
1204  *
1205  * CPLES_URL(2): Everything except alphanumerics and the underscore are
1206  * converted to a percent followed by a two digit hex encoding of the character
1207  * (leading zero supplied if needed).  This is the mechanism used for encoding
1208  * values to be passed in URLs.
1209  *
1210  * @param pszInput the string to escape.
1211  * @param nLength The number of bytes of data to preserve.  If this is -1
1212  * the strlen(pszString) function will be used to compute the length.
1213  * @param nScheme the encoding scheme to use.
1214  *
1215  * @return an escaped, zero terminated string that should be freed with
1216  * CPLFree() when no longer needed.
1217  */
1218 
CPLEscapeString(const char * pszInput,int nLength,int nScheme)1219 char *CPLEscapeString( const char *pszInput, int nLength,
1220                        int nScheme )
1221 
1222 {
1223     char        *pszOutput;
1224     char        *pszShortOutput;
1225 
1226     if( nLength == -1 )
1227         nLength = strlen(pszInput);
1228 
1229     pszOutput = (char *) CPLMalloc( nLength * 6 + 1 );
1230 
1231     if( nScheme == CPLES_BackslashQuotable )
1232     {
1233         int iOut = 0, iIn;
1234 
1235         for( iIn = 0; iIn < nLength; iIn++ )
1236         {
1237             if( pszInput[iIn] == '\0' )
1238             {
1239                 pszOutput[iOut++] = '\\';
1240                 pszOutput[iOut++] = '0';
1241             }
1242             else if( pszInput[iIn] == '"' )
1243             {
1244                 pszOutput[iOut++] = '\\';
1245                 pszOutput[iOut++] = 'n';
1246             }
1247             else if( pszInput[iIn] == '\\' )
1248             {
1249                 pszOutput[iOut++] = '\\';
1250                 pszOutput[iOut++] = '\\';
1251             }
1252             else
1253                 pszOutput[iOut++] = pszInput[iIn];
1254         }
1255         pszOutput[iOut] = '\0';
1256     }
1257     else if( nScheme == CPLES_URL ) /* Untested at implementation */
1258     {
1259         int iOut = 0, iIn;
1260 
1261         for( iIn = 0; iIn < nLength; iIn++ )
1262         {
1263             if( (pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z')
1264                 || (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z')
1265                 || (pszInput[iIn] >= '0' && pszInput[iIn] <= '9')
1266                 || pszInput[iIn] == '_' )
1267             {
1268                 pszOutput[iOut++] = pszInput[iIn];
1269             }
1270             else
1271             {
1272                 sprintf( pszOutput, "%%%02X", pszInput[iIn] );
1273                 iOut += 3;
1274             }
1275         }
1276         pszOutput[iOut] = '\0';
1277     }
1278     else if( nScheme == CPLES_XML )
1279     {
1280         int iOut = 0, iIn;
1281 
1282         for( iIn = 0; iIn < nLength; iIn++ )
1283         {
1284             if( pszInput[iIn] == '<' )
1285             {
1286                 pszOutput[iOut++] = '&';
1287                 pszOutput[iOut++] = 'l';
1288                 pszOutput[iOut++] = 't';
1289                 pszOutput[iOut++] = ';';
1290             }
1291             else if( pszInput[iIn] == '>' )
1292             {
1293                 pszOutput[iOut++] = '&';
1294                 pszOutput[iOut++] = 'g';
1295                 pszOutput[iOut++] = 't';
1296                 pszOutput[iOut++] = ';';
1297             }
1298             else if( pszInput[iIn] == '&' )
1299             {
1300                 pszOutput[iOut++] = '&';
1301                 pszOutput[iOut++] = 'a';
1302                 pszOutput[iOut++] = 'm';
1303                 pszOutput[iOut++] = 'p';
1304                 pszOutput[iOut++] = ';';
1305             }
1306             else if( pszInput[iIn] == '"' )
1307             {
1308                 pszOutput[iOut++] = '&';
1309                 pszOutput[iOut++] = 'q';
1310                 pszOutput[iOut++] = 'u';
1311                 pszOutput[iOut++] = 'o';
1312                 pszOutput[iOut++] = 't';
1313                 pszOutput[iOut++] = ';';
1314             }
1315             else
1316                 pszOutput[iOut++] = pszInput[iIn];
1317         }
1318         pszOutput[iOut] = '\0';
1319     }
1320     else
1321     {
1322         pszOutput[0] = '\0';
1323         CPLError( CE_Failure, CPLE_AppDefined,
1324                   "Undefined escaping scheme (%d) in CPLEscapeString()",
1325                   nScheme );
1326     }
1327 
1328     pszShortOutput = CPLStrdup( pszOutput );
1329     CPLFree( pszOutput );
1330 
1331     return pszShortOutput;
1332 }
1333 
1334 /************************************************************************/
1335 /*                         CPLUnescapeString()                          */
1336 /************************************************************************/
1337 
1338 /**
1339  * Unescape a string.
1340  *
1341  * This function does the opposite of CPLEscapeString().  Given a string
1342  * with special values escaped according to some scheme, it will return a
1343  * new copy of the string returned to it's original form.
1344  *
1345  * @param pszInput the input string.  This is a zero terminated string.
1346  * @param pnLength location to return the length of the unescaped string,
1347  * which may in some cases include embedded '\0' characters.
1348  * @param nScheme the escaped scheme to undo (see CPLEscapeString() for a
1349  * list).
1350  *
1351  * @return a copy of the unescaped string that should be freed by the
1352  * application using CPLFree() when no longer needed.
1353  */
1354 
CPLUnescapeString(const char * pszInput,int * pnLength,int nScheme)1355 char *CPLUnescapeString( const char *pszInput, int *pnLength, int nScheme )
1356 
1357 {
1358     char *pszOutput;
1359     int iOut=0, iIn;
1360 
1361     pszOutput = (char *) CPLMalloc(strlen(pszInput)+1);
1362     pszOutput[0] = '\0';
1363 
1364     if( nScheme == CPLES_XML )
1365     {
1366         for( iIn = 0; pszInput[iIn] != '\0'; iIn++ )
1367         {
1368             if( EQUALN(pszInput+iIn,"&lt;",4) )
1369             {
1370                 pszOutput[iOut++] = '<';
1371                 iIn += 3;
1372             }
1373             else if( EQUALN(pszInput+iIn,"&gt;",4) )
1374             {
1375                 pszOutput[iOut++] = '>';
1376                 iIn += 3;
1377             }
1378             else if( EQUALN(pszInput+iIn,"&amp;",5) )
1379             {
1380                 pszOutput[iOut++] = '&';
1381                 iIn += 4;
1382             }
1383             else if( EQUALN(pszInput+iIn,"&quot;",6) )
1384             {
1385                 pszOutput[iOut++] = '"';
1386                 iIn += 5;
1387             }
1388             else
1389             {
1390                 pszOutput[iOut++] = pszInput[iIn];
1391             }
1392         }
1393     }
1394     else if( nScheme == CPLES_URL )
1395     {
1396         for( iIn = 0; pszInput[iIn] != '\0'; iIn++ )
1397         {
1398             if( pszInput[iIn] == '%'
1399                 && pszInput[iIn+1] != '\0'
1400                 && pszInput[iIn+2] != '\0' )
1401             {
1402                 int nHexChar = 0;
1403 
1404                 if( pszInput[iIn+1] >= 'A' && pszInput[iIn+1] <= 'F' )
1405                     nHexChar += 16 * (pszInput[iIn+1] - 'A' + 10);
1406                 else if( pszInput[iIn+1] >= 'a' && pszInput[iIn+1] <= 'f' )
1407                     nHexChar += 16 * (pszInput[iIn+1] - 'a' + 10);
1408                 else if( pszInput[iIn+1] >= '0' && pszInput[iIn+1] <= '9' )
1409                     nHexChar += 16 * (pszInput[iIn+1] - '0');
1410                 else
1411                     CPLDebug( "CPL",
1412                               "Error unescaping CPLES_URL text, percent not "
1413                               "followed by two hex digits." );
1414 
1415                 if( pszInput[iIn+2] >= 'A' && pszInput[iIn+2] <= 'F' )
1416                     nHexChar += pszInput[iIn+2] - 'A' + 10;
1417                 else if( pszInput[iIn+2] >= 'a' && pszInput[iIn+2] <= 'f' )
1418                     nHexChar += pszInput[iIn+2] - 'a' + 10;
1419                 else if( pszInput[iIn+2] >= '0' && pszInput[iIn+2] <= '9' )
1420                     nHexChar += pszInput[iIn+2] - '0';
1421                 else
1422                     CPLDebug( "CPL",
1423                               "Error unescaping CPLES_URL text, percent not "
1424                               "followed by two hex digits." );
1425 
1426                 pszOutput[iOut++] = (char) nHexChar;
1427                 iIn += 2;
1428             }
1429             else if( pszInput[iIn] == '+' )
1430             {
1431                 pszOutput[iOut++] = ' ';
1432             }
1433             else
1434             {
1435                 pszOutput[iOut++] = pszInput[iIn];
1436             }
1437         }
1438     }
1439     else /* if( nScheme == CPLES_BackslashQuoteable ) */
1440     {
1441         for( iIn = 0; pszInput[iIn] != '\0'; iIn++ )
1442         {
1443             if( pszInput[iIn] == '\\' )
1444             {
1445                 iIn++;
1446                 if( pszInput[iIn] == 'n' )
1447                     pszOutput[iOut++] = '\n';
1448                 else if( pszInput[iIn] == '0' )
1449                     pszOutput[iOut++] = '\0';
1450                 else
1451                     pszOutput[iOut++] = pszInput[iIn];
1452             }
1453             else
1454             {
1455                 pszOutput[iOut++] = pszInput[iIn];
1456             }
1457         }
1458     }
1459 
1460     pszOutput[iOut] = '\0';
1461 
1462     if( pnLength != NULL )
1463         *pnLength = iOut;
1464 
1465     return pszOutput;
1466 }
1467