www.pudn.com > mitab.rar > cpl_conv.cpp


/****************************************************************************** 
 * $Id: cpl_conv.cpp,v 1.43 2005/04/04 15:23:30 fwarmerdam Exp $ 
 * 
 * Project:  CPL - Common Portability Library 
 * Purpose:  Convenience functions. 
 * Author:   Frank Warmerdam, warmerdam@pobox.com 
 * 
 ****************************************************************************** 
 * Copyright (c) 1998, Frank Warmerdam 
 * 
 * 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. 
 ****************************************************************************** 
 * 
 * $Log: cpl_conv.cpp,v $ 
 * Revision 1.43  2005/04/04 15:23:30  fwarmerdam 
 * some functions now CPL_STDCALL 
 * 
 * Revision 1.42  2004/11/17 22:57:21  fwarmerdam 
 * added CPLScanPointer() and CPLPrintPointer() 
 * 
 * Revision 1.41  2004/08/16 20:24:07  warmerda 
 * added CPLUnlinkTree 
 * 
 * Revision 1.40  2004/07/31 04:51:36  warmerda 
 * added shared file open support 
 * 
 * Revision 1.39  2004/06/01 20:40:02  warmerda 
 * expanded tabs 
 * 
 * Revision 1.38  2004/03/28 16:22:02  warmerda 
 * const correctness changes in scan functions 
 * 
 * Revision 1.37  2004/03/24 09:01:17  dron 
 * Added CPLPrintUIntBig(). 
 * 
 * Revision 1.36  2004/02/25 09:02:25  dron 
 * Fixed bug in CPLPackedDMSToDec(). 
 * 
 * Revision 1.35  2004/02/21 10:08:54  dron 
 * Fixes in various string handling functions. 
 * 
 * Revision 1.34  2004/02/07 14:03:30  dron 
 * CPLDecToPackedDMS() added. 
 * 
 * Revision 1.33  2004/02/01 08:37:55  dron 
 * Added CPLPackedDMSToDec(). 
 * 
 * Revision 1.32  2003/12/28 17:24:43  warmerda 
 * added CPLFreeConfig 
 * 
 * Revision 1.31  2003/10/17 07:06:06  dron 
 * Added locale selection option to CPLScanDouble() and CPLPrintDOuble(). 
 * 
 * Revision 1.30  2003/09/28 14:14:16  dron 
 * Added CPLScanString(). 
 * 
 * Revision 1.29  2003/09/12 20:49:24  warmerda 
 * reimplement CPLFGets() to avoid textmode problems 
 * 
 * Revision 1.28  2003/09/08 12:54:42  dron 
 * Fixed warnings. 
 * 
 * Revision 1.27  2003/09/08 11:09:53  dron 
 * Added CPLPrintDouble() and CPLPrintTime(). 
 * 
 * Revision 1.26  2003/09/07 14:38:43  dron 
 * Added CPLPrintString(), CPLPrintStringFill(), CPLPrintInt32(), CPLPrintUIntBig(). 
 * 
 * Revision 1.25  2003/09/03 13:07:26  warmerda 
 * Cleaned up CPLScanLong() a bit to avoid warnings, and 
 * unnecessary conversion to/from double. 
 * 
 * Revision 1.24  2003/08/31 14:48:05  dron 
 * Added CPLScanLong() and CPLScanDouble(). 
 * 
 * Revision 1.23  2003/08/25 20:01:58  dron 
 * Added CPLFGets() helper function. 
 * 
 * Revision 1.22  2003/05/08 21:51:14  warmerda 
 * added CPL{G,S}etConfigOption() usage 
 * 
 * Revision 1.21  2003/03/05 16:46:54  warmerda 
 * Cast strchr() result for Sun (patch from Graeme). 
 * 
 * Revision 1.20  2003/03/02 04:44:38  warmerda 
 * added CPLStringToComplex 
 * 
 * Revision 1.19  2003/02/14 22:12:07  warmerda 
 * expand tabs 
 * 
 * Revision 1.18  2002/12/18 20:22:53  warmerda 
 * fiddle with roundoff issues in DecToDMS 
 * 
 * Revision 1.17  2002/12/10 19:46:04  warmerda 
 * modified CPLReadLine() to seek back if it overreads past a CR or LF 
 * 
 * Revision 1.16  2002/12/09 18:52:51  warmerda 
 * added DMS conversion 
 * 
 * Revision 1.15  2002/03/05 14:26:57  warmerda 
 * expanded tabs 
 * 
 * Revision 1.14  2001/12/12 17:06:57  warmerda 
 * added CPLStat 
 * 
 * Revision 1.13  2001/07/18 04:00:49  warmerda 
 * added CPL_CVSID 
 * 
 * Revision 1.12  2001/03/09 03:19:24  danmo 
 * Set pszRLBuffer=NULL after freeing it to avoid reallocating an invalid ptr 
 * 
 * Revision 1.11  2001/03/05 03:37:19  warmerda 
 * Improve support for recovering CPLReadLine() working buffer. 
 * 
 * Revision 1.10  2001/01/19 21:16:41  warmerda 
 * expanded tabs 
 * 
 * Revision 1.9  2000/04/17 15:56:11  warmerda 
 * make configuration tests always happen 
 * 
 * Revision 1.8  2000/04/05 21:02:47  warmerda 
 * Added CPLVerifyConfiguration() 
 * 
 * Revision 1.7  1999/08/27 12:55:39  danmo 
 * Support 0 bytes allocations in CPLRealloc() 
 * 
 * Revision 1.6  1999/06/25 04:38:03  warmerda 
 * Fixed CPLReadLine() to work for long lines. 
 * 
 * Revision 1.5  1999/05/20 02:54:37  warmerda 
 * Added API documentation 
 * 
 * Revision 1.4  1999/01/02 20:29:53  warmerda 
 * Allow zero length allocations 
 * 
 * Revision 1.3  1998/12/15 19:01:07  warmerda 
 * Added CPLReadLine(). 
 * 
 * Revision 1.2  1998/12/03 18:30:04  warmerda 
 * Use CPLError() instead of GPSError(). 
 * 
 * Revision 1.1  1998/12/02 19:33:23  warmerda 
 * New 
 * 
 */ 
 
#include "cpl_conv.h" 
#include "cpl_string.h" 
#include "cpl_vsi.h" 
 
CPL_CVSID("$Id: cpl_conv.cpp,v 1.43 2005/04/04 15:23:30 fwarmerdam Exp $"); 
 
static char **papszConfigOptions = NULL; 
 
static int nSharedFileCount = 0; 
static CPLSharedFileInfo *pasSharedFileList = NULL; 
 
 
/************************************************************************/ 
/*                             CPLCalloc()                              */ 
/************************************************************************/ 
 
/** 
 * Safe version of calloc(). 
 * 
 * This function is like the C library calloc(), but raises a CE_Fatal 
 * error with CPLError() if it fails to allocate the desired memory.  It 
 * should be used for small memory allocations that are unlikely to fail 
 * and for which the application is unwilling to test for out of memory 
 * conditions.  It uses VSICalloc() to get the memory, so any hooking of 
 * VSICalloc() will apply to CPLCalloc() as well.  CPLFree() or VSIFree() 
 * can be used free memory allocated by CPLCalloc(). 
 * 
 * @param nCount number of objects to allocate. 
 * @param nSize size (in bytes) of object to allocate. 
 * @return pointer to newly allocated memory, only NULL if nSize * nCount is 
 * NULL. 
 */ 
 
void *CPLCalloc( size_t nCount, size_t nSize ) 
 
{ 
    void        *pReturn; 
 
    if( nSize * nCount == 0 ) 
        return NULL; 
     
    pReturn = VSICalloc( nCount, nSize ); 
    if( pReturn == NULL ) 
    { 
        CPLError( CE_Fatal, CPLE_OutOfMemory, 
                  "CPLCalloc(): Out of memory allocating %d bytes.\n", 
                  nSize * nCount ); 
    } 
 
    return pReturn; 
} 
 
/************************************************************************/ 
/*                             CPLMalloc()                              */ 
/************************************************************************/ 
 
/** 
 * Safe version of malloc(). 
 * 
 * This function is like the C library malloc(), but raises a CE_Fatal 
 * error with CPLError() if it fails to allocate the desired memory.  It 
 * should be used for small memory allocations that are unlikely to fail 
 * and for which the application is unwilling to test for out of memory 
 * conditions.  It uses VSIMalloc() to get the memory, so any hooking of 
 * VSIMalloc() will apply to CPLMalloc() as well.  CPLFree() or VSIFree() 
 * can be used free memory allocated by CPLMalloc(). 
 * 
 * @param nSize size (in bytes) of memory block to allocate. 
 * @return pointer to newly allocated memory, only NULL if nSize is zero. 
 */ 
 
void *CPLMalloc( size_t nSize ) 
 
{ 
    void        *pReturn; 
 
    CPLVerifyConfiguration(); 
 
    if( nSize == 0 ) 
        return NULL; 
     
    pReturn = VSIMalloc( nSize ); 
    if( pReturn == NULL ) 
    { 
        CPLError( CE_Fatal, CPLE_OutOfMemory, 
                  "CPLMalloc(): Out of memory allocating %d bytes.\n", 
                  nSize ); 
    } 
 
    return pReturn; 
} 
 
/************************************************************************/ 
/*                             CPLRealloc()                             */ 
/************************************************************************/ 
 
/** 
 * Safe version of realloc(). 
 * 
 * This function is like the C library realloc(), but raises a CE_Fatal 
 * error with CPLError() if it fails to allocate the desired memory.  It 
 * should be used for small memory allocations that are unlikely to fail 
 * and for which the application is unwilling to test for out of memory 
 * conditions.  It uses VSIRealloc() to get the memory, so any hooking of 
 * VSIRealloc() will apply to CPLRealloc() as well.  CPLFree() or VSIFree() 
 * can be used free memory allocated by CPLRealloc(). 
 * 
 * It is also safe to pass NULL in as the existing memory block for 
 * CPLRealloc(), in which case it uses VSIMalloc() to allocate a new block. 
 * 
 * @param pData existing memory block which should be copied to the new block. 
 * @param nNewSize new size (in bytes) of memory block to allocate. 
 * @return pointer to allocated memory, only NULL if nNewSize is zero. 
 */ 
 
 
void * CPLRealloc( void * pData, size_t nNewSize ) 
 
{ 
    void        *pReturn; 
 
    if ( nNewSize == 0 ) 
    { 
        VSIFree(pData); 
        return NULL; 
    } 
 
    if( pData == NULL ) 
        pReturn = VSIMalloc( nNewSize ); 
    else 
        pReturn = VSIRealloc( pData, nNewSize ); 
     
    if( pReturn == NULL ) 
    { 
        CPLError( CE_Fatal, CPLE_OutOfMemory, 
                  "CPLRealloc(): Out of memory allocating %d bytes.\n", 
                  nNewSize ); 
    } 
 
    return pReturn; 
} 
 
/************************************************************************/ 
/*                             CPLStrdup()                              */ 
/************************************************************************/ 
 
/** 
 * Safe version of strdup() function. 
 * 
 * This function is similar to the C library strdup() function, but if 
 * the memory allocation fails it will issue a CE_Fatal error with 
 * CPLError() instead of returning NULL.  It uses VSIStrdup(), so any 
 * hooking of that function will apply to CPLStrdup() as well.  Memory 
 * allocated with CPLStrdup() can be freed with CPLFree() or VSIFree(). 
 * 
 * It is also safe to pass a NULL string into CPLStrdup().  CPLStrdup() 
 * will allocate and return a zero length string (as opposed to a NULL 
 * string). 
 * 
 * @param pszString input string to be duplicated.  May be NULL. 
 * @return pointer to a newly allocated copy of the string.  Free with 
 * CPLFree() or VSIFree(). 
 */ 
 
char *CPLStrdup( const char * pszString ) 
 
{ 
    char        *pszReturn; 
 
    if( pszString == NULL ) 
        pszString = ""; 
 
    pszReturn = VSIStrdup( pszString ); 
         
    if( pszReturn == NULL ) 
    { 
        CPLError( CE_Fatal, CPLE_OutOfMemory, 
                  "CPLStrdup(): Out of memory allocating %d bytes.\n", 
                  strlen(pszString) ); 
         
    } 
     
    return( pszReturn ); 
} 
 
/************************************************************************/ 
/*                              CPLFGets()                              */ 
/*                                                                      */ 
/*      Note: CR = \r = ASCII 13                                        */ 
/*            LF = \n = ASCII 10                                        */ 
/************************************************************************/ 
 
/** 
 * Reads in at most one less than nBufferSize characters from the fp 
 * stream and stores them into the buffer pointed to by pszBuffer. 
 * Reading stops after an EOF or a newline. If a newline is read, it 
 * is _not_ stored into the buffer. A '\0' is stored after the last 
 * character in the buffer. All three types of newline terminators 
 * recognized by the CPLFGets(): single '\r' and '\n' and '\r\n' 
 * combination. 
 * 
 * @param pszBuffer pointer to the targeting character buffer. 
 * @param nBufferSize maximum size of the string to read (not including 
 * termonating '\0'). 
 * @param fp file pointer to read from. 
 * @return pointer to the pszBuffer containing a string read 
 * from the file or NULL if the error or end of file was encountered. 
 */ 
 
char *CPLFGets( char *pszBuffer, int nBufferSize, FILE * fp ) 
 
{ 
    int nActuallyRead, nOriginalOffset; 
 
    if ( nBufferSize == 0 || pszBuffer == NULL || fp == NULL ) 
        return NULL; 
 
/* -------------------------------------------------------------------- */ 
/*      Let the OS level call read what it things is one line.  This    */ 
/*      will include the newline.  On windows, if the file happens      */ 
/*      to be in text mode, the CRLF will have been converted to        */ 
/*      just the newline (LF).  If it is in binary mode it may well     */ 
/*      have both.                                                      */ 
/* -------------------------------------------------------------------- */ 
    nOriginalOffset = VSIFTell( fp ); 
    if( VSIFGets( pszBuffer, nBufferSize, fp ) == NULL ) 
        return NULL; 
     
    nActuallyRead = strlen(pszBuffer); 
    if ( nActuallyRead == 0 ) 
        return NULL; 
 
/* -------------------------------------------------------------------- */ 
/*      Trim off \n, \r or \r\n if it appears at the end.  We don't     */ 
/*      need to do any "seeking" since we want the newline eaten.       */ 
/* -------------------------------------------------------------------- */ 
    if( nActuallyRead > 1  
        && pszBuffer[nActuallyRead-1] == 10  
        && pszBuffer[nActuallyRead-2] == 13 ) 
    { 
        pszBuffer[nActuallyRead-2] = '\0'; 
    } 
    else if( pszBuffer[nActuallyRead-1] == 10  
             || pszBuffer[nActuallyRead-1] == 13 ) 
    { 
        pszBuffer[nActuallyRead-1] = '\0'; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Search within the string for a \r (MacOS convention             */ 
/*      apparently), and if we find it we need to trim the string,      */ 
/*      and seek back.                                                  */ 
/* -------------------------------------------------------------------- */ 
    char *pszExtraNewline = strchr( pszBuffer, 13 ); 
     
    if( pszExtraNewline != NULL ) 
    { 
        int chCheck; 
 
        nActuallyRead = pszExtraNewline - pszBuffer + 1; 
         
        *pszExtraNewline = '\0'; 
        VSIFSeek( fp, nOriginalOffset + nActuallyRead - 1, SEEK_SET ); 
 
        /*  
         * This hackery is necessary to try and find our correct 
         * spot on win32 systems with text mode line translation going  
         * on.  Sometimes the fseek back overshoots, but it doesn't 
         * "realize it" till a character has been read. Try to read till 
         * we get to the right spot and get our CR.  
         */  
        chCheck = fgetc( fp ); 
        while( (chCheck != 13 && chCheck != EOF) 
               || VSIFTell(fp) < nOriginalOffset + nActuallyRead ) 
        { 
            static int bWarned = FALSE; 
 
            if( !bWarned ) 
            { 
                bWarned = TRUE; 
                CPLDebug( "CPL", "CPLFGets() correcting for DOS text mode translation seek problem." ); 
            } 
            chCheck = fgetc( fp ); 
        } 
    } 
 
    return pszBuffer; 
} 
 
/************************************************************************/ 
/*                            CPLReadLine()                             */ 
/************************************************************************/ 
 
/** 
 * Simplified line reading from text file. 
 *  
 * Read a line of text from the given file handle, taking care 
 * to capture CR and/or LF and strip off ... equivelent of 
 * DKReadLine().  Pointer to an internal buffer is returned. 
 * The application shouldn't free it, or depend on it's value 
 * past the next call to CPLReadLine(). 
 *  
 * Note that CPLReadLine() uses VSIFGets(), so any hooking of VSI file 
 * services should apply to CPLReadLine() as well. 
 * 
 * CPLReadLine() maintains an internal buffer, which will appear as a  
 * single block memory leak in some circumstances.  CPLReadLine() may  
 * be called with a NULL FILE * at any time to free this working buffer. 
 * 
 * @param fp file pointer opened with VSIFOpen(). 
 * 
 * @return pointer to an internal buffer containing a line of text read 
 * from the file or NULL if the end of file was encountered. 
 */ 
 
const char *CPLReadLine( FILE * fp ) 
 
{ 
    static char *pszRLBuffer = NULL; 
    static int  nRLBufferSize = 0; 
    int         nReadSoFar = 0; 
 
/* -------------------------------------------------------------------- */ 
/*      Cleanup case.                                                   */ 
/* -------------------------------------------------------------------- */ 
    if( fp == NULL ) 
    { 
        CPLFree( pszRLBuffer ); 
        pszRLBuffer = NULL; 
        nRLBufferSize = 0; 
        return NULL; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Loop reading chunks of the line till we get to the end of       */ 
/*      the line.                                                       */ 
/* -------------------------------------------------------------------- */ 
    do { 
/* -------------------------------------------------------------------- */ 
/*      Grow the working buffer if we have it nearly full.  Fail out    */ 
/*      of read line if we can't reallocate it big enough (for          */ 
/*      instance for a _very large_ file with no newlines).             */ 
/* -------------------------------------------------------------------- */ 
        if( nRLBufferSize-nReadSoFar < 128 ) 
        { 
            nRLBufferSize = nRLBufferSize*2 + 128; 
            pszRLBuffer = (char *) VSIRealloc(pszRLBuffer, nRLBufferSize); 
            if( pszRLBuffer == NULL ) 
            { 
                nRLBufferSize = 0; 
                return NULL; 
            } 
        } 
 
/* -------------------------------------------------------------------- */ 
/*      Do the actual read.                                             */ 
/* -------------------------------------------------------------------- */ 
        if( CPLFGets( pszRLBuffer+nReadSoFar, nRLBufferSize-nReadSoFar, fp ) 
            == NULL ) 
        { 
            CPLFree( pszRLBuffer ); 
            pszRLBuffer = NULL; 
            nRLBufferSize = 0; 
 
            return NULL; 
        } 
 
        nReadSoFar = strlen(pszRLBuffer); 
 
    } while( nReadSoFar == nRLBufferSize - 1 
             && pszRLBuffer[nRLBufferSize-2] != 13 
             && pszRLBuffer[nRLBufferSize-2] != 10 ); 
 
    return( pszRLBuffer ); 
} 
 
/************************************************************************/ 
/*                            CPLScanString()                           */ 
/************************************************************************/ 
 
/** 
 * Scan up to a maximum number of characters from a given string, 
 * allocate a buffer for a new string and fill it with scanned characters. 
 * 
 * @param pszString String containing characters to be scanned. It may be 
 * terminated with a null character. 
 * 
 * @param nMaxLength The maximum number of character to read. Less 
 * characters will be read if a null character is encountered. 
 * 
 * @param bTrimSpaces If TRUE, trim ending spaces from the input string. 
 * Character considered as empty using isspace(3) function. 
 * 
 * @param bNormalize If TRUE, replace ':' symbol with the '_'. It is needed if 
 * resulting string will be used in CPL dictionaries. 
 *  
 * @return Pointer to the resulting string buffer. Caller responsible to free 
 * this buffer with CPLFree(). 
 */ 
 
char *CPLScanString( const char *pszString, int nMaxLength, 
                     int bTrimSpaces, int bNormalize ) 
{ 
    char    *pszBuffer; 
 
    if ( !pszString ) 
        return NULL; 
 
    if ( !nMaxLength ) 
        return CPLStrdup( "" ); 
 
    pszBuffer = (char *)CPLMalloc( nMaxLength + 1 ); 
    if ( !pszBuffer ) 
        return NULL; 
 
    strncpy( pszBuffer, pszString,  nMaxLength ); 
    pszBuffer[nMaxLength] = '\0'; 
 
    if ( bTrimSpaces ) 
    { 
        size_t  i = strlen( pszBuffer ); 
        while ( i-- > 0 && isspace(pszBuffer[i]) ) 
            pszBuffer[i] = '\0'; 
    } 
 
    if ( bNormalize ) 
    { 
        size_t  i = strlen( pszBuffer ); 
        while ( i-- > 0 ) 
        { 
            if ( pszBuffer[i] == ':' ) 
                pszBuffer[i] = '_'; 
        } 
    } 
 
    return pszBuffer; 
} 
 
/************************************************************************/ 
/*                             CPLScanLong()                            */ 
/************************************************************************/ 
 
/** 
 * Scan up to a maximum number of characters from a string and convert 
 * the result to a long. 
 * 
 * @param pszString String containing characters to be scanned. It may be 
 * terminated with a null character. 
 * 
 * @param nMaxLength The maximum number of character to consider as part 
 * of the number. Less characters will be considered if a null character 
 * is encountered. 
 *  
 * @return Long value, converted from its ASCII form. 
 */ 
 
long CPLScanLong( const char *pszString, int nMaxLength ) 
{ 
    long    iValue; 
    char    *pszValue = (char *)CPLMalloc( nMaxLength + 1); 
 
/* -------------------------------------------------------------------- */ 
/*      Compute string into local buffer, and terminate it.             */ 
/* -------------------------------------------------------------------- */ 
    strncpy( pszValue, pszString, nMaxLength ); 
    pszValue[nMaxLength] = '\0'; 
 
/* -------------------------------------------------------------------- */ 
/*      Use atol() to fetch out the result                              */ 
/* -------------------------------------------------------------------- */ 
    iValue = atol( pszValue ); 
 
    CPLFree( pszValue ); 
    return iValue; 
} 
 
/************************************************************************/ 
/*                           CPLScanUIntBig()                           */ 
/************************************************************************/ 
 
/** 
 * Extract big integer from string. 
 * 
 * Scan up to a maximum number of characters from a string and convert 
 * the result to a GUIntBig.  
 * 
 * @param pszString String containing characters to be scanned. It may be 
 * terminated with a null character. 
 * 
 * @param nMaxLength The maximum number of character to consider as part 
 * of the number. Less characters will be considered if a null character 
 * is encountered. 
 *  
 * @return GUIntBig value, converted from its ASCII form. 
 */ 
 
GUIntBig CPLScanUIntBig( const char *pszString, int nMaxLength ) 
{ 
    GUIntBig    iValue; 
    char        *pszValue = (char *)CPLMalloc( nMaxLength + 1); 
 
/* -------------------------------------------------------------------- */ 
/*      Compute string into local buffer, and terminate it.             */ 
/* -------------------------------------------------------------------- */ 
    strncpy( pszValue, pszString, nMaxLength ); 
    pszValue[nMaxLength] = '\0'; 
 
/* -------------------------------------------------------------------- */ 
/*      Fetch out the result                                            */ 
/* -------------------------------------------------------------------- */ 
#if defined(WIN32) && defined(_MSC_VER) 
    iValue = _atoi64( pszValue ); 
# elif HAVE_ATOLL 
    iValue = atoll( pszValue ); 
#else 
    iValue = atol( pszValue ); 
#endif 
 
    CPLFree( pszValue ); 
    return iValue; 
} 
 
/************************************************************************/ 
/*                           CPLScanPointer()                           */ 
/************************************************************************/ 
 
/** 
 * Extract pointer from string. 
 * 
 * Scan up to a maximum number of characters from a string and convert 
 * the result to a GUIntBig.  
 * 
 * @param pszString String containing characters to be scanned. It may be 
 * terminated with a null character. 
 * 
 * @param nMaxLength The maximum number of character to consider as part 
 * of the number. Less characters will be considered if a null character 
 * is encountered. 
 *  
 * @return pointer value, converted from its ASCII form. 
 */ 
 
void *CPLScanPointer( const char *pszString, int nMaxLength ) 
{ 
    void  *pResult; 
    char  szTemp[128]; 
 
/* -------------------------------------------------------------------- */ 
/*      Compute string into local buffer, and terminate it.             */ 
/* -------------------------------------------------------------------- */ 
    if( nMaxLength > (int) sizeof(szTemp)-1 ) 
        nMaxLength = sizeof(szTemp)-1; 
 
    strncpy( szTemp, pszString, nMaxLength ); 
    szTemp[nMaxLength] = '\0'; 
 
/* -------------------------------------------------------------------- */ 
/*      On MSVC we have to scanf pointer values without the 0x          */ 
/*      prefix.                                                         */ 
/* -------------------------------------------------------------------- */ 
    if( EQUALN(szTemp,"0x",2) ) 
    { 
        pResult = NULL; 
 
#if defined(WIN32) && defined(_MSC_VER) 
        sscanf( szTemp+2, "%p", &pResult ); 
#else 
        sscanf( szTemp, "%p", &pResult ); 
#endif 
    } 
     
    else 
    { 
        pResult = (void *) CPLScanUIntBig( szTemp, nMaxLength ); 
    } 
 
    return pResult; 
} 
 
/************************************************************************/ 
/*                             CPLScanDouble()                          */ 
/************************************************************************/ 
 
/** 
 * Scan up to a maximum number of characters from a string and convert 
 * the result to a double. 
 * 
 * @param pszString String containing characters to be scanned. It may be 
 * terminated with a null character. 
 * 
 * @param nMaxLength The maximum number of character to consider as part 
 * of the number. Less characters will be considered if a null character 
 * is encountered. 
 *  
 * @param pszLocale Pointer to a character string containing locale name 
 * ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL, we will not 
 * manipulate with locale settings and current process locale will be used for 
 * printing. Wee need this setting because in different locales decimal 
 * delimiter represented with the different characters. With the pszLocale 
 * option we can control what exact locale will be used for scanning a numeric 
 * value from the string (in most cases it should be C/POSIX). 
 * 
 * @return Double value, converted from its ASCII form. 
 */ 
 
double CPLScanDouble( const char *pszString, int nMaxLength, char *pszLocale ) 
{ 
    int     i; 
    double  dfValue; 
    char    *pszValue = (char *)CPLMalloc( nMaxLength + 1); 
 
/* -------------------------------------------------------------------- */ 
/*      Compute string into local buffer, and terminate it.             */ 
/* -------------------------------------------------------------------- */ 
    strncpy( pszValue, pszString, nMaxLength ); 
    pszValue[nMaxLength] = '\0'; 
 
/* -------------------------------------------------------------------- */ 
/*      Make a pass through converting 'D's to 'E's.                    */ 
/* -------------------------------------------------------------------- */ 
    for( i = 0; i < nMaxLength; i++ ) 
        if ( pszValue[i] == 'd' || pszValue[i] == 'D' ) 
            pszValue[i] = 'E'; 
 
/* -------------------------------------------------------------------- */ 
/*      Use atof() to fetch out the result                              */ 
/* -------------------------------------------------------------------- */ 
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) 
    char        *pszCurLocale = NULL; 
 
    if ( pszLocale || EQUAL( pszLocale, "" ) ) 
    { 
        // Save the current locale 
        pszCurLocale = setlocale(LC_ALL, NULL ); 
        // Set locale to the specified value 
        setlocale(LC_ALL, pszLocale ); 
    } 
#endif 
 
    dfValue = atof( pszValue ); 
 
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) 
    // Restore stored locale back 
    if ( pszCurLocale ) 
        setlocale(LC_ALL, pszCurLocale ); 
#endif 
 
    CPLFree( pszValue ); 
    return dfValue; 
} 
 
/************************************************************************/ 
/*                      CPLPrintString()                                */ 
/************************************************************************/ 
 
/** 
 * Copy the string pointed to by pszSrc, NOT including the terminating 
 * `\0' character, to the array pointed to by pszDest. 
 * 
 * @param pszDest Pointer to the destination string buffer. Should be 
 * large enough to hold the resulting string. 
 * 
 * @param pszDest Pointer to the source buffer. 
 *  
 * @param nMaxLen Maximum length of the resulting string. If string length 
 * is greater than nMaxLen, it will be truncated. 
 *  
 * @return Number of characters printed. 
 */ 
 
int CPLPrintString( char *pszDest, const char *pszSrc, int nMaxLen ) 
{ 
    char    *pszTemp = pszDest; 
    int     nChars = 0; 
 
    if ( !pszDest ) 
        return 0; 
 
    if ( !pszSrc ) 
    { 
        *pszDest = '\0'; 
        return 1; 
    } 
 
    while ( nChars < nMaxLen && *pszSrc ) 
    { 
        *pszTemp++ = *pszSrc++; 
        nChars++; 
    } 
 
    return nChars; 
} 
 
/************************************************************************/ 
/*                         CPLPrintStringFill()                         */ 
/************************************************************************/ 
 
/** 
 * Copy the string pointed to by pszSrc, NOT including the terminating 
 * `\0' character, to the array pointed to by pszDest. Remainder of the 
 * destination string will be filled with space characters. This is only 
 * difference from the PrintString(). 
 * 
 * @param pszDest Pointer to the destination string buffer. Should be 
 * large enough to hold the resulting string. 
 * 
 * @param pszDest Pointer to the source buffer. 
 *  
 * @param nMaxLen Maximum length of the resulting string. If string length 
 * is greater than nMaxLen, it will be truncated. 
 *  
 * @return Number of characters printed. 
 */ 
 
int CPLPrintStringFill( char *pszDest, const char *pszSrc, int nMaxLen ) 
{ 
    char    *pszTemp = pszDest; 
 
    if ( !pszDest ) 
        return 0; 
 
    if ( !pszSrc ) 
    { 
        memset( pszDest, ' ', nMaxLen ); 
        return nMaxLen; 
    } 
 
    while ( nMaxLen && *pszSrc ) 
    { 
        *pszTemp++ = *pszSrc++; 
        nMaxLen--; 
    } 
 
    if ( nMaxLen ) 
        memset( pszTemp, ' ', nMaxLen ); 
 
    return nMaxLen; 
} 
 
/************************************************************************/ 
/*                          CPLPrintInt32()                             */ 
/************************************************************************/ 
 
/** 
 * Print GInt32 value into specified string buffer. This string will not 
 * be NULL-terminated. 
 * 
 * @param Pointer to the destination string buffer. Should be 
 * large enough to hold the resulting string. Note, that the string will 
 * not be NULL-terminated, so user should do this himself, if needed. 
 * 
 * @param iValue Numerical value to print. 
 *  
 * @param nMaxLen Maximum length of the resulting string. If string length 
 * is greater than nMaxLen, it will be truncated. 
 *  
 * @return Number of characters printed. 
 */ 
 
int CPLPrintInt32( char *pszBuffer, GInt32 iValue, int nMaxLen ) 
{ 
    char    szTemp[64]; 
 
    if ( !pszBuffer ) 
        return 0; 
 
    if ( nMaxLen >= 64 ) 
        nMaxLen = 63; 
 
#if UINT_MAX == 65535 
    sprintf( szTemp, "%*ld", nMaxLen, iValue ); 
#else 
    sprintf( szTemp, "%*d", nMaxLen, iValue ); 
#endif 
 
    return CPLPrintString( pszBuffer, szTemp, nMaxLen ); 
} 
 
/************************************************************************/ 
/*                          CPLPrintUIntBig()                           */ 
/************************************************************************/ 
 
/** 
 * Print GUIntBig value into specified string buffer. This string will not 
 * be NULL-terminated. 
 * 
 * @param Pointer to the destination string buffer. Should be 
 * large enough to hold the resulting string. Note, that the string will 
 * not be NULL-terminated, so user should do this himself, if needed. 
 * 
 * @param iValue Numerical value to print. 
 *  
 * @param nMaxLen Maximum length of the resulting string. If string length 
 * is greater than nMaxLen, it will be truncated. 
 *  
 * @return Number of characters printed. 
 */ 
 
int CPLPrintUIntBig( char *pszBuffer, GUIntBig iValue, int nMaxLen ) 
{ 
    char    szTemp[64]; 
 
    if ( !pszBuffer ) 
        return 0; 
 
    if ( nMaxLen >= 64 ) 
        nMaxLen = 63; 
 
#if defined(WIN32) && defined(_MSC_VER) 
    sprintf( szTemp, "%*I64d", nMaxLen, iValue ); 
# elif HAVE_LONG_LONG 
    sprintf( szTemp, "%*Ld", nMaxLen, iValue ); 
#else 
    sprintf( szTemp, "%*ld", nMaxLen, iValue ); 
#endif 
 
    return CPLPrintString( pszBuffer, szTemp, nMaxLen ); 
} 
 
/************************************************************************/ 
/*                          CPLPrintPointer()                           */ 
/************************************************************************/ 
 
/** 
 * Print pointer value into specified string buffer. This string will not 
 * be NULL-terminated. 
 * 
 * @param Pointer to the destination string buffer. Should be 
 * large enough to hold the resulting string. Note, that the string will 
 * not be NULL-terminated, so user should do this himself, if needed. 
 * 
 * @param pValue Pointer to ASCII encode. 
 *  
 * @param nMaxLen Maximum length of the resulting string. If string length 
 * is greater than nMaxLen, it will be truncated. 
 *  
 * @return Number of characters printed. 
 */ 
 
int CPLPrintPointer( char *pszBuffer, void *pValue, int nMaxLen ) 
{ 
    char    szTemp[64]; 
 
    if ( !pszBuffer ) 
        return 0; 
 
    if ( nMaxLen >= 64 ) 
        nMaxLen = 63; 
 
    sprintf( szTemp, "%p", pValue ); 
 
    // On windows, and possibly some other platforms the sprintf("%p") 
    // does not prefix things with 0x so it is hard to know later if the 
    // value is hex encoded.  Fix this up here.  
 
    if( !EQUALN(szTemp,"0x",2) ) 
        sprintf( szTemp, "0x%p", pValue ); 
 
    return CPLPrintString( pszBuffer, szTemp, nMaxLen ); 
} 
 
/************************************************************************/ 
/*                          CPLPrintDouble()                            */ 
/************************************************************************/ 
 
/** 
 * Print double value into specified string buffer. Exponential character 
 * flag 'E' (or 'e') will be replaced with 'D', as in Fortran. Resulting 
 * string will not to be NULL-terminated. 
 * 
 * @param Pointer to the destination string buffer. Should be 
 * large enough to hold the resulting string. Note, that the string will 
 * not be NULL-terminated, so user should do this himself, if needed. 
 * 
 * @param Format specifier (for example, "%16.9E"). 
 * 
 * @param dfValue Numerical value to print. 
 *  
 * @param pszLocale Pointer to a character string containing locale name 
 * ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL we will not 
 * manipulate with locale settings and current process locale will be used for 
 * printing. With the pszLocale option we can control what exact locale 
 * will be used for printing a numeric value to the string (in most cases 
 * it should be C/POSIX). 
 * 
 * @return Number of characters printed. 
 */ 
 
int CPLPrintDouble( char *pszBuffer, const char *pszFormat, 
                    double dfValue, char *pszLocale ) 
{ 
 
#define DOUBLE_BUFFER_SIZE 64 
 
    char    szTemp[DOUBLE_BUFFER_SIZE]; 
    int     i; 
 
    if ( !pszBuffer ) 
        return 0; 
 
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) 
    char        *pszCurLocale = NULL; 
 
    if ( pszLocale || EQUAL( pszLocale, "" ) ) 
    { 
        // Save the current locale 
        pszCurLocale = setlocale(LC_ALL, NULL ); 
        // Set locale to the specified value 
        setlocale( LC_ALL, pszLocale ); 
    } 
#endif 
 
#if defined(HAVE_SNPRINTF) 
    snprintf( szTemp, DOUBLE_BUFFER_SIZE, pszFormat, dfValue ); 
#else 
    sprintf( szTemp, pszFormat, dfValue ); 
#endif 
    szTemp[DOUBLE_BUFFER_SIZE - 1] = '\0'; 
 
    for( i = 0; szTemp[i] != '\0'; i++ ) 
    { 
        if( szTemp[i] == 'E' || szTemp[i] == 'e' ) 
            szTemp[i] = 'D'; 
    } 
 
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) 
    // Restore stored locale back 
    if ( pszCurLocale ) 
        setlocale( LC_ALL, pszCurLocale ); 
#endif 
 
    return CPLPrintString( pszBuffer, szTemp, 64 ); 
 
#undef DOUBLE_BUFFER_SIZE 
 
} 
 
/************************************************************************/ 
/*                            CPLPrintTime()                            */ 
/************************************************************************/ 
 
/** 
 * Print specified time value accordingly to the format options and 
 * specified locale name. This function does following: 
 *  
 *  - if locale parameter is not NULL, the current locale setting will be 
 *  stored and replaced with the specified one; 
 *  - format time value with the strftime(3) function; 
 *  - restore back current locale, if was saved. 
 *  
 * @param pszBuffer Pointer to the destination string buffer. Should be 
 * large enough to hold the resulting string. Note, that the string will 
 * not be NULL-terminated, so user should do this himself, if needed. 
 * 
 * @param nMaxLen Maximum length of the resulting string. If string length is 
 * greater than nMaxLen, it will be truncated. 
 *  
 * @param pszFormat Controls the output format. Options are the same as 
 * for strftime(3) function. 
 * 
 * @param poBrokenTime Pointer to the broken-down time structure. May be 
 * requested with the VSIGMTime() and VSILocalTime() functions. 
 * 
 * @param pszLocale Pointer to a character string containing locale name 
 * ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL we will not 
 * manipulate with locale settings and current process locale will be used for 
 * printing. Be aware that it may be unsuitable to use current locale for 
 * printing time, because all names will be printed in your native language, 
 * as well as time format settings also may be ajusted differently from the 
 * C/POSIX defaults. To solve these problems this option was introdiced. 
 * 
 * @return Number of characters printed. 
 */ 
 
int CPLPrintTime( char *pszBuffer, int nMaxLen, const char *pszFormat, 
                  const struct tm *poBrokenTime, char *pszLocale ) 
{ 
    char    *pszTemp = (char *)CPLMalloc( (nMaxLen + 1) * sizeof(char) ); 
    int     nChars; 
 
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) 
    char        *pszCurLocale = NULL; 
 
    if ( pszLocale || EQUAL( pszLocale, "" ) ) 
    { 
        // Save the current locale 
        pszCurLocale = setlocale(LC_ALL, NULL ); 
        // Set locale to the specified value 
        setlocale( LC_ALL, pszLocale ); 
    } 
#endif 
     
    if ( !strftime( pszTemp, nMaxLen + 1, pszFormat, poBrokenTime ) ) 
        memset( pszTemp, 0, nMaxLen + 1); 
 
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) 
    // Restore stored locale back 
    if ( pszCurLocale ) 
        setlocale( LC_ALL, pszCurLocale ); 
#endif 
 
    nChars = CPLPrintString( pszBuffer, pszTemp, nMaxLen ); 
 
    CPLFree( pszTemp ); 
 
    return nChars; 
} 
 
/************************************************************************/ 
/*                       CPLVerifyConfiguration()                       */ 
/************************************************************************/ 
 
void CPLVerifyConfiguration() 
 
{ 
/* -------------------------------------------------------------------- */ 
/*      Verify data types.                                              */ 
/* -------------------------------------------------------------------- */ 
    CPLAssert( sizeof(GInt32) == 4 ); 
    CPLAssert( sizeof(GInt16) == 2 ); 
    CPLAssert( sizeof(GByte) == 1 ); 
 
    if( sizeof(GInt32) != 4 ) 
        CPLError( CE_Fatal, CPLE_AppDefined,  
                  "sizeof(GInt32) == %d ... yow!\n",  
                  sizeof(GInt32) ); 
 
/* -------------------------------------------------------------------- */ 
/*      Verify byte order                                               */ 
/* -------------------------------------------------------------------- */ 
    GInt32   nTest; 
 
    nTest = 1; 
 
#ifdef CPL_LSB 
    if( ((GByte *) &nTest)[0] != 1 ) 
#endif 
#ifdef CPL_MSB 
    if( ((GByte *) &nTest)[3] != 1 ) 
#endif     
        CPLError( CE_Fatal, CPLE_AppDefined,  
                  "CPLVerifyConfiguration(): byte order set wrong.\n" ); 
} 
 
/************************************************************************/ 
/*                         CPLGetConfigOption()                         */ 
/************************************************************************/ 
 
const char * CPL_STDCALL 
CPLGetConfigOption( const char *pszKey, const char *pszDefault ) 
 
{ 
    const char *pszResult = CSLFetchNameValue( papszConfigOptions, pszKey ); 
 
    if( pszResult == NULL ) 
        pszResult = getenv( pszKey ); 
 
    if( pszResult == NULL ) 
        return pszDefault; 
    else 
        return pszResult; 
} 
 
/************************************************************************/ 
/*                         CPLSetConfigOption()                         */ 
/************************************************************************/ 
 
void CPL_STDCALL  
CPLSetConfigOption( const char *pszKey, const char *pszValue ) 
 
{ 
    papszConfigOptions =  
        CSLSetNameValue( papszConfigOptions, pszKey, pszValue ); 
} 
 
/************************************************************************/ 
/*                           CPLFreeConfig()                            */ 
/************************************************************************/ 
 
void CPL_STDCALL CPLFreeConfig() 
 
{ 
    CSLDestroy(papszConfigOptions); 
    papszConfigOptions = NULL; 
} 
 
/************************************************************************/ 
/*                              CPLStat()                               */ 
/*                                                                      */ 
/*      Same as VSIStat() except it works on "C:" as if it were         */ 
/*      "C:\".                                                          */ 
/************************************************************************/ 
 
int CPLStat( const char *pszPath, VSIStatBuf *psStatBuf ) 
 
{ 
    if( strlen(pszPath) == 2 && pszPath[1] == ':' ) 
    { 
        char    szAltPath[10]; 
         
        strncpy( szAltPath, pszPath, 10 ); 
        strcat( szAltPath, "\\" ); 
        return VSIStat( szAltPath, psStatBuf ); 
    } 
    else 
        return VSIStat( pszPath, psStatBuf ); 
} 
 
/************************************************************************/ 
/*                            proj_strtod()                             */ 
/************************************************************************/ 
static double 
proj_strtod(char *nptr, char **endptr)  
 
{ 
    char c, *cp = nptr; 
    double result; 
 
    /* 
     * Scan for characters which cause problems with VC++ strtod() 
     */ 
    while ((c = *cp) != '\0') { 
        if (c == 'd' || c == 'D') { 
 
            /* 
             * Found one, so NUL it out, call strtod(), 
             * then restore it and return 
             */ 
            *cp = '\0'; 
            result = strtod(nptr, endptr); 
            *cp = c; 
            return result; 
        } 
        ++cp; 
    } 
 
    /* no offending characters, just handle normally */ 
 
    return strtod(nptr, endptr); 
} 
 
/************************************************************************/ 
/*                            CPLDMSToDec()                             */ 
/************************************************************************/ 
 
static const char*sym = "NnEeSsWw"; 
static const double vm[] = { 1.0, 0.0166666666667, 0.00027777778 }; 
 
double CPLDMSToDec( const char *is ) 
 
{ 
    int sign, n, nl; 
    char *p, *s, work[64]; 
    double v, tv; 
 
    /* copy sting into work space */ 
    while (isspace(sign = *is)) ++is; 
    for (n = sizeof(work), s = work, p = (char *)is; isgraph(*p) && --n ; ) 
        *s++ = *p++; 
    *s = '\0'; 
    /* it is possible that a really odd input (like lots of leading 
       zeros) could be truncated in copying into work.  But ... */ 
    sign = *(s = work); 
    if (sign == '+' || sign == '-') s++; 
    else sign = '+'; 
    for (v = 0., nl = 0 ; nl < 3 ; nl = n + 1 ) { 
        if (!(isdigit(*s) || *s == '.')) break; 
        if ((tv = proj_strtod(s, &s)) == HUGE_VAL) 
            return tv; 
        switch (*s) { 
          case 'D': case 'd': 
            n = 0; break; 
          case '\'': 
            n = 1; break; 
          case '"': 
            n = 2; break; 
          case 'r': case 'R': 
            if (nl) { 
                return 0.0; 
            } 
            ++s; 
            v = tv; 
            goto skip; 
          default: 
            v += tv * vm[nl]; 
          skip: n = 4; 
            continue; 
        } 
        if (n < nl) { 
            return 0.0; 
        } 
        v += tv * vm[n]; 
        ++s; 
    } 
    /* postfix sign */ 
    if (*s && (p = (char *) strchr(sym, *s))) { 
        sign = (p - sym) >= 4 ? '-' : '+'; 
        ++s; 
    } 
    if (sign == '-') 
        v = -v; 
 
    return v; 
} 
 
 
/************************************************************************/ 
/*                            CPLDecToDMS()                             */ 
/*                                                                      */ 
/*      Translate a decimal degrees value to a DMS string with          */ 
/*      hemisphere.                                                     */ 
/************************************************************************/ 
 
const char *CPLDecToDMS( double dfAngle, const char * pszAxis, 
                         int nPrecision ) 
 
{ 
    int         nDegrees, nMinutes; 
    double      dfSeconds, dfABSAngle, dfEpsilon; 
    char        szFormat[30]; 
    static char szBuffer[50]; 
    const char  *pszHemisphere; 
     
    dfEpsilon = (0.5/3600.0) * pow(0.1,nPrecision); 
 
    dfABSAngle = ABS(dfAngle) + dfEpsilon; 
 
    nDegrees = (int) dfABSAngle; 
    nMinutes = (int) ((dfABSAngle - nDegrees) * 60); 
    dfSeconds = dfABSAngle * 3600 - nDegrees*3600 - nMinutes*60; 
 
    if( dfSeconds > dfEpsilon * 3600.0 ) 
        dfSeconds -= dfEpsilon * 3600.0; 
 
    if( EQUAL(pszAxis,"Long") && dfAngle < 0.0 ) 
        pszHemisphere = "W"; 
    else if( EQUAL(pszAxis,"Long") ) 
        pszHemisphere = "E"; 
    else if( dfAngle < 0.0 ) 
        pszHemisphere = "S"; 
    else 
        pszHemisphere = "N"; 
 
    sprintf( szFormat, "%%3dd%%2d\'%%.%df\"%s", nPrecision, pszHemisphere ); 
    sprintf( szBuffer, szFormat, nDegrees, nMinutes, dfSeconds ); 
 
    return( szBuffer ); 
} 
 
/************************************************************************/ 
/*                         CPLPackedDMSToDec()                          */ 
/************************************************************************/ 
 
/** 
 * Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees. 
 *  
 * This function converts a packed DMS angle to seconds. The standard 
 * packed DMS format is: 
 * 
 *  degrees * 1000000 + minutes * 1000 + seconds 
 * 
 * Example:     ang = 120025045.25 yields 
 *              deg = 120 
 *              min = 25 
 *              sec = 45.25 
 *  
 * The algorithm used for the conversion is as follows: 
 * 
 * 1.  The absolute value of the angle is used. 
 * 
 * 2.  The degrees are separated out: 
 *     deg = ang/1000000                    (fractional portion truncated) 
 * 
 * 3.  The minutes are separated out: 
 *     min = (ang - deg * 1000000) / 1000   (fractional portion truncated) 
 * 
 * 4.  The seconds are then computed: 
 *     sec = ang - deg * 1000000 - min * 1000 
 * 
 * 5.  The total angle in seconds is computed: 
 *     sec = deg * 3600.0 + min * 60.0 + sec 
 * 
 * 6.  The sign of sec is set to that of the input angle. 
 * 
 * Packed DMS values used by the USGS GCTP package and probably by other 
 * software. 
 * 
 * NOTE: This code does not validate input value. If you give the wrong 
 * value, you will get the wrong result. 
 * 
 * @param dfPacked Angle in packed DMS format. 
 * 
 * @return Angle in decimal degrees. 
 *  
 */ 
 
double CPLPackedDMSToDec( double dfPacked ) 
{ 
    double  dfDegrees, dfMinutes, dfSeconds, dfSign; 
 
    dfSign = ( dfPacked < 0.0 )? -1 : 1; 
         
    dfSeconds = ABS( dfPacked ); 
    dfDegrees = floor(dfSeconds / 1000000.0); 
    dfSeconds = dfSeconds - dfDegrees * 1000000.0; 
    dfMinutes = floor(dfSeconds / 1000.0); 
    dfSeconds = dfSeconds - dfMinutes * 1000.0; 
    dfSeconds = dfSign * ( dfDegrees * 3600.0 + dfMinutes * 60.0 + dfSeconds); 
    dfDegrees = dfSeconds / 3600.0; 
 
    return dfDegrees; 
} 
 
/************************************************************************/ 
/*                         CPLDecToPackedDMS()                          */ 
/************************************************************************/ 
/** 
 * Convert decimal degrees into packed DMS value (DDDMMMSSS.SS). 
 *  
 * This function converts a value, specified in decimal degrees into 
 * packed DMS angle. The standard packed DMS format is: 
 * 
 *  degrees * 1000000 + minutes * 1000 + seconds 
 * 
 * See also CPLPackedDMSToDec(). 
 * 
 * @param dfDec Angle in decimal degrees. 
 * 
 * @return Angle in packed DMS format. 
 *  
 */ 
 
double CPLDecToPackedDMS( double dfDec ) 
{ 
    double  dfDegrees, dfMinutes, dfSeconds, dfSign; 
 
    dfSign = ( dfDec < 0.0 )? -1 : 1; 
 
    dfDec = ABS( dfDec ); 
    dfDegrees = floor( dfDec ); 
    dfMinutes = floor( ( dfDec - dfDegrees ) * 60.0 ); 
    dfSeconds = ( dfDec - dfDegrees ) * 3600.0 - dfMinutes * 60.0; 
 
    return dfSign * (dfDegrees * 1000000.0 + dfMinutes * 1000.0 + dfSeconds); 
} 
 
/************************************************************************/ 
/*                         CPLStringToComplex()                         */ 
/************************************************************************/ 
 
void CPL_DLL CPLStringToComplex( const char *pszString,  
                                 double *pdfReal, double *pdfImag ) 
 
{ 
    int  i; 
    int  iPlus = -1, iImagEnd = -1; 
 
    while( *pszString == ' ' ) 
        pszString++; 
 
    *pdfReal = atof(pszString); 
    *pdfImag = 0.0; 
 
    for( i = 0; pszString[i] != '\0' && pszString[i] != ' ' && i < 100; i++ ) 
    { 
        if( pszString[i] == '+' && i > 0 ) 
            iPlus = i; 
        if( pszString[i] == '-' && i > 0 ) 
            iPlus = i; 
        if( pszString[i] == 'i' ) 
            iImagEnd = i; 
    } 
 
    if( iPlus > -1 && iImagEnd > -1 && iPlus < iImagEnd ) 
    { 
        *pdfImag = atof(pszString + iPlus); 
    } 
 
    return; 
} 
 
/************************************************************************/ 
/*                           CPLOpenShared()                            */ 
/************************************************************************/ 
 
/** 
 * Open a shared file handle.  
 * 
 * Some operating systems have limits on the number of file handles that can 
 * be open at one time.  This function attempts to maintain a registry of 
 * already open file handles, and reuse existing ones if the same file 
 * is requested by another part of the application.  
 * 
 * Note that access is only shared for access types "r", "rb", "r+" and  
 * "rb+".  All others will just result in direct VSIOpen() calls.  Keep in 
 * mind that a file is only reused if the file name is exactly the same.  
 * Different names referring to the same file will result in different  
 * handles.   
 * 
 * The VSIFOpen() or VSIFOpenL() function is used to actually open the file,  
 * when an existing file handle can't be shared.  
 * 
 * @param pszFilename the name of the file to open. 
 * @param pszAccess the normal fopen()/VSIFOpen() style access string. 
 * @param bLarge If TRUE VSIFOpenL() (for large files) will be used instead of 
 * VSIFOpen().  
 * 
 * @return a file handle or NULL if opening fails.  
 */ 
 
FILE *CPLOpenShared( const char *pszFilename, const char *pszAccess, 
                     int bLarge ) 
 
{ 
    int i; 
    int bReuse; 
 
/* -------------------------------------------------------------------- */ 
/*      Is there an existing file we can use?                           */ 
/* -------------------------------------------------------------------- */ 
    bReuse = EQUAL(pszAccess,"rb") || EQUAL(pszAccess, "rb+"); 
 
    for( i = 0; bReuse && i < nSharedFileCount; i++ ) 
    { 
        if( strcmp(pasSharedFileList[i].pszFilename,pszFilename) == 0  
            && !bLarge == !pasSharedFileList[i].bLarge 
            && EQUAL(pasSharedFileList[i].pszAccess,pszAccess) ) 
        { 
            pasSharedFileList[i].nRefCount++; 
            return pasSharedFileList[i].fp; 
        } 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Open the file.                                                  */ 
/* -------------------------------------------------------------------- */ 
    FILE *fp; 
 
    if( bLarge ) 
        fp = VSIFOpenL( pszFilename, pszAccess ); 
    else 
        fp = VSIFOpen( pszFilename, pszAccess ); 
 
    if( fp == NULL ) 
        return NULL; 
 
/* -------------------------------------------------------------------- */ 
/*      Add an entry to the list.                                       */ 
/* -------------------------------------------------------------------- */ 
    nSharedFileCount++; 
 
    pasSharedFileList = (CPLSharedFileInfo *) 
        CPLRealloc( pasSharedFileList,  
                    sizeof(CPLSharedFileInfo) * nSharedFileCount ); 
 
    pasSharedFileList[nSharedFileCount-1].fp = fp; 
    pasSharedFileList[nSharedFileCount-1].nRefCount = 1; 
    pasSharedFileList[nSharedFileCount-1].bLarge = bLarge; 
    pasSharedFileList[nSharedFileCount-1].pszFilename =CPLStrdup(pszFilename); 
    pasSharedFileList[nSharedFileCount-1].pszAccess = CPLStrdup(pszAccess); 
 
    return fp; 
} 
 
/************************************************************************/ 
/*                           CPLCloseShared()                           */ 
/************************************************************************/ 
 
/** 
 * Close shared file. 
 * 
 * Dereferences the indicated file handle, and closes it if the reference 
 * count has dropped to zero.  A CPLError() is issued if the file is not 
 * in the shared file list. 
 * 
 * @param fp file handle from CPLOpenShared() to deaccess. 
 */ 
 
void CPLCloseShared( FILE * fp ) 
 
{ 
    int i; 
 
/* -------------------------------------------------------------------- */ 
/*      Search for matching information.                                */ 
/* -------------------------------------------------------------------- */ 
    for( i = 0; i < nSharedFileCount && fp != pasSharedFileList[i].fp; i++ ){} 
 
    if( i == nSharedFileCount ) 
    { 
        CPLError( CE_Failure, CPLE_AppDefined,  
                  "Unable to find file handle %p in CPLCloseShared().", 
                  fp ); 
        return; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      Dereference and return if there are still some references.      */ 
/* -------------------------------------------------------------------- */ 
    if( --pasSharedFileList[i].nRefCount > 0 ) 
        return; 
 
/* -------------------------------------------------------------------- */ 
/*      Close the file, and remove the information.                     */ 
/* -------------------------------------------------------------------- */ 
    if( pasSharedFileList[i].bLarge ) 
        VSIFCloseL( pasSharedFileList[i].fp ); 
    else 
        VSIFClose( pasSharedFileList[i].fp ); 
 
    CPLFree( pasSharedFileList[i].pszFilename ); 
    CPLFree( pasSharedFileList[i].pszAccess ); 
 
    pasSharedFileList[i] = pasSharedFileList[--nSharedFileCount]; 
 
    if( nSharedFileCount == 0 ) 
    { 
        CPLFree( pasSharedFileList ); 
        pasSharedFileList = NULL; 
    } 
} 
 
/************************************************************************/ 
/*                          CPLGetSharedList()                          */ 
/************************************************************************/ 
 
/** 
 * Fetch list of open shared files. 
 * 
 * @param pnCount place to put the count of entries.  
 * 
 * @return the pointer to the first in the array of shared file info  
 * structures. 
 */ 
 
CPLSharedFileInfo *CPLGetSharedList( int *pnCount ) 
 
{ 
    if( pnCount != NULL ) 
        *pnCount = nSharedFileCount; 
         
    return pasSharedFileList; 
} 
 
/************************************************************************/ 
/*                         CPLDumpSharedList()                          */ 
/************************************************************************/ 
 
/** 
 * Report open shared files. 
 * 
 * Dumps all open shared files to the indicated file handle.  If the 
 * file handle is NULL information is sent via the CPLDebug() call.  
 * 
 * @param fp File handle to write to. 
 */ 
 
void CPLDumpSharedList( FILE *fp ) 
 
{ 
    int i; 
 
    if( nSharedFileCount > 0 ) 
    { 
        if( fp == NULL ) 
            CPLDebug( "CPL", "%d Shared files open.", nSharedFileCount ); 
        else 
            fprintf( fp, "%d Shared files open.", nSharedFileCount ); 
    } 
 
    for( i = 0; i < nSharedFileCount; i++ ) 
    { 
        if( fp == NULL ) 
            CPLDebug( "CPL",  
                      "%2d %d %4s %s",  
                      pasSharedFileList[i].nRefCount,  
                      pasSharedFileList[i].bLarge, 
                      pasSharedFileList[i].pszAccess, 
                      pasSharedFileList[i].pszFilename ); 
        else 
            fprintf( fp, "%2d %d %4s %s",  
                     pasSharedFileList[i].nRefCount,  
                     pasSharedFileList[i].bLarge, 
                     pasSharedFileList[i].pszAccess, 
                     pasSharedFileList[i].pszFilename ); 
    } 
} 
 
/************************************************************************/ 
/*                           CPLUnlinkTree()                            */ 
/************************************************************************/ 
 
int CPLUnlinkTree( const char *pszPath ) 
 
{ 
/* -------------------------------------------------------------------- */ 
/*      First, ensure there isn't any such file yet.                    */ 
/* -------------------------------------------------------------------- */ 
    VSIStatBuf sStatBuf; 
 
    if( VSIStat( pszPath, &sStatBuf ) != 0 ) 
    { 
        CPLError( CE_Failure, CPLE_AppDefined,  
                  "It seems no file system object called '%s' exists.", 
                  pszPath ); 
 
        return errno; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      If it's a simple file, just delete it.                          */ 
/* -------------------------------------------------------------------- */ 
    if( VSI_ISREG( sStatBuf.st_mode ) ) 
    { 
        if( VSIUnlink( pszPath ) != 0 ) 
        { 
            CPLError( CE_Failure, CPLE_AppDefined,  
                      "Failed to unlink %s.\n%s",  
                      pszPath, VSIStrerror( errno ) ); 
            return errno; 
        } 
        else 
            return 0; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      If it is a directory recurse then unlink the directory.         */ 
/* -------------------------------------------------------------------- */ 
    else if( VSI_ISDIR( sStatBuf.st_mode ) ) 
    { 
        char **papszItems = CPLReadDir( pszPath ); 
        int  i; 
 
        for( i = 0; papszItems != NULL && papszItems[i] != NULL; i++ ) 
        { 
            char *pszSubPath; 
            int nErr; 
 
            if( EQUAL(papszItems[i],".") || EQUAL(papszItems[i],"..") ) 
                continue; 
 
            pszSubPath = CPLStrdup( 
                CPLFormFilename( pszPath, papszItems[i], NULL ) ); 
 
            nErr = CPLUnlinkTree( pszSubPath ); 
            CPLFree( pszSubPath ); 
 
            if( nErr != 0 ) 
            { 
                CSLDestroy( papszItems ); 
                return nErr; 
            } 
        } 
         
        CSLDestroy( papszItems ); 
 
        if( VSIRmdir( pszPath ) != 0 ) 
        { 
            CPLError( CE_Failure, CPLE_AppDefined,  
                      "Failed to unlink %s.\n%s",  
                      pszPath, VSIStrerror( errno ) ); 
            return errno; 
        } 
        else 
            return 0; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*      otherwise report an error.                                      */ 
/* -------------------------------------------------------------------- */ 
    else 
    { 
        CPLError( CE_Failure, CPLE_AppDefined,  
                  "Failed to unlink %s.\nUnrecognised filesystem object.", 
                  pszPath ); 
        return 1000; 
    } 
}