| 1 | /******************************************************************************
|
|---|
| 2 | * $Id: dbfopen.c 41851 2018-03-17 15:20:21Z rouault $
|
|---|
| 3 | *
|
|---|
| 4 | * Project: Shapelib
|
|---|
| 5 | * Purpose: Implementation of .dbf access API documented in dbf_api.html.
|
|---|
| 6 | * Author: Frank Warmerdam, warmerdam@pobox.com
|
|---|
| 7 | *
|
|---|
| 8 | ******************************************************************************
|
|---|
| 9 | * Copyright (c) 1999, Frank Warmerdam
|
|---|
| 10 | * Copyright (c) 2012-2013, Even Rouault <even dot rouault at mines-paris dot org>
|
|---|
| 11 | *
|
|---|
| 12 | * This software is available under the following "MIT Style" license,
|
|---|
| 13 | * or at the option of the licensee under the LGPL (see COPYING). This
|
|---|
| 14 | * option is discussed in more detail in shapelib.html.
|
|---|
| 15 | *
|
|---|
| 16 | * --
|
|---|
| 17 | *
|
|---|
| 18 | * Permission is hereby granted, free of charge, to any person obtaining a
|
|---|
| 19 | * copy of this software and associated documentation files (the "Software"),
|
|---|
| 20 | * to deal in the Software without restriction, including without limitation
|
|---|
| 21 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|---|
| 22 | * and/or sell copies of the Software, and to permit persons to whom the
|
|---|
| 23 | * Software is furnished to do so, subject to the following conditions:
|
|---|
| 24 | *
|
|---|
| 25 | * The above copyright notice and this permission notice shall be included
|
|---|
| 26 | * in all copies or substantial portions of the Software.
|
|---|
| 27 | *
|
|---|
| 28 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|---|
| 29 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|---|
| 30 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|---|
| 31 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|---|
| 32 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|---|
| 33 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|---|
| 34 | * DEALINGS IN THE SOFTWARE.
|
|---|
| 35 | ******************************************************************************
|
|---|
| 36 | *
|
|---|
| 37 | * $Log: dbfopen.c,v $
|
|---|
| 38 | * Revision 1.92 2016-12-05 18:44:08 erouault
|
|---|
| 39 | * * dbfopen.c, shapefil.h: write DBF end-of-file character 0x1A by default.
|
|---|
| 40 | * This behaviour can be controlled with the DBFSetWriteEndOfFileChar()
|
|---|
| 41 | * function.
|
|---|
| 42 | *
|
|---|
| 43 | * Revision 1.91 2016-12-05 12:44:05 erouault
|
|---|
| 44 | * * Major overhaul of Makefile build system to use autoconf/automake.
|
|---|
| 45 | *
|
|---|
| 46 | * * Warning fixes in contrib/
|
|---|
| 47 | *
|
|---|
| 48 | * Revision 1.90 2016-12-04 15:30:15 erouault
|
|---|
| 49 | * * shpopen.c, dbfopen.c, shptree.c, shapefil.h: resync with
|
|---|
| 50 | * GDAL Shapefile driver. Mostly cleanups. SHPObject and DBFInfo
|
|---|
| 51 | * structures extended with new members. New functions:
|
|---|
| 52 | * DBFSetLastModifiedDate, SHPOpenLLEx, SHPRestoreSHX,
|
|---|
| 53 | * SHPSetFastModeReadObject
|
|---|
| 54 | *
|
|---|
| 55 | * * sbnsearch.c: new file to implement original ESRI .sbn spatial
|
|---|
| 56 | * index reading. (no write support). New functions:
|
|---|
| 57 | * SBNOpenDiskTree, SBNCloseDiskTree, SBNSearchDiskTree,
|
|---|
| 58 | * SBNSearchDiskTreeInteger, SBNSearchFreeIds
|
|---|
| 59 | *
|
|---|
| 60 | * * Makefile, makefile.vc, CMakeLists.txt, shapelib.def: updates
|
|---|
| 61 | * with new file and symbols.
|
|---|
| 62 | *
|
|---|
| 63 | * * commit: helper script to cvs commit
|
|---|
| 64 | *
|
|---|
| 65 | * Revision 1.89 2011-07-24 05:59:25 fwarmerdam
|
|---|
| 66 | * minimize use of CPLError in favor of SAHooks.Error()
|
|---|
| 67 | *
|
|---|
| 68 | * Revision 1.88 2011-05-13 17:35:17 fwarmerdam
|
|---|
| 69 | * added DBFReorderFields() and DBFAlterFields() functions (from Even)
|
|---|
| 70 | *
|
|---|
| 71 | * Revision 1.87 2011-05-07 22:41:02 fwarmerdam
|
|---|
| 72 | * ensure pending record is flushed when adding a native field (GDAL #4073)
|
|---|
| 73 | *
|
|---|
| 74 | * Revision 1.86 2011-04-17 15:15:29 fwarmerdam
|
|---|
| 75 | * Removed unused variable.
|
|---|
| 76 | *
|
|---|
| 77 | * Revision 1.85 2010-12-06 16:09:34 fwarmerdam
|
|---|
| 78 | * fix buffer read overrun fetching code page (bug 2276)
|
|---|
| 79 | *
|
|---|
| 80 | * Revision 1.84 2009-10-29 19:59:48 fwarmerdam
|
|---|
| 81 | * avoid crash on truncated header (gdal #3093)
|
|---|
| 82 | *
|
|---|
| 83 | * Revision 1.83 2008/11/12 14:28:15 fwarmerdam
|
|---|
| 84 | * DBFCreateField() now works on files with records
|
|---|
| 85 | *
|
|---|
| 86 | * Revision 1.82 2008/11/11 17:47:09 fwarmerdam
|
|---|
| 87 | * added DBFDeleteField() function
|
|---|
| 88 | *
|
|---|
| 89 | * Revision 1.81 2008/01/03 17:48:13 bram
|
|---|
| 90 | * in DBFCreate, use default code page LDID/87 (= 0x57, ANSI)
|
|---|
| 91 | * instead of LDID/3. This seems to be the same as what ESRI
|
|---|
| 92 | * would be doing by default.
|
|---|
| 93 | *
|
|---|
| 94 | * Revision 1.80 2007/12/30 14:36:39 fwarmerdam
|
|---|
| 95 | * avoid syntax issue with last comment.
|
|---|
| 96 | *
|
|---|
| 97 | * Revision 1.79 2007/12/30 14:35:48 fwarmerdam
|
|---|
| 98 | * Avoid char* / unsigned char* warnings.
|
|---|
| 99 | *
|
|---|
| 100 | * Revision 1.78 2007/12/18 18:28:07 bram
|
|---|
| 101 | * - create hook for client specific atof (bugzilla ticket 1615)
|
|---|
| 102 | * - check for NULL handle before closing cpCPG file, and close after reading.
|
|---|
| 103 | *
|
|---|
| 104 | * Revision 1.77 2007/12/15 20:25:21 bram
|
|---|
| 105 | * dbfopen.c now reads the Code Page information from the DBF file, and exports
|
|---|
| 106 | * this information as a string through the DBFGetCodePage function. This is
|
|---|
| 107 | * either the number from the LDID header field ("LDID/<number>") or as the
|
|---|
| 108 | * content of an accompanying .CPG file. When creating a DBF file, the code can
|
|---|
| 109 | * be set using DBFCreateEx.
|
|---|
| 110 | *
|
|---|
| 111 | * Revision 1.76 2007/12/12 22:21:32 bram
|
|---|
| 112 | * DBFClose: check for NULL psDBF handle before trying to close it.
|
|---|
| 113 | *
|
|---|
| 114 | * Revision 1.75 2007/12/06 13:58:19 fwarmerdam
|
|---|
| 115 | * make sure file offset calculations are done in as SAOffset
|
|---|
| 116 | *
|
|---|
| 117 | * Revision 1.74 2007/12/06 07:00:25 fwarmerdam
|
|---|
| 118 | * dbfopen now using SAHooks for fileio
|
|---|
| 119 | *
|
|---|
| 120 | * Revision 1.73 2007/09/03 19:48:11 fwarmerdam
|
|---|
| 121 | * move DBFReadAttribute() static dDoubleField into dbfinfo
|
|---|
| 122 | *
|
|---|
| 123 | * Revision 1.72 2007/09/03 19:34:06 fwarmerdam
|
|---|
| 124 | * Avoid use of static tuple buffer in DBFReadTuple()
|
|---|
| 125 | *
|
|---|
| 126 | * Revision 1.71 2006/06/22 14:37:18 fwarmerdam
|
|---|
| 127 | * avoid memory leak if dbfopen fread fails
|
|---|
| 128 | *
|
|---|
| 129 | * Revision 1.70 2006/06/17 17:47:05 fwarmerdam
|
|---|
| 130 | * use calloc() for dbfinfo in DBFCreate
|
|---|
| 131 | *
|
|---|
| 132 | * Revision 1.69 2006/06/17 15:34:32 fwarmerdam
|
|---|
| 133 | * disallow creating fields wider than 255
|
|---|
| 134 | *
|
|---|
| 135 | * Revision 1.68 2006/06/17 15:12:40 fwarmerdam
|
|---|
| 136 | * Fixed C++ style comments.
|
|---|
| 137 | *
|
|---|
| 138 | * Revision 1.67 2006/06/17 00:24:53 fwarmerdam
|
|---|
| 139 | * Don't treat non-zero decimals values as high order byte for length
|
|---|
| 140 | * for strings. It causes serious corruption for some files.
|
|---|
| 141 | * http://bugzilla.remotesensing.org/show_bug.cgi?id=1202
|
|---|
| 142 | *
|
|---|
| 143 | * Revision 1.66 2006/03/29 18:26:20 fwarmerdam
|
|---|
| 144 | * fixed bug with size of pachfieldtype in dbfcloneempty
|
|---|
| 145 | *
|
|---|
| 146 | * Revision 1.65 2006/02/15 01:14:30 fwarmerdam
|
|---|
| 147 | * added DBFAddNativeFieldType
|
|---|
| 148 | *
|
|---|
| 149 | * Revision 1.64 2006/02/09 00:29:04 fwarmerdam
|
|---|
| 150 | * Changed to put spaces into string fields that are NULL as
|
|---|
| 151 | * per http://bugzilla.maptools.org/show_bug.cgi?id=316.
|
|---|
| 152 | *
|
|---|
| 153 | * Revision 1.63 2006/01/25 15:35:43 fwarmerdam
|
|---|
| 154 | * check success on DBFFlushRecord
|
|---|
| 155 | *
|
|---|
| 156 | * Revision 1.62 2006/01/10 16:28:03 fwarmerdam
|
|---|
| 157 | * Fixed typo in CPLError.
|
|---|
| 158 | *
|
|---|
| 159 | * Revision 1.61 2006/01/10 16:26:29 fwarmerdam
|
|---|
| 160 | * Push loading record buffer into DBFLoadRecord.
|
|---|
| 161 | * Implement CPL error reporting if USE_CPL defined.
|
|---|
| 162 | *
|
|---|
| 163 | * Revision 1.60 2006/01/05 01:27:27 fwarmerdam
|
|---|
| 164 | * added dbf deletion mark/fetch
|
|---|
| 165 | *
|
|---|
| 166 | * Revision 1.59 2005/03/14 15:20:28 fwarmerdam
|
|---|
| 167 | * Fixed last change.
|
|---|
| 168 | *
|
|---|
| 169 | * Revision 1.58 2005/03/14 15:18:54 fwarmerdam
|
|---|
| 170 | * Treat very wide fields with no decimals as double. This is
|
|---|
| 171 | * more than 32bit integer fields.
|
|---|
| 172 | *
|
|---|
| 173 | * Revision 1.57 2005/02/10 20:16:54 fwarmerdam
|
|---|
| 174 | * Make the pszStringField buffer for DBFReadAttribute() static char [256]
|
|---|
| 175 | * as per bug 306.
|
|---|
| 176 | *
|
|---|
| 177 | * Revision 1.56 2005/02/10 20:07:56 fwarmerdam
|
|---|
| 178 | * Fixed bug 305 in DBFCloneEmpty() - header length problem.
|
|---|
| 179 | *
|
|---|
| 180 | * Revision 1.55 2004/09/26 20:23:46 fwarmerdam
|
|---|
| 181 | * avoid warnings with rcsid and signed/unsigned stuff
|
|---|
| 182 | *
|
|---|
| 183 | * Revision 1.54 2004/09/15 16:26:10 fwarmerdam
|
|---|
| 184 | * Treat all blank numeric fields as null too.
|
|---|
| 185 | */
|
|---|
| 186 |
|
|---|
| 187 | #include "shapefil.h"
|
|---|
| 188 |
|
|---|
| 189 | #include <math.h>
|
|---|
| 190 | #include <stdlib.h>
|
|---|
| 191 | #include <ctype.h>
|
|---|
| 192 | #include <string.h>
|
|---|
| 193 |
|
|---|
| 194 | #ifdef USE_CPL
|
|---|
| 195 | #include "cpl_string.h"
|
|---|
| 196 | #else
|
|---|
| 197 |
|
|---|
| 198 | #if defined(_MSC_VER)
|
|---|
| 199 | # if _MSC_VER < 1900
|
|---|
| 200 | # define snprintf _snprintf
|
|---|
| 201 | # endif
|
|---|
| 202 | #elif defined(WIN32) || defined(_WIN32)
|
|---|
| 203 | # ifndef snprintf
|
|---|
| 204 | # define snprintf _snprintf
|
|---|
| 205 | # endif
|
|---|
| 206 | #endif
|
|---|
| 207 |
|
|---|
| 208 | #define CPLsprintf sprintf
|
|---|
| 209 | #define CPLsnprintf snprintf
|
|---|
| 210 | #endif
|
|---|
| 211 |
|
|---|
| 212 | SHP_CVSID("$Id: dbfopen.c 41851 2018-03-17 15:20:21Z rouault $")
|
|---|
| 213 |
|
|---|
| 214 | #ifndef FALSE
|
|---|
| 215 | # define FALSE 0
|
|---|
| 216 | # define TRUE 1
|
|---|
| 217 | #endif
|
|---|
| 218 |
|
|---|
| 219 | /* File header size */
|
|---|
| 220 | #define XBASE_FILEHDR_SZ 32
|
|---|
| 221 |
|
|---|
| 222 | #define HEADER_RECORD_TERMINATOR 0x0D
|
|---|
| 223 |
|
|---|
| 224 | /* See http://www.manmrk.net/tutorials/database/xbase/dbf.html */
|
|---|
| 225 | #define END_OF_FILE_CHARACTER 0x1A
|
|---|
| 226 |
|
|---|
| 227 | #ifdef USE_CPL
|
|---|
| 228 | CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused) {}
|
|---|
| 229 | #else
|
|---|
| 230 | #define CPL_IGNORE_RET_VAL_INT(x) x
|
|---|
| 231 | #endif
|
|---|
| 232 |
|
|---|
| 233 | /************************************************************************/
|
|---|
| 234 | /* SfRealloc() */
|
|---|
| 235 | /* */
|
|---|
| 236 | /* A realloc cover function that will access a NULL pointer as */
|
|---|
| 237 | /* a valid input. */
|
|---|
| 238 | /************************************************************************/
|
|---|
| 239 |
|
|---|
| 240 | static void * SfRealloc( void * pMem, int nNewSize )
|
|---|
| 241 |
|
|---|
| 242 | {
|
|---|
| 243 | if( pMem == NULL )
|
|---|
| 244 | return( (void *) malloc(nNewSize) );
|
|---|
| 245 | else
|
|---|
| 246 | return( (void *) realloc(pMem,nNewSize) );
|
|---|
| 247 | }
|
|---|
| 248 |
|
|---|
| 249 | /************************************************************************/
|
|---|
| 250 | /* DBFWriteHeader() */
|
|---|
| 251 | /* */
|
|---|
| 252 | /* This is called to write out the file header, and field */
|
|---|
| 253 | /* descriptions before writing any actual data records. This */
|
|---|
| 254 | /* also computes all the DBFDataSet field offset/size/decimals */
|
|---|
| 255 | /* and so forth values. */
|
|---|
| 256 | /************************************************************************/
|
|---|
| 257 |
|
|---|
| 258 | static void DBFWriteHeader(DBFHandle psDBF)
|
|---|
| 259 |
|
|---|
| 260 | {
|
|---|
| 261 | unsigned char abyHeader[XBASE_FILEHDR_SZ] = { 0 };
|
|---|
| 262 |
|
|---|
| 263 | if( !psDBF->bNoHeader )
|
|---|
| 264 | return;
|
|---|
| 265 |
|
|---|
| 266 | psDBF->bNoHeader = FALSE;
|
|---|
| 267 |
|
|---|
| 268 | /* -------------------------------------------------------------------- */
|
|---|
| 269 | /* Initialize the file header information. */
|
|---|
| 270 | /* -------------------------------------------------------------------- */
|
|---|
| 271 | abyHeader[0] = 0x03; /* memo field? - just copying */
|
|---|
| 272 |
|
|---|
| 273 | /* write out update date */
|
|---|
| 274 | abyHeader[1] = (unsigned char) psDBF->nUpdateYearSince1900;
|
|---|
| 275 | abyHeader[2] = (unsigned char) psDBF->nUpdateMonth;
|
|---|
| 276 | abyHeader[3] = (unsigned char) psDBF->nUpdateDay;
|
|---|
| 277 |
|
|---|
| 278 | /* record count preset at zero */
|
|---|
| 279 |
|
|---|
| 280 | abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256);
|
|---|
| 281 | abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256);
|
|---|
| 282 |
|
|---|
| 283 | abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256);
|
|---|
| 284 | abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256);
|
|---|
| 285 |
|
|---|
| 286 | abyHeader[29] = (unsigned char) (psDBF->iLanguageDriver);
|
|---|
| 287 |
|
|---|
| 288 | /* -------------------------------------------------------------------- */
|
|---|
| 289 | /* Write the initial 32 byte file header, and all the field */
|
|---|
| 290 | /* descriptions. */
|
|---|
| 291 | /* -------------------------------------------------------------------- */
|
|---|
| 292 | psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
|
|---|
| 293 | psDBF->sHooks.FWrite( abyHeader, XBASE_FILEHDR_SZ, 1, psDBF->fp );
|
|---|
| 294 | psDBF->sHooks.FWrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields,
|
|---|
| 295 | psDBF->fp );
|
|---|
| 296 |
|
|---|
| 297 | /* -------------------------------------------------------------------- */
|
|---|
| 298 | /* Write out the newline character if there is room for it. */
|
|---|
| 299 | /* -------------------------------------------------------------------- */
|
|---|
| 300 | if( psDBF->nHeaderLength > XBASE_FLDHDR_SZ*psDBF->nFields +
|
|---|
| 301 | XBASE_FLDHDR_SZ )
|
|---|
| 302 | {
|
|---|
| 303 | char cNewline;
|
|---|
| 304 |
|
|---|
| 305 | cNewline = HEADER_RECORD_TERMINATOR;
|
|---|
| 306 | psDBF->sHooks.FWrite( &cNewline, 1, 1, psDBF->fp );
|
|---|
| 307 | }
|
|---|
| 308 |
|
|---|
| 309 | /* -------------------------------------------------------------------- */
|
|---|
| 310 | /* If the file is new, add a EOF character. */
|
|---|
| 311 | /* -------------------------------------------------------------------- */
|
|---|
| 312 | if( psDBF->nRecords == 0 && psDBF->bWriteEndOfFileChar )
|
|---|
| 313 | {
|
|---|
| 314 | char ch = END_OF_FILE_CHARACTER;
|
|---|
| 315 |
|
|---|
| 316 | psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp );
|
|---|
| 317 | }
|
|---|
| 318 | }
|
|---|
| 319 |
|
|---|
| 320 | /************************************************************************/
|
|---|
| 321 | /* DBFFlushRecord() */
|
|---|
| 322 | /* */
|
|---|
| 323 | /* Write out the current record if there is one. */
|
|---|
| 324 | /************************************************************************/
|
|---|
| 325 |
|
|---|
| 326 | static int DBFFlushRecord( DBFHandle psDBF )
|
|---|
| 327 |
|
|---|
| 328 | {
|
|---|
| 329 | SAOffset nRecordOffset;
|
|---|
| 330 |
|
|---|
| 331 | if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 )
|
|---|
| 332 | {
|
|---|
| 333 | psDBF->bCurrentRecordModified = FALSE;
|
|---|
| 334 |
|
|---|
| 335 | nRecordOffset =
|
|---|
| 336 | psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord
|
|---|
| 337 | + psDBF->nHeaderLength;
|
|---|
| 338 |
|
|---|
| 339 | if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0
|
|---|
| 340 | || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord,
|
|---|
| 341 | psDBF->nRecordLength,
|
|---|
| 342 | 1, psDBF->fp ) != 1 )
|
|---|
| 343 | {
|
|---|
| 344 | char szMessage[128];
|
|---|
| 345 | snprintf( szMessage, sizeof(szMessage), "Failure writing DBF record %d.",
|
|---|
| 346 | psDBF->nCurrentRecord );
|
|---|
| 347 | psDBF->sHooks.Error( szMessage );
|
|---|
| 348 | return FALSE;
|
|---|
| 349 | }
|
|---|
| 350 |
|
|---|
| 351 | if( psDBF->nCurrentRecord == psDBF->nRecords - 1 )
|
|---|
| 352 | {
|
|---|
| 353 | if( psDBF->bWriteEndOfFileChar )
|
|---|
| 354 | {
|
|---|
| 355 | char ch = END_OF_FILE_CHARACTER;
|
|---|
| 356 | psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp );
|
|---|
| 357 | }
|
|---|
| 358 | }
|
|---|
| 359 | }
|
|---|
| 360 |
|
|---|
| 361 | return TRUE;
|
|---|
| 362 | }
|
|---|
| 363 |
|
|---|
| 364 | /************************************************************************/
|
|---|
| 365 | /* DBFLoadRecord() */
|
|---|
| 366 | /************************************************************************/
|
|---|
| 367 |
|
|---|
| 368 | static int DBFLoadRecord( DBFHandle psDBF, int iRecord )
|
|---|
| 369 |
|
|---|
| 370 | {
|
|---|
| 371 | if( psDBF->nCurrentRecord != iRecord )
|
|---|
| 372 | {
|
|---|
| 373 | SAOffset nRecordOffset;
|
|---|
| 374 |
|
|---|
| 375 | if( !DBFFlushRecord( psDBF ) )
|
|---|
| 376 | return FALSE;
|
|---|
| 377 |
|
|---|
| 378 | nRecordOffset =
|
|---|
| 379 | psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
|
|---|
| 380 |
|
|---|
| 381 | if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, SEEK_SET ) != 0 )
|
|---|
| 382 | {
|
|---|
| 383 | char szMessage[128];
|
|---|
| 384 | snprintf( szMessage, sizeof(szMessage), "fseek(%ld) failed on DBF file.",
|
|---|
| 385 | (long) nRecordOffset );
|
|---|
| 386 | psDBF->sHooks.Error( szMessage );
|
|---|
| 387 | return FALSE;
|
|---|
| 388 | }
|
|---|
| 389 |
|
|---|
| 390 | if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord,
|
|---|
| 391 | psDBF->nRecordLength, 1, psDBF->fp ) != 1 )
|
|---|
| 392 | {
|
|---|
| 393 | char szMessage[128];
|
|---|
| 394 | snprintf( szMessage, sizeof(szMessage), "fread(%d) failed on DBF file.",
|
|---|
| 395 | psDBF->nRecordLength );
|
|---|
| 396 | psDBF->sHooks.Error( szMessage );
|
|---|
| 397 | return FALSE;
|
|---|
| 398 | }
|
|---|
| 399 |
|
|---|
| 400 | psDBF->nCurrentRecord = iRecord;
|
|---|
| 401 | }
|
|---|
| 402 |
|
|---|
| 403 | return TRUE;
|
|---|
| 404 | }
|
|---|
| 405 |
|
|---|
| 406 | /************************************************************************/
|
|---|
| 407 | /* DBFUpdateHeader() */
|
|---|
| 408 | /************************************************************************/
|
|---|
| 409 |
|
|---|
| 410 | void SHPAPI_CALL
|
|---|
| 411 | DBFUpdateHeader( DBFHandle psDBF )
|
|---|
| 412 |
|
|---|
| 413 | {
|
|---|
| 414 | unsigned char abyFileHeader[XBASE_FILEHDR_SZ];
|
|---|
| 415 |
|
|---|
| 416 | if( psDBF->bNoHeader )
|
|---|
| 417 | DBFWriteHeader( psDBF );
|
|---|
| 418 |
|
|---|
| 419 | if( !DBFFlushRecord( psDBF ) )
|
|---|
| 420 | return;
|
|---|
| 421 |
|
|---|
| 422 | psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
|
|---|
| 423 | psDBF->sHooks.FRead( abyFileHeader, sizeof(abyFileHeader), 1, psDBF->fp );
|
|---|
| 424 |
|
|---|
| 425 | abyFileHeader[1] = (unsigned char) psDBF->nUpdateYearSince1900;
|
|---|
| 426 | abyFileHeader[2] = (unsigned char) psDBF->nUpdateMonth;
|
|---|
| 427 | abyFileHeader[3] = (unsigned char) psDBF->nUpdateDay;
|
|---|
| 428 | abyFileHeader[4] = (unsigned char) (psDBF->nRecords & 0xFF);
|
|---|
| 429 | abyFileHeader[5] = (unsigned char) ((psDBF->nRecords>>8) & 0xFF);
|
|---|
| 430 | abyFileHeader[6] = (unsigned char) ((psDBF->nRecords>>16) & 0xFF);
|
|---|
| 431 | abyFileHeader[7] = (unsigned char) ((psDBF->nRecords>>24) & 0xFF);
|
|---|
| 432 |
|
|---|
| 433 | psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
|
|---|
| 434 | psDBF->sHooks.FWrite( abyFileHeader, sizeof(abyFileHeader), 1, psDBF->fp );
|
|---|
| 435 |
|
|---|
| 436 | psDBF->sHooks.FFlush( psDBF->fp );
|
|---|
| 437 | }
|
|---|
| 438 |
|
|---|
| 439 | /************************************************************************/
|
|---|
| 440 | /* DBFSetLastModifiedDate() */
|
|---|
| 441 | /************************************************************************/
|
|---|
| 442 |
|
|---|
| 443 | void SHPAPI_CALL
|
|---|
| 444 | DBFSetLastModifiedDate( DBFHandle psDBF, int nYYSince1900, int nMM, int nDD )
|
|---|
| 445 | {
|
|---|
| 446 | psDBF->nUpdateYearSince1900 = nYYSince1900;
|
|---|
| 447 | psDBF->nUpdateMonth = nMM;
|
|---|
| 448 | psDBF->nUpdateDay = nDD;
|
|---|
| 449 | }
|
|---|
| 450 |
|
|---|
| 451 | /************************************************************************/
|
|---|
| 452 | /* DBFOpen() */
|
|---|
| 453 | /* */
|
|---|
| 454 | /* Open a .dbf file. */
|
|---|
| 455 | /************************************************************************/
|
|---|
| 456 |
|
|---|
| 457 | DBFHandle SHPAPI_CALL
|
|---|
| 458 | DBFOpen( const char * pszFilename, const char * pszAccess )
|
|---|
| 459 |
|
|---|
| 460 | {
|
|---|
| 461 | SAHooks sHooks;
|
|---|
| 462 |
|
|---|
| 463 | SASetupDefaultHooks( &sHooks );
|
|---|
| 464 |
|
|---|
| 465 | return DBFOpenLL( pszFilename, pszAccess, &sHooks );
|
|---|
| 466 | }
|
|---|
| 467 |
|
|---|
| 468 | /************************************************************************/
|
|---|
| 469 | /* DBFGetLenWithoutExtension() */
|
|---|
| 470 | /************************************************************************/
|
|---|
| 471 |
|
|---|
| 472 | static int DBFGetLenWithoutExtension(const char* pszBasename)
|
|---|
| 473 | {
|
|---|
| 474 | int i;
|
|---|
| 475 | int nLen = (int)strlen(pszBasename);
|
|---|
| 476 | for( i = nLen-1;
|
|---|
| 477 | i > 0 && pszBasename[i] != '/' && pszBasename[i] != '\\';
|
|---|
| 478 | i-- )
|
|---|
| 479 | {
|
|---|
| 480 | if( pszBasename[i] == '.' )
|
|---|
| 481 | {
|
|---|
| 482 | return i;
|
|---|
| 483 | }
|
|---|
| 484 | }
|
|---|
| 485 | return nLen;
|
|---|
| 486 | }
|
|---|
| 487 |
|
|---|
| 488 | /************************************************************************/
|
|---|
| 489 | /* DBFOpen() */
|
|---|
| 490 | /* */
|
|---|
| 491 | /* Open a .dbf file. */
|
|---|
| 492 | /************************************************************************/
|
|---|
| 493 |
|
|---|
| 494 | DBFHandle SHPAPI_CALL
|
|---|
| 495 | DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks )
|
|---|
| 496 |
|
|---|
| 497 | {
|
|---|
| 498 | DBFHandle psDBF;
|
|---|
| 499 | SAFile pfCPG;
|
|---|
| 500 | unsigned char *pabyBuf;
|
|---|
| 501 | int nFields, nHeadLen, iField;
|
|---|
| 502 | char *pszFullname;
|
|---|
| 503 | int nBufSize = 500;
|
|---|
| 504 | int nLenWithoutExtension;
|
|---|
| 505 |
|
|---|
| 506 | /* -------------------------------------------------------------------- */
|
|---|
| 507 | /* We only allow the access strings "rb" and "r+". */
|
|---|
| 508 | /* -------------------------------------------------------------------- */
|
|---|
| 509 | if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0
|
|---|
| 510 | && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0
|
|---|
| 511 | && strcmp(pszAccess,"r+b") != 0 )
|
|---|
| 512 | return( NULL );
|
|---|
| 513 |
|
|---|
| 514 | if( strcmp(pszAccess,"r") == 0 )
|
|---|
| 515 | pszAccess = "rb";
|
|---|
| 516 |
|
|---|
| 517 | if( strcmp(pszAccess,"r+") == 0 )
|
|---|
| 518 | pszAccess = "rb+";
|
|---|
| 519 |
|
|---|
| 520 | /* -------------------------------------------------------------------- */
|
|---|
| 521 | /* Compute the base (layer) name. If there is any extension */
|
|---|
| 522 | /* on the passed in filename we will strip it off. */
|
|---|
| 523 | /* -------------------------------------------------------------------- */
|
|---|
| 524 | nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
|
|---|
| 525 | pszFullname = (char *) malloc(nLenWithoutExtension + 5);
|
|---|
| 526 | memcpy(pszFullname, pszFilename, nLenWithoutExtension);
|
|---|
| 527 | memcpy(pszFullname + nLenWithoutExtension, ".dbf", 5);
|
|---|
| 528 |
|
|---|
| 529 | psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) );
|
|---|
| 530 | psDBF->fp = psHooks->FOpen( pszFullname, pszAccess );
|
|---|
| 531 | memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
|
|---|
| 532 |
|
|---|
| 533 | if( psDBF->fp == NULL )
|
|---|
| 534 | {
|
|---|
| 535 | memcpy(pszFullname + nLenWithoutExtension, ".DBF", 5);
|
|---|
| 536 | psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess );
|
|---|
| 537 | }
|
|---|
| 538 |
|
|---|
| 539 | memcpy(pszFullname + nLenWithoutExtension, ".cpg", 5);
|
|---|
| 540 | pfCPG = psHooks->FOpen( pszFullname, "r" );
|
|---|
| 541 | if( pfCPG == NULL )
|
|---|
| 542 | {
|
|---|
| 543 | memcpy(pszFullname + nLenWithoutExtension, ".CPG", 5);
|
|---|
| 544 | pfCPG = psHooks->FOpen( pszFullname, "r" );
|
|---|
| 545 | }
|
|---|
| 546 |
|
|---|
| 547 | free( pszFullname );
|
|---|
| 548 |
|
|---|
| 549 | if( psDBF->fp == NULL )
|
|---|
| 550 | {
|
|---|
| 551 | free( psDBF );
|
|---|
| 552 | if( pfCPG ) psHooks->FClose( pfCPG );
|
|---|
| 553 | return( NULL );
|
|---|
| 554 | }
|
|---|
| 555 |
|
|---|
| 556 | psDBF->bNoHeader = FALSE;
|
|---|
| 557 | psDBF->nCurrentRecord = -1;
|
|---|
| 558 | psDBF->bCurrentRecordModified = FALSE;
|
|---|
| 559 |
|
|---|
| 560 | /* -------------------------------------------------------------------- */
|
|---|
| 561 | /* Read Table Header info */
|
|---|
| 562 | /* -------------------------------------------------------------------- */
|
|---|
| 563 | pabyBuf = (unsigned char *) malloc(nBufSize);
|
|---|
| 564 | if( psDBF->sHooks.FRead( pabyBuf, XBASE_FILEHDR_SZ, 1, psDBF->fp ) != 1 )
|
|---|
| 565 | {
|
|---|
| 566 | psDBF->sHooks.FClose( psDBF->fp );
|
|---|
| 567 | if( pfCPG ) psDBF->sHooks.FClose( pfCPG );
|
|---|
| 568 | free( pabyBuf );
|
|---|
| 569 | free( psDBF );
|
|---|
| 570 | return NULL;
|
|---|
| 571 | }
|
|---|
| 572 |
|
|---|
| 573 | DBFSetLastModifiedDate(psDBF, pabyBuf[1], pabyBuf[2], pabyBuf[3]);
|
|---|
| 574 |
|
|---|
| 575 | psDBF->nRecords =
|
|---|
| 576 | pabyBuf[4]|(pabyBuf[5]<<8)|(pabyBuf[6]<<16)|((pabyBuf[7]&0x7f)<<24);
|
|---|
| 577 |
|
|---|
| 578 | psDBF->nHeaderLength = nHeadLen = pabyBuf[8]|(pabyBuf[9]<<8);
|
|---|
| 579 | psDBF->nRecordLength = pabyBuf[10]|(pabyBuf[11]<<8);
|
|---|
| 580 | psDBF->iLanguageDriver = pabyBuf[29];
|
|---|
| 581 |
|
|---|
| 582 | if (psDBF->nRecordLength == 0 || nHeadLen < XBASE_FILEHDR_SZ)
|
|---|
| 583 | {
|
|---|
| 584 | psDBF->sHooks.FClose( psDBF->fp );
|
|---|
| 585 | if( pfCPG ) psDBF->sHooks.FClose( pfCPG );
|
|---|
| 586 | free( pabyBuf );
|
|---|
| 587 | free( psDBF );
|
|---|
| 588 | return NULL;
|
|---|
| 589 | }
|
|---|
| 590 |
|
|---|
| 591 | psDBF->nFields = nFields = (nHeadLen - XBASE_FILEHDR_SZ) / XBASE_FLDHDR_SZ;
|
|---|
| 592 |
|
|---|
| 593 | /* coverity[tainted_data] */
|
|---|
| 594 | psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength);
|
|---|
| 595 |
|
|---|
| 596 | /* -------------------------------------------------------------------- */
|
|---|
| 597 | /* Figure out the code page from the LDID and CPG */
|
|---|
| 598 | /* -------------------------------------------------------------------- */
|
|---|
| 599 |
|
|---|
| 600 | psDBF->pszCodePage = NULL;
|
|---|
| 601 | if( pfCPG )
|
|---|
| 602 | {
|
|---|
| 603 | size_t n;
|
|---|
| 604 | memset( pabyBuf, 0, nBufSize);
|
|---|
| 605 | psDBF->sHooks.FRead( pabyBuf, nBufSize - 1, 1, pfCPG );
|
|---|
| 606 | n = strcspn( (char *) pabyBuf, "\n\r" );
|
|---|
| 607 | if( n > 0 )
|
|---|
| 608 | {
|
|---|
| 609 | pabyBuf[n] = '\0';
|
|---|
| 610 | psDBF->pszCodePage = (char *) malloc(n + 1);
|
|---|
| 611 | memcpy( psDBF->pszCodePage, pabyBuf, n + 1 );
|
|---|
| 612 | }
|
|---|
| 613 | psDBF->sHooks.FClose( pfCPG );
|
|---|
| 614 | }
|
|---|
| 615 | if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 )
|
|---|
| 616 | {
|
|---|
| 617 | snprintf( (char *) pabyBuf, nBufSize, "LDID/%d", psDBF->iLanguageDriver );
|
|---|
| 618 | psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1);
|
|---|
| 619 | strcpy( psDBF->pszCodePage, (char *) pabyBuf );
|
|---|
| 620 | }
|
|---|
| 621 |
|
|---|
| 622 | /* -------------------------------------------------------------------- */
|
|---|
| 623 | /* Read in Field Definitions */
|
|---|
| 624 | /* -------------------------------------------------------------------- */
|
|---|
| 625 |
|
|---|
| 626 | pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen);
|
|---|
| 627 | psDBF->pszHeader = (char *) pabyBuf;
|
|---|
| 628 |
|
|---|
| 629 | psDBF->sHooks.FSeek( psDBF->fp, XBASE_FILEHDR_SZ, 0 );
|
|---|
| 630 | if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-XBASE_FILEHDR_SZ, 1,
|
|---|
| 631 | psDBF->fp ) != 1 )
|
|---|
| 632 | {
|
|---|
| 633 | psDBF->sHooks.FClose( psDBF->fp );
|
|---|
| 634 | free( pabyBuf );
|
|---|
| 635 | free( psDBF->pszCurrentRecord );
|
|---|
| 636 | free( psDBF->pszCodePage );
|
|---|
| 637 | free( psDBF );
|
|---|
| 638 | return NULL;
|
|---|
| 639 | }
|
|---|
| 640 |
|
|---|
| 641 | psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields);
|
|---|
| 642 | psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields);
|
|---|
| 643 | psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields);
|
|---|
| 644 | psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields);
|
|---|
| 645 |
|
|---|
| 646 | for( iField = 0; iField < nFields; iField++ )
|
|---|
| 647 | {
|
|---|
| 648 | unsigned char *pabyFInfo;
|
|---|
| 649 |
|
|---|
| 650 | pabyFInfo = pabyBuf+iField*XBASE_FLDHDR_SZ;
|
|---|
| 651 | if( pabyFInfo[0] == HEADER_RECORD_TERMINATOR )
|
|---|
| 652 | {
|
|---|
| 653 | psDBF->nFields = iField;
|
|---|
| 654 | break;
|
|---|
| 655 | }
|
|---|
| 656 |
|
|---|
| 657 | if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' )
|
|---|
| 658 | {
|
|---|
| 659 | psDBF->panFieldSize[iField] = pabyFInfo[16];
|
|---|
| 660 | psDBF->panFieldDecimals[iField] = pabyFInfo[17];
|
|---|
| 661 | }
|
|---|
| 662 | else
|
|---|
| 663 | {
|
|---|
| 664 | psDBF->panFieldSize[iField] = pabyFInfo[16];
|
|---|
| 665 | psDBF->panFieldDecimals[iField] = 0;
|
|---|
| 666 |
|
|---|
| 667 | /*
|
|---|
| 668 | ** The following seemed to be used sometimes to handle files with long
|
|---|
| 669 | ** string fields, but in other cases (such as bug 1202) the decimals field
|
|---|
| 670 | ** just seems to indicate some sort of preferred formatting, not very
|
|---|
| 671 | ** wide fields. So I have disabled this code. FrankW.
|
|---|
| 672 | psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256;
|
|---|
| 673 | psDBF->panFieldDecimals[iField] = 0;
|
|---|
| 674 | */
|
|---|
| 675 | }
|
|---|
| 676 |
|
|---|
| 677 | psDBF->pachFieldType[iField] = (char) pabyFInfo[11];
|
|---|
| 678 | if( iField == 0 )
|
|---|
| 679 | psDBF->panFieldOffset[iField] = 1;
|
|---|
| 680 | else
|
|---|
| 681 | psDBF->panFieldOffset[iField] =
|
|---|
| 682 | psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1];
|
|---|
| 683 | }
|
|---|
| 684 |
|
|---|
| 685 | /* Check that the total width of fields does not exceed the record width */
|
|---|
| 686 | if( psDBF->nFields > 0 &&
|
|---|
| 687 | psDBF->panFieldOffset[psDBF->nFields-1] +
|
|---|
| 688 | psDBF->panFieldSize[psDBF->nFields-1] > psDBF->nRecordLength )
|
|---|
| 689 | {
|
|---|
| 690 | DBFClose( psDBF );
|
|---|
| 691 | return NULL;
|
|---|
| 692 | }
|
|---|
| 693 |
|
|---|
| 694 | DBFSetWriteEndOfFileChar( psDBF, TRUE );
|
|---|
| 695 |
|
|---|
| 696 | return( psDBF );
|
|---|
| 697 | }
|
|---|
| 698 |
|
|---|
| 699 | /************************************************************************/
|
|---|
| 700 | /* DBFClose() */
|
|---|
| 701 | /************************************************************************/
|
|---|
| 702 |
|
|---|
| 703 | void SHPAPI_CALL
|
|---|
| 704 | DBFClose(DBFHandle psDBF)
|
|---|
| 705 | {
|
|---|
| 706 | if( psDBF == NULL )
|
|---|
| 707 | return;
|
|---|
| 708 |
|
|---|
| 709 | /* -------------------------------------------------------------------- */
|
|---|
| 710 | /* Write out header if not already written. */
|
|---|
| 711 | /* -------------------------------------------------------------------- */
|
|---|
| 712 | if( psDBF->bNoHeader )
|
|---|
| 713 | DBFWriteHeader( psDBF );
|
|---|
| 714 |
|
|---|
| 715 | CPL_IGNORE_RET_VAL_INT(DBFFlushRecord( psDBF ));
|
|---|
| 716 |
|
|---|
| 717 | /* -------------------------------------------------------------------- */
|
|---|
| 718 | /* Update last access date, and number of records if we have */
|
|---|
| 719 | /* write access. */
|
|---|
| 720 | /* -------------------------------------------------------------------- */
|
|---|
| 721 | if( psDBF->bUpdated )
|
|---|
| 722 | DBFUpdateHeader( psDBF );
|
|---|
| 723 |
|
|---|
| 724 | /* -------------------------------------------------------------------- */
|
|---|
| 725 | /* Close, and free resources. */
|
|---|
| 726 | /* -------------------------------------------------------------------- */
|
|---|
| 727 | psDBF->sHooks.FClose( psDBF->fp );
|
|---|
| 728 |
|
|---|
| 729 | if( psDBF->panFieldOffset != NULL )
|
|---|
| 730 | {
|
|---|
| 731 | free( psDBF->panFieldOffset );
|
|---|
| 732 | free( psDBF->panFieldSize );
|
|---|
| 733 | free( psDBF->panFieldDecimals );
|
|---|
| 734 | free( psDBF->pachFieldType );
|
|---|
| 735 | }
|
|---|
| 736 |
|
|---|
| 737 | if( psDBF->pszWorkField != NULL )
|
|---|
| 738 | free( psDBF->pszWorkField );
|
|---|
| 739 |
|
|---|
| 740 | free( psDBF->pszHeader );
|
|---|
| 741 | free( psDBF->pszCurrentRecord );
|
|---|
| 742 | free( psDBF->pszCodePage );
|
|---|
| 743 |
|
|---|
| 744 | free( psDBF );
|
|---|
| 745 | }
|
|---|
| 746 |
|
|---|
| 747 | /************************************************************************/
|
|---|
| 748 | /* DBFCreate() */
|
|---|
| 749 | /* */
|
|---|
| 750 | /* Create a new .dbf file with default code page LDID/87 (0x57) */
|
|---|
| 751 | /************************************************************************/
|
|---|
| 752 |
|
|---|
| 753 | DBFHandle SHPAPI_CALL
|
|---|
| 754 | DBFCreate( const char * pszFilename )
|
|---|
| 755 |
|
|---|
| 756 | {
|
|---|
| 757 | return DBFCreateEx( pszFilename, "LDID/87" ); // 0x57
|
|---|
| 758 | }
|
|---|
| 759 |
|
|---|
| 760 | /************************************************************************/
|
|---|
| 761 | /* DBFCreateEx() */
|
|---|
| 762 | /* */
|
|---|
| 763 | /* Create a new .dbf file. */
|
|---|
| 764 | /************************************************************************/
|
|---|
| 765 |
|
|---|
| 766 | DBFHandle SHPAPI_CALL
|
|---|
| 767 | DBFCreateEx( const char * pszFilename, const char* pszCodePage )
|
|---|
| 768 |
|
|---|
| 769 | {
|
|---|
| 770 | SAHooks sHooks;
|
|---|
| 771 |
|
|---|
| 772 | SASetupDefaultHooks( &sHooks );
|
|---|
| 773 |
|
|---|
| 774 | return DBFCreateLL( pszFilename, pszCodePage , &sHooks );
|
|---|
| 775 | }
|
|---|
| 776 |
|
|---|
| 777 | /************************************************************************/
|
|---|
| 778 | /* DBFCreate() */
|
|---|
| 779 | /* */
|
|---|
| 780 | /* Create a new .dbf file. */
|
|---|
| 781 | /************************************************************************/
|
|---|
| 782 |
|
|---|
| 783 | DBFHandle SHPAPI_CALL
|
|---|
| 784 | DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHooks )
|
|---|
| 785 |
|
|---|
| 786 | {
|
|---|
| 787 | DBFHandle psDBF;
|
|---|
| 788 | SAFile fp;
|
|---|
| 789 | char *pszFullname;
|
|---|
| 790 | int ldid = -1;
|
|---|
| 791 | char chZero = '\0';
|
|---|
| 792 | int nLenWithoutExtension;
|
|---|
| 793 |
|
|---|
| 794 | /* -------------------------------------------------------------------- */
|
|---|
| 795 | /* Compute the base (layer) name. If there is any extension */
|
|---|
| 796 | /* on the passed in filename we will strip it off. */
|
|---|
| 797 | /* -------------------------------------------------------------------- */
|
|---|
| 798 | nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
|
|---|
| 799 | pszFullname = (char *) malloc(nLenWithoutExtension + 5);
|
|---|
| 800 | memcpy(pszFullname, pszFilename, nLenWithoutExtension);
|
|---|
| 801 | memcpy(pszFullname + nLenWithoutExtension, ".dbf", 5);
|
|---|
| 802 |
|
|---|
| 803 | /* -------------------------------------------------------------------- */
|
|---|
| 804 | /* Create the file. */
|
|---|
| 805 | /* -------------------------------------------------------------------- */
|
|---|
| 806 | fp = psHooks->FOpen( pszFullname, "wb" );
|
|---|
| 807 | if( fp == NULL )
|
|---|
| 808 | {
|
|---|
| 809 | free( pszFullname );
|
|---|
| 810 | return( NULL );
|
|---|
| 811 | }
|
|---|
| 812 |
|
|---|
| 813 | psHooks->FWrite( &chZero, 1, 1, fp );
|
|---|
| 814 | psHooks->FClose( fp );
|
|---|
| 815 |
|
|---|
| 816 | fp = psHooks->FOpen( pszFullname, "rb+" );
|
|---|
| 817 | if( fp == NULL )
|
|---|
| 818 | {
|
|---|
| 819 | free( pszFullname );
|
|---|
| 820 | return( NULL );
|
|---|
| 821 | }
|
|---|
| 822 |
|
|---|
| 823 | memcpy(pszFullname + nLenWithoutExtension, ".cpg", 5);
|
|---|
| 824 | if( pszCodePage != NULL )
|
|---|
| 825 | {
|
|---|
| 826 | if( strncmp( pszCodePage, "LDID/", 5 ) == 0 )
|
|---|
| 827 | {
|
|---|
| 828 | ldid = atoi( pszCodePage + 5 );
|
|---|
| 829 | if( ldid > 255 )
|
|---|
| 830 | ldid = -1; // don't use 0 to indicate out of range as LDID/0 is a valid one
|
|---|
| 831 | }
|
|---|
| 832 | if( ldid < 0 )
|
|---|
| 833 | {
|
|---|
| 834 | SAFile fpCPG = psHooks->FOpen( pszFullname, "w" );
|
|---|
| 835 | psHooks->FWrite( (char*) pszCodePage, strlen(pszCodePage), 1, fpCPG );
|
|---|
| 836 | psHooks->FClose( fpCPG );
|
|---|
| 837 | }
|
|---|
| 838 | }
|
|---|
| 839 | if( pszCodePage == NULL || ldid >= 0 )
|
|---|
| 840 | {
|
|---|
| 841 | psHooks->Remove( pszFullname );
|
|---|
| 842 | }
|
|---|
| 843 |
|
|---|
| 844 | free( pszFullname );
|
|---|
| 845 |
|
|---|
| 846 | /* -------------------------------------------------------------------- */
|
|---|
| 847 | /* Create the info structure. */
|
|---|
| 848 | /* -------------------------------------------------------------------- */
|
|---|
| 849 | psDBF = (DBFHandle) calloc(1,sizeof(DBFInfo));
|
|---|
| 850 |
|
|---|
| 851 | memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
|
|---|
| 852 | psDBF->fp = fp;
|
|---|
| 853 | psDBF->nRecords = 0;
|
|---|
| 854 | psDBF->nFields = 0;
|
|---|
| 855 | psDBF->nRecordLength = 1;
|
|---|
| 856 | psDBF->nHeaderLength = XBASE_FILEHDR_SZ + 1; /* + 1 for HEADER_RECORD_TERMINATOR */
|
|---|
| 857 |
|
|---|
| 858 | psDBF->panFieldOffset = NULL;
|
|---|
| 859 | psDBF->panFieldSize = NULL;
|
|---|
| 860 | psDBF->panFieldDecimals = NULL;
|
|---|
| 861 | psDBF->pachFieldType = NULL;
|
|---|
| 862 | psDBF->pszHeader = NULL;
|
|---|
| 863 |
|
|---|
| 864 | psDBF->nCurrentRecord = -1;
|
|---|
| 865 | psDBF->bCurrentRecordModified = FALSE;
|
|---|
| 866 | psDBF->pszCurrentRecord = NULL;
|
|---|
| 867 |
|
|---|
| 868 | psDBF->bNoHeader = TRUE;
|
|---|
| 869 |
|
|---|
| 870 | psDBF->iLanguageDriver = ldid > 0 ? ldid : 0;
|
|---|
| 871 | psDBF->pszCodePage = NULL;
|
|---|
| 872 | if( pszCodePage )
|
|---|
| 873 | {
|
|---|
| 874 | psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 );
|
|---|
| 875 | strcpy( psDBF->pszCodePage, pszCodePage );
|
|---|
| 876 | }
|
|---|
| 877 | DBFSetLastModifiedDate(psDBF, 95, 7, 26); /* dummy date */
|
|---|
| 878 |
|
|---|
| 879 | DBFSetWriteEndOfFileChar(psDBF, TRUE);
|
|---|
| 880 |
|
|---|
| 881 | return( psDBF );
|
|---|
| 882 | }
|
|---|
| 883 |
|
|---|
| 884 | /************************************************************************/
|
|---|
| 885 | /* DBFAddField() */
|
|---|
| 886 | /* */
|
|---|
| 887 | /* Add a field to a newly created .dbf or to an existing one */
|
|---|
| 888 | /************************************************************************/
|
|---|
| 889 |
|
|---|
| 890 | int SHPAPI_CALL
|
|---|
| 891 | DBFAddField(DBFHandle psDBF, const char * pszFieldName,
|
|---|
| 892 | DBFFieldType eType, int nWidth, int nDecimals )
|
|---|
| 893 |
|
|---|
| 894 | {
|
|---|
| 895 | char chNativeType = 'C';
|
|---|
| 896 |
|
|---|
| 897 | if( eType == FTLogical )
|
|---|
| 898 | chNativeType = 'L';
|
|---|
| 899 | else if( eType == FTDate )
|
|---|
| 900 | chNativeType = 'D';
|
|---|
| 901 | else if( eType == FTString )
|
|---|
| 902 | chNativeType = 'C';
|
|---|
| 903 | else
|
|---|
| 904 | chNativeType = 'N';
|
|---|
| 905 |
|
|---|
| 906 | return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType,
|
|---|
| 907 | nWidth, nDecimals );
|
|---|
| 908 | }
|
|---|
| 909 |
|
|---|
| 910 | /************************************************************************/
|
|---|
| 911 | /* DBFGetNullCharacter() */
|
|---|
| 912 | /************************************************************************/
|
|---|
| 913 |
|
|---|
| 914 | static char DBFGetNullCharacter(char chType)
|
|---|
| 915 | {
|
|---|
| 916 | switch (chType)
|
|---|
| 917 | {
|
|---|
| 918 | case 'N':
|
|---|
| 919 | case 'F':
|
|---|
| 920 | return '*';
|
|---|
| 921 | case 'D':
|
|---|
| 922 | return '0';
|
|---|
| 923 | case 'L':
|
|---|
| 924 | return '?';
|
|---|
| 925 | default:
|
|---|
| 926 | return ' ';
|
|---|
| 927 | }
|
|---|
| 928 | }
|
|---|
| 929 |
|
|---|
| 930 | /************************************************************************/
|
|---|
| 931 | /* DBFAddField() */
|
|---|
| 932 | /* */
|
|---|
| 933 | /* Add a field to a newly created .dbf file before any records */
|
|---|
| 934 | /* are written. */
|
|---|
| 935 | /************************************************************************/
|
|---|
| 936 |
|
|---|
| 937 | int SHPAPI_CALL
|
|---|
| 938 | DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName,
|
|---|
| 939 | char chType, int nWidth, int nDecimals )
|
|---|
| 940 |
|
|---|
| 941 | {
|
|---|
| 942 | char *pszFInfo;
|
|---|
| 943 | int i;
|
|---|
| 944 | int nOldRecordLength, nOldHeaderLength;
|
|---|
| 945 | char *pszRecord;
|
|---|
| 946 | char chFieldFill;
|
|---|
| 947 | SAOffset nRecordOffset;
|
|---|
| 948 |
|
|---|
| 949 | /* make sure that everything is written in .dbf */
|
|---|
| 950 | if( !DBFFlushRecord( psDBF ) )
|
|---|
| 951 | return -1;
|
|---|
| 952 |
|
|---|
| 953 | if( psDBF->nHeaderLength + XBASE_FLDHDR_SZ > 65535 )
|
|---|
| 954 | {
|
|---|
| 955 | char szMessage[128];
|
|---|
| 956 | snprintf( szMessage, sizeof(szMessage),
|
|---|
| 957 | "Cannot add field %s. Header length limit reached "
|
|---|
| 958 | "(max 65535 bytes, 2046 fields).",
|
|---|
| 959 | pszFieldName );
|
|---|
| 960 | psDBF->sHooks.Error( szMessage );
|
|---|
| 961 | return -1;
|
|---|
| 962 | }
|
|---|
| 963 |
|
|---|
| 964 | /* -------------------------------------------------------------------- */
|
|---|
| 965 | /* Do some checking to ensure we can add records to this file. */
|
|---|
| 966 | /* -------------------------------------------------------------------- */
|
|---|
| 967 | if( nWidth < 1 )
|
|---|
| 968 | return -1;
|
|---|
| 969 |
|
|---|
| 970 | if( nWidth > XBASE_FLD_MAX_WIDTH )
|
|---|
| 971 | nWidth = XBASE_FLD_MAX_WIDTH;
|
|---|
| 972 |
|
|---|
| 973 | if( psDBF->nRecordLength + nWidth > 65535 )
|
|---|
| 974 | {
|
|---|
| 975 | char szMessage[128];
|
|---|
| 976 | snprintf( szMessage, sizeof(szMessage),
|
|---|
| 977 | "Cannot add field %s. Record length limit reached "
|
|---|
| 978 | "(max 65535 bytes).",
|
|---|
| 979 | pszFieldName );
|
|---|
| 980 | psDBF->sHooks.Error( szMessage );
|
|---|
| 981 | return -1;
|
|---|
| 982 | }
|
|---|
| 983 |
|
|---|
| 984 | nOldRecordLength = psDBF->nRecordLength;
|
|---|
| 985 | nOldHeaderLength = psDBF->nHeaderLength;
|
|---|
| 986 |
|
|---|
| 987 | /* -------------------------------------------------------------------- */
|
|---|
| 988 | /* SfRealloc all the arrays larger to hold the additional field */
|
|---|
| 989 | /* information. */
|
|---|
| 990 | /* -------------------------------------------------------------------- */
|
|---|
| 991 | psDBF->nFields++;
|
|---|
| 992 |
|
|---|
| 993 | psDBF->panFieldOffset = (int *)
|
|---|
| 994 | SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
|
|---|
| 995 |
|
|---|
| 996 | psDBF->panFieldSize = (int *)
|
|---|
| 997 | SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
|
|---|
| 998 |
|
|---|
| 999 | psDBF->panFieldDecimals = (int *)
|
|---|
| 1000 | SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
|
|---|
| 1001 |
|
|---|
| 1002 | psDBF->pachFieldType = (char *)
|
|---|
| 1003 | SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
|
|---|
| 1004 |
|
|---|
| 1005 | /* -------------------------------------------------------------------- */
|
|---|
| 1006 | /* Assign the new field information fields. */
|
|---|
| 1007 | /* -------------------------------------------------------------------- */
|
|---|
| 1008 | psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength;
|
|---|
| 1009 | psDBF->nRecordLength += nWidth;
|
|---|
| 1010 | psDBF->panFieldSize[psDBF->nFields-1] = nWidth;
|
|---|
| 1011 | psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals;
|
|---|
| 1012 | psDBF->pachFieldType[psDBF->nFields-1] = chType;
|
|---|
| 1013 |
|
|---|
| 1014 | /* -------------------------------------------------------------------- */
|
|---|
| 1015 | /* Extend the required header information. */
|
|---|
| 1016 | /* -------------------------------------------------------------------- */
|
|---|
| 1017 | psDBF->nHeaderLength += XBASE_FLDHDR_SZ;
|
|---|
| 1018 | psDBF->bUpdated = FALSE;
|
|---|
| 1019 |
|
|---|
| 1020 | psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,
|
|---|
| 1021 | psDBF->nFields*XBASE_FLDHDR_SZ);
|
|---|
| 1022 |
|
|---|
| 1023 | pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * (psDBF->nFields-1);
|
|---|
| 1024 |
|
|---|
| 1025 | for( i = 0; i < XBASE_FLDHDR_SZ; i++ )
|
|---|
| 1026 | pszFInfo[i] = '\0';
|
|---|
| 1027 |
|
|---|
| 1028 | strncpy( pszFInfo, pszFieldName, XBASE_FLDNAME_LEN_WRITE );
|
|---|
| 1029 |
|
|---|
| 1030 | pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1];
|
|---|
| 1031 |
|
|---|
| 1032 | if( chType == 'C' )
|
|---|
| 1033 | {
|
|---|
| 1034 | pszFInfo[16] = (unsigned char) (nWidth % 256);
|
|---|
| 1035 | pszFInfo[17] = (unsigned char) (nWidth / 256);
|
|---|
| 1036 | }
|
|---|
| 1037 | else
|
|---|
| 1038 | {
|
|---|
| 1039 | pszFInfo[16] = (unsigned char) nWidth;
|
|---|
| 1040 | pszFInfo[17] = (unsigned char) nDecimals;
|
|---|
| 1041 | }
|
|---|
| 1042 |
|
|---|
| 1043 | /* -------------------------------------------------------------------- */
|
|---|
| 1044 | /* Make the current record buffer appropriately larger. */
|
|---|
| 1045 | /* -------------------------------------------------------------------- */
|
|---|
| 1046 | psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
|
|---|
| 1047 | psDBF->nRecordLength);
|
|---|
| 1048 |
|
|---|
| 1049 | /* we're done if dealing with new .dbf */
|
|---|
| 1050 | if( psDBF->bNoHeader )
|
|---|
| 1051 | return( psDBF->nFields - 1 );
|
|---|
| 1052 |
|
|---|
| 1053 | /* -------------------------------------------------------------------- */
|
|---|
| 1054 | /* For existing .dbf file, shift records */
|
|---|
| 1055 | /* -------------------------------------------------------------------- */
|
|---|
| 1056 |
|
|---|
| 1057 | /* alloc record */
|
|---|
| 1058 | pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
|
|---|
| 1059 |
|
|---|
| 1060 | chFieldFill = DBFGetNullCharacter(chType);
|
|---|
| 1061 |
|
|---|
| 1062 | for (i = psDBF->nRecords-1; i >= 0; --i)
|
|---|
| 1063 | {
|
|---|
| 1064 | nRecordOffset = nOldRecordLength * (SAOffset) i + nOldHeaderLength;
|
|---|
| 1065 |
|
|---|
| 1066 | /* load record */
|
|---|
| 1067 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 1068 | psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
|
|---|
| 1069 |
|
|---|
| 1070 | /* set new field's value to NULL */
|
|---|
| 1071 | memset(pszRecord + nOldRecordLength, chFieldFill, nWidth);
|
|---|
| 1072 |
|
|---|
| 1073 | nRecordOffset = psDBF->nRecordLength * (SAOffset) i + psDBF->nHeaderLength;
|
|---|
| 1074 |
|
|---|
| 1075 | /* move record to the new place*/
|
|---|
| 1076 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 1077 | psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
|
|---|
| 1078 | }
|
|---|
| 1079 |
|
|---|
| 1080 | if( psDBF->bWriteEndOfFileChar )
|
|---|
| 1081 | {
|
|---|
| 1082 | char ch = END_OF_FILE_CHARACTER;
|
|---|
| 1083 |
|
|---|
| 1084 | nRecordOffset =
|
|---|
| 1085 | psDBF->nRecordLength * (SAOffset) psDBF->nRecords + psDBF->nHeaderLength;
|
|---|
| 1086 |
|
|---|
| 1087 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 1088 | psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp );
|
|---|
| 1089 | }
|
|---|
| 1090 |
|
|---|
| 1091 | /* free record */
|
|---|
| 1092 | free(pszRecord);
|
|---|
| 1093 |
|
|---|
| 1094 | /* force update of header with new header, record length and new field */
|
|---|
| 1095 | psDBF->bNoHeader = TRUE;
|
|---|
| 1096 | DBFUpdateHeader( psDBF );
|
|---|
| 1097 |
|
|---|
| 1098 | psDBF->nCurrentRecord = -1;
|
|---|
| 1099 | psDBF->bCurrentRecordModified = FALSE;
|
|---|
| 1100 | psDBF->bUpdated = TRUE;
|
|---|
| 1101 |
|
|---|
| 1102 | return( psDBF->nFields-1 );
|
|---|
| 1103 | }
|
|---|
| 1104 |
|
|---|
| 1105 | /************************************************************************/
|
|---|
| 1106 | /* DBFReadAttribute() */
|
|---|
| 1107 | /* */
|
|---|
| 1108 | /* Read one of the attribute fields of a record. */
|
|---|
| 1109 | /************************************************************************/
|
|---|
| 1110 |
|
|---|
| 1111 | static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField,
|
|---|
| 1112 | char chReqType )
|
|---|
| 1113 |
|
|---|
| 1114 | {
|
|---|
| 1115 | unsigned char *pabyRec;
|
|---|
| 1116 | void *pReturnField = NULL;
|
|---|
| 1117 |
|
|---|
| 1118 | /* -------------------------------------------------------------------- */
|
|---|
| 1119 | /* Verify selection. */
|
|---|
| 1120 | /* -------------------------------------------------------------------- */
|
|---|
| 1121 | if( hEntity < 0 || hEntity >= psDBF->nRecords )
|
|---|
| 1122 | return( NULL );
|
|---|
| 1123 |
|
|---|
| 1124 | if( iField < 0 || iField >= psDBF->nFields )
|
|---|
| 1125 | return( NULL );
|
|---|
| 1126 |
|
|---|
| 1127 | /* -------------------------------------------------------------------- */
|
|---|
| 1128 | /* Have we read the record? */
|
|---|
| 1129 | /* -------------------------------------------------------------------- */
|
|---|
| 1130 | if( !DBFLoadRecord( psDBF, hEntity ) )
|
|---|
| 1131 | return NULL;
|
|---|
| 1132 |
|
|---|
| 1133 | pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
|
|---|
| 1134 |
|
|---|
| 1135 | /* -------------------------------------------------------------------- */
|
|---|
| 1136 | /* Ensure we have room to extract the target field. */
|
|---|
| 1137 | /* -------------------------------------------------------------------- */
|
|---|
| 1138 | if( psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength )
|
|---|
| 1139 | {
|
|---|
| 1140 | psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100;
|
|---|
| 1141 | if( psDBF->pszWorkField == NULL )
|
|---|
| 1142 | psDBF->pszWorkField = (char *) malloc(psDBF->nWorkFieldLength);
|
|---|
| 1143 | else
|
|---|
| 1144 | psDBF->pszWorkField = (char *) realloc(psDBF->pszWorkField,
|
|---|
| 1145 | psDBF->nWorkFieldLength);
|
|---|
| 1146 | }
|
|---|
| 1147 |
|
|---|
| 1148 | /* -------------------------------------------------------------------- */
|
|---|
| 1149 | /* Extract the requested field. */
|
|---|
| 1150 | /* -------------------------------------------------------------------- */
|
|---|
| 1151 | memcpy( psDBF->pszWorkField,
|
|---|
| 1152 | ((const char *) pabyRec) + psDBF->panFieldOffset[iField],
|
|---|
| 1153 | psDBF->panFieldSize[iField] );
|
|---|
| 1154 | psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0';
|
|---|
| 1155 |
|
|---|
| 1156 | pReturnField = psDBF->pszWorkField;
|
|---|
| 1157 |
|
|---|
| 1158 | /* -------------------------------------------------------------------- */
|
|---|
| 1159 | /* Decode the field. */
|
|---|
| 1160 | /* -------------------------------------------------------------------- */
|
|---|
| 1161 | if( chReqType == 'I' )
|
|---|
| 1162 | {
|
|---|
| 1163 | psDBF->fieldValue.nIntField = atoi(psDBF->pszWorkField);
|
|---|
| 1164 |
|
|---|
| 1165 | pReturnField = &(psDBF->fieldValue.nIntField);
|
|---|
| 1166 | }
|
|---|
| 1167 | else if( chReqType == 'N' )
|
|---|
| 1168 | {
|
|---|
| 1169 | psDBF->fieldValue.dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField);
|
|---|
| 1170 |
|
|---|
| 1171 | pReturnField = &(psDBF->fieldValue.dfDoubleField);
|
|---|
| 1172 | }
|
|---|
| 1173 |
|
|---|
| 1174 | /* -------------------------------------------------------------------- */
|
|---|
| 1175 | /* Should we trim white space off the string attribute value? */
|
|---|
| 1176 | /* -------------------------------------------------------------------- */
|
|---|
| 1177 | #ifdef TRIM_DBF_WHITESPACE
|
|---|
| 1178 | else
|
|---|
| 1179 | {
|
|---|
| 1180 | char *pchSrc, *pchDst;
|
|---|
| 1181 |
|
|---|
| 1182 | pchDst = pchSrc = psDBF->pszWorkField;
|
|---|
| 1183 | while( *pchSrc == ' ' )
|
|---|
| 1184 | pchSrc++;
|
|---|
| 1185 |
|
|---|
| 1186 | while( *pchSrc != '\0' )
|
|---|
| 1187 | *(pchDst++) = *(pchSrc++);
|
|---|
| 1188 | *pchDst = '\0';
|
|---|
| 1189 |
|
|---|
| 1190 | while( pchDst != psDBF->pszWorkField && *(--pchDst) == ' ' )
|
|---|
| 1191 | *pchDst = '\0';
|
|---|
| 1192 | }
|
|---|
| 1193 | #endif
|
|---|
| 1194 |
|
|---|
| 1195 | return( pReturnField );
|
|---|
| 1196 | }
|
|---|
| 1197 |
|
|---|
| 1198 | /************************************************************************/
|
|---|
| 1199 | /* DBFReadIntAttribute() */
|
|---|
| 1200 | /* */
|
|---|
| 1201 | /* Read an integer attribute. */
|
|---|
| 1202 | /************************************************************************/
|
|---|
| 1203 |
|
|---|
| 1204 | int SHPAPI_CALL
|
|---|
| 1205 | DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField )
|
|---|
| 1206 |
|
|---|
| 1207 | {
|
|---|
| 1208 | int *pnValue;
|
|---|
| 1209 |
|
|---|
| 1210 | pnValue = (int *) DBFReadAttribute( psDBF, iRecord, iField, 'I' );
|
|---|
| 1211 |
|
|---|
| 1212 | if( pnValue == NULL )
|
|---|
| 1213 | return 0;
|
|---|
| 1214 | else
|
|---|
| 1215 | return( *pnValue );
|
|---|
| 1216 | }
|
|---|
| 1217 |
|
|---|
| 1218 | /************************************************************************/
|
|---|
| 1219 | /* DBFReadDoubleAttribute() */
|
|---|
| 1220 | /* */
|
|---|
| 1221 | /* Read a double attribute. */
|
|---|
| 1222 | /************************************************************************/
|
|---|
| 1223 |
|
|---|
| 1224 | double SHPAPI_CALL
|
|---|
| 1225 | DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField )
|
|---|
| 1226 |
|
|---|
| 1227 | {
|
|---|
| 1228 | double *pdValue;
|
|---|
| 1229 |
|
|---|
| 1230 | pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
|
|---|
| 1231 |
|
|---|
| 1232 | if( pdValue == NULL )
|
|---|
| 1233 | return 0.0;
|
|---|
| 1234 | else
|
|---|
| 1235 | return( *pdValue );
|
|---|
| 1236 | }
|
|---|
| 1237 |
|
|---|
| 1238 | /************************************************************************/
|
|---|
| 1239 | /* DBFReadStringAttribute() */
|
|---|
| 1240 | /* */
|
|---|
| 1241 | /* Read a string attribute. */
|
|---|
| 1242 | /************************************************************************/
|
|---|
| 1243 |
|
|---|
| 1244 | const char SHPAPI_CALL1(*)
|
|---|
| 1245 | DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField )
|
|---|
| 1246 |
|
|---|
| 1247 | {
|
|---|
| 1248 | return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) );
|
|---|
| 1249 | }
|
|---|
| 1250 |
|
|---|
| 1251 | /************************************************************************/
|
|---|
| 1252 | /* DBFReadLogicalAttribute() */
|
|---|
| 1253 | /* */
|
|---|
| 1254 | /* Read a logical attribute. */
|
|---|
| 1255 | /************************************************************************/
|
|---|
| 1256 |
|
|---|
| 1257 | const char SHPAPI_CALL1(*)
|
|---|
| 1258 | DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField )
|
|---|
| 1259 |
|
|---|
| 1260 | {
|
|---|
| 1261 | return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) );
|
|---|
| 1262 | }
|
|---|
| 1263 |
|
|---|
| 1264 |
|
|---|
| 1265 | /************************************************************************/
|
|---|
| 1266 | /* DBFIsValueNULL() */
|
|---|
| 1267 | /* */
|
|---|
| 1268 | /* Return TRUE if the passed string is NULL. */
|
|---|
| 1269 | /************************************************************************/
|
|---|
| 1270 |
|
|---|
| 1271 | static int DBFIsValueNULL( char chType, const char* pszValue )
|
|---|
| 1272 | {
|
|---|
| 1273 | int i;
|
|---|
| 1274 |
|
|---|
| 1275 | if( pszValue == NULL )
|
|---|
| 1276 | return TRUE;
|
|---|
| 1277 |
|
|---|
| 1278 | switch(chType)
|
|---|
| 1279 | {
|
|---|
| 1280 | case 'N':
|
|---|
| 1281 | case 'F':
|
|---|
| 1282 | /*
|
|---|
| 1283 | ** We accept all asterisks or all blanks as NULL
|
|---|
| 1284 | ** though according to the spec I think it should be all
|
|---|
| 1285 | ** asterisks.
|
|---|
| 1286 | */
|
|---|
| 1287 | if( pszValue[0] == '*' )
|
|---|
| 1288 | return TRUE;
|
|---|
| 1289 |
|
|---|
| 1290 | for( i = 0; pszValue[i] != '\0'; i++ )
|
|---|
| 1291 | {
|
|---|
| 1292 | if( pszValue[i] != ' ' )
|
|---|
| 1293 | return FALSE;
|
|---|
| 1294 | }
|
|---|
| 1295 | return TRUE;
|
|---|
| 1296 |
|
|---|
| 1297 | case 'D':
|
|---|
| 1298 | /* NULL date fields have value "00000000" */
|
|---|
| 1299 | return strncmp(pszValue,"00000000",8) == 0;
|
|---|
| 1300 |
|
|---|
| 1301 | case 'L':
|
|---|
| 1302 | /* NULL boolean fields have value "?" */
|
|---|
| 1303 | return pszValue[0] == '?';
|
|---|
| 1304 |
|
|---|
| 1305 | default:
|
|---|
| 1306 | /* empty string fields are considered NULL */
|
|---|
| 1307 | return strlen(pszValue) == 0;
|
|---|
| 1308 | }
|
|---|
| 1309 | }
|
|---|
| 1310 |
|
|---|
| 1311 | /************************************************************************/
|
|---|
| 1312 | /* DBFIsAttributeNULL() */
|
|---|
| 1313 | /* */
|
|---|
| 1314 | /* Return TRUE if value for field is NULL. */
|
|---|
| 1315 | /* */
|
|---|
| 1316 | /* Contributed by Jim Matthews. */
|
|---|
| 1317 | /************************************************************************/
|
|---|
| 1318 |
|
|---|
| 1319 | int SHPAPI_CALL
|
|---|
| 1320 | DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField )
|
|---|
| 1321 |
|
|---|
| 1322 | {
|
|---|
| 1323 | const char *pszValue;
|
|---|
| 1324 |
|
|---|
| 1325 | pszValue = DBFReadStringAttribute( psDBF, iRecord, iField );
|
|---|
| 1326 |
|
|---|
| 1327 | if( pszValue == NULL )
|
|---|
| 1328 | return TRUE;
|
|---|
| 1329 |
|
|---|
| 1330 | return DBFIsValueNULL( psDBF->pachFieldType[iField], pszValue );
|
|---|
| 1331 | }
|
|---|
| 1332 |
|
|---|
| 1333 | /************************************************************************/
|
|---|
| 1334 | /* DBFGetFieldCount() */
|
|---|
| 1335 | /* */
|
|---|
| 1336 | /* Return the number of fields in this table. */
|
|---|
| 1337 | /************************************************************************/
|
|---|
| 1338 |
|
|---|
| 1339 | int SHPAPI_CALL
|
|---|
| 1340 | DBFGetFieldCount( DBFHandle psDBF )
|
|---|
| 1341 |
|
|---|
| 1342 | {
|
|---|
| 1343 | return( psDBF->nFields );
|
|---|
| 1344 | }
|
|---|
| 1345 |
|
|---|
| 1346 | /************************************************************************/
|
|---|
| 1347 | /* DBFGetRecordCount() */
|
|---|
| 1348 | /* */
|
|---|
| 1349 | /* Return the number of records in this table. */
|
|---|
| 1350 | /************************************************************************/
|
|---|
| 1351 |
|
|---|
| 1352 | int SHPAPI_CALL
|
|---|
| 1353 | DBFGetRecordCount( DBFHandle psDBF )
|
|---|
| 1354 |
|
|---|
| 1355 | {
|
|---|
| 1356 | return( psDBF->nRecords );
|
|---|
| 1357 | }
|
|---|
| 1358 |
|
|---|
| 1359 | /************************************************************************/
|
|---|
| 1360 | /* DBFGetFieldInfo() */
|
|---|
| 1361 | /* */
|
|---|
| 1362 | /* Return any requested information about the field. */
|
|---|
| 1363 | /* pszFieldName must be at least XBASE_FLDNAME_LEN_READ+1 (=12) */
|
|---|
| 1364 | /* bytes long. */
|
|---|
| 1365 | /************************************************************************/
|
|---|
| 1366 |
|
|---|
| 1367 | DBFFieldType SHPAPI_CALL
|
|---|
| 1368 | DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName,
|
|---|
| 1369 | int * pnWidth, int * pnDecimals )
|
|---|
| 1370 |
|
|---|
| 1371 | {
|
|---|
| 1372 | if( iField < 0 || iField >= psDBF->nFields )
|
|---|
| 1373 | return( FTInvalid );
|
|---|
| 1374 |
|
|---|
| 1375 | if( pnWidth != NULL )
|
|---|
| 1376 | *pnWidth = psDBF->panFieldSize[iField];
|
|---|
| 1377 |
|
|---|
| 1378 | if( pnDecimals != NULL )
|
|---|
| 1379 | *pnDecimals = psDBF->panFieldDecimals[iField];
|
|---|
| 1380 |
|
|---|
| 1381 | if( pszFieldName != NULL )
|
|---|
| 1382 | {
|
|---|
| 1383 | int i;
|
|---|
| 1384 |
|
|---|
| 1385 | strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*XBASE_FLDHDR_SZ,
|
|---|
| 1386 | XBASE_FLDNAME_LEN_READ );
|
|---|
| 1387 | pszFieldName[XBASE_FLDNAME_LEN_READ] = '\0';
|
|---|
| 1388 | for( i = XBASE_FLDNAME_LEN_READ - 1; i > 0 && pszFieldName[i] == ' '; i-- )
|
|---|
| 1389 | pszFieldName[i] = '\0';
|
|---|
| 1390 | }
|
|---|
| 1391 |
|
|---|
| 1392 | if ( psDBF->pachFieldType[iField] == 'L' )
|
|---|
| 1393 | return( FTLogical );
|
|---|
| 1394 |
|
|---|
| 1395 | else if( psDBF->pachFieldType[iField] == 'D' )
|
|---|
| 1396 | return( FTDate );
|
|---|
| 1397 |
|
|---|
| 1398 | else if( psDBF->pachFieldType[iField] == 'N'
|
|---|
| 1399 | || psDBF->pachFieldType[iField] == 'F' )
|
|---|
| 1400 | {
|
|---|
| 1401 | if( psDBF->panFieldDecimals[iField] > 0
|
|---|
| 1402 | || psDBF->panFieldSize[iField] >= 10 )
|
|---|
| 1403 | return( FTDouble );
|
|---|
| 1404 | else
|
|---|
| 1405 | return( FTInteger );
|
|---|
| 1406 | }
|
|---|
| 1407 | else
|
|---|
| 1408 | {
|
|---|
| 1409 | return( FTString );
|
|---|
| 1410 | }
|
|---|
| 1411 | }
|
|---|
| 1412 |
|
|---|
| 1413 | /************************************************************************/
|
|---|
| 1414 | /* DBFWriteAttribute() */
|
|---|
| 1415 | /* */
|
|---|
| 1416 | /* Write an attribute record to the file. */
|
|---|
| 1417 | /************************************************************************/
|
|---|
| 1418 |
|
|---|
| 1419 | static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
|
|---|
| 1420 | void * pValue )
|
|---|
| 1421 |
|
|---|
| 1422 | {
|
|---|
| 1423 | int i, j, nRetResult = TRUE;
|
|---|
| 1424 | unsigned char *pabyRec;
|
|---|
| 1425 | char szSField[XBASE_FLD_MAX_WIDTH+1], szFormat[20];
|
|---|
| 1426 |
|
|---|
| 1427 | /* -------------------------------------------------------------------- */
|
|---|
| 1428 | /* Is this a valid record? */
|
|---|
| 1429 | /* -------------------------------------------------------------------- */
|
|---|
| 1430 | if( hEntity < 0 || hEntity > psDBF->nRecords )
|
|---|
| 1431 | return( FALSE );
|
|---|
| 1432 |
|
|---|
| 1433 | if( psDBF->bNoHeader )
|
|---|
| 1434 | DBFWriteHeader(psDBF);
|
|---|
| 1435 |
|
|---|
| 1436 | /* -------------------------------------------------------------------- */
|
|---|
| 1437 | /* Is this a brand new record? */
|
|---|
| 1438 | /* -------------------------------------------------------------------- */
|
|---|
| 1439 | if( hEntity == psDBF->nRecords )
|
|---|
| 1440 | {
|
|---|
| 1441 | if( !DBFFlushRecord( psDBF ) )
|
|---|
| 1442 | return FALSE;
|
|---|
| 1443 |
|
|---|
| 1444 | psDBF->nRecords++;
|
|---|
| 1445 | for( i = 0; i < psDBF->nRecordLength; i++ )
|
|---|
| 1446 | psDBF->pszCurrentRecord[i] = ' ';
|
|---|
| 1447 |
|
|---|
| 1448 | psDBF->nCurrentRecord = hEntity;
|
|---|
| 1449 | }
|
|---|
| 1450 |
|
|---|
| 1451 | /* -------------------------------------------------------------------- */
|
|---|
| 1452 | /* Is this an existing record, but different than the last one */
|
|---|
| 1453 | /* we accessed? */
|
|---|
| 1454 | /* -------------------------------------------------------------------- */
|
|---|
| 1455 | if( !DBFLoadRecord( psDBF, hEntity ) )
|
|---|
| 1456 | return FALSE;
|
|---|
| 1457 |
|
|---|
| 1458 | pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
|
|---|
| 1459 |
|
|---|
| 1460 | psDBF->bCurrentRecordModified = TRUE;
|
|---|
| 1461 | psDBF->bUpdated = TRUE;
|
|---|
| 1462 |
|
|---|
| 1463 | /* -------------------------------------------------------------------- */
|
|---|
| 1464 | /* Translate NULL value to valid DBF file representation. */
|
|---|
| 1465 | /* */
|
|---|
| 1466 | /* Contributed by Jim Matthews. */
|
|---|
| 1467 | /* -------------------------------------------------------------------- */
|
|---|
| 1468 | if( pValue == NULL )
|
|---|
| 1469 | {
|
|---|
| 1470 | memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]),
|
|---|
| 1471 | DBFGetNullCharacter(psDBF->pachFieldType[iField]),
|
|---|
| 1472 | psDBF->panFieldSize[iField] );
|
|---|
| 1473 | return TRUE;
|
|---|
| 1474 | }
|
|---|
| 1475 |
|
|---|
| 1476 | /* -------------------------------------------------------------------- */
|
|---|
| 1477 | /* Assign all the record fields. */
|
|---|
| 1478 | /* -------------------------------------------------------------------- */
|
|---|
| 1479 | switch( psDBF->pachFieldType[iField] )
|
|---|
| 1480 | {
|
|---|
| 1481 | case 'D':
|
|---|
| 1482 | case 'N':
|
|---|
| 1483 | case 'F':
|
|---|
| 1484 | {
|
|---|
| 1485 | int nWidth = psDBF->panFieldSize[iField];
|
|---|
| 1486 |
|
|---|
| 1487 | if( (int) sizeof(szSField)-2 < nWidth )
|
|---|
| 1488 | nWidth = sizeof(szSField)-2;
|
|---|
| 1489 |
|
|---|
| 1490 | snprintf( szFormat, sizeof(szFormat), "%%%d.%df",
|
|---|
| 1491 | nWidth, psDBF->panFieldDecimals[iField] );
|
|---|
| 1492 | CPLsnprintf(szSField, sizeof(szSField), szFormat, *((double *) pValue) );
|
|---|
| 1493 | szSField[sizeof(szSField)-1] = '\0';
|
|---|
| 1494 | if( (int) strlen(szSField) > psDBF->panFieldSize[iField] )
|
|---|
| 1495 | {
|
|---|
| 1496 | szSField[psDBF->panFieldSize[iField]] = '\0';
|
|---|
| 1497 | nRetResult = FALSE;
|
|---|
| 1498 | }
|
|---|
| 1499 | strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
|
|---|
| 1500 | szSField, strlen(szSField) );
|
|---|
| 1501 | break;
|
|---|
| 1502 | }
|
|---|
| 1503 |
|
|---|
| 1504 | case 'L':
|
|---|
| 1505 | if (psDBF->panFieldSize[iField] >= 1 &&
|
|---|
| 1506 | (*(char*)pValue == 'F' || *(char*)pValue == 'T'))
|
|---|
| 1507 | *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue;
|
|---|
| 1508 | break;
|
|---|
| 1509 |
|
|---|
| 1510 | default:
|
|---|
| 1511 | if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] )
|
|---|
| 1512 | {
|
|---|
| 1513 | j = psDBF->panFieldSize[iField];
|
|---|
| 1514 | nRetResult = FALSE;
|
|---|
| 1515 | }
|
|---|
| 1516 | else
|
|---|
| 1517 | {
|
|---|
| 1518 | memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
|
|---|
| 1519 | psDBF->panFieldSize[iField] );
|
|---|
| 1520 | j = (int)strlen((char *) pValue);
|
|---|
| 1521 | }
|
|---|
| 1522 |
|
|---|
| 1523 | strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
|
|---|
| 1524 | (char *) pValue, j );
|
|---|
| 1525 | break;
|
|---|
| 1526 | }
|
|---|
| 1527 |
|
|---|
| 1528 | return( nRetResult );
|
|---|
| 1529 | }
|
|---|
| 1530 |
|
|---|
| 1531 | /************************************************************************/
|
|---|
| 1532 | /* DBFWriteAttributeDirectly() */
|
|---|
| 1533 | /* */
|
|---|
| 1534 | /* Write an attribute record to the file, but without any */
|
|---|
| 1535 | /* reformatting based on type. The provided buffer is written */
|
|---|
| 1536 | /* as is to the field position in the record. */
|
|---|
| 1537 | /************************************************************************/
|
|---|
| 1538 |
|
|---|
| 1539 | int SHPAPI_CALL
|
|---|
| 1540 | DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField,
|
|---|
| 1541 | void * pValue )
|
|---|
| 1542 |
|
|---|
| 1543 | {
|
|---|
| 1544 | int i, j;
|
|---|
| 1545 | unsigned char *pabyRec;
|
|---|
| 1546 |
|
|---|
| 1547 | /* -------------------------------------------------------------------- */
|
|---|
| 1548 | /* Is this a valid record? */
|
|---|
| 1549 | /* -------------------------------------------------------------------- */
|
|---|
| 1550 | if( hEntity < 0 || hEntity > psDBF->nRecords )
|
|---|
| 1551 | return( FALSE );
|
|---|
| 1552 |
|
|---|
| 1553 | if( psDBF->bNoHeader )
|
|---|
| 1554 | DBFWriteHeader(psDBF);
|
|---|
| 1555 |
|
|---|
| 1556 | /* -------------------------------------------------------------------- */
|
|---|
| 1557 | /* Is this a brand new record? */
|
|---|
| 1558 | /* -------------------------------------------------------------------- */
|
|---|
| 1559 | if( hEntity == psDBF->nRecords )
|
|---|
| 1560 | {
|
|---|
| 1561 | if( !DBFFlushRecord( psDBF ) )
|
|---|
| 1562 | return FALSE;
|
|---|
| 1563 |
|
|---|
| 1564 | psDBF->nRecords++;
|
|---|
| 1565 | for( i = 0; i < psDBF->nRecordLength; i++ )
|
|---|
| 1566 | psDBF->pszCurrentRecord[i] = ' ';
|
|---|
| 1567 |
|
|---|
| 1568 | psDBF->nCurrentRecord = hEntity;
|
|---|
| 1569 | }
|
|---|
| 1570 |
|
|---|
| 1571 | /* -------------------------------------------------------------------- */
|
|---|
| 1572 | /* Is this an existing record, but different than the last one */
|
|---|
| 1573 | /* we accessed? */
|
|---|
| 1574 | /* -------------------------------------------------------------------- */
|
|---|
| 1575 | if( !DBFLoadRecord( psDBF, hEntity ) )
|
|---|
| 1576 | return FALSE;
|
|---|
| 1577 |
|
|---|
| 1578 | pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
|
|---|
| 1579 |
|
|---|
| 1580 | /* -------------------------------------------------------------------- */
|
|---|
| 1581 | /* Assign all the record fields. */
|
|---|
| 1582 | /* -------------------------------------------------------------------- */
|
|---|
| 1583 | if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] )
|
|---|
| 1584 | j = psDBF->panFieldSize[iField];
|
|---|
| 1585 | else
|
|---|
| 1586 | {
|
|---|
| 1587 | memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
|
|---|
| 1588 | psDBF->panFieldSize[iField] );
|
|---|
| 1589 | j = (int)strlen((char *) pValue);
|
|---|
| 1590 | }
|
|---|
| 1591 |
|
|---|
| 1592 | strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
|
|---|
| 1593 | (char *) pValue, j );
|
|---|
| 1594 |
|
|---|
| 1595 | psDBF->bCurrentRecordModified = TRUE;
|
|---|
| 1596 | psDBF->bUpdated = TRUE;
|
|---|
| 1597 |
|
|---|
| 1598 | return( TRUE );
|
|---|
| 1599 | }
|
|---|
| 1600 |
|
|---|
| 1601 | /************************************************************************/
|
|---|
| 1602 | /* DBFWriteDoubleAttribute() */
|
|---|
| 1603 | /* */
|
|---|
| 1604 | /* Write a double attribute. */
|
|---|
| 1605 | /************************************************************************/
|
|---|
| 1606 |
|
|---|
| 1607 | int SHPAPI_CALL
|
|---|
| 1608 | DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField,
|
|---|
| 1609 | double dValue )
|
|---|
| 1610 |
|
|---|
| 1611 | {
|
|---|
| 1612 | return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
|
|---|
| 1613 | }
|
|---|
| 1614 |
|
|---|
| 1615 | /************************************************************************/
|
|---|
| 1616 | /* DBFWriteIntegerAttribute() */
|
|---|
| 1617 | /* */
|
|---|
| 1618 | /* Write a integer attribute. */
|
|---|
| 1619 | /************************************************************************/
|
|---|
| 1620 |
|
|---|
| 1621 | int SHPAPI_CALL
|
|---|
| 1622 | DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField,
|
|---|
| 1623 | int nValue )
|
|---|
| 1624 |
|
|---|
| 1625 | {
|
|---|
| 1626 | double dValue = nValue;
|
|---|
| 1627 |
|
|---|
| 1628 | return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
|
|---|
| 1629 | }
|
|---|
| 1630 |
|
|---|
| 1631 | /************************************************************************/
|
|---|
| 1632 | /* DBFWriteStringAttribute() */
|
|---|
| 1633 | /* */
|
|---|
| 1634 | /* Write a string attribute. */
|
|---|
| 1635 | /************************************************************************/
|
|---|
| 1636 |
|
|---|
| 1637 | int SHPAPI_CALL
|
|---|
| 1638 | DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField,
|
|---|
| 1639 | const char * pszValue )
|
|---|
| 1640 |
|
|---|
| 1641 | {
|
|---|
| 1642 | return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) );
|
|---|
| 1643 | }
|
|---|
| 1644 |
|
|---|
| 1645 | /************************************************************************/
|
|---|
| 1646 | /* DBFWriteNULLAttribute() */
|
|---|
| 1647 | /* */
|
|---|
| 1648 | /* Write a string attribute. */
|
|---|
| 1649 | /************************************************************************/
|
|---|
| 1650 |
|
|---|
| 1651 | int SHPAPI_CALL
|
|---|
| 1652 | DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField )
|
|---|
| 1653 |
|
|---|
| 1654 | {
|
|---|
| 1655 | return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) );
|
|---|
| 1656 | }
|
|---|
| 1657 |
|
|---|
| 1658 | /************************************************************************/
|
|---|
| 1659 | /* DBFWriteLogicalAttribute() */
|
|---|
| 1660 | /* */
|
|---|
| 1661 | /* Write a logical attribute. */
|
|---|
| 1662 | /************************************************************************/
|
|---|
| 1663 |
|
|---|
| 1664 | int SHPAPI_CALL
|
|---|
| 1665 | DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField,
|
|---|
| 1666 | const char lValue)
|
|---|
| 1667 |
|
|---|
| 1668 | {
|
|---|
| 1669 | return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) (&lValue) ) );
|
|---|
| 1670 | }
|
|---|
| 1671 |
|
|---|
| 1672 | /************************************************************************/
|
|---|
| 1673 | /* DBFWriteTuple() */
|
|---|
| 1674 | /* */
|
|---|
| 1675 | /* Write an attribute record to the file. */
|
|---|
| 1676 | /************************************************************************/
|
|---|
| 1677 |
|
|---|
| 1678 | int SHPAPI_CALL
|
|---|
| 1679 | DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple )
|
|---|
| 1680 |
|
|---|
| 1681 | {
|
|---|
| 1682 | int i;
|
|---|
| 1683 | unsigned char *pabyRec;
|
|---|
| 1684 |
|
|---|
| 1685 | /* -------------------------------------------------------------------- */
|
|---|
| 1686 | /* Is this a valid record? */
|
|---|
| 1687 | /* -------------------------------------------------------------------- */
|
|---|
| 1688 | if( hEntity < 0 || hEntity > psDBF->nRecords )
|
|---|
| 1689 | return( FALSE );
|
|---|
| 1690 |
|
|---|
| 1691 | if( psDBF->bNoHeader )
|
|---|
| 1692 | DBFWriteHeader(psDBF);
|
|---|
| 1693 |
|
|---|
| 1694 | /* -------------------------------------------------------------------- */
|
|---|
| 1695 | /* Is this a brand new record? */
|
|---|
| 1696 | /* -------------------------------------------------------------------- */
|
|---|
| 1697 | if( hEntity == psDBF->nRecords )
|
|---|
| 1698 | {
|
|---|
| 1699 | if( !DBFFlushRecord( psDBF ) )
|
|---|
| 1700 | return FALSE;
|
|---|
| 1701 |
|
|---|
| 1702 | psDBF->nRecords++;
|
|---|
| 1703 | for( i = 0; i < psDBF->nRecordLength; i++ )
|
|---|
| 1704 | psDBF->pszCurrentRecord[i] = ' ';
|
|---|
| 1705 |
|
|---|
| 1706 | psDBF->nCurrentRecord = hEntity;
|
|---|
| 1707 | }
|
|---|
| 1708 |
|
|---|
| 1709 | /* -------------------------------------------------------------------- */
|
|---|
| 1710 | /* Is this an existing record, but different than the last one */
|
|---|
| 1711 | /* we accessed? */
|
|---|
| 1712 | /* -------------------------------------------------------------------- */
|
|---|
| 1713 | if( !DBFLoadRecord( psDBF, hEntity ) )
|
|---|
| 1714 | return FALSE;
|
|---|
| 1715 |
|
|---|
| 1716 | pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
|
|---|
| 1717 |
|
|---|
| 1718 | memcpy ( pabyRec, pRawTuple, psDBF->nRecordLength );
|
|---|
| 1719 |
|
|---|
| 1720 | psDBF->bCurrentRecordModified = TRUE;
|
|---|
| 1721 | psDBF->bUpdated = TRUE;
|
|---|
| 1722 |
|
|---|
| 1723 | return( TRUE );
|
|---|
| 1724 | }
|
|---|
| 1725 |
|
|---|
| 1726 | /************************************************************************/
|
|---|
| 1727 | /* DBFReadTuple() */
|
|---|
| 1728 | /* */
|
|---|
| 1729 | /* Read a complete record. Note that the result is only valid */
|
|---|
| 1730 | /* till the next record read for any reason. */
|
|---|
| 1731 | /************************************************************************/
|
|---|
| 1732 |
|
|---|
| 1733 | const char SHPAPI_CALL1(*)
|
|---|
| 1734 | DBFReadTuple(DBFHandle psDBF, int hEntity )
|
|---|
| 1735 |
|
|---|
| 1736 | {
|
|---|
| 1737 | if( hEntity < 0 || hEntity >= psDBF->nRecords )
|
|---|
| 1738 | return( NULL );
|
|---|
| 1739 |
|
|---|
| 1740 | if( !DBFLoadRecord( psDBF, hEntity ) )
|
|---|
| 1741 | return NULL;
|
|---|
| 1742 |
|
|---|
| 1743 | return (const char *) psDBF->pszCurrentRecord;
|
|---|
| 1744 | }
|
|---|
| 1745 |
|
|---|
| 1746 | /************************************************************************/
|
|---|
| 1747 | /* DBFCloneEmpty() */
|
|---|
| 1748 | /* */
|
|---|
| 1749 | /* Read one of the attribute fields of a record. */
|
|---|
| 1750 | /************************************************************************/
|
|---|
| 1751 |
|
|---|
| 1752 | DBFHandle SHPAPI_CALL
|
|---|
| 1753 | DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename )
|
|---|
| 1754 | {
|
|---|
| 1755 | DBFHandle newDBF;
|
|---|
| 1756 |
|
|---|
| 1757 | newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage );
|
|---|
| 1758 | if ( newDBF == NULL ) return ( NULL );
|
|---|
| 1759 |
|
|---|
| 1760 | newDBF->nFields = psDBF->nFields;
|
|---|
| 1761 | newDBF->nRecordLength = psDBF->nRecordLength;
|
|---|
| 1762 | newDBF->nHeaderLength = psDBF->nHeaderLength;
|
|---|
| 1763 |
|
|---|
| 1764 | if( psDBF->pszHeader )
|
|---|
| 1765 | {
|
|---|
| 1766 | newDBF->pszHeader = (char *) malloc ( XBASE_FLDHDR_SZ * psDBF->nFields );
|
|---|
| 1767 | memcpy ( newDBF->pszHeader, psDBF->pszHeader, XBASE_FLDHDR_SZ * psDBF->nFields );
|
|---|
| 1768 | }
|
|---|
| 1769 |
|
|---|
| 1770 | newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields );
|
|---|
| 1771 | memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
|
|---|
| 1772 | newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields );
|
|---|
| 1773 | memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
|
|---|
| 1774 | newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields );
|
|---|
| 1775 | memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
|
|---|
| 1776 | newDBF->pachFieldType = (char *) malloc ( sizeof(char) * psDBF->nFields );
|
|---|
| 1777 | memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(char)*psDBF->nFields );
|
|---|
| 1778 |
|
|---|
| 1779 | newDBF->bNoHeader = TRUE;
|
|---|
| 1780 | newDBF->bUpdated = TRUE;
|
|---|
| 1781 | newDBF->bWriteEndOfFileChar = psDBF->bWriteEndOfFileChar;
|
|---|
| 1782 |
|
|---|
| 1783 | DBFWriteHeader ( newDBF );
|
|---|
| 1784 | DBFClose ( newDBF );
|
|---|
| 1785 |
|
|---|
| 1786 | newDBF = DBFOpen ( pszFilename, "rb+" );
|
|---|
| 1787 | newDBF->bWriteEndOfFileChar = psDBF->bWriteEndOfFileChar;
|
|---|
| 1788 |
|
|---|
| 1789 | return ( newDBF );
|
|---|
| 1790 | }
|
|---|
| 1791 |
|
|---|
| 1792 | /************************************************************************/
|
|---|
| 1793 | /* DBFGetNativeFieldType() */
|
|---|
| 1794 | /* */
|
|---|
| 1795 | /* Return the DBase field type for the specified field. */
|
|---|
| 1796 | /* */
|
|---|
| 1797 | /* Value can be one of: 'C' (String), 'D' (Date), 'F' (Float), */
|
|---|
| 1798 | /* 'N' (Numeric, with or without decimal), */
|
|---|
| 1799 | /* 'L' (Logical), */
|
|---|
| 1800 | /* 'M' (Memo: 10 digits .DBT block ptr) */
|
|---|
| 1801 | /************************************************************************/
|
|---|
| 1802 |
|
|---|
| 1803 | char SHPAPI_CALL
|
|---|
| 1804 | DBFGetNativeFieldType( DBFHandle psDBF, int iField )
|
|---|
| 1805 |
|
|---|
| 1806 | {
|
|---|
| 1807 | if( iField >=0 && iField < psDBF->nFields )
|
|---|
| 1808 | return psDBF->pachFieldType[iField];
|
|---|
| 1809 |
|
|---|
| 1810 | return ' ';
|
|---|
| 1811 | }
|
|---|
| 1812 |
|
|---|
| 1813 | /************************************************************************/
|
|---|
| 1814 | /* str_to_upper() */
|
|---|
| 1815 | /************************************************************************/
|
|---|
| 1816 |
|
|---|
| 1817 | static void str_to_upper (char *string)
|
|---|
| 1818 | {
|
|---|
| 1819 | int len;
|
|---|
| 1820 | int i = -1;
|
|---|
| 1821 |
|
|---|
| 1822 | len = (int)strlen (string);
|
|---|
| 1823 |
|
|---|
| 1824 | while (++i < len)
|
|---|
| 1825 | if (isalpha(string[i]) && islower(string[i]))
|
|---|
| 1826 | string[i] = (char) toupper ((int)string[i]);
|
|---|
| 1827 | }
|
|---|
| 1828 |
|
|---|
| 1829 | /************************************************************************/
|
|---|
| 1830 | /* DBFGetFieldIndex() */
|
|---|
| 1831 | /* */
|
|---|
| 1832 | /* Get the index number for a field in a .dbf file. */
|
|---|
| 1833 | /* */
|
|---|
| 1834 | /* Contributed by Jim Matthews. */
|
|---|
| 1835 | /************************************************************************/
|
|---|
| 1836 |
|
|---|
| 1837 | int SHPAPI_CALL
|
|---|
| 1838 | DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
|
|---|
| 1839 |
|
|---|
| 1840 | {
|
|---|
| 1841 | char name[XBASE_FLDNAME_LEN_READ+1],
|
|---|
| 1842 | name1[XBASE_FLDNAME_LEN_READ+1],
|
|---|
| 1843 | name2[XBASE_FLDNAME_LEN_READ+1];
|
|---|
| 1844 | int i;
|
|---|
| 1845 |
|
|---|
| 1846 | strncpy(name1, pszFieldName,XBASE_FLDNAME_LEN_READ);
|
|---|
| 1847 | name1[XBASE_FLDNAME_LEN_READ] = '\0';
|
|---|
| 1848 | str_to_upper(name1);
|
|---|
| 1849 |
|
|---|
| 1850 | for( i = 0; i < DBFGetFieldCount(psDBF); i++ )
|
|---|
| 1851 | {
|
|---|
| 1852 | DBFGetFieldInfo( psDBF, i, name, NULL, NULL );
|
|---|
| 1853 | strncpy(name2,name,XBASE_FLDNAME_LEN_READ);
|
|---|
| 1854 | name2[XBASE_FLDNAME_LEN_READ] = '\0';
|
|---|
| 1855 | str_to_upper(name2);
|
|---|
| 1856 |
|
|---|
| 1857 | if(!strcmp(name1,name2))
|
|---|
| 1858 | return(i);
|
|---|
| 1859 | }
|
|---|
| 1860 | return(-1);
|
|---|
| 1861 | }
|
|---|
| 1862 |
|
|---|
| 1863 | /************************************************************************/
|
|---|
| 1864 | /* DBFIsRecordDeleted() */
|
|---|
| 1865 | /* */
|
|---|
| 1866 | /* Returns TRUE if the indicated record is deleted, otherwise */
|
|---|
| 1867 | /* it returns FALSE. */
|
|---|
| 1868 | /************************************************************************/
|
|---|
| 1869 |
|
|---|
| 1870 | int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape )
|
|---|
| 1871 |
|
|---|
| 1872 | {
|
|---|
| 1873 | /* -------------------------------------------------------------------- */
|
|---|
| 1874 | /* Verify selection. */
|
|---|
| 1875 | /* -------------------------------------------------------------------- */
|
|---|
| 1876 | if( iShape < 0 || iShape >= psDBF->nRecords )
|
|---|
| 1877 | return TRUE;
|
|---|
| 1878 |
|
|---|
| 1879 | /* -------------------------------------------------------------------- */
|
|---|
| 1880 | /* Have we read the record? */
|
|---|
| 1881 | /* -------------------------------------------------------------------- */
|
|---|
| 1882 | if( !DBFLoadRecord( psDBF, iShape ) )
|
|---|
| 1883 | return FALSE;
|
|---|
| 1884 |
|
|---|
| 1885 | /* -------------------------------------------------------------------- */
|
|---|
| 1886 | /* '*' means deleted. */
|
|---|
| 1887 | /* -------------------------------------------------------------------- */
|
|---|
| 1888 | return psDBF->pszCurrentRecord[0] == '*';
|
|---|
| 1889 | }
|
|---|
| 1890 |
|
|---|
| 1891 | /************************************************************************/
|
|---|
| 1892 | /* DBFMarkRecordDeleted() */
|
|---|
| 1893 | /************************************************************************/
|
|---|
| 1894 |
|
|---|
| 1895 | int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape,
|
|---|
| 1896 | int bIsDeleted )
|
|---|
| 1897 |
|
|---|
| 1898 | {
|
|---|
| 1899 | char chNewFlag;
|
|---|
| 1900 |
|
|---|
| 1901 | /* -------------------------------------------------------------------- */
|
|---|
| 1902 | /* Verify selection. */
|
|---|
| 1903 | /* -------------------------------------------------------------------- */
|
|---|
| 1904 | if( iShape < 0 || iShape >= psDBF->nRecords )
|
|---|
| 1905 | return FALSE;
|
|---|
| 1906 |
|
|---|
| 1907 | /* -------------------------------------------------------------------- */
|
|---|
| 1908 | /* Is this an existing record, but different than the last one */
|
|---|
| 1909 | /* we accessed? */
|
|---|
| 1910 | /* -------------------------------------------------------------------- */
|
|---|
| 1911 | if( !DBFLoadRecord( psDBF, iShape ) )
|
|---|
| 1912 | return FALSE;
|
|---|
| 1913 |
|
|---|
| 1914 | /* -------------------------------------------------------------------- */
|
|---|
| 1915 | /* Assign value, marking record as dirty if it changes. */
|
|---|
| 1916 | /* -------------------------------------------------------------------- */
|
|---|
| 1917 | if( bIsDeleted )
|
|---|
| 1918 | chNewFlag = '*';
|
|---|
| 1919 | else
|
|---|
| 1920 | chNewFlag = ' ';
|
|---|
| 1921 |
|
|---|
| 1922 | if( psDBF->pszCurrentRecord[0] != chNewFlag )
|
|---|
| 1923 | {
|
|---|
| 1924 | psDBF->bCurrentRecordModified = TRUE;
|
|---|
| 1925 | psDBF->bUpdated = TRUE;
|
|---|
| 1926 | psDBF->pszCurrentRecord[0] = chNewFlag;
|
|---|
| 1927 | }
|
|---|
| 1928 |
|
|---|
| 1929 | return TRUE;
|
|---|
| 1930 | }
|
|---|
| 1931 |
|
|---|
| 1932 | /************************************************************************/
|
|---|
| 1933 | /* DBFGetCodePage */
|
|---|
| 1934 | /************************************************************************/
|
|---|
| 1935 |
|
|---|
| 1936 | const char SHPAPI_CALL1(*)
|
|---|
| 1937 | DBFGetCodePage(DBFHandle psDBF )
|
|---|
| 1938 | {
|
|---|
| 1939 | if( psDBF == NULL )
|
|---|
| 1940 | return NULL;
|
|---|
| 1941 | return psDBF->pszCodePage;
|
|---|
| 1942 | }
|
|---|
| 1943 |
|
|---|
| 1944 | /************************************************************************/
|
|---|
| 1945 | /* DBFDeleteField() */
|
|---|
| 1946 | /* */
|
|---|
| 1947 | /* Remove a field from a .dbf file */
|
|---|
| 1948 | /************************************************************************/
|
|---|
| 1949 |
|
|---|
| 1950 | int SHPAPI_CALL
|
|---|
| 1951 | DBFDeleteField(DBFHandle psDBF, int iField)
|
|---|
| 1952 | {
|
|---|
| 1953 | int nOldRecordLength, nOldHeaderLength;
|
|---|
| 1954 | int nDeletedFieldOffset, nDeletedFieldSize;
|
|---|
| 1955 | SAOffset nRecordOffset;
|
|---|
| 1956 | char* pszRecord;
|
|---|
| 1957 | int i, iRecord;
|
|---|
| 1958 |
|
|---|
| 1959 | if (iField < 0 || iField >= psDBF->nFields)
|
|---|
| 1960 | return FALSE;
|
|---|
| 1961 |
|
|---|
| 1962 | /* make sure that everything is written in .dbf */
|
|---|
| 1963 | if( !DBFFlushRecord( psDBF ) )
|
|---|
| 1964 | return FALSE;
|
|---|
| 1965 |
|
|---|
| 1966 | /* get information about field to be deleted */
|
|---|
| 1967 | nOldRecordLength = psDBF->nRecordLength;
|
|---|
| 1968 | nOldHeaderLength = psDBF->nHeaderLength;
|
|---|
| 1969 | nDeletedFieldOffset = psDBF->panFieldOffset[iField];
|
|---|
| 1970 | nDeletedFieldSize = psDBF->panFieldSize[iField];
|
|---|
| 1971 |
|
|---|
| 1972 | /* update fields info */
|
|---|
| 1973 | for (i = iField + 1; i < psDBF->nFields; i++)
|
|---|
| 1974 | {
|
|---|
| 1975 | psDBF->panFieldOffset[i-1] = psDBF->panFieldOffset[i] - nDeletedFieldSize;
|
|---|
| 1976 | psDBF->panFieldSize[i-1] = psDBF->panFieldSize[i];
|
|---|
| 1977 | psDBF->panFieldDecimals[i-1] = psDBF->panFieldDecimals[i];
|
|---|
| 1978 | psDBF->pachFieldType[i-1] = psDBF->pachFieldType[i];
|
|---|
| 1979 | }
|
|---|
| 1980 |
|
|---|
| 1981 | /* resize fields arrays */
|
|---|
| 1982 | psDBF->nFields--;
|
|---|
| 1983 |
|
|---|
| 1984 | psDBF->panFieldOffset = (int *)
|
|---|
| 1985 | SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
|
|---|
| 1986 |
|
|---|
| 1987 | psDBF->panFieldSize = (int *)
|
|---|
| 1988 | SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
|
|---|
| 1989 |
|
|---|
| 1990 | psDBF->panFieldDecimals = (int *)
|
|---|
| 1991 | SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
|
|---|
| 1992 |
|
|---|
| 1993 | psDBF->pachFieldType = (char *)
|
|---|
| 1994 | SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
|
|---|
| 1995 |
|
|---|
| 1996 | /* update header information */
|
|---|
| 1997 | psDBF->nHeaderLength -= XBASE_FLDHDR_SZ;
|
|---|
| 1998 | psDBF->nRecordLength -= nDeletedFieldSize;
|
|---|
| 1999 |
|
|---|
| 2000 | /* overwrite field information in header */
|
|---|
| 2001 | memmove(psDBF->pszHeader + iField*XBASE_FLDHDR_SZ,
|
|---|
| 2002 | psDBF->pszHeader + (iField+1)*XBASE_FLDHDR_SZ,
|
|---|
| 2003 | sizeof(char) * (psDBF->nFields - iField)*XBASE_FLDHDR_SZ);
|
|---|
| 2004 |
|
|---|
| 2005 | psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,
|
|---|
| 2006 | psDBF->nFields*XBASE_FLDHDR_SZ);
|
|---|
| 2007 |
|
|---|
| 2008 | /* update size of current record appropriately */
|
|---|
| 2009 | psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
|
|---|
| 2010 | psDBF->nRecordLength);
|
|---|
| 2011 |
|
|---|
| 2012 | /* we're done if we're dealing with not yet created .dbf */
|
|---|
| 2013 | if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
|
|---|
| 2014 | return TRUE;
|
|---|
| 2015 |
|
|---|
| 2016 | /* force update of header with new header and record length */
|
|---|
| 2017 | psDBF->bNoHeader = TRUE;
|
|---|
| 2018 | DBFUpdateHeader( psDBF );
|
|---|
| 2019 |
|
|---|
| 2020 | /* alloc record */
|
|---|
| 2021 | pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
|
|---|
| 2022 |
|
|---|
| 2023 | /* shift records to their new positions */
|
|---|
| 2024 | for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
|
|---|
| 2025 | {
|
|---|
| 2026 | nRecordOffset =
|
|---|
| 2027 | nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength;
|
|---|
| 2028 |
|
|---|
| 2029 | /* load record */
|
|---|
| 2030 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2031 | psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
|
|---|
| 2032 |
|
|---|
| 2033 | nRecordOffset =
|
|---|
| 2034 | psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
|
|---|
| 2035 |
|
|---|
| 2036 | /* move record in two steps */
|
|---|
| 2037 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2038 | psDBF->sHooks.FWrite( pszRecord, nDeletedFieldOffset, 1, psDBF->fp );
|
|---|
| 2039 | psDBF->sHooks.FWrite( pszRecord + nDeletedFieldOffset + nDeletedFieldSize,
|
|---|
| 2040 | nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize,
|
|---|
| 2041 | 1, psDBF->fp );
|
|---|
| 2042 |
|
|---|
| 2043 | }
|
|---|
| 2044 |
|
|---|
| 2045 | if( psDBF->bWriteEndOfFileChar )
|
|---|
| 2046 | {
|
|---|
| 2047 | char ch = END_OF_FILE_CHARACTER;
|
|---|
| 2048 | psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp );
|
|---|
| 2049 | }
|
|---|
| 2050 |
|
|---|
| 2051 | /* TODO: truncate file */
|
|---|
| 2052 |
|
|---|
| 2053 | /* free record */
|
|---|
| 2054 | free(pszRecord);
|
|---|
| 2055 |
|
|---|
| 2056 | psDBF->nCurrentRecord = -1;
|
|---|
| 2057 | psDBF->bCurrentRecordModified = FALSE;
|
|---|
| 2058 | psDBF->bUpdated = TRUE;
|
|---|
| 2059 |
|
|---|
| 2060 | return TRUE;
|
|---|
| 2061 | }
|
|---|
| 2062 |
|
|---|
| 2063 | /************************************************************************/
|
|---|
| 2064 | /* DBFReorderFields() */
|
|---|
| 2065 | /* */
|
|---|
| 2066 | /* Reorder the fields of a .dbf file */
|
|---|
| 2067 | /* */
|
|---|
| 2068 | /* panMap must be exactly psDBF->nFields long and be a permutation */
|
|---|
| 2069 | /* of [0, psDBF->nFields-1]. This assumption will not be asserted in the*/
|
|---|
| 2070 | /* code of DBFReorderFields. */
|
|---|
| 2071 | /************************************************************************/
|
|---|
| 2072 |
|
|---|
| 2073 | int SHPAPI_CALL
|
|---|
| 2074 | DBFReorderFields( DBFHandle psDBF, int* panMap )
|
|---|
| 2075 | {
|
|---|
| 2076 | SAOffset nRecordOffset;
|
|---|
| 2077 | int i, iRecord;
|
|---|
| 2078 | int *panFieldOffsetNew;
|
|---|
| 2079 | int *panFieldSizeNew;
|
|---|
| 2080 | int *panFieldDecimalsNew;
|
|---|
| 2081 | char *pachFieldTypeNew;
|
|---|
| 2082 | char *pszHeaderNew;
|
|---|
| 2083 | char *pszRecord;
|
|---|
| 2084 | char *pszRecordNew;
|
|---|
| 2085 |
|
|---|
| 2086 | if ( psDBF->nFields == 0 )
|
|---|
| 2087 | return TRUE;
|
|---|
| 2088 |
|
|---|
| 2089 | /* make sure that everything is written in .dbf */
|
|---|
| 2090 | if( !DBFFlushRecord( psDBF ) )
|
|---|
| 2091 | return FALSE;
|
|---|
| 2092 |
|
|---|
| 2093 | /* a simple malloc() would be enough, but calloc() helps clang static analyzer */
|
|---|
| 2094 | panFieldOffsetNew = (int *) calloc(sizeof(int), psDBF->nFields);
|
|---|
| 2095 | panFieldSizeNew = (int *) calloc(sizeof(int), psDBF->nFields);
|
|---|
| 2096 | panFieldDecimalsNew = (int *) calloc(sizeof(int), psDBF->nFields);
|
|---|
| 2097 | pachFieldTypeNew = (char *) calloc(sizeof(char), psDBF->nFields);
|
|---|
| 2098 | pszHeaderNew = (char*) malloc(sizeof(char) * XBASE_FLDHDR_SZ *
|
|---|
| 2099 | psDBF->nFields);
|
|---|
| 2100 |
|
|---|
| 2101 | /* shuffle fields definitions */
|
|---|
| 2102 | for(i=0; i < psDBF->nFields; i++)
|
|---|
| 2103 | {
|
|---|
| 2104 | panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]];
|
|---|
| 2105 | panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]];
|
|---|
| 2106 | pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]];
|
|---|
| 2107 | memcpy(pszHeaderNew + i * XBASE_FLDHDR_SZ,
|
|---|
| 2108 | psDBF->pszHeader + panMap[i] * XBASE_FLDHDR_SZ, XBASE_FLDHDR_SZ);
|
|---|
| 2109 | }
|
|---|
| 2110 | panFieldOffsetNew[0] = 1;
|
|---|
| 2111 | for(i=1; i < psDBF->nFields; i++)
|
|---|
| 2112 | {
|
|---|
| 2113 | panFieldOffsetNew[i] = panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
|
|---|
| 2114 | }
|
|---|
| 2115 |
|
|---|
| 2116 | free(psDBF->pszHeader);
|
|---|
| 2117 | psDBF->pszHeader = pszHeaderNew;
|
|---|
| 2118 |
|
|---|
| 2119 | /* we're done if we're dealing with not yet created .dbf */
|
|---|
| 2120 | if ( !(psDBF->bNoHeader && psDBF->nRecords == 0) )
|
|---|
| 2121 | {
|
|---|
| 2122 | /* force update of header with new header and record length */
|
|---|
| 2123 | psDBF->bNoHeader = TRUE;
|
|---|
| 2124 | DBFUpdateHeader( psDBF );
|
|---|
| 2125 |
|
|---|
| 2126 | /* alloc record */
|
|---|
| 2127 | pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
|
|---|
| 2128 | pszRecordNew = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
|
|---|
| 2129 |
|
|---|
| 2130 | /* shuffle fields in records */
|
|---|
| 2131 | for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
|
|---|
| 2132 | {
|
|---|
| 2133 | nRecordOffset =
|
|---|
| 2134 | psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
|
|---|
| 2135 |
|
|---|
| 2136 | /* load record */
|
|---|
| 2137 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2138 | psDBF->sHooks.FRead( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
|
|---|
| 2139 |
|
|---|
| 2140 | pszRecordNew[0] = pszRecord[0];
|
|---|
| 2141 |
|
|---|
| 2142 | for(i=0; i < psDBF->nFields; i++)
|
|---|
| 2143 | {
|
|---|
| 2144 | memcpy(pszRecordNew + panFieldOffsetNew[i],
|
|---|
| 2145 | pszRecord + psDBF->panFieldOffset[panMap[i]],
|
|---|
| 2146 | psDBF->panFieldSize[panMap[i]]);
|
|---|
| 2147 | }
|
|---|
| 2148 |
|
|---|
| 2149 | /* write record */
|
|---|
| 2150 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2151 | psDBF->sHooks.FWrite( pszRecordNew, psDBF->nRecordLength, 1, psDBF->fp );
|
|---|
| 2152 | }
|
|---|
| 2153 |
|
|---|
| 2154 | /* free record */
|
|---|
| 2155 | free(pszRecord);
|
|---|
| 2156 | free(pszRecordNew);
|
|---|
| 2157 | }
|
|---|
| 2158 |
|
|---|
| 2159 | free(psDBF->panFieldOffset);
|
|---|
| 2160 | free(psDBF->panFieldSize);
|
|---|
| 2161 | free(psDBF->panFieldDecimals);
|
|---|
| 2162 | free(psDBF->pachFieldType);
|
|---|
| 2163 |
|
|---|
| 2164 | psDBF->panFieldOffset = panFieldOffsetNew;
|
|---|
| 2165 | psDBF->panFieldSize = panFieldSizeNew;
|
|---|
| 2166 | psDBF->panFieldDecimals =panFieldDecimalsNew;
|
|---|
| 2167 | psDBF->pachFieldType = pachFieldTypeNew;
|
|---|
| 2168 |
|
|---|
| 2169 | psDBF->nCurrentRecord = -1;
|
|---|
| 2170 | psDBF->bCurrentRecordModified = FALSE;
|
|---|
| 2171 | psDBF->bUpdated = TRUE;
|
|---|
| 2172 |
|
|---|
| 2173 | return TRUE;
|
|---|
| 2174 | }
|
|---|
| 2175 |
|
|---|
| 2176 |
|
|---|
| 2177 | /************************************************************************/
|
|---|
| 2178 | /* DBFAlterFieldDefn() */
|
|---|
| 2179 | /* */
|
|---|
| 2180 | /* Alter a field definition in a .dbf file */
|
|---|
| 2181 | /************************************************************************/
|
|---|
| 2182 |
|
|---|
| 2183 | int SHPAPI_CALL
|
|---|
| 2184 | DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName,
|
|---|
| 2185 | char chType, int nWidth, int nDecimals )
|
|---|
| 2186 | {
|
|---|
| 2187 | int i;
|
|---|
| 2188 | int iRecord;
|
|---|
| 2189 | int nOffset;
|
|---|
| 2190 | int nOldWidth;
|
|---|
| 2191 | int nOldRecordLength;
|
|---|
| 2192 | SAOffset nRecordOffset;
|
|---|
| 2193 | char* pszFInfo;
|
|---|
| 2194 | char chOldType;
|
|---|
| 2195 | int bIsNULL;
|
|---|
| 2196 | char chFieldFill;
|
|---|
| 2197 |
|
|---|
| 2198 | if (iField < 0 || iField >= psDBF->nFields)
|
|---|
| 2199 | return FALSE;
|
|---|
| 2200 |
|
|---|
| 2201 | /* make sure that everything is written in .dbf */
|
|---|
| 2202 | if( !DBFFlushRecord( psDBF ) )
|
|---|
| 2203 | return FALSE;
|
|---|
| 2204 |
|
|---|
| 2205 | chFieldFill = DBFGetNullCharacter(chType);
|
|---|
| 2206 |
|
|---|
| 2207 | chOldType = psDBF->pachFieldType[iField];
|
|---|
| 2208 | nOffset = psDBF->panFieldOffset[iField];
|
|---|
| 2209 | nOldWidth = psDBF->panFieldSize[iField];
|
|---|
| 2210 | nOldRecordLength = psDBF->nRecordLength;
|
|---|
| 2211 |
|
|---|
| 2212 | /* -------------------------------------------------------------------- */
|
|---|
| 2213 | /* Do some checking to ensure we can add records to this file. */
|
|---|
| 2214 | /* -------------------------------------------------------------------- */
|
|---|
| 2215 | if( nWidth < 1 )
|
|---|
| 2216 | return -1;
|
|---|
| 2217 |
|
|---|
| 2218 | if( nWidth > XBASE_FLD_MAX_WIDTH )
|
|---|
| 2219 | nWidth = XBASE_FLD_MAX_WIDTH;
|
|---|
| 2220 |
|
|---|
| 2221 | /* -------------------------------------------------------------------- */
|
|---|
| 2222 | /* Assign the new field information fields. */
|
|---|
| 2223 | /* -------------------------------------------------------------------- */
|
|---|
| 2224 | psDBF->panFieldSize[iField] = nWidth;
|
|---|
| 2225 | psDBF->panFieldDecimals[iField] = nDecimals;
|
|---|
| 2226 | psDBF->pachFieldType[iField] = chType;
|
|---|
| 2227 |
|
|---|
| 2228 | /* -------------------------------------------------------------------- */
|
|---|
| 2229 | /* Update the header information. */
|
|---|
| 2230 | /* -------------------------------------------------------------------- */
|
|---|
| 2231 | pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * iField;
|
|---|
| 2232 |
|
|---|
| 2233 | for( i = 0; i < XBASE_FLDHDR_SZ; i++ )
|
|---|
| 2234 | pszFInfo[i] = '\0';
|
|---|
| 2235 |
|
|---|
| 2236 | strncpy( pszFInfo, pszFieldName, XBASE_FLDNAME_LEN_WRITE );
|
|---|
| 2237 |
|
|---|
| 2238 | pszFInfo[11] = psDBF->pachFieldType[iField];
|
|---|
| 2239 |
|
|---|
| 2240 | if( chType == 'C' )
|
|---|
| 2241 | {
|
|---|
| 2242 | pszFInfo[16] = (unsigned char) (nWidth % 256);
|
|---|
| 2243 | pszFInfo[17] = (unsigned char) (nWidth / 256);
|
|---|
| 2244 | }
|
|---|
| 2245 | else
|
|---|
| 2246 | {
|
|---|
| 2247 | pszFInfo[16] = (unsigned char) nWidth;
|
|---|
| 2248 | pszFInfo[17] = (unsigned char) nDecimals;
|
|---|
| 2249 | }
|
|---|
| 2250 |
|
|---|
| 2251 | /* -------------------------------------------------------------------- */
|
|---|
| 2252 | /* Update offsets */
|
|---|
| 2253 | /* -------------------------------------------------------------------- */
|
|---|
| 2254 | if (nWidth != nOldWidth)
|
|---|
| 2255 | {
|
|---|
| 2256 | for (i = iField + 1; i < psDBF->nFields; i++)
|
|---|
| 2257 | psDBF->panFieldOffset[i] += nWidth - nOldWidth;
|
|---|
| 2258 | psDBF->nRecordLength += nWidth - nOldWidth;
|
|---|
| 2259 |
|
|---|
| 2260 | psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
|
|---|
| 2261 | psDBF->nRecordLength);
|
|---|
| 2262 | }
|
|---|
| 2263 |
|
|---|
| 2264 | /* we're done if we're dealing with not yet created .dbf */
|
|---|
| 2265 | if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
|
|---|
| 2266 | return TRUE;
|
|---|
| 2267 |
|
|---|
| 2268 | /* force update of header with new header and record length */
|
|---|
| 2269 | psDBF->bNoHeader = TRUE;
|
|---|
| 2270 | DBFUpdateHeader( psDBF );
|
|---|
| 2271 |
|
|---|
| 2272 | if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType))
|
|---|
| 2273 | {
|
|---|
| 2274 | char* pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
|
|---|
| 2275 | char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
|
|---|
| 2276 |
|
|---|
| 2277 | /* cppcheck-suppress uninitdata */
|
|---|
| 2278 | pszOldField[nOldWidth] = 0;
|
|---|
| 2279 |
|
|---|
| 2280 | /* move records to their new positions */
|
|---|
| 2281 | for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
|
|---|
| 2282 | {
|
|---|
| 2283 | nRecordOffset =
|
|---|
| 2284 | nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
|
|---|
| 2285 |
|
|---|
| 2286 | /* load record */
|
|---|
| 2287 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2288 | psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
|
|---|
| 2289 |
|
|---|
| 2290 | memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
|
|---|
| 2291 | bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
|
|---|
| 2292 |
|
|---|
| 2293 | if (nWidth != nOldWidth)
|
|---|
| 2294 | {
|
|---|
| 2295 | if ((chOldType == 'N' || chOldType == 'F' || chOldType == 'D') && pszOldField[0] == ' ')
|
|---|
| 2296 | {
|
|---|
| 2297 | /* Strip leading spaces when truncating a numeric field */
|
|---|
| 2298 | memmove( pszRecord + nOffset,
|
|---|
| 2299 | pszRecord + nOffset + nOldWidth - nWidth,
|
|---|
| 2300 | nWidth );
|
|---|
| 2301 | }
|
|---|
| 2302 | if (nOffset + nOldWidth < nOldRecordLength)
|
|---|
| 2303 | {
|
|---|
| 2304 | memmove( pszRecord + nOffset + nWidth,
|
|---|
| 2305 | pszRecord + nOffset + nOldWidth,
|
|---|
| 2306 | nOldRecordLength - (nOffset + nOldWidth));
|
|---|
| 2307 | }
|
|---|
| 2308 | }
|
|---|
| 2309 |
|
|---|
| 2310 | /* Convert null value to the appropriate value of the new type */
|
|---|
| 2311 | if (bIsNULL)
|
|---|
| 2312 | {
|
|---|
| 2313 | memset( pszRecord + nOffset, chFieldFill, nWidth);
|
|---|
| 2314 | }
|
|---|
| 2315 |
|
|---|
| 2316 | nRecordOffset =
|
|---|
| 2317 | psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
|
|---|
| 2318 |
|
|---|
| 2319 | /* write record */
|
|---|
| 2320 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2321 | psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
|
|---|
| 2322 | }
|
|---|
| 2323 |
|
|---|
| 2324 | if( psDBF->bWriteEndOfFileChar )
|
|---|
| 2325 | {
|
|---|
| 2326 | char ch = END_OF_FILE_CHARACTER;
|
|---|
| 2327 |
|
|---|
| 2328 | nRecordOffset =
|
|---|
| 2329 | psDBF->nRecordLength * (SAOffset) psDBF->nRecords + psDBF->nHeaderLength;
|
|---|
| 2330 |
|
|---|
| 2331 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2332 | psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp );
|
|---|
| 2333 | }
|
|---|
| 2334 | /* TODO: truncate file */
|
|---|
| 2335 |
|
|---|
| 2336 | free(pszRecord);
|
|---|
| 2337 | free(pszOldField);
|
|---|
| 2338 | }
|
|---|
| 2339 | else if (nWidth > nOldWidth)
|
|---|
| 2340 | {
|
|---|
| 2341 | char* pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
|
|---|
| 2342 | char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
|
|---|
| 2343 |
|
|---|
| 2344 | /* cppcheck-suppress uninitdata */
|
|---|
| 2345 | pszOldField[nOldWidth] = 0;
|
|---|
| 2346 |
|
|---|
| 2347 | /* move records to their new positions */
|
|---|
| 2348 | for (iRecord = psDBF->nRecords - 1; iRecord >= 0; iRecord--)
|
|---|
| 2349 | {
|
|---|
| 2350 | nRecordOffset =
|
|---|
| 2351 | nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
|
|---|
| 2352 |
|
|---|
| 2353 | /* load record */
|
|---|
| 2354 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2355 | psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
|
|---|
| 2356 |
|
|---|
| 2357 | memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
|
|---|
| 2358 | bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
|
|---|
| 2359 |
|
|---|
| 2360 | if (nOffset + nOldWidth < nOldRecordLength)
|
|---|
| 2361 | {
|
|---|
| 2362 | memmove( pszRecord + nOffset + nWidth,
|
|---|
| 2363 | pszRecord + nOffset + nOldWidth,
|
|---|
| 2364 | nOldRecordLength - (nOffset + nOldWidth));
|
|---|
| 2365 | }
|
|---|
| 2366 |
|
|---|
| 2367 | /* Convert null value to the appropriate value of the new type */
|
|---|
| 2368 | if (bIsNULL)
|
|---|
| 2369 | {
|
|---|
| 2370 | memset( pszRecord + nOffset, chFieldFill, nWidth);
|
|---|
| 2371 | }
|
|---|
| 2372 | else
|
|---|
| 2373 | {
|
|---|
| 2374 | if ((chOldType == 'N' || chOldType == 'F'))
|
|---|
| 2375 | {
|
|---|
| 2376 | /* Add leading spaces when expanding a numeric field */
|
|---|
| 2377 | memmove( pszRecord + nOffset + nWidth - nOldWidth,
|
|---|
| 2378 | pszRecord + nOffset, nOldWidth );
|
|---|
| 2379 | memset( pszRecord + nOffset, ' ', nWidth - nOldWidth );
|
|---|
| 2380 | }
|
|---|
| 2381 | else
|
|---|
| 2382 | {
|
|---|
| 2383 | /* Add trailing spaces */
|
|---|
| 2384 | memset(pszRecord + nOffset + nOldWidth, ' ', nWidth - nOldWidth);
|
|---|
| 2385 | }
|
|---|
| 2386 | }
|
|---|
| 2387 |
|
|---|
| 2388 | nRecordOffset =
|
|---|
| 2389 | psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
|
|---|
| 2390 |
|
|---|
| 2391 | /* write record */
|
|---|
| 2392 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2393 | psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
|
|---|
| 2394 | }
|
|---|
| 2395 |
|
|---|
| 2396 | if( psDBF->bWriteEndOfFileChar )
|
|---|
| 2397 | {
|
|---|
| 2398 | char ch = END_OF_FILE_CHARACTER;
|
|---|
| 2399 |
|
|---|
| 2400 | nRecordOffset =
|
|---|
| 2401 | psDBF->nRecordLength * (SAOffset) psDBF->nRecords + psDBF->nHeaderLength;
|
|---|
| 2402 |
|
|---|
| 2403 | psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
|
|---|
| 2404 | psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp );
|
|---|
| 2405 | }
|
|---|
| 2406 |
|
|---|
| 2407 | free(pszRecord);
|
|---|
| 2408 | free(pszOldField);
|
|---|
| 2409 | }
|
|---|
| 2410 |
|
|---|
| 2411 | psDBF->nCurrentRecord = -1;
|
|---|
| 2412 | psDBF->bCurrentRecordModified = FALSE;
|
|---|
| 2413 | psDBF->bUpdated = TRUE;
|
|---|
| 2414 |
|
|---|
| 2415 | return TRUE;
|
|---|
| 2416 | }
|
|---|
| 2417 |
|
|---|
| 2418 | /************************************************************************/
|
|---|
| 2419 | /* DBFSetWriteEndOfFileChar() */
|
|---|
| 2420 | /************************************************************************/
|
|---|
| 2421 |
|
|---|
| 2422 | void SHPAPI_CALL DBFSetWriteEndOfFileChar( DBFHandle psDBF, int bWriteFlag )
|
|---|
| 2423 | {
|
|---|
| 2424 | psDBF->bWriteEndOfFileChar = bWriteFlag;
|
|---|
| 2425 | }
|
|---|