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