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