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