PostGIS  3.0.6dev-r@@SVN_REVISION@@
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 
165 SHP_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 
179 static void *
180 SfRealloc(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 
197 static void
198 DBFWriteHeader(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 
257 static int
258 DBFFlushRecord(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 
285 static int
286 DBFLoadRecord(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 
323 void SHPAPI_CALL
324 DBFUpdateHeader(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 
353 DBFHandle SHPAPI_CALL
354 DBFOpen(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 
369 DBFHandle SHPAPI_CALL
370 DBFOpenLL(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 
565 void SHPAPI_CALL
566 DBFClose(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 
615 DBFHandle SHPAPI_CALL
616 DBFCreate(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 
627 DBFHandle SHPAPI_CALL
628 DBFCreateEx(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 
643 DBFHandle SHPAPI_CALL
644 DBFCreateLL(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 
756 int SHPAPI_CALL
757 DBFAddField(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 
777 static 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 
801 int SHPAPI_CALL
802 DBFAddNativeFieldType(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 
933 static void *
934 DBFReadAttribute(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 
1017 int SHPAPI_CALL
1018 DBFReadIntegerAttribute(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 
1036 double SHPAPI_CALL
1037 DBFReadDoubleAttribute(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 
1055 const char
1056 SHPAPI_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 
1067 const char
1068 SHPAPI_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 
1079 static int
1080 DBFIsValueNULL(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 
1144 int SHPAPI_CALL
1145 DBFIsAttributeNULL(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 
1163 int SHPAPI_CALL
1164 DBFGetFieldCount(DBFHandle psDBF)
1165 {
1166  return (psDBF->nFields);
1167 }
1168 
1169 /************************************************************************/
1170 /* DBFGetRecordCount() */
1171 /* */
1172 /* Return the number of records in this table. */
1173 /************************************************************************/
1174 
1175 int SHPAPI_CALL
1176 DBFGetRecordCount(DBFHandle psDBF)
1177 {
1178  return (psDBF->nRecords);
1179 }
1180 
1181 /************************************************************************/
1182 /* DBFGetFieldInfo() */
1183 /* */
1184 /* Return any requested information about the field. */
1185 /************************************************************************/
1186 
1187 DBFFieldType SHPAPI_CALL
1188 DBFGetFieldInfo(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 
1234 static int
1235 DBFWriteAttribute(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 
1365 int SHPAPI_CALL
1366 DBFWriteAttributeDirectly(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 
1429 int SHPAPI_CALL
1430 DBFWriteDoubleAttribute(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 
1441 int SHPAPI_CALL
1442 DBFWriteIntegerAttribute(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 
1455 int SHPAPI_CALL
1456 DBFWriteStringAttribute(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 
1467 int SHPAPI_CALL
1468 DBFWriteNULLAttribute(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 
1479 int SHPAPI_CALL
1480 DBFWriteLogicalAttribute(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 
1491 int SHPAPI_CALL
1492 DBFWriteTuple(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 
1545 const char
1546 SHPAPI_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 
1563 DBFHandle SHPAPI_CALL
1564 DBFCloneEmpty(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 
1610 char SHPAPI_CALL
1611 DBFGetNativeFieldType(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 
1623 static void
1624 str_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 
1644 int SHPAPI_CALL
1645 DBFGetFieldIndex(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 
1673 int SHPAPI_CALL
1674 DBFIsRecordDeleted(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 
1698 int SHPAPI_CALL
1699 DBFMarkRecordDeleted(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 
1738 const char
1739 SHPAPI_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 
1752 int SHPAPI_CALL
1753 DBFDeleteField(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 
1860 int SHPAPI_CALL
1861 DBFReorderFields(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 
1964 int SHPAPI_CALL
1965 DBFAlterFieldDefn(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
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
const char SHPAPI_CALL1 * DBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField){ return((const char *) DBFReadAttribute(psDBF, iRecord, iField, 'C')
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
const char SHPAPI_CALL1 * DBFReadTuple(DBFHandle psDBF, int hEntity){ if(hEntity< 0||hEntity >=psDBF->nRecords) return(NULL
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
double SHPAPI_CALL DBFReadDoubleAttribute(DBFHandle psDBF, int iRecord, int iField)
Definition: dbfopen.c:1037
if(!DBFLoadRecord(psDBF, hEntity)) return NULL
int SHPAPI_CALL DBFAddField(DBFHandle psDBF, const char *pszFieldName, DBFFieldType eType, int nWidth, int nDecimals)
Definition: dbfopen.c:757
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
const char SHPAPI_CALL1 * DBFReadLogicalAttribute(DBFHandle psDBF, int iRecord, int iField){ return((const char *) DBFReadAttribute(psDBF, iRecord, iField, 'L')
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
#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
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
static void * SfRealloc(void *pMem, int nNewSize)
Definition: dbfopen.c:180
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
char SHPAPI_CALL DBFGetNativeFieldType(DBFHandle psDBF, int iField)
Definition: dbfopen.c:1611
static void * DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, char chReqType)
Definition: dbfopen.c:934
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