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
CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)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
SfRealloc(void * pMem,int nNewSize)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
DBFWriteHeader(DBFHandle psDBF)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
DBFFlushRecord(DBFHandle psDBF)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
DBFLoadRecord(DBFHandle psDBF,int iRecord)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
DBFUpdateHeader(DBFHandle psDBF)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
DBFSetLastModifiedDate(DBFHandle psDBF,int nYYSince1900,int nMM,int nDD)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
DBFOpen(const char * pszFilename,const char * pszAccess)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
DBFGetLenWithoutExtension(const char * pszBasename)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
DBFOpenLL(const char * pszFilename,const char * pszAccess,SAHooks * psHooks)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
DBFClose(DBFHandle psDBF)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
DBFCreate(const char * pszFilename)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
DBFCreateEx(const char * pszFilename,const char * pszCodePage)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
DBFCreateLL(const char * pszFilename,const char * pszCodePage,SAHooks * psHooks)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
DBFAddField(DBFHandle psDBF,const char * pszFieldName,DBFFieldType eType,int nWidth,int nDecimals)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
DBFGetNullCharacter(char chType)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
DBFAddNativeFieldType(DBFHandle psDBF,const char * pszFieldName,char chType,int nWidth,int nDecimals)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
DBFReadAttribute(DBFHandle psDBF,int hEntity,int iField,char chReqType)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
DBFReadIntegerAttribute(DBFHandle psDBF,int iRecord,int iField)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
DBFReadDoubleAttribute(DBFHandle psDBF,int iRecord,int iField)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(*)
DBFReadStringAttribute(DBFHandle psDBF,int iRecord,int iField)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(*)
DBFReadLogicalAttribute(DBFHandle psDBF,int iRecord,int iField)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
DBFIsValueNULL(char chType,const char * pszValue)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
DBFIsAttributeNULL(DBFHandle psDBF,int iRecord,int iField)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
DBFGetFieldCount(DBFHandle psDBF)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
DBFGetRecordCount(DBFHandle psDBF)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
DBFGetFieldInfo(DBFHandle psDBF,int iField,char * pszFieldName,int * pnWidth,int * pnDecimals)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
DBFWriteAttribute(DBFHandle psDBF,int hEntity,int iField,void * pValue)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
DBFWriteAttributeDirectly(DBFHandle psDBF,int hEntity,int iField,void * pValue)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
DBFWriteDoubleAttribute(DBFHandle psDBF,int iRecord,int iField,double dValue)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
DBFWriteIntegerAttribute(DBFHandle psDBF,int iRecord,int iField,int nValue)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
DBFWriteStringAttribute(DBFHandle psDBF,int iRecord,int iField,const char * pszValue)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
DBFWriteNULLAttribute(DBFHandle psDBF,int iRecord,int iField)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
DBFWriteLogicalAttribute(DBFHandle psDBF,int iRecord,int iField,const char lValue)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
DBFWriteTuple(DBFHandle psDBF,int hEntity,void * pRawTuple)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(*)
DBFReadTuple(DBFHandle psDBF,int hEntity)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
DBFCloneEmpty(DBFHandle psDBF,const char * pszFilename)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
DBFGetNativeFieldType(DBFHandle psDBF,int iField)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
DBFGetFieldIndex(DBFHandle psDBF,const char * pszFieldName)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
DBFIsRecordDeleted(DBFHandle psDBF,int iShape)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
DBFMarkRecordDeleted(DBFHandle psDBF,int iShape,int bIsDeleted)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(*)
DBFGetCodePage(DBFHandle psDBF)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
DBFDeleteField(DBFHandle psDBF,int iField)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
DBFReorderFields(DBFHandle psDBF,int * panMap)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
DBFAlterFieldDefn(DBFHandle psDBF,int iField,const char * pszFieldName,char chType,int nWidth,int nDecimals)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
DBFSetWriteEndOfFileChar(DBFHandle psDBF,int bWriteFlag)2209 void SHPAPI_CALL DBFSetWriteEndOfFileChar( DBFHandle psDBF, int bWriteFlag )
2210 {
2211 psDBF->bWriteEndOfFileChar = bWriteFlag;
2212 }
2213