ultimatepp/bazaar/plugin/gdal/port/cpl_string.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

2656 lines
83 KiB
C++

/**********************************************************************
* $Id: cpl_string.cpp 28689 2015-03-08 20:05:50Z rouault $
*
* Name: cpl_string.cpp
* Project: CPL - Common Portability Library
* Purpose: String and Stringlist manipulation functions.
* Author: Daniel Morissette, danmo@videotron.ca
*
**********************************************************************
* Copyright (c) 1998, Daniel Morissette
* Copyright (c) 2008-2013, Even Rouault <even dot rouault at mines-paris dot org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**********************************************************************
*
* Independent Security Audit 2003/04/04 Andrey Kiselev:
* Completed audit of this module. All functions may be used without buffer
* overflows and stack corruptions with any kind of input data strings with
* except of CPLSPrintf() and CSLAppendPrintf() (see note below).
*
* Security Audit 2003/03/28 warmerda:
* Completed security audit. I believe that this module may be safely used
* to parse tokenize arbitrary input strings, assemble arbitrary sets of
* names values into string lists, unescape and escape text even if provided
* by a potentially hostile source.
*
* CPLSPrintf() and CSLAppendPrintf() may not be safely invoked on
* arbitrary length inputs since it has a fixed size output buffer on system
* without vsnprintf().
*
**********************************************************************/
#undef WARN_STANDARD_PRINTF
#include "cpl_string.h"
#include "cpl_vsi.h"
#include "cpl_multiproc.h"
#if defined(WIN32CE)
# include <wce_errno.h>
# include <wce_string.h>
#endif
CPL_CVSID("$Id: cpl_string.cpp 28689 2015-03-08 20:05:50Z rouault $");
/*=====================================================================
StringList manipulation functions.
=====================================================================*/
/**********************************************************************
* CSLAddString()
*
* Append a string to a StringList and return a pointer to the modified
* StringList.
* If the input StringList is NULL, then a new StringList is created.
* Note that CSLAddString performance when building a list is in O(n^2)
* which can cause noticable slow down when n > 10000.
**********************************************************************/
char **CSLAddString(char **papszStrList, const char *pszNewString)
{
int nItems=0;
if (pszNewString == NULL)
return papszStrList; /* Nothing to do!*/
/* Allocate room for the new string */
if (papszStrList == NULL)
papszStrList = (char**) CPLCalloc(2,sizeof(char*));
else
{
nItems = CSLCount(papszStrList);
papszStrList = (char**)CPLRealloc(papszStrList,
(nItems+2)*sizeof(char*));
}
/* Copy the string in the list */
papszStrList[nItems] = CPLStrdup(pszNewString);
papszStrList[nItems+1] = NULL;
return papszStrList;
}
/************************************************************************/
/* CSLCount() */
/************************************************************************/
/**
* Return number of items in a string list.
*
* Returns the number of items in a string list, not counting the
* terminating NULL. Passing in NULL is safe, and will result in a count
* of zero.
*
* Lists are counted by iterating through them so long lists will
* take more time than short lists. Care should be taken to avoid using
* CSLCount() as an end condition for loops as it will result in O(n^2)
* behavior.
*
* @param papszStrList the string list to count.
*
* @return the number of entries.
*/
int CSLCount(char **papszStrList)
{
int nItems=0;
if (papszStrList)
{
while(*papszStrList != NULL)
{
nItems++;
papszStrList++;
}
}
return nItems;
}
/************************************************************************/
/* CSLGetField() */
/* */
/* Fetches the indicated field, being careful not to crash if */
/* the field doesn't exist within this string list. The */
/* returned pointer should not be freed, and doesn't */
/* necessarily last long. */
/************************************************************************/
const char * CSLGetField( char ** papszStrList, int iField )
{
int i;
if( papszStrList == NULL || iField < 0 )
return( "" );
for( i = 0; i < iField+1; i++ )
{
if( papszStrList[i] == NULL )
return "";
}
return( papszStrList[iField] );
}
/************************************************************************/
/* CSLDestroy() */
/************************************************************************/
/**
* Free string list.
*
* Frees the passed string list (null terminated array of strings).
* It is safe to pass NULL.
*
* @param papszStrList the list to free.
*/
void CPL_STDCALL CSLDestroy(char **papszStrList)
{
char **papszPtr;
if (papszStrList)
{
papszPtr = papszStrList;
while(*papszPtr != NULL)
{
CPLFree(*papszPtr);
papszPtr++;
}
CPLFree(papszStrList);
}
}
/************************************************************************/
/* CSLDuplicate() */
/************************************************************************/
/**
* Clone a string list.
*
* Efficiently allocates a copy of a string list. The returned list is
* owned by the caller and should be freed with CSLDestroy().
*
* @param papszStrList the input string list.
*
* @return newly allocated copy.
*/
char **CSLDuplicate(char **papszStrList)
{
char **papszNewList, **papszSrc, **papszDst;
int nLines;
nLines = CSLCount(papszStrList);
if (nLines == 0)
return NULL;
papszNewList = (char **)CPLMalloc((nLines+1)*sizeof(char*));
papszSrc = papszStrList;
papszDst = papszNewList;
while(*papszSrc != NULL)
{
*papszDst = CPLStrdup(*papszSrc);
papszSrc++;
papszDst++;
}
*papszDst = NULL;
return papszNewList;
}
/************************************************************************/
/* CSLMerge */
/************************************************************************/
/**
* \brief Merge two lists.
*
* The two lists are merged, ensuring that if any keys appear in both
* that the value from the second (papszOverride) list take precidence.
*
* @param papszOrig the original list, being modified.
* @param papszOverride the list of items being merged in. This list
* is unaltered and remains owned by the caller.
*
* @return updated list.
*/
char **CSLMerge( char **papszOrig, char **papszOverride )
{
int i;
if( papszOrig == NULL && papszOverride != NULL )
return CSLDuplicate( papszOverride );
if( papszOverride == NULL )
return papszOrig;
for( i = 0; papszOverride[i] != NULL; i++ )
{
char *pszKey = NULL;
const char *pszValue = CPLParseNameValue( papszOverride[i], &pszKey );
papszOrig = CSLSetNameValue( papszOrig, pszKey, pszValue );
CPLFree( pszKey );
}
return papszOrig;
}
/************************************************************************/
/* CSLLoad2() */
/************************************************************************/
/**
* Load a text file into a string list.
*
* The VSI*L API is used, so VSIFOpenL() supported objects that aren't
* physical files can also be accessed. Files are returned as a string list,
* with one item in the string list per line. End of line markers are
* stripped (by CPLReadLineL()).
*
* If reading the file fails a CPLError() will be issued and NULL returned.
*
* @param pszFname the name of the file to read.
* @param nMaxLines maximum number of lines to read before stopping, or -1 for no limit.
* @param nMaxCols maximum number of characters in a line before stopping, or -1 for no limit.
* @param papszOptions NULL-terminated array of options. Unused for now.
*
* @return a string list with the files lines, now owned by caller. To be freed with CSLDestroy()
*
* @since GDAL 1.7.0
*/
char **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, char** papszOptions)
{
VSILFILE *fp;
const char *pszLine;
char **papszStrList=NULL;
int nLines = 0;
int nAllocatedLines = 0;
fp = VSIFOpenL(pszFname, "rb");
if (fp)
{
CPLErrorReset();
while(!VSIFEofL(fp) && (nMaxLines == -1 || nLines < nMaxLines))
{
if ( (pszLine = CPLReadLine2L(fp, nMaxCols, papszOptions)) != NULL )
{
if (nLines + 1 >= nAllocatedLines)
{
char** papszStrListNew;
nAllocatedLines = 16 + nAllocatedLines * 2;
papszStrListNew = (char**) VSIRealloc(papszStrList,
nAllocatedLines * sizeof(char*));
if (papszStrListNew == NULL)
{
VSIFCloseL(fp);
CPLReadLineL( NULL );
CPLError( CE_Failure, CPLE_OutOfMemory,
"CSLLoad2(\"%s\") failed: not enough memory to allocate lines.",
pszFname );
return papszStrList;
}
papszStrList = papszStrListNew;
}
papszStrList[nLines] = CPLStrdup(pszLine);
papszStrList[nLines + 1] = NULL;
nLines ++;
}
else
break;
}
VSIFCloseL(fp);
CPLReadLineL( NULL );
}
else
{
if (CSLFetchBoolean(papszOptions, "EMIT_ERROR_IF_CANNOT_OPEN_FILE", TRUE))
{
/* Unable to open file */
CPLError( CE_Failure, CPLE_OpenFailed,
"CSLLoad2(\"%s\") failed: unable to open file.",
pszFname );
}
}
return papszStrList;
}
/************************************************************************/
/* CSLLoad() */
/************************************************************************/
/**
* Load a text file into a string list.
*
* The VSI*L API is used, so VSIFOpenL() supported objects that aren't
* physical files can also be accessed. Files are returned as a string list,
* with one item in the string list per line. End of line markers are
* stripped (by CPLReadLineL()).
*
* If reading the file fails a CPLError() will be issued and NULL returned.
*
* @param pszFname the name of the file to read.
*
* @return a string list with the files lines, now owned by caller. To be freed with CSLDestroy()
*/
char **CSLLoad(const char *pszFname)
{
return CSLLoad2(pszFname, -1, -1, NULL);
}
/**********************************************************************
* CSLSave()
*
* Write a stringlist to a text file.
*
* Returns the number of lines written, or 0 if the file could not
* be written.
**********************************************************************/
int CSLSave(char **papszStrList, const char *pszFname)
{
VSILFILE *fp;
int nLines = 0;
if (papszStrList)
{
if ((fp = VSIFOpenL(pszFname, "wt")) != NULL)
{
while(*papszStrList != NULL)
{
if( VSIFPrintfL( fp, "%s\n", *papszStrList ) < 1 )
{
CPLError( CE_Failure, CPLE_FileIO,
"CSLSave(\"%s\") failed: unable to write to output file.",
pszFname );
break; /* A Problem happened... abort */
}
nLines++;
papszStrList++;
}
VSIFCloseL(fp);
}
else
{
/* Unable to open file */
CPLError( CE_Failure, CPLE_OpenFailed,
"CSLSave(\"%s\") failed: unable to open output file.",
pszFname );
}
}
return nLines;
}
/**********************************************************************
* CSLPrint()
*
* Print a StringList to fpOut. If fpOut==NULL, then output is sent
* to stdout.
*
* Returns the number of lines printed.
**********************************************************************/
int CSLPrint(char **papszStrList, FILE *fpOut)
{
int nLines=0;
if (fpOut == NULL)
fpOut = stdout;
if (papszStrList)
{
while(*papszStrList != NULL)
{
VSIFPrintf(fpOut, "%s\n", *papszStrList);
nLines++;
papszStrList++;
}
}
return nLines;
}
/**********************************************************************
* CSLInsertStrings()
*
* Copies the contents of a StringList inside another StringList
* before the specified line.
*
* nInsertAtLineNo is a 0-based line index before which the new strings
* should be inserted. If this value is -1 or is larger than the actual
* number of strings in the list then the strings are added at the end
* of the source StringList.
*
* Returns the modified StringList.
**********************************************************************/
char **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo,
char **papszNewLines)
{
int i, nSrcLines, nDstLines, nToInsert;
char **ppszSrc, **ppszDst;
if (papszNewLines == NULL ||
( nToInsert = CSLCount(papszNewLines) ) == 0)
return papszStrList; /* Nothing to do!*/
nSrcLines = CSLCount(papszStrList);
nDstLines = nSrcLines + nToInsert;
/* Allocate room for the new strings */
papszStrList = (char**)CPLRealloc(papszStrList,
(nDstLines+1)*sizeof(char*));
/* Make sure the array is NULL-terminated... it may not be if
* papszStrList was NULL before Realloc()
*/
papszStrList[nSrcLines] = NULL;
/* Make some room in the original list at the specified location
* Note that we also have to move the NULL pointer at the end of
* the source StringList.
*/
if (nInsertAtLineNo == -1 || nInsertAtLineNo > nSrcLines)
nInsertAtLineNo = nSrcLines;
ppszSrc = papszStrList + nSrcLines;
ppszDst = papszStrList + nDstLines;
for (i=nSrcLines; i>=nInsertAtLineNo; i--)
{
*ppszDst = *ppszSrc;
ppszDst--;
ppszSrc--;
}
/* Copy the strings to the list */
ppszSrc = papszNewLines;
ppszDst = papszStrList + nInsertAtLineNo;
for (; *ppszSrc != NULL; ppszSrc++, ppszDst++)
{
*ppszDst = CPLStrdup(*ppszSrc);
}
return papszStrList;
}
/**********************************************************************
* CSLInsertString()
*
* Insert a string at a given line number inside a StringList
*
* nInsertAtLineNo is a 0-based line index before which the new string
* should be inserted. If this value is -1 or is larger than the actual
* number of strings in the list then the string is added at the end
* of the source StringList.
*
* Returns the modified StringList.
**********************************************************************/
char **CSLInsertString(char **papszStrList, int nInsertAtLineNo,
const char *pszNewLine)
{
char *apszList[2];
/* Create a temporary StringList and call CSLInsertStrings()
*/
apszList[0] = (char *) pszNewLine;
apszList[1] = NULL;
return CSLInsertStrings(papszStrList, nInsertAtLineNo, apszList);
}
/**********************************************************************
* CSLRemoveStrings()
*
* Remove strings inside a StringList
*
* nFirstLineToDelete is the 0-based line index of the first line to
* remove. If this value is -1 or is larger than the actual
* number of strings in list then the nNumToRemove last strings are
* removed.
*
* If ppapszRetStrings != NULL then the deleted strings won't be
* free'd, they will be stored in a new StringList and the pointer to
* this new list will be returned in *ppapszRetStrings.
*
* Returns the modified StringList.
**********************************************************************/
char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete,
int nNumToRemove, char ***ppapszRetStrings)
{
int i, nSrcLines, nDstLines;
char **ppszSrc, **ppszDst;
nSrcLines = CSLCount(papszStrList);
nDstLines = nSrcLines - nNumToRemove;
if (nNumToRemove < 1 || nSrcLines == 0)
return papszStrList; /* Nothing to do!*/
/* If operation will result in an empty StringList then don't waste
* time here!
*/
if (nDstLines < 1)
{
CSLDestroy(papszStrList);
return NULL;
}
/* Remove lines from the source StringList...
* Either free() each line or store them to a new StringList depending on
* the caller's choice.
*/
ppszDst = papszStrList + nFirstLineToDelete;
if (ppapszRetStrings == NULL)
{
/* free() all the strings that will be removed.
*/
for (i=0; i < nNumToRemove; i++)
{
CPLFree(*ppszDst);
*ppszDst = NULL;
}
}
else
{
/* Store the strings to remove in a new StringList
*/
*ppapszRetStrings = (char **)CPLCalloc(nNumToRemove+1, sizeof(char*));
for (i=0; i < nNumToRemove; i++)
{
(*ppapszRetStrings)[i] = *ppszDst;
*ppszDst = NULL;
ppszDst++;
}
}
/* Shift down all the lines that follow the lines to remove.
*/
if (nFirstLineToDelete == -1 || nFirstLineToDelete > nSrcLines)
nFirstLineToDelete = nDstLines;
ppszSrc = papszStrList + nFirstLineToDelete + nNumToRemove;
ppszDst = papszStrList + nFirstLineToDelete;
for ( ; *ppszSrc != NULL; ppszSrc++, ppszDst++)
{
*ppszDst = *ppszSrc;
}
/* Move the NULL pointer at the end of the StringList */
*ppszDst = *ppszSrc;
/* At this point, we could realloc() papszStrList to a smaller size, but
* since this array will likely grow again in further operations on the
* StringList we'll leave it as it is.
*/
return papszStrList;
}
/************************************************************************/
/* CSLFindString() */
/************************************************************************/
/**
* Find a string within a string list (case insensitive).
*
* Returns the index of the entry in the string list that contains the
* target string. The string in the string list must be a full match for
* the target, but the search is case insensitive.
*
* @param papszList the string list to be searched.
* @param pszTarget the string to be searched for.
*
* @return the index of the string within the list or -1 on failure.
*/
int CSLFindString( char ** papszList, const char * pszTarget )
{
int i;
if( papszList == NULL )
return -1;
for( i = 0; papszList[i] != NULL; i++ )
{
if( EQUAL(papszList[i],pszTarget) )
return i;
}
return -1;
}
/************************************************************************/
/* CSLFindStringCaseSensitive() */
/************************************************************************/
/**
* Find a string within a string list(case sensitive)
*
* Returns the index of the entry in the string list that contains the
* target string. The string in the string list must be a full match for
* the target.
*
* @param papszList the string list to be searched.
* @param pszTarget the string to be searched for.
*
* @return the index of the string within the list or -1 on failure.
*
* @since GDAL 2.0
*/
int CSLFindStringCaseSensitive( char ** papszList, const char * pszTarget )
{
int i;
if( papszList == NULL )
return -1;
for( i = 0; papszList[i] != NULL; i++ )
{
if( strcmp(papszList[i],pszTarget) == 0 )
return i;
}
return -1;
}
/************************************************************************/
/* CSLPartialFindString() */
/************************************************************************/
/**
* Find a substring within a string list.
*
* Returns the index of the entry in the string list that contains the
* target string as a substring. The search is case sensitive (unlike
* CSLFindString()).
*
* @param papszHaystack the string list to be searched.
* @param pszNeedle the substring to be searched for.
*
* @return the index of the string within the list or -1 on failure.
*/
int CSLPartialFindString( char **papszHaystack, const char * pszNeedle )
{
int i;
if (papszHaystack == NULL || pszNeedle == NULL)
return -1;
for (i = 0; papszHaystack[i] != NULL; i++)
{
if (strstr(papszHaystack[i],pszNeedle))
return i;
}
return -1;
}
/**********************************************************************
* CSLTokenizeString()
*
* Tokenizes a string and returns a StringList with one string for
* each token.
**********************************************************************/
char **CSLTokenizeString( const char *pszString )
{
return CSLTokenizeString2( pszString, " ", CSLT_HONOURSTRINGS );
}
/************************************************************************/
/* CSLTokenizeStringComplex() */
/* */
/* Obsolete tokenizing api. */
/************************************************************************/
char ** CSLTokenizeStringComplex( const char * pszString,
const char * pszDelimiters,
int bHonourStrings, int bAllowEmptyTokens )
{
int nFlags = 0;
if( bHonourStrings )
nFlags |= CSLT_HONOURSTRINGS;
if( bAllowEmptyTokens )
nFlags |= CSLT_ALLOWEMPTYTOKENS;
return CSLTokenizeString2( pszString, pszDelimiters, nFlags );
}
/************************************************************************/
/* CSLTokenizeString2() */
/************************************************************************/
/**
* Tokenize a string.
*
* This function will split a string into tokens based on specified'
* delimeter(s) with a variety of options. The returned result is a
* string list that should be freed with CSLDestroy() when no longer
* needed.
*
* The available parsing options are:
*
* - CSLT_ALLOWEMPTYTOKENS: allow the return of empty tokens when two
* delimiters in a row occur with no other text between them. If not set,
* empty tokens will be discarded;
* - CSLT_STRIPLEADSPACES: strip leading space characters from the token (as
* reported by isspace());
* - CSLT_STRIPENDSPACES: strip ending space characters from the token (as
* reported by isspace());
* - CSLT_HONOURSTRINGS: double quotes can be used to hold values that should
* not be broken into multiple tokens;
* - CSLT_PRESERVEQUOTES: string quotes are carried into the tokens when this
* is set, otherwise they are removed;
* - CSLT_PRESERVEESCAPES: if set backslash escapes (for backslash itself,
* and for literal double quotes) will be preserved in the tokens, otherwise
* the backslashes will be removed in processing.
*
* \b Example:
*
* Parse a string into tokens based on various white space (space, newline,
* tab) and then print out results and cleanup. Quotes may be used to hold
* white space in tokens.
\code
char **papszTokens;
int i;
papszTokens =
CSLTokenizeString2( pszCommand, " \t\n",
CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS );
for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++ )
printf( "arg %d: '%s'", papszTokens[i] );
CSLDestroy( papszTokens );
\endcode
* @param pszString the string to be split into tokens.
* @param pszDelimiters one or more characters to be used as token delimeters.
* @param nCSLTFlags an ORing of one or more of the CSLT_ flag values.
*
* @return a string list of tokens owned by the caller.
*/
char ** CSLTokenizeString2( const char * pszString,
const char * pszDelimiters,
int nCSLTFlags )
{
if( pszString == NULL )
return (char **) CPLCalloc(sizeof(char *),1);
CPLStringList oRetList;
char *pszToken;
int nTokenMax, nTokenLen;
int bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS);
int bAllowEmptyTokens = (nCSLTFlags & CSLT_ALLOWEMPTYTOKENS);
int bStripLeadSpaces = (nCSLTFlags & CSLT_STRIPLEADSPACES);
int bStripEndSpaces = (nCSLTFlags & CSLT_STRIPENDSPACES);
pszToken = (char *) CPLCalloc(10,1);
nTokenMax = 10;
while( pszString != NULL && *pszString != '\0' )
{
int bInString = FALSE;
int bStartString = TRUE;
nTokenLen = 0;
/* Try to find the next delimeter, marking end of token */
for( ; *pszString != '\0'; pszString++ )
{
/* End if this is a delimeter skip it and break. */
if( !bInString && strchr(pszDelimiters, *pszString) != NULL )
{
pszString++;
break;
}
/* If this is a quote, and we are honouring constant
strings, then process the constant strings, with out delim
but don't copy over the quotes */
if( bHonourStrings && *pszString == '"' )
{
if( nCSLTFlags & CSLT_PRESERVEQUOTES )
{
pszToken[nTokenLen] = *pszString;
nTokenLen++;
}
if( bInString )
{
bInString = FALSE;
continue;
}
else
{
bInString = TRUE;
continue;
}
}
/*
* Within string constants we allow for escaped quotes, but in
* processing them we will unescape the quotes and \\ sequence
* reduces to \
*/
if( bInString && pszString[0] == '\\' )
{
if ( pszString[1] == '"' || pszString[1] == '\\' )
{
if( nCSLTFlags & CSLT_PRESERVEESCAPES )
{
pszToken[nTokenLen] = *pszString;
nTokenLen++;
}
pszString++;
}
}
/*
* Strip spaces at the token start if requested.
*/
if ( !bInString && bStripLeadSpaces
&& bStartString && isspace((unsigned char)*pszString) )
continue;
bStartString = FALSE;
/*
* Extend token buffer if we are running close to its end.
*/
if( nTokenLen >= nTokenMax-3 )
{
nTokenMax = nTokenMax * 2 + 10;
pszToken = (char *) CPLRealloc( pszToken, nTokenMax );
}
pszToken[nTokenLen] = *pszString;
nTokenLen++;
}
/*
* Strip spaces at the token end if requested.
*/
if ( !bInString && bStripEndSpaces )
{
while ( nTokenLen && isspace((unsigned char)pszToken[nTokenLen - 1]) )
nTokenLen--;
}
pszToken[nTokenLen] = '\0';
/*
* Add the token.
*/
if( pszToken[0] != '\0' || bAllowEmptyTokens )
oRetList.AddString( pszToken );
}
/*
* If the last token was empty, then we need to capture
* it now, as the loop would skip it.
*/
if( *pszString == '\0' && bAllowEmptyTokens && oRetList.Count() > 0
&& strchr(pszDelimiters,*(pszString-1)) != NULL )
{
oRetList.AddString( "" );
}
CPLFree( pszToken );
if( oRetList.List() == NULL )
{
// we prefer to return empty lists as a pointer to
// a null pointer since some client code might depend on this.
oRetList.Assign( (char**) CPLCalloc(sizeof(char*),1) );
}
return oRetList.StealList();
}
/**********************************************************************
* CPLSPrintf()
*
* My own version of CPLSPrintf() that works with 10 static buffer.
*
* It returns a ref. to a static buffer that should not be freed and
* is valid only until the next call to CPLSPrintf().
*
* NOTE: This function should move to cpl_conv.cpp.
**********************************************************************/
/* For now, assume that a 8000 chars buffer will be enough.
*/
#define CPLSPrintf_BUF_SIZE 8000
#define CPLSPrintf_BUF_Count 10
const char *CPLSPrintf(const char *fmt, ...)
{
va_list args;
/* -------------------------------------------------------------------- */
/* Get the thread local buffer ring data. */
/* -------------------------------------------------------------------- */
char *pachBufRingInfo = (char *) CPLGetTLS( CTLS_CPLSPRINTF );
if( pachBufRingInfo == NULL )
{
pachBufRingInfo = (char *)
CPLCalloc(1,sizeof(int)+CPLSPrintf_BUF_Count*CPLSPrintf_BUF_SIZE);
CPLSetTLS( CTLS_CPLSPRINTF, pachBufRingInfo, TRUE );
}
/* -------------------------------------------------------------------- */
/* Work out which string in the "ring" we want to use this */
/* time. */
/* -------------------------------------------------------------------- */
int *pnBufIndex = (int *) pachBufRingInfo;
int nOffset = sizeof(int) + *pnBufIndex * CPLSPrintf_BUF_SIZE;
char *pachBuffer = pachBufRingInfo + nOffset;
*pnBufIndex = (*pnBufIndex + 1) % CPLSPrintf_BUF_Count;
/* -------------------------------------------------------------------- */
/* Format the result. */
/* -------------------------------------------------------------------- */
va_start(args, fmt);
int ret = CPLvsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE-1, fmt, args);
if( ret < 0 || ret >= CPLSPrintf_BUF_SIZE-1 )
{
CPLError(CE_Failure, CPLE_AppDefined, "CPLSPrintf() called with too big string. Output will be truncated !");
}
va_end(args);
return pachBuffer;
}
/**********************************************************************
* CSLAppendPrintf()
*
* Use CPLSPrintf() to append a new line at the end of a StringList.
*
* Returns the modified StringList.
**********************************************************************/
char **CSLAppendPrintf(char **papszStrList, const char *fmt, ...)
{
CPLString osWork;
va_list args;
va_start( args, fmt );
osWork.vPrintf( fmt, args );
va_end( args );
return CSLAddString(papszStrList, osWork);
}
/************************************************************************/
/* CPLVASPrintf() */
/* */
/* This is intended to serve as an easy to use C callabable */
/* vasprintf() alternative. Used in the geojson library for */
/* instance. */
/************************************************************************/
int CPLVASPrintf( char **buf, const char *fmt, va_list ap )
{
CPLString osWork;
osWork.vPrintf( fmt, ap );
if( buf )
*buf = CPLStrdup(osWork.c_str());
return strlen(osWork);
}
/************************************************************************/
/* CPLvsnprintf_get_end_of_formatting() */
/************************************************************************/
static const char* CPLvsnprintf_get_end_of_formatting(const char* fmt)
{
char ch;
/*flag */
for( ; (ch = *fmt) != '\0'; fmt ++ )
{
if( ch == '\'' )
continue; /* bad idea as this is locale specific */
if( ch == '-' || ch == '+' || ch == ' ' || ch == '#' || ch == '0' )
continue;
break;
}
/* field width */
for( ; (ch = *fmt) != '\0'; fmt ++ )
{
if( ch == '$' )
return NULL; /* we don't want to support this */
if( *fmt >= '0' && *fmt <= '9' )
continue;
break;
}
/* precision */
if( ch == '.' )
{
fmt ++;
for( ; (ch = *fmt) != '\0'; fmt ++ )
{
if( ch == '$' )
return NULL; /* we don't want to support this */
if( *fmt >= '0' && *fmt <= '9' )
continue;
break;
}
}
/* length modifier */
for( ; (ch = *fmt) != '\0'; fmt ++ )
{
if( ch == 'h' || ch == 'l' || ch == 'j' || ch == 'z' ||
ch == 't' || ch == 'L' )
continue;
else if( ch == 'I' && fmt[1] == '6' && fmt[2] == '4' )
fmt += 2;
else
return fmt;
}
return NULL;
}
/************************************************************************/
/* CPLvsnprintf() */
/************************************************************************/
#define call_native_snprintf(type) \
local_ret = snprintf(str + offset_out, size - offset_out, localfmt, va_arg(wrk_args, type))
/** vsnprintf() wrapper that is not sensitive to LC_NUMERIC settings.
*
* This function has the same contract as standard vsnprintf(), except that
* formatting of floating-point numbers will use decimal point, whatever the
* current locale is set.
*
* @param str output buffer
* @param size size of the output buffer (including space for terminating nul)
* @param fmt formatting string
* @param args arguments
* @return the number of characters (excluding terminating nul) that would be written if size is big enough
* @since GDAL 2.0
*/
int CPLvsnprintf(char *str, size_t size, const char* fmt, va_list args)
{
const char* fmt_ori = fmt;
size_t offset_out = 0;
va_list wrk_args;
char ch;
int bFormatUnknown = FALSE;
if( size == 0 )
return vsnprintf(str, size, fmt, args);
#ifdef va_copy
va_copy( wrk_args, args );
#else
wrk_args = args;
#endif
for( ; (ch = *fmt) != '\0'; fmt ++ )
{
if( ch == '%' )
{
const char* ptrend = CPLvsnprintf_get_end_of_formatting(fmt+1);
if( ptrend == NULL || ptrend - fmt >= 20 )
{
bFormatUnknown = TRUE;
break;
}
char end = *ptrend;
char end_m1 = ptrend[-1];
char localfmt[22];
memcpy(localfmt, fmt, ptrend - fmt + 1);
localfmt[ptrend-fmt+1] = '\0';
int local_ret;
if( end == '%' )
{
if( offset_out == size-1 )
break;
local_ret = 1;
str[offset_out] = '%';
}
else if( end == 'd' || end == 'i' || end == 'c' )
{
if( end_m1 == 'h' )
call_native_snprintf(int);
else if( end_m1 == 'l' && ptrend[-2] != 'l' )
call_native_snprintf(long);
else if( end_m1 == 'l' && ptrend[-2] == 'l' )
call_native_snprintf(GIntBig);
else if( end_m1 == '4' && ptrend[-2] == '6' && ptrend[-3] == 'I' ) /* Microsoft I64 modifier */
call_native_snprintf(GIntBig);
else if( end_m1 == 'z')
call_native_snprintf(size_t);
else if( (end_m1 >= 'a' && end_m1 <= 'z') ||
(end_m1 >= 'A' && end_m1 <= 'Z') )
{
bFormatUnknown = TRUE;
break;
}
else
call_native_snprintf(int);
}
else if( end == 'o' || end == 'u' || end == 'x' || end == 'X' )
{
if( end_m1 == 'h' )
call_native_snprintf(unsigned int);
else if( end_m1 == 'l' && ptrend[-2] != 'l' )
call_native_snprintf(unsigned long);
else if( end_m1 == 'l' && ptrend[-2] == 'l' )
call_native_snprintf(GUIntBig);
else if( end_m1 == '4' && ptrend[-2] == '6' && ptrend[-3] == 'I' ) /* Microsoft I64 modifier */
call_native_snprintf(GUIntBig);
else if( end_m1 == 'z')
call_native_snprintf(size_t);
else if( (end_m1 >= 'a' && end_m1 <= 'z') ||
(end_m1 >= 'A' && end_m1 <= 'Z') )
{
bFormatUnknown = TRUE;
break;
}
else
call_native_snprintf(unsigned int);
}
else if( end == 'e' || end == 'E' ||
end == 'f' || end == 'F' ||
end == 'g' || end == 'G' ||
end == 'a' || end == 'A' )
{
if( end_m1 == 'L' )
call_native_snprintf(long double);
else
call_native_snprintf(double);
/* MSVC vsnprintf() returns -1... */
if( local_ret < 0 || offset_out + local_ret >= size )
break;
for( int j = 0; j < local_ret; j ++ )
{
if( str[offset_out + j] == ',' )
{
str[offset_out + j] = '.';
break;
}
}
}
else if( end == 's' )
{
call_native_snprintf(char*);
}
else if( end == 'p' )
{
call_native_snprintf(void*);
}
else
{
bFormatUnknown = TRUE;
break;
}
/* MSVC vsnprintf() returns -1... */
if( local_ret < 0 || offset_out + local_ret >= size )
break;
offset_out += local_ret;
fmt = ptrend;
}
else
{
if( offset_out == size-1 )
break;
str[offset_out++] = *fmt;
}
}
if( ch == '\0' && offset_out < size )
str[offset_out] = '\0';
else
{
if( bFormatUnknown )
{
CPLDebug("CPL", "CPLvsnprintf() called with unsupported formatting string: %s", fmt_ori);
}
#ifdef va_copy
va_end( wrk_args );
va_copy( wrk_args, args );
#else
wrk_args = args;
#endif
#if defined(HAVE_VSNPRINTF)
offset_out = vsnprintf(str, size, fmt_ori, wrk_args);
#else
offset_out = vsprintf(str, fmt_ori, wrk_args);
#endif
}
#ifdef va_copy
va_end( wrk_args );
#endif
return (int)offset_out;
}
/************************************************************************/
/* CPLsnprintf() */
/************************************************************************/
/** snprintf() wrapper that is not sensitive to LC_NUMERIC settings.
*
* This function has the same contract as standard snprintf(), except that
* formatting of floating-point numbers will use decimal point, whatever the
* current locale is set.
*
* @param str output buffer
* @param size size of the output buffer (including space for terminating nul)
* @param fmt formatting string
* @param ... arguments
* @return the number of characters (excluding terminating nul) that would be written if size is big enough
* @since GDAL 2.0
*/
int CPLsnprintf(char *str, size_t size, const char* fmt, ...)
{
int ret;
va_list args;
va_start( args, fmt );
ret = CPLvsnprintf( str, size, fmt, args );
va_end( args );
return ret;
}
/************************************************************************/
/* CPLsprintf() */
/************************************************************************/
/** sprintf() wrapper that is not sensitive to LC_NUMERIC settings.
*
* This function has the same contract as standard sprintf(), except that
* formatting of floating-point numbers will use decimal point, whatever the
* current locale is set.
*
* @param str output buffer (must be large enough to hold the result)
* @param fmt formatting string
* @param ... arguments
* @return the number of characters (excluding terminating nul) written in output buffer.
* @since GDAL 2.0
*/
int CPLsprintf(char *str, const char* fmt, ...)
{
int ret;
va_list args;
va_start( args, fmt );
ret = CPLvsnprintf( str, INT_MAX, fmt, args );
va_end( args );
return ret;
}
/************************************************************************/
/* CPLprintf() */
/************************************************************************/
/** printf() wrapper that is not sensitive to LC_NUMERIC settings.
*
* This function has the same contract as standard printf(), except that
* formatting of floating-point numbers will use decimal point, whatever the
* current locale is set.
*
* @param fmt formatting string
* @param ... arguments
* @return the number of characters (excluding terminating nul) written in output buffer.
* @since GDAL 2.0
*/
int CPLprintf(const char* fmt, ...)
{
char szBuffer[4096];
int ret;
va_list wrk_args, args;
va_start( args, fmt );
#ifdef va_copy
va_copy( wrk_args, args );
#else
wrk_args = args;
#endif
ret = CPLvsnprintf( szBuffer, sizeof(szBuffer), fmt, wrk_args );
if( ret < (int)sizeof(szBuffer)-1 )
ret = printf("%s", szBuffer);
else
{
#ifdef va_copy
va_end( wrk_args );
va_copy( wrk_args, args );
#else
wrk_args = args;
#endif
ret = vfprintf(stdout, fmt, wrk_args);
}
va_end( wrk_args );
return ret;
}
/************************************************************************/
/* CPLsscanf() */
/************************************************************************/
/** sscanf() wrapper that is not sensitive to LC_NUMERIC settings.
*
* This function has the same contract as standard sscanf(), except that
* formatting of floating-point numbers will use decimal point, whatever the
* current locale is set.
*
* CAUTION: only works with a very limited number of formatting strings,
* consisting only of "%lf" and regular characters.
*
* param str input string
* @param fmt formatting string
* @param ... arguments
* @return the number of matched patterns;
* @since GDAL 2.0
*/
int CPL_DLL CPLsscanf(const char* str, const char* fmt, ...)
{
int error = FALSE;
int ret = 0;
const char* fmt_ori = fmt;
va_list args;
va_start( args, fmt );
for( ; *fmt != '\0' && *str != '\0'; fmt++ )
{
if( *fmt == '%' )
{
if( fmt[1] == 'l' && fmt[2] == 'f' )
{
fmt += 2;
char* end;
*(va_arg(args, double*)) = CPLStrtod(str, &end);
if( end > str )
{
ret ++;
str = end;
}
else
break;
}
else
{
error = TRUE;
break;
}
}
else if( *str != *fmt )
break;
else
str ++;
}
va_end( args );
if( error )
{
CPLError(CE_Failure, CPLE_NotSupported,
"Format %s not supported by CPLsscanf()",
fmt_ori);
}
return ret;
}
/************************************************************************/
/* CSLTestBoolean() */
/************************************************************************/
/**
* Test what boolean value contained in the string.
*
* If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE.
* Otherwise, TRUE will be returned.
*
* @param pszValue the string should be tested.
*
* @return TRUE or FALSE.
*/
int CSLTestBoolean( const char *pszValue )
{
if( EQUAL(pszValue,"NO")
|| EQUAL(pszValue,"FALSE")
|| EQUAL(pszValue,"OFF")
|| EQUAL(pszValue,"0") )
return FALSE;
else
return TRUE;
}
/**********************************************************************
* CSLFetchBoolean()
*
* Check for boolean key value.
*
* In a StringList of "Name=Value" pairs, look to see if there is a key
* with the given name, and if it can be interpreted as being TRUE. If
* the key appears without any "=Value" portion it will be considered true.
* If the value is NO, FALSE or 0 it will be considered FALSE otherwise
* if the key appears in the list it will be considered TRUE. If the key
* doesn't appear at all, the indicated default value will be returned.
*
* @param papszStrList the string list to search.
* @param pszKey the key value to look for (case insensitive).
* @param bDefault the value to return if the key isn't found at all.
*
* @return TRUE or FALSE
**********************************************************************/
int CSLFetchBoolean( char **papszStrList, const char *pszKey, int bDefault )
{
const char *pszValue;
if( CSLFindString( papszStrList, pszKey ) != -1 )
return TRUE;
pszValue = CSLFetchNameValue(papszStrList, pszKey );
if( pszValue == NULL )
return bDefault;
else
return CSLTestBoolean( pszValue );
}
/************************************************************************/
/* CSLFetchNameValueDefaulted() */
/************************************************************************/
const char *CSLFetchNameValueDef( char **papszStrList,
const char *pszName,
const char *pszDefault )
{
const char *pszResult = CSLFetchNameValue( papszStrList, pszName );
if( pszResult )
return pszResult;
else
return pszDefault;
}
/**********************************************************************
* CSLFetchNameValue()
*
* In a StringList of "Name=Value" pairs, look for the
* first value associated with the specified name. The search is not
* case sensitive.
* ("Name:Value" pairs are also supported for backward compatibility
* with older stuff.)
*
* Returns a reference to the value in the StringList that the caller
* should not attempt to free.
*
* Returns NULL if the name is not found.
**********************************************************************/
const char *CSLFetchNameValue(char **papszStrList, const char *pszName)
{
size_t nLen;
if (papszStrList == NULL || pszName == NULL)
return NULL;
nLen = strlen(pszName);
while(*papszStrList != NULL)
{
if (EQUALN(*papszStrList, pszName, nLen)
&& ( (*papszStrList)[nLen] == '=' ||
(*papszStrList)[nLen] == ':' ) )
{
return (*papszStrList)+nLen+1;
}
papszStrList++;
}
return NULL;
}
/************************************************************************/
/* CSLFindName() */
/************************************************************************/
/**
* Find StringList entry with given key name.
*
* @param papszStrList the string list to search.
* @param pszName the key value to look for (case insensitive).
*
* @return -1 on failure or the list index of the first occurance
* matching the given key.
*/
int CSLFindName(char **papszStrList, const char *pszName)
{
size_t nLen;
int iIndex = 0;
if (papszStrList == NULL || pszName == NULL)
return -1;
nLen = strlen(pszName);
while(*papszStrList != NULL)
{
if (EQUALN(*papszStrList, pszName, nLen)
&& ( (*papszStrList)[nLen] == '=' ||
(*papszStrList)[nLen] == ':' ) )
{
return iIndex;
}
iIndex++;
papszStrList++;
}
return -1;
}
/**********************************************************************
* CPLParseNameValue()
**********************************************************************/
/**
* Parse NAME=VALUE string into name and value components.
*
* Note that if ppszKey is non-NULL, the key (or name) portion will be
* allocated using VSIMalloc(), and returned in that pointer. It is the
* applications responsibility to free this string, but the application should
* not modify or free the returned value portion.
*
* This function also support "NAME:VALUE" strings and will strip white
* space from around the delimeter when forming name and value strings.
*
* Eventually CSLFetchNameValue() and friends may be modified to use
* CPLParseNameValue().
*
* @param pszNameValue string in "NAME=VALUE" format.
* @param ppszKey optional pointer though which to return the name
* portion.
* @return the value portion (pointing into original string).
*/
const char *CPLParseNameValue(const char *pszNameValue, char **ppszKey )
{
int i;
const char *pszValue;
for( i = 0; pszNameValue[i] != '\0'; i++ )
{
if( pszNameValue[i] == '=' || pszNameValue[i] == ':' )
{
pszValue = pszNameValue + i + 1;
while( *pszValue == ' ' || *pszValue == '\t' )
pszValue++;
if( ppszKey != NULL )
{
*ppszKey = (char *) CPLMalloc(i+1);
strncpy( *ppszKey, pszNameValue, i );
(*ppszKey)[i] = '\0';
while( i > 0 &&
( (*ppszKey)[i] == ' ' || (*ppszKey)[i] == '\t') )
{
(*ppszKey)[i] = '\0';
i--;
}
}
return pszValue;
}
}
return NULL;
}
/**********************************************************************
* CSLFetchNameValueMultiple()
*
* In a StringList of "Name=Value" pairs, look for all the
* values with the specified name. The search is not case
* sensitive.
* ("Name:Value" pairs are also supported for backward compatibility
* with older stuff.)
*
* Returns stringlist with one entry for each occurence of the
* specified name. The stringlist should eventually be destroyed
* by calling CSLDestroy().
*
* Returns NULL if the name is not found.
**********************************************************************/
char **CSLFetchNameValueMultiple(char **papszStrList, const char *pszName)
{
size_t nLen;
char **papszValues = NULL;
if (papszStrList == NULL || pszName == NULL)
return NULL;
nLen = strlen(pszName);
while(*papszStrList != NULL)
{
if (EQUALN(*papszStrList, pszName, nLen)
&& ( (*papszStrList)[nLen] == '=' ||
(*papszStrList)[nLen] == ':' ) )
{
papszValues = CSLAddString(papszValues,
(*papszStrList)+nLen+1);
}
papszStrList++;
}
return papszValues;
}
/**********************************************************************
* CSLAddNameValue()
*
* Add a new entry to a StringList of "Name=Value" pairs,
* ("Name:Value" pairs are also supported for backward compatibility
* with older stuff.)
*
* This function does not check if a "Name=Value" pair already exists
* for that name and can generate multiple entryes for the same name.
* Use CSLSetNameValue() if you want each name to have only one value.
*
* Returns the modified stringlist.
**********************************************************************/
char **CSLAddNameValue(char **papszStrList,
const char *pszName, const char *pszValue)
{
char *pszLine;
if (pszName == NULL || pszValue==NULL)
return papszStrList;
pszLine = (char *) CPLMalloc(strlen(pszName)+strlen(pszValue)+2);
CPLsprintf( pszLine, "%s=%s", pszName, pszValue );
papszStrList = CSLAddString(papszStrList, pszLine);
CPLFree( pszLine );
return papszStrList;
}
/************************************************************************/
/* CSLSetNameValue() */
/************************************************************************/
/**
* Assign value to name in StringList.
*
* Set the value for a given name in a StringList of "Name=Value" pairs
* ("Name:Value" pairs are also supported for backward compatibility
* with older stuff.)
*
* If there is already a value for that name in the list then the value
* is changed, otherwise a new "Name=Value" pair is added.
*
* @param papszList the original list, the modified version is returned.
* @param pszName the name to be assigned a value. This should be a well
* formed token (no spaces or very special characters).
* @param pszValue the value to assign to the name. This should not contain
* any newlines (CR or LF) but is otherwise pretty much unconstrained. If
* NULL any corresponding value will be removed.
*
* @return modified stringlist.
*/
char **CSLSetNameValue(char **papszList,
const char *pszName, const char *pszValue)
{
char **papszPtr;
size_t nLen;
if (pszName == NULL )
return papszList;
nLen = strlen(pszName);
papszPtr = papszList;
while(papszPtr && *papszPtr != NULL)
{
if (EQUALN(*papszPtr, pszName, nLen)
&& ( (*papszPtr)[nLen] == '=' ||
(*papszPtr)[nLen] == ':' ) )
{
/* Found it!
* Change the value... make sure to keep the ':' or '='
*/
char cSep;
cSep = (*papszPtr)[nLen];
CPLFree(*papszPtr);
/*
* If the value is NULL, remove this entry completely/
*/
if( pszValue == NULL )
{
while( papszPtr[1] != NULL )
{
*papszPtr = papszPtr[1];
papszPtr++;
}
*papszPtr = NULL;
}
/*
* Otherwise replace with new value.
*/
else
{
*papszPtr = (char *) CPLMalloc(strlen(pszName)+strlen(pszValue)+2);
CPLsprintf( *papszPtr, "%s%c%s", pszName, cSep, pszValue );
}
return papszList;
}
papszPtr++;
}
if( pszValue == NULL )
return papszList;
/* The name does not exist yet... create a new entry
*/
return CSLAddNameValue(papszList, pszName, pszValue);
}
/************************************************************************/
/* CSLSetNameValueSeparator() */
/************************************************************************/
/**
* Replace the default separator (":" or "=") with the passed separator
* in the given name/value list.
*
* Note that if a separator other than ":" or "=" is used, the resulting
* list will not be manipulatable by the CSL name/value functions any more.
*
* The CPLParseNameValue() function is used to break the existing lines,
* and it also strips white space from around the existing delimiter, thus
* the old separator, and any white space will be replaced by the new
* separator. For formatting purposes it may be desireable to include some
* white space in the new separator. eg. ": " or " = ".
*
* @param papszList the list to update. Component strings may be freed
* but the list array will remain at the same location.
*
* @param pszSeparator the new separator string to insert.
*
*/
void CSLSetNameValueSeparator( char ** papszList, const char *pszSeparator )
{
int nLines = CSLCount(papszList), iLine;
for( iLine = 0; iLine < nLines; iLine++ )
{
char *pszKey = NULL;
const char *pszValue;
char *pszNewLine;
pszValue = CPLParseNameValue( papszList[iLine], &pszKey );
if( pszValue == NULL || pszKey == NULL )
continue;
pszNewLine = (char *) CPLMalloc( strlen(pszValue) + strlen(pszKey)
+ strlen(pszSeparator) + 1 );
strcpy( pszNewLine, pszKey );
strcat( pszNewLine, pszSeparator );
strcat( pszNewLine, pszValue );
CPLFree( papszList[iLine] );
papszList[iLine] = pszNewLine;
CPLFree( pszKey );
}
}
/************************************************************************/
/* CPLEscapeString() */
/************************************************************************/
/**
* Apply escaping to string to preserve special characters.
*
* This function will "escape" a variety of special characters
* to make the string suitable to embed within a string constant
* or to write within a text stream but in a form that can be
* reconstitued to it's original form. The escaping will even preserve
* zero bytes allowing preservation of raw binary data.
*
* CPLES_BackslashQuotable(0): This scheme turns a binary string into
* a form suitable to be placed within double quotes as a string constant.
* The backslash, quote, '\\0' and newline characters are all escaped in
* the usual C style.
*
* CPLES_XML(1): This scheme converts the '<', '>', '"' and '&' characters into
* their XML/HTML equivelent (&lt;, &gt;, &quot; and &amp;) making a string safe
* to embed as CDATA within an XML element. The '\\0' is not escaped and
* should not be included in the input.
*
* CPLES_URL(2): Everything except alphanumerics and the underscore are
* converted to a percent followed by a two digit hex encoding of the character
* (leading zero supplied if needed). This is the mechanism used for encoding
* values to be passed in URLs.
*
* CPLES_SQL(3): All single quotes are replaced with two single quotes.
* Suitable for use when constructing literal values for SQL commands where
* the literal will be enclosed in single quotes.
*
* CPLES_CSV(4): If the values contains commas, semicolons, tabs, double quotes, or newlines it
* placed in double quotes, and double quotes in the value are doubled.
* Suitable for use when constructing field values for .csv files. Note that
* CPLUnescapeString() currently does not support this format, only
* CPLEscapeString(). See cpl_csv.cpp for csv parsing support.
*
* @param pszInput the string to escape.
* @param nLength The number of bytes of data to preserve. If this is -1
* the strlen(pszString) function will be used to compute the length.
* @param nScheme the encoding scheme to use.
*
* @return an escaped, zero terminated string that should be freed with
* CPLFree() when no longer needed.
*/
char *CPLEscapeString( const char *pszInput, int nLength,
int nScheme )
{
char *pszOutput;
char *pszShortOutput;
if( nLength == -1 )
nLength = strlen(pszInput);
pszOutput = (char *) CPLMalloc( nLength * 6 + 1 );
if( nScheme == CPLES_BackslashQuotable )
{
int iOut = 0, iIn;
for( iIn = 0; iIn < nLength; iIn++ )
{
if( pszInput[iIn] == '\0' )
{
pszOutput[iOut++] = '\\';
pszOutput[iOut++] = '0';
}
else if( pszInput[iIn] == '\n' )
{
pszOutput[iOut++] = '\\';
pszOutput[iOut++] = 'n';
}
else if( pszInput[iIn] == '"' )
{
pszOutput[iOut++] = '\\';
pszOutput[iOut++] = '\"';
}
else if( pszInput[iIn] == '\\' )
{
pszOutput[iOut++] = '\\';
pszOutput[iOut++] = '\\';
}
else
pszOutput[iOut++] = pszInput[iIn];
}
pszOutput[iOut] = '\0';
}
else if( nScheme == CPLES_URL ) /* Untested at implementation */
{
int iOut = 0, iIn;
for( iIn = 0; iIn < nLength; iIn++ )
{
if( (pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z')
|| (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z')
|| (pszInput[iIn] >= '0' && pszInput[iIn] <= '9')
|| pszInput[iIn] == '_' || pszInput[iIn] == '.' )
{
pszOutput[iOut++] = pszInput[iIn];
}
else
{
CPLsprintf( pszOutput+iOut, "%%%02X", ((unsigned char*)pszInput)[iIn] );
iOut += 3;
}
}
pszOutput[iOut] = '\0';
}
else if( nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES )
{
int iOut = 0, iIn;
for( iIn = 0; iIn < nLength; iIn++ )
{
if( pszInput[iIn] == '<' )
{
pszOutput[iOut++] = '&';
pszOutput[iOut++] = 'l';
pszOutput[iOut++] = 't';
pszOutput[iOut++] = ';';
}
else if( pszInput[iIn] == '>' )
{
pszOutput[iOut++] = '&';
pszOutput[iOut++] = 'g';
pszOutput[iOut++] = 't';
pszOutput[iOut++] = ';';
}
else if( pszInput[iIn] == '&' )
{
pszOutput[iOut++] = '&';
pszOutput[iOut++] = 'a';
pszOutput[iOut++] = 'm';
pszOutput[iOut++] = 'p';
pszOutput[iOut++] = ';';
}
else if( pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES )
{
pszOutput[iOut++] = '&';
pszOutput[iOut++] = 'q';
pszOutput[iOut++] = 'u';
pszOutput[iOut++] = 'o';
pszOutput[iOut++] = 't';
pszOutput[iOut++] = ';';
}
/* Python 2 doesn't like displaying the UTF-8 character corresponding */
/* to BOM, so escape it */
else if( ((GByte*)pszInput)[iIn] == 0xEF &&
((GByte*)pszInput)[iIn+1] == 0xBB &&
((GByte*)pszInput)[iIn+2] == 0xBF )
{
pszOutput[iOut++] = '&';
pszOutput[iOut++] = '#';
pszOutput[iOut++] = 'x';
pszOutput[iOut++] = 'F';
pszOutput[iOut++] = 'E';
pszOutput[iOut++] = 'F';
pszOutput[iOut++] = 'F';
pszOutput[iOut++] = ';';
iIn += 2;
}
else if( ((GByte*)pszInput)[iIn] < 0x20
&& pszInput[iIn] != 0x9
&& pszInput[iIn] != 0xA
&& pszInput[iIn] != 0xD )
{
// These control characters are unrepresentable in XML format,
// so we just drop them. #4117
}
else
pszOutput[iOut++] = pszInput[iIn];
}
pszOutput[iOut] = '\0';
}
else if( nScheme == CPLES_SQL )
{
int iOut = 0, iIn;
for( iIn = 0; iIn < nLength; iIn++ )
{
if( pszInput[iIn] == '\'' )
{
pszOutput[iOut++] = '\'';
pszOutput[iOut++] = '\'';
}
else
pszOutput[iOut++] = pszInput[iIn];
}
pszOutput[iOut] = '\0';
}
else if( nScheme == CPLES_CSV )
{
if( strchr( pszInput, '\"' ) == NULL
&& strchr( pszInput, ',') == NULL
&& strchr( pszInput, ';') == NULL
&& strchr( pszInput, '\t') == NULL
&& strchr( pszInput, 10) == NULL
&& strchr( pszInput, 13) == NULL )
{
strcpy( pszOutput, pszInput );
}
else
{
int iOut = 1, iIn;
pszOutput[0] = '\"';
for( iIn = 0; iIn < nLength; iIn++ )
{
if( pszInput[iIn] == '\"' )
{
pszOutput[iOut++] = '\"';
pszOutput[iOut++] = '\"';
}
else
pszOutput[iOut++] = pszInput[iIn];
}
pszOutput[iOut++] = '\"';
pszOutput[iOut++] = '\0';
}
}
else
{
pszOutput[0] = '\0';
CPLError( CE_Failure, CPLE_AppDefined,
"Undefined escaping scheme (%d) in CPLEscapeString()",
nScheme );
}
pszShortOutput = CPLStrdup( pszOutput );
CPLFree( pszOutput );
return pszShortOutput;
}
/************************************************************************/
/* CPLUnescapeString() */
/************************************************************************/
/**
* Unescape a string.
*
* This function does the opposite of CPLEscapeString(). Given a string
* with special values escaped according to some scheme, it will return a
* new copy of the string returned to it's original form.
*
* @param pszInput the input string. This is a zero terminated string.
* @param pnLength location to return the length of the unescaped string,
* which may in some cases include embedded '\\0' characters.
* @param nScheme the escaped scheme to undo (see CPLEscapeString() for a
* list).
*
* @return a copy of the unescaped string that should be freed by the
* application using CPLFree() when no longer needed.
*/
char *CPLUnescapeString( const char *pszInput, int *pnLength, int nScheme )
{
char *pszOutput;
int iOut=0, iIn;
pszOutput = (char *) CPLMalloc(4 * strlen(pszInput)+1);
pszOutput[0] = '\0';
if( nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES )
{
char ch;
for( iIn = 0; (ch = pszInput[iIn]) != '\0'; iIn++ )
{
if( ch != '&' )
{
pszOutput[iOut++] = ch;
}
else if( EQUALN(pszInput+iIn,"&lt;",4) )
{
pszOutput[iOut++] = '<';
iIn += 3;
}
else if( EQUALN(pszInput+iIn,"&gt;",4) )
{
pszOutput[iOut++] = '>';
iIn += 3;
}
else if( EQUALN(pszInput+iIn,"&amp;",5) )
{
pszOutput[iOut++] = '&';
iIn += 4;
}
else if( EQUALN(pszInput+iIn,"&apos;",6) )
{
pszOutput[iOut++] = '\'';
iIn += 5;
}
else if( EQUALN(pszInput+iIn,"&quot;",6) )
{
pszOutput[iOut++] = '"';
iIn += 5;
}
else if( EQUALN(pszInput+iIn,"&#x",3) )
{
wchar_t anVal[2] = {0 , 0};
iIn += 3;
while(TRUE)
{
ch = pszInput[iIn ++];
if (ch >= 'a' && ch <= 'f')
anVal[0] = anVal[0] * 16 + ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
anVal[0] = anVal[0] * 16 + ch - 'A' + 10;
else if (ch >= '0' && ch <= '9')
anVal[0] = anVal[0] * 16 + ch - '0';
else
break;
}
if (ch != ';')
break;
iIn --;
char * pszUTF8 = CPLRecodeFromWChar( anVal, "WCHAR_T", CPL_ENC_UTF8);
int nLen = strlen(pszUTF8);
memcpy(pszOutput + iOut, pszUTF8, nLen);
CPLFree(pszUTF8);
iOut += nLen;
}
else if( EQUALN(pszInput+iIn,"&#",2) )
{
char ch;
wchar_t anVal[2] = {0 , 0};
iIn += 2;
while(TRUE)
{
ch = pszInput[iIn ++];
if (ch >= '0' && ch <= '9')
anVal[0] = anVal[0] * 10 + ch - '0';
else
break;
}
if (ch != ';')
break;
iIn --;
char * pszUTF8 = CPLRecodeFromWChar( anVal, "WCHAR_T", CPL_ENC_UTF8);
int nLen = strlen(pszUTF8);
memcpy(pszOutput + iOut, pszUTF8, nLen);
CPLFree(pszUTF8);
iOut += nLen;
}
else
{
/* illegal escape sequence */
CPLDebug( "CPL",
"Error unescaping CPLES_XML text, '&' character followed by unhandled escape sequence." );
break;
}
}
}
else if( nScheme == CPLES_URL )
{
for( iIn = 0; pszInput[iIn] != '\0'; iIn++ )
{
if( pszInput[iIn] == '%'
&& pszInput[iIn+1] != '\0'
&& pszInput[iIn+2] != '\0' )
{
int nHexChar = 0;
if( pszInput[iIn+1] >= 'A' && pszInput[iIn+1] <= 'F' )
nHexChar += 16 * (pszInput[iIn+1] - 'A' + 10);
else if( pszInput[iIn+1] >= 'a' && pszInput[iIn+1] <= 'f' )
nHexChar += 16 * (pszInput[iIn+1] - 'a' + 10);
else if( pszInput[iIn+1] >= '0' && pszInput[iIn+1] <= '9' )
nHexChar += 16 * (pszInput[iIn+1] - '0');
else
CPLDebug( "CPL",
"Error unescaping CPLES_URL text, percent not "
"followed by two hex digits." );
if( pszInput[iIn+2] >= 'A' && pszInput[iIn+2] <= 'F' )
nHexChar += pszInput[iIn+2] - 'A' + 10;
else if( pszInput[iIn+2] >= 'a' && pszInput[iIn+2] <= 'f' )
nHexChar += pszInput[iIn+2] - 'a' + 10;
else if( pszInput[iIn+2] >= '0' && pszInput[iIn+2] <= '9' )
nHexChar += pszInput[iIn+2] - '0';
else
CPLDebug( "CPL",
"Error unescaping CPLES_URL text, percent not "
"followed by two hex digits." );
pszOutput[iOut++] = (char) nHexChar;
iIn += 2;
}
else if( pszInput[iIn] == '+' )
{
pszOutput[iOut++] = ' ';
}
else
{
pszOutput[iOut++] = pszInput[iIn];
}
}
}
else if( nScheme == CPLES_SQL )
{
for( iIn = 0; pszInput[iIn] != '\0'; iIn++ )
{
if( pszInput[iIn] == '\'' && pszInput[iIn+1] == '\'' )
{
iIn++;
pszOutput[iOut++] = pszInput[iIn];
}
else
{
pszOutput[iOut++] = pszInput[iIn];
}
}
}
else /* if( nScheme == CPLES_BackslashQuoteable ) */
{
for( iIn = 0; pszInput[iIn] != '\0'; iIn++ )
{
if( pszInput[iIn] == '\\' )
{
iIn++;
if( pszInput[iIn] == 'n' )
pszOutput[iOut++] = '\n';
else if( pszInput[iIn] == '0' )
pszOutput[iOut++] = '\0';
else
pszOutput[iOut++] = pszInput[iIn];
}
else
{
pszOutput[iOut++] = pszInput[iIn];
}
}
}
pszOutput[iOut] = '\0';
if( pnLength != NULL )
*pnLength = iOut;
return pszOutput;
}
/************************************************************************/
/* CPLBinaryToHex() */
/************************************************************************/
/**
* Binary to hexadecimal translation.
*
* @param nBytes number of bytes of binary data in pabyData.
* @param pabyData array of data bytes to translate.
*
* @return hexadecimal translation, zero terminated. Free with CPLFree().
*/
char *CPLBinaryToHex( int nBytes, const GByte *pabyData )
{
char *pszHex = (char *) CPLMalloc(nBytes * 2 + 1 );
int i;
static const char achHex[] = "0123456789ABCDEF";
pszHex[nBytes*2] = '\0';
for( i = 0; i < nBytes; i++ )
{
int nLow = pabyData[i] & 0x0f;
int nHigh = (pabyData[i] & 0xf0) >> 4;
pszHex[i*2] = achHex[nHigh];
pszHex[i*2+1] = achHex[nLow];
}
return pszHex;
}
/************************************************************************/
/* CPLHexToBinary() */
/************************************************************************/
/**
* Hexadecimal to binary translation
*
* @param pszHex the input hex encoded string.
* @param pnBytes the returned count of decoded bytes placed here.
*
* @return returns binary buffer of data - free with CPLFree().
*/
static const unsigned char hex2char[256] = {
/* not Hex characters */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/* 0-9 */
0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
/* A-F */
0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,
/* not Hex characters */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/* a-f */
0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/* not Hex characters (upper 128 characters) */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
GByte *CPLHexToBinary( const char *pszHex, int *pnBytes )
{
size_t nHexLen = strlen(pszHex);
size_t i;
register unsigned char h1, h2;
GByte *pabyWKB;
pabyWKB = (GByte *) CPLMalloc(nHexLen / 2 + 2);
for( i = 0; i < nHexLen/2; i++ )
{
h1 = hex2char[(int)pszHex[2*i]];
h2 = hex2char[(int)pszHex[2*i+1]];
/* First character is high bits, second is low bits */
pabyWKB[i] = (GByte)((h1 << 4) | h2);
}
pabyWKB[nHexLen/2] = 0;
*pnBytes = nHexLen/2;
return pabyWKB;
}
/************************************************************************/
/* CPLGetValueType() */
/************************************************************************/
/**
* Detect the type of the value contained in a string, whether it is
* a real, an integer or a string
* Leading and trailing spaces are skipped in the analysis.
*
* Note: in the context of this function, integer must be understood in a
* broad sense. It does not mean that the value can fit into a 32 bit integer
* for example. It might be larger.
*
* @param pszValue the string to analyze
*
* @return returns the type of the value contained in the string.
*/
CPLValueType CPLGetValueType(const char* pszValue)
{
/*
doubles : "+25.e+3", "-25.e-3", "25.e3", "25e3", " 25e3 "
not doubles: "25e 3", "25e.3", "-2-5e3", "2-5e3", "25.25.3", "-3d"
*/
int bFoundDot = FALSE;
int bFoundExponent = FALSE;
int bIsLastCharExponent = FALSE;
int bIsReal = FALSE;
if (pszValue == NULL)
return CPL_VALUE_STRING;
/* Skip leading spaces */
while( isspace( (unsigned char)*pszValue ) )
pszValue ++;
if (*pszValue == '\0')
return CPL_VALUE_STRING;
/* Skip leading + or - */
if (*pszValue == '+' || *pszValue == '-')
pszValue ++;
for(; *pszValue != '\0'; pszValue++ )
{
if( isdigit( *pszValue))
{
bIsLastCharExponent = FALSE;
/* do nothing */
}
else if ( isspace ((unsigned char)*pszValue) )
{
const char* pszTmp = pszValue;
while( isspace( (unsigned char)*pszTmp ) )
pszTmp ++;
if (*pszTmp == 0)
break;
else
return CPL_VALUE_STRING;
}
else if ( *pszValue == '-' || *pszValue == '+' )
{
if (bIsLastCharExponent)
{
/* do nothing */
}
else
return CPL_VALUE_STRING;
bIsLastCharExponent = FALSE;
}
else if ( *pszValue == '.')
{
bIsReal = TRUE;
if (!bFoundDot && bIsLastCharExponent == FALSE)
bFoundDot = TRUE;
else
return CPL_VALUE_STRING;
bIsLastCharExponent = FALSE;
}
else if (*pszValue == 'D' || *pszValue == 'd'
|| *pszValue == 'E' || *pszValue == 'e' )
{
if (!(pszValue[1] == '+' || pszValue[1] == '-' ||
isdigit(pszValue[1])))
return CPL_VALUE_STRING;
bIsReal = TRUE;
if (!bFoundExponent)
bFoundExponent = TRUE;
else
return CPL_VALUE_STRING;
bIsLastCharExponent = TRUE;
}
else
{
return CPL_VALUE_STRING;
}
}
return (bIsReal) ? CPL_VALUE_REAL : CPL_VALUE_INTEGER;
}
/************************************************************************/
/* CPLStrlcpy() */
/************************************************************************/
/**
* Copy source string to a destination buffer.
*
* This function ensures that the destination buffer is always NUL terminated
* (provided that its length is at least 1).
*
* This function is designed to be a safer, more consistent, and less error
* prone replacement for strncpy. Its contract is identical to libbsd's strlcpy.
*
* Truncation can be detected by testing if the return value of CPLStrlcpy
* is greater or equal to nDestSize.
\verbatim
char szDest[5];
if (CPLStrlcpy(szDest, "abcde", sizeof(szDest)) >= sizeof(szDest))
fprintf(stderr, "truncation occured !\n");
\endverbatim
* @param pszDest destination buffer
* @param pszSrc source string. Must be NUL terminated
* @param nDestSize size of destination buffer (including space for the NUL terminator character)
*
* @return the length of the source string (=strlen(pszSrc))
*
* @since GDAL 1.7.0
*/
size_t CPLStrlcpy(char* pszDest, const char* pszSrc, size_t nDestSize)
{
char* pszDestIter = pszDest;
const char* pszSrcIter = pszSrc;
if (nDestSize == 0)
return strlen(pszSrc);
nDestSize --;
while(nDestSize != 0 && *pszSrcIter != '\0')
{
*pszDestIter = *pszSrcIter;
pszDestIter ++;
pszSrcIter ++;
nDestSize --;
}
*pszDestIter = '\0';
return pszSrcIter - pszSrc + strlen(pszSrcIter);
}
/************************************************************************/
/* CPLStrlcat() */
/************************************************************************/
/**
* Appends a source string to a destination buffer.
*
* This function ensures that the destination buffer is always NUL terminated
* (provided that its length is at least 1 and that there is at least one byte
* free in pszDest, that is to say strlen(pszDest_before) < nDestSize)
*
* This function is designed to be a safer, more consistent, and less error
* prone replacement for strncat. Its contract is identical to libbsd's strlcat.
*
* Truncation can be detected by testing if the return value of CPLStrlcat
* is greater or equal to nDestSize.
\verbatim
char szDest[5];
CPLStrlcpy(szDest, "ab", sizeof(szDest));
if (CPLStrlcat(szDest, "cde", sizeof(szDest)) >= sizeof(szDest))
fprintf(stderr, "truncation occured !\n");
\endverbatim
* @param pszDest destination buffer. Must be NUL terminated before running CPLStrlcat
* @param pszSrc source string. Must be NUL terminated
* @param nDestSize size of destination buffer (including space for the NUL terminator character)
*
* @return the thoretical length of the destination string after concatenation
* (=strlen(pszDest_before) + strlen(pszSrc)).
* If strlen(pszDest_before) >= nDestSize, then it returns nDestSize + strlen(pszSrc)
*
* @since GDAL 1.7.0
*/
size_t CPLStrlcat(char* pszDest, const char* pszSrc, size_t nDestSize)
{
char* pszDestIter = pszDest;
while(nDestSize != 0 && *pszDestIter != '\0')
{
pszDestIter ++;
nDestSize --;
}
return pszDestIter - pszDest + CPLStrlcpy(pszDestIter, pszSrc, nDestSize);
}
/************************************************************************/
/* CPLStrnlen() */
/************************************************************************/
/**
* Returns the length of a NUL terminated string by reading at most
* the specified number of bytes.
*
* The CPLStrnlen() function returns MIN(strlen(pszStr), nMaxLen).
* Only the first nMaxLen bytes of the string will be read. Useful to
* test if a string contains at least nMaxLen characters without reading
* the full string up to the NUL terminating character.
*
* @param pszStr a NUL terminated string
* @param nMaxLen maximum number of bytes to read in pszStr
*
* @return strlen(pszStr) if the length is lesser than nMaxLen, otherwise
* nMaxLen if the NUL character has not been found in the first nMaxLen bytes.
*
* @since GDAL 1.7.0
*/
size_t CPLStrnlen (const char *pszStr, size_t nMaxLen)
{
size_t nLen = 0;
while(nLen < nMaxLen && *pszStr != '\0')
{
nLen ++;
pszStr ++;
}
return nLen;
}