diff options
Diffstat (limited to 'navit/support')
-rw-r--r-- | navit/support/shapefile/dbfopen.c | 1028 | ||||
-rw-r--r-- | navit/support/shapefile/shapefil.h | 394 | ||||
-rw-r--r-- | navit/support/shapefile/shpopen.c | 1615 | ||||
-rw-r--r-- | navit/support/shapefile/shptree.c | 472 |
4 files changed, 2573 insertions, 936 deletions
diff --git a/navit/support/shapefile/dbfopen.c b/navit/support/shapefile/dbfopen.c index 51944fc53..148e593a4 100644 --- a/navit/support/shapefile/dbfopen.c +++ b/navit/support/shapefile/dbfopen.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: dbfopen.c,v 1.83 2008/11/12 14:28:15 fwarmerdam Exp $ + * $Id: dbfopen.c,v 1.92 2016-12-05 18:44:08 erouault Exp $ * * Project: Shapelib * Purpose: Implementation of .dbf access API documented in dbf_api.html. @@ -7,13 +7,14 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2012-2013, Even Rouault <even dot rouault at mines-paris dot org> * * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * or at the option of the licensee under the LGPL (see COPYING). This * option is discussed in more detail in shapelib.html. * * -- - * + * * 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 @@ -34,6 +35,51 @@ ****************************************************************************** * * $Log: dbfopen.c,v $ + * Revision 1.92 2016-12-05 18:44:08 erouault + * * dbfopen.c, shapefil.h: write DBF end-of-file character 0x1A by default. + * This behaviour can be controlled with the DBFSetWriteEndOfFileChar() + * function. + * + * Revision 1.91 2016-12-05 12:44:05 erouault + * * Major overhaul of Makefile build system to use autoconf/automake. + * + * * Warning fixes in contrib/ + * + * Revision 1.90 2016-12-04 15:30:15 erouault + * * shpopen.c, dbfopen.c, shptree.c, shapefil.h: resync with + * GDAL Shapefile driver. Mostly cleanups. SHPObject and DBFInfo + * structures extended with new members. New functions: + * DBFSetLastModifiedDate, SHPOpenLLEx, SHPRestoreSHX, + * SHPSetFastModeReadObject + * + * * sbnsearch.c: new file to implement original ESRI .sbn spatial + * index reading. (no write support). New functions: + * SBNOpenDiskTree, SBNCloseDiskTree, SBNSearchDiskTree, + * SBNSearchDiskTreeInteger, SBNSearchFreeIds + * + * * Makefile, makefile.vc, CMakeLists.txt, shapelib.def: updates + * with new file and symbols. + * + * * commit: helper script to cvs commit + * + * Revision 1.89 2011-07-24 05:59:25 fwarmerdam + * minimize use of CPLError in favor of SAHooks.Error() + * + * Revision 1.88 2011-05-13 17:35:17 fwarmerdam + * added DBFReorderFields() and DBFAlterFields() functions (from Even) + * + * Revision 1.87 2011-05-07 22:41:02 fwarmerdam + * ensure pending record is flushed when adding a native field (GDAL #4073) + * + * Revision 1.86 2011-04-17 15:15:29 fwarmerdam + * Removed unused variable. + * + * Revision 1.85 2010-12-06 16:09:34 fwarmerdam + * fix buffer read overrun fetching code page (bug 2276) + * + * Revision 1.84 2009-10-29 19:59:48 fwarmerdam + * avoid crash on truncated header (gdal #3093) + * * Revision 1.83 2008/11/12 14:28:15 fwarmerdam * DBFCreateField() now works on files with records * @@ -57,8 +103,8 @@ * * Revision 1.77 2007/12/15 20:25:21 bram * dbfopen.c now reads the Code Page information from the DBF file, and exports - * this information as a string through the DBFGetCodePage function. This is - * either the number from the LDID header field ("LDID/<number>") or as the + * this information as a string through the DBFGetCodePage function. This is + * either the number from the LDID header field ("LDID/<number>") or as the * content of an accompanying .CPG file. When creating a DBF file, the code can * be set using DBFCreateEx. * @@ -145,11 +191,45 @@ #include <ctype.h> #include <string.h> +#ifdef USE_CPL +#include "cpl_string.h" +#else + +#if defined(_MSC_VER) +# if _MSC_VER < 1900 +# define snprintf _snprintf +# endif +#elif defined(WIN32) || defined(_WIN32) +# ifndef snprintf +# define snprintf _snprintf +# endif +#endif + +#define CPLsprintf sprintf +#define CPLsnprintf snprintf +#endif + +SHP_CVSID("$Id: dbfopen.c,v 1.92 2016-12-05 18:44:08 erouault Exp $") + #ifndef FALSE # define FALSE 0 # define TRUE 1 #endif +/* File header size */ +#define XBASE_FILEHDR_SZ 32 + +#define HEADER_RECORD_TERMINATOR 0x0D + +/* See http://www.manmrk.net/tutorials/database/xbase/dbf.html */ +#define END_OF_FILE_CHARACTER 0x1A + +#ifdef USE_CPL +CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused) {} +#else +#define CPL_IGNORE_RET_VAL_INT(x) x +#endif + /************************************************************************/ /* SfRealloc() */ /* */ @@ -178,8 +258,7 @@ static void * SfRealloc( void * pMem, int nNewSize ) static void DBFWriteHeader(DBFHandle psDBF) { - unsigned char abyHeader[XBASE_FLDHDR_SZ]; - int i; + unsigned char abyHeader[XBASE_FILEHDR_SZ] = { 0 }; if( !psDBF->bNoHeader ) return; @@ -189,21 +268,18 @@ static void DBFWriteHeader(DBFHandle psDBF) /* -------------------------------------------------------------------- */ /* Initialize the file header information. */ /* -------------------------------------------------------------------- */ - for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) - abyHeader[i] = 0; - abyHeader[0] = 0x03; /* memo field? - just copying */ - /* write out a dummy date */ - abyHeader[1] = 95; /* YY */ - abyHeader[2] = 7; /* MM */ - abyHeader[3] = 26; /* DD */ + /* write out update date */ + abyHeader[1] = (unsigned char) psDBF->nUpdateYearSince1900; + abyHeader[2] = (unsigned char) psDBF->nUpdateMonth; + abyHeader[3] = (unsigned char) psDBF->nUpdateDay; /* record count preset at zero */ abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256); abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256); - + abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256); abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256); @@ -214,20 +290,31 @@ static void DBFWriteHeader(DBFHandle psDBF) /* descriptions. */ /* -------------------------------------------------------------------- */ psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); - psDBF->sHooks.FWrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp ); - psDBF->sHooks.FWrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, + psDBF->sHooks.FWrite( abyHeader, XBASE_FILEHDR_SZ, 1, psDBF->fp ); + psDBF->sHooks.FWrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, psDBF->fp ); /* -------------------------------------------------------------------- */ /* Write out the newline character if there is room for it. */ /* -------------------------------------------------------------------- */ - if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 ) + if( psDBF->nHeaderLength > XBASE_FLDHDR_SZ*psDBF->nFields + + XBASE_FLDHDR_SZ ) { char cNewline; - cNewline = 0x0d; + cNewline = HEADER_RECORD_TERMINATOR; psDBF->sHooks.FWrite( &cNewline, 1, 1, psDBF->fp ); } + +/* -------------------------------------------------------------------- */ +/* If the file is new, add a EOF character. */ +/* -------------------------------------------------------------------- */ + if( psDBF->nRecords == 0 && psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } } /************************************************************************/ @@ -245,25 +332,30 @@ static int DBFFlushRecord( DBFHandle psDBF ) { psDBF->bCurrentRecordModified = FALSE; - nRecordOffset = - psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord + psDBF->nHeaderLength; - if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0 - || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord, - psDBF->nRecordLength, + if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0 + || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord, + psDBF->nRecordLength, 1, psDBF->fp ) != 1 ) { -#ifdef USE_CPL - CPLError( CE_Failure, CPLE_FileIO, - "Failure writing DBF record %d.", - psDBF->nCurrentRecord ); -#else - fprintf( stderr, "Failure writing DBF record %d.", + char szMessage[128]; + snprintf( szMessage, sizeof(szMessage), "Failure writing DBF record %d.", psDBF->nCurrentRecord ); -#endif + psDBF->sHooks.Error( szMessage ); return FALSE; } + + if( psDBF->nCurrentRecord == psDBF->nRecords - 1 ) + { + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + } } return TRUE; @@ -283,33 +375,25 @@ static int DBFLoadRecord( DBFHandle psDBF, int iRecord ) if( !DBFFlushRecord( psDBF ) ) return FALSE; - nRecordOffset = + nRecordOffset = psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, SEEK_SET ) != 0 ) { -#ifdef USE_CPL - CPLError( CE_Failure, CPLE_FileIO, - "fseek(%ld) failed on DBF file.\n", - (long) nRecordOffset ); -#else - fprintf( stderr, "fseek(%ld) failed on DBF file.\n", + char szMessage[128]; + snprintf( szMessage, sizeof(szMessage), "fseek(%ld) failed on DBF file.", (long) nRecordOffset ); -#endif + psDBF->sHooks.Error( szMessage ); return FALSE; } - if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord, + if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ) != 1 ) { -#ifdef USE_CPL - CPLError( CE_Failure, CPLE_FileIO, - "fread(%d) failed on DBF file.\n", - psDBF->nRecordLength ); -#else - fprintf( stderr, "fread(%d) failed on DBF file.\n", + char szMessage[128]; + snprintf( szMessage, sizeof(szMessage), "fread(%d) failed on DBF file.", psDBF->nRecordLength ); -#endif + psDBF->sHooks.Error( szMessage ); return FALSE; } @@ -327,33 +411,49 @@ void SHPAPI_CALL DBFUpdateHeader( DBFHandle psDBF ) { - unsigned char abyFileHeader[32]; + unsigned char abyFileHeader[XBASE_FILEHDR_SZ]; if( psDBF->bNoHeader ) DBFWriteHeader( psDBF ); - DBFFlushRecord( psDBF ); + if( !DBFFlushRecord( psDBF ) ) + return; psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); - psDBF->sHooks.FRead( abyFileHeader, 32, 1, psDBF->fp ); - + psDBF->sHooks.FRead( abyFileHeader, sizeof(abyFileHeader), 1, psDBF->fp ); + + abyFileHeader[1] = (unsigned char) psDBF->nUpdateYearSince1900; + abyFileHeader[2] = (unsigned char) psDBF->nUpdateMonth; + abyFileHeader[3] = (unsigned char) psDBF->nUpdateDay; abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256); abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256); abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256); abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256); - + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); - psDBF->sHooks.FWrite( abyFileHeader, 32, 1, psDBF->fp ); + psDBF->sHooks.FWrite( abyFileHeader, sizeof(abyFileHeader), 1, psDBF->fp ); psDBF->sHooks.FFlush( psDBF->fp ); } /************************************************************************/ +/* DBFSetLastModifiedDate() */ +/************************************************************************/ + +void SHPAPI_CALL +DBFSetLastModifiedDate( DBFHandle psDBF, int nYYSince1900, int nMM, int nDD ) +{ + psDBF->nUpdateYearSince1900 = nYYSince1900; + psDBF->nUpdateMonth = nMM; + psDBF->nUpdateDay = nDD; +} + +/************************************************************************/ /* DBFOpen() */ /* */ /* Open a .dbf file. */ /************************************************************************/ - + DBFHandle SHPAPI_CALL DBFOpen( const char * pszFilename, const char * pszAccess ) @@ -370,7 +470,7 @@ DBFOpen( const char * pszFilename, const char * pszAccess ) /* */ /* Open a .dbf file. */ /************************************************************************/ - + DBFHandle SHPAPI_CALL DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) @@ -381,18 +481,19 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) int nFields, nHeadLen, iField, i; char *pszBasename, *pszFullname; int nBufSize = 500; + size_t nFullnameLen; /* -------------------------------------------------------------------- */ /* We only allow the access strings "rb" and "r+". */ /* -------------------------------------------------------------------- */ - if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 + if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0 && strcmp(pszAccess,"r+b") != 0 ) return( NULL ); if( strcmp(pszAccess,"r") == 0 ) pszAccess = "rb"; - + if( strcmp(pszAccess,"r+") == 0 ) pszAccess = "rb+"; @@ -402,7 +503,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszFilename)+5); strcpy( pszBasename, pszFilename ); - for( i = strlen(pszBasename)-1; + for( i = (int)strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} @@ -410,30 +511,31 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.dbf", pszBasename ); - + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.dbf", pszBasename ); + psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) ); psDBF->fp = psHooks->FOpen( pszFullname, pszAccess ); memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) ); if( psDBF->fp == NULL ) { - sprintf( pszFullname, "%s.DBF", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.DBF", pszBasename ); psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess ); } - sprintf( pszFullname, "%s.cpg", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.cpg", pszBasename ); pfCPG = psHooks->FOpen( pszFullname, "r" ); if( pfCPG == NULL ) { - sprintf( pszFullname, "%s.CPG", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.CPG", pszBasename ); pfCPG = psHooks->FOpen( pszFullname, "r" ); } free( pszBasename ); free( pszFullname ); - + if( psDBF->fp == NULL ) { free( psDBF ); @@ -449,7 +551,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) /* Read Table Header info */ /* -------------------------------------------------------------------- */ pabyBuf = (unsigned char *) malloc(nBufSize); - if( psDBF->sHooks.FRead( pabyBuf, 32, 1, psDBF->fp ) != 1 ) + if( psDBF->sHooks.FRead( pabyBuf, XBASE_FILEHDR_SZ, 1, psDBF->fp ) != 1 ) { psDBF->sHooks.FClose( psDBF->fp ); if( pfCPG ) psDBF->sHooks.FClose( pfCPG ); @@ -458,14 +560,25 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) return NULL; } - psDBF->nRecords = - pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256; + DBFSetLastModifiedDate(psDBF, pabyBuf[1], pabyBuf[2], pabyBuf[3]); + + psDBF->nRecords = + pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + (pabyBuf[7] & 0x7f) *256*256*256; psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; psDBF->nRecordLength = pabyBuf[10] + pabyBuf[11]*256; psDBF->iLanguageDriver = pabyBuf[29]; - psDBF->nFields = nFields = (nHeadLen - 32) / 32; + if (psDBF->nRecordLength == 0 || nHeadLen < XBASE_FILEHDR_SZ) + { + psDBF->sHooks.FClose( psDBF->fp ); + if( pfCPG ) psDBF->sHooks.FClose( pfCPG ); + free( pabyBuf ); + free( psDBF ); + return NULL; + } + + psDBF->nFields = nFields = (nHeadLen - XBASE_FILEHDR_SZ) / XBASE_FLDHDR_SZ; psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength); @@ -477,8 +590,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) if( pfCPG ) { size_t n; - char *buffer = (char *) pabyBuf; - buffer[0] = '\0'; + memset( pabyBuf, 0, nBufSize); psDBF->sHooks.FRead( pabyBuf, nBufSize - 1, 1, pfCPG ); n = strcspn( (char *) pabyBuf, "\n\r" ); if( n > 0 ) @@ -491,7 +603,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) } if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 ) { - sprintf( (char *) pabyBuf, "LDID/%d", psDBF->iLanguageDriver ); + snprintf( (char *) pabyBuf, nBufSize, "LDID/%d", psDBF->iLanguageDriver ); psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1); strcpy( psDBF->pszCodePage, (char *) pabyBuf ); } @@ -499,12 +611,13 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ /* Read in Field Definitions */ /* -------------------------------------------------------------------- */ - + pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen); psDBF->pszHeader = (char *) pabyBuf; - psDBF->sHooks.FSeek( psDBF->fp, 32, 0 ); - if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 ) + psDBF->sHooks.FSeek( psDBF->fp, XBASE_FILEHDR_SZ, 0 ); + if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-XBASE_FILEHDR_SZ, 1, + psDBF->fp ) != 1 ) { psDBF->sHooks.FClose( psDBF->fp ); free( pabyBuf ); @@ -522,7 +635,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) { unsigned char *pabyFInfo; - pabyFInfo = pabyBuf+iField*32; + pabyFInfo = pabyBuf+iField*XBASE_FLDHDR_SZ; if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' ) { @@ -548,10 +661,12 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) if( iField == 0 ) psDBF->panFieldOffset[iField] = 1; else - psDBF->panFieldOffset[iField] = + psDBF->panFieldOffset[iField] = psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1]; } + DBFSetWriteEndOfFileChar( psDBF, TRUE ); + return( psDBF ); } @@ -571,7 +686,7 @@ DBFClose(DBFHandle psDBF) if( psDBF->bNoHeader ) DBFWriteHeader( psDBF ); - DBFFlushRecord( psDBF ); + CPL_IGNORE_RET_VAL_INT(DBFFlushRecord( psDBF )); /* -------------------------------------------------------------------- */ /* Update last access date, and number of records if we have */ @@ -648,6 +763,7 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook char *pszFullname, *pszBasename; int i, ldid = -1; char chZero = '\0'; + size_t nFullnameLen; /* -------------------------------------------------------------------- */ /* Compute the base (layer) name. If there is any extension */ @@ -655,7 +771,7 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszFilename)+5); strcpy( pszBasename, pszFilename ); - for( i = strlen(pszBasename)-1; + for( i = (int)strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} @@ -663,25 +779,33 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.dbf", pszBasename ); + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.dbf", pszBasename ); /* -------------------------------------------------------------------- */ /* Create the file. */ /* -------------------------------------------------------------------- */ fp = psHooks->FOpen( pszFullname, "wb" ); if( fp == NULL ) + { + free( pszBasename ); + free( pszFullname ); return( NULL ); - + } + psHooks->FWrite( &chZero, 1, 1, fp ); psHooks->FClose( fp ); fp = psHooks->FOpen( pszFullname, "rb+" ); if( fp == NULL ) + { + free( pszBasename ); + free( pszFullname ); return( NULL ); + } - - sprintf( pszFullname, "%s.cpg", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.cpg", pszBasename ); if( pszCodePage != NULL ) { if( strncmp( pszCodePage, "LDID/", 5 ) == 0 ) @@ -715,8 +839,8 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook psDBF->nRecords = 0; psDBF->nFields = 0; psDBF->nRecordLength = 1; - psDBF->nHeaderLength = 33; - + psDBF->nHeaderLength = XBASE_FILEHDR_SZ + 1; /* + 1 for HEADER_RECORD_TERMINATOR */ + psDBF->panFieldOffset = NULL; psDBF->panFieldSize = NULL; psDBF->panFieldDecimals = NULL; @@ -736,6 +860,9 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 ); strcpy( psDBF->pszCodePage, pszCodePage ); } + DBFSetLastModifiedDate(psDBF, 95, 7, 26); /* dummy date */ + + DBFSetWriteEndOfFileChar(psDBF, TRUE); return( psDBF ); } @@ -747,7 +874,7 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook /************************************************************************/ int SHPAPI_CALL -DBFAddField(DBFHandle psDBF, const char * pszFieldName, +DBFAddField(DBFHandle psDBF, const char * pszFieldName, DBFFieldType eType, int nWidth, int nDecimals ) { @@ -760,11 +887,31 @@ DBFAddField(DBFHandle psDBF, const char * pszFieldName, else chNativeType = 'N'; - return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType, + return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType, nWidth, nDecimals ); } /************************************************************************/ +/* DBFGetNullCharacter() */ +/************************************************************************/ + +static char DBFGetNullCharacter(char chType) +{ + switch (chType) + { + case 'N': + case 'F': + return '*'; + case 'D': + return '0'; + case 'L': + return '?'; + default: + return ' '; + } +} + +/************************************************************************/ /* DBFAddField() */ /* */ /* Add a field to a newly created .dbf file before any records */ @@ -772,7 +919,7 @@ DBFAddField(DBFHandle psDBF, const char * pszFieldName, /************************************************************************/ int SHPAPI_CALL -DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, +DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, char chType, int nWidth, int nDecimals ) { @@ -783,14 +930,40 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, char chFieldFill; SAOffset nRecordOffset; + /* make sure that everything is written in .dbf */ + if( !DBFFlushRecord( psDBF ) ) + return -1; + + if( psDBF->nHeaderLength + XBASE_FLDHDR_SZ > 65535 ) + { + char szMessage[128]; + snprintf( szMessage, sizeof(szMessage), + "Cannot add field %s. Header length limit reached " + "(max 65535 bytes, 2046 fields).", + pszFieldName ); + psDBF->sHooks.Error( szMessage ); + return -1; + } + /* -------------------------------------------------------------------- */ /* Do some checking to ensure we can add records to this file. */ /* -------------------------------------------------------------------- */ if( nWidth < 1 ) return -1; - if( nWidth > 255 ) - nWidth = 255; + if( nWidth > XBASE_FLD_MAX_WIDTH ) + nWidth = XBASE_FLD_MAX_WIDTH; + + if( psDBF->nRecordLength + nWidth > 65535 ) + { + char szMessage[128]; + snprintf( szMessage, sizeof(szMessage), + "Cannot add field %s. Record length limit reached " + "(max 65535 bytes).", + pszFieldName ); + psDBF->sHooks.Error( szMessage ); + return -1; + } nOldRecordLength = psDBF->nRecordLength; nOldHeaderLength = psDBF->nHeaderLength; @@ -801,16 +974,16 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, /* -------------------------------------------------------------------- */ psDBF->nFields++; - psDBF->panFieldOffset = (int *) + psDBF->panFieldOffset = (int *) SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); - psDBF->panFieldSize = (int *) + psDBF->panFieldSize = (int *) SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); - psDBF->panFieldDecimals = (int *) + psDBF->panFieldDecimals = (int *) SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); - psDBF->pachFieldType = (char *) + psDBF->pachFieldType = (char *) SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); /* -------------------------------------------------------------------- */ @@ -825,20 +998,18 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, /* -------------------------------------------------------------------- */ /* Extend the required header information. */ /* -------------------------------------------------------------------- */ - psDBF->nHeaderLength += 32; + psDBF->nHeaderLength += XBASE_FLDHDR_SZ; psDBF->bUpdated = FALSE; - psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader, + psDBF->nFields*XBASE_FLDHDR_SZ); - pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1); + pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * (psDBF->nFields-1); - for( i = 0; i < 32; i++ ) + for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) pszFInfo[i] = '\0'; - if( (int) strlen(pszFieldName) < 10 ) - strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); - else - strncpy( pszFInfo, pszFieldName, 10); + strncpy( pszFInfo, pszFieldName, XBASE_FLDNAME_LEN_WRITE ); pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1]; @@ -852,7 +1023,7 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, pszFInfo[16] = (unsigned char) nWidth; pszFInfo[17] = (unsigned char) nDecimals; } - + /* -------------------------------------------------------------------- */ /* Make the current record buffer appropriately larger. */ /* -------------------------------------------------------------------- */ @@ -870,22 +1041,7 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, /* alloc record */ pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength); - switch (chType) - { - case 'N': - case 'F': - chFieldFill = '*'; - break; - case 'D': - chFieldFill = '0'; - break; - case 'L': - chFieldFill = '?'; - break; - default: - chFieldFill = ' '; - break; - } + chFieldFill = DBFGetNullCharacter(chType); for (i = psDBF->nRecords-1; i >= 0; --i) { @@ -905,6 +1061,17 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); } + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nRecords + psDBF->nHeaderLength; + + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + /* free record */ free(pszRecord); @@ -912,6 +1079,10 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, psDBF->bNoHeader = TRUE; DBFUpdateHeader( psDBF ); + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; + return( psDBF->nFields-1 ); } @@ -961,7 +1132,7 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, /* -------------------------------------------------------------------- */ /* Extract the requested field. */ /* -------------------------------------------------------------------- */ - strncpy( psDBF->pszWorkField, + memcpy( psDBF->pszWorkField, ((const char *) pabyRec) + psDBF->panFieldOffset[iField], psDBF->panFieldSize[iField] ); psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0'; @@ -971,11 +1142,17 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, /* -------------------------------------------------------------------- */ /* Decode the field. */ /* -------------------------------------------------------------------- */ - if( chReqType == 'N' ) + if( chReqType == 'I' ) + { + psDBF->fieldValue.nIntField = atoi(psDBF->pszWorkField); + + pReturnField = &(psDBF->fieldValue.nIntField); + } + else if( chReqType == 'N' ) { - psDBF->dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField); + psDBF->fieldValue.dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField); - pReturnField = &(psDBF->dfDoubleField); + pReturnField = &(psDBF->fieldValue.dfDoubleField); } /* -------------------------------------------------------------------- */ @@ -998,7 +1175,7 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, *pchDst = '\0'; } #endif - + return( pReturnField ); } @@ -1012,14 +1189,14 @@ int SHPAPI_CALL DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) { - double *pdValue; + int *pnValue; - pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); + pnValue = (int *) DBFReadAttribute( psDBF, iRecord, iField, 'I' ); - if( pdValue == NULL ) + if( pnValue == NULL ) return 0; else - return( (int) *pdValue ); + return( *pnValue ); } /************************************************************************/ @@ -1068,34 +1245,28 @@ DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField ) return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) ); } + /************************************************************************/ -/* DBFIsAttributeNULL() */ -/* */ -/* Return TRUE if value for field is NULL. */ +/* DBFIsValueNULL() */ /* */ -/* Contributed by Jim Matthews. */ +/* Return TRUE if the passed string is NULL. */ /************************************************************************/ -int SHPAPI_CALL -DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) - +static int DBFIsValueNULL( char chType, const char* pszValue ) { - const char *pszValue; int i; - pszValue = DBFReadStringAttribute( psDBF, iRecord, iField ); - if( pszValue == NULL ) return TRUE; - switch(psDBF->pachFieldType[iField]) + switch(chType) { case 'N': case 'F': /* - ** We accept all asterisks or all blanks as NULL - ** though according to the spec I think it should be all - ** asterisks. + ** We accept all asterisks or all blanks as NULL + ** though according to the spec I think it should be all + ** asterisks. */ if( pszValue[0] == '*' ) return TRUE; @@ -1112,7 +1283,7 @@ DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) return strncmp(pszValue,"00000000",8) == 0; case 'L': - /* NULL boolean fields have value "?" */ + /* NULL boolean fields have value "?" */ return pszValue[0] == '?'; default: @@ -1122,6 +1293,28 @@ DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) } /************************************************************************/ +/* DBFIsAttributeNULL() */ +/* */ +/* Return TRUE if value for field is NULL. */ +/* */ +/* Contributed by Jim Matthews. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) + +{ + const char *pszValue; + + pszValue = DBFReadStringAttribute( psDBF, iRecord, iField ); + + if( pszValue == NULL ) + return TRUE; + + return DBFIsValueNULL( psDBF->pachFieldType[iField], pszValue ); +} + +/************************************************************************/ /* DBFGetFieldCount() */ /* */ /* Return the number of fields in this table. */ @@ -1151,6 +1344,8 @@ DBFGetRecordCount( DBFHandle psDBF ) /* DBFGetFieldInfo() */ /* */ /* Return any requested information about the field. */ +/* pszFieldName must be at least XBASE_FLDNAME_LEN_READ+1 (=12) */ +/* bytes long. */ /************************************************************************/ DBFFieldType SHPAPI_CALL @@ -1171,20 +1366,21 @@ DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, { int i; - strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 ); - pszFieldName[11] = '\0'; - for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- ) + strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*XBASE_FLDHDR_SZ, + XBASE_FLDNAME_LEN_READ ); + pszFieldName[XBASE_FLDNAME_LEN_READ] = '\0'; + for( i = XBASE_FLDNAME_LEN_READ - 1; i > 0 && pszFieldName[i] == ' '; i-- ) pszFieldName[i] = '\0'; } if ( psDBF->pachFieldType[iField] == 'L' ) return( FTLogical); - else if( psDBF->pachFieldType[iField] == 'N' + else if( psDBF->pachFieldType[iField] == 'N' || psDBF->pachFieldType[iField] == 'F' ) { - if( psDBF->panFieldDecimals[iField] > 0 - || psDBF->panFieldSize[iField] > 10 ) + if( psDBF->panFieldDecimals[iField] > 0 + || psDBF->panFieldSize[iField] >= 10 ) return( FTDouble ); else return( FTInteger ); @@ -1207,7 +1403,7 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, { int i, j, nRetResult = TRUE; unsigned char *pabyRec; - char szSField[400], szFormat[20]; + char szSField[XBASE_FLD_MAX_WIDTH+1], szFormat[20]; /* -------------------------------------------------------------------- */ /* Is this a valid record? */ @@ -1252,33 +1448,9 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, /* -------------------------------------------------------------------- */ if( pValue == NULL ) { - switch(psDBF->pachFieldType[iField]) - { - case 'N': - case 'F': - /* NULL numeric fields have value "****************" */ - memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '*', - psDBF->panFieldSize[iField] ); - break; - - case 'D': - /* NULL date fields have value "00000000" */ - memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '0', - psDBF->panFieldSize[iField] ); - break; - - case 'L': - /* NULL boolean fields have value "?" */ - memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '?', - psDBF->panFieldSize[iField] ); - break; - - default: - /* empty string fields are considered NULL */ - memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), ' ', - psDBF->panFieldSize[iField] ); - break; - } + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), + DBFGetNullCharacter(psDBF->pachFieldType[iField]), + psDBF->panFieldSize[iField] ); return TRUE; } @@ -1290,46 +1462,27 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, case 'D': case 'N': case 'F': - if( psDBF->panFieldDecimals[iField] == 0 ) - { - int nWidth = psDBF->panFieldSize[iField]; + { + int nWidth = psDBF->panFieldSize[iField]; - if( (int) sizeof(szSField)-2 < nWidth ) - nWidth = sizeof(szSField)-2; + if( (int) sizeof(szSField)-2 < nWidth ) + nWidth = sizeof(szSField)-2; - sprintf( szFormat, "%%%dd", nWidth ); - sprintf(szSField, szFormat, (int) *((double *) pValue) ); - if( (int)strlen(szSField) > psDBF->panFieldSize[iField] ) - { - szSField[psDBF->panFieldSize[iField]] = '\0'; - nRetResult = FALSE; - } - - strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), - szSField, strlen(szSField) ); - } - else - { - int nWidth = psDBF->panFieldSize[iField]; - - if( (int) sizeof(szSField)-2 < nWidth ) - nWidth = sizeof(szSField)-2; - - sprintf( szFormat, "%%%d.%df", - nWidth, psDBF->panFieldDecimals[iField] ); - sprintf(szSField, szFormat, *((double *) pValue) ); - if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) - { - szSField[psDBF->panFieldSize[iField]] = '\0'; - nRetResult = FALSE; - } - strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), - szSField, strlen(szSField) ); - } - break; + snprintf( szFormat, sizeof(szFormat), "%%%d.%df", + nWidth, psDBF->panFieldDecimals[iField] ); + CPLsnprintf(szSField, sizeof(szSField), szFormat, *((double *) pValue) ); + if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) + { + szSField[psDBF->panFieldSize[iField]] = '\0'; + nRetResult = FALSE; + } + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + szSField, strlen(szSField) ); + break; + } case 'L': - if (psDBF->panFieldSize[iField] >= 1 && + if (psDBF->panFieldSize[iField] >= 1 && (*(char*)pValue == 'F' || *(char*)pValue == 'T')) *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue; break; @@ -1344,7 +1497,7 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, { memset( pabyRec+psDBF->panFieldOffset[iField], ' ', psDBF->panFieldSize[iField] ); - j = strlen((char *) pValue); + j = (int)strlen((char *) pValue); } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), @@ -1413,7 +1566,7 @@ DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, { memset( pabyRec+psDBF->panFieldOffset[iField], ' ', psDBF->panFieldSize[iField] ); - j = strlen((char *) pValue); + j = (int)strlen((char *) pValue); } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), @@ -1577,21 +1730,24 @@ DBFReadTuple(DBFHandle psDBF, int hEntity ) /************************************************************************/ DBFHandle SHPAPI_CALL -DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) +DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) { DBFHandle newDBF; newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage ); - if ( newDBF == NULL ) return ( NULL ); - + if ( newDBF == NULL ) return ( NULL ); + newDBF->nFields = psDBF->nFields; newDBF->nRecordLength = psDBF->nRecordLength; newDBF->nHeaderLength = psDBF->nHeaderLength; - - newDBF->pszHeader = (char *) malloc ( newDBF->nHeaderLength ); - memcpy ( newDBF->pszHeader, psDBF->pszHeader, newDBF->nHeaderLength ); - - newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); + + if( psDBF->pszHeader ) + { + newDBF->pszHeader = (char *) malloc ( XBASE_FLDHDR_SZ * psDBF->nFields ); + memcpy ( newDBF->pszHeader, psDBF->pszHeader, XBASE_FLDHDR_SZ * psDBF->nFields ); + } + + newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields ); memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); @@ -1602,11 +1758,13 @@ DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) newDBF->bNoHeader = TRUE; newDBF->bUpdated = TRUE; - + newDBF->bWriteEndOfFileChar = psDBF->bWriteEndOfFileChar; + DBFWriteHeader ( newDBF ); DBFClose ( newDBF ); - + newDBF = DBFOpen ( pszFilename, "rb+" ); + newDBF->bWriteEndOfFileChar = psDBF->bWriteEndOfFileChar; return ( newDBF ); } @@ -1639,9 +1797,9 @@ DBFGetNativeFieldType( DBFHandle psDBF, int iField ) static void str_to_upper (char *string) { int len; - short i = -1; + int i = -1; - len = strlen (string); + len = (int)strlen (string); while (++i < len) if (isalpha(string[i]) && islower(string[i])) @@ -1660,20 +1818,23 @@ int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) { - char name[12], name1[12], name2[12]; + char name[XBASE_FLDNAME_LEN_READ+1], + name1[XBASE_FLDNAME_LEN_READ+1], + name2[XBASE_FLDNAME_LEN_READ+1]; int i; - strncpy(name1, pszFieldName,11); - name1[11] = '\0'; + strncpy(name1, pszFieldName,XBASE_FLDNAME_LEN_READ); + name1[XBASE_FLDNAME_LEN_READ] = '\0'; str_to_upper(name1); for( i = 0; i < DBFGetFieldCount(psDBF); i++ ) { DBFGetFieldInfo( psDBF, i, name, NULL, NULL ); - strncpy(name2,name,11); + strncpy(name2,name,XBASE_FLDNAME_LEN_READ); + name2[XBASE_FLDNAME_LEN_READ] = '\0'; str_to_upper(name2); - if(!strncmp(name1,name2,10)) + if(!strcmp(name1,name2)) return(i); } return(-1); @@ -1711,7 +1872,7 @@ int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ) /* DBFMarkRecordDeleted() */ /************************************************************************/ -int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, int bIsDeleted ) { @@ -1735,7 +1896,7 @@ int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, /* -------------------------------------------------------------------- */ if( bIsDeleted ) chNewFlag = '*'; - else + else chNewFlag = ' '; if( psDBF->pszCurrentRecord[0] != chNewFlag ) @@ -1800,28 +1961,29 @@ DBFDeleteField(DBFHandle psDBF, int iField) /* resize fields arrays */ psDBF->nFields--; - psDBF->panFieldOffset = (int *) + psDBF->panFieldOffset = (int *) SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); - psDBF->panFieldSize = (int *) + psDBF->panFieldSize = (int *) SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); - psDBF->panFieldDecimals = (int *) + psDBF->panFieldDecimals = (int *) SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); - psDBF->pachFieldType = (char *) + psDBF->pachFieldType = (char *) SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); /* update header information */ - psDBF->nHeaderLength -= 32; + psDBF->nHeaderLength -= XBASE_FLDHDR_SZ; psDBF->nRecordLength -= nDeletedFieldSize; /* overwrite field information in header */ - memcpy(psDBF->pszHeader + iField*32, - psDBF->pszHeader + (iField+1)*32, - sizeof(char) * (psDBF->nFields - iField)*32); + memmove(psDBF->pszHeader + iField*XBASE_FLDHDR_SZ, + psDBF->pszHeader + (iField+1)*XBASE_FLDHDR_SZ, + sizeof(char) * (psDBF->nFields - iField)*XBASE_FLDHDR_SZ); - psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader, + psDBF->nFields*XBASE_FLDHDR_SZ); /* update size of current record appropriately */ psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, @@ -1841,14 +2003,14 @@ DBFDeleteField(DBFHandle psDBF, int iField) /* shift records to their new positions */ for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++) { - nRecordOffset = + nRecordOffset = nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength; /* load record */ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); - nRecordOffset = + nRecordOffset = psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; /* move record in two steps */ @@ -1860,10 +2022,384 @@ DBFDeleteField(DBFHandle psDBF, int iField) } + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + /* TODO: truncate file */ /* free record */ free(pszRecord); + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* DBFReorderFields() */ +/* */ +/* Reorder the fields of a .dbf file */ +/* */ +/* panMap must be exactly psDBF->nFields long and be a permutation */ +/* of [0, psDBF->nFields-1]. This assumption will not be asserted in the*/ +/* code of DBFReorderFields. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFReorderFields( DBFHandle psDBF, int* panMap ) +{ + SAOffset nRecordOffset; + int i, iRecord; + int *panFieldOffsetNew; + int *panFieldSizeNew; + int *panFieldDecimalsNew; + char *pachFieldTypeNew; + char *pszHeaderNew; + char *pszRecord; + char *pszRecordNew; + + if ( psDBF->nFields == 0 ) + return TRUE; + + /* make sure that everything is written in .dbf */ + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + /* a simple malloc() would be enough, but calloc() helps clang static analyzer */ + panFieldOffsetNew = (int *) calloc(sizeof(int), psDBF->nFields); + panFieldSizeNew = (int *) calloc(sizeof(int), psDBF->nFields); + panFieldDecimalsNew = (int *) calloc(sizeof(int), psDBF->nFields); + pachFieldTypeNew = (char *) calloc(sizeof(char), psDBF->nFields); + pszHeaderNew = (char*) malloc(sizeof(char) * XBASE_FLDHDR_SZ * + psDBF->nFields); + + /* shuffle fields definitions */ + for(i=0; i < psDBF->nFields; i++) + { + panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]]; + panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]]; + pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]]; + memcpy(pszHeaderNew + i * XBASE_FLDHDR_SZ, + psDBF->pszHeader + panMap[i] * XBASE_FLDHDR_SZ, XBASE_FLDHDR_SZ); + } + panFieldOffsetNew[0] = 1; + for(i=1; i < psDBF->nFields; i++) + { + panFieldOffsetNew[i] = panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1]; + } + + free(psDBF->pszHeader); + psDBF->pszHeader = pszHeaderNew; + + /* we're done if we're dealing with not yet created .dbf */ + if ( !(psDBF->bNoHeader && psDBF->nRecords == 0) ) + { + /* force update of header with new header and record length */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + /* alloc record */ + pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength); + pszRecordNew = (char *) malloc(sizeof(char) * psDBF->nRecordLength); + + /* shuffle fields in records */ + for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++) + { + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); + + pszRecordNew[0] = pszRecord[0]; + + for(i=0; i < psDBF->nFields; i++) + { + memcpy(pszRecordNew + panFieldOffsetNew[i], + pszRecord + psDBF->panFieldOffset[panMap[i]], + psDBF->panFieldSize[panMap[i]]); + } + + /* write record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecordNew, psDBF->nRecordLength, 1, psDBF->fp ); + } + + /* free record */ + free(pszRecord); + free(pszRecordNew); + } + + free(psDBF->panFieldOffset); + free(psDBF->panFieldSize); + free(psDBF->panFieldDecimals); + free(psDBF->pachFieldType); + + psDBF->panFieldOffset = panFieldOffsetNew; + psDBF->panFieldSize = panFieldSizeNew; + psDBF->panFieldDecimals =panFieldDecimalsNew; + psDBF->pachFieldType = pachFieldTypeNew; + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; + return TRUE; } + + +/************************************************************************/ +/* DBFAlterFieldDefn() */ +/* */ +/* Alter a field definition in a .dbf file */ +/************************************************************************/ + +int SHPAPI_CALL +DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, + char chType, int nWidth, int nDecimals ) +{ + int i; + int iRecord; + int nOffset; + int nOldWidth; + int nOldRecordLength; + SAOffset nRecordOffset; + char* pszFInfo; + char chOldType; + int bIsNULL; + char chFieldFill; + + if (iField < 0 || iField >= psDBF->nFields) + return FALSE; + + /* make sure that everything is written in .dbf */ + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + chFieldFill = DBFGetNullCharacter(chType); + + chOldType = psDBF->pachFieldType[iField]; + nOffset = psDBF->panFieldOffset[iField]; + nOldWidth = psDBF->panFieldSize[iField]; + nOldRecordLength = psDBF->nRecordLength; + +/* -------------------------------------------------------------------- */ +/* Do some checking to ensure we can add records to this file. */ +/* -------------------------------------------------------------------- */ + if( nWidth < 1 ) + return -1; + + if( nWidth > XBASE_FLD_MAX_WIDTH ) + nWidth = XBASE_FLD_MAX_WIDTH; + +/* -------------------------------------------------------------------- */ +/* Assign the new field information fields. */ +/* -------------------------------------------------------------------- */ + psDBF->panFieldSize[iField] = nWidth; + psDBF->panFieldDecimals[iField] = nDecimals; + psDBF->pachFieldType[iField] = chType; + +/* -------------------------------------------------------------------- */ +/* Update the header information. */ +/* -------------------------------------------------------------------- */ + pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * iField; + + for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) + pszFInfo[i] = '\0'; + + strncpy( pszFInfo, pszFieldName, XBASE_FLDNAME_LEN_WRITE ); + + pszFInfo[11] = psDBF->pachFieldType[iField]; + + if( chType == 'C' ) + { + pszFInfo[16] = (unsigned char) (nWidth % 256); + pszFInfo[17] = (unsigned char) (nWidth / 256); + } + else + { + pszFInfo[16] = (unsigned char) nWidth; + pszFInfo[17] = (unsigned char) nDecimals; + } + +/* -------------------------------------------------------------------- */ +/* Update offsets */ +/* -------------------------------------------------------------------- */ + if (nWidth != nOldWidth) + { + for (i = iField + 1; i < psDBF->nFields; i++) + psDBF->panFieldOffset[i] += nWidth - nOldWidth; + psDBF->nRecordLength += nWidth - nOldWidth; + + psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, + psDBF->nRecordLength); + } + + /* we're done if we're dealing with not yet created .dbf */ + if ( psDBF->bNoHeader && psDBF->nRecords == 0 ) + return TRUE; + + /* force update of header with new header and record length */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType)) + { + char* pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength); + char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1)); + + /* cppcheck-suppress uninitdata */ + pszOldField[nOldWidth] = 0; + + /* move records to their new positions */ + for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++) + { + nRecordOffset = + nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + memcpy(pszOldField, pszRecord + nOffset, nOldWidth); + bIsNULL = DBFIsValueNULL( chOldType, pszOldField ); + + if (nWidth != nOldWidth) + { + if ((chOldType == 'N' || chOldType == 'F') && pszOldField[0] == ' ') + { + /* Strip leading spaces when truncating a numeric field */ + memmove( pszRecord + nOffset, + pszRecord + nOffset + nOldWidth - nWidth, + nWidth ); + } + if (nOffset + nOldWidth < nOldRecordLength) + { + memmove( pszRecord + nOffset + nWidth, + pszRecord + nOffset + nOldWidth, + nOldRecordLength - (nOffset + nOldWidth)); + } + } + + /* Convert null value to the appropriate value of the new type */ + if (bIsNULL) + { + memset( pszRecord + nOffset, chFieldFill, nWidth); + } + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* write record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); + } + + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nRecords + psDBF->nHeaderLength; + + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + /* TODO: truncate file */ + + free(pszRecord); + free(pszOldField); + } + else if (nWidth > nOldWidth) + { + char* pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength); + char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1)); + + /* cppcheck-suppress uninitdata */ + pszOldField[nOldWidth] = 0; + + /* move records to their new positions */ + for (iRecord = psDBF->nRecords - 1; iRecord >= 0; iRecord--) + { + nRecordOffset = + nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + memcpy(pszOldField, pszRecord + nOffset, nOldWidth); + bIsNULL = DBFIsValueNULL( chOldType, pszOldField ); + + if (nOffset + nOldWidth < nOldRecordLength) + { + memmove( pszRecord + nOffset + nWidth, + pszRecord + nOffset + nOldWidth, + nOldRecordLength - (nOffset + nOldWidth)); + } + + /* Convert null value to the appropriate value of the new type */ + if (bIsNULL) + { + memset( pszRecord + nOffset, chFieldFill, nWidth); + } + else + { + if ((chOldType == 'N' || chOldType == 'F')) + { + /* Add leading spaces when expanding a numeric field */ + memmove( pszRecord + nOffset + nWidth - nOldWidth, + pszRecord + nOffset, nOldWidth ); + memset( pszRecord + nOffset, ' ', nWidth - nOldWidth ); + } + else + { + /* Add trailing spaces */ + memset(pszRecord + nOffset + nOldWidth, ' ', nWidth - nOldWidth); + } + } + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* write record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); + } + + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nRecords + psDBF->nHeaderLength; + + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + + free(pszRecord); + free(pszOldField); + } + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* DBFSetWriteEndOfFileChar() */ +/************************************************************************/ + +void SHPAPI_CALL DBFSetWriteEndOfFileChar( DBFHandle psDBF, int bWriteFlag ) +{ + psDBF->bWriteEndOfFileChar = bWriteFlag; +} diff --git a/navit/support/shapefile/shapefil.h b/navit/support/shapefile/shapefil.h index 059244b03..08c645996 100644 --- a/navit/support/shapefile/shapefil.h +++ b/navit/support/shapefile/shapefil.h @@ -2,7 +2,7 @@ #define SHAPEFILE_H_INCLUDED /****************************************************************************** - * $Id: shapefil.h 15715 2008-11-12 15:15:21Z warmerdam $ + * $Id: shapefil.h,v 1.55 2016-12-05 18:44:08 erouault Exp $ * * Project: Shapelib * Purpose: Primary include file for Shapelib. @@ -10,13 +10,14 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2012-2016, Even Rouault <even dot rouault at mines-paris dot org> * * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * or at the option of the licensee under the LGPL (see COPYING). This * option is discussed in more detail in shapelib.html. * * -- - * + * * 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 @@ -37,7 +38,52 @@ ****************************************************************************** * * $Log: shapefil.h,v $ - * Revision 1.46 2008/11/12 14:28:15 fwarmerdam + * Revision 1.55 2016-12-05 18:44:08 erouault + * * dbfopen.c, shapefil.h: write DBF end-of-file character 0x1A by default. + * This behaviour can be controlled with the DBFSetWriteEndOfFileChar() + * function. + * + * Revision 1.54 2016-12-05 12:44:05 erouault + * * Major overhaul of Makefile build system to use autoconf/automake. + * + * * Warning fixes in contrib/ + * + * Revision 1.53 2016-12-04 15:30:15 erouault + * * shpopen.c, dbfopen.c, shptree.c, shapefil.h: resync with + * GDAL Shapefile driver. Mostly cleanups. SHPObject and DBFInfo + * structures extended with new members. New functions: + * DBFSetLastModifiedDate, SHPOpenLLEx, SHPRestoreSHX, + * SHPSetFastModeReadObject + * + * * sbnsearch.c: new file to implement original ESRI .sbn spatial + * index reading. (no write support). New functions: + * SBNOpenDiskTree, SBNCloseDiskTree, SBNSearchDiskTree, + * SBNSearchDiskTreeInteger, SBNSearchFreeIds + * + * * Makefile, makefile.vc, CMakeLists.txt, shapelib.def: updates + * with new file and symbols. + * + * * commit: helper script to cvs commit + * + * Revision 1.52 2011-12-11 22:26:46 fwarmerdam + * upgrade .qix access code to use SAHooks (gdal #3365) + * + * Revision 1.51 2011-07-24 05:59:25 fwarmerdam + * minimize use of CPLError in favor of SAHooks.Error() + * + * Revision 1.50 2011-05-13 17:35:17 fwarmerdam + * added DBFReorderFields() and DBFAlterFields() functions (from Even) + * + * Revision 1.49 2011-04-16 14:38:21 fwarmerdam + * avoid warnings with gcc on SHP_CVSID + * + * Revision 1.48 2010-08-27 23:42:52 fwarmerdam + * add SHPAPI_CALL attribute in code + * + * Revision 1.47 2010-01-28 11:34:34 fwarmerdam + * handle the shape file length limits more gracefully (#3236) + * + * Revision 1.46 2008-11-12 14:28:15 fwarmerdam * DBFCreateField() now works on files with records * * Revision 1.45 2008/11/11 17:47:10 fwarmerdam @@ -129,8 +175,7 @@ #endif #ifdef USE_CPL -#include "cpl_error.h" -#include "cpl_vsi.h" +#include "cpl_conv.h" #endif #ifdef __cplusplus @@ -153,7 +198,7 @@ extern "C" { /* is disabled. */ /* -------------------------------------------------------------------- */ #define DISABLE_MULTIPATCH_MEASURE - + /* -------------------------------------------------------------------- */ /* SHPAPI_CALL */ /* */ @@ -177,7 +222,7 @@ extern "C" { /* #define SHPAPI_CALL __declspec(dllexport) __stdcall */ /* #define SHPAPI_CALL1 __declspec(dllexport) * __stdcall */ /* */ -/* The complexity of the situtation is partly caused by the */ +/* The complexity of the situation is partly caused by the */ /* peculiar requirement of Visual C++ that __stdcall appear */ /* after any "*"'s in the return value of a function while the */ /* __declspec(dllexport) must appear before them. */ @@ -200,14 +245,18 @@ extern "C" { #ifndef SHPAPI_CALL1 # define SHPAPI_CALL1(x) x SHPAPI_CALL #endif - + /* -------------------------------------------------------------------- */ /* Macros for controlling CVSID and ensuring they don't appear */ /* as unreferenced variables resulting in lots of warnings. */ /* -------------------------------------------------------------------- */ #ifndef DISABLE_CVSID -# define SHP_CVSID(string) static char cpl_cvsid[] = string; \ -static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); } +# if defined(__GNUC__) && __GNUC__ >= 4 +# define SHP_CVSID(string) static const char cpl_cvsid[] __attribute__((used)) = string; +# else +# define SHP_CVSID(string) static const char cpl_cvsid[] = string; \ +static const char *cvsid_aw() { return( cvsid_aw() ? NULL : cpl_cvsid ); } +# endif #else # define SHP_CVSID(string) #endif @@ -217,8 +266,8 @@ static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); } /* UTF-8 encoded filenames Unicode filenames */ /* -------------------------------------------------------------------- */ #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) -# define SHPAPI_WINDOWS -# define SHPAPI_UTF8_HOOKS +# define SHPAPI_WINDOWS +# define SHPAPI_UTF8_HOOKS #endif /* -------------------------------------------------------------------- */ @@ -252,29 +301,36 @@ void SHPAPI_CALL SASetupUtf8Hooks( SAHooks *psHooks ); /************************************************************************/ /* SHP Support. */ /************************************************************************/ -typedef struct +typedef struct tagSHPObject SHPObject; + +typedef struct { SAHooks sHooks; SAFile fpSHP; - SAFile fpSHX; + SAFile fpSHX; + + int nShapeType; /* SHPT_* */ - int nShapeType; /* SHPT_* */ - - int nFileSize; /* SHP file */ + unsigned int nFileSize; /* SHP file */ int nRecords; - int nMaxRecords; - int *panRecOffset; - int *panRecSize; + int nMaxRecords; + unsigned int*panRecOffset; + unsigned int *panRecSize; - double adBoundsMin[4]; - double adBoundsMax[4]; + double adBoundsMin[4]; + double adBoundsMax[4]; - int bUpdated; + int bUpdated; unsigned char *pabyRec; int nBufSize; + + int bFastModeReadObject; + unsigned char *pabyObjectBuf; + int nObjectBufSize; + SHPObject* psCachedObject; } SHPInfo; typedef SHPInfo * SHPHandle; @@ -282,66 +338,66 @@ typedef SHPInfo * SHPHandle; /* -------------------------------------------------------------------- */ /* Shape types (nSHPType) */ /* -------------------------------------------------------------------- */ -#define SHPT_NULL 0 -#define SHPT_POINT 1 -#define SHPT_ARC 3 -#define SHPT_POLYGON 5 -#define SHPT_MULTIPOINT 8 -#define SHPT_POINTZ 11 -#define SHPT_ARCZ 13 -#define SHPT_POLYGONZ 15 +#define SHPT_NULL 0 +#define SHPT_POINT 1 +#define SHPT_ARC 3 +#define SHPT_POLYGON 5 +#define SHPT_MULTIPOINT 8 +#define SHPT_POINTZ 11 +#define SHPT_ARCZ 13 +#define SHPT_POLYGONZ 15 #define SHPT_MULTIPOINTZ 18 -#define SHPT_POINTM 21 -#define SHPT_ARCM 23 -#define SHPT_POLYGONM 25 +#define SHPT_POINTM 21 +#define SHPT_ARCM 23 +#define SHPT_POLYGONM 25 #define SHPT_MULTIPOINTM 28 #define SHPT_MULTIPATCH 31 - /* -------------------------------------------------------------------- */ /* Part types - everything but SHPT_MULTIPATCH just uses */ /* SHPP_RING. */ /* -------------------------------------------------------------------- */ -#define SHPP_TRISTRIP 0 -#define SHPP_TRIFAN 1 -#define SHPP_OUTERRING 2 -#define SHPP_INNERRING 3 -#define SHPP_FIRSTRING 4 -#define SHPP_RING 5 +#define SHPP_TRISTRIP 0 +#define SHPP_TRIFAN 1 +#define SHPP_OUTERRING 2 +#define SHPP_INNERRING 3 +#define SHPP_FIRSTRING 4 +#define SHPP_RING 5 /* -------------------------------------------------------------------- */ /* SHPObject - represents on shape (without attributes) read */ /* from the .shp file. */ /* -------------------------------------------------------------------- */ -typedef struct +struct tagSHPObject { - int nSHPType; + int nSHPType; - int nShapeId; /* -1 is unknown/unassigned */ + int nShapeId; /* -1 is unknown/unassigned */ - int nParts; - int *panPartStart; - int *panPartType; - - int nVertices; - double *padfX; - double *padfY; - double *padfZ; - double *padfM; + int nParts; + int *panPartStart; + int *panPartType; - double dfXMin; - double dfYMin; - double dfZMin; - double dfMMin; + int nVertices; + double *padfX; + double *padfY; + double *padfZ; + double *padfM; - double dfXMax; - double dfYMax; - double dfZMax; - double dfMMax; + double dfXMin; + double dfYMin; + double dfZMin; + double dfMMin; - int bMeasureIsUsed; -} SHPObject; + double dfXMax; + double dfYMax; + double dfZMax; + double dfMMax; + + int bMeasureIsUsed; + int bFastModeReadObject; +}; /* -------------------------------------------------------------------- */ /* SHP API Prototypes */ @@ -352,9 +408,23 @@ typedef struct SHPHandle SHPAPI_CALL SHPOpen( const char * pszShapeFile, const char * pszAccess ); SHPHandle SHPAPI_CALL - SHPOpenLL( const char *pszShapeFile, const char *pszAccess, + SHPOpenLL( const char *pszShapeFile, const char *pszAccess, SAHooks *psHooks ); SHPHandle SHPAPI_CALL + SHPOpenLLEx( const char *pszShapeFile, const char *pszAccess, + SAHooks *psHooks, int bRestoreSHX ); + +int SHPAPI_CALL + SHPRestoreSHX( const char *pszShapeFile, const char *pszAccess, + SAHooks *psHooks ); + +/* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */ +/* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */ +/* The SHPObject padfZ and padfM members may be NULL depending on the geometry */ +/* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */ +void SHPAPI_CALL SHPSetFastModeReadObject( SHPHandle hSHP, int bFastMode ); + +SHPHandle SHPAPI_CALL SHPCreate( const char * pszShapeFile, int nShapeType ); SHPHandle SHPAPI_CALL SHPCreateLL( const char * pszShapeFile, int nShapeType, @@ -373,15 +443,15 @@ void SHPAPI_CALL void SHPAPI_CALL SHPComputeExtents( SHPObject * psObject ); SHPObject SHPAPI_CALL1(*) - SHPCreateObject( int nSHPType, int nShapeId, int nParts, + SHPCreateObject( int nSHPType, int nShapeId, int nParts, const int * panPartStart, const int * panPartType, - int nVertices, + int nVertices, const double * padfX, const double * padfY, const double * padfZ, const double * padfM ); SHPObject SHPAPI_CALL1(*) SHPCreateSimpleObject( int nSHPType, int nVertices, - const double * padfX, - const double * padfY, + const double * padfX, + const double * padfY, const double * padfZ ); int SHPAPI_CALL @@ -400,7 +470,7 @@ const char SHPAPI_CALL1(*) /* -------------------------------------------------------------------- */ /* this can be two or four for binary or quad tree */ -#define MAX_SUBNODE 4 +#define MAX_SUBNODE 4 /* upper limit of tree levels for automatic estimation */ #define MAX_DEFAULT_TREE_DEPTH 12 @@ -408,100 +478,150 @@ const char SHPAPI_CALL1(*) typedef struct shape_tree_node { /* region covered by this node */ - double adfBoundsMin[4]; - double adfBoundsMax[4]; + double adfBoundsMin[4]; + double adfBoundsMax[4]; /* list of shapes stored at this node. The papsShapeObj pointers or the whole list can be NULL */ - int nShapeCount; - int *panShapeIds; + int nShapeCount; + int *panShapeIds; SHPObject **papsShapeObj; - int nSubNodes; + int nSubNodes; struct shape_tree_node *apsSubNode[MAX_SUBNODE]; - + } SHPTreeNode; typedef struct { SHPHandle hSHP; - - int nMaxDepth; - int nDimension; + + int nMaxDepth; + int nDimension; int nTotalCount; - - SHPTreeNode *psRoot; + + SHPTreeNode *psRoot; } SHPTree; SHPTree SHPAPI_CALL1(*) SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, double *padfBoundsMin, double *padfBoundsMax ); -void SHPAPI_CALL +void SHPAPI_CALL SHPDestroyTree( SHPTree * hTree ); -int SHPAPI_CALL +int SHPAPI_CALL SHPWriteTree( SHPTree *hTree, const char * pszFilename ); -SHPTree SHPAPI_CALL - SHPReadTree( const char * pszFilename ); -int SHPAPI_CALL - SHPTreeAddObject( SHPTree * hTree, SHPObject * psObject ); -int SHPAPI_CALL +int SHPAPI_CALL SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject ); -int SHPAPI_CALL +int SHPAPI_CALL SHPTreeRemoveShapeId( SHPTree * hTree, int nShapeId ); -void SHPAPI_CALL +void SHPAPI_CALL SHPTreeTrimExtraNodes( SHPTree * hTree ); -int SHPAPI_CALL1(*) +int SHPAPI_CALL1(*) SHPTreeFindLikelyShapes( SHPTree * hTree, double * padfBoundsMin, double * padfBoundsMax, int * ); -int SHPAPI_CALL +int SHPAPI_CALL SHPCheckBoundsOverlap( double *, double *, double *, double *, int ); -int SHPAPI_CALL1(*) -SHPSearchDiskTree( FILE *fp, +int SHPAPI_CALL1(*) +SHPSearchDiskTree( FILE *fp, double *padfBoundsMin, double *padfBoundsMax, int *pnShapeCount ); +typedef struct SHPDiskTreeInfo* SHPTreeDiskHandle; + +SHPTreeDiskHandle SHPAPI_CALL + SHPOpenDiskTree( const char* pszQIXFilename, + SAHooks *psHooks ); + +void SHPAPI_CALL + SHPCloseDiskTree( SHPTreeDiskHandle hDiskTree ); + +int SHPAPI_CALL1(*) +SHPSearchDiskTreeEx( SHPTreeDiskHandle hDiskTree, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ); + +int SHPAPI_CALL + SHPWriteTreeLL(SHPTree *hTree, const char *pszFilename, SAHooks *psHooks ); + +/* -------------------------------------------------------------------- */ +/* SBN Search API */ +/* -------------------------------------------------------------------- */ + +typedef struct SBNSearchInfo* SBNSearchHandle; + +SBNSearchHandle SHPAPI_CALL + SBNOpenDiskTree( const char* pszSBNFilename, + SAHooks *psHooks ); + +void SHPAPI_CALL + SBNCloseDiskTree( SBNSearchHandle hSBN ); + +int SHPAPI_CALL1(*) +SBNSearchDiskTree( SBNSearchHandle hSBN, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ); + +int SHPAPI_CALL1(*) +SBNSearchDiskTreeInteger( SBNSearchHandle hSBN, + int bMinX, int bMinY, int bMaxX, int bMaxY, + int *pnShapeCount ); + +void SHPAPI_CALL SBNSearchFreeIds( int* panShapeId ); + /************************************************************************/ /* DBF Support. */ /************************************************************************/ -typedef struct +typedef struct { SAHooks sHooks; - SAFile fp; + SAFile fp; int nRecords; - int nRecordLength; - int nHeaderLength; - int nFields; - int *panFieldOffset; - int *panFieldSize; - int *panFieldDecimals; - char *pachFieldType; + int nRecordLength; /* Must fit on uint16 */ + int nHeaderLength; /* File header length (32) + field + descriptor length + spare space. + Must fit on uint16 */ + int nFields; + int *panFieldOffset; + int *panFieldSize; + int *panFieldDecimals; + char *pachFieldType; - char *pszHeader; + char *pszHeader; /* Field descriptors */ - int nCurrentRecord; - int bCurrentRecordModified; - char *pszCurrentRecord; + int nCurrentRecord; + int bCurrentRecordModified; + char *pszCurrentRecord; int nWorkFieldLength; char *pszWorkField; - - int bNoHeader; - int bUpdated; - double dfDoubleField; + int bNoHeader; + int bUpdated; + + union + { + double dfDoubleField; + int nIntField; + } fieldValue; int iLanguageDriver; char *pszCodePage; + + int nUpdateYearSince1900; /* 0-255 */ + int nUpdateMonth; /* 1-12 */ + int nUpdateDay; /* 1-31 */ + + int bWriteEndOfFileChar; /* defaults to TRUE */ } DBFInfo; typedef DBFInfo * DBFHandle; @@ -514,8 +634,14 @@ typedef enum { FTInvalid } DBFFieldType; -#define XBASE_FLDHDR_SZ 32 - +/* Field descriptor/header size */ +#define XBASE_FLDHDR_SZ 32 +/* Shapelib read up to 11 characters, even if only 10 should normally be used */ +#define XBASE_FLDNAME_LEN_READ 11 +/* On writing, we limit to 10 characters */ +#define XBASE_FLDNAME_LEN_WRITE 10 +/* Normally only 254 characters should be used. We tolerate 255 historically */ +#define XBASE_FLD_MAX_WIDTH 255 DBFHandle SHPAPI_CALL DBFOpen( const char * pszDBFFile, const char * pszAccess ); @@ -529,41 +655,48 @@ DBFHandle SHPAPI_CALL DBFHandle SHPAPI_CALL DBFCreateLL( const char * pszDBFFile, const char * pszCodePage, SAHooks *psHooks ); -int SHPAPI_CALL +int SHPAPI_CALL DBFGetFieldCount( DBFHandle psDBF ); -int SHPAPI_CALL +int SHPAPI_CALL DBFGetRecordCount( DBFHandle psDBF ); -int SHPAPI_CALL +int SHPAPI_CALL DBFAddField( DBFHandle hDBF, const char * pszFieldName, DBFFieldType eType, int nWidth, int nDecimals ); -int SHPAPI_CALL +int SHPAPI_CALL DBFAddNativeFieldType( DBFHandle hDBF, const char * pszFieldName, char chType, int nWidth, int nDecimals ); -int SHPAPI_CALL +int SHPAPI_CALL DBFDeleteField( DBFHandle hDBF, int iField ); +int SHPAPI_CALL + DBFReorderFields( DBFHandle psDBF, int* panMap ); + +int SHPAPI_CALL + DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, + char chType, int nWidth, int nDecimals ); + DBFFieldType SHPAPI_CALL - DBFGetFieldInfo( DBFHandle psDBF, int iField, + DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, int * pnWidth, int * pnDecimals ); int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName); -int SHPAPI_CALL +int SHPAPI_CALL DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); -double SHPAPI_CALL +double SHPAPI_CALL DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); const char SHPAPI_CALL1(*) DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); const char SHPAPI_CALL1(*) DBFReadLogicalAttribute( DBFHandle hDBF, int iShape, int iField ); -int SHPAPI_CALL +int SHPAPI_CALL DBFIsAttributeNULL( DBFHandle hDBF, int iShape, int iField ); int SHPAPI_CALL - DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, + DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, int nFieldValue ); int SHPAPI_CALL DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, @@ -576,7 +709,7 @@ int SHPAPI_CALL int SHPAPI_CALL DBFWriteLogicalAttribute( DBFHandle hDBF, int iShape, int iField, - const char lFieldValue); + const char lFieldValue); int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, void * pValue ); @@ -586,22 +719,27 @@ int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ); int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ); -int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, int bIsDeleted ); DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ); - -void SHPAPI_CALL + +void SHPAPI_CALL DBFClose( DBFHandle hDBF ); void SHPAPI_CALL DBFUpdateHeader( DBFHandle hDBF ); -char SHPAPI_CALL +char SHPAPI_CALL DBFGetNativeFieldType( DBFHandle hDBF, int iField ); const char SHPAPI_CALL1(*) DBFGetCodePage(DBFHandle psDBF ); +void SHPAPI_CALL + DBFSetLastModifiedDate( DBFHandle psDBF, int nYYSince1900, int nMM, int nDD ); + +void SHPAPI_CALL DBFSetWriteEndOfFileChar( DBFHandle psDBF, int bWriteFlag ); + #ifdef __cplusplus } #endif diff --git a/navit/support/shapefile/shpopen.c b/navit/support/shapefile/shpopen.c index da5365281..2ace6843b 100644 --- a/navit/support/shapefile/shpopen.c +++ b/navit/support/shapefile/shpopen.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: shpopen.c,v 1.59 2008/03/14 05:25:31 fwarmerdam Exp $ + * $Id: shpopen.c,v 1.75 2016-12-05 12:44:05 erouault Exp $ * * Project: Shapelib * Purpose: Implementation of core Shapefile read/write functions. @@ -7,13 +7,14 @@ * ****************************************************************************** * Copyright (c) 1999, 2001, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault <even dot rouault at mines-paris dot org> * * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * or at the option of the licensee under the LGPL (see COPYING). This * option is discussed in more detail in shapelib.html. * * -- - * + * * 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 @@ -34,7 +35,71 @@ ****************************************************************************** * * $Log: shpopen.c,v $ - * Revision 1.59 2008/03/14 05:25:31 fwarmerdam + * Revision 1.75 2016-12-05 12:44:05 erouault + * * Major overhaul of Makefile build system to use autoconf/automake. + * + * * Warning fixes in contrib/ + * + * Revision 1.74 2016-12-04 15:30:15 erouault + * * shpopen.c, dbfopen.c, shptree.c, shapefil.h: resync with + * GDAL Shapefile driver. Mostly cleanups. SHPObject and DBFInfo + * structures extended with new members. New functions: + * DBFSetLastModifiedDate, SHPOpenLLEx, SHPRestoreSHX, + * SHPSetFastModeReadObject + * + * * sbnsearch.c: new file to implement original ESRI .sbn spatial + * index reading. (no write support). New functions: + * SBNOpenDiskTree, SBNCloseDiskTree, SBNSearchDiskTree, + * SBNSearchDiskTreeInteger, SBNSearchFreeIds + * + * * Makefile, makefile.vc, CMakeLists.txt, shapelib.def: updates + * with new file and symbols. + * + * * commit: helper script to cvs commit + * + * Revision 1.73 2012-01-24 22:33:01 fwarmerdam + * fix memory leak on failure to open .shp (gdal #4410) + * + * Revision 1.72 2011-12-11 22:45:28 fwarmerdam + * fix failure return from SHPOpenLL. + * + * Revision 1.71 2011-09-15 03:33:58 fwarmerdam + * fix missing cast (#2344) + * + * Revision 1.70 2011-07-24 05:59:25 fwarmerdam + * minimize use of CPLError in favor of SAHooks.Error() + * + * Revision 1.69 2011-07-24 03:24:22 fwarmerdam + * fix memory leaks in error cases creating shapefiles (#2061) + * + * Revision 1.68 2010-08-27 23:42:52 fwarmerdam + * add SHPAPI_CALL attribute in code + * + * Revision 1.67 2010-07-01 08:15:48 fwarmerdam + * do not error out on an object with zero vertices + * + * Revision 1.66 2010-07-01 07:58:57 fwarmerdam + * minor cleanup of error handling + * + * Revision 1.65 2010-07-01 07:27:13 fwarmerdam + * white space formatting adjustments + * + * Revision 1.64 2010-01-28 11:34:34 fwarmerdam + * handle the shape file length limits more gracefully (#3236) + * + * Revision 1.63 2010-01-28 04:04:40 fwarmerdam + * improve numerical accuracy of SHPRewind() algs (gdal #3363) + * + * Revision 1.62 2010-01-17 05:34:13 fwarmerdam + * Remove asserts on x/y being null (#2148). + * + * Revision 1.61 2010-01-16 05:07:42 fwarmerdam + * allow 0/nulls in shpcreateobject (#2148) + * + * Revision 1.60 2009-09-17 20:50:02 bram + * on Win32, define snprintf as alias to _snprintf + * + * Revision 1.59 2008-03-14 05:25:31 fwarmerdam * Correct crash on buggy geometries (gdal #2218) * * Revision 1.58 2008/01/08 23:28:26 bram @@ -117,7 +182,7 @@ * move pabyRec into SHPInfo for thread safety * * Revision 1.33 2001/07/03 12:18:15 warmerda - * Improved cleanup if SHX not found, provied by Riccardo Cohen. + * Improved cleanup if SHX not found, provided by Riccardo Cohen. * * Revision 1.32 2001/06/22 01:58:07 warmerda * be more careful about establishing initial bounds in face of NULL shapes @@ -230,14 +295,16 @@ #include <stdlib.h> #include <string.h> #include <stdio.h> -#include <glib.h> +#include <errno.h> + +SHP_CVSID("$Id: shpopen.c,v 1.75 2016-12-05 12:44:05 erouault Exp $") typedef unsigned char uchar; #if UINT_MAX == 65535 -typedef long int32; +typedef unsigned long int32; #else -typedef int int32; +typedef unsigned int int32; #endif #ifndef FALSE @@ -251,8 +318,33 @@ typedef int int32; # define MAX(a,b) ((a>b) ? a : b) #endif -static int bBigEndian; +#ifndef USE_CPL +#if defined(_MSC_VER) +# if _MSC_VER < 1900 +# define snprintf _snprintf +# endif +#elif defined(WIN32) || defined(_WIN32) +# ifndef snprintf +# define snprintf _snprintf +# endif +#endif +#endif +#ifndef CPL_UNUSED +#if defined(__GNUC__) && __GNUC__ >= 4 +# define CPL_UNUSED __attribute((__unused__)) +#else +# define CPL_UNUSED +#endif +#endif + +#if defined(CPL_LSB) +#define bBigEndian FALSE +#elif defined(CPL_MSB) +#define bBigEndian TRUE +#else +static int bBigEndian; +#endif /************************************************************************/ /* SwapWord() */ @@ -297,15 +389,15 @@ static void * SfRealloc( void * pMem, int nNewSize ) /* contents of the index (.shx) file. */ /************************************************************************/ -void SHPWriteHeader( SHPHandle psSHP ) +void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP ) { - uchar abyHeader[100]; + uchar abyHeader[100] = { 0 }; int i; int32 i32; double dValue; int32 *panSHX; - + if (psSHP->fpSHX == NULL) { psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed"); @@ -315,8 +407,6 @@ void SHPWriteHeader( SHPHandle psSHP ) /* -------------------------------------------------------------------- */ /* Prepare header block for .shp file. */ /* -------------------------------------------------------------------- */ - for( i = 0; i < 100; i++ ) - abyHeader[i] = 0; abyHeader[2] = 0x27; /* magic cookie */ abyHeader[3] = 0x0a; @@ -324,11 +414,11 @@ void SHPWriteHeader( SHPHandle psSHP ) i32 = psSHP->nFileSize/2; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - + i32 = 1000; /* version */ ByteCopy( &i32, abyHeader+28, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+28 ); - + i32 = psSHP->nShapeType; /* shape type */ ByteCopy( &i32, abyHeader+32, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+32 ); @@ -368,10 +458,14 @@ void SHPWriteHeader( SHPHandle psSHP ) /* -------------------------------------------------------------------- */ /* Write .shp file header. */ /* -------------------------------------------------------------------- */ - if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0 + if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 ) { - psSHP->sHooks.Error( "Failure writing .shp header" ); + char szError[200]; + + snprintf( szError, sizeof(szError), + "Failure writing .shp header: %s", strerror(errno) ); + psSHP->sHooks.Error( szError ); return; } @@ -381,11 +475,16 @@ void SHPWriteHeader( SHPHandle psSHP ) i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - - if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0 + + if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 ) { - psSHP->sHooks.Error( "Failure writing .shx header" ); + char szError[200]; + + snprintf( szError, sizeof(szError), + "Failure writing .shx header: %s", strerror(errno) ); + psSHP->sHooks.Error( szError ); + return; } @@ -393,19 +492,28 @@ void SHPWriteHeader( SHPHandle psSHP ) /* Write out the .shx contents. */ /* -------------------------------------------------------------------- */ panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords); + if( panSHX == NULL ) + { + psSHP->sHooks.Error( "Failure allocatin panSHX" ); + return; + } for( i = 0; i < psSHP->nRecords; i++ ) { - panSHX[i*2 ] = psSHP->panRecOffset[i]/2; - panSHX[i*2+1] = psSHP->panRecSize[i]/2; - if( !bBigEndian ) SwapWord( 4, panSHX+i*2 ); - if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); + panSHX[i*2 ] = psSHP->panRecOffset[i]/2; + panSHX[i*2+1] = psSHP->panRecSize[i]/2; + if( !bBigEndian ) SwapWord( 4, panSHX+i*2 ); + if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); } - if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ) + if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ) != psSHP->nRecords ) { - psSHP->sHooks.Error( "Failure writing .shx contents" ); + char szError[200]; + + snprintf( szError, sizeof(szError), + "Failure writing .shx contents: %s", strerror(errno) ); + psSHP->sHooks.Error( szError ); } free( panSHX ); @@ -438,18 +546,20 @@ SHPOpen( const char * pszLayer, const char * pszAccess ) /* Open the .shp and .shx files based on the basename of the */ /* files or either file name. */ /************************************************************************/ - + SHPHandle SHPAPI_CALL SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) { - char *pszFullname, *pszBasename; - SHPHandle psSHP; - - uchar *pabyBuf; - int i; - double dValue; - + char *pszFullname, *pszBasename; + SHPHandle psSHP; + + uchar *pabyBuf; + int i; + double dValue; + int bLazySHXLoading = FALSE; + size_t nFullnameLen; + /* -------------------------------------------------------------------- */ /* Ensure the access string is one of the legal ones. We */ /* ensure the result string indicates binary to avoid common */ @@ -459,19 +569,24 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) || strcmp(pszAccess,"r+") == 0 ) pszAccess = "r+b"; else + { + bLazySHXLoading = strchr(pszAccess, 'l') != NULL; pszAccess = "rb"; - + } + /* -------------------------------------------------------------------- */ -/* Establish the byte order on this machine. */ +/* Establish the byte order on this machine. */ /* -------------------------------------------------------------------- */ +#if !defined(bBigEndian) i = 1; if( *((uchar *) &i) == 1 ) bBigEndian = FALSE; else bBigEndian = TRUE; +#endif /* -------------------------------------------------------------------- */ -/* Initialize the info structure. */ +/* Initialize the info structure. */ /* -------------------------------------------------------------------- */ psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1); @@ -479,60 +594,67 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) ); /* -------------------------------------------------------------------- */ -/* Compute the base (layer) name. If there is any extension */ -/* on the passed in filename we will strip it off. */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszLayer)+5); strcpy( pszBasename, pszLayer ); - for( i = strlen(pszBasename)-1; - i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' - && pszBasename[i] != '\\'; - i-- ) {} + for( i = (int)strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; /* -------------------------------------------------------------------- */ -/* Open the .shp and .shx files. Note that files pulled from */ -/* a PC to Unix with upper case filenames won't work! */ +/* Open the .shp and .shx files. Note that files pulled from */ +/* a PC to Unix with upper case filenames won't work! */ /* -------------------------------------------------------------------- */ - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.shp", pszBasename ) ; + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.shp", pszBasename ) ; psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); if( psSHP->fpSHP == NULL ) { - sprintf( pszFullname, "%s.SHP", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.SHP", pszBasename ); psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); } - + if( psSHP->fpSHP == NULL ) { -#ifdef USE_CPL - CPLError( CE_Failure, CPLE_OpenFailed, - "Unable to open %s.shp or %s.SHP.", + size_t nMessageLen = strlen(pszBasename)*2+256; + char *pszMessage = (char *) malloc(nMessageLen); + snprintf( pszMessage, nMessageLen, "Unable to open %s.shp or %s.SHP.", pszBasename, pszBasename ); -#endif + psHooks->Error( pszMessage ); + free( pszMessage ); + free( psSHP ); free( pszBasename ); free( pszFullname ); - return( NULL ); + + return NULL; } - sprintf( pszFullname, "%s.shx", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.shx", pszBasename ); psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); if( psSHP->fpSHX == NULL ) { - sprintf( pszFullname, "%s.SHX", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.SHX", pszBasename ); psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); } - + if( psSHP->fpSHX == NULL ) { -#ifdef USE_CPL - CPLError( CE_Failure, CPLE_OpenFailed, - "Unable to open %s.shx or %s.SHX.", + size_t nMessageLen = strlen(pszBasename)*2+256; + char *pszMessage = (char *) malloc(nMessageLen); + snprintf( pszMessage, nMessageLen, "Unable to open %s.shx or %s.SHX." + "Try --config SHAPE_RESTORE_SHX true to restore or create it", pszBasename, pszBasename ); -#endif + psHooks->Error( pszMessage ); + free( pszMessage ); + psSHP->sHooks.FClose( psSHP->fpSHP ); free( psSHP ); free( pszBasename ); @@ -544,54 +666,73 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) free( pszBasename ); /* -------------------------------------------------------------------- */ -/* Read the file size from the SHP file. */ +/* Read the file size from the SHP file. */ /* -------------------------------------------------------------------- */ pabyBuf = (uchar *) malloc(100); psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP ); - psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256 - + pabyBuf[25] * 256 * 256 - + pabyBuf[26] * 256 - + pabyBuf[27]) * 2; + psSHP->nFileSize = ((unsigned int)pabyBuf[24] * 256 * 256 * 256 + + (unsigned int)pabyBuf[25] * 256 * 256 + + (unsigned int)pabyBuf[26] * 256 + + (unsigned int)pabyBuf[27]); + if( psSHP->nFileSize < 0xFFFFFFFFU / 2 ) + psSHP->nFileSize *= 2; + else + psSHP->nFileSize = 0xFFFFFFFEU; /* -------------------------------------------------------------------- */ /* Read SHX file Header info */ /* -------------------------------------------------------------------- */ - if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1 - || pabyBuf[0] != 0 - || pabyBuf[1] != 0 - || pabyBuf[2] != 0x27 + if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1 + || pabyBuf[0] != 0 + || pabyBuf[1] != 0 + || pabyBuf[2] != 0x27 || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) { psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." ); - psSHP->sHooks.FClose( psSHP->fpSHP ); - psSHP->sHooks.FClose( psSHP->fpSHX ); - free( psSHP ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP ); - return( NULL ); + return( NULL ); } psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 - + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256; - psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8; + + pabyBuf[25] * 256 * 256 + (pabyBuf[24] & 0x7F) * 256 * 256 * 256; + psSHP->nRecords = (psSHP->nRecords - 50) / 4; psSHP->nShapeType = pabyBuf[32]; if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 ) { char szError[200]; - - sprintf( szError, + + snprintf( szError, sizeof(szError), "Record count in .shp header is %d, which seems\n" "unreasonable. Assuming header is corrupt.", - psSHP->nRecords ); - psSHP->sHooks.Error( szError ); - psSHP->sHooks.FClose( psSHP->fpSHP ); - psSHP->sHooks.FClose( psSHP->fpSHX ); - free( psSHP ); + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP ); free(pabyBuf); - return( NULL ); + return( NULL ); + } + + /* If a lot of records are advertized, check that the file is big enough */ + /* to hold them */ + if( psSHP->nRecords >= 1024 * 1024 ) + { + SAOffset nFileSize; + psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 2 ); + nFileSize = psSHP->sHooks.FTell( psSHP->fpSHX ); + if( nFileSize > 100 && + nFileSize/2 < (SAOffset)(psSHP->nRecords * 4 + 50) ) + { + psSHP->nRecords = (int)((nFileSize - 100) / 8); + } + psSHP->sHooks.FSeek( psSHP->fpSHX, 100, 0 ); } /* -------------------------------------------------------------------- */ @@ -613,15 +754,15 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) memcpy( &dValue, pabyBuf+60, 8 ); psSHP->adBoundsMax[1] = dValue; - if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ + if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ memcpy( &dValue, pabyBuf+68, 8 ); psSHP->adBoundsMin[2] = dValue; - + if( bBigEndian ) SwapWord( 8, pabyBuf+76 ); memcpy( &dValue, pabyBuf+76, 8 ); psSHP->adBoundsMax[2] = dValue; - - if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ + + if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ memcpy( &dValue, pabyBuf+84, 8 ); psSHP->adBoundsMin[3] = dValue; @@ -632,30 +773,33 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) free( pabyBuf ); /* -------------------------------------------------------------------- */ -/* Read the .shx file to get the offsets to each record in */ -/* the .shp file. */ +/* Read the .shx file to get the offsets to each record in */ +/* the .shp file. */ /* -------------------------------------------------------------------- */ psSHP->nMaxRecords = psSHP->nRecords; - psSHP->panRecOffset = - (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); - psSHP->panRecSize = - (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); - pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); + psSHP->panRecOffset = (unsigned int *) + malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + psSHP->panRecSize = (unsigned int *) + malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + if( bLazySHXLoading ) + pabyBuf = NULL; + else + pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); if (psSHP->panRecOffset == NULL || psSHP->panRecSize == NULL || - pabyBuf == NULL) + (!bLazySHXLoading && pabyBuf == NULL)) { char szError[200]; - sprintf(szError, + snprintf( szError, sizeof(szError), "Not enough memory to allocate requested memory (nRecords=%d).\n" - "Probably broken SHP file", + "Probably broken SHP file", psSHP->nRecords ); psSHP->sHooks.Error( szError ); - psSHP->sHooks.FClose( psSHP->fpSHP ); - psSHP->sHooks.FClose( psSHP->fpSHX ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); if (psSHP->panRecOffset) free( psSHP->panRecOffset ); if (psSHP->panRecSize) free( psSHP->panRecSize ); if (pabyBuf) free( pabyBuf ); @@ -663,27 +807,34 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) return( NULL ); } - if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ) - != psSHP->nRecords ) + if( bLazySHXLoading ) + { + memset(psSHP->panRecOffset, 0, sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + memset(psSHP->panRecSize, 0, sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + return( psSHP ); + } + + if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ) + != psSHP->nRecords ) { char szError[200]; - sprintf( szError, - "Failed to read all values for %d records in .shx file.", - psSHP->nRecords ); + snprintf( szError, sizeof(szError), + "Failed to read all values for %d records in .shx file: %s.", + psSHP->nRecords, strerror(errno) ); psSHP->sHooks.Error( szError ); /* SHX is short or unreadable for some reason. */ - psSHP->sHooks.FClose( psSHP->fpSHP ); - psSHP->sHooks.FClose( psSHP->fpSHX ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); free( psSHP->panRecOffset ); free( psSHP->panRecSize ); free( pabyBuf ); - free( psSHP ); + free( psSHP ); - return( NULL ); + return( NULL ); } - + /* In read-only mode, we can close the SHX now */ if (strcmp(pszAccess, "rb") == 0) { @@ -693,16 +844,38 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) for( i = 0; i < psSHP->nRecords; i++ ) { - int32 nOffset, nLength; + unsigned int nOffset, nLength; + + memcpy( &nOffset, pabyBuf + i * 8, 4 ); + if( !bBigEndian ) SwapWord( 4, &nOffset ); - memcpy( &nOffset, pabyBuf + i * 8, 4 ); - if( !bBigEndian ) SwapWord( 4, &nOffset ); + memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); + if( !bBigEndian ) SwapWord( 4, &nLength ); - memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); - if( !bBigEndian ) SwapWord( 4, &nLength ); + if( nOffset > (unsigned int)INT_MAX ) + { + char str[128]; + snprintf( str, sizeof(str), + "Invalid offset for entity %d", i); - psSHP->panRecOffset[i] = nOffset*2; - psSHP->panRecSize[i] = nLength*2; + psSHP->sHooks.Error( str ); + SHPClose(psSHP); + free( pabyBuf ); + return NULL; + } + if( nLength > (unsigned int)(INT_MAX / 2 - 4) ) + { + char str[128]; + snprintf( str, sizeof(str), + "Invalid length for entity %d", i); + + psSHP->sHooks.Error( str ); + SHPClose(psSHP); + free( pabyBuf ); + return NULL; + } + psSHP->panRecOffset[i] = nOffset*2; + psSHP->panRecSize[i] = nLength*2; } free( pabyBuf ); @@ -710,6 +883,231 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) } /************************************************************************/ +/* SHPOpenLLEx() */ +/* */ +/* Open the .shp and .shx files based on the basename of the */ +/* files or either file name. It generally invokes SHPRestoreSHX() */ +/* in case when bRestoreSHX equals true. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPOpenLLEx( const char * pszLayer, const char * pszAccess, SAHooks *psHooks, + int bRestoreSHX ) + +{ + if ( !bRestoreSHX ) return SHPOpenLL ( pszLayer, pszAccess, psHooks ); + else + { + if ( SHPRestoreSHX ( pszLayer, pszAccess, psHooks ) ) + { + return SHPOpenLL ( pszLayer, pszAccess, psHooks ); + } + } + + return( NULL ); +} + +/************************************************************************/ +/* SHPRestoreSHX() */ +/* */ +/* Restore .SHX file using associated .SHP file. */ +/* */ +/************************************************************************/ + +int SHPAPI_CALL +SHPRestoreSHX ( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) + +{ + char *pszFullname, *pszBasename; + SAFile fpSHP, fpSHX; + + + uchar *pabyBuf; + int i; + size_t nFullnameLen; + unsigned int nSHPFilesize; + + size_t nMessageLen; + char *pszMessage; + + unsigned int nCurrentRecordOffset = 0; + unsigned int nCurrentSHPOffset = 100; + size_t nRealSHXContentSize = 100; + + const char pszSHXAccess[] = "w+b"; + char *pabySHXHeader; + char abyReadedRecord[8]; + unsigned int niRecord = 0; + unsigned int nRecordLength = 0; + unsigned int nRecordOffset = 50; + +/* -------------------------------------------------------------------- */ +/* Ensure the access string is one of the legal ones. We */ +/* ensure the result string indicates binary to avoid common */ +/* problems on Windows. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 + || strcmp(pszAccess,"r+") == 0 ) + pszAccess = "r+b"; + else + { + pszAccess = "rb"; + } + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ +#if !defined(bBigEndian) + i = 1; + if( *((uchar *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; +#endif + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszLayer)+5); + strcpy( pszBasename, pszLayer ); + for( i = (int)strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Open the .shp file. Note that files pulled from */ +/* a PC to Unix with upper case filenames won't work! */ +/* -------------------------------------------------------------------- */ + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.shp", pszBasename ) ; + fpSHP = psHooks->FOpen(pszFullname, pszAccess ); + if( fpSHP == NULL ) + { + snprintf( pszFullname, nFullnameLen, "%s.SHP", pszBasename ); + fpSHP = psHooks->FOpen(pszFullname, pszAccess ); + } + + if( fpSHP == NULL ) + { + nMessageLen = strlen(pszBasename)*2+256; + pszMessage = (char *) malloc(nMessageLen); + snprintf( pszMessage, nMessageLen, "Unable to open %s.shp or %s.SHP.", + pszBasename, pszBasename ); + psHooks->Error( pszMessage ); + free( pszMessage ); + + free( pszBasename ); + free( pszFullname ); + + return( 0 ); + } + +/* -------------------------------------------------------------------- */ +/* Read the file size from the SHP file. */ +/* -------------------------------------------------------------------- */ + pabyBuf = (uchar *) malloc(100); + psHooks->FRead( pabyBuf, 100, 1, fpSHP ); + + nSHPFilesize = ((unsigned int)pabyBuf[24] * 256 * 256 * 256 + + (unsigned int)pabyBuf[25] * 256 * 256 + + (unsigned int)pabyBuf[26] * 256 + + (unsigned int)pabyBuf[27]); + if( nSHPFilesize < 0xFFFFFFFFU / 2 ) + nSHPFilesize *= 2; + else + nSHPFilesize = 0xFFFFFFFEU; + + snprintf( pszFullname, nFullnameLen, "%s.shx", pszBasename ); + fpSHX = psHooks->FOpen( pszFullname, pszSHXAccess ); + + if( fpSHX == NULL ) + { + nMessageLen = strlen( pszBasename ) * 2 + 256; + pszMessage = (char *) malloc( nMessageLen ); + snprintf( pszMessage, nMessageLen, "Error opening file %s.shx for writing", + pszBasename ); + psHooks->Error( pszMessage ); + free( pszMessage ); + + psHooks->FClose( fpSHX ); + + free( pabyBuf ); + free( pszBasename ); + free( pszFullname ); + + return( 0 ); + } + +/* -------------------------------------------------------------------- */ +/* Open SHX and create it using SHP file content. */ +/* -------------------------------------------------------------------- */ + psHooks->FSeek( fpSHP, 100, 0 ); + pabySHXHeader = (char *) malloc ( 100 ); + memcpy( pabySHXHeader, pabyBuf, 100 ); + psHooks->FWrite( pabySHXHeader, 100, 1, fpSHX ); + + while( nCurrentSHPOffset < nSHPFilesize ) + { + if( psHooks->FRead( &niRecord, 4, 1, fpSHP ) == 1 && + psHooks->FRead( &nRecordLength, 4, 1, fpSHP ) == 1) + { + if( !bBigEndian ) SwapWord( 4, &nRecordOffset ); + memcpy( abyReadedRecord, &nRecordOffset, 4 ); + memcpy( abyReadedRecord + 4, &nRecordLength, 4 ); + + psHooks->FWrite( abyReadedRecord, 8, 1, fpSHX ); + + if ( !bBigEndian ) SwapWord( 4, &nRecordOffset ); + if ( !bBigEndian ) SwapWord( 4, &nRecordLength ); + nRecordOffset += nRecordLength + 4; + nCurrentRecordOffset += 8; + nCurrentSHPOffset += 8 + nRecordLength * 2; + + psHooks->FSeek( fpSHP, nCurrentSHPOffset, 0 ); + nRealSHXContentSize += 8; + } + else + { + nMessageLen = strlen( pszBasename ) * 2 + 256; + pszMessage = (char *) malloc( nMessageLen ); + snprintf( pszMessage, nMessageLen, "Error parsing .shp to restore .shx" ); + psHooks->Error( pszMessage ); + free( pszMessage ); + + psHooks->FClose( fpSHX ); + psHooks->FClose( fpSHP ); + + free( pabySHXHeader ); + free( pszBasename ); + free( pszFullname ); + + return( 0 ); + } + } + + nRealSHXContentSize /= 2; // Bytes counted -> WORDs + if( !bBigEndian ) SwapWord( 4, &nRealSHXContentSize ); + psHooks->FSeek( fpSHX, 24, 0 ); + psHooks->FWrite( &nRealSHXContentSize, 4, 1, fpSHX ); + + psHooks->FClose( fpSHP ); + psHooks->FClose( fpSHX ); + + free ( pabyBuf ); + free ( pszFullname ); + free ( pszBasename ); + free ( pabySHXHeader ); + + return( 1 ); +} + +/************************************************************************/ /* SHPClose() */ /* */ /* Close the .shp and .shx files. */ @@ -742,11 +1140,42 @@ SHPClose(SHPHandle psSHP ) { free( psSHP->pabyRec ); } - + + if( psSHP->pabyObjectBuf != NULL ) + { + free( psSHP->pabyObjectBuf ); + } + if( psSHP->psCachedObject != NULL ) + { + free( psSHP->psCachedObject ); + } + free( psSHP ); } /************************************************************************/ +/* SHPSetFastModeReadObject() */ +/************************************************************************/ + +/* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */ +/* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */ +/* The SHPObject padfZ and padfM members may be NULL depending on the geometry */ +/* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */ +void SHPAPI_CALL SHPSetFastModeReadObject( SHPHandle hSHP, int bFastMode ) +{ + if( bFastMode ) + { + if( hSHP->psCachedObject == NULL ) + { + hSHP->psCachedObject = (SHPObject*) calloc(1, sizeof(SHPObject)); + assert( hSHP->psCachedObject != NULL ); + } + } + + hSHP->bFastModeReadObject = bFastMode; +} + +/************************************************************************/ /* SHPGetInfo() */ /* */ /* Fetch general information about the shape file. */ @@ -761,7 +1190,7 @@ SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, if( psSHP == NULL ) return; - + if( pnEntities != NULL ) *pnEntities = psSHP->nRecords; @@ -806,21 +1235,24 @@ SHPHandle SHPAPI_CALL SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) { - char *pszBasename, *pszFullname; + char *pszBasename = NULL, *pszFullname = NULL; int i; - SAFile fpSHP, fpSHX; + SAFile fpSHP = NULL, fpSHX = NULL; uchar abyHeader[100]; int32 i32; double dValue; - + size_t nFullnameLen; + /* -------------------------------------------------------------------- */ /* Establish the byte order on this system. */ /* -------------------------------------------------------------------- */ +#if !defined(bBigEndian) i = 1; if( *((uchar *) &i) == 1 ) bBigEndian = FALSE; else bBigEndian = TRUE; +#endif /* -------------------------------------------------------------------- */ /* Compute the base (layer) name. If there is any extension */ @@ -828,10 +1260,10 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszLayer)+5); strcpy( pszBasename, pszLayer ); - for( i = strlen(pszBasename)-1; - i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' - && pszBasename[i] != '\\'; - i-- ) {} + for( i = (int)strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; @@ -839,31 +1271,31 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ /* Open the two files so we can write their headers. */ /* -------------------------------------------------------------------- */ - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.shp", pszBasename ); + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.shp", pszBasename ); fpSHP = psHooks->FOpen(pszFullname, "wb" ); if( fpSHP == NULL ) { psHooks->Error( "Failed to create file .shp file." ); - return( NULL ); + goto error; } - sprintf( pszFullname, "%s.shx", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.shx", pszBasename ); fpSHX = psHooks->FOpen(pszFullname, "wb" ); if( fpSHX == NULL ) { psHooks->Error( "Failed to create file .shx file." ); - return( NULL ); + goto error; } - free( pszFullname ); - free( pszBasename ); + free( pszFullname ); pszFullname = NULL; + free( pszBasename ); pszBasename = NULL; /* -------------------------------------------------------------------- */ /* Prepare header block for .shp file. */ /* -------------------------------------------------------------------- */ - for( i = 0; i < 100; i++ ) - abyHeader[i] = 0; + memset( abyHeader, 0, sizeof(abyHeader) ); abyHeader[2] = 0x27; /* magic cookie */ abyHeader[3] = 0x0a; @@ -871,11 +1303,11 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) i32 = 50; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - + i32 = 1000; /* version */ ByteCopy( &i32, abyHeader+28, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+28 ); - + i32 = nShapeType; /* shape type */ ByteCopy( &i32, abyHeader+32, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+32 ); @@ -891,8 +1323,13 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 ) { - psHooks->Error( "Failed to write .shp header." ); - return NULL; + char szError[200]; + + snprintf( szError, sizeof(szError), + "Failed to write .shp header: %s", strerror(errno) ); + psHooks->Error( szError ); + + goto error; } /* -------------------------------------------------------------------- */ @@ -901,11 +1338,16 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) i32 = 50; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - + if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 ) { - psHooks->Error( "Failed to write .shx header." ); - return NULL; + char szError[200]; + + snprintf( szError, sizeof(szError), + "Failure writing .shx header: %s", strerror(errno) ); + psHooks->Error( szError ); + + goto error; } /* -------------------------------------------------------------------- */ @@ -915,6 +1357,13 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) psHooks->FClose( fpSHX ); return( SHPOpenLL( pszLayer, "r+b", psHooks ) ); + +error: + if (pszFullname) free(pszFullname); + if (pszBasename) free(pszBasename); + if (fpSHP) psHooks->FClose( fpSHP ); + if (fpSHX) psHooks->FClose( fpSHX ); + return NULL; } /************************************************************************/ @@ -953,7 +1402,7 @@ SHPComputeExtents( SHPObject * psObject ) { int i; - + /* -------------------------------------------------------------------- */ /* Build extents for this object. */ /* -------------------------------------------------------------------- */ @@ -964,7 +1413,7 @@ SHPComputeExtents( SHPObject * psObject ) psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; } - + for( i = 0; i < psObject->nVertices; i++ ) { psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); @@ -1039,16 +1488,17 @@ SHPCreateObject( int nSHPType, int nShapeId, int nParts, psObject->nParts = MAX(1,nParts); psObject->panPartStart = (int *) - malloc(sizeof(int) * psObject->nParts); + calloc(sizeof(int), psObject->nParts); psObject->panPartType = (int *) malloc(sizeof(int) * psObject->nParts); psObject->panPartStart[0] = 0; psObject->panPartType[0] = SHPP_RING; - + for( i = 0; i < nParts; i++ ) { - psObject->panPartStart[i] = panPartStart[i]; + if( panPartStart != NULL ) + psObject->panPartStart[i] = panPartStart[i]; if( panPartType != NULL ) psObject->panPartType[i] = panPartType[i]; @@ -1061,8 +1511,7 @@ SHPCreateObject( int nSHPType, int nShapeId, int nParts, } /* -------------------------------------------------------------------- */ -/* Capture vertices. Note that Z and M are optional, but X and */ -/* Y are not. */ +/* Capture vertices. Note that X, Y, Z and M are optional. */ /* -------------------------------------------------------------------- */ if( nVertices > 0 ) { @@ -1071,13 +1520,12 @@ SHPCreateObject( int nSHPType, int nShapeId, int nParts, psObject->padfZ = (double *) calloc(sizeof(double),nVertices); psObject->padfM = (double *) calloc(sizeof(double),nVertices); - assert( padfX != NULL ); - assert( padfY != NULL ); - for( i = 0; i < nVertices; i++ ) { - psObject->padfX[i] = padfX[i]; - psObject->padfY[i] = padfY[i]; + if( padfX != NULL ) + psObject->padfX[i] = padfX[i]; + if( padfY != NULL ) + psObject->padfY[i] = padfY[i]; if( padfZ != NULL && bHasZ ) psObject->padfZ[i] = padfZ[i]; if( padfM != NULL && bHasM ) @@ -1112,7 +1560,7 @@ SHPCreateSimpleObject( int nSHPType, int nVertices, return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, nVertices, padfX, padfY, padfZ, NULL ) ); } - + /************************************************************************/ /* SHPWriteObject() */ /* */ @@ -1122,11 +1570,13 @@ SHPCreateSimpleObject( int nSHPType, int nVertices, int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) - + { - int nRecordOffset, i, nRecordSize=0; + unsigned int nRecordOffset, nRecordSize=0; + int i; uchar *pabyRec; int32 i32; + int bExtendFile = FALSE; psSHP->bUpdated = TRUE; @@ -1134,7 +1584,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* Ensure that shape object matches the type of the file it is */ /* being written to. */ /* -------------------------------------------------------------------- */ - assert( psObject->nSHPType == psSHP->nShapeType + assert( psObject->nSHPType == psSHP->nShapeType || psObject->nSHPType == SHPT_NULL ); /* -------------------------------------------------------------------- */ @@ -1142,7 +1592,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* assertion, or if they are disabled, set the shapeid to -1 */ /* for appends. */ /* -------------------------------------------------------------------- */ - assert( nShapeId == -1 + assert( nShapeId == -1 || (nShapeId >= 0 && nShapeId < psSHP->nRecords) ); if( nShapeId != -1 && nShapeId >= psSHP->nRecords ) @@ -1153,57 +1603,69 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* -------------------------------------------------------------------- */ if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords ) { - psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100); - - psSHP->panRecOffset = (int *) - SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords ); - psSHP->panRecSize = (int *) - SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords ); + int nNewMaxRecords = psSHP->nMaxRecords + psSHP->nMaxRecords / 3 + 100; + unsigned int* panRecOffsetNew; + unsigned int* panRecSizeNew; + + panRecOffsetNew = (unsigned int *) + SfRealloc(psSHP->panRecOffset,sizeof(unsigned int) * nNewMaxRecords ); + if( panRecOffsetNew == NULL ) + return -1; + psSHP->panRecOffset = panRecOffsetNew; + + panRecSizeNew = (unsigned int *) + SfRealloc(psSHP->panRecSize,sizeof(unsigned int) * nNewMaxRecords ); + if( panRecSizeNew == NULL ) + return -1; + psSHP->panRecSize = panRecSizeNew; + + psSHP->nMaxRecords = nNewMaxRecords; } /* -------------------------------------------------------------------- */ /* Initialize record. */ /* -------------------------------------------------------------------- */ - pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) - + psObject->nParts * 8 + 128); - + pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) + + psObject->nParts * 8 + 128); + if( pabyRec == NULL ) + return -1; + /* -------------------------------------------------------------------- */ /* Extract vertices for a Polygon or Arc. */ /* -------------------------------------------------------------------- */ if( psObject->nSHPType == SHPT_POLYGON || psObject->nSHPType == SHPT_POLYGONZ || psObject->nSHPType == SHPT_POLYGONM - || psObject->nSHPType == SHPT_ARC + || psObject->nSHPType == SHPT_ARC || psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_ARCM || psObject->nSHPType == SHPT_MULTIPATCH ) { - int32 nPoints, nParts; - int i; + int32 nPoints, nParts; - nPoints = psObject->nVertices; - nParts = psObject->nParts; + nPoints = psObject->nVertices; + nParts = psObject->nParts; - _SHPSetBounds( pabyRec + 12, psObject ); + _SHPSetBounds( pabyRec + 12, psObject ); - if( bBigEndian ) SwapWord( 4, &nPoints ); - if( bBigEndian ) SwapWord( 4, &nParts ); + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); - ByteCopy( &nPoints, pabyRec + 40 + 8, 4 ); - ByteCopy( &nParts, pabyRec + 36 + 8, 4 ); + ByteCopy( &nPoints, pabyRec + 40 + 8, 4 ); + ByteCopy( &nParts, pabyRec + 36 + 8, 4 ); nRecordSize = 52; /* * Write part start positions. */ - ByteCopy( psObject->panPartStart, pabyRec + 44 + 8, + ByteCopy( psObject->panPartStart, pabyRec + 44 + 8, 4 * psObject->nParts ); - for( i = 0; i < psObject->nParts; i++ ) - { - if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i ); + for( i = 0; i < psObject->nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i ); nRecordSize += 4; - } + } /* * Write multipatch part types if needed. @@ -1222,19 +1684,19 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* * Write the (x,y) vertex values. */ - for( i = 0; i < psObject->nVertices; i++ ) - { - ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 ); - ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 ); + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 ); - if( bBigEndian ) + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - - if( bBigEndian ) + + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize + 8 ); nRecordSize += 2 * 8; - } + } /* * Write the Z coordinates (if any). @@ -1246,7 +1708,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; - + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; @@ -1264,17 +1726,17 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) */ if( psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_POLYGONM - || psObject->nSHPType == SHPT_ARCM -#ifndef DISABLE_MULTIPATCH_MEASURE - || psObject->nSHPType == SHPT_MULTIPATCH -#endif - || psObject->nSHPType == SHPT_POLYGONZ - || psObject->nSHPType == SHPT_ARCZ) ) + || psObject->nSHPType == SHPT_ARCM +#ifndef DISABLE_MULTIPATCH_MEASURE + || psObject->nSHPType == SHPT_MULTIPATCH +#endif + || psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_ARCZ) ) { ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; - + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; @@ -1295,26 +1757,25 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) || psObject->nSHPType == SHPT_MULTIPOINTZ || psObject->nSHPType == SHPT_MULTIPOINTM ) { - int32 nPoints; - int i; + int32 nPoints; - nPoints = psObject->nVertices; + nPoints = psObject->nVertices; _SHPSetBounds( pabyRec + 12, psObject ); - if( bBigEndian ) SwapWord( 4, &nPoints ); - ByteCopy( &nPoints, pabyRec + 44, 4 ); - - for( i = 0; i < psObject->nVertices; i++ ) - { - ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); - ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 ); + if( bBigEndian ) SwapWord( 4, &nPoints ); + ByteCopy( &nPoints, pabyRec + 44, 4 ); - if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 ); - if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 ); - } + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 ); + } - nRecordSize = 48 + 16 * psObject->nVertices; + nRecordSize = 48 + 16 * psObject->nVertices; if( psObject->nSHPType == SHPT_MULTIPOINTZ ) { @@ -1325,7 +1786,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; - + for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); @@ -1336,7 +1797,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) if( psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_MULTIPOINTZ - || psObject->nSHPType == SHPT_MULTIPOINTM) ) + || psObject->nSHPType == SHPT_MULTIPOINTM) ) { ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); @@ -1345,7 +1806,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; - + for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); @@ -1362,24 +1823,24 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) || psObject->nSHPType == SHPT_POINTZ || psObject->nSHPType == SHPT_POINTM ) { - ByteCopy( psObject->padfX, pabyRec + 12, 8 ); - ByteCopy( psObject->padfY, pabyRec + 20, 8 ); + ByteCopy( psObject->padfX, pabyRec + 12, 8 ); + ByteCopy( psObject->padfY, pabyRec + 20, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + 12 ); - if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 12 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); nRecordSize = 28; - + if( psObject->nSHPType == SHPT_POINTZ ) { ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; } - + if( psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_POINTZ - || psObject->nSHPType == SHPT_POINTM) ) + || psObject->nSHPType == SHPT_POINTM) ) { ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); @@ -1408,23 +1869,30 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* -------------------------------------------------------------------- */ if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) { - if( nShapeId == -1 ) - nShapeId = psSHP->nRecords++; + unsigned int nExpectedSize = psSHP->nFileSize + nRecordSize; + if( nExpectedSize < psSHP->nFileSize ) // due to unsigned int overflow + { + char str[128]; + snprintf( str, sizeof(str), "Failed to write shape object. " + "File size cannot reach %u + %u.", + psSHP->nFileSize, nRecordSize ); + psSHP->sHooks.Error( str ); + free( pabyRec ); + return -1; + } - psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize; - psSHP->panRecSize[nShapeId] = nRecordSize-8; - psSHP->nFileSize += nRecordSize; + bExtendFile = TRUE; + nRecordOffset = psSHP->nFileSize; } else { nRecordOffset = psSHP->panRecOffset[nShapeId]; - psSHP->panRecSize[nShapeId] = nRecordSize-8; } - + /* -------------------------------------------------------------------- */ /* Set the shape type, record number, and record size. */ /* -------------------------------------------------------------------- */ - i32 = nShapeId+1; /* record # */ + i32 = (nShapeId < 0) ? psSHP->nRecords+1 : nShapeId+1; /* record # */ if( !bBigEndian ) SwapWord( 4, &i32 ); ByteCopy( &i32, pabyRec, 4 ); @@ -1439,16 +1907,43 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* -------------------------------------------------------------------- */ /* Write out record. */ /* -------------------------------------------------------------------- */ - if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 - || psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 ) + if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 ) { - psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() or fwrite() writing object to .shp file." ); + char szError[200]; + + snprintf( szError, sizeof(szError), + "Error in psSHP->sHooks.FSeek() while writing object to .shp file: %s", + strerror(errno) ); + psSHP->sHooks.Error( szError ); + + free( pabyRec ); + return -1; + } + if( psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 ) + { + char szError[200]; + + snprintf( szError, sizeof(szError), + "Error in psSHP->sHooks.FWrite() while writing object of %u bytes to .shp file: %s", + nRecordSize, strerror(errno) ); + psSHP->sHooks.Error( szError ); + free( pabyRec ); return -1; } - + free( pabyRec ); + if( bExtendFile ) + { + if( nShapeId == -1 ) + nShapeId = psSHP->nRecords++; + + psSHP->panRecOffset[nShapeId] = psSHP->nFileSize; + psSHP->nFileSize += nRecordSize; + } + psSHP->panRecSize[nShapeId] = nRecordSize-8; + /* -------------------------------------------------------------------- */ /* Expand file wide bounds based on this shape. */ /* -------------------------------------------------------------------- */ @@ -1468,27 +1963,78 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) { psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; - psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0]; - psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0]; + psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ ? psObject->padfZ[0] : 0.0; + psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM ? psObject->padfM[0] : 0.0; } } for( i = 0; i < psObject->nVertices; i++ ) { - psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); - psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); - psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); - psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); - psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); - psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); - psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); - psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); + psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); + psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); + psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); + psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); + if( psObject->padfZ ) + { + psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); + psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); + } + if( psObject->padfM ) + { + psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); + psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); + } } return( nShapeId ); } /************************************************************************/ +/* SHPAllocBuffer() */ +/************************************************************************/ + +static void* SHPAllocBuffer(unsigned char** pBuffer, int nSize) +{ + unsigned char* pRet; + + if( pBuffer == NULL ) + return calloc(1, nSize); + + pRet = *pBuffer; + if( pRet == NULL ) + return NULL; + + (*pBuffer) += nSize; + return pRet; +} + +/************************************************************************/ +/* SHPReallocObjectBufIfNecessary() */ +/************************************************************************/ + +static unsigned char* SHPReallocObjectBufIfNecessary ( SHPHandle psSHP, + int nObjectBufSize ) +{ + unsigned char* pBuffer; + if( nObjectBufSize == 0 ) + { + nObjectBufSize = 4 * sizeof(double); + } + if( nObjectBufSize > psSHP->nObjectBufSize ) + { + pBuffer = (unsigned char*) realloc( psSHP->pabyObjectBuf, nObjectBufSize ); + if( pBuffer != NULL ) + { + psSHP->pabyObjectBuf = pBuffer; + psSHP->nObjectBufSize = nObjectBufSize; + } + } + else + pBuffer = psSHP->pabyObjectBuf; + return pBuffer; +} + +/************************************************************************/ /* SHPReadObject() */ /* */ /* Read the vertices, parts, and other non-attribute information */ @@ -1500,8 +2046,10 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) { int nEntitySize, nRequiredSize; - SHPObject *psShape; - char pszErrorMsg[128]; + SHPObject *psShape; + char szErrorMsg[128]; + int nSHPType; + int nBytesRead; /* -------------------------------------------------------------------- */ /* Validate the record/entity number. */ @@ -1510,28 +2058,105 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) return( NULL ); /* -------------------------------------------------------------------- */ +/* Read offset/length from SHX loading if necessary. */ +/* -------------------------------------------------------------------- */ + if( psSHP->panRecOffset[hEntity] == 0 && psSHP->fpSHX != NULL ) + { + unsigned int nOffset, nLength; + + if( psSHP->sHooks.FSeek( psSHP->fpSHX, 100 + 8 * hEntity, 0 ) != 0 || + psSHP->sHooks.FRead( &nOffset, 1, 4, psSHP->fpSHX ) != 4 || + psSHP->sHooks.FRead( &nLength, 1, 4, psSHP->fpSHX ) != 4 ) + { + char str[128]; + snprintf( str, sizeof(str), + "Error in fseek()/fread() reading object from .shx file at offset %d", + 100 + 8 * hEntity); + + psSHP->sHooks.Error( str ); + return NULL; + } + if( !bBigEndian ) SwapWord( 4, &nOffset ); + if( !bBigEndian ) SwapWord( 4, &nLength ); + + if( nOffset > (unsigned int)INT_MAX ) + { + char str[128]; + snprintf( str, sizeof(str), + "Invalid offset for entity %d", hEntity); + + psSHP->sHooks.Error( str ); + return NULL; + } + if( nLength > (unsigned int)(INT_MAX / 2 - 4) ) + { + char str[128]; + snprintf( str, sizeof(str), + "Invalid length for entity %d", hEntity); + + psSHP->sHooks.Error( str ); + return NULL; + } + + psSHP->panRecOffset[hEntity] = nOffset*2; + psSHP->panRecSize[hEntity] = nLength*2; + } + +/* -------------------------------------------------------------------- */ /* Ensure our record buffer is large enough. */ /* -------------------------------------------------------------------- */ nEntitySize = psSHP->panRecSize[hEntity]+8; if( nEntitySize > psSHP->nBufSize ) { - psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize); - if (psSHP->pabyRec == NULL) + uchar* pabyRecNew; + int nNewBufSize = nEntitySize; + if( nNewBufSize < INT_MAX - nNewBufSize / 3 ) + nNewBufSize += nNewBufSize / 3; + else + nNewBufSize = INT_MAX; + + /* Before allocating too much memory, check that the file is big enough */ + if( nEntitySize >= 10 * 1024 * 1024 && + (psSHP->panRecOffset[hEntity] >= psSHP->nFileSize || + (unsigned int)nEntitySize > psSHP->nFileSize - psSHP->panRecOffset[hEntity]) ) { - char szError[200]; + /* We do as is we didn't trust the file size in the header */ + SAOffset nFileSize; + psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 2 ); + nFileSize = psSHP->sHooks.FTell(psSHP->fpSHP); + if( nFileSize >= 0xFFFFFFFFU ) + psSHP->nFileSize = 0xFFFFFFFFU; + else + psSHP->nFileSize = (unsigned int)nFileSize; + + if( psSHP->panRecOffset[hEntity] >= psSHP->nFileSize || + (unsigned int)nEntitySize > psSHP->nFileSize - psSHP->panRecOffset[hEntity] ) + { + char str[128]; + snprintf( str, sizeof(str), + "Error in fread() reading object of size %d at offset %u from .shp file", + nEntitySize, psSHP->panRecOffset[hEntity] ); + + psSHP->sHooks.Error( str ); + return NULL; + } + } - /* Reallocate previous successfull size for following features */ - psSHP->pabyRec = malloc(psSHP->nBufSize); + pabyRecNew = (uchar *) SfRealloc(psSHP->pabyRec,nNewBufSize); + if (pabyRecNew == NULL) + { + char szError[200]; - sprintf( szError, - "Not enough memory to allocate requested memory (nBufSize=%d). " - "Probably broken SHP file", psSHP->nBufSize ); + snprintf( szError, sizeof(szError), + "Not enough memory to allocate requested memory (nNewBufSize=%d). " + "Probably broken SHP file", nNewBufSize); psSHP->sHooks.Error( szError ); return NULL; } - /* Only set new buffer size after successfull alloc */ - psSHP->nBufSize = nEntitySize; + /* Only set new buffer size after successful alloc */ + psSHP->pabyRec = pabyRecNew; + psSHP->nBufSize = nNewBufSize; } /* In case we were not able to reallocate the buffer on a previous step */ @@ -1543,37 +2168,96 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ /* Read the record. */ /* -------------------------------------------------------------------- */ - if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 - || psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1, - psSHP->fpSHP ) != 1 ) + if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 ) { /* * TODO - mloskot: Consider detailed diagnostics of shape file, * for example to detect if file is truncated. */ + char str[128]; + snprintf( str, sizeof(str), + "Error in fseek() reading object from .shp file at offset %u", + psSHP->panRecOffset[hEntity]); - psSHP->sHooks.Error( "Error in fseek() or fread() reading object from .shp file." ); + psSHP->sHooks.Error( str ); return NULL; } -/* -------------------------------------------------------------------- */ -/* Allocate and minimally initialize the object. */ -/* -------------------------------------------------------------------- */ - psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); - psShape->nShapeId = hEntity; - psShape->bMeasureIsUsed = FALSE; + nBytesRead = (int)psSHP->sHooks.FRead( psSHP->pabyRec, 1, nEntitySize, psSHP->fpSHP ); + + /* Special case for a shapefile whose .shx content length field is not equal */ + /* to the content length field of the .shp, which is a violation of "The */ + /* content length stored in the index record is the same as the value stored in the main */ + /* file record header." (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf, page 24) */ + /* Actually in that case the .shx content length is equal to the .shp content length + */ + /* 4 (16 bit words), representing the 8 bytes of the record header... */ + if( nBytesRead >= 8 && nBytesRead == nEntitySize - 8 ) + { + /* Do a sanity check */ + int nSHPContentLength; + memcpy( &nSHPContentLength, psSHP->pabyRec + 4, 4 ); + if( !bBigEndian ) SwapWord( 4, &(nSHPContentLength) ); + if( nSHPContentLength < 0 || + nSHPContentLength > INT_MAX / 2 - 4 || + 2 * nSHPContentLength + 8 != nBytesRead ) + { + char str[128]; + snprintf( str, sizeof(str), + "Sanity check failed when trying to recover from inconsistent .shx/.shp with shape %d", + hEntity ); + + psSHP->sHooks.Error( str ); + return NULL; + } + } + else if( nBytesRead != nEntitySize ) + { + /* + * TODO - mloskot: Consider detailed diagnostics of shape file, + * for example to detect if file is truncated. + */ + char str[128]; + snprintf( str, sizeof(str), + "Error in fread() reading object of size %d at offset %u from .shp file", + nEntitySize, psSHP->panRecOffset[hEntity] ); + + psSHP->sHooks.Error( str ); + return NULL; + } if ( 8 + 4 > nEntitySize ) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", - hEntity, nEntitySize); - psSHP->sHooks.Error( pszErrorMsg ); - SHPDestroyObject(psShape); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); return NULL; } - memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 ); + memcpy( &nSHPType, psSHP->pabyRec + 8, 4 ); + + if( bBigEndian ) SwapWord( 4, &(nSHPType) ); + +/* -------------------------------------------------------------------- */ +/* Allocate and minimally initialize the object. */ +/* -------------------------------------------------------------------- */ + if( psSHP->bFastModeReadObject ) + { + if( psSHP->psCachedObject->bFastModeReadObject ) + { + psSHP->sHooks.Error( "Invalid read pattern in fast read mode. " + "SHPDestroyObject() should be called." ); + return NULL; + } - if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) ); + psShape = psSHP->psCachedObject; + memset(psShape, 0, sizeof(SHPObject)); + } + else + psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); + psShape->nShapeId = hEntity; + psShape->nSHPType = nSHPType; + psShape->bMeasureIsUsed = FALSE; + psShape->bFastModeReadObject = psSHP->bFastModeReadObject; /* ==================================================================== */ /* Extract vertices for a Polygon or Arc. */ @@ -1585,14 +2269,17 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) || psShape->nSHPType == SHPT_ARCM || psShape->nSHPType == SHPT_MULTIPATCH ) { - int32 nPoints, nParts; - int i, nOffset; + int32 nPoints, nParts; + int i, nOffset; + unsigned char* pBuffer = NULL; + unsigned char** ppBuffer = NULL; if ( 40 + 8 + 4 > nEntitySize ) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", - hEntity, nEntitySize); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } @@ -1604,38 +2291,40 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); /* -------------------------------------------------------------------- */ /* Extract part/point count, and build vertex and part arrays */ /* to proper size. */ /* -------------------------------------------------------------------- */ - memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 ); - memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 ); + memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 ); + memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 ); - if( bBigEndian ) SwapWord( 4, &nPoints ); - if( bBigEndian ) SwapWord( 4, &nParts ); + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); - if (nPoints < 0 || nParts < 0 || + /* nPoints and nParts are unsigned */ + if (/* nPoints < 0 || nParts < 0 || */ nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.", - hEntity, nPoints, nParts); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u.", + hEntity, nPoints, nParts); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - + /* With the previous checks on nPoints and nParts, */ /* we should not overflow here and after */ /* since 50 M * (16 + 8 + 8) = 1 600 MB */ nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints; if ( psShape->nSHPType == SHPT_POLYGONZ - || psShape->nSHPType == SHPT_ARCZ - || psShape->nSHPType == SHPT_MULTIPATCH ) + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_MULTIPATCH ) { nRequiredSize += 16 + 8 * nPoints; } @@ -1645,23 +2334,31 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) } if (nRequiredSize > nEntitySize) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.", - hEntity, nPoints, nParts, nEntitySize); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u, nEntitySize=%d.", + hEntity, nPoints, nParts, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - psShape->nVertices = nPoints; - psShape->padfX = (double *) calloc(nPoints,sizeof(double)); - psShape->padfY = (double *) calloc(nPoints,sizeof(double)); - psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); - psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + if( psShape->bFastModeReadObject ) + { + int nObjectBufSize = 4 * sizeof(double) * nPoints + 2 * sizeof(int) * nParts; + pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize); + ppBuffer = &pBuffer; + } + + psShape->nVertices = nPoints; + psShape->padfX = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfY = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfZ = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfM = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + + psShape->nParts = nParts; + psShape->panPartStart = (int *) SHPAllocBuffer(ppBuffer, nParts * sizeof(int)); + psShape->panPartType = (int *) SHPAllocBuffer(ppBuffer, nParts * sizeof(int)); - psShape->nParts = nParts; - psShape->panPartStart = (int *) calloc(nParts,sizeof(int)); - psShape->panPartType = (int *) calloc(nParts,sizeof(int)); - if (psShape->padfX == NULL || psShape->padfY == NULL || psShape->padfZ == NULL || @@ -1669,46 +2366,50 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) psShape->panPartStart == NULL || psShape->panPartType == NULL) { - g_snprintf(pszErrorMsg, 128, - "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. " - "Probably broken SHP file", hEntity, nPoints, nParts ); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Not enough memory to allocate requested memory (nPoints=%u, nParts=%u) for shape %d. " + "Probably broken SHP file", nPoints, nParts, hEntity ); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - for( i = 0; i < nParts; i++ ) + for( i = 0; (int32)i < nParts; i++ ) psShape->panPartType[i] = SHPP_RING; /* -------------------------------------------------------------------- */ /* Copy out the part array from the record. */ /* -------------------------------------------------------------------- */ - memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); - for( i = 0; i < nParts; i++ ) - { - if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); + memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); + for( i = 0; (int32)i < nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); /* We check that the offset is inside the vertex array */ - if (psShape->panPartStart[i] < 0 || - psShape->panPartStart[i] >= psShape->nVertices) + if (psShape->panPartStart[i] < 0 + || (psShape->panPartStart[i] >= psShape->nVertices + && psShape->nVertices > 0) + || (psShape->panPartStart[i] > 0 && psShape->nVertices == 0) ) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d", - hEntity, i, psShape->panPartStart[i], psShape->nVertices); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d", + hEntity, i, psShape->panPartStart[i], psShape->nVertices); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1]) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d", - hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d", + hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - } + } - nOffset = 44 + 8 + 4*nParts; + nOffset = 44 + 8 + 4*nParts; /* -------------------------------------------------------------------- */ /* If this is a multipatch, we will also have parts types. */ @@ -1716,33 +2417,33 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if( psShape->nSHPType == SHPT_MULTIPATCH ) { memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts ); - for( i = 0; i < nParts; i++ ) + for( i = 0; (int32)i < nParts; i++ ) { if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); } nOffset += 4*nParts; } - + /* -------------------------------------------------------------------- */ /* Copy out the vertices from the record. */ /* -------------------------------------------------------------------- */ - for( i = 0; i < nPoints; i++ ) - { - memcpy(psShape->padfX + i, - psSHP->pabyRec + nOffset + i * 16, - 8 ); + for( i = 0; (int32)i < nPoints; i++ ) + { + memcpy(psShape->padfX + i, + psSHP->pabyRec + nOffset + i * 16, + 8 ); - memcpy(psShape->padfY + i, - psSHP->pabyRec + nOffset + i * 16 + 8, - 8 ); + memcpy(psShape->padfY + i, + psSHP->pabyRec + nOffset + i * 16 + 8, + 8 ); - if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); - if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); - } + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } nOffset += 16*nPoints; - + /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ @@ -1752,11 +2453,11 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) { memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); - + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); - - for( i = 0; i < nPoints; i++ ) + + for( i = 0; (int32)i < nPoints; i++ ) { memcpy( psShape->padfZ + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); @@ -1765,6 +2466,10 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) nOffset += 16 + 8*nPoints; } + else if( psShape->bFastModeReadObject ) + { + psShape->padfZ = NULL; + } /* -------------------------------------------------------------------- */ /* If we have a M measure value, then read it now. We assume */ @@ -1772,15 +2477,15 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) /* big enough, but really it will only occur for the Z shapes */ /* (options), and the M shapes. */ /* -------------------------------------------------------------------- */ - if( nEntitySize >= nOffset + 16 + 8*nPoints ) + if( nEntitySize >= (int)(nOffset + 16 + 8*nPoints) ) { memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); - + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); - - for( i = 0; i < nPoints; i++ ) + + for( i = 0; (int32)i < nPoints; i++ ) { memcpy( psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); @@ -1788,6 +2493,10 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) } psShape->bMeasureIsUsed = TRUE; } + else if( psShape->bFastModeReadObject ) + { + psShape->padfM = NULL; + } } /* ==================================================================== */ @@ -1797,26 +2506,31 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) || psShape->nSHPType == SHPT_MULTIPOINTM || psShape->nSHPType == SHPT_MULTIPOINTZ ) { - int32 nPoints; - int i, nOffset; + int32 nPoints; + int i, nOffset; + unsigned char* pBuffer = NULL; + unsigned char** ppBuffer = NULL; if ( 44 + 4 > nEntitySize ) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", - hEntity, nEntitySize); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - memcpy( &nPoints, psSHP->pabyRec + 44, 4 ); + memcpy( &nPoints, psSHP->pabyRec + 44, 4 ); - if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nPoints ); - if (nPoints < 0 || nPoints > 50 * 1000 * 1000) + /* nPoints is unsigned */ + if (/* nPoints < 0 || */ nPoints > 50 * 1000 * 1000) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d", - hEntity, nPoints); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nPoints = %u", + hEntity, nPoints); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } @@ -1828,43 +2542,52 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) } if (nRequiredSize > nEntitySize) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d", - hEntity, nPoints, nEntitySize); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nPoints = %u, nEntitySize = %d", + hEntity, nPoints, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - - psShape->nVertices = nPoints; - psShape->padfX = (double *) calloc(nPoints,sizeof(double)); - psShape->padfY = (double *) calloc(nPoints,sizeof(double)); - psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); - psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + + if( psShape->bFastModeReadObject ) + { + int nObjectBufSize = 4 * sizeof(double) * nPoints; + pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize); + ppBuffer = &pBuffer; + } + + psShape->nVertices = nPoints; + + psShape->padfX = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfY = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfZ = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfM = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); if (psShape->padfX == NULL || psShape->padfY == NULL || psShape->padfZ == NULL || psShape->padfM == NULL) { - g_snprintf(pszErrorMsg, 128, - "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. " - "Probably broken SHP file", hEntity, nPoints ); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Not enough memory to allocate requested memory (nPoints=%u) for shape %d. " + "Probably broken SHP file", nPoints, hEntity ); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - for( i = 0; i < nPoints; i++ ) - { - memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); - memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); + for( i = 0; (int32)i < nPoints; i++ ) + { + memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); + memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); - if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); - if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); - } + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } nOffset = 48 + 16*nPoints; - + /* -------------------------------------------------------------------- */ /* Get the X/Y bounds. */ /* -------------------------------------------------------------------- */ @@ -1873,10 +2596,10 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ @@ -1885,11 +2608,11 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) { memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); - + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); - - for( i = 0; i < nPoints; i++ ) + + for( i = 0; (int32)i < nPoints; i++ ) { memcpy( psShape->padfZ + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); @@ -1898,6 +2621,8 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) nOffset += 16 + 8*nPoints; } + else if( psShape->bFastModeReadObject ) + psShape->padfZ = NULL; /* -------------------------------------------------------------------- */ /* If we have a M measure value, then read it now. We assume */ @@ -1905,15 +2630,15 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) /* big enough, but really it will only occur for the Z shapes */ /* (options), and the M shapes. */ /* -------------------------------------------------------------------- */ - if( nEntitySize >= nOffset + 16 + 8*nPoints ) + if( nEntitySize >= (int)(nOffset + 16 + 8*nPoints) ) { memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); - + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); - - for( i = 0; i < nPoints; i++ ) + + for( i = 0; (int32)i < nPoints; i++ ) { memcpy( psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); @@ -1921,6 +2646,8 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) } psShape->bMeasureIsUsed = TRUE; } + else if( psShape->bFastModeReadObject ) + psShape->padfM = NULL; } /* ==================================================================== */ @@ -1931,38 +2658,51 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) || psShape->nSHPType == SHPT_POINTZ ) { int nOffset; - - psShape->nVertices = 1; - psShape->padfX = (double *) calloc(1,sizeof(double)); - psShape->padfY = (double *) calloc(1,sizeof(double)); - psShape->padfZ = (double *) calloc(1,sizeof(double)); - psShape->padfM = (double *) calloc(1,sizeof(double)); + + psShape->nVertices = 1; + if( psShape->bFastModeReadObject ) + { + psShape->padfX = &(psShape->dfXMin); + psShape->padfY = &(psShape->dfYMin); + psShape->padfZ = &(psShape->dfZMin); + psShape->padfM = &(psShape->dfMMin); + psShape->padfZ[0] = 0.0; + psShape->padfM[0] = 0.0; + } + else + { + psShape->padfX = (double *) calloc(1,sizeof(double)); + psShape->padfY = (double *) calloc(1,sizeof(double)); + psShape->padfZ = (double *) calloc(1,sizeof(double)); + psShape->padfM = (double *) calloc(1,sizeof(double)); + } if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize) { - g_snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", - hEntity, nEntitySize); - psSHP->sHooks.Error( pszErrorMsg ); + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 ); - memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 ); + memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 ); + memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 ); - if( bBigEndian ) SwapWord( 8, psShape->padfX ); - if( bBigEndian ) SwapWord( 8, psShape->padfY ); + if( bBigEndian ) SwapWord( 8, psShape->padfX ); + if( bBigEndian ) SwapWord( 8, psShape->padfY ); nOffset = 20 + 8; - + /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ if( psShape->nSHPType == SHPT_POINTZ ) { memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 ); - + if( bBigEndian ) SwapWord( 8, psShape->padfZ ); - + nOffset += 8; } @@ -1975,7 +2715,7 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if( nEntitySize >= nOffset + 8 ) { memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 ); - + if( bBigEndian ) SwapWord( 8, psShape->padfM ); psShape->bMeasureIsUsed = TRUE; } @@ -2017,7 +2757,7 @@ SHPTypeName( int nSHPType ) case SHPT_MULTIPOINT: return "MultiPoint"; - + case SHPT_POINTZ: return "PointZ"; @@ -2029,7 +2769,7 @@ SHPTypeName( int nSHPType ) case SHPT_MULTIPOINTZ: return "MultiPointZ"; - + case SHPT_POINTM: return "PointM"; @@ -2062,7 +2802,7 @@ SHPPartTypeName( int nPartType ) { case SHPP_TRISTRIP: return "TriangleStrip"; - + case SHPP_TRIFAN: return "TriangleFan"; @@ -2093,7 +2833,13 @@ SHPDestroyObject( SHPObject * psShape ) { if( psShape == NULL ) return; - + + if( psShape->bFastModeReadObject ) + { + psShape->bFastModeReadObject = FALSE; + return; + } + if( psShape->padfX != NULL ) free( psShape->padfX ); if( psShape->padfY != NULL ) @@ -2119,8 +2865,8 @@ SHPDestroyObject( SHPObject * psShape ) /************************************************************************/ int SHPAPI_CALL -SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) - +SHPRewindObject( CPL_UNUSED SHPHandle hSHP, + SHPObject * psObject ) { int iOpRing, bAltered = 0; @@ -2167,14 +2913,14 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) if( iCheckRing == iOpRing ) continue; - + nVertStart = psObject->panPartStart[iCheckRing]; if( iCheckRing == psObject->nParts-1 ) - nVertCount = psObject->nVertices + nVertCount = psObject->nVertices - psObject->panPartStart[iCheckRing]; else - nVertCount = psObject->panPartStart[iCheckRing+1] + nVertCount = psObject->panPartStart[iCheckRing+1] - psObject->panPartStart[iCheckRing]; for( iEdge = 0; iEdge < nVertCount; iEdge++ ) @@ -2188,19 +2934,19 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) /* Rule #1: * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY) - * The rule #1 also excludes edges collinear with the ray. + * The rule #1 also excludes edges colinear with the ray. */ if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY && dfTestY <= psObject->padfY[iNext+nVertStart] ) - || ( psObject->padfY[iNext+nVertStart] < dfTestY - && dfTestY <= psObject->padfY[iEdge+nVertStart] ) ) + || ( psObject->padfY[iNext+nVertStart] < dfTestY + && dfTestY <= psObject->padfY[iEdge+nVertStart] ) ) { /* Rule #2: - * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY) - */ - double const intersect = + * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY) + */ + double const intersect = ( psObject->padfX[iEdge+nVertStart] - + ( dfTestY - psObject->padfY[iEdge+nVertStart] ) + + ( dfTestY - psObject->padfY[iEdge+nVertStart] ) / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] ) * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) ); @@ -2208,7 +2954,7 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) { bInner = !bInner; } - } + } } } /* for iCheckRing */ @@ -2221,18 +2967,19 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) if( iOpRing == psObject->nParts-1 ) nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing]; else - nVertCount = psObject->panPartStart[iOpRing+1] + nVertCount = psObject->panPartStart[iOpRing+1] - psObject->panPartStart[iOpRing]; - dfSum = 0.0; - for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ ) + if (nVertCount < 2) + continue; + + dfSum = psObject->padfX[nVertStart] * (psObject->padfY[nVertStart+1] - psObject->padfY[nVertStart+nVertCount-1]); + for( iVert = nVertStart + 1; iVert < nVertStart+nVertCount-1; iVert++ ) { - dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1] - - psObject->padfY[iVert] * psObject->padfX[iVert+1]; + dfSum += psObject->padfX[iVert] * (psObject->padfY[iVert+1] - psObject->padfY[iVert-1]); } - dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart] - - psObject->padfY[iVert] * psObject->padfX[nVertStart]; + dfSum += psObject->padfX[iVert] * (psObject->padfY[nVertStart] - psObject->padfY[iVert-1]); /* -------------------------------------------------------------------- */ /* Reverse if necessary. */ @@ -2248,13 +2995,13 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) /* Swap X */ dfSaved = psObject->padfX[nVertStart+i]; - psObject->padfX[nVertStart+i] = + psObject->padfX[nVertStart+i] = psObject->padfX[nVertStart+nVertCount-i-1]; psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved; /* Swap Y */ dfSaved = psObject->padfY[nVertStart+i]; - psObject->padfY[nVertStart+i] = + psObject->padfY[nVertStart+i] = psObject->padfY[nVertStart+nVertCount-i-1]; psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved; @@ -2262,7 +3009,7 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) if( psObject->padfZ ) { dfSaved = psObject->padfZ[nVertStart+i]; - psObject->padfZ[nVertStart+i] = + psObject->padfZ[nVertStart+i] = psObject->padfZ[nVertStart+nVertCount-i-1]; psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved; } @@ -2271,7 +3018,7 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) if( psObject->padfM ) { dfSaved = psObject->padfM[nVertStart+i]; - psObject->padfM[nVertStart+i] = + psObject->padfM[nVertStart+i] = psObject->padfM[nVertStart+nVertCount-i-1]; psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved; } diff --git a/navit/support/shapefile/shptree.c b/navit/support/shapefile/shptree.c index 56d33b227..d81533c95 100644 --- a/navit/support/shapefile/shptree.c +++ b/navit/support/shapefile/shptree.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: shptree.c,v 1.12 2008/11/12 15:39:50 fwarmerdam Exp $ + * $Id: shptree.c,v 1.19 2016-12-05 12:44:06 erouault Exp $ * * Project: Shapelib * Purpose: Implementation of quadtree building and searching functions. @@ -7,13 +7,14 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2012, Even Rouault <even dot rouault at mines-paris dot org> * * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * or at the option of the licensee under the LGPL (see COPYING). This * option is discussed in more detail in shapelib.html. * * -- - * + * * 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 @@ -34,7 +35,44 @@ ****************************************************************************** * * $Log: shptree.c,v $ - * Revision 1.12 2008/11/12 15:39:50 fwarmerdam + * Revision 1.19 2016-12-05 12:44:06 erouault + * * Major overhaul of Makefile build system to use autoconf/automake. + * + * * Warning fixes in contrib/ + * + * Revision 1.18 2016-12-04 15:30:15 erouault + * * shpopen.c, dbfopen.c, shptree.c, shapefil.h: resync with + * GDAL Shapefile driver. Mostly cleanups. SHPObject and DBFInfo + * structures extended with new members. New functions: + * DBFSetLastModifiedDate, SHPOpenLLEx, SHPRestoreSHX, + * SHPSetFastModeReadObject + * + * * sbnsearch.c: new file to implement original ESRI .sbn spatial + * index reading. (no write support). New functions: + * SBNOpenDiskTree, SBNCloseDiskTree, SBNSearchDiskTree, + * SBNSearchDiskTreeInteger, SBNSearchFreeIds + * + * * Makefile, makefile.vc, CMakeLists.txt, shapelib.def: updates + * with new file and symbols. + * + * * commit: helper script to cvs commit + * + * Revision 1.17 2012-01-27 21:09:26 fwarmerdam + * optimize .qix output (gdal #4472) + * + * Revision 1.16 2011-12-11 22:26:46 fwarmerdam + * upgrade .qix access code to use SAHooks (gdal #3365) + * + * Revision 1.15 2011-07-24 05:59:25 fwarmerdam + * minimize use of CPLError in favor of SAHooks.Error() + * + * Revision 1.14 2010-08-27 23:43:27 fwarmerdam + * add SHPAPI_CALL attribute in code + * + * Revision 1.13 2010-06-29 05:50:15 fwarmerdam + * fix sign of Z/M comparisons in SHPCheckObjectContained (#2223) + * + * Revision 1.12 2008-11-12 15:39:50 fwarmerdam * improve safety in face of buggy .shp file. * * Revision 1.11 2007/10/27 03:31:14 fwarmerdam @@ -78,13 +116,13 @@ #include <assert.h> #include <stdlib.h> #include <string.h> +#include <limits.h> + #ifdef USE_CPL -#include <cpl_error.h> +#include "cpl_error.h" #endif -#if 0 -SHP_CVSID("$Id: shptree.c,v 1.12 2008/11/12 15:39:50 fwarmerdam Exp $") -#endif +SHP_CVSID("$Id: shptree.c,v 1.19 2016-12-05 12:44:06 erouault Exp $") #ifndef TRUE # define TRUE 1 @@ -93,9 +131,6 @@ SHP_CVSID("$Id: shptree.c,v 1.12 2008/11/12 15:39:50 fwarmerdam Exp $") static int bBigEndian = 0; -void SHPAPI_CALL SHPTreeSplitBounds( double *padfBoundsMinIn, double *padfBoundsMaxIn, double *padfBoundsMin1, double * padfBoundsMax1, double *padfBoundsMin2, double * padfBoundsMax2 ); -void SHPAPI_CALL SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode, double * padfBoundsMin, double * padfBoundsMax, int * pnShapeCount, int * pnMaxShapes, int ** ppanShapeList ); - /* -------------------------------------------------------------------- */ /* If the following is 0.5, nodes will be split in half. If it */ @@ -136,13 +171,8 @@ static SHPTreeNode *SHPTreeNodeCreate( double * padfBoundsMin, SHPTreeNode *psTreeNode; psTreeNode = (SHPTreeNode *) malloc(sizeof(SHPTreeNode)); - if( NULL == psTreeNode ) - { -#ifdef USE_CPL - CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); -#endif - return NULL; - } + if( NULL == psTreeNode ) + return NULL; psTreeNode->nShapeCount = 0; psTreeNode->panShapeIds = NULL; @@ -165,8 +195,8 @@ static SHPTreeNode *SHPTreeNodeCreate( double * padfBoundsMin, /************************************************************************/ SHPTree SHPAPI_CALL1(*) -SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, - double *padfBoundsMin, double *padfBoundsMax ) + SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, + double *padfBoundsMin, double *padfBoundsMax ) { SHPTree *psTree; @@ -178,13 +208,10 @@ SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, /* Allocate the tree object */ /* -------------------------------------------------------------------- */ psTree = (SHPTree *) malloc(sizeof(SHPTree)); - if( NULL == psTree ) - { -#ifdef USE_CPL - CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); -#endif - return NULL; - } + if( NULL == psTree ) + { + return NULL; + } psTree->hSHP = hSHP; psTree->nMaxDepth = nMaxDepth; @@ -222,9 +249,9 @@ SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, psTree->nMaxDepth = MAX_DEFAULT_TREE_DEPTH; #ifdef USE_CPL - CPLDebug( "Shape", - "Falling back to max number of allowed index tree levels (%d).", - MAX_DEFAULT_TREE_DEPTH ); + CPLDebug( "Shape", + "Falling back to max number of allowed index tree levels (%d).", + MAX_DEFAULT_TREE_DEPTH ); #endif } } @@ -233,23 +260,21 @@ SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, /* Allocate the root node. */ /* -------------------------------------------------------------------- */ psTree->psRoot = SHPTreeNodeCreate( padfBoundsMin, padfBoundsMax ); - if( NULL == psTree->psRoot ) - { - return NULL; - } + if( NULL == psTree->psRoot ) + { + free( psTree ); + return NULL; + } /* -------------------------------------------------------------------- */ /* Assign the bounds to the root node. If none are passed in, */ /* use the bounds of the provided file otherwise the create */ /* function will have already set the bounds. */ /* -------------------------------------------------------------------- */ - assert( NULL != psTree ); - assert( NULL != psTree->psRoot ); - - if( padfBoundsMin == NULL ) + if( padfBoundsMin == NULL ) { SHPGetInfo( hSHP, NULL, NULL, - psTree->psRoot->adfBoundsMin, + psTree->psRoot->adfBoundsMin, psTree->psRoot->adfBoundsMax ); } @@ -259,13 +284,13 @@ SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, if( hSHP != NULL ) { int iShape, nShapeCount; - + SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL ); for( iShape = 0; iShape < nShapeCount; iShape++ ) { SHPObject *psShape; - + psShape = SHPReadObject( hSHP, iShape ); if( psShape != NULL ) { @@ -273,7 +298,7 @@ SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, SHPDestroyObject( psShape ); } } - } + } return psTree; } @@ -286,7 +311,7 @@ static void SHPDestroyTreeNode( SHPTreeNode * psTreeNode ) { int i; - + assert( NULL != psTreeNode ); for( i = 0; i < psTreeNode->nSubNodes; i++ ) @@ -294,7 +319,7 @@ static void SHPDestroyTreeNode( SHPTreeNode * psTreeNode ) if( psTreeNode->apsSubNode[i] != NULL ) SHPDestroyTreeNode( psTreeNode->apsSubNode[i] ); } - + if( psTreeNode->panShapeIds != NULL ) free( psTreeNode->panShapeIds ); @@ -342,7 +367,7 @@ SHPCheckBoundsOverlap( double * padfBox1Min, double * padfBox1Max, { if( padfBox2Max[iDim] < padfBox1Min[iDim] ) return FALSE; - + if( padfBox1Max[iDim] < padfBox2Min[iDim] ) return FALSE; } @@ -363,23 +388,23 @@ static int SHPCheckObjectContained( SHPObject * psObject, int nDimension, if( psObject->dfXMin < padfBoundsMin[0] || psObject->dfXMax > padfBoundsMax[0] ) return FALSE; - + if( psObject->dfYMin < padfBoundsMin[1] || psObject->dfYMax > padfBoundsMax[1] ) return FALSE; if( nDimension == 2 ) return TRUE; - + if( psObject->dfZMin < padfBoundsMin[2] - || psObject->dfZMax < padfBoundsMax[2] ) + || psObject->dfZMax > padfBoundsMax[2] ) return FALSE; - + if( nDimension == 3 ) return TRUE; if( psObject->dfMMin < padfBoundsMin[3] - || psObject->dfMMax < padfBoundsMax[3] ) + || psObject->dfMMax > padfBoundsMax[3] ) return FALSE; return TRUE; @@ -392,7 +417,7 @@ static int SHPCheckObjectContained( SHPObject * psObject, int nDimension, /* longest dimension. */ /************************************************************************/ -void SHPAPI_CALL +static void SHPTreeSplitBounds( double *padfBoundsMinIn, double *padfBoundsMaxIn, double *padfBoundsMin1, double * padfBoundsMax1, double *padfBoundsMin2, double * padfBoundsMax2 ) @@ -406,7 +431,7 @@ SHPTreeSplitBounds( double *padfBoundsMinIn, double *padfBoundsMaxIn, memcpy( padfBoundsMax1, padfBoundsMaxIn, sizeof(double) * 4 ); memcpy( padfBoundsMin2, padfBoundsMinIn, sizeof(double) * 4 ); memcpy( padfBoundsMax2, padfBoundsMaxIn, sizeof(double) * 4 ); - + /* -------------------------------------------------------------------- */ /* Split in X direction. */ /* -------------------------------------------------------------------- */ @@ -441,9 +466,9 @@ SHPTreeNodeAddShapeId( SHPTreeNode * psTreeNode, SHPObject * psObject, { int i; - + /* -------------------------------------------------------------------- */ -/* If there are subnodes, then consider wiether this object */ +/* If there are subnodes, then consider whether this object */ /* will fit in them. */ /* -------------------------------------------------------------------- */ if( nMaxDepth > 1 && psTreeNode->nSubNodes > 0 ) @@ -560,7 +585,7 @@ SHPTreeNodeAddShapeId( SHPTreeNode * psTreeNode, SHPObject * psObject, /* -------------------------------------------------------------------- */ psTreeNode->nShapeCount++; - psTreeNode->panShapeIds = (int *) + psTreeNode->panShapeIds = (int *) SfRealloc( psTreeNode->panShapeIds, sizeof(int) * psTreeNode->nShapeCount ); psTreeNode->panShapeIds[psTreeNode->nShapeCount-1] = psObject->nShapeId; @@ -600,7 +625,7 @@ SHPTreeAddShapeId( SHPTree * psTree, SHPObject * psObject ) /* tree node by tree node basis. */ /************************************************************************/ -void SHPAPI_CALL +static void SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode, double * padfBoundsMin, double * padfBoundsMax, int * pnShapeCount, int * pnMaxShapes, @@ -608,7 +633,7 @@ SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode, { int i; - + /* -------------------------------------------------------------------- */ /* Does this node overlap the area of interest at all? If not, */ /* return without adding to the list at all. */ @@ -637,7 +662,7 @@ SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode, { (*ppanShapeList)[(*pnShapeCount)++] = psTreeNode->panShapeIds[i]; } - + /* -------------------------------------------------------------------- */ /* Recurse to subnodes if they exist. */ /* -------------------------------------------------------------------- */ @@ -690,7 +715,8 @@ SHPTreeFindLikelyShapes( SHPTree * hTree, /* Sort the id array */ /* -------------------------------------------------------------------- */ - qsort(panShapeList, *pnShapeCount, sizeof(int), compare_ints); + if( panShapeList != NULL ) + qsort(panShapeList, *pnShapeCount, sizeof(int), compare_ints); return panShapeList; } @@ -698,7 +724,7 @@ SHPTreeFindLikelyShapes( SHPTree * hTree, /************************************************************************/ /* SHPTreeNodeTrim() */ /* */ -/* This is the recurve version of SHPTreeTrimExtraNodes() that */ +/* This is the recursive version of SHPTreeTrimExtraNodes() that */ /* walks the tree cleaning it up. */ /************************************************************************/ @@ -726,6 +752,29 @@ static int SHPTreeNodeTrim( SHPTreeNode * psTreeNode ) } /* -------------------------------------------------------------------- */ +/* If the current node has 1 subnode and no shapes, promote that */ +/* subnode to the current node position. */ +/* -------------------------------------------------------------------- */ + if( psTreeNode->nSubNodes == 1 && psTreeNode->nShapeCount == 0) + { + SHPTreeNode* psSubNode = psTreeNode->apsSubNode[0]; + + memcpy(psTreeNode->adfBoundsMin, psSubNode->adfBoundsMin, + sizeof(psSubNode->adfBoundsMin)); + memcpy(psTreeNode->adfBoundsMax, psSubNode->adfBoundsMax, + sizeof(psSubNode->adfBoundsMax)); + psTreeNode->nShapeCount = psSubNode->nShapeCount; + assert(psTreeNode->panShapeIds == NULL); + psTreeNode->panShapeIds = psSubNode->panShapeIds; + assert(psTreeNode->papsShapeObj == NULL); + psTreeNode->papsShapeObj = psSubNode->papsShapeObj; + psTreeNode->nSubNodes = psSubNode->nSubNodes; + for( i = 0; i < psSubNode->nSubNodes; i++ ) + psTreeNode->apsSubNode[i] = psSubNode->apsSubNode[i]; + free(psSubNode); + } + +/* -------------------------------------------------------------------- */ /* We should be trimmed if we have no subnodes, and no shapes. */ /* -------------------------------------------------------------------- */ return( psTreeNode->nSubNodes == 0 && psTreeNode->nShapeCount == 0 ); @@ -765,29 +814,76 @@ static void SwapWord( int length, void * wordP ) } } + +struct SHPDiskTreeInfo +{ + SAHooks sHooks; + SAFile fpQIX; +}; + +/************************************************************************/ +/* SHPOpenDiskTree() */ +/************************************************************************/ + +SHPTreeDiskHandle SHPOpenDiskTree( const char* pszQIXFilename, + SAHooks *psHooks ) +{ + SHPTreeDiskHandle hDiskTree; + + hDiskTree = (SHPTreeDiskHandle) calloc(sizeof(struct SHPDiskTreeInfo),1); + + if (psHooks == NULL) + SASetupDefaultHooks( &(hDiskTree->sHooks) ); + else + memcpy( &(hDiskTree->sHooks), psHooks, sizeof(SAHooks) ); + + hDiskTree->fpQIX = hDiskTree->sHooks.FOpen(pszQIXFilename, "rb"); + if (hDiskTree->fpQIX == NULL) + { + free(hDiskTree); + return NULL; + } + + return hDiskTree; +} + +/***********************************************************************/ +/* SHPCloseDiskTree() */ +/************************************************************************/ + +void SHPCloseDiskTree( SHPTreeDiskHandle hDiskTree ) +{ + if (hDiskTree == NULL) + return; + + hDiskTree->sHooks.FClose(hDiskTree->fpQIX); + free(hDiskTree); +} + /************************************************************************/ /* SHPSearchDiskTreeNode() */ /************************************************************************/ static int -SHPSearchDiskTreeNode( FILE *fp, double *padfBoundsMin, double *padfBoundsMax, - int **ppanResultBuffer, int *pnBufferMax, - int *pnResultCount, int bNeedSwap ) +SHPSearchDiskTreeNode( SHPTreeDiskHandle hDiskTree, double *padfBoundsMin, double *padfBoundsMax, + int **ppanResultBuffer, int *pnBufferMax, + int *pnResultCount, int bNeedSwap, int nRecLevel ) { - int i; - int offset; - int numshapes, numsubnodes; + unsigned int i; + unsigned int offset; + unsigned int numshapes, numsubnodes; double adfNodeBoundsMin[2], adfNodeBoundsMax[2]; + int nFReadAcc; /* -------------------------------------------------------------------- */ /* Read and unswap first part of node info. */ /* -------------------------------------------------------------------- */ - fread( &offset, 4, 1, fp ); + nFReadAcc = (int)hDiskTree->sHooks.FRead( &offset, 4, 1, hDiskTree->fpQIX ); if ( bNeedSwap ) SwapWord ( 4, &offset ); - fread( adfNodeBoundsMin, sizeof(double), 2, fp ); - fread( adfNodeBoundsMax, sizeof(double), 2, fp ); + nFReadAcc += (int)hDiskTree->sHooks.FRead( adfNodeBoundsMin, sizeof(double), 2, hDiskTree->fpQIX ); + nFReadAcc += (int)hDiskTree->sHooks.FRead( adfNodeBoundsMax, sizeof(double), 2, hDiskTree->fpQIX ); if ( bNeedSwap ) { SwapWord( 8, adfNodeBoundsMin + 0 ); @@ -795,36 +891,75 @@ SHPSearchDiskTreeNode( FILE *fp, double *padfBoundsMin, double *padfBoundsMax, SwapWord( 8, adfNodeBoundsMax + 0 ); SwapWord( 8, adfNodeBoundsMax + 1 ); } - - fread( &numshapes, 4, 1, fp ); + + nFReadAcc += (int)hDiskTree->sHooks.FRead( &numshapes, 4, 1, hDiskTree->fpQIX ); if ( bNeedSwap ) SwapWord ( 4, &numshapes ); + /* Check that we could read all previous values */ + if( nFReadAcc != 1 + 2 + 2 + 1 ) + { + hDiskTree->sHooks.Error("I/O error"); + return FALSE; + } + + /* Sanity checks to avoid int overflows in later computation */ + if( offset > INT_MAX - sizeof(int) ) + { + hDiskTree->sHooks.Error("Invalid value for offset"); + return FALSE; + } + + if( numshapes > (INT_MAX - offset - sizeof(int)) / sizeof(int) || + numshapes > INT_MAX / sizeof(int) - *pnResultCount ) + { + hDiskTree->sHooks.Error("Invalid value for numshapes"); + return FALSE; + } + /* -------------------------------------------------------------------- */ /* If we don't overlap this node at all, we can just fseek() */ /* pass this node info and all subnodes. */ /* -------------------------------------------------------------------- */ - if( !SHPCheckBoundsOverlap( adfNodeBoundsMin, adfNodeBoundsMax, + if( !SHPCheckBoundsOverlap( adfNodeBoundsMin, adfNodeBoundsMax, padfBoundsMin, padfBoundsMax, 2 ) ) { offset += numshapes*sizeof(int) + sizeof(int); - fseek(fp, offset, SEEK_CUR); + hDiskTree->sHooks.FSeek(hDiskTree->fpQIX, offset, SEEK_CUR); return TRUE; } /* -------------------------------------------------------------------- */ /* Add all the shapeids at this node to our list. */ /* -------------------------------------------------------------------- */ - if(numshapes > 0) + if(numshapes > 0) { - if( *pnResultCount + numshapes > *pnBufferMax ) + if( *pnResultCount + numshapes > (unsigned int)*pnBufferMax ) { - *pnBufferMax = (int) ((*pnResultCount + numshapes + 100) * 1.25); - *ppanResultBuffer = (int *) + int* pNewBuffer; + + *pnBufferMax = (*pnResultCount + numshapes + 100) * 5 / 4; + + if( (size_t)*pnBufferMax > INT_MAX / sizeof(int) ) + *pnBufferMax = *pnResultCount + numshapes; + + pNewBuffer = (int *) SfRealloc( *ppanResultBuffer, *pnBufferMax * sizeof(int) ); + + if( pNewBuffer == NULL ) + { + hDiskTree->sHooks.Error("Out of memory error"); + return FALSE; + } + + *ppanResultBuffer = pNewBuffer; } - fread( *ppanResultBuffer + *pnResultCount, - sizeof(int), numshapes, fp ); + if( hDiskTree->sHooks.FRead( *ppanResultBuffer + *pnResultCount, + sizeof(int), numshapes, hDiskTree->fpQIX ) != numshapes ) + { + hDiskTree->sHooks.Error("I/O error"); + return FALSE; + } if (bNeedSwap ) { @@ -832,20 +967,29 @@ SHPSearchDiskTreeNode( FILE *fp, double *padfBoundsMin, double *padfBoundsMax, SwapWord( 4, *ppanResultBuffer + *pnResultCount + i ); } - *pnResultCount += numshapes; - } + *pnResultCount += numshapes; + } /* -------------------------------------------------------------------- */ /* Process the subnodes. */ /* -------------------------------------------------------------------- */ - fread( &numsubnodes, 4, 1, fp ); + if( hDiskTree->sHooks.FRead( &numsubnodes, 4, 1, hDiskTree->fpQIX ) != 1 ) + { + hDiskTree->sHooks.Error("I/O error"); + return FALSE; + } if ( bNeedSwap ) SwapWord ( 4, &numsubnodes ); + if( numsubnodes > 0 && nRecLevel == 32 ) + { + hDiskTree->sHooks.Error("Shape tree is too deep"); + return FALSE; + } for(i=0; i<numsubnodes; i++) { - if( !SHPSearchDiskTreeNode( fp, padfBoundsMin, padfBoundsMax, - ppanResultBuffer, pnBufferMax, - pnResultCount, bNeedSwap ) ) + if( !SHPSearchDiskTreeNode( hDiskTree, padfBoundsMin, padfBoundsMax, + ppanResultBuffer, pnBufferMax, + pnResultCount, bNeedSwap, nRecLevel + 1 ) ) return FALSE; } @@ -853,13 +997,58 @@ SHPSearchDiskTreeNode( FILE *fp, double *padfBoundsMin, double *padfBoundsMax, } /************************************************************************/ +/* SHPTreeReadLibc() */ +/************************************************************************/ + +static +SAOffset SHPTreeReadLibc( void *p, SAOffset size, SAOffset nmemb, SAFile file ) + +{ + return (SAOffset) fread( p, (size_t) size, (size_t) nmemb, + (FILE *) file ); +} + +/************************************************************************/ +/* SHPTreeSeekLibc() */ +/************************************************************************/ + +static +SAOffset SHPTreeSeekLibc( SAFile file, SAOffset offset, int whence ) + +{ + return (SAOffset) fseek( (FILE *) file, (long) offset, whence ); +} + +/************************************************************************/ /* SHPSearchDiskTree() */ /************************************************************************/ -int SHPAPI_CALL1(*) -SHPSearchDiskTree( FILE *fp, +int SHPAPI_CALL1(*) +SHPSearchDiskTree( FILE *fp, double *padfBoundsMin, double *padfBoundsMax, int *pnShapeCount ) +{ + struct SHPDiskTreeInfo sDiskTree; + memset(&sDiskTree.sHooks, 0, sizeof(sDiskTree.sHooks)); + + /* We do not use SASetupDefaultHooks() because the FILE* */ + /* is a libc FILE* */ + sDiskTree.sHooks.FSeek = SHPTreeSeekLibc; + sDiskTree.sHooks.FRead = SHPTreeReadLibc; + + sDiskTree.fpQIX = (SAFile)fp; + + return SHPSearchDiskTreeEx( &sDiskTree, padfBoundsMin, padfBoundsMax, + pnShapeCount ); +} + +/***********************************************************************/ +/* SHPSearchDiskTreeEx() */ +/************************************************************************/ + +int* SHPSearchDiskTreeEx( SHPTreeDiskHandle hDiskTree, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ) { int i, bNeedSwap, nBufferMax = 0; @@ -880,8 +1069,8 @@ SHPSearchDiskTree( FILE *fp, /* -------------------------------------------------------------------- */ /* Read the header. */ /* -------------------------------------------------------------------- */ - fseek( fp, 0, SEEK_SET ); - fread( abyBuf, 16, 1, fp ); + hDiskTree->sHooks.FSeek( hDiskTree->fpQIX, 0, SEEK_SET ); + hDiskTree->sHooks.FRead( abyBuf, 16, 1, hDiskTree->fpQIX ); if( memcmp( abyBuf, "SQT", 3 ) != 0 ) return NULL; @@ -893,11 +1082,11 @@ SHPSearchDiskTree( FILE *fp, bNeedSwap = TRUE; /* -------------------------------------------------------------------- */ -/* Search through root node and it's decendents. */ +/* Search through root node and it's descendants. */ /* -------------------------------------------------------------------- */ - if( !SHPSearchDiskTreeNode( fp, padfBoundsMin, padfBoundsMax, - &panResultBuffer, &nBufferMax, - pnShapeCount, bNeedSwap ) ) + if( !SHPSearchDiskTreeNode( hDiskTree, padfBoundsMin, padfBoundsMax, + &panResultBuffer, &nBufferMax, + pnShapeCount, bNeedSwap, 0 ) ) { if( panResultBuffer != NULL ) free( panResultBuffer ); @@ -907,8 +1096,14 @@ SHPSearchDiskTree( FILE *fp, /* -------------------------------------------------------------------- */ /* Sort the id array */ /* -------------------------------------------------------------------- */ - qsort(panResultBuffer, *pnShapeCount, sizeof(int), compare_ints); - + + /* To distinguish between empty intersection from error case */ + if( panResultBuffer == NULL ) + panResultBuffer = (int*) calloc(1, sizeof(int)); + else + qsort(panResultBuffer, *pnShapeCount, sizeof(int), compare_ints); + + return panResultBuffer; } @@ -920,16 +1115,16 @@ SHPSearchDiskTree( FILE *fp, /* seek past them all efficiently. */ /************************************************************************/ -static int SHPGetSubNodeOffset( SHPTreeNode *node) +static int SHPGetSubNodeOffset( SHPTreeNode *node) { int i; - long offset=0; + int offset=0; - for(i=0; i<node->nSubNodes; i++ ) + for(i=0; i<node->nSubNodes; i++ ) { - if(node->apsSubNode[i]) + if(node->apsSubNode[i]) { - offset += 4*sizeof(double) + offset += 4*sizeof(double) + (node->apsSubNode[i]->nShapeCount+3)*sizeof(int); offset += SHPGetSubNodeOffset(node->apsSubNode[i]); } @@ -942,26 +1137,26 @@ static int SHPGetSubNodeOffset( SHPTreeNode *node) /* SHPWriteTreeNode() */ /************************************************************************/ -static void SHPWriteTreeNode( FILE *fp, SHPTreeNode *node) +static void SHPWriteTreeNode( SAFile fp, SHPTreeNode *node, SAHooks* psHooks) { int i,j; int offset; unsigned char *pabyRec = NULL; - assert( NULL != node ); + assert( NULL != node ); offset = SHPGetSubNodeOffset(node); - - pabyRec = (unsigned char *) + + pabyRec = (unsigned char *) malloc(sizeof(double) * 4 + (3 * sizeof(int)) + (node->nShapeCount * sizeof(int)) ); - if( NULL == pabyRec ) - { + if( NULL == pabyRec ) + { #ifdef USE_CPL - CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); + CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); #endif - assert( 0 ); - } - assert( NULL != pabyRec ); + assert( 0 ); + return; + } memcpy( pabyRec, &offset, 4); @@ -973,16 +1168,17 @@ static void SHPWriteTreeNode( FILE *fp, SHPTreeNode *node) memcpy( pabyRec+36, &node->nShapeCount, 4); j = node->nShapeCount * sizeof(int); - memcpy( pabyRec+40, node->panShapeIds, j); + if( j ) + memcpy( pabyRec+40, node->panShapeIds, j); memcpy( pabyRec+j+40, &node->nSubNodes, 4); - fwrite( pabyRec, 44+j, 1, fp ); + psHooks->FWrite( pabyRec, 44+j, 1, fp ); free (pabyRec); - - for(i=0; i<node->nSubNodes; i++ ) + + for(i=0; i<node->nSubNodes; i++ ) { if(node->apsSubNode[i]) - SHPWriteTreeNode( fp, node->apsSubNode[i]); + SHPWriteTreeNode( fp, node->apsSubNode[i], psHooks); } } @@ -990,18 +1186,38 @@ static void SHPWriteTreeNode( FILE *fp, SHPTreeNode *node) /* SHPWriteTree() */ /************************************************************************/ -int SHPWriteTree(SHPTree *tree, const char *filename ) +int SHPAPI_CALL SHPWriteTree(SHPTree *tree, const char *filename ) +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return SHPWriteTreeLL(tree, filename, &sHooks); +} + +/************************************************************************/ +/* SHPWriteTreeLL() */ +/************************************************************************/ + +int SHPWriteTreeLL(SHPTree *tree, const char *filename, SAHooks* psHooks ) { char signature[4] = "SQT"; int i; char abyBuf[32]; - FILE *fp; - + SAFile fp; + + SAHooks sHooks; + if (psHooks == NULL) + { + SASetupDefaultHooks( &sHooks ); + psHooks = &sHooks; + } + /* -------------------------------------------------------------------- */ /* Open the output file. */ /* -------------------------------------------------------------------- */ - fp = fopen(filename, "wb"); - if( fp == NULL ) + fp = psHooks->FOpen(filename, "wb"); + if( fp == NULL ) { return FALSE; } @@ -1014,12 +1230,12 @@ int SHPWriteTree(SHPTree *tree, const char *filename ) bBigEndian = FALSE; else bBigEndian = TRUE; - + /* -------------------------------------------------------------------- */ /* Write the header. */ /* -------------------------------------------------------------------- */ memcpy( abyBuf+0, signature, 3 ); - + if( bBigEndian ) abyBuf[3] = 2; /* New MSB */ else @@ -1030,21 +1246,21 @@ int SHPWriteTree(SHPTree *tree, const char *filename ) abyBuf[6] = 0; abyBuf[7] = 0; - fwrite( abyBuf, 8, 1, fp ); + psHooks->FWrite( abyBuf, 8, 1, fp ); - fwrite( &(tree->nTotalCount), 4, 1, fp ); + psHooks->FWrite( &(tree->nTotalCount), 4, 1, fp ); /* write maxdepth */ - fwrite( &(tree->nMaxDepth), 4, 1, fp ); + psHooks->FWrite( &(tree->nMaxDepth), 4, 1, fp ); /* -------------------------------------------------------------------- */ /* Write all the nodes "in order". */ /* -------------------------------------------------------------------- */ - SHPWriteTreeNode( fp, tree->psRoot ); - - fclose( fp ); + SHPWriteTreeNode( fp, tree->psRoot, psHooks ); + + psHooks->FClose( fp ); return TRUE; } |