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