diff options
Diffstat (limited to 'navit/support/shapefile/dbfopen.c')
-rw-r--r-- | navit/support/shapefile/dbfopen.c | 1028 |
1 files changed, 782 insertions, 246 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; +} |