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