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