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