/****************************************************************************** * Copyright (c) 1999, 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. ****************************************************************************** * * cpl_csv.c: Support functions for accessing CSV files. * * $Log: cpl_csv.c,v $ * Revision 1.16 2003/07/10 18:03:28 warmerda * don't crash if NULL passed to gtCSVAccess() * * Revision 1.15 2003/01/20 06:46:35 warmerda * search for pcs.csv, not horiz_cs.csv * * Revision 1.14 2003/01/15 04:39:58 warmerda * change internal name of CSVAccess * * Revision 1.13 2002/11/28 22:26:41 warmerda * upgraded to proper CSV formatting, and in-memory caching * * Revision 1.12 2002/06/19 03:51:15 warmerda * migrated cpl_csv.h into cpl_serv.h * * Revision 1.11 2001/03/05 04:49:56 warmerda * try to clear CPLReadLine buffer on deaccess * * Revision 1.10 2001/01/17 15:32:19 warmerda * Include /usr/share/epsg_csv and share/epsg_csv in csv search path. * * Revision 1.9 2000/12/12 19:34:36 warmerda * Use CSV_DATA_DIR if defined. * * Revision 1.8 2000/08/22 04:33:33 warmerda * added support for /usr/local/shared/epsg_csv * * Revision 1.7 1999/12/03 14:42:59 warmerda * Passing a NULL filename into CSVAccess() now results in a graceful * failure to open the file. * * Revision 1.6 1999/06/26 17:28:51 warmerda * Fixed reading of records with newlines embedded in quoted strings. * * Revision 1.5 1999/05/04 03:07:24 warmerda * avoid warning * * Revision 1.4 1999/04/28 19:59:56 warmerda * added some doxygen style documentation * * Revision 1.3 1999/03/17 19:53:15 geotiff * sys includes moved to cpl_serv.h * * Revision 1.2 1999/03/10 16:54:42 geotiff * Added use of the GEOTIFF_CSV environment variable to locate CSV files. * * Revision 1.1 1999/03/09 15:57:04 geotiff * New * * Revision 1.2 1999/02/24 16:23:21 warmerda * added lots * * Revision 1.1 1999/01/05 16:52:36 warmerda * New * */ #include "cpl_serv.h" #include "geo_tiffp.h" /* ==================================================================== */ /* The CSVTable is a persistant set of info about an open CSV */ /* table. While it doesn't currently maintain a record index, */ /* or in-memory copy of the table, it could be changed to do so */ /* in the future. */ /* ==================================================================== */ typedef struct ctb { FILE *fp; struct ctb *psNext; char *pszFilename; char **papszFieldNames; char **papszRecFields; int iLastLine; /* Cache for whole file */ int nLineCount; char **papszLines; int *panLineIndex; char *pszRawData; } CSVTable; static CSVTable *psCSVTableList = NULL; /************************************************************************/ /* CSVAccess() */ /* */ /* This function will fetch a handle to the requested table. */ /* If not found in the ``open table list'' the table will be */ /* opened and added to the list. Eventually this function may */ /* become public with an abstracted return type so that */ /* applications can set options about the table. For now this */ /* isn't done. */ /************************************************************************/ static CSVTable *gtCSVAccess( const char * pszFilename ) { CSVTable *psTable; FILE *fp; if( pszFilename == NULL ) return NULL; /* -------------------------------------------------------------------- */ /* Is the table already in the list. */ /* -------------------------------------------------------------------- */ for( psTable = psCSVTableList; psTable != NULL; psTable = psTable->psNext ) { if( EQUAL(psTable->pszFilename,pszFilename) ) { /* * Eventually we should consider promoting to the front of * the list to accelerate frequently accessed tables. */ return( psTable ); } } /* -------------------------------------------------------------------- */ /* If not, try to open it. */ /* -------------------------------------------------------------------- */ fp = VSIFOpen( pszFilename, "rb" ); if( fp == NULL ) return NULL; /* -------------------------------------------------------------------- */ /* Create an information structure about this table, and add to */ /* the front of the list. */ /* -------------------------------------------------------------------- */ psTable = (CSVTable *) CPLCalloc(sizeof(CSVTable),1); psTable->fp = fp; psTable->pszFilename = CPLStrdup( pszFilename ); psTable->psNext = psCSVTableList; psCSVTableList = psTable; /* -------------------------------------------------------------------- */ /* Read the table header record containing the field names. */ /* -------------------------------------------------------------------- */ psTable->papszFieldNames = CSVReadParseLine( fp ); return( psTable ); } /************************************************************************/ /* CSVDeaccess() */ /************************************************************************/ void CSVDeaccess( const char * pszFilename ) { CSVTable *psLast, *psTable; /* -------------------------------------------------------------------- */ /* A NULL means deaccess all tables. */ /* -------------------------------------------------------------------- */ if( pszFilename == NULL ) { while( psCSVTableList != NULL ) CSVDeaccess( psCSVTableList->pszFilename ); return; } /* -------------------------------------------------------------------- */ /* Find this table. */ /* -------------------------------------------------------------------- */ psLast = NULL; for( psTable = psCSVTableList; psTable != NULL && !EQUAL(psTable->pszFilename,pszFilename); psTable = psTable->psNext ) { psLast = psTable; } if( psTable == NULL ) { return; } /* -------------------------------------------------------------------- */ /* Remove the link from the list. */ /* -------------------------------------------------------------------- */ if( psLast != NULL ) psLast->psNext = psTable->psNext; else psCSVTableList = psTable->psNext; /* -------------------------------------------------------------------- */ /* Free the table. */ /* -------------------------------------------------------------------- */ if( psTable->fp != NULL ) VSIFClose( psTable->fp ); CSLDestroy( psTable->papszFieldNames ); CSLDestroy( psTable->papszRecFields ); CPLFree( psTable->pszFilename ); CPLFree( psTable->panLineIndex ); CPLFree( psTable->pszRawData ); CPLFree( psTable->papszLines ); CPLFree( psTable ); CPLReadLine( NULL ); } /************************************************************************/ /* CSVSplitLine() */ /* */ /* Tokenize a CSV line into fields in the form of a string */ /* list. This is used instead of the CPLTokenizeString() */ /* because it provides correct CSV escaping and quoting */ /* semantics. */ /************************************************************************/ static char **CSVSplitLine( const char *pszString ) { char **papszRetList = NULL; char *pszToken; int nTokenMax, nTokenLen; pszToken = (char *) CPLCalloc(10,1); nTokenMax = 10; while( pszString != NULL && *pszString != '\0' ) { int bInString = FALSE; 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 && *pszString == ',' ) { pszString++; break; } if( *pszString == '"' ) { if( !bInString || pszString[1] != '"' ) { bInString = !bInString; continue; } else /* doubled quotes in string resolve to one quote */ { pszString++; } } if( nTokenLen >= nTokenMax-2 ) { nTokenMax = nTokenMax * 2 + 10; pszToken = (char *) CPLRealloc( pszToken, nTokenMax ); } pszToken[nTokenLen] = *pszString; nTokenLen++; } pszToken[nTokenLen] = '\0'; papszRetList = CSLAddString( papszRetList, pszToken ); /* If the last token is an empty token, then we have to catch * it now, otherwise we won't reenter the loop and it will be lost. */ if ( *pszString == '\0' && *(pszString-1) == ',' ) { papszRetList = CSLAddString( papszRetList, "" ); } } if( papszRetList == NULL ) papszRetList = (char **) CPLCalloc(sizeof(char *),1); CPLFree( pszToken ); return papszRetList; } /************************************************************************/ /* CSVFindNextLine() */ /* */ /* Find the start of the next line, while at the same time zero */ /* terminating this line. Take into account that there may be */ /* newline indicators within quoted strings, and that quotes */ /* can be escaped with a backslash. */ /************************************************************************/ static char *CSVFindNextLine( char *pszThisLine ) { int nQuoteCount = 0, i; for( i = 0; pszThisLine[i] != '\0'; i++ ) { if( pszThisLine[i] == '\"' && (i == 0 || pszThisLine[i-1] != '\\') ) nQuoteCount++; if( (pszThisLine[i] == 10 || pszThisLine[i] == 13) && (nQuoteCount % 2) == 0 ) break; } while( pszThisLine[i] == 10 || pszThisLine[i] == 13 ) pszThisLine[i++] = '\0'; if( pszThisLine[i] == '\0' ) return NULL; else return pszThisLine + i; } /************************************************************************/ /* CSVIngest() */ /* */ /* Load entire file into memory and setup index if possible. */ /************************************************************************/ static void CSVIngest( const char *pszFilename ) { CSVTable *psTable = gtCSVAccess( pszFilename ); int nFileLen, i, nMaxLineCount, iLine = 0; char *pszThisLine; if( psTable->pszRawData != NULL ) return; /* -------------------------------------------------------------------- */ /* Ingest whole file. */ /* -------------------------------------------------------------------- */ VSIFSeek( psTable->fp, 0, SEEK_END ); nFileLen = VSIFTell( psTable->fp ); VSIRewind( psTable->fp ); psTable->pszRawData = (char *) CPLMalloc(nFileLen+1); if( (int) VSIFRead( psTable->pszRawData, 1, nFileLen, psTable->fp ) != nFileLen ) { CPLFree( psTable->pszRawData ); psTable->pszRawData = NULL; CPLError( CE_Failure, CPLE_FileIO, "Read of file %s failed.", psTable->pszFilename ); return; } psTable->pszRawData[nFileLen] = '\0'; /* -------------------------------------------------------------------- */ /* Get count of newlines so we can allocate line array. */ /* -------------------------------------------------------------------- */ nMaxLineCount = 0; for( i = 0; i < nFileLen; i++ ) { if( psTable->pszRawData[i] == 10 ) nMaxLineCount++; } psTable->papszLines = (char **) CPLCalloc(sizeof(char*),nMaxLineCount); /* -------------------------------------------------------------------- */ /* Build a list of record pointers into the raw data buffer */ /* based on line terminators. Zero terminate the line */ /* strings. */ /* -------------------------------------------------------------------- */ /* skip header line */ pszThisLine = CSVFindNextLine( psTable->pszRawData ); while( pszThisLine != NULL && iLine < nMaxLineCount ) { psTable->papszLines[iLine++] = pszThisLine; pszThisLine = CSVFindNextLine( pszThisLine ); } psTable->nLineCount = iLine; /* -------------------------------------------------------------------- */ /* Allocate and populate index array. Ensure they are in */ /* ascending order so that binary searches can be done on the */ /* array. */ /* -------------------------------------------------------------------- */ psTable->panLineIndex = (int *) CPLMalloc(sizeof(int)*psTable->nLineCount); for( i = 0; i < psTable->nLineCount; i++ ) { psTable->panLineIndex[i] = atoi(psTable->papszLines[i]); if( i > 0 && psTable->panLineIndex[i] < psTable->panLineIndex[i-1] ) { CPLFree( psTable->panLineIndex ); psTable->panLineIndex = NULL; break; } } psTable->iLastLine = -1; /* -------------------------------------------------------------------- */ /* We should never need the file handle against, so close it. */ /* -------------------------------------------------------------------- */ VSIFClose( psTable->fp ); psTable->fp = NULL; } /************************************************************************/ /* CSVReadParseLine() */ /* */ /* Read one line, and return split into fields. The return */ /* result is a stringlist, in the sense of the CSL functions. */ /************************************************************************/ char **CSVReadParseLine( FILE * fp ) { const char *pszLine; char *pszWorkLine; char **papszReturn; CPLAssert( fp != NULL ); if( fp == NULL ) return( NULL ); pszLine = CPLReadLine( fp ); if( pszLine == NULL ) return( NULL ); /* -------------------------------------------------------------------- */ /* If there are no quotes, then this is the simple case. */ /* Parse, and return tokens. */ /* -------------------------------------------------------------------- */ if( strchr(pszLine,'\"') == NULL ) return CSVSplitLine( pszLine ); /* -------------------------------------------------------------------- */ /* We must now count the quotes in our working string, and as */ /* long as it is odd, keep adding new lines. */ /* -------------------------------------------------------------------- */ pszWorkLine = CPLStrdup( pszLine ); while( TRUE ) { int i, nCount = 0; for( i = 0; pszWorkLine[i] != '\0'; i++ ) { if( pszWorkLine[i] == '\"' && (i == 0 || pszWorkLine[i-1] != '\\') ) nCount++; } if( nCount % 2 == 0 ) break; pszLine = CPLReadLine( fp ); if( pszLine == NULL ) break; pszWorkLine = (char *) CPLRealloc(pszWorkLine, strlen(pszWorkLine) + strlen(pszLine) + 1); strcat( pszWorkLine, pszLine ); } papszReturn = CSVSplitLine( pszWorkLine ); CPLFree( pszWorkLine ); return papszReturn; } /************************************************************************/ /* CSVCompare() */ /* */ /* Compare a field to a search value using a particular */ /* criteria. */ /************************************************************************/ static int CSVCompare( const char * pszFieldValue, const char * pszTarget, CSVCompareCriteria eCriteria ) { if( eCriteria == CC_ExactString ) { return( strcmp( pszFieldValue, pszTarget ) == 0 ); } else if( eCriteria == CC_ApproxString ) { return( EQUAL( pszFieldValue, pszTarget ) ); } else if( eCriteria == CC_Integer ) { return( atoi(pszFieldValue) == atoi(pszTarget) ); } return FALSE; } /************************************************************************/ /* CSVScanLines() */ /* */ /* Read the file scanline for lines where the key field equals */ /* the indicated value with the suggested comparison criteria. */ /* Return the first matching line split into fields. */ /************************************************************************/ char **CSVScanLines( FILE *fp, int iKeyField, const char * pszValue, CSVCompareCriteria eCriteria ) { char **papszFields = NULL; int bSelected = FALSE, nTestValue; CPLAssert( pszValue != NULL ); CPLAssert( iKeyField >= 0 ); CPLAssert( fp != NULL ); nTestValue = atoi(pszValue); while( !bSelected ) { papszFields = CSVReadParseLine( fp ); if( papszFields == NULL ) return( NULL ); if( CSLCount( papszFields ) < iKeyField+1 ) { /* not selected */ } else if( eCriteria == CC_Integer && atoi(papszFields[iKeyField]) == nTestValue ) { bSelected = TRUE; } else { bSelected = CSVCompare( papszFields[iKeyField], pszValue, eCriteria ); } if( !bSelected ) { CSLDestroy( papszFields ); papszFields = NULL; } } return( papszFields ); } /************************************************************************/ /* CSVScanLinesIndexed() */ /* */ /* Read the file scanline for lines where the key field equals */ /* the indicated value with the suggested comparison criteria. */ /* Return the first matching line split into fields. */ /************************************************************************/ static char ** CSVScanLinesIndexed( CSVTable *psTable, int nKeyValue ) { int iTop, iBottom, iMiddle, iResult = -1; CPLAssert( psTable->panLineIndex != NULL ); /* -------------------------------------------------------------------- */ /* Find target record with binary search. */ /* -------------------------------------------------------------------- */ iTop = psTable->nLineCount-1; iBottom = 0; while( iTop >= iBottom ) { iMiddle = (iTop + iBottom) / 2; if( psTable->panLineIndex[iMiddle] > nKeyValue ) iTop = iMiddle - 1; else if( psTable->panLineIndex[iMiddle] < nKeyValue ) iBottom = iMiddle + 1; else { iResult = iMiddle; break; } } if( iResult == -1 ) return NULL; /* -------------------------------------------------------------------- */ /* Parse target line, and update iLastLine indicator. */ /* -------------------------------------------------------------------- */ psTable->iLastLine = iResult; return CSVSplitLine( psTable->papszLines[iResult] ); } /************************************************************************/ /* CSVScanLinesIngested() */ /* */ /* Read the file scanline for lines where the key field equals */ /* the indicated value with the suggested comparison criteria. */ /* Return the first matching line split into fields. */ /************************************************************************/ static char ** CSVScanLinesIngested( CSVTable *psTable, int iKeyField, const char * pszValue, CSVCompareCriteria eCriteria ) { char **papszFields = NULL; int bSelected = FALSE, nTestValue; CPLAssert( pszValue != NULL ); CPLAssert( iKeyField >= 0 ); nTestValue = atoi(pszValue); /* -------------------------------------------------------------------- */ /* Short cut for indexed files. */ /* -------------------------------------------------------------------- */ if( iKeyField == 0 && eCriteria == CC_Integer && psTable->panLineIndex != NULL ) return CSVScanLinesIndexed( psTable, nTestValue ); /* -------------------------------------------------------------------- */ /* Scan from in-core lines. */ /* -------------------------------------------------------------------- */ while( !bSelected && psTable->iLastLine+1 < psTable->nLineCount ) { psTable->iLastLine++; papszFields = CSVSplitLine( psTable->papszLines[psTable->iLastLine] ); if( CSLCount( papszFields ) < iKeyField+1 ) { /* not selected */ } else if( eCriteria == CC_Integer && atoi(papszFields[iKeyField]) == nTestValue ) { bSelected = TRUE; } else { bSelected = CSVCompare( papszFields[iKeyField], pszValue, eCriteria ); } if( !bSelected ) { CSLDestroy( papszFields ); papszFields = NULL; } } return( papszFields ); } /************************************************************************/ /* CSVScanFile() */ /* */ /* Scan a whole file using criteria similar to above, but also */ /* taking care of file opening and closing. */ /************************************************************************/ char **CSVScanFile( const char * pszFilename, int iKeyField, const char * pszValue, CSVCompareCriteria eCriteria ) { CSVTable *psTable; /* -------------------------------------------------------------------- */ /* Get access to the table. */ /* -------------------------------------------------------------------- */ CPLAssert( pszFilename != NULL ); if( iKeyField < 0 ) return NULL; psTable = gtCSVAccess( pszFilename ); if( psTable == NULL ) return NULL; CSVIngest( pszFilename ); /* -------------------------------------------------------------------- */ /* Does the current record match the criteria? If so, just */ /* return it again. */ /* -------------------------------------------------------------------- */ if( iKeyField >= 0 && iKeyField < CSLCount(psTable->papszRecFields) && CSVCompare(pszValue,psTable->papszRecFields[iKeyField],eCriteria) ) { return psTable->papszRecFields; } /* -------------------------------------------------------------------- */ /* Scan the file from the beginning, replacing the ``current */ /* record'' in our structure with the one that is found. */ /* -------------------------------------------------------------------- */ psTable->iLastLine = -1; CSLDestroy( psTable->papszRecFields ); if( psTable->pszRawData != NULL ) psTable->papszRecFields = CSVScanLinesIngested( psTable, iKeyField, pszValue, eCriteria ); else { VSIRewind( psTable->fp ); CPLReadLine( psTable->fp ); /* throw away the header line */ psTable->papszRecFields = CSVScanLines( psTable->fp, iKeyField, pszValue, eCriteria ); } return( psTable->papszRecFields ); } /************************************************************************/ /* CPLGetFieldId() */ /* */ /* Read the first record of a CSV file (rewinding to be sure), */ /* and find the field with the indicated name. Returns -1 if */ /* it fails to find the field name. Comparison is case */ /* insensitive, but otherwise exact. After this function has */ /* been called the file pointer will be positioned just after */ /* the first record. */ /************************************************************************/ int CSVGetFieldId( FILE * fp, const char * pszFieldName ) { char **papszFields; int i; CPLAssert( fp != NULL && pszFieldName != NULL ); VSIRewind( fp ); papszFields = CSVReadParseLine( fp ); for( i = 0; papszFields != NULL && papszFields[i] != NULL; i++ ) { if( EQUAL(papszFields[i],pszFieldName) ) { CSLDestroy( papszFields ); return i; } } CSLDestroy( papszFields ); return -1; } /************************************************************************/ /* CSVGetFileFieldId() */ /* */ /* Same as CPLGetFieldId(), except that we get the file based */ /* on filename, rather than having an existing handle. */ /************************************************************************/ int CSVGetFileFieldId( const char * pszFilename, const char * pszFieldName ) { CSVTable *psTable; int i; /* -------------------------------------------------------------------- */ /* Get access to the table. */ /* -------------------------------------------------------------------- */ CPLAssert( pszFilename != NULL ); psTable = gtCSVAccess( pszFilename ); if( psTable == NULL ) return -1; /* -------------------------------------------------------------------- */ /* Find the requested field. */ /* -------------------------------------------------------------------- */ for( i = 0; psTable->papszFieldNames != NULL && psTable->papszFieldNames[i] != NULL; i++ ) { if( EQUAL(psTable->papszFieldNames[i],pszFieldName) ) { return i; } } return -1; } /************************************************************************/ /* CSVScanFileByName() */ /* */ /* Same as CSVScanFile(), but using a field name instead of a */ /* field number. */ /************************************************************************/ char **CSVScanFileByName( const char * pszFilename, const char * pszKeyFieldName, const char * pszValue, CSVCompareCriteria eCriteria ) { int iKeyField; iKeyField = CSVGetFileFieldId( pszFilename, pszKeyFieldName ); if( iKeyField == -1 ) return NULL; return( CSVScanFile( pszFilename, iKeyField, pszValue, eCriteria ) ); } /************************************************************************/ /* CSVGetField() */ /* */ /* The all-in-one function to fetch a particular field value */ /* from a CSV file. Note this function will return an empty */ /* string, rather than NULL if it fails to find the desired */ /* value for some reason. The caller can't establish that the */ /* fetch failed. */ /************************************************************************/ const char *CSVGetField( const char * pszFilename, const char * pszKeyFieldName, const char * pszKeyFieldValue, CSVCompareCriteria eCriteria, const char * pszTargetField ) { CSVTable *psTable; char **papszRecord; int iTargetField; /* -------------------------------------------------------------------- */ /* Find the table. */ /* -------------------------------------------------------------------- */ psTable = gtCSVAccess( pszFilename ); if( psTable == NULL ) return ""; /* -------------------------------------------------------------------- */ /* Find the correct record. */ /* -------------------------------------------------------------------- */ papszRecord = CSVScanFileByName( pszFilename, pszKeyFieldName, pszKeyFieldValue, eCriteria ); if( papszRecord == NULL ) return ""; /* -------------------------------------------------------------------- */ /* Figure out which field we want out of this. */ /* -------------------------------------------------------------------- */ iTargetField = CSVGetFileFieldId( pszFilename, pszTargetField ); if( iTargetField < 0 ) return ""; if( iTargetField >= CSLCount( papszRecord ) ) return ""; return( papszRecord[iTargetField] ); } /************************************************************************/ /* CSVFilename() */ /* */ /* Return the full path to a particular CSV file. This will */ /* eventually be something the application can override. */ /************************************************************************/ static const char *(*pfnCSVFilenameHook)(const char *) = NULL; const char * CSVFilename( const char *pszBasename ) { static char szPath[512]; if( pfnCSVFilenameHook == NULL ) { FILE *fp = NULL; if( getenv("GEOTIFF_CSV") != NULL ) { sprintf( szPath, "%s/%s", getenv("GEOTIFF_CSV"), pszBasename ); } #ifdef CSV_DATA_DIR else { sprintf( szPath, "%s/%s", CSV_DATA_DIR, pszBasename ); } #else else if( (fp = fopen( "/usr/local/share/epsg/csv/pcs.csv", "rt" )) != NULL ) { sprintf( szPath, "/usr/local/share/epsg/csv/%s", pszBasename ); } else if( (fp = fopen( "csv/pcs.csv", "rt" )) != NULL ) { sprintf( szPath, "csv/%s", pszBasename ); } else if( (fp = fopen( "share/epsg_csv/pcs.csv", "rt" )) != NULL ) { sprintf( szPath, "share/epsg_csv/%s", pszBasename ); } else if( (fp = fopen( "/usr/share/epsg_csv/pcs.csv", "rt" )) != NULL ) { sprintf( szPath, "/usr/share/epsg_csv/%s", pszBasename ); } else { sprintf( szPath, "/usr/local/share/epsg_csv/%s", pszBasename ); } #endif if( fp != NULL ) fclose( fp ); return( szPath ); } else return( pfnCSVFilenameHook( pszBasename ) ); } /************************************************************************/ /* SetCSVFilenameHook() */ /* */ /* Applications can use this to set a function that will */ /* massage CSV filenames. */ /************************************************************************/ /** * Override CSV file search method. * * @param CSVFileOverride The pointer to a function which will return the * full path for a given filename. * This function allows an application to override how the GTIFGetDefn() and related function find the CSV (Comma Separated Value) values required. The pfnHook argument should be a pointer to a function that will take in a CSV filename and return a full path to the file. The returned string should be to an internal static buffer so that the caller doesn't have to free the result. Example:
The listgeo utility uses the following override function if the user specified a CSV file directory with the -t commandline switch (argument put into CSVDirName).


    ...


    SetCSVFilenameHook( CSVFileOverride );

    ...


static const char *CSVFileOverride( const char * pszInput )

{
    static char         szPath[1024];

#ifdef WIN32
    sprintf( szPath, "%s\\%s", CSVDirName, pszInput );
#else    
    sprintf( szPath, "%s/%s", CSVDirName, pszInput );
#endif    

    return( szPath );
}
*/ void SetCSVFilenameHook( const char *(*pfnNewHook)( const char * ) ) { pfnCSVFilenameHook = pfnNewHook; }