LCOV - code coverage report
Current view: directory - port - cpl_string.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 666 553 83.0 %
Date: 2012-04-28 Functions: 39 37 94.9 %

       1                 : /**********************************************************************
       2                 :  * $Id: cpl_string.cpp 23719 2012-01-07 18:18:41Z rouault $
       3                 :  *
       4                 :  * Name:     cpl_string.cpp
       5                 :  * Project:  CPL - Common Portability Library
       6                 :  * Purpose:  String and Stringlist manipulation functions.
       7                 :  * Author:   Daniel Morissette, danmo@videotron.ca
       8                 :  *
       9                 :  **********************************************************************
      10                 :  * Copyright (c) 1998, Daniel Morissette
      11                 :  *
      12                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      13                 :  * copy of this software and associated documentation files (the "Software"),
      14                 :  * to deal in the Software without restriction, including without limitation
      15                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16                 :  * and/or sell copies of the Software, and to permit persons to whom the
      17                 :  * Software is furnished to do so, subject to the following conditions:
      18                 :  * 
      19                 :  * The above copyright notice and this permission notice shall be included
      20                 :  * in all copies or substantial portions of the Software.
      21                 :  * 
      22                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      23                 :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      25                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
      28                 :  * DEALINGS IN THE SOFTWARE.
      29                 :  **********************************************************************
      30                 :  *
      31                 :  * Independent Security Audit 2003/04/04 Andrey Kiselev:
      32                 :  *   Completed audit of this module. All functions may be used without buffer
      33                 :  *   overflows and stack corruptions with any kind of input data strings with
      34                 :  *   except of CPLSPrintf() and CSLAppendPrintf() (see note below).
      35                 :  * 
      36                 :  * Security Audit 2003/03/28 warmerda:
      37                 :  *   Completed security audit.  I believe that this module may be safely used 
      38                 :  *   to parse tokenize arbitrary input strings, assemble arbitrary sets of
      39                 :  *   names values into string lists, unescape and escape text even if provided
      40                 :  *   by a potentially hostile source.   
      41                 :  *
      42                 :  *   CPLSPrintf() and CSLAppendPrintf() may not be safely invoked on 
      43                 :  *   arbitrary length inputs since it has a fixed size output buffer on system 
      44                 :  *   without vsnprintf(). 
      45                 :  *
      46                 :  **********************************************************************/
      47                 : 
      48                 : #include "cpl_string.h"
      49                 : #include "cpl_vsi.h"
      50                 : #include "cpl_multiproc.h"
      51                 : 
      52                 : #if defined(WIN32CE)
      53                 : #  include <wce_errno.h>
      54                 : #  include <wce_string.h>
      55                 : #endif
      56                 : 
      57                 : CPL_CVSID("$Id: cpl_string.cpp 23719 2012-01-07 18:18:41Z rouault $");
      58                 : 
      59                 : /*=====================================================================
      60                 :                     StringList manipulation functions.
      61                 :  =====================================================================*/
      62                 : 
      63                 : /**********************************************************************
      64                 :  *                       CSLAddString()
      65                 :  *
      66                 :  * Append a string to a StringList and return a pointer to the modified
      67                 :  * StringList.
      68                 :  * If the input StringList is NULL, then a new StringList is created.
      69                 :  * Note that CSLAddString performance when building a list is in O(n^2)
      70                 :  * which can cause noticable slow down when n > 10000.
      71                 :  **********************************************************************/
      72        15125151 : char **CSLAddString(char **papszStrList, const char *pszNewString)
      73                 : {
      74        15125151 :     int nItems=0;
      75                 : 
      76        15125151 :     if (pszNewString == NULL)
      77               0 :         return papszStrList;    /* Nothing to do!*/
      78                 : 
      79                 :     /* Allocate room for the new string */
      80        15125151 :     if (papszStrList == NULL)
      81         1350726 :         papszStrList = (char**) CPLCalloc(2,sizeof(char*));
      82                 :     else
      83                 :     {
      84        13774425 :         nItems = CSLCount(papszStrList);
      85                 :         papszStrList = (char**)CPLRealloc(papszStrList, 
      86        13774425 :                                           (nItems+2)*sizeof(char*));
      87                 :     }
      88                 : 
      89                 :     /* Copy the string in the list */
      90        15125151 :     papszStrList[nItems] = CPLStrdup(pszNewString);
      91        15125151 :     papszStrList[nItems+1] = NULL;
      92                 : 
      93        15125151 :     return papszStrList;
      94                 : }
      95                 : 
      96                 : /************************************************************************/
      97                 : /*                              CSLCount()                              */
      98                 : /************************************************************************/
      99                 : 
     100                 : /**
     101                 :  * Return number of items in a string list.
     102                 :  *
     103                 :  * Returns the number of items in a string list, not counting the 
     104                 :  * terminating NULL.  Passing in NULL is safe, and will result in a count
     105                 :  * of zero.  
     106                 :  *
     107                 :  * Lists are counted by iterating through them so long lists will
     108                 :  * take more time than short lists.  Care should be taken to avoid using
     109                 :  * CSLCount() as an end condition for loops as it will result in O(n^2)
     110                 :  * behavior. 
     111                 :  *
     112                 :  * @param papszStrList the string list to count.
     113                 :  * 
     114                 :  * @return the number of entries.
     115                 :  */
     116        22560358 : int CSLCount(char **papszStrList)
     117                 : {
     118        22560358 :     int nItems=0;
     119                 : 
     120        22560358 :     if (papszStrList)
     121                 :     {
     122       300317085 :         while(*papszStrList != NULL)
     123                 :         {
     124       259987309 :             nItems++;
     125       259987309 :             papszStrList++;
     126                 :         }
     127                 :     }
     128                 : 
     129        22560358 :     return nItems;
     130                 : }
     131                 : 
     132                 : 
     133                 : /************************************************************************/
     134                 : /*                            CSLGetField()                             */
     135                 : /*                                                                      */
     136                 : /*      Fetches the indicated field, being careful not to crash if      */
     137                 : /*      the field doesn't exist within this string list.  The           */
     138                 : /*      returned pointer should not be freed, and doesn't               */
     139                 : /*      necessarily last long.                                          */
     140                 : /************************************************************************/
     141                 : 
     142          230942 : const char * CSLGetField( char ** papszStrList, int iField )
     143                 : 
     144                 : {
     145                 :     int         i;
     146                 : 
     147          230942 :     if( papszStrList == NULL || iField < 0 )
     148           23428 :         return( "" );
     149                 : 
     150         1528348 :     for( i = 0; i < iField+1; i++ )
     151                 :     {
     152         1320836 :         if( papszStrList[i] == NULL )
     153               2 :             return "";
     154                 :     }
     155                 : 
     156          207512 :     return( papszStrList[iField] );
     157                 : }
     158                 : 
     159                 : /************************************************************************/
     160                 : /*                             CSLDestroy()                             */
     161                 : /************************************************************************/
     162                 : 
     163                 : /**
     164                 :  * Free string list.
     165                 :  * 
     166                 :  * Frees the passed string list (null terminated array of strings).
     167                 :  * It is safe to pass NULL. 
     168                 :  *
     169                 :  * @param papszStrList the list to free.
     170                 :  */
     171        13744137 : void CPL_STDCALL CSLDestroy(char **papszStrList)
     172                 : {
     173                 :     char **papszPtr;
     174                 : 
     175        13744137 :     if (papszStrList)
     176                 :     {
     177         9960038 :         papszPtr = papszStrList;
     178        46995590 :         while(*papszPtr != NULL)
     179                 :         {
     180        27075514 :             CPLFree(*papszPtr);
     181        27075514 :             papszPtr++;
     182                 :         }
     183                 : 
     184         9960038 :         CPLFree(papszStrList);
     185                 :     }
     186        13744137 : }
     187                 : 
     188                 : /************************************************************************/
     189                 : /*                            CSLDuplicate()                            */
     190                 : /************************************************************************/
     191                 : 
     192                 : /**
     193                 :  * Clone a string list.
     194                 :  *
     195                 :  * Efficiently allocates a copy of a string list.  The returned list is
     196                 :  * owned by the caller and should be freed with CSLDestroy().
     197                 :  *
     198                 :  * @param papszStrList the input string list.
     199                 :  * 
     200                 :  * @return newly allocated copy.
     201                 :  */
     202                 : 
     203          212832 : char **CSLDuplicate(char **papszStrList)
     204                 : {
     205                 :     char **papszNewList, **papszSrc, **papszDst;
     206                 :     int  nLines;
     207                 : 
     208          212832 :     nLines = CSLCount(papszStrList);
     209                 : 
     210          212832 :     if (nLines == 0)
     211          162680 :         return NULL;
     212                 : 
     213           50152 :     papszNewList = (char **)CPLMalloc((nLines+1)*sizeof(char*));
     214           50152 :     papszSrc = papszStrList;
     215           50152 :     papszDst = papszNewList;
     216                 : 
     217         1157198 :     while(*papszSrc != NULL)
     218                 :     {
     219         1056894 :         *papszDst = CPLStrdup(*papszSrc);
     220                 : 
     221         1056894 :         papszSrc++;
     222         1056894 :         papszDst++;
     223                 :     }
     224           50152 :     *papszDst = NULL;
     225                 : 
     226           50152 :     return papszNewList;
     227                 : }
     228                 : 
     229                 : /************************************************************************/
     230                 : /*                               CSLMerge                               */
     231                 : /************************************************************************/
     232                 : 
     233                 : /**
     234                 :  * \brief Merge two lists.
     235                 :  *
     236                 :  * The two lists are merged, ensuring that if any keys appear in both
     237                 :  * that the value from the second (papszOverride) list take precidence.
     238                 :  *
     239                 :  * @param papszOrig the original list, being modified.
     240                 :  * @param papszOverride the list of items being merged in.  This list
     241                 :  * is unaltered and remains owned by the caller.
     242                 :  *
     243                 :  * @return updated list.
     244                 :  */
     245                 : 
     246            1198 : char **CSLMerge( char **papszOrig, char **papszOverride )
     247                 : 
     248                 : {
     249                 :     int i;
     250                 : 
     251            1198 :     if( papszOrig == NULL && papszOverride != NULL )
     252             106 :         return CSLDuplicate( papszOverride );
     253                 :     
     254            1092 :     if( papszOverride == NULL )
     255             764 :         return papszOrig;
     256                 : 
     257             984 :     for( i = 0; papszOverride[i] != NULL; i++ )
     258                 :     {
     259             656 :         char *pszKey = NULL;
     260             656 :         const char *pszValue = CPLParseNameValue( papszOverride[i], &pszKey );
     261                 : 
     262             656 :         papszOrig = CSLSetNameValue( papszOrig, pszKey, pszValue );
     263             656 :         CPLFree( pszKey );
     264                 :     }
     265                 : 
     266             328 :     return papszOrig;
     267                 : }
     268                 : 
     269                 : /************************************************************************/
     270                 : /*                             CSLLoad2()                               */
     271                 : /************************************************************************/
     272                 : 
     273                 : /**
     274                 :  * Load a text file into a string list.
     275                 :  *
     276                 :  * The VSI*L API is used, so VSIFOpenL() supported objects that aren't
     277                 :  * physical files can also be accessed.  Files are returned as a string list,
     278                 :  * with one item in the string list per line.  End of line markers are
     279                 :  * stripped (by CPLReadLineL()). 
     280                 :  *
     281                 :  * If reading the file fails a CPLError() will be issued and NULL returned.
     282                 :  *
     283                 :  * @param pszFname the name of the file to read.
     284                 :  * @param nMaxLines maximum number of lines to read before stopping, or -1 for no limit.
     285                 :  * @param nMaxCols  maximum number of characters in a line before stopping, or -1 for no limit.
     286                 :  * @param papszOptions NULL-terminated array of options. Unused for now.
     287                 :  * 
     288                 :  * @return a string list with the files lines, now owned by caller. To be freed with CSLDestroy()
     289                 :  *
     290                 :  * @since GDAL 1.7.0
     291                 :  */
     292                 : 
     293            3588 : char **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, char** papszOptions)
     294                 : {
     295                 :     VSILFILE    *fp;
     296                 :     const char  *pszLine;
     297            3588 :     char        **papszStrList=NULL;
     298            3588 :     int          nLines = 0;
     299            3588 :     int          nAllocatedLines = 0;
     300                 : 
     301            3588 :     fp = VSIFOpenL(pszFname, "rb");
     302                 : 
     303            3588 :     if (fp)
     304                 :     {
     305             890 :         CPLErrorReset();
     306             890 :         while(!VSIFEofL(fp) && (nMaxLines == -1 || nLines < nMaxLines))
     307                 :         {
     308            7374 :             if ( (pszLine = CPLReadLine2L(fp, nMaxCols, papszOptions)) != NULL )
     309                 :             {
     310            7374 :                 if (nLines + 1 >= nAllocatedLines)
     311                 :                 {
     312                 :                     char** papszStrListNew;
     313            1004 :                     nAllocatedLines = 16 + nAllocatedLines * 2;
     314                 :                     papszStrListNew = (char**) VSIRealloc(papszStrList,
     315            1004 :                                                 nAllocatedLines * sizeof(char*));
     316            1004 :                     if (papszStrListNew == NULL)
     317                 :                     {
     318               0 :                         VSIFCloseL(fp);
     319               0 :                         CPLReadLineL( NULL );
     320                 :                         CPLError( CE_Failure, CPLE_OutOfMemory,
     321                 :                              "CSLLoad2(\"%s\") failed: not enough memory to allocate lines.",
     322               0 :                             pszFname );
     323               0 :                         return papszStrList;
     324                 :                     }
     325            1004 :                     papszStrList = papszStrListNew;
     326                 :                 }
     327            7374 :                 papszStrList[nLines] = CPLStrdup(pszLine);
     328            7374 :                 papszStrList[nLines + 1] = NULL;
     329            7374 :                 nLines ++;
     330                 :             }
     331                 :             else
     332               0 :                 break;
     333                 :         }
     334                 : 
     335             890 :         VSIFCloseL(fp);
     336                 : 
     337             890 :         CPLReadLineL( NULL );
     338                 :     }
     339                 :     else
     340                 :     {
     341            2698 :         if (CSLFetchBoolean(papszOptions, "EMIT_ERROR_IF_CANNOT_OPEN_FILE", TRUE))
     342                 :         {
     343                 :             /* Unable to open file */
     344                 :             CPLError( CE_Failure, CPLE_OpenFailed,
     345                 :                     "CSLLoad2(\"%s\") failed: unable to open file.",
     346               0 :                     pszFname );
     347                 :         }
     348                 :     }
     349                 : 
     350            3588 :     return papszStrList;
     351                 : }
     352                 : 
     353                 : /************************************************************************/
     354                 : /*                              CSLLoad()                               */
     355                 : /************************************************************************/
     356                 : 
     357                 : /**
     358                 :  * Load a text file into a string list.
     359                 :  *
     360                 :  * The VSI*L API is used, so VSIFOpenL() supported objects that aren't
     361                 :  * physical files can also be accessed.  Files are returned as a string list,
     362                 :  * with one item in the string list per line.  End of line markers are
     363                 :  * stripped (by CPLReadLineL()). 
     364                 :  *
     365                 :  * If reading the file fails a CPLError() will be issued and NULL returned.
     366                 :  *
     367                 :  * @param pszFname the name of the file to read.
     368                 :  * 
     369                 :  * @return a string list with the files lines, now owned by caller. To be freed with CSLDestroy()
     370                 :  */
     371                 :  
     372             462 : char **CSLLoad(const char *pszFname)
     373                 : {
     374             462 :     return CSLLoad2(pszFname, -1, -1, NULL);
     375                 : }
     376                 : 
     377                 : /**********************************************************************
     378                 :  *                       CSLSave()
     379                 :  *
     380                 :  * Write a stringlist to a text file.
     381                 :  *
     382                 :  * Returns the number of lines written, or 0 if the file could not 
     383                 :  * be written.
     384                 :  **********************************************************************/
     385              92 : int  CSLSave(char **papszStrList, const char *pszFname)
     386                 : {
     387                 :     VSILFILE *fp;
     388              92 :     int     nLines = 0;
     389                 : 
     390              92 :     if (papszStrList)
     391                 :     {
     392              92 :         if ((fp = VSIFOpenL(pszFname, "wt")) != NULL)
     393                 :         {
     394             830 :             while(*papszStrList != NULL)
     395                 :             {
     396             646 :                 if( VSIFPrintfL( fp, "%s\n", *papszStrList ) < 1 )
     397                 :                 {
     398                 :                     CPLError( CE_Failure, CPLE_FileIO,
     399                 :                     "CSLSave(\"%s\") failed: unable to write to output file.",
     400               0 :                               pszFname );
     401               0 :                     break;  /* A Problem happened... abort */
     402                 :                 }
     403                 : 
     404             646 :                 nLines++;
     405             646 :                 papszStrList++;
     406                 :             }
     407                 : 
     408              92 :             VSIFCloseL(fp);
     409                 :         }
     410                 :         else
     411                 :         {
     412                 :             /* Unable to open file */
     413                 :             CPLError( CE_Failure, CPLE_OpenFailed,
     414                 :                       "CSLSave(\"%s\") failed: unable to open output file.",
     415               0 :                       pszFname );
     416                 :         }
     417                 :     }
     418                 : 
     419              92 :     return nLines;
     420                 : }
     421                 : 
     422                 : /**********************************************************************
     423                 :  *                       CSLPrint()
     424                 :  *
     425                 :  * Print a StringList to fpOut.  If fpOut==NULL, then output is sent
     426                 :  * to stdout.
     427                 :  *
     428                 :  * Returns the number of lines printed.
     429                 :  **********************************************************************/
     430               0 : int  CSLPrint(char **papszStrList, FILE *fpOut)
     431                 : {
     432               0 :     int     nLines=0;
     433                 : 
     434               0 :     if (fpOut == NULL)
     435               0 :         fpOut = stdout;
     436                 : 
     437               0 :     if (papszStrList)
     438                 :     {
     439               0 :         while(*papszStrList != NULL)
     440                 :         {
     441               0 :             VSIFPrintf(fpOut, "%s\n", *papszStrList);
     442               0 :             nLines++;
     443               0 :             papszStrList++;
     444                 :         }
     445                 :     }
     446                 : 
     447               0 :     return nLines;
     448                 : }
     449                 : 
     450                 : 
     451                 : /**********************************************************************
     452                 :  *                       CSLInsertStrings()
     453                 :  *
     454                 :  * Copies the contents of a StringList inside another StringList 
     455                 :  * before the specified line.
     456                 :  *
     457                 :  * nInsertAtLineNo is a 0-based line index before which the new strings
     458                 :  * should be inserted.  If this value is -1 or is larger than the actual 
     459                 :  * number of strings in the list then the strings are added at the end
     460                 :  * of the source StringList.
     461                 :  *
     462                 :  * Returns the modified StringList.
     463                 :  **********************************************************************/
     464            2374 : char **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo, 
     465                 :                         char **papszNewLines)
     466                 : {
     467                 :     int     i, nSrcLines, nDstLines, nToInsert;
     468                 :     char    **ppszSrc, **ppszDst;
     469                 : 
     470            2374 :     if (papszNewLines == NULL ||
     471                 :         ( nToInsert = CSLCount(papszNewLines) ) == 0)
     472               4 :         return papszStrList;    /* Nothing to do!*/
     473                 : 
     474            2370 :     nSrcLines = CSLCount(papszStrList);
     475            2370 :     nDstLines = nSrcLines + nToInsert;
     476                 : 
     477                 :     /* Allocate room for the new strings */
     478                 :     papszStrList = (char**)CPLRealloc(papszStrList, 
     479            2370 :                                       (nDstLines+1)*sizeof(char*));
     480                 : 
     481                 :     /* Make sure the array is NULL-terminated... it may not be if
     482                 :      * papszStrList was NULL before Realloc()
     483                 :      */
     484            2370 :     papszStrList[nSrcLines] = NULL;
     485                 : 
     486                 :     /* Make some room in the original list at the specified location 
     487                 :      * Note that we also have to move the NULL pointer at the end of
     488                 :      * the source StringList.
     489                 :      */
     490            2370 :     if (nInsertAtLineNo == -1 || nInsertAtLineNo > nSrcLines)
     491            1164 :         nInsertAtLineNo = nSrcLines;
     492                 : 
     493            2370 :     ppszSrc = papszStrList + nSrcLines;
     494            2370 :     ppszDst = papszStrList + nDstLines;
     495                 : 
     496            6752 :     for (i=nSrcLines; i>=nInsertAtLineNo; i--)
     497                 :     {
     498            4382 :         *ppszDst = *ppszSrc;
     499            4382 :         ppszDst--;
     500            4382 :         ppszSrc--;
     501                 :     }
     502                 : 
     503                 :     /* Copy the strings to the list */
     504            2370 :     ppszSrc = papszNewLines;
     505            2370 :     ppszDst = papszStrList + nInsertAtLineNo;
     506                 : 
     507           38866 :     for (; *ppszSrc != NULL; ppszSrc++, ppszDst++)
     508                 :     {
     509           36496 :         *ppszDst = CPLStrdup(*ppszSrc);
     510                 :     }
     511                 :     
     512            2370 :     return papszStrList;
     513                 : }
     514                 : 
     515                 : /**********************************************************************
     516                 :  *                       CSLInsertString()
     517                 :  *
     518                 :  * Insert a string at a given line number inside a StringList 
     519                 :  *
     520                 :  * nInsertAtLineNo is a 0-based line index before which the new string
     521                 :  * should be inserted.  If this value is -1 or is larger than the actual 
     522                 :  * number of strings in the list then the string is added at the end
     523                 :  * of the source StringList.
     524                 :  *
     525                 :  * Returns the modified StringList.
     526                 :  **********************************************************************/
     527              78 : char **CSLInsertString(char **papszStrList, int nInsertAtLineNo, 
     528                 :                        const char *pszNewLine)
     529                 : {
     530                 :     char *apszList[2];
     531                 : 
     532                 :     /* Create a temporary StringList and call CSLInsertStrings()
     533                 :      */
     534              78 :     apszList[0] = (char *) pszNewLine;
     535              78 :     apszList[1] = NULL;
     536                 : 
     537              78 :     return CSLInsertStrings(papszStrList, nInsertAtLineNo, apszList);
     538                 : }
     539                 : 
     540                 : 
     541                 : /**********************************************************************
     542                 :  *                       CSLRemoveStrings()
     543                 :  *
     544                 :  * Remove strings inside a StringList 
     545                 :  *
     546                 :  * nFirstLineToDelete is the 0-based line index of the first line to 
     547                 :  * remove. If this value is -1 or is larger than the actual 
     548                 :  * number of strings in list then the nNumToRemove last strings are
     549                 :  * removed.
     550                 :  *
     551                 :  * If ppapszRetStrings != NULL then the deleted strings won't be
     552                 :  * free'd, they will be stored in a new StringList and the pointer to
     553                 :  * this new list will be returned in *ppapszRetStrings.
     554                 :  *
     555                 :  * Returns the modified StringList.
     556                 :  **********************************************************************/
     557             134 : char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete,
     558                 :                         int nNumToRemove, char ***ppapszRetStrings)
     559                 : {
     560                 :     int     i, nSrcLines, nDstLines;
     561                 :     char    **ppszSrc, **ppszDst;
     562                 : 
     563             134 :     nSrcLines = CSLCount(papszStrList);
     564             134 :     nDstLines = nSrcLines - nNumToRemove;
     565                 : 
     566             134 :     if (nNumToRemove < 1 || nSrcLines == 0)
     567               0 :         return papszStrList;    /* Nothing to do!*/
     568                 : 
     569                 :     /* If operation will result in an empty StringList then don't waste
     570                 :      * time here!
     571                 :      */
     572             134 :     if (nDstLines < 1)
     573                 :     {
     574              38 :         CSLDestroy(papszStrList);
     575              38 :         return NULL;
     576                 :     }
     577                 : 
     578                 :     
     579                 :     /* Remove lines from the source StringList...
     580                 :      * Either free() each line or store them to a new StringList depending on
     581                 :      * the caller's choice.
     582                 :      */
     583              96 :     ppszDst = papszStrList + nFirstLineToDelete;
     584                 : 
     585              96 :     if (ppapszRetStrings == NULL)
     586                 :     {
     587                 :         /* free() all the strings that will be removed.
     588                 :          */
     589             192 :         for (i=0; i < nNumToRemove; i++)
     590                 :         {
     591              96 :             CPLFree(*ppszDst);
     592              96 :             *ppszDst = NULL;
     593                 :         }
     594                 :     }
     595                 :     else
     596                 :     {
     597                 :         /* Store the strings to remove in a new StringList
     598                 :          */
     599               0 :         *ppapszRetStrings = (char **)CPLCalloc(nNumToRemove+1, sizeof(char*));
     600                 : 
     601               0 :         for (i=0; i < nNumToRemove; i++)
     602                 :         {
     603               0 :             (*ppapszRetStrings)[i] = *ppszDst;
     604               0 :             *ppszDst = NULL;
     605               0 :             ppszDst++;
     606                 :         }
     607                 :     }
     608                 : 
     609                 : 
     610                 :     /* Shift down all the lines that follow the lines to remove.
     611                 :      */
     612              96 :     if (nFirstLineToDelete == -1 || nFirstLineToDelete > nSrcLines)
     613               0 :         nFirstLineToDelete = nDstLines;
     614                 : 
     615              96 :     ppszSrc = papszStrList + nFirstLineToDelete + nNumToRemove;
     616              96 :     ppszDst = papszStrList + nFirstLineToDelete;
     617                 : 
     618             422 :     for ( ; *ppszSrc != NULL; ppszSrc++, ppszDst++)
     619                 :     {
     620             326 :         *ppszDst = *ppszSrc;
     621                 :     }
     622                 :     /* Move the NULL pointer at the end of the StringList     */
     623              96 :     *ppszDst = *ppszSrc; 
     624                 : 
     625                 :     /* At this point, we could realloc() papszStrList to a smaller size, but
     626                 :      * since this array will likely grow again in further operations on the
     627                 :      * StringList we'll leave it as it is.
     628                 :      */
     629                 : 
     630              96 :     return papszStrList;
     631                 : }
     632                 : 
     633                 : /************************************************************************/
     634                 : /*                           CSLFindString()                            */
     635                 : /************************************************************************/
     636                 : 
     637                 : /**
     638                 :  * Find a string within a string list.
     639                 :  *
     640                 :  * Returns the index of the entry in the string list that contains the 
     641                 :  * target string.  The string in the string list must be a full match for
     642                 :  * the target, but the search is case insensitive. 
     643                 :  * 
     644                 :  * @param papszList the string list to be searched.
     645                 :  * @param pszTarget the string to be searched for. 
     646                 :  * 
     647                 :  * @return the index of the string within the list or -1 on failure.
     648                 :  */
     649                 : 
     650         3557262 : int CSLFindString( char ** papszList, const char * pszTarget )
     651                 : 
     652                 : {
     653                 :     int         i;
     654                 : 
     655         3557262 :     if( papszList == NULL )
     656          406156 :         return -1;
     657                 : 
     658         9942837 :     for( i = 0; papszList[i] != NULL; i++ )
     659                 :     {
     660         8845881 :         if( EQUAL(papszList[i],pszTarget) )
     661         2054150 :             return i;
     662                 :     }
     663                 : 
     664         1096956 :     return -1;
     665                 : }
     666                 : 
     667                 : /************************************************************************/
     668                 : /*                           CSLPartialFindString()                     */
     669                 : /************************************************************************/
     670                 : 
     671                 : /**
     672                 :  * Find a substring within a string list.
     673                 :  *
     674                 :  * Returns the index of the entry in the string list that contains the 
     675                 :  * target string as a substring.  The search is case sensitive (unlike 
     676                 :  * CSLFindString()). 
     677                 :  * 
     678                 :  * @param papszHaystack the string list to be searched.
     679                 :  * @param pszNeedle the substring to be searched for. 
     680                 :  * 
     681                 :  * @return the index of the string within the list or -1 on failure.
     682                 :  */
     683                 : 
     684               6 : int CSLPartialFindString( char **papszHaystack, const char * pszNeedle )
     685                 : {
     686                 :     int i;
     687               6 :     if (papszHaystack == NULL || pszNeedle == NULL)
     688               0 :         return -1;
     689                 : 
     690              22 :     for (i = 0; papszHaystack[i] != NULL; i++) 
     691                 :     {
     692              16 :         if (strstr(papszHaystack[i],pszNeedle))
     693               0 :             return i;
     694                 :     }
     695                 : 
     696               6 :     return -1;
     697                 : }
     698                 : 
     699                 : 
     700                 : /**********************************************************************
     701                 :  *                       CSLTokenizeString()
     702                 :  *
     703                 :  * Tokenizes a string and returns a StringList with one string for
     704                 :  * each token.
     705                 :  **********************************************************************/
     706           10076 : char    **CSLTokenizeString( const char *pszString )
     707                 : {
     708           10076 :     return CSLTokenizeString2( pszString, " ", CSLT_HONOURSTRINGS );
     709                 : }
     710                 : 
     711                 : /************************************************************************/
     712                 : /*                      CSLTokenizeStringComplex()                      */
     713                 : /*                                                                      */
     714                 : /*      Obsolete tokenizing api.                                        */
     715                 : /************************************************************************/
     716                 : 
     717         2883632 : char ** CSLTokenizeStringComplex( const char * pszString,
     718                 :                                   const char * pszDelimiters,
     719                 :                                   int bHonourStrings, int bAllowEmptyTokens )
     720                 : 
     721                 : {
     722         2883632 :     int         nFlags = 0;
     723                 : 
     724         2883632 :     if( bHonourStrings )
     725         2813780 :         nFlags |= CSLT_HONOURSTRINGS;
     726         2883632 :     if( bAllowEmptyTokens )
     727             992 :         nFlags |= CSLT_ALLOWEMPTYTOKENS;
     728                 : 
     729         2883632 :     return CSLTokenizeString2( pszString, pszDelimiters, nFlags );
     730                 : }
     731                 : 
     732                 : /************************************************************************/
     733                 : /*                         CSLTokenizeString2()                         */
     734                 : /************************************************************************/
     735                 : 
     736                 : /**
     737                 :  * Tokenize a string. 
     738                 :  *
     739                 :  * This function will split a string into tokens based on specified'
     740                 :  * delimeter(s) with a variety of options.  The returned result is a
     741                 :  * string list that should be freed with CSLDestroy() when no longer
     742                 :  * needed.
     743                 :  * 
     744                 :  * The available parsing options are:
     745                 :  * 
     746                 :  * - CSLT_ALLOWEMPTYTOKENS: allow the return of empty tokens when two 
     747                 :  * delimiters in a row occur with no other text between them.  If not set, 
     748                 :  * empty tokens will be discarded;
     749                 :  * - CSLT_STRIPLEADSPACES: strip leading space characters from the token (as
     750                 :  * reported by isspace());
     751                 :  * - CSLT_STRIPENDSPACES: strip ending space characters from the token (as
     752                 :  * reported by isspace());
     753                 :  * - CSLT_HONOURSTRINGS: double quotes can be used to hold values that should 
     754                 :  * not be broken into multiple tokens; 
     755                 :  * - CSLT_PRESERVEQUOTES: string quotes are carried into the tokens when this
     756                 :  * is set, otherwise they are removed;
     757                 :  * - CSLT_PRESERVEESCAPES: if set backslash escapes (for backslash itself, 
     758                 :  * and for literal double quotes) will be preserved in the tokens, otherwise
     759                 :  * the backslashes will be removed in processing.
     760                 :  *
     761                 :  * \b Example:
     762                 :  * 
     763                 :  * Parse a string into tokens based on various white space (space, newline, 
     764                 :  * tab) and then print out results and cleanup.  Quotes may be used to hold 
     765                 :  * white space in tokens.
     766                 : 
     767                 : \code
     768                 :     char **papszTokens;
     769                 :     int i;
     770                 : 
     771                 :     papszTokens = 
     772                 :         CSLTokenizeString2( pszCommand, " \t\n", 
     773                 :                             CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS );
     774                 : 
     775                 :     for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++ )
     776                 :         printf( "arg %d: '%s'", papszTokens[i] );
     777                 :     CSLDestroy( papszTokens );
     778                 : \endcode
     779                 : 
     780                 :  * @param pszString the string to be split into tokens.
     781                 :  * @param pszDelimiters one or more characters to be used as token delimeters.
     782                 :  * @param nCSLTFlags an ORing of one or more of the CSLT_ flag values.
     783                 :  *
     784                 :  * @return a string list of tokens owned by the caller.
     785                 :  */
     786                 : 
     787         2938914 : char ** CSLTokenizeString2( const char * pszString,
     788                 :                             const char * pszDelimiters,
     789                 :                             int nCSLTFlags )
     790                 : 
     791                 : {
     792         2938914 :     if( pszString == NULL )
     793               0 :         return (char **) CPLCalloc(sizeof(char *),1);
     794         2938914 :     CPLStringList oRetList;
     795                 :     char        *pszToken;
     796                 :     int         nTokenMax, nTokenLen;
     797         2938914 :     int         bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS);
     798         2938914 :     int         bAllowEmptyTokens = (nCSLTFlags & CSLT_ALLOWEMPTYTOKENS);
     799         2938914 :     int         bStripLeadSpaces = (nCSLTFlags & CSLT_STRIPLEADSPACES);
     800         2938914 :     int         bStripEndSpaces = (nCSLTFlags & CSLT_STRIPENDSPACES);
     801                 : 
     802         2938914 :     pszToken = (char *) CPLCalloc(10,1);
     803         2938914 :     nTokenMax = 10;
     804                 :     
     805         9444540 :     while( pszString != NULL && *pszString != '\0' )
     806                 :     {
     807         3566712 :         int     bInString = FALSE;
     808         3566712 :         int     bStartString = TRUE;
     809                 : 
     810         3566712 :         nTokenLen = 0;
     811                 :         
     812                 :         /* Try to find the next delimeter, marking end of token */
     813        25992732 :         for( ; *pszString != '\0'; pszString++ )
     814                 :         {
     815                 : 
     816                 :             /* End if this is a delimeter skip it and break. */
     817        23082299 :             if( !bInString && strchr(pszDelimiters, *pszString) != NULL )
     818                 :             {
     819          656279 :                 pszString++;
     820          656279 :                 break;
     821                 :             }
     822                 :             
     823                 :             /* If this is a quote, and we are honouring constant
     824                 :                strings, then process the constant strings, with out delim
     825                 :                but don't copy over the quotes */
     826        22426020 :             if( bHonourStrings && *pszString == '"' )
     827                 :             {
     828            3668 :                 if( nCSLTFlags & CSLT_PRESERVEQUOTES )
     829                 :                 {
     830             404 :                     pszToken[nTokenLen] = *pszString;
     831             404 :                     nTokenLen++;
     832                 :                 }
     833                 : 
     834            3668 :                 if( bInString )
     835                 :                 {
     836            1784 :                     bInString = FALSE;
     837            1784 :                     continue;
     838                 :                 }
     839                 :                 else
     840                 :                 {
     841            1884 :                     bInString = TRUE;
     842            1884 :                     continue;
     843                 :                 }
     844                 :             }
     845                 : 
     846                 :             /*
     847                 :              * Within string constants we allow for escaped quotes, but in
     848                 :              * processing them we will unescape the quotes and \\ sequence
     849                 :              * reduces to \
     850                 :              */
     851        22422352 :             if( bInString && pszString[0] == '\\' )
     852                 :             {
     853               0 :                 if ( pszString[1] == '"' || pszString[1] == '\\' )
     854                 :                 {
     855               0 :                     if( nCSLTFlags & CSLT_PRESERVEESCAPES )
     856                 :                     {
     857               0 :                         pszToken[nTokenLen] = *pszString;
     858               0 :                         nTokenLen++;
     859                 :                     }
     860                 : 
     861               0 :                     pszString++;
     862                 :                 }
     863                 :             }
     864                 : 
     865                 :             /*
     866                 :              * Strip spaces at the token start if requested.
     867                 :              */
     868        22422352 :             if ( !bInString && bStripLeadSpaces
     869                 :                  && bStartString && isspace((unsigned char)*pszString) )
     870            3578 :                 continue;
     871                 : 
     872        22418774 :             bStartString = FALSE;
     873                 : 
     874                 :             /*
     875                 :              * Extend token buffer if we are running close to its end.
     876                 :              */
     877        22418774 :             if( nTokenLen >= nTokenMax-3 )
     878                 :             {
     879          633578 :                 nTokenMax = nTokenMax * 2 + 10;
     880          633578 :                 pszToken = (char *) CPLRealloc( pszToken, nTokenMax );
     881                 :             }
     882                 : 
     883        22418774 :             pszToken[nTokenLen] = *pszString;
     884        22418774 :             nTokenLen++;
     885                 :         }
     886                 : 
     887                 :         /*
     888                 :          * Strip spaces at the token end if requested.
     889                 :          */
     890         3566712 :         if ( !bInString && bStripEndSpaces )
     891                 :         {
     892           13296 :             while ( nTokenLen && isspace((unsigned char)pszToken[nTokenLen - 1]) )
     893               0 :                 nTokenLen--;
     894                 :         }
     895                 : 
     896         3566712 :         pszToken[nTokenLen] = '\0';
     897                 : 
     898                 :         /*
     899                 :          * Add the token.
     900                 :          */
     901         3566712 :         if( pszToken[0] != '\0' || bAllowEmptyTokens )
     902         3499858 :             oRetList.AddString( pszToken );
     903                 :     }
     904                 : 
     905                 :     /*
     906                 :      * If the last token was empty, then we need to capture
     907                 :      * it now, as the loop would skip it.
     908                 :      */
     909         2938914 :     if( *pszString == '\0' && bAllowEmptyTokens && oRetList.Count() > 0 
     910                 :         && strchr(pszDelimiters,*(pszString-1)) != NULL )
     911                 :     {
     912              34 :         oRetList.AddString( "" );
     913                 :     }
     914                 : 
     915         2938914 :     CPLFree( pszToken );
     916                 : 
     917         2938914 :     if( oRetList.List() == NULL )
     918                 :     {
     919                 :         // we prefer to return empty lists as a pointer to 
     920                 :         // a null pointer since some client code might depend on this.
     921           23687 :         oRetList.Assign( (char**) CPLCalloc(sizeof(char**),1) );
     922                 :     }
     923                 : 
     924         2938914 :     return oRetList.StealList();
     925                 : }
     926                 : 
     927                 : /**********************************************************************
     928                 :  *                       CPLSPrintf()
     929                 :  *
     930                 :  * My own version of CPLSPrintf() that works with 10 static buffer.
     931                 :  *
     932                 :  * It returns a ref. to a static buffer that should not be freed and
     933                 :  * is valid only until the next call to CPLSPrintf(). 
     934                 :  *
     935                 :  * NOTE: This function should move to cpl_conv.cpp. 
     936                 :  **********************************************************************/
     937                 : /* For now, assume that a 8000 chars buffer will be enough.
     938                 :  */
     939                 : #define CPLSPrintf_BUF_SIZE 8000
     940                 : #define CPLSPrintf_BUF_Count 10
     941                 : 
     942        45407186 : const char *CPLSPrintf(const char *fmt, ...)
     943                 : {
     944                 :     va_list args;
     945                 : 
     946                 : /* -------------------------------------------------------------------- */
     947                 : /*      Get the thread local buffer ring data.                          */
     948                 : /* -------------------------------------------------------------------- */
     949        45407186 :     char *pachBufRingInfo = (char *) CPLGetTLS( CTLS_CPLSPRINTF );
     950                 : 
     951        45407186 :     if( pachBufRingInfo == NULL )
     952                 :     {
     953                 :         pachBufRingInfo = (char *) 
     954             177 :             CPLCalloc(1,sizeof(int)+CPLSPrintf_BUF_Count*CPLSPrintf_BUF_SIZE);
     955             177 :         CPLSetTLS( CTLS_CPLSPRINTF, pachBufRingInfo, TRUE );
     956                 :     }
     957                 : 
     958                 : /* -------------------------------------------------------------------- */
     959                 : /*      Work out which string in the "ring" we want to use this         */
     960                 : /*      time.                                                           */
     961                 : /* -------------------------------------------------------------------- */
     962        45407186 :     int *pnBufIndex = (int *) pachBufRingInfo;
     963        45407186 :     int nOffset = sizeof(int) + *pnBufIndex * CPLSPrintf_BUF_SIZE;
     964        45407186 :     char *pachBuffer = pachBufRingInfo + nOffset;
     965                 : 
     966        45407186 :     *pnBufIndex = (*pnBufIndex + 1) % CPLSPrintf_BUF_Count;
     967                 : 
     968                 : /* -------------------------------------------------------------------- */
     969                 : /*      Format the result.                                              */
     970                 : /* -------------------------------------------------------------------- */
     971                 : 
     972        45407186 :     va_start(args, fmt);
     973                 : #if defined(HAVE_VSNPRINTF)
     974        45407186 :     vsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE-1, fmt, args);
     975                 : #else
     976                 :     vsprintf(pachBuffer, fmt, args);
     977                 : #endif
     978        45407186 :     va_end(args);
     979                 :     
     980        45407186 :     return pachBuffer;
     981                 : }
     982                 : 
     983                 : /**********************************************************************
     984                 :  *                       CSLAppendPrintf()
     985                 :  *
     986                 :  * Use CPLSPrintf() to append a new line at the end of a StringList.
     987                 :  *
     988                 :  * Returns the modified StringList.
     989                 :  **********************************************************************/
     990               0 : char **CSLAppendPrintf(char **papszStrList, const char *fmt, ...)
     991                 : {
     992               0 :     CPLString osWork;
     993                 :     va_list args;
     994                 : 
     995               0 :     va_start( args, fmt );
     996               0 :     osWork.vPrintf( fmt, args );
     997               0 :     va_end( args );
     998                 : 
     999               0 :     return CSLAddString(papszStrList, osWork);
    1000                 : }
    1001                 : 
    1002                 : /************************************************************************/
    1003                 : /*                            CPLVASPrintf()                            */
    1004                 : /*                                                                      */
    1005                 : /*      This is intended to serve as an easy to use C callabable        */
    1006                 : /*      vasprintf() alternative.  Used in the geojson library for       */
    1007                 : /*      instance.                                                       */
    1008                 : /************************************************************************/
    1009                 : 
    1010            4048 : int CPLVASPrintf( char **buf, const char *fmt, va_list ap )
    1011                 : 
    1012                 : {
    1013            4048 :     CPLString osWork;
    1014                 : 
    1015            4048 :     osWork.vPrintf( fmt, ap );
    1016                 : 
    1017            4048 :     if( buf )
    1018            4048 :         *buf = CPLStrdup(osWork.c_str());
    1019                 : 
    1020            4048 :     return strlen(osWork);
    1021                 : }
    1022                 : 
    1023                 : /************************************************************************/
    1024                 : /*                         CSLTestBoolean()                             */
    1025                 : /************************************************************************/
    1026                 : 
    1027                 : /**
    1028                 :  * Test what boolean value contained in the string.
    1029                 :  *
    1030                 :  * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE.
    1031                 :  * Otherwise, TRUE will be returned.
    1032                 :  *
    1033                 :  * @param pszValue the string should be tested.
    1034                 :  * 
    1035                 :  * @return TRUE or FALSE.
    1036                 :  */
    1037                 : 
    1038         1616380 : int CSLTestBoolean( const char *pszValue )
    1039                 : {
    1040         1616380 :     if( EQUAL(pszValue,"NO")
    1041                 :         || EQUAL(pszValue,"FALSE") 
    1042                 :         || EQUAL(pszValue,"OFF") 
    1043                 :         || EQUAL(pszValue,"0") )
    1044         1556022 :         return FALSE;
    1045                 :     else
    1046           60358 :         return TRUE;
    1047                 : }
    1048                 : 
    1049                 : /**********************************************************************
    1050                 :  *                       CSLFetchBoolean()
    1051                 :  *
    1052                 :  * Check for boolean key value.
    1053                 :  *
    1054                 :  * In a StringList of "Name=Value" pairs, look to see if there is a key
    1055                 :  * with the given name, and if it can be interpreted as being TRUE.  If
    1056                 :  * the key appears without any "=Value" portion it will be considered true. 
    1057                 :  * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
    1058                 :  * if the key appears in the list it will be considered TRUE.  If the key
    1059                 :  * doesn't appear at all, the indicated default value will be returned. 
    1060                 :  * 
    1061                 :  * @param papszStrList the string list to search.
    1062                 :  * @param pszKey the key value to look for (case insensitive).
    1063                 :  * @param bDefault the value to return if the key isn't found at all. 
    1064                 :  * 
    1065                 :  * @return TRUE or FALSE 
    1066                 :  **********************************************************************/
    1067                 : 
    1068           41150 : int CSLFetchBoolean( char **papszStrList, const char *pszKey, int bDefault )
    1069                 : 
    1070                 : {
    1071                 :     const char *pszValue;
    1072                 : 
    1073           41150 :     if( CSLFindString( papszStrList, pszKey ) != -1 )
    1074               0 :         return TRUE;
    1075                 : 
    1076           41150 :     pszValue = CSLFetchNameValue(papszStrList, pszKey );
    1077           41150 :     if( pszValue == NULL )
    1078           37878 :         return bDefault;
    1079                 :     else 
    1080            3272 :         return CSLTestBoolean( pszValue );
    1081                 : }
    1082                 : 
    1083                 : /************************************************************************/
    1084                 : /*                     CSLFetchNameValueDefaulted()                     */
    1085                 : /************************************************************************/
    1086                 : 
    1087            2206 : const char *CSLFetchNameValueDef( char **papszStrList, 
    1088                 :                                   const char *pszName,
    1089                 :                                   const char *pszDefault )
    1090                 : 
    1091                 : {
    1092            2206 :     const char *pszResult = CSLFetchNameValue( papszStrList, pszName );
    1093            2206 :     if( pszResult )
    1094             226 :         return pszResult;
    1095                 :     else
    1096            1980 :         return pszDefault;
    1097                 : }
    1098                 : 
    1099                 : /**********************************************************************
    1100                 :  *                       CSLFetchNameValue()
    1101                 :  *
    1102                 :  * In a StringList of "Name=Value" pairs, look for the
    1103                 :  * first value associated with the specified name.  The search is not
    1104                 :  * case sensitive.
    1105                 :  * ("Name:Value" pairs are also supported for backward compatibility
    1106                 :  * with older stuff.)
    1107                 :  * 
    1108                 :  * Returns a reference to the value in the StringList that the caller
    1109                 :  * should not attempt to free.
    1110                 :  *
    1111                 :  * Returns NULL if the name is not found.
    1112                 :  **********************************************************************/
    1113         3965593 : const char *CSLFetchNameValue(char **papszStrList, const char *pszName)
    1114                 : {
    1115                 :     size_t nLen;
    1116                 : 
    1117         3965593 :     if (papszStrList == NULL || pszName == NULL)
    1118          225228 :         return NULL;
    1119                 : 
    1120         3740365 :     nLen = strlen(pszName);
    1121        28572989 :     while(*papszStrList != NULL)
    1122                 :     {
    1123        23860011 :         if (EQUALN(*papszStrList, pszName, nLen)
    1124         1383244 :             && ( (*papszStrList)[nLen] == '=' || 
    1125            1756 :                  (*papszStrList)[nLen] == ':' ) )
    1126                 :         {
    1127         1382752 :             return (*papszStrList)+nLen+1;
    1128                 :         }
    1129        21092259 :         papszStrList++;
    1130                 :     }
    1131         2357613 :     return NULL;
    1132                 : }
    1133                 : 
    1134                 : /************************************************************************/
    1135                 : /*                            CSLFindName()                             */
    1136                 : /************************************************************************/
    1137                 : 
    1138                 : /**
    1139                 :  * Find StringList entry with given key name.
    1140                 :  *
    1141                 :  * @param papszStrList the string list to search.
    1142                 :  * @param pszName the key value to look for (case insensitive).
    1143                 :  *
    1144                 :  * @return -1 on failure or the list index of the first occurance 
    1145                 :  * matching the given key.
    1146                 :  */
    1147                 : 
    1148             218 : int CSLFindName(char **papszStrList, const char *pszName)
    1149                 : {
    1150                 :     size_t nLen;
    1151             218 :     int    iIndex = 0;
    1152                 : 
    1153             218 :     if (papszStrList == NULL || pszName == NULL)
    1154              84 :         return -1;
    1155                 : 
    1156             134 :     nLen = strlen(pszName);
    1157             426 :     while(*papszStrList != NULL)
    1158                 :     {
    1159             214 :         if (EQUALN(*papszStrList, pszName, nLen)
    1160              28 :             && ( (*papszStrList)[nLen] == '=' || 
    1161               0 :                  (*papszStrList)[nLen] == ':' ) )
    1162                 :         {
    1163              28 :             return iIndex;
    1164                 :         }
    1165             158 :         iIndex++;
    1166             158 :         papszStrList++;
    1167                 :     }
    1168             106 :     return -1;
    1169                 : }
    1170                 : 
    1171                 : /**********************************************************************
    1172                 :  *                       CPLParseNameValue()
    1173                 :  **********************************************************************/
    1174                 : 
    1175                 : /**
    1176                 :  * Parse NAME=VALUE string into name and value components.
    1177                 :  *
    1178                 :  * Note that if ppszKey is non-NULL, the key (or name) portion will be
    1179                 :  * allocated using VSIMalloc(), and returned in that pointer.  It is the
    1180                 :  * applications responsibility to free this string, but the application should
    1181                 :  * not modify or free the returned value portion. 
    1182                 :  *
    1183                 :  * This function also support "NAME:VALUE" strings and will strip white
    1184                 :  * space from around the delimeter when forming name and value strings.
    1185                 :  *
    1186                 :  * Eventually CSLFetchNameValue() and friends may be modified to use 
    1187                 :  * CPLParseNameValue(). 
    1188                 :  * 
    1189                 :  * @param pszNameValue string in "NAME=VALUE" format. 
    1190                 :  * @param ppszKey optional pointer though which to return the name
    1191                 :  * portion. 
    1192                 :  * @return the value portion (pointing into original string). 
    1193                 :  */
    1194                 : 
    1195           21660 : const char *CPLParseNameValue(const char *pszNameValue, char **ppszKey )
    1196                 : 
    1197                 : {
    1198                 :     int  i;
    1199                 :     const char *pszValue;
    1200                 : 
    1201          283312 :     for( i = 0; pszNameValue[i] != '\0'; i++ )
    1202                 :     {
    1203          282646 :         if( pszNameValue[i] == '=' || pszNameValue[i] == ':' )
    1204                 :         {
    1205           20994 :             pszValue = pszNameValue + i + 1;
    1206           46236 :             while( *pszValue == ' ' || *pszValue == '\t' )
    1207            4248 :                 pszValue++;
    1208                 : 
    1209           20994 :             if( ppszKey != NULL )
    1210                 :             {
    1211           20964 :                 *ppszKey = (char *) CPLMalloc(i+1);
    1212           20964 :                 strncpy( *ppszKey, pszNameValue, i );
    1213           20964 :                 (*ppszKey)[i] = '\0';
    1214           83856 :                 while( i > 0 && 
    1215           41928 :                        ( (*ppszKey)[i] == ' ' || (*ppszKey)[i] == '\t') )
    1216                 :                 {
    1217               0 :                     (*ppszKey)[i] = '\0';
    1218               0 :                     i--;
    1219                 :                 }
    1220                 :             }
    1221                 : 
    1222           20994 :             return pszValue;
    1223                 :         }
    1224                 :     }
    1225                 : 
    1226             666 :     return NULL;
    1227                 : }
    1228                 : 
    1229                 : /**********************************************************************
    1230                 :  *                       CSLFetchNameValueMultiple()
    1231                 :  *
    1232                 :  * In a StringList of "Name=Value" pairs, look for all the
    1233                 :  * values with the specified name.  The search is not case
    1234                 :  * sensitive.
    1235                 :  * ("Name:Value" pairs are also supported for backward compatibility
    1236                 :  * with older stuff.)
    1237                 :  * 
    1238                 :  * Returns stringlist with one entry for each occurence of the
    1239                 :  * specified name.  The stringlist should eventually be destroyed
    1240                 :  * by calling CSLDestroy().
    1241                 :  *
    1242                 :  * Returns NULL if the name is not found.
    1243                 :  **********************************************************************/
    1244             860 : char **CSLFetchNameValueMultiple(char **papszStrList, const char *pszName)
    1245                 : {
    1246                 :     size_t nLen;
    1247             860 :     char **papszValues = NULL;
    1248                 : 
    1249             860 :     if (papszStrList == NULL || pszName == NULL)
    1250             616 :         return NULL;
    1251                 : 
    1252             244 :     nLen = strlen(pszName);
    1253             948 :     while(*papszStrList != NULL)
    1254                 :     {
    1255             504 :         if (EQUALN(*papszStrList, pszName, nLen)
    1256              44 :             && ( (*papszStrList)[nLen] == '=' || 
    1257               0 :                  (*papszStrList)[nLen] == ':' ) )
    1258                 :         {
    1259                 :             papszValues = CSLAddString(papszValues, 
    1260              44 :                                           (*papszStrList)+nLen+1);
    1261                 :         }
    1262             460 :         papszStrList++;
    1263                 :     }
    1264                 : 
    1265             244 :     return papszValues;
    1266                 : }
    1267                 : 
    1268                 : 
    1269                 : /**********************************************************************
    1270                 :  *                       CSLAddNameValue()
    1271                 :  *
    1272                 :  * Add a new entry to a StringList of "Name=Value" pairs,
    1273                 :  * ("Name:Value" pairs are also supported for backward compatibility
    1274                 :  * with older stuff.)
    1275                 :  * 
    1276                 :  * This function does not check if a "Name=Value" pair already exists
    1277                 :  * for that name and can generate multiple entryes for the same name.
    1278                 :  * Use CSLSetNameValue() if you want each name to have only one value.
    1279                 :  *
    1280                 :  * Returns the modified stringlist.
    1281                 :  **********************************************************************/
    1282          344034 : char **CSLAddNameValue(char **papszStrList, 
    1283                 :                     const char *pszName, const char *pszValue)
    1284                 : {
    1285                 :     char *pszLine;
    1286                 : 
    1287          344034 :     if (pszName == NULL || pszValue==NULL)
    1288               0 :         return papszStrList;
    1289                 : 
    1290          344034 :     pszLine = (char *) CPLMalloc(strlen(pszName)+strlen(pszValue)+2);
    1291          344034 :     sprintf( pszLine, "%s=%s", pszName, pszValue );
    1292          344034 :     papszStrList = CSLAddString(papszStrList, pszLine);
    1293          344034 :     CPLFree( pszLine );
    1294                 : 
    1295          344034 :     return papszStrList;
    1296                 : }
    1297                 : 
    1298                 : /************************************************************************/
    1299                 : /*                          CSLSetNameValue()                           */
    1300                 : /************************************************************************/
    1301                 : 
    1302                 : /**
    1303                 :  * Assign value to name in StringList.
    1304                 :  *
    1305                 :  * Set the value for a given name in a StringList of "Name=Value" pairs
    1306                 :  * ("Name:Value" pairs are also supported for backward compatibility
    1307                 :  * with older stuff.)
    1308                 :  * 
    1309                 :  * If there is already a value for that name in the list then the value
    1310                 :  * is changed, otherwise a new "Name=Value" pair is added.
    1311                 :  *
    1312                 :  * @param papszList the original list, the modified version is returned.
    1313                 :  * @param pszName the name to be assigned a value.  This should be a well
    1314                 :  * formed token (no spaces or very special characters). 
    1315                 :  * @param pszValue the value to assign to the name.  This should not contain
    1316                 :  * any newlines (CR or LF) but is otherwise pretty much unconstrained.  If
    1317                 :  * NULL any corresponding value will be removed.
    1318                 :  *
    1319                 :  * @return modified stringlist.
    1320                 :  */
    1321                 : 
    1322          332442 : char **CSLSetNameValue(char **papszList, 
    1323                 :                        const char *pszName, const char *pszValue)
    1324                 : {
    1325                 :     char **papszPtr;
    1326                 :     size_t nLen;
    1327                 : 
    1328          332442 :     if (pszName == NULL )
    1329             636 :         return papszList;
    1330                 : 
    1331          331806 :     nLen = strlen(pszName);
    1332          331806 :     papszPtr = papszList;
    1333        13243714 :     while(papszPtr && *papszPtr != NULL)
    1334                 :     {
    1335        12589518 :         if (EQUALN(*papszPtr, pszName, nLen)
    1336            4542 :             && ( (*papszPtr)[nLen] == '=' || 
    1337            2950 :                  (*papszPtr)[nLen] == ':' ) )
    1338                 :         {
    1339                 :             /* Found it!  
    1340                 :              * Change the value... make sure to keep the ':' or '='
    1341                 :              */
    1342                 :             char cSep;
    1343            1924 :             cSep = (*papszPtr)[nLen];
    1344                 : 
    1345            1924 :             CPLFree(*papszPtr);
    1346                 : 
    1347                 :             /* 
    1348                 :              * If the value is NULL, remove this entry completely/
    1349                 :              */
    1350            1924 :             if( pszValue == NULL )
    1351                 :             {
    1352             492 :                 while( papszPtr[1] != NULL )
    1353                 :                 {
    1354              60 :                     *papszPtr = papszPtr[1];
    1355              60 :                     papszPtr++;
    1356                 :                 }
    1357             216 :                 *papszPtr = NULL;
    1358                 :             }
    1359                 : 
    1360                 :             /*
    1361                 :              * Otherwise replace with new value.
    1362                 :              */
    1363                 :             else
    1364                 :             {
    1365            1708 :                 *papszPtr = (char *) CPLMalloc(strlen(pszName)+strlen(pszValue)+2);
    1366            1708 :                 sprintf( *papszPtr, "%s%c%s", pszName, cSep, pszValue );
    1367                 :             }
    1368            1924 :             return papszList;
    1369                 :         }
    1370        12580102 :         papszPtr++;
    1371                 :     }
    1372                 : 
    1373          329882 :     if( pszValue == NULL )
    1374              32 :         return papszList;
    1375                 : 
    1376                 :     /* The name does not exist yet... create a new entry
    1377                 :      */
    1378          329850 :     return CSLAddNameValue(papszList, pszName, pszValue);
    1379                 : }
    1380                 : 
    1381                 : /************************************************************************/
    1382                 : /*                      CSLSetNameValueSeparator()                      */
    1383                 : /************************************************************************/
    1384                 : 
    1385                 : /**
    1386                 :  * Replace the default separator (":" or "=") with the passed separator
    1387                 :  * in the given name/value list. 
    1388                 :  *
    1389                 :  * Note that if a separator other than ":" or "=" is used, the resulting
    1390                 :  * list will not be manipulatable by the CSL name/value functions any more.
    1391                 :  *
    1392                 :  * The CPLParseNameValue() function is used to break the existing lines, 
    1393                 :  * and it also strips white space from around the existing delimiter, thus
    1394                 :  * the old separator, and any white space will be replaced by the new
    1395                 :  * separator.  For formatting purposes it may be desireable to include some
    1396                 :  * white space in the new separator.  eg. ": " or " = ".
    1397                 :  * 
    1398                 :  * @param papszList the list to update.  Component strings may be freed
    1399                 :  * but the list array will remain at the same location.
    1400                 :  *
    1401                 :  * @param pszSeparator the new separator string to insert.  
    1402                 :  *
    1403                 :  */
    1404                 : 
    1405             132 : void CSLSetNameValueSeparator( char ** papszList, const char *pszSeparator )
    1406                 : 
    1407                 : {
    1408             132 :     int         nLines = CSLCount(papszList), iLine;
    1409                 : 
    1410            2792 :     for( iLine = 0; iLine < nLines; iLine++ )
    1411                 :     {
    1412            2660 :         char        *pszKey = NULL;
    1413                 :         const char  *pszValue;
    1414                 :         char        *pszNewLine;
    1415                 : 
    1416            2660 :         pszValue = CPLParseNameValue( papszList[iLine], &pszKey );
    1417            2660 :         if( pszValue == NULL || pszKey == NULL )
    1418               0 :             continue;
    1419                 :         
    1420                 :         pszNewLine = (char *) CPLMalloc( strlen(pszValue) + strlen(pszKey)
    1421            2660 :                                          + strlen(pszSeparator) + 1 );
    1422            2660 :         strcpy( pszNewLine, pszKey );
    1423            2660 :         strcat( pszNewLine, pszSeparator );
    1424            2660 :         strcat( pszNewLine, pszValue );
    1425            2660 :         CPLFree( papszList[iLine] );
    1426            2660 :         papszList[iLine] = pszNewLine;
    1427            2660 :         CPLFree( pszKey );
    1428                 :     }
    1429             132 : }
    1430                 : 
    1431                 : /************************************************************************/
    1432                 : /*                          CPLEscapeString()                           */
    1433                 : /************************************************************************/
    1434                 : 
    1435                 : /**
    1436                 :  * Apply escaping to string to preserve special characters.
    1437                 :  *
    1438                 :  * This function will "escape" a variety of special characters
    1439                 :  * to make the string suitable to embed within a string constant
    1440                 :  * or to write within a text stream but in a form that can be
    1441                 :  * reconstitued to it's original form.  The escaping will even preserve
    1442                 :  * zero bytes allowing preservation of raw binary data.
    1443                 :  *
    1444                 :  * CPLES_BackslashQuotable(0): This scheme turns a binary string into 
    1445                 :  * a form suitable to be placed within double quotes as a string constant.
    1446                 :  * The backslash, quote, '\\0' and newline characters are all escaped in 
    1447                 :  * the usual C style. 
    1448                 :  *
    1449                 :  * CPLES_XML(1): This scheme converts the '<', '>', '"' and '&' characters into
    1450                 :  * their XML/HTML equivelent (&lt;, &gt;, &quot; and &amp;) making a string safe
    1451                 :  * to embed as CDATA within an XML element.  The '\\0' is not escaped and 
    1452                 :  * should not be included in the input.
    1453                 :  *
    1454                 :  * CPLES_URL(2): Everything except alphanumerics and the underscore are 
    1455                 :  * converted to a percent followed by a two digit hex encoding of the character
    1456                 :  * (leading zero supplied if needed).  This is the mechanism used for encoding
    1457                 :  * values to be passed in URLs.
    1458                 :  *
    1459                 :  * CPLES_SQL(3): All single quotes are replaced with two single quotes.  
    1460                 :  * Suitable for use when constructing literal values for SQL commands where
    1461                 :  * the literal will be enclosed in single quotes.
    1462                 :  *
    1463                 :  * CPLES_CSV(4): If the values contains commas, semicolons, tabs, double quotes, or newlines it 
    1464                 :  * placed in double quotes, and double quotes in the value are doubled.
    1465                 :  * Suitable for use when constructing field values for .csv files.  Note that
    1466                 :  * CPLUnescapeString() currently does not support this format, only 
    1467                 :  * CPLEscapeString().  See cpl_csv.cpp for csv parsing support.
    1468                 :  *
    1469                 :  * @param pszInput the string to escape.  
    1470                 :  * @param nLength The number of bytes of data to preserve.  If this is -1
    1471                 :  * the strlen(pszString) function will be used to compute the length.
    1472                 :  * @param nScheme the encoding scheme to use.  
    1473                 :  *
    1474                 :  * @return an escaped, zero terminated string that should be freed with 
    1475                 :  * CPLFree() when no longer needed.
    1476                 :  */
    1477                 : 
    1478           99498 : char *CPLEscapeString( const char *pszInput, int nLength, 
    1479                 :                        int nScheme )
    1480                 : 
    1481                 : {
    1482                 :     char        *pszOutput;
    1483                 :     char        *pszShortOutput;
    1484                 : 
    1485           99498 :     if( nLength == -1 )
    1486           99400 :         nLength = strlen(pszInput);
    1487                 : 
    1488           99498 :     pszOutput = (char *) CPLMalloc( nLength * 6 + 1 );
    1489                 :     
    1490           99498 :     if( nScheme == CPLES_BackslashQuotable )
    1491                 :     {
    1492              66 :         int iOut = 0, iIn;
    1493                 : 
    1494         2459332 :         for( iIn = 0; iIn < nLength; iIn++ )
    1495                 :         {
    1496         2459266 :             if( pszInput[iIn] == '\0' )
    1497                 :             {
    1498          702836 :                 pszOutput[iOut++] = '\\';
    1499          702836 :                 pszOutput[iOut++] = '0';
    1500                 :             }
    1501         1756430 :             else if( pszInput[iIn] == '\n' )
    1502                 :             {
    1503            7574 :                 pszOutput[iOut++] = '\\';
    1504            7574 :                 pszOutput[iOut++] = 'n';
    1505                 :             }
    1506         1748856 :             else if( pszInput[iIn] == '"' )
    1507                 :             {
    1508            4938 :                 pszOutput[iOut++] = '\\';
    1509            4938 :                 pszOutput[iOut++] = '\"';
    1510                 :             }
    1511         1743918 :             else if( pszInput[iIn] == '\\' )
    1512                 :             {
    1513            5030 :                 pszOutput[iOut++] = '\\';
    1514            5030 :                 pszOutput[iOut++] = '\\';
    1515                 :             }
    1516                 :             else
    1517         1738888 :                 pszOutput[iOut++] = pszInput[iIn];
    1518                 :         }
    1519              66 :         pszOutput[iOut] = '\0';
    1520                 :     }
    1521           99432 :     else if( nScheme == CPLES_URL ) /* Untested at implementation */
    1522                 :     {
    1523             446 :         int iOut = 0, iIn;
    1524                 : 
    1525           12834 :         for( iIn = 0; iIn < nLength; iIn++ )
    1526                 :         {
    1527           34342 :             if( (pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z')
    1528            3010 :                 || (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z')
    1529            3984 :                 || (pszInput[iIn] >= '0' && pszInput[iIn] <= '9')
    1530            4172 :                 || pszInput[iIn] == '_' || pszInput[iIn] == '.' )
    1531                 :             {
    1532           10788 :                 pszOutput[iOut++] = pszInput[iIn];
    1533                 :             }
    1534                 :             else
    1535                 :             {
    1536            1600 :                 sprintf( pszOutput+iOut, "%%%02X", ((unsigned char*)pszInput)[iIn] );
    1537            1600 :                 iOut += 3;
    1538                 :             }
    1539                 :         }
    1540             446 :         pszOutput[iOut] = '\0';
    1541                 :     }
    1542          197020 :     else if( nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES )
    1543                 :     {
    1544           98034 :         int iOut = 0, iIn;
    1545                 : 
    1546         2685658 :         for( iIn = 0; iIn < nLength; iIn++ )
    1547                 :         {
    1548         2587624 :             if( pszInput[iIn] == '<' )
    1549                 :             {
    1550              14 :                 pszOutput[iOut++] = '&';
    1551              14 :                 pszOutput[iOut++] = 'l';
    1552              14 :                 pszOutput[iOut++] = 't';
    1553              14 :                 pszOutput[iOut++] = ';';
    1554                 :             }
    1555         2587610 :             else if( pszInput[iIn] == '>' )
    1556                 :             {
    1557              14 :                 pszOutput[iOut++] = '&';
    1558              14 :                 pszOutput[iOut++] = 'g';
    1559              14 :                 pszOutput[iOut++] = 't';
    1560              14 :                 pszOutput[iOut++] = ';';
    1561                 :             }
    1562         2587596 :             else if( pszInput[iIn] == '&' )
    1563                 :             {
    1564             242 :                 pszOutput[iOut++] = '&';
    1565             242 :                 pszOutput[iOut++] = 'a';
    1566             242 :                 pszOutput[iOut++] = 'm';
    1567             242 :                 pszOutput[iOut++] = 'p';
    1568             242 :                 pszOutput[iOut++] = ';';
    1569                 :             }
    1570         2587534 :             else if( pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES )
    1571                 :             {
    1572             180 :                 pszOutput[iOut++] = '&';
    1573             180 :                 pszOutput[iOut++] = 'q';
    1574             180 :                 pszOutput[iOut++] = 'u';
    1575             180 :                 pszOutput[iOut++] = 'o';
    1576             180 :                 pszOutput[iOut++] = 't';
    1577             180 :                 pszOutput[iOut++] = ';';
    1578                 :             }
    1579         2594964 :             else if( ((GByte*)pszInput)[iIn] < 0x20 
    1580            4972 :                      && pszInput[iIn] != 0x9
    1581            2818 :                      && pszInput[iIn] != 0xA 
    1582               0 :                      && pszInput[iIn] != 0xD ) 
    1583                 :             {
    1584                 :                 // These control characters are unrepresentable in XML format, 
    1585                 :                 // so we just drop them.  #4117
    1586                 :             }
    1587                 :             else
    1588         2587174 :                 pszOutput[iOut++] = pszInput[iIn];
    1589                 :         }
    1590           98034 :         pszOutput[iOut] = '\0';
    1591                 :     }
    1592             952 :     else if( nScheme == CPLES_SQL )
    1593                 :     {
    1594              32 :         int iOut = 0, iIn;
    1595                 : 
    1596             156 :         for( iIn = 0; iIn < nLength; iIn++ )
    1597                 :         {
    1598             124 :             if( pszInput[iIn] == '\'' )
    1599                 :             {
    1600               0 :                 pszOutput[iOut++] = '\'';
    1601               0 :                 pszOutput[iOut++] = '\'';
    1602                 :             }
    1603                 :             else
    1604             124 :                 pszOutput[iOut++] = pszInput[iIn];
    1605                 :         }
    1606              32 :         pszOutput[iOut] = '\0';
    1607                 :     }
    1608             920 :     else if( nScheme == CPLES_CSV )
    1609                 :     {
    1610            1818 :         if( strchr( pszInput, '\"' ) == NULL
    1611                 :             && strchr( pszInput, ',') == NULL
    1612                 :             && strchr( pszInput, ';') == NULL
    1613                 :             && strchr( pszInput, '\t') == NULL
    1614                 :             && strchr( pszInput, 10) == NULL 
    1615                 :             && strchr( pszInput, 13) == NULL )
    1616                 :         {
    1617             898 :             strcpy( pszOutput, pszInput );
    1618                 :         }
    1619                 :         else
    1620                 :         {
    1621              22 :             int iOut = 1, iIn;
    1622                 : 
    1623              22 :             pszOutput[0] = '\"';
    1624                 : 
    1625             926 :             for( iIn = 0; iIn < nLength; iIn++ )
    1626                 :             {
    1627             904 :                 if( pszInput[iIn] == '\"' )
    1628                 :                 {
    1629              12 :                     pszOutput[iOut++] = '\"';
    1630              12 :                     pszOutput[iOut++] = '\"';
    1631                 :                 }
    1632             892 :                 else if( pszInput[iIn] == 13 )
    1633                 :                     /* drop DOS LF's in strings. */;
    1634                 :                 else
    1635             892 :                     pszOutput[iOut++] = pszInput[iIn];
    1636                 :             }
    1637              22 :             pszOutput[iOut++] = '\"';
    1638              22 :             pszOutput[iOut++] = '\0';
    1639                 :         }
    1640                 :     }
    1641                 :     else
    1642                 :     {
    1643               0 :         pszOutput[0] = '\0';
    1644                 :         CPLError( CE_Failure, CPLE_AppDefined, 
    1645                 :                   "Undefined escaping scheme (%d) in CPLEscapeString()",
    1646               0 :                   nScheme );
    1647                 :     }
    1648                 : 
    1649           99498 :     pszShortOutput = CPLStrdup( pszOutput );
    1650           99498 :     CPLFree( pszOutput );
    1651                 : 
    1652           99498 :     return pszShortOutput;
    1653                 : }
    1654                 : 
    1655                 : /************************************************************************/
    1656                 : /*                         CPLUnescapeString()                          */
    1657                 : /************************************************************************/
    1658                 : 
    1659                 : /**
    1660                 :  * Unescape a string.
    1661                 :  *
    1662                 :  * This function does the opposite of CPLEscapeString().  Given a string
    1663                 :  * with special values escaped according to some scheme, it will return a
    1664                 :  * new copy of the string returned to it's original form. 
    1665                 :  *
    1666                 :  * @param pszInput the input string.  This is a zero terminated string.
    1667                 :  * @param pnLength location to return the length of the unescaped string, 
    1668                 :  * which may in some cases include embedded '\\0' characters.
    1669                 :  * @param nScheme the escaped scheme to undo (see CPLEscapeString() for a
    1670                 :  * list). 
    1671                 :  * 
    1672                 :  * @return a copy of the unescaped string that should be freed by the 
    1673                 :  * application using CPLFree() when no longer needed.
    1674                 :  */
    1675                 : 
    1676            7268 : char *CPLUnescapeString( const char *pszInput, int *pnLength, int nScheme )
    1677                 : 
    1678                 : {
    1679                 :     char *pszOutput;
    1680            7268 :     int iOut=0, iIn;
    1681                 : 
    1682            7268 :     pszOutput = (char *) CPLMalloc(4 * strlen(pszInput)+1);
    1683            7268 :     pszOutput[0] = '\0';
    1684                 : 
    1685           14222 :     if( nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES  )
    1686                 :     {
    1687                 :         char ch;
    1688          247816 :         for( iIn = 0; (ch = pszInput[iIn]) != '\0'; iIn++ )
    1689                 :         {
    1690          240862 :             if( ch != '&' )
    1691                 :             {
    1692          229414 :                 pszOutput[iOut++] = ch;
    1693                 :             }
    1694           11448 :             else if( EQUALN(pszInput+iIn,"&lt;",4) )
    1695                 :             {
    1696              12 :                 pszOutput[iOut++] = '<';
    1697              12 :                 iIn += 3;
    1698                 :             }
    1699           11436 :             else if( EQUALN(pszInput+iIn,"&gt;",4) )
    1700                 :             {
    1701              12 :                 pszOutput[iOut++] = '>';
    1702              12 :                 iIn += 3;
    1703                 :             }
    1704           11424 :             else if( EQUALN(pszInput+iIn,"&amp;",5) )
    1705                 :             {
    1706             536 :                 pszOutput[iOut++] = '&';
    1707             536 :                 iIn += 4;
    1708                 :             }
    1709           10888 :             else if( EQUALN(pszInput+iIn,"&apos;",6) )
    1710                 :             {
    1711               0 :                 pszOutput[iOut++] = '\'';
    1712               0 :                 iIn += 5;
    1713                 :             }
    1714           10888 :             else if( EQUALN(pszInput+iIn,"&quot;",6) )
    1715                 :             {
    1716           10884 :                 pszOutput[iOut++] = '"';
    1717           10884 :                 iIn += 5;
    1718                 :             }
    1719               4 :             else if( EQUALN(pszInput+iIn,"&#x",3) )
    1720                 :             {
    1721               4 :                 wchar_t anVal[2] = {0 , 0};
    1722               4 :                 iIn += 3;
    1723                 : 
    1724               8 :                 while(TRUE)
    1725                 :                 {
    1726              12 :                     ch = pszInput[iIn ++];
    1727              12 :                     if (ch >= 'a' && ch <= 'f')
    1728               0 :                         anVal[0] = anVal[0] * 16 + ch - 'a' + 10;
    1729              12 :                     else if (ch >= 'A' && ch <= 'A')
    1730               0 :                         anVal[0] = anVal[0] * 16 + ch - 'A' + 10;
    1731              20 :                     else if (ch >= '0' && ch <= '9')
    1732               8 :                         anVal[0] = anVal[0] * 16 + ch - '0';
    1733                 :                     else
    1734                 :                         break;
    1735                 :                 }
    1736               4 :                 if (ch != ';')
    1737               0 :                     break;
    1738               4 :                 iIn --;
    1739                 : 
    1740               4 :                 char * pszUTF8 = CPLRecodeFromWChar( anVal, "WCHAR_T", CPL_ENC_UTF8);
    1741               4 :                 int nLen = strlen(pszUTF8);
    1742               4 :                 memcpy(pszOutput + iOut, pszUTF8, nLen);
    1743               4 :                 CPLFree(pszUTF8);
    1744               4 :                 iOut += nLen;
    1745                 :             }
    1746               0 :             else if( EQUALN(pszInput+iIn,"&#",2) )
    1747                 :             {
    1748                 :                 char ch;
    1749               0 :                 wchar_t anVal[2] = {0 , 0};
    1750               0 :                 iIn += 2;
    1751                 : 
    1752               0 :                 while(TRUE)
    1753                 :                 {
    1754               0 :                     ch = pszInput[iIn ++];
    1755               0 :                     if (ch >= '0' && ch <= '9')
    1756               0 :                         anVal[0] = anVal[0] * 10 + ch - '0';
    1757                 :                     else
    1758                 :                         break;
    1759                 :                 }
    1760               0 :                 if (ch != ';')
    1761               0 :                     break;
    1762               0 :                 iIn --;
    1763                 : 
    1764               0 :                 char * pszUTF8 = CPLRecodeFromWChar( anVal, "WCHAR_T", CPL_ENC_UTF8);
    1765               0 :                 int nLen = strlen(pszUTF8);
    1766               0 :                 memcpy(pszOutput + iOut, pszUTF8, nLen);
    1767               0 :                 CPLFree(pszUTF8);
    1768               0 :                 iOut += nLen;
    1769                 :             }
    1770                 :             else
    1771                 :             {
    1772                 :                 /* illegal escape sequence */
    1773                 :                 CPLDebug( "CPL",
    1774               0 :                           "Error unescaping CPLES_XML text, '&' character followed by unhandled escape sequence." );
    1775               0 :                 break;
    1776                 :             }
    1777                 :         }
    1778                 :     }
    1779             314 :     else if( nScheme == CPLES_URL )
    1780                 :     {
    1781             304 :         for( iIn = 0; pszInput[iIn] != '\0'; iIn++ )
    1782                 :         {
    1783             274 :             if( pszInput[iIn] == '%' 
    1784               0 :                 && pszInput[iIn+1] != '\0' 
    1785               0 :                 && pszInput[iIn+2] != '\0' )
    1786                 :             {
    1787               0 :                 int nHexChar = 0;
    1788                 : 
    1789               0 :                 if( pszInput[iIn+1] >= 'A' && pszInput[iIn+1] <= 'F' )
    1790               0 :                     nHexChar += 16 * (pszInput[iIn+1] - 'A' + 10);
    1791               0 :                 else if( pszInput[iIn+1] >= 'a' && pszInput[iIn+1] <= 'f' )
    1792               0 :                     nHexChar += 16 * (pszInput[iIn+1] - 'a' + 10);
    1793               0 :                 else if( pszInput[iIn+1] >= '0' && pszInput[iIn+1] <= '9' )
    1794               0 :                     nHexChar += 16 * (pszInput[iIn+1] - '0');
    1795                 :                 else
    1796                 :                     CPLDebug( "CPL", 
    1797                 :                               "Error unescaping CPLES_URL text, percent not "
    1798               0 :                               "followed by two hex digits." );
    1799                 :                     
    1800               0 :                 if( pszInput[iIn+2] >= 'A' && pszInput[iIn+2] <= 'F' )
    1801               0 :                     nHexChar += pszInput[iIn+2] - 'A' + 10;
    1802               0 :                 else if( pszInput[iIn+2] >= 'a' && pszInput[iIn+2] <= 'f' )
    1803               0 :                     nHexChar += pszInput[iIn+2] - 'a' + 10;
    1804               0 :                 else if( pszInput[iIn+2] >= '0' && pszInput[iIn+2] <= '9' )
    1805               0 :                     nHexChar += pszInput[iIn+2] - '0';
    1806                 :                 else
    1807                 :                     CPLDebug( "CPL", 
    1808                 :                               "Error unescaping CPLES_URL text, percent not "
    1809               0 :                               "followed by two hex digits." );
    1810                 : 
    1811               0 :                 pszOutput[iOut++] = (char) nHexChar;
    1812               0 :                 iIn += 2;
    1813                 :             }
    1814             274 :             else if( pszInput[iIn] == '+' )
    1815                 :             {
    1816               0 :                 pszOutput[iOut++] = ' ';
    1817                 :             }   
    1818                 :             else
    1819                 :             {
    1820             274 :                 pszOutput[iOut++] = pszInput[iIn];
    1821                 :             }
    1822                 :         }
    1823                 :     }
    1824             284 :     else if( nScheme == CPLES_SQL )
    1825                 :     {
    1826               0 :         for( iIn = 0; pszInput[iIn] != '\0'; iIn++ )
    1827                 :         {
    1828               0 :             if( pszInput[iIn] == '\'' && pszInput[iIn+1] == '\'' )
    1829                 :             {
    1830               0 :                 iIn++;
    1831               0 :                 pszOutput[iOut++] = pszInput[iIn];
    1832                 :             }
    1833                 :             else
    1834                 :             {
    1835               0 :                 pszOutput[iOut++] = pszInput[iIn];
    1836                 :             }
    1837                 :         }
    1838                 :     }
    1839                 :     else /* if( nScheme == CPLES_BackslashQuoteable ) */
    1840                 :     {
    1841          410450 :         for( iIn = 0; pszInput[iIn] != '\0'; iIn++ )
    1842                 :         {
    1843          410166 :             if( pszInput[iIn] == '\\' )
    1844                 :             {
    1845              62 :                 iIn++;
    1846              62 :                 if( pszInput[iIn] == 'n' )
    1847               0 :                     pszOutput[iOut++] = '\n';
    1848              62 :                 else if( pszInput[iIn] == '0' )
    1849               2 :                     pszOutput[iOut++] = '\0';
    1850                 :                 else 
    1851              60 :                     pszOutput[iOut++] = pszInput[iIn];
    1852                 :             }
    1853                 :             else
    1854                 :             {
    1855          410104 :                 pszOutput[iOut++] = pszInput[iIn];
    1856                 :             }
    1857                 :         }
    1858                 :     }
    1859                 : 
    1860            7268 :     pszOutput[iOut] = '\0';
    1861                 : 
    1862            7268 :     if( pnLength != NULL )
    1863             662 :         *pnLength = iOut;
    1864                 : 
    1865            7268 :     return pszOutput;
    1866                 : }
    1867                 : 
    1868                 : /************************************************************************/
    1869                 : /*                           CPLBinaryToHex()                           */
    1870                 : /************************************************************************/
    1871                 : 
    1872                 : /**
    1873                 :  * Binary to hexadecimal translation.
    1874                 :  *
    1875                 :  * @param nBytes number of bytes of binary data in pabyData.
    1876                 :  * @param pabyData array of data bytes to translate. 
    1877                 :  * 
    1878                 :  * @return hexadecimal translation, zero terminated.  Free with CPLFree().
    1879                 :  */
    1880                 : 
    1881             998 : char *CPLBinaryToHex( int nBytes, const GByte *pabyData )
    1882                 : 
    1883                 : {
    1884             998 :     char *pszHex = (char *) CPLMalloc(nBytes * 2 + 1 );
    1885                 :     int i;
    1886                 :     static const char achHex[] = "0123456789ABCDEF";
    1887                 : 
    1888             998 :     pszHex[nBytes*2] = '\0';
    1889                 : 
    1890          115442 :     for( i = 0; i < nBytes; i++ )
    1891                 :     {
    1892          114444 :         int nLow = pabyData[i] & 0x0f;
    1893          114444 :         int nHigh = (pabyData[i] & 0xf0) >> 4;
    1894                 : 
    1895          114444 :         pszHex[i*2] = achHex[nHigh];
    1896          114444 :         pszHex[i*2+1] = achHex[nLow];
    1897                 :     }
    1898                 : 
    1899             998 :     return pszHex;
    1900                 : }
    1901                 : 
    1902                 : 
    1903                 : /************************************************************************/
    1904                 : /*                           CPLHexToBinary()                           */
    1905                 : /************************************************************************/
    1906                 : 
    1907                 : /**
    1908                 :  * Hexadecimal to binary translation
    1909                 :  *
    1910                 :  * @param pszHex the input hex encoded string.
    1911                 :  * @param pnBytes the returned count of decoded bytes placed here.
    1912                 :  *
    1913                 :  * @return returns binary buffer of data - free with CPLFree().
    1914                 :  */
    1915                 : 
    1916             642 : GByte *CPLHexToBinary( const char *pszHex, int *pnBytes )
    1917                 : 
    1918                 : {
    1919             642 :     int     iSrc = 0, iDst = 0;
    1920             642 :     size_t  nHexLen = strlen(pszHex);
    1921                 : 
    1922                 :     GByte *pabyWKB;
    1923                 : 
    1924             642 :     pabyWKB = (GByte *) CPLMalloc(nHexLen / 2 + 2);
    1925                 : 
    1926          266304 :     while( pszHex[iSrc] != '\0' )
    1927                 :     {
    1928          512292 :         if( pszHex[iSrc] >= '0' && pszHex[iSrc] <= '9' )
    1929          247272 :             pabyWKB[iDst] = pszHex[iSrc] - '0';
    1930           35496 :         else if( pszHex[iSrc] >= 'A' && pszHex[iSrc] <= 'F' )
    1931           17748 :             pabyWKB[iDst] = pszHex[iSrc] - 'A' + 10;
    1932               0 :         else if( pszHex[iSrc] >= 'a' && pszHex[iSrc] <= 'f' )
    1933               0 :             pabyWKB[iDst] = pszHex[iSrc] - 'a' + 10;
    1934                 :         else 
    1935               0 :             break;
    1936                 : 
    1937          265020 :         pabyWKB[iDst] *= 16;
    1938                 : 
    1939          265020 :         iSrc++;
    1940                 : 
    1941          492718 :         if( pszHex[iSrc] >= '0' && pszHex[iSrc] <= '9' )
    1942          227698 :             pabyWKB[iDst] += pszHex[iSrc] - '0';
    1943           74644 :         else if( pszHex[iSrc] >= 'A' && pszHex[iSrc] <= 'F' )
    1944           37322 :             pabyWKB[iDst] += pszHex[iSrc] - 'A' + 10;
    1945               0 :         else if( pszHex[iSrc] >= 'a' && pszHex[iSrc] <= 'f' )
    1946               0 :             pabyWKB[iDst] += pszHex[iSrc] - 'a' + 10;
    1947                 :         else
    1948               0 :             break;
    1949                 : 
    1950          265020 :         iSrc++;
    1951          265020 :         iDst++;
    1952                 :     }
    1953                 :     
    1954             642 :     pabyWKB[iDst] = 0;
    1955             642 :     *pnBytes = iDst;
    1956                 : 
    1957             642 :     return pabyWKB;
    1958                 : }
    1959                 : 
    1960                 : 
    1961                 : 
    1962                 : /************************************************************************/
    1963                 : /*                         CPLGetValueType()                            */
    1964                 : /************************************************************************/
    1965                 : 
    1966                 : /**
    1967                 :  * Detect the type of the value contained in a string, whether it is
    1968                 :  * a real, an integer or a string
    1969                 :  * Leading and trailing spaces are skipped in the analysis.
    1970                 :  *
    1971                 :  * Note: in the context of this function, integer must be understood in a
    1972                 :  * broad sense. It does not mean that the value can fit into a 32 bit integer
    1973                 :  * for example. It might be larger.
    1974                 :  *
    1975                 :  * @param pszValue the string to analyze
    1976                 :  *
    1977                 :  * @return returns the type of the value contained in the string.
    1978                 :  */
    1979                 : 
    1980            7894 : CPLValueType CPLGetValueType(const char* pszValue)
    1981                 : {
    1982                 :     /*
    1983                 :     doubles : "+25.e+3", "-25.e-3", "25.e3", "25e3", " 25e3 "
    1984                 :     not doubles: "25e 3", "25e.3", "-2-5e3", "2-5e3", "25.25.3"
    1985                 :     */
    1986                 : 
    1987            7894 :     int bFoundDot = FALSE;
    1988            7894 :     int bFoundExponent = FALSE;
    1989            7894 :     int bIsLastCharExponent = FALSE;
    1990            7894 :     int bIsReal = FALSE;
    1991                 : 
    1992            7894 :     if (pszValue == NULL)
    1993               0 :         return CPL_VALUE_STRING;
    1994                 : 
    1995                 :     /* Skip leading + or - */
    1996            7894 :     if (*pszValue == '+' || *pszValue == '-')
    1997              28 :         pszValue ++;
    1998                 : 
    1999                 :     /* Skip leading spaces */
    2000           15800 :     while( isspace( (unsigned char)*pszValue ) )
    2001              12 :         pszValue ++;
    2002                 : 
    2003           27120 :     for(; *pszValue != '\0'; pszValue++ )
    2004                 :     {
    2005           22620 :         if( isdigit( *pszValue))
    2006                 :         {
    2007           18320 :             bIsLastCharExponent = FALSE;
    2008                 :             /* do nothing */
    2009                 :         }
    2010            4300 :         else if ( isspace ((unsigned char)*pszValue) )
    2011                 :         {
    2012               8 :             const char* pszTmp = pszValue;
    2013              24 :             while( isspace( (unsigned char)*pszTmp ) )
    2014               8 :                 pszTmp ++;
    2015               8 :             if (*pszTmp == 0)
    2016               0 :                 break;
    2017                 :             else
    2018               8 :                 return CPL_VALUE_STRING;
    2019                 :         }
    2020            4292 :         else if ( *pszValue == '-' || *pszValue == '+' )
    2021                 :         {
    2022              14 :             if (bIsLastCharExponent)
    2023                 :             {
    2024                 :                 /* do nothing */
    2025                 :             }
    2026                 :             else
    2027              14 :                 return CPL_VALUE_STRING;
    2028               0 :             bIsLastCharExponent = FALSE;
    2029                 :         }
    2030            4278 :         else if ( *pszValue == '.')
    2031                 :         {
    2032             850 :             bIsReal = TRUE;
    2033             850 :             if (!bFoundDot && bIsLastCharExponent == FALSE)
    2034             850 :                 bFoundDot = TRUE;
    2035                 :             else
    2036               0 :                 return CPL_VALUE_STRING;
    2037             850 :             bIsLastCharExponent = FALSE;
    2038                 :         }
    2039            3484 :         else if (*pszValue == 'D' || *pszValue == 'd'
    2040                 :                  || *pszValue == 'E' || *pszValue == 'e' )
    2041                 :         {
    2042              86 :             bIsReal = TRUE;
    2043              86 :             if (!bFoundExponent)
    2044              56 :                 bFoundExponent = TRUE;
    2045                 :             else
    2046              30 :                 return CPL_VALUE_STRING;
    2047              56 :             bIsLastCharExponent = TRUE;
    2048                 :         }
    2049                 :         else 
    2050                 :         {
    2051            3342 :             return CPL_VALUE_STRING;
    2052                 :         }
    2053                 :     }
    2054                 : 
    2055            4500 :     return (bIsReal) ? CPL_VALUE_REAL : CPL_VALUE_INTEGER;
    2056                 : }
    2057                 : 
    2058                 : /************************************************************************/
    2059                 : /*                              CPLStrlcpy()                            */
    2060                 : /************************************************************************/
    2061                 : 
    2062                 : /**
    2063                 :  * Copy source string to a destination buffer.
    2064                 :  *
    2065                 :  * This function ensures that the destination buffer is always NUL terminated
    2066                 :  * (provided that its length is at least 1).
    2067                 :  *
    2068                 :  * This function is designed to be a safer, more consistent, and less error
    2069                 :  * prone replacement for strncpy. Its contract is identical to libbsd's strlcpy.
    2070                 :  *
    2071                 :  * Truncation can be detected by testing if the return value of CPLStrlcpy
    2072                 :  * is greater or equal to nDestSize.
    2073                 : 
    2074                 : \verbatim
    2075                 : char szDest[5];
    2076                 : if (CPLStrlcpy(szDest, "abcde", sizeof(szDest)) >= sizeof(szDest))
    2077                 :     fprintf(stderr, "truncation occured !\n");
    2078                 : \endverbatim
    2079                 : 
    2080                 :  * @param pszDest   destination buffer
    2081                 :  * @param pszSrc    source string. Must be NUL terminated
    2082                 :  * @param nDestSize size of destination buffer (including space for the NUL terminator character)
    2083                 :  *
    2084                 :  * @return the length of the source string (=strlen(pszSrc))
    2085                 :  *
    2086                 :  * @since GDAL 1.7.0
    2087                 :  */
    2088         1327108 : size_t CPLStrlcpy(char* pszDest, const char* pszSrc, size_t nDestSize)
    2089                 : {
    2090         1327108 :     char* pszDestIter = pszDest;
    2091         1327108 :     const char* pszSrcIter = pszSrc;
    2092                 : 
    2093         1327108 :     if (nDestSize == 0)
    2094               0 :         return strlen(pszSrc);
    2095                 : 
    2096         1327108 :     nDestSize --;
    2097        11346013 :     while(nDestSize != 0 && *pszSrcIter != '\0')
    2098                 :     {
    2099         8691797 :         *pszDestIter = *pszSrcIter;
    2100         8691797 :         pszDestIter ++;
    2101         8691797 :         pszSrcIter ++;
    2102         8691797 :         nDestSize --;
    2103                 :     }
    2104         1327108 :     *pszDestIter = '\0';
    2105         1327108 :     return pszSrcIter - pszSrc + strlen(pszSrcIter);
    2106                 : }
    2107                 : 
    2108                 : /************************************************************************/
    2109                 : /*                              CPLStrlcat()                            */
    2110                 : /************************************************************************/
    2111                 : 
    2112                 : /**
    2113                 :  * Appends a source string to a destination buffer.
    2114                 :  *
    2115                 :  * This function ensures that the destination buffer is always NUL terminated
    2116                 :  * (provided that its length is at least 1 and that there is at least one byte
    2117                 :  * free in pszDest, that is to say strlen(pszDest_before) < nDestSize)
    2118                 :  *
    2119                 :  * This function is designed to be a safer, more consistent, and less error
    2120                 :  * prone replacement for strncat. Its contract is identical to libbsd's strlcat.
    2121                 :  *
    2122                 :  * Truncation can be detected by testing if the return value of CPLStrlcat
    2123                 :  * is greater or equal to nDestSize.
    2124                 : 
    2125                 : \verbatim
    2126                 : char szDest[5];
    2127                 : CPLStrlcpy(szDest, "ab", sizeof(szDest));
    2128                 : if (CPLStrlcat(szDest, "cde", sizeof(szDest)) >= sizeof(szDest))
    2129                 :     fprintf(stderr, "truncation occured !\n");
    2130                 : \endverbatim
    2131                 : 
    2132                 :  * @param pszDest   destination buffer. Must be NUL terminated before running CPLStrlcat
    2133                 :  * @param pszSrc    source string. Must be NUL terminated
    2134                 :  * @param nDestSize size of destination buffer (including space for the NUL terminator character)
    2135                 :  *
    2136                 :  * @return the thoretical length of the destination string after concatenation
    2137                 :  *         (=strlen(pszDest_before) + strlen(pszSrc)).
    2138                 :  *         If strlen(pszDest_before) >= nDestSize, then it returns nDestSize + strlen(pszSrc)
    2139                 :  *
    2140                 :  * @since GDAL 1.7.0
    2141                 :  */
    2142          364268 : size_t CPLStrlcat(char* pszDest, const char* pszSrc, size_t nDestSize)
    2143                 : {
    2144          364268 :     char* pszDestIter = pszDest;
    2145                 : 
    2146         8758418 :     while(nDestSize != 0 && *pszDestIter != '\0')
    2147                 :     {
    2148         8029882 :         pszDestIter ++;
    2149         8029882 :         nDestSize --;
    2150                 :     }
    2151                 : 
    2152          364268 :     return pszDestIter - pszDest + CPLStrlcpy(pszDestIter, pszSrc, nDestSize);
    2153                 : }
    2154                 : 
    2155                 : /************************************************************************/
    2156                 : /*                              CPLStrnlen()                            */
    2157                 : /************************************************************************/
    2158                 : 
    2159                 : /**
    2160                 :  * Returns the length of a NUL terminated string by reading at most
    2161                 :  * the specified number of bytes.
    2162                 :  *
    2163                 :  * The CPLStrnlen() function returns MIN(strlen(pszStr), nMaxLen).
    2164                 :  * Only the first nMaxLen bytes of the string will be read. Usefull to
    2165                 :  * test if a string contains at least nMaxLen characters without reading
    2166                 :  * the full string up to the NUL terminating character.
    2167                 :  *
    2168                 :  * @param pszStr    a NUL terminated string
    2169                 :  * @param nMaxLen   maximum number of bytes to read in pszStr
    2170                 :  *
    2171                 :  * @return strlen(pszStr) if the length is lesser than nMaxLen, otherwise
    2172                 :  * nMaxLen if the NUL character has not been found in the first nMaxLen bytes.
    2173                 :  *
    2174                 :  * @since GDAL 1.7.0
    2175                 :  */
    2176                 :  
    2177            5402 : size_t CPLStrnlen (const char *pszStr, size_t nMaxLen)
    2178                 : {
    2179            5402 :     size_t nLen = 0;
    2180           82308 :     while(nLen < nMaxLen && *pszStr != '\0')
    2181                 :     {
    2182           71504 :         nLen ++;
    2183           71504 :         pszStr ++;
    2184                 :     }
    2185            5402 :     return nLen;
    2186                 : }

Generated by: LCOV version 1.7