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