PostGIS  2.4.9dev-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 15722 2017-09-14 13:52:25Z 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  {
690  free(pszBasename);
691  free(pszFullname);
692  return( NULL );
693  }
694 
695  psHooks->FWrite( &chZero, 1, 1, fp );
696  psHooks->FClose( fp );
697 
698  fp = psHooks->FOpen( pszFullname, "rb+" );
699  if( fp == NULL )
700  {
701  free(pszBasename);
702  free(pszFullname);
703  return( NULL );
704  }
705 
706 
707  sprintf( pszFullname, "%s.cpg", pszBasename );
708  if( pszCodePage != NULL )
709  {
710  if( strncmp( pszCodePage, "LDID/", 5 ) == 0 )
711  {
712  ldid = atoi( pszCodePage + 5 );
713  if( ldid > 255 )
714  ldid = -1; /* don't use 0 to indicate out of range as LDID/0 is a valid one */
715  }
716  if( ldid < 0 )
717  {
718  SAFile fpCPG = psHooks->FOpen( pszFullname, "w" );
719  psHooks->FWrite( (char*) pszCodePage, strlen(pszCodePage), 1, fpCPG );
720  psHooks->FClose( fpCPG );
721  }
722  }
723  if( pszCodePage == NULL || ldid >= 0 )
724  {
725  psHooks->Remove( pszFullname );
726  }
727 
728  free( pszBasename );
729  free( pszFullname );
730 
731 /* -------------------------------------------------------------------- */
732 /* Create the info structure. */
733 /* -------------------------------------------------------------------- */
734  psDBF = (DBFHandle) calloc(1,sizeof(DBFInfo));
735 
736  memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
737  psDBF->fp = fp;
738  psDBF->nRecords = 0;
739  psDBF->nFields = 0;
740  psDBF->nRecordLength = 1;
741  psDBF->nHeaderLength = 33;
742 
743  psDBF->panFieldOffset = NULL;
744  psDBF->panFieldSize = NULL;
745  psDBF->panFieldDecimals = NULL;
746  psDBF->pachFieldType = NULL;
747  psDBF->pszHeader = NULL;
748 
749  psDBF->nCurrentRecord = -1;
750  psDBF->bCurrentRecordModified = FALSE;
751  psDBF->pszCurrentRecord = NULL;
752 
753  psDBF->bNoHeader = TRUE;
754 
755  psDBF->iLanguageDriver = ldid > 0 ? ldid : 0;
756  psDBF->pszCodePage = NULL;
757  if( pszCodePage )
758  {
759  psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 );
760  strcpy( psDBF->pszCodePage, pszCodePage );
761  }
762 
763  return( psDBF );
764 }
765 
766 /************************************************************************/
767 /* DBFAddField() */
768 /* */
769 /* Add a field to a newly created .dbf or to an existing one */
770 /************************************************************************/
771 
772 int SHPAPI_CALL
773 DBFAddField(DBFHandle psDBF, const char * pszFieldName,
774  DBFFieldType eType, int nWidth, int nDecimals )
775 
776 {
777  char chNativeType = 'C';
778 
779  if( eType == FTLogical )
780  chNativeType = 'L';
781  else if( eType == FTString )
782  chNativeType = 'C';
783  else if( eType == FTDate )
784  chNativeType = 'D';
785  else
786  chNativeType = 'N';
787 
788  return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType,
789  nWidth, nDecimals );
790 }
791 
792 /************************************************************************/
793 /* DBFGetNullCharacter() */
794 /************************************************************************/
795 
796 static char DBFGetNullCharacter(char chType)
797 {
798  switch (chType)
799  {
800  case 'N':
801  case 'F':
802  return '*';
803  case 'D':
804  return '0';
805  case 'L':
806  return '?';
807  default:
808  return ' ';
809  }
810 }
811 
812 /************************************************************************/
813 /* DBFAddField() */
814 /* */
815 /* Add a field to a newly created .dbf file before any records */
816 /* are written. */
817 /************************************************************************/
818 
819 int SHPAPI_CALL
820 DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName,
821  char chType, int nWidth, int nDecimals )
822 
823 {
824  char *pszFInfo;
825  int i;
826  int nOldRecordLength, nOldHeaderLength;
827  char *pszRecord;
828  char chFieldFill;
829  SAOffset nRecordOffset;
830 
831  /* make sure that everything is written in .dbf */
832  if( !DBFFlushRecord( psDBF ) )
833  return -1;
834 
835 /* -------------------------------------------------------------------- */
836 /* Do some checking to ensure we can add records to this file. */
837 /* -------------------------------------------------------------------- */
838  if( nWidth < 1 )
839  return -1;
840 
841  if( nWidth > 255 )
842  nWidth = 255;
843 
844  nOldRecordLength = psDBF->nRecordLength;
845  nOldHeaderLength = psDBF->nHeaderLength;
846 
847 /* -------------------------------------------------------------------- */
848 /* SfRealloc all the arrays larger to hold the additional field */
849 /* information. */
850 /* -------------------------------------------------------------------- */
851  psDBF->nFields++;
852 
853  psDBF->panFieldOffset = (int *)
854  SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
855 
856  psDBF->panFieldSize = (int *)
857  SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
858 
859  psDBF->panFieldDecimals = (int *)
860  SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
861 
862  psDBF->pachFieldType = (char *)
863  SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
864 
865 /* -------------------------------------------------------------------- */
866 /* Assign the new field information fields. */
867 /* -------------------------------------------------------------------- */
868  psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength;
869  psDBF->nRecordLength += nWidth;
870  psDBF->panFieldSize[psDBF->nFields-1] = nWidth;
871  psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals;
872  psDBF->pachFieldType[psDBF->nFields-1] = chType;
873 
874 /* -------------------------------------------------------------------- */
875 /* Extend the required header information. */
876 /* -------------------------------------------------------------------- */
877  psDBF->nHeaderLength += 32;
878  psDBF->bUpdated = FALSE;
879 
880  psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
881 
882  pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1);
883 
884  for( i = 0; i < 32; i++ )
885  pszFInfo[i] = '\0';
886 
887  if( (int) strlen(pszFieldName) < 10 )
888  strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
889  else
890  strncpy( pszFInfo, pszFieldName, 10);
891 
892  pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1];
893 
894  if( chType == 'C' )
895  {
896  pszFInfo[16] = (unsigned char) (nWidth % 256);
897  pszFInfo[17] = (unsigned char) (nWidth / 256);
898  }
899  else
900  {
901  pszFInfo[16] = (unsigned char) nWidth;
902  pszFInfo[17] = (unsigned char) nDecimals;
903  }
904 
905 /* -------------------------------------------------------------------- */
906 /* Make the current record buffer appropriately larger. */
907 /* -------------------------------------------------------------------- */
908  psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
909  psDBF->nRecordLength);
910 
911  /* we're done if dealing with new .dbf */
912  if( psDBF->bNoHeader )
913  return( psDBF->nFields - 1 );
914 
915 /* -------------------------------------------------------------------- */
916 /* For existing .dbf file, shift records */
917 /* -------------------------------------------------------------------- */
918 
919  /* alloc record */
920  pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
921 
922  chFieldFill = DBFGetNullCharacter(chType);
923 
924  for (i = psDBF->nRecords-1; i >= 0; --i)
925  {
926  nRecordOffset = nOldRecordLength * (SAOffset) i + nOldHeaderLength;
927 
928  /* load record */
929  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
930  psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
931 
932  /* set new field's value to NULL */
933  memset(pszRecord + nOldRecordLength, chFieldFill, nWidth);
934 
935  nRecordOffset = psDBF->nRecordLength * (SAOffset) i + psDBF->nHeaderLength;
936 
937  /* move record to the new place*/
938  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
939  psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
940  }
941 
942  /* free record */
943  free(pszRecord);
944 
945  /* force update of header with new header, record length and new field */
946  psDBF->bNoHeader = TRUE;
947  DBFUpdateHeader( psDBF );
948 
949  psDBF->nCurrentRecord = -1;
950  psDBF->bCurrentRecordModified = FALSE;
951 
952  return( psDBF->nFields-1 );
953 }
954 
955 /************************************************************************/
956 /* DBFReadAttribute() */
957 /* */
958 /* Read one of the attribute fields of a record. */
959 /************************************************************************/
960 
961 static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField,
962  char chReqType )
963 
964 {
965  unsigned char *pabyRec;
966  void *pReturnField = NULL;
967 
968 /* -------------------------------------------------------------------- */
969 /* Verify selection. */
970 /* -------------------------------------------------------------------- */
971  if( hEntity < 0 || hEntity >= psDBF->nRecords )
972  return( NULL );
973 
974  if( iField < 0 || iField >= psDBF->nFields )
975  return( NULL );
976 
977 /* -------------------------------------------------------------------- */
978 /* Have we read the record? */
979 /* -------------------------------------------------------------------- */
980  if( !DBFLoadRecord( psDBF, hEntity ) )
981  return NULL;
982 
983  pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
984 
985 /* -------------------------------------------------------------------- */
986 /* Ensure we have room to extract the target field. */
987 /* -------------------------------------------------------------------- */
988  if( psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength )
989  {
990  psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100;
991  if( psDBF->pszWorkField == NULL )
992  psDBF->pszWorkField = (char *) malloc(psDBF->nWorkFieldLength);
993  else
994  psDBF->pszWorkField = (char *) realloc(psDBF->pszWorkField,
995  psDBF->nWorkFieldLength);
996  }
997 
998 /* -------------------------------------------------------------------- */
999 /* Extract the requested field. */
1000 /* -------------------------------------------------------------------- */
1001  strncpy( psDBF->pszWorkField,
1002  ((const char *) pabyRec) + psDBF->panFieldOffset[iField],
1003  psDBF->panFieldSize[iField] );
1004  psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0';
1005 
1006  pReturnField = psDBF->pszWorkField;
1007 
1008 /* -------------------------------------------------------------------- */
1009 /* Decode the field. */
1010 /* -------------------------------------------------------------------- */
1011  if( chReqType == 'N' )
1012  {
1013  psDBF->dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField);
1014 
1015  pReturnField = &(psDBF->dfDoubleField);
1016  }
1017 
1018 /* -------------------------------------------------------------------- */
1019 /* Should we trim white space off the string attribute value? */
1020 /* -------------------------------------------------------------------- */
1021 #ifdef TRIM_DBF_WHITESPACE
1022  else
1023  {
1024  char *pchSrc, *pchDst;
1025 
1026  pchDst = pchSrc = psDBF->pszWorkField;
1027  while( *pchSrc == ' ' )
1028  pchSrc++;
1029 
1030  while( *pchSrc != '\0' )
1031  *(pchDst++) = *(pchSrc++);
1032  *pchDst = '\0';
1033 
1034  while( pchDst != psDBF->pszWorkField && *(--pchDst) == ' ' )
1035  *pchDst = '\0';
1036  }
1037 #endif
1038 
1039  return( pReturnField );
1040 }
1041 
1042 /************************************************************************/
1043 /* DBFReadIntAttribute() */
1044 /* */
1045 /* Read an integer attribute. */
1046 /************************************************************************/
1047 
1048 int SHPAPI_CALL
1049 DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField )
1050 
1051 {
1052  double *pdValue;
1053 
1054  pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
1055 
1056  if( pdValue == NULL )
1057  return 0;
1058  else
1059  return( (int) *pdValue );
1060 }
1061 
1062 /************************************************************************/
1063 /* DBFReadDoubleAttribute() */
1064 /* */
1065 /* Read a double attribute. */
1066 /************************************************************************/
1067 
1068 double SHPAPI_CALL
1069 DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField )
1070 
1071 {
1072  double *pdValue;
1073 
1074  pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
1075 
1076  if( pdValue == NULL )
1077  return 0.0;
1078  else
1079  return( *pdValue );
1080 }
1081 
1082 /************************************************************************/
1083 /* DBFReadStringAttribute() */
1084 /* */
1085 /* Read a string attribute. */
1086 /************************************************************************/
1087 
1088 const char SHPAPI_CALL1(*)
1089 DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField )
1090 
1091 {
1092  return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) );
1093 }
1094 
1095 /************************************************************************/
1096 /* DBFReadLogicalAttribute() */
1097 /* */
1098 /* Read a logical attribute. */
1099 /************************************************************************/
1100 
1101 const char SHPAPI_CALL1(*)
1102 DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField )
1103 
1104 {
1105  return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) );
1106 }
1107 
1108 
1109 /************************************************************************/
1110 /* DBFIsValueNULL() */
1111 /* */
1112 /* Return TRUE if the passed string is NULL. */
1113 /************************************************************************/
1114 
1115 static int DBFIsValueNULL( char chType, const char* pszValue )
1116 {
1117  int i;
1118 
1119  if( pszValue == NULL )
1120  return TRUE;
1121 
1122  switch(chType)
1123  {
1124  case 'N':
1125  case 'F':
1126  /*
1127  ** We accept all asterisks or all blanks as NULL
1128  ** though according to the spec I think it should be all
1129  ** asterisks.
1130  */
1131  if( pszValue[0] == '*' )
1132  return TRUE;
1133 
1134  for( i = 0; pszValue[i] != '\0'; i++ )
1135  {
1136  if( pszValue[i] != ' ' )
1137  return FALSE;
1138  }
1139  return TRUE;
1140 
1141  case 'D':
1142  /* NULL date fields have value "00000000" or blank or empty */
1143  if (pszValue[0] == '\0' || /* emtpy string */
1144  strncmp(pszValue,"00000000",8) == 0 ||
1145  strncmp(pszValue," ",8) == 0) {
1146  return 1;
1147  } else {
1148  return 0;
1149  }
1150  /* return strncmp(pszValue,"00000000",8) == 0; */
1151 
1152  case 'L':
1153  /* NULL boolean fields have value "?" or empty */
1154  if (pszValue[0] == '\0' || pszValue[0] == '?') {
1155  return 1;
1156  } else {
1157  return 0;
1158  }
1159 
1160  default:
1161  /* empty string fields are considered NULL */
1162  return strlen(pszValue) == 0;
1163  }
1164 }
1165 
1166 /************************************************************************/
1167 /* DBFIsAttributeNULL() */
1168 /* */
1169 /* Return TRUE if value for field is NULL. */
1170 /* */
1171 /* Contributed by Jim Matthews. */
1172 /************************************************************************/
1173 
1174 int SHPAPI_CALL
1175 DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField )
1176 
1177 {
1178  const char *pszValue;
1179 
1180  pszValue = DBFReadStringAttribute( psDBF, iRecord, iField );
1181 
1182  if( pszValue == NULL )
1183  return TRUE;
1184 
1185  return DBFIsValueNULL( psDBF->pachFieldType[iField], pszValue );
1186 }
1187 
1188 /************************************************************************/
1189 /* DBFGetFieldCount() */
1190 /* */
1191 /* Return the number of fields in this table. */
1192 /************************************************************************/
1193 
1194 int SHPAPI_CALL
1195 DBFGetFieldCount( DBFHandle psDBF )
1196 
1197 {
1198  return( psDBF->nFields );
1199 }
1200 
1201 /************************************************************************/
1202 /* DBFGetRecordCount() */
1203 /* */
1204 /* Return the number of records in this table. */
1205 /************************************************************************/
1206 
1207 int SHPAPI_CALL
1208 DBFGetRecordCount( DBFHandle psDBF )
1209 
1210 {
1211  return( psDBF->nRecords );
1212 }
1213 
1214 /************************************************************************/
1215 /* DBFGetFieldInfo() */
1216 /* */
1217 /* Return any requested information about the field. */
1218 /************************************************************************/
1219 
1220 DBFFieldType SHPAPI_CALL
1221 DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName,
1222  int * pnWidth, int * pnDecimals )
1223 
1224 {
1225  if( iField < 0 || iField >= psDBF->nFields )
1226  return( FTInvalid );
1227 
1228  if( pnWidth != NULL )
1229  *pnWidth = psDBF->panFieldSize[iField];
1230 
1231  if( pnDecimals != NULL )
1232  *pnDecimals = psDBF->panFieldDecimals[iField];
1233 
1234  if( pszFieldName != NULL )
1235  {
1236  int i;
1237 
1238  strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 );
1239  pszFieldName[11] = '\0';
1240  for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- )
1241  pszFieldName[i] = '\0';
1242  }
1243 
1244  if ( psDBF->pachFieldType[iField] == 'L' )
1245  return( FTLogical);
1246 
1247  else if ( psDBF->pachFieldType[iField] == 'D' )
1248  return ( FTDate );
1249 
1250  else if( psDBF->pachFieldType[iField] == 'N'
1251  || psDBF->pachFieldType[iField] == 'F' )
1252  {
1253  if( psDBF->panFieldDecimals[iField] > 0
1254  || psDBF->panFieldSize[iField] > 10 )
1255  return( FTDouble );
1256  else
1257  return( FTInteger );
1258  }
1259  else
1260  {
1261  return( FTString );
1262  }
1263 }
1264 
1265 /************************************************************************/
1266 /* DBFWriteAttribute() */
1267 /* */
1268 /* Write an attribute record to the file. */
1269 /************************************************************************/
1270 
1271 static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
1272  void * pValue )
1273 
1274 {
1275  int i, j, nRetResult = TRUE;
1276  unsigned char *pabyRec;
1277  char szSField[400], szFormat[20];
1278 
1279 /* -------------------------------------------------------------------- */
1280 /* Is this a valid record? */
1281 /* -------------------------------------------------------------------- */
1282  if( hEntity < 0 || hEntity > psDBF->nRecords )
1283  return( FALSE );
1284 
1285  if( psDBF->bNoHeader )
1286  DBFWriteHeader(psDBF);
1287 
1288 /* -------------------------------------------------------------------- */
1289 /* Is this a brand new record? */
1290 /* -------------------------------------------------------------------- */
1291  if( hEntity == psDBF->nRecords )
1292  {
1293  if( !DBFFlushRecord( psDBF ) )
1294  return FALSE;
1295 
1296  psDBF->nRecords++;
1297  for( i = 0; i < psDBF->nRecordLength; i++ )
1298  psDBF->pszCurrentRecord[i] = ' ';
1299 
1300  psDBF->nCurrentRecord = hEntity;
1301  }
1302 
1303 /* -------------------------------------------------------------------- */
1304 /* Is this an existing record, but different than the last one */
1305 /* we accessed? */
1306 /* -------------------------------------------------------------------- */
1307  if( !DBFLoadRecord( psDBF, hEntity ) )
1308  return FALSE;
1309 
1310  pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1311 
1312  psDBF->bCurrentRecordModified = TRUE;
1313  psDBF->bUpdated = TRUE;
1314 
1315 /* -------------------------------------------------------------------- */
1316 /* Translate NULL value to valid DBF file representation. */
1317 /* */
1318 /* Contributed by Jim Matthews. */
1319 /* -------------------------------------------------------------------- */
1320  if( pValue == NULL )
1321  {
1322  memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]),
1323  DBFGetNullCharacter(psDBF->pachFieldType[iField]),
1324  psDBF->panFieldSize[iField] );
1325  return TRUE;
1326  }
1327 
1328 /* -------------------------------------------------------------------- */
1329 /* Assign all the record fields. */
1330 /* -------------------------------------------------------------------- */
1331  switch( psDBF->pachFieldType[iField] )
1332  {
1333  case 'D':
1334  case 'N':
1335  case 'F':
1336  if( psDBF->panFieldDecimals[iField] == 0 )
1337  {
1338  int nWidth = psDBF->panFieldSize[iField];
1339 
1340  if( (int) sizeof(szSField)-2 < nWidth )
1341  nWidth = sizeof(szSField)-2;
1342 
1343  sprintf( szFormat, "%%%dd", nWidth );
1344  sprintf(szSField, szFormat, (int) *((double *) pValue) );
1345  if( (int)strlen(szSField) > psDBF->panFieldSize[iField] )
1346  {
1347  szSField[psDBF->panFieldSize[iField]] = '\0';
1348  nRetResult = FALSE;
1349  }
1350 
1351  strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1352  szSField, strlen(szSField) );
1353  }
1354  else
1355  {
1356  int nWidth = psDBF->panFieldSize[iField];
1357 
1358  if( (int) sizeof(szSField)-2 < nWidth )
1359  nWidth = sizeof(szSField)-2;
1360 
1361  sprintf( szFormat, "%%%d.%df",
1362  nWidth, psDBF->panFieldDecimals[iField] );
1363  sprintf(szSField, szFormat, *((double *) pValue) );
1364  if( (int) strlen(szSField) > psDBF->panFieldSize[iField] )
1365  {
1366  szSField[psDBF->panFieldSize[iField]] = '\0';
1367  nRetResult = FALSE;
1368  }
1369  strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1370  szSField, strlen(szSField) );
1371  }
1372  break;
1373 
1374  case 'L':
1375  if (psDBF->panFieldSize[iField] >= 1 &&
1376  (*(char*)pValue == 'F' || *(char*)pValue == 'T'))
1377  *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue;
1378  break;
1379 
1380  default:
1381  if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] )
1382  {
1383  j = psDBF->panFieldSize[iField];
1384  nRetResult = FALSE;
1385  }
1386  else
1387  {
1388  memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
1389  psDBF->panFieldSize[iField] );
1390  j = strlen((char *) pValue);
1391  }
1392 
1393  strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1394  (char *) pValue, j );
1395  break;
1396  }
1397 
1398  return( nRetResult );
1399 }
1400 
1401 /************************************************************************/
1402 /* DBFWriteAttributeDirectly() */
1403 /* */
1404 /* Write an attribute record to the file, but without any */
1405 /* reformatting based on type. The provided buffer is written */
1406 /* as is to the field position in the record. */
1407 /************************************************************************/
1408 
1409 int SHPAPI_CALL
1410 DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField,
1411  void * pValue )
1412 
1413 {
1414  int i, j;
1415  unsigned char *pabyRec;
1416 
1417 /* -------------------------------------------------------------------- */
1418 /* Is this a valid record? */
1419 /* -------------------------------------------------------------------- */
1420  if( hEntity < 0 || hEntity > psDBF->nRecords )
1421  return( FALSE );
1422 
1423  if( psDBF->bNoHeader )
1424  DBFWriteHeader(psDBF);
1425 
1426 /* -------------------------------------------------------------------- */
1427 /* Is this a brand new record? */
1428 /* -------------------------------------------------------------------- */
1429  if( hEntity == psDBF->nRecords )
1430  {
1431  if( !DBFFlushRecord( psDBF ) )
1432  return FALSE;
1433 
1434  psDBF->nRecords++;
1435  for( i = 0; i < psDBF->nRecordLength; i++ )
1436  psDBF->pszCurrentRecord[i] = ' ';
1437 
1438  psDBF->nCurrentRecord = hEntity;
1439  }
1440 
1441 /* -------------------------------------------------------------------- */
1442 /* Is this an existing record, but different than the last one */
1443 /* we accessed? */
1444 /* -------------------------------------------------------------------- */
1445  if( !DBFLoadRecord( psDBF, hEntity ) )
1446  return FALSE;
1447 
1448  pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1449 
1450 /* -------------------------------------------------------------------- */
1451 /* Assign all the record fields. */
1452 /* -------------------------------------------------------------------- */
1453  if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] )
1454  j = psDBF->panFieldSize[iField];
1455  else
1456  {
1457  memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
1458  psDBF->panFieldSize[iField] );
1459  j = strlen((char *) pValue);
1460  }
1461 
1462  strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1463  (char *) pValue, j );
1464 
1465  psDBF->bCurrentRecordModified = TRUE;
1466  psDBF->bUpdated = TRUE;
1467 
1468  return( TRUE );
1469 }
1470 
1471 /************************************************************************/
1472 /* DBFWriteDoubleAttribute() */
1473 /* */
1474 /* Write a double attribute. */
1475 /************************************************************************/
1476 
1477 int SHPAPI_CALL
1478 DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField,
1479  double dValue )
1480 
1481 {
1482  return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
1483 }
1484 
1485 /************************************************************************/
1486 /* DBFWriteIntegerAttribute() */
1487 /* */
1488 /* Write a integer attribute. */
1489 /************************************************************************/
1490 
1491 int SHPAPI_CALL
1492 DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField,
1493  int nValue )
1494 
1495 {
1496  double dValue = nValue;
1497 
1498  return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
1499 }
1500 
1501 /************************************************************************/
1502 /* DBFWriteStringAttribute() */
1503 /* */
1504 /* Write a string attribute. */
1505 /************************************************************************/
1506 
1507 int SHPAPI_CALL
1508 DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField,
1509  const char * pszValue )
1510 
1511 {
1512  return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) );
1513 }
1514 
1515 /************************************************************************/
1516 /* DBFWriteNULLAttribute() */
1517 /* */
1518 /* Write a string attribute. */
1519 /************************************************************************/
1520 
1521 int SHPAPI_CALL
1522 DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField )
1523 
1524 {
1525  return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) );
1526 }
1527 
1528 /************************************************************************/
1529 /* DBFWriteLogicalAttribute() */
1530 /* */
1531 /* Write a logical attribute. */
1532 /************************************************************************/
1533 
1534 int SHPAPI_CALL
1535 DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField,
1536  const char lValue)
1537 
1538 {
1539  return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) (&lValue) ) );
1540 }
1541 
1542 /************************************************************************/
1543 /* DBFWriteTuple() */
1544 /* */
1545 /* Write an attribute record to the file. */
1546 /************************************************************************/
1547 
1548 int SHPAPI_CALL
1549 DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple )
1550 
1551 {
1552  int i;
1553  unsigned char *pabyRec;
1554 
1555 /* -------------------------------------------------------------------- */
1556 /* Is this a valid record? */
1557 /* -------------------------------------------------------------------- */
1558  if( hEntity < 0 || hEntity > psDBF->nRecords )
1559  return( FALSE );
1560 
1561  if( psDBF->bNoHeader )
1562  DBFWriteHeader(psDBF);
1563 
1564 /* -------------------------------------------------------------------- */
1565 /* Is this a brand new record? */
1566 /* -------------------------------------------------------------------- */
1567  if( hEntity == psDBF->nRecords )
1568  {
1569  if( !DBFFlushRecord( psDBF ) )
1570  return FALSE;
1571 
1572  psDBF->nRecords++;
1573  for( i = 0; i < psDBF->nRecordLength; i++ )
1574  psDBF->pszCurrentRecord[i] = ' ';
1575 
1576  psDBF->nCurrentRecord = hEntity;
1577  }
1578 
1579 /* -------------------------------------------------------------------- */
1580 /* Is this an existing record, but different than the last one */
1581 /* we accessed? */
1582 /* -------------------------------------------------------------------- */
1583  if( !DBFLoadRecord( psDBF, hEntity ) )
1584  return FALSE;
1585 
1586  pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1587 
1588  memcpy ( pabyRec, pRawTuple, psDBF->nRecordLength );
1589 
1590  psDBF->bCurrentRecordModified = TRUE;
1591  psDBF->bUpdated = TRUE;
1592 
1593  return( TRUE );
1594 }
1595 
1596 /************************************************************************/
1597 /* DBFReadTuple() */
1598 /* */
1599 /* Read a complete record. Note that the result is only valid */
1600 /* till the next record read for any reason. */
1601 /************************************************************************/
1602 
1603 const char SHPAPI_CALL1(*)
1604 DBFReadTuple(DBFHandle psDBF, int hEntity )
1605 
1606 {
1607  if( hEntity < 0 || hEntity >= psDBF->nRecords )
1608  return( NULL );
1609 
1610  if( !DBFLoadRecord( psDBF, hEntity ) )
1611  return NULL;
1612 
1613  return (const char *) psDBF->pszCurrentRecord;
1614 }
1615 
1616 /************************************************************************/
1617 /* DBFCloneEmpty() */
1618 /* */
1619 /* Read one of the attribute fields of a record. */
1620 /************************************************************************/
1621 
1622 DBFHandle SHPAPI_CALL
1623 DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename )
1624 {
1625  DBFHandle newDBF;
1626 
1627  newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage );
1628  if ( newDBF == NULL ) return ( NULL );
1629 
1630  newDBF->nFields = psDBF->nFields;
1631  newDBF->nRecordLength = psDBF->nRecordLength;
1632  newDBF->nHeaderLength = psDBF->nHeaderLength;
1633 
1634  newDBF->pszHeader = (char *) malloc ( newDBF->nHeaderLength );
1635  memcpy ( newDBF->pszHeader, psDBF->pszHeader, newDBF->nHeaderLength );
1636 
1637  newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields );
1638  memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
1639  newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields );
1640  memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
1641  newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields );
1642  memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
1643  newDBF->pachFieldType = (char *) malloc ( sizeof(char) * psDBF->nFields );
1644  memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(char)*psDBF->nFields );
1645 
1646  newDBF->bNoHeader = TRUE;
1647  newDBF->bUpdated = TRUE;
1648 
1649  DBFWriteHeader ( newDBF );
1650  DBFClose ( newDBF );
1651 
1652  newDBF = DBFOpen ( pszFilename, "rb+" );
1653 
1654  return ( newDBF );
1655 }
1656 
1657 /************************************************************************/
1658 /* DBFGetNativeFieldType() */
1659 /* */
1660 /* Return the DBase field type for the specified field. */
1661 /* */
1662 /* Value can be one of: 'C' (String), 'D' (Date), 'F' (Float), */
1663 /* 'N' (Numeric, with or without decimal), */
1664 /* 'L' (Logical), */
1665 /* 'M' (Memo: 10 digits .DBT block ptr) */
1666 /************************************************************************/
1667 
1668 char SHPAPI_CALL
1669 DBFGetNativeFieldType( DBFHandle psDBF, int iField )
1670 
1671 {
1672  if( iField >=0 && iField < psDBF->nFields )
1673  return psDBF->pachFieldType[iField];
1674 
1675  return ' ';
1676 }
1677 
1678 /************************************************************************/
1679 /* str_to_upper() */
1680 /************************************************************************/
1681 
1682 static void str_to_upper (char *string)
1683 {
1684  int len;
1685  short i = -1;
1686 
1687  len = strlen (string);
1688 
1689  while (++i < len)
1690  if (isalpha(string[i]) && islower(string[i]))
1691  string[i] = (char) toupper ((int)string[i]);
1692 }
1693 
1694 /************************************************************************/
1695 /* DBFGetFieldIndex() */
1696 /* */
1697 /* Get the index number for a field in a .dbf file. */
1698 /* */
1699 /* Contributed by Jim Matthews. */
1700 /************************************************************************/
1701 
1702 int SHPAPI_CALL
1703 DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
1704 
1705 {
1706  char name[12], name1[12], name2[12];
1707  int i;
1708 
1709  strncpy(name1, pszFieldName,11);
1710  name1[11] = '\0';
1711  str_to_upper(name1);
1712 
1713  for( i = 0; i < DBFGetFieldCount(psDBF); i++ )
1714  {
1715  DBFGetFieldInfo( psDBF, i, name, NULL, NULL );
1716  strncpy(name2,name,11);
1717  str_to_upper(name2);
1718 
1719  if(!strncmp(name1,name2,10))
1720  return(i);
1721  }
1722  return(-1);
1723 }
1724 
1725 /************************************************************************/
1726 /* DBFIsRecordDeleted() */
1727 /* */
1728 /* Returns TRUE if the indicated record is deleted, otherwise */
1729 /* it returns FALSE. */
1730 /************************************************************************/
1731 
1732 int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape )
1733 
1734 {
1735 /* -------------------------------------------------------------------- */
1736 /* Verify selection. */
1737 /* -------------------------------------------------------------------- */
1738  if( iShape < 0 || (psDBF->nRecords > 0 && iShape >= psDBF->nRecords) )
1739  return TRUE;
1740 
1741 /* -------------------------------------------------------------------- */
1742 /* Have we read the record? */
1743 /* -------------------------------------------------------------------- */
1744  if( psDBF->nRecords > 0 && !DBFLoadRecord( psDBF, iShape ) )
1745  return FALSE;
1746 
1747 /* -------------------------------------------------------------------- */
1748 /* '*' means deleted. */
1749 /* -------------------------------------------------------------------- */
1750  return psDBF->pszCurrentRecord[0] == '*';
1751 }
1752 
1753 /************************************************************************/
1754 /* DBFMarkRecordDeleted() */
1755 /************************************************************************/
1756 
1757 int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape,
1758  int bIsDeleted )
1759 
1760 {
1761  char chNewFlag;
1762 
1763 /* -------------------------------------------------------------------- */
1764 /* Verify selection. */
1765 /* -------------------------------------------------------------------- */
1766  if( iShape < 0 || iShape >= psDBF->nRecords )
1767  return FALSE;
1768 
1769 /* -------------------------------------------------------------------- */
1770 /* Is this an existing record, but different than the last one */
1771 /* we accessed? */
1772 /* -------------------------------------------------------------------- */
1773  if( !DBFLoadRecord( psDBF, iShape ) )
1774  return FALSE;
1775 
1776 /* -------------------------------------------------------------------- */
1777 /* Assign value, marking record as dirty if it changes. */
1778 /* -------------------------------------------------------------------- */
1779  if( bIsDeleted )
1780  chNewFlag = '*';
1781  else
1782  chNewFlag = ' ';
1783 
1784  if( psDBF->pszCurrentRecord[0] != chNewFlag )
1785  {
1786  psDBF->bCurrentRecordModified = TRUE;
1787  psDBF->bUpdated = TRUE;
1788  psDBF->pszCurrentRecord[0] = chNewFlag;
1789  }
1790 
1791  return TRUE;
1792 }
1793 
1794 /************************************************************************/
1795 /* DBFGetCodePage */
1796 /************************************************************************/
1797 
1798 const char SHPAPI_CALL1(*)
1799 DBFGetCodePage(DBFHandle psDBF )
1800 {
1801  if( psDBF == NULL )
1802  return NULL;
1803  return psDBF->pszCodePage;
1804 }
1805 
1806 /************************************************************************/
1807 /* DBFDeleteField() */
1808 /* */
1809 /* Remove a field from a .dbf file */
1810 /************************************************************************/
1811 
1812 int SHPAPI_CALL
1813 DBFDeleteField(DBFHandle psDBF, int iField)
1814 {
1815  int nOldRecordLength, nOldHeaderLength;
1816  int nDeletedFieldOffset, nDeletedFieldSize;
1817  SAOffset nRecordOffset;
1818  char* pszRecord;
1819  int i, iRecord;
1820 
1821  if (iField < 0 || iField >= psDBF->nFields)
1822  return FALSE;
1823 
1824  /* make sure that everything is written in .dbf */
1825  if( !DBFFlushRecord( psDBF ) )
1826  return FALSE;
1827 
1828  /* get information about field to be deleted */
1829  nOldRecordLength = psDBF->nRecordLength;
1830  nOldHeaderLength = psDBF->nHeaderLength;
1831  nDeletedFieldOffset = psDBF->panFieldOffset[iField];
1832  nDeletedFieldSize = psDBF->panFieldSize[iField];
1833 
1834  /* update fields info */
1835  for (i = iField + 1; i < psDBF->nFields; i++)
1836  {
1837  psDBF->panFieldOffset[i-1] = psDBF->panFieldOffset[i] - nDeletedFieldSize;
1838  psDBF->panFieldSize[i-1] = psDBF->panFieldSize[i];
1839  psDBF->panFieldDecimals[i-1] = psDBF->panFieldDecimals[i];
1840  psDBF->pachFieldType[i-1] = psDBF->pachFieldType[i];
1841  }
1842 
1843  /* resize fields arrays */
1844  psDBF->nFields--;
1845 
1846  psDBF->panFieldOffset = (int *)
1847  SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
1848 
1849  psDBF->panFieldSize = (int *)
1850  SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
1851 
1852  psDBF->panFieldDecimals = (int *)
1853  SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
1854 
1855  psDBF->pachFieldType = (char *)
1856  SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
1857 
1858  /* update header information */
1859  psDBF->nHeaderLength -= 32;
1860  psDBF->nRecordLength -= nDeletedFieldSize;
1861 
1862  /* overwrite field information in header */
1863  memmove(psDBF->pszHeader + iField*32,
1864  psDBF->pszHeader + (iField+1)*32,
1865  sizeof(char) * (psDBF->nFields - iField)*32);
1866 
1867  psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
1868 
1869  /* update size of current record appropriately */
1870  psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
1871  psDBF->nRecordLength);
1872 
1873  /* we're done if we're dealing with not yet created .dbf */
1874  if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
1875  return TRUE;
1876 
1877  /* force update of header with new header and record length */
1878  psDBF->bNoHeader = TRUE;
1879  DBFUpdateHeader( psDBF );
1880 
1881  /* alloc record */
1882  pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
1883 
1884  /* shift records to their new positions */
1885  for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
1886  {
1887  nRecordOffset =
1888  nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength;
1889 
1890  /* load record */
1891  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1892  psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
1893 
1894  nRecordOffset =
1895  psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
1896 
1897  /* move record in two steps */
1898  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1899  psDBF->sHooks.FWrite( pszRecord, nDeletedFieldOffset, 1, psDBF->fp );
1900  psDBF->sHooks.FWrite( pszRecord + nDeletedFieldOffset + nDeletedFieldSize,
1901  nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize,
1902  1, psDBF->fp );
1903 
1904  }
1905 
1906  /* TODO: truncate file */
1907 
1908  /* free record */
1909  free(pszRecord);
1910 
1911  psDBF->nCurrentRecord = -1;
1912  psDBF->bCurrentRecordModified = FALSE;
1913 
1914  return TRUE;
1915 }
1916 
1917 /************************************************************************/
1918 /* DBFReorderFields() */
1919 /* */
1920 /* Reorder the fields of a .dbf file */
1921 /* */
1922 /* panMap must be exactly psDBF->nFields long and be a permutation */
1923 /* of [0, psDBF->nFields-1]. This assumption will not be asserted in the*/
1924 /* code of DBFReorderFields. */
1925 /************************************************************************/
1926 
1927 int SHPAPI_CALL
1928 DBFReorderFields( DBFHandle psDBF, int* panMap )
1929 {
1930  SAOffset nRecordOffset;
1931  int i, iRecord;
1932  int *panFieldOffsetNew;
1933  int *panFieldSizeNew;
1934  int *panFieldDecimalsNew;
1935  char *pachFieldTypeNew;
1936  char *pszHeaderNew;
1937  char *pszRecord;
1938  char *pszRecordNew;
1939 
1940  if ( psDBF->nFields == 0 )
1941  return TRUE;
1942 
1943  /* make sure that everything is written in .dbf */
1944  if( !DBFFlushRecord( psDBF ) )
1945  return FALSE;
1946 
1947  panFieldOffsetNew = (int *) malloc(sizeof(int) * psDBF->nFields);
1948  panFieldSizeNew = (int *) malloc(sizeof(int) * psDBF->nFields);
1949  panFieldDecimalsNew = (int *) malloc(sizeof(int) * psDBF->nFields);
1950  pachFieldTypeNew = (char *) malloc(sizeof(char) * psDBF->nFields);
1951  pszHeaderNew = (char*) malloc(sizeof(char) * 32 * psDBF->nFields);
1952 
1953  /* shuffle fields definitions */
1954  for(i=0; i < psDBF->nFields; i++)
1955  {
1956  panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]];
1957  panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]];
1958  pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]];
1959  memcpy(pszHeaderNew + i * 32,
1960  psDBF->pszHeader + panMap[i] * 32, 32);
1961  }
1962  panFieldOffsetNew[0] = 1;
1963  for(i=1; i < psDBF->nFields; i++)
1964  {
1965  panFieldOffsetNew[i] = panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
1966  }
1967 
1968  free(psDBF->pszHeader);
1969  psDBF->pszHeader = pszHeaderNew;
1970 
1971  /* we're done if we're dealing with not yet created .dbf */
1972  if ( !(psDBF->bNoHeader && psDBF->nRecords == 0) )
1973  {
1974  /* force update of header with new header and record length */
1975  psDBF->bNoHeader = TRUE;
1976  DBFUpdateHeader( psDBF );
1977 
1978  /* alloc record */
1979  pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
1980  pszRecordNew = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
1981 
1982  /* shuffle fields in records */
1983  for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
1984  {
1985  nRecordOffset =
1986  psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
1987 
1988  /* load record */
1989  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1990  psDBF->sHooks.FRead( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
1991 
1992  pszRecordNew[0] = pszRecord[0];
1993 
1994  for(i=0; i < psDBF->nFields; i++)
1995  {
1996  memcpy(pszRecordNew + panFieldOffsetNew[i],
1997  pszRecord + psDBF->panFieldOffset[panMap[i]],
1998  psDBF->panFieldSize[panMap[i]]);
1999  }
2000 
2001  /* write record */
2002  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2003  psDBF->sHooks.FWrite( pszRecordNew, psDBF->nRecordLength, 1, psDBF->fp );
2004  }
2005 
2006  /* free record */
2007  free(pszRecord);
2008  free(pszRecordNew);
2009  }
2010 
2011  free(psDBF->panFieldOffset);
2012  free(psDBF->panFieldSize);
2013  free(psDBF->panFieldDecimals);
2014  free(psDBF->pachFieldType);
2015 
2016  psDBF->panFieldOffset = panFieldOffsetNew;
2017  psDBF->panFieldSize = panFieldSizeNew;
2018  psDBF->panFieldDecimals =panFieldDecimalsNew;
2019  psDBF->pachFieldType = pachFieldTypeNew;
2020 
2021  psDBF->nCurrentRecord = -1;
2022  psDBF->bCurrentRecordModified = FALSE;
2023 
2024  return TRUE;
2025 }
2026 
2027 
2028 /************************************************************************/
2029 /* DBFAlterFieldDefn() */
2030 /* */
2031 /* Alter a field definition in a .dbf file */
2032 /************************************************************************/
2033 
2034 int SHPAPI_CALL
2035 DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName,
2036  char chType, int nWidth, int nDecimals )
2037 {
2038  int i;
2039  int iRecord;
2040  int nOffset;
2041  int nOldWidth;
2042  int nOldRecordLength;
2043  int nRecordOffset;
2044  char* pszFInfo;
2045  char chOldType;
2046  int bIsNULL;
2047  char chFieldFill;
2048 
2049  if (iField < 0 || iField >= psDBF->nFields)
2050  return FALSE;
2051 
2052  /* make sure that everything is written in .dbf */
2053  if( !DBFFlushRecord( psDBF ) )
2054  return FALSE;
2055 
2056  chFieldFill = DBFGetNullCharacter(chType);
2057 
2058  chOldType = psDBF->pachFieldType[iField];
2059  nOffset = psDBF->panFieldOffset[iField];
2060  nOldWidth = psDBF->panFieldSize[iField];
2061  nOldRecordLength = psDBF->nRecordLength;
2062 
2063 /* -------------------------------------------------------------------- */
2064 /* Do some checking to ensure we can add records to this file. */
2065 /* -------------------------------------------------------------------- */
2066  if( nWidth < 1 )
2067  return -1;
2068 
2069  if( nWidth > 255 )
2070  nWidth = 255;
2071 
2072 /* -------------------------------------------------------------------- */
2073 /* Assign the new field information fields. */
2074 /* -------------------------------------------------------------------- */
2075  psDBF->panFieldSize[iField] = nWidth;
2076  psDBF->panFieldDecimals[iField] = nDecimals;
2077  psDBF->pachFieldType[iField] = chType;
2078 
2079 /* -------------------------------------------------------------------- */
2080 /* Update the header information. */
2081 /* -------------------------------------------------------------------- */
2082  pszFInfo = psDBF->pszHeader + 32 * iField;
2083 
2084  for( i = 0; i < 32; i++ )
2085  pszFInfo[i] = '\0';
2086 
2087  if( (int) strlen(pszFieldName) < 10 )
2088  strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
2089  else
2090  strncpy( pszFInfo, pszFieldName, 10);
2091 
2092  pszFInfo[11] = psDBF->pachFieldType[iField];
2093 
2094  if( chType == 'C' )
2095  {
2096  pszFInfo[16] = (unsigned char) (nWidth % 256);
2097  pszFInfo[17] = (unsigned char) (nWidth / 256);
2098  }
2099  else
2100  {
2101  pszFInfo[16] = (unsigned char) nWidth;
2102  pszFInfo[17] = (unsigned char) nDecimals;
2103  }
2104 
2105 /* -------------------------------------------------------------------- */
2106 /* Update offsets */
2107 /* -------------------------------------------------------------------- */
2108  if (nWidth != nOldWidth)
2109  {
2110  for (i = iField + 1; i < psDBF->nFields; i++)
2111  psDBF->panFieldOffset[i] += nWidth - nOldWidth;
2112  psDBF->nRecordLength += nWidth - nOldWidth;
2113 
2114  psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
2115  psDBF->nRecordLength);
2116  }
2117 
2118  /* we're done if we're dealing with not yet created .dbf */
2119  if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
2120  return TRUE;
2121 
2122  /* force update of header with new header and record length */
2123  psDBF->bNoHeader = TRUE;
2124  DBFUpdateHeader( psDBF );
2125 
2126  if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType))
2127  {
2128  char* pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
2129  char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
2130 
2131  pszOldField[nOldWidth] = 0;
2132 
2133  /* move records to their new positions */
2134  for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
2135  {
2136  nRecordOffset =
2137  nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2138 
2139  /* load record */
2140  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2141  psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
2142 
2143  memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2144  bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
2145 
2146  if (nWidth != nOldWidth)
2147  {
2148  if ((chOldType == 'N' || chOldType == 'F') && pszOldField[0] == ' ')
2149  {
2150  /* Strip leading spaces when truncating a numeric field */
2151  memmove( pszRecord + nOffset,
2152  pszRecord + nOffset + nOldWidth - nWidth,
2153  nWidth );
2154  }
2155  if (nOffset + nOldWidth < nOldRecordLength)
2156  {
2157  memmove( pszRecord + nOffset + nWidth,
2158  pszRecord + nOffset + nOldWidth,
2159  nOldRecordLength - (nOffset + nOldWidth));
2160  }
2161  }
2162 
2163  /* Convert null value to the appropriate value of the new type */
2164  if (bIsNULL)
2165  {
2166  memset( pszRecord + nOffset, chFieldFill, nWidth);
2167  }
2168 
2169  nRecordOffset =
2170  psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2171 
2172  /* write record */
2173  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2174  psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
2175  }
2176 
2177  free(pszRecord);
2178  free(pszOldField);
2179  }
2180  else if (nWidth > nOldWidth)
2181  {
2182  char* pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
2183  char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
2184 
2185  pszOldField[nOldWidth] = 0;
2186 
2187  /* move records to their new positions */
2188  for (iRecord = psDBF->nRecords - 1; iRecord >= 0; iRecord--)
2189  {
2190  nRecordOffset =
2191  nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2192 
2193  /* load record */
2194  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2195  psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
2196 
2197  memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2198  bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
2199 
2200  if (nOffset + nOldWidth < nOldRecordLength)
2201  {
2202  memmove( pszRecord + nOffset + nWidth,
2203  pszRecord + nOffset + nOldWidth,
2204  nOldRecordLength - (nOffset + nOldWidth));
2205  }
2206 
2207  /* Convert null value to the appropriate value of the new type */
2208  if (bIsNULL)
2209  {
2210  memset( pszRecord + nOffset, chFieldFill, nWidth);
2211  }
2212  else
2213  {
2214  if ((chOldType == 'N' || chOldType == 'F'))
2215  {
2216  /* Add leading spaces when expanding a numeric field */
2217  memmove( pszRecord + nOffset + nWidth - nOldWidth,
2218  pszRecord + nOffset, nOldWidth );
2219  memset( pszRecord + nOffset, ' ', nWidth - nOldWidth );
2220  }
2221  else
2222  {
2223  /* Add trailing spaces */
2224  memset(pszRecord + nOffset + nOldWidth, ' ', nWidth - nOldWidth);
2225  }
2226  }
2227 
2228  nRecordOffset =
2229  psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2230 
2231  /* write record */
2232  psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2233  psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
2234  }
2235 
2236  free(pszRecord);
2237  free(pszOldField);
2238  }
2239 
2240  psDBF->nCurrentRecord = -1;
2241  psDBF->bCurrentRecordModified = FALSE;
2242 
2243  return TRUE;
2244 }
DBFHandle SHPAPI_CALL DBFCreate(const char *pszFilename)
Definition: dbfopen.c:628
int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
Definition: dbfopen.c:1703
int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, int *panMap)
Definition: dbfopen.c:1928
int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void *pRawTuple)
Definition: dbfopen.c:1549
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:1049
int SHPAPI_CALL DBFWriteStringAttribute(DBFHandle psDBF, int iRecord, int iField, const char *pszValue)
Definition: dbfopen.c:1508
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:1478
int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName, char chType, int nWidth, int nDecimals)
Definition: dbfopen.c:820
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:1410
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:2035
#define XBASE_FLDHDR_SZ
int SHPAPI_CALL DBFGetRecordCount(DBFHandle psDBF)
Definition: dbfopen.c:1208
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:1115
static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, void *pValue)
Definition: dbfopen.c:1271
int SHPAPI_CALL DBFMarkRecordDeleted(DBFHandle psDBF, int iShape, int bIsDeleted)
Definition: dbfopen.c:1757
int SHPAPI_CALL DBFWriteNULLAttribute(DBFHandle psDBF, int iRecord, int iField)
Definition: dbfopen.c:1522
DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField, char *pszFieldName, int *pnWidth, int *pnDecimals)
Definition: dbfopen.c:1221
int SHPAPI_CALL DBFGetFieldCount(DBFHandle psDBF)
Definition: dbfopen.c:1195
DBFHandle SHPAPI_CALL DBFOpen(const char *pszFilename, const char *pszAccess)
Definition: dbfopen.c:365
static void str_to_upper(char *string)
Definition: dbfopen.c:1682
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:1799
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:1535
static int DBFFlushRecord(DBFHandle psDBF)
Definition: dbfopen.c:258
double SHPAPI_CALL DBFReadDoubleAttribute(DBFHandle psDBF, int iRecord, int iField)
Definition: dbfopen.c:1069
#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:773
DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char *pszFilename)
static void * DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, char chReqType)
Definition: dbfopen.c:961
#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:1492
void SHPAPI_CALL DBFClose(DBFHandle psDBF)
Definition: dbfopen.c:578
static char DBFGetNullCharacter(char chType)
Definition: dbfopen.c:796
void free(void *)
void * malloc(YYSIZE_T)
int SHPAPI_CALL DBFIsAttributeNULL(DBFHandle psDBF, int iRecord, int iField)
Definition: dbfopen.c:1175
int * SAFile
Definition: shapefil.h:242
char SHPAPI_CALL DBFGetNativeFieldType(DBFHandle psDBF, int iField)
Definition: dbfopen.c:1669
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:195
int SHPAPI_CALL DBFIsRecordDeleted(DBFHandle psDBF, int iShape)
Definition: dbfopen.c:1732