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