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 (>, < and &) 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,"<",4) )
1369 {
1370 pszOutput[iOut++] = '<';
1371 iIn += 3;
1372 }
1373 else if( EQUALN(pszInput+iIn,">",4) )
1374 {
1375 pszOutput[iOut++] = '>';
1376 iIn += 3;
1377 }
1378 else if( EQUALN(pszInput+iIn,"&",5) )
1379 {
1380 pszOutput[iOut++] = '&';
1381 iIn += 4;
1382 }
1383 else if( EQUALN(pszInput+iIn,""",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