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