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