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