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 : static void * SfRealloc( void * pMem, int nNewSize )
166 :
167 2396 : {
168 2396 : if( pMem == NULL )
169 762 : return( (void *) malloc(nNewSize) );
170 : else
171 1634 : 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 : static void DBFWriteHeader(DBFHandle psDBF)
184 :
185 131 : {
186 : unsigned char abyHeader[XBASE_FLDHDR_SZ];
187 : int i;
188 :
189 131 : if( !psDBF->bNoHeader )
190 0 : return;
191 :
192 131 : psDBF->bNoHeader = FALSE;
193 :
194 : /* -------------------------------------------------------------------- */
195 : /* Initialize the file header information. */
196 : /* -------------------------------------------------------------------- */
197 4323 : for( i = 0; i < XBASE_FLDHDR_SZ; i++ )
198 4192 : abyHeader[i] = 0;
199 :
200 131 : abyHeader[0] = 0x03; /* memo field? - just copying */
201 :
202 : /* write out a dummy date */
203 131 : abyHeader[1] = 95; /* YY */
204 131 : abyHeader[2] = 7; /* MM */
205 131 : abyHeader[3] = 26; /* DD */
206 :
207 : /* record count preset at zero */
208 :
209 131 : abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256);
210 131 : abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256);
211 :
212 131 : abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256);
213 131 : abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256);
214 :
215 131 : abyHeader[29] = (unsigned char) (psDBF->iLanguageDriver);
216 :
217 : /* -------------------------------------------------------------------- */
218 : /* Write the initial 32 byte file header, and all the field */
219 : /* descriptions. */
220 : /* -------------------------------------------------------------------- */
221 131 : psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
222 131 : psDBF->sHooks.FWrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp );
223 131 : 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 131 : if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 )
230 : {
231 : char cNewline;
232 :
233 131 : cNewline = 0x0d;
234 131 : 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 : static int DBFFlushRecord( DBFHandle psDBF )
245 :
246 34962 : {
247 : SAOffset nRecordOffset;
248 :
249 34962 : if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 )
250 : {
251 15901 : psDBF->bCurrentRecordModified = FALSE;
252 :
253 15901 : nRecordOffset =
254 : psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord
255 : + psDBF->nHeaderLength;
256 :
257 15901 : if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0
258 : || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord,
259 : psDBF->nRecordLength,
260 : 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 34962 : return TRUE;
275 : }
276 :
277 : /************************************************************************/
278 : /* DBFLoadRecord() */
279 : /************************************************************************/
280 :
281 : static int DBFLoadRecord( DBFHandle psDBF, int iRecord )
282 :
283 94426 : {
284 94426 : if( psDBF->nCurrentRecord != iRecord )
285 : {
286 : SAOffset nRecordOffset;
287 :
288 17906 : if( !DBFFlushRecord( psDBF ) )
289 0 : return FALSE;
290 :
291 17906 : nRecordOffset =
292 : psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
293 :
294 17906 : 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 17906 : if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord,
308 : 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 17906 : psDBF->nCurrentRecord = iRecord;
322 : }
323 :
324 94426 : return TRUE;
325 : }
326 :
327 : /************************************************************************/
328 : /* DBFUpdateHeader() */
329 : /************************************************************************/
330 :
331 : void SHPAPI_CALL
332 : DBFUpdateHeader( DBFHandle psDBF )
333 :
334 147 : {
335 : unsigned char abyFileHeader[32];
336 :
337 147 : if( psDBF->bNoHeader )
338 1 : DBFWriteHeader( psDBF );
339 :
340 147 : DBFFlushRecord( psDBF );
341 :
342 147 : psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
343 147 : psDBF->sHooks.FRead( abyFileHeader, 32, 1, psDBF->fp );
344 :
345 147 : abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256);
346 147 : abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256);
347 147 : abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256);
348 147 : abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256);
349 :
350 147 : psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
351 147 : psDBF->sHooks.FWrite( abyFileHeader, 32, 1, psDBF->fp );
352 :
353 147 : psDBF->sHooks.FFlush( psDBF->fp );
354 147 : }
355 :
356 : /************************************************************************/
357 : /* DBFOpen() */
358 : /* */
359 : /* Open a .dbf file. */
360 : /************************************************************************/
361 :
362 : DBFHandle SHPAPI_CALL
363 : DBFOpen( const char * pszFilename, const char * pszAccess )
364 :
365 902 : {
366 : SAHooks sHooks;
367 :
368 902 : SASetupDefaultHooks( &sHooks );
369 :
370 902 : return DBFOpenLL( pszFilename, pszAccess, &sHooks );
371 : }
372 :
373 : /************************************************************************/
374 : /* DBFOpen() */
375 : /* */
376 : /* Open a .dbf file. */
377 : /************************************************************************/
378 :
379 : DBFHandle SHPAPI_CALL
380 : DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks )
381 :
382 902 : {
383 : DBFHandle psDBF;
384 : SAFile pfCPG;
385 : unsigned char *pabyBuf;
386 : int nFields, nHeadLen, iField, i;
387 : char *pszBasename, *pszFullname;
388 902 : int nBufSize = 500;
389 :
390 : /* -------------------------------------------------------------------- */
391 : /* We only allow the access strings "rb" and "r+". */
392 : /* -------------------------------------------------------------------- */
393 902 : if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0
394 : && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0
395 : && strcmp(pszAccess,"r+b") != 0 )
396 0 : return( NULL );
397 :
398 902 : if( strcmp(pszAccess,"r") == 0 )
399 240 : pszAccess = "rb";
400 :
401 902 : if( strcmp(pszAccess,"r+") == 0 )
402 659 : 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 902 : pszBasename = (char *) malloc(strlen(pszFilename)+5);
409 902 : strcpy( pszBasename, pszFilename );
410 902 : for( i = strlen(pszBasename)-1;
411 4510 : i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
412 : && pszBasename[i] != '\\';
413 2706 : i-- ) {}
414 :
415 902 : if( pszBasename[i] == '.' )
416 902 : pszBasename[i] = '\0';
417 :
418 902 : pszFullname = (char *) malloc(strlen(pszBasename) + 5);
419 902 : sprintf( pszFullname, "%s.dbf", pszBasename );
420 :
421 902 : psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) );
422 902 : psDBF->fp = psHooks->FOpen( pszFullname, pszAccess );
423 902 : memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
424 :
425 902 : if( psDBF->fp == NULL )
426 : {
427 20 : sprintf( pszFullname, "%s.DBF", pszBasename );
428 20 : psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess );
429 : }
430 :
431 902 : sprintf( pszFullname, "%s.cpg", pszBasename );
432 902 : pfCPG = psHooks->FOpen( pszFullname, "r" );
433 902 : if( pfCPG == NULL )
434 : {
435 902 : sprintf( pszFullname, "%s.CPG", pszBasename );
436 902 : pfCPG = psHooks->FOpen( pszFullname, "r" );
437 : }
438 :
439 902 : free( pszBasename );
440 902 : free( pszFullname );
441 :
442 902 : if( psDBF->fp == NULL )
443 : {
444 18 : free( psDBF );
445 18 : if( pfCPG ) psHooks->FClose( pfCPG );
446 18 : return( NULL );
447 : }
448 :
449 884 : psDBF->bNoHeader = FALSE;
450 884 : psDBF->nCurrentRecord = -1;
451 884 : psDBF->bCurrentRecordModified = FALSE;
452 :
453 : /* -------------------------------------------------------------------- */
454 : /* Read Table Header info */
455 : /* -------------------------------------------------------------------- */
456 884 : pabyBuf = (unsigned char *) malloc(nBufSize);
457 884 : 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 884 : psDBF->nRecords =
467 : pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256;
468 :
469 884 : psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256;
470 884 : psDBF->nRecordLength = pabyBuf[10] + pabyBuf[11]*256;
471 884 : psDBF->iLanguageDriver = pabyBuf[29];
472 :
473 884 : 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 884 : psDBF->nFields = nFields = (nHeadLen - 32) / 32;
483 :
484 884 : psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength);
485 :
486 : /* -------------------------------------------------------------------- */
487 : /* Figure out the code page from the LDID and CPG */
488 : /* -------------------------------------------------------------------- */
489 :
490 884 : psDBF->pszCodePage = NULL;
491 884 : 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 884 : if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 )
507 : {
508 725 : sprintf( (char *) pabyBuf, "LDID/%d", psDBF->iLanguageDriver );
509 725 : psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1);
510 725 : strcpy( psDBF->pszCodePage, (char *) pabyBuf );
511 : }
512 :
513 : /* -------------------------------------------------------------------- */
514 : /* Read in Field Definitions */
515 : /* -------------------------------------------------------------------- */
516 :
517 884 : pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen);
518 884 : psDBF->pszHeader = (char *) pabyBuf;
519 :
520 884 : psDBF->sHooks.FSeek( psDBF->fp, 32, 0 );
521 884 : 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 884 : psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields);
531 884 : psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields);
532 884 : psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields);
533 884 : psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields);
534 :
535 2361 : for( iField = 0; iField < nFields; iField++ )
536 : {
537 : unsigned char *pabyFInfo;
538 :
539 1477 : pabyFInfo = pabyBuf+iField*32;
540 :
541 2585 : if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' )
542 : {
543 1108 : psDBF->panFieldSize[iField] = pabyFInfo[16];
544 1108 : psDBF->panFieldDecimals[iField] = pabyFInfo[17];
545 : }
546 : else
547 : {
548 369 : psDBF->panFieldSize[iField] = pabyFInfo[16];
549 369 : 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 1477 : psDBF->pachFieldType[iField] = (char) pabyFInfo[11];
562 1477 : if( iField == 0 )
563 884 : psDBF->panFieldOffset[iField] = 1;
564 : else
565 593 : psDBF->panFieldOffset[iField] =
566 : psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1];
567 : }
568 :
569 884 : return( psDBF );
570 : }
571 :
572 : /************************************************************************/
573 : /* DBFClose() */
574 : /************************************************************************/
575 :
576 : void SHPAPI_CALL
577 : DBFClose(DBFHandle psDBF)
578 1014 : {
579 1014 : if( psDBF == NULL )
580 0 : return;
581 :
582 : /* -------------------------------------------------------------------- */
583 : /* Write out header if not already written. */
584 : /* -------------------------------------------------------------------- */
585 1014 : if( psDBF->bNoHeader )
586 3 : DBFWriteHeader( psDBF );
587 :
588 1014 : DBFFlushRecord( psDBF );
589 :
590 : /* -------------------------------------------------------------------- */
591 : /* Update last access date, and number of records if we have */
592 : /* write access. */
593 : /* -------------------------------------------------------------------- */
594 1014 : if( psDBF->bUpdated )
595 139 : DBFUpdateHeader( psDBF );
596 :
597 : /* -------------------------------------------------------------------- */
598 : /* Close, and free resources. */
599 : /* -------------------------------------------------------------------- */
600 1014 : psDBF->sHooks.FClose( psDBF->fp );
601 :
602 1014 : if( psDBF->panFieldOffset != NULL )
603 : {
604 1014 : free( psDBF->panFieldOffset );
605 1014 : free( psDBF->panFieldSize );
606 1014 : free( psDBF->panFieldDecimals );
607 1014 : free( psDBF->pachFieldType );
608 : }
609 :
610 1014 : if( psDBF->pszWorkField != NULL )
611 212 : free( psDBF->pszWorkField );
612 :
613 1014 : free( psDBF->pszHeader );
614 1014 : free( psDBF->pszCurrentRecord );
615 1014 : free( psDBF->pszCodePage );
616 :
617 1014 : 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 : DBFCreate( const char * pszFilename )
628 :
629 127 : {
630 127 : 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 : DBFCreateEx( const char * pszFilename, const char* pszCodePage )
641 :
642 130 : {
643 : SAHooks sHooks;
644 :
645 130 : SASetupDefaultHooks( &sHooks );
646 :
647 130 : return DBFCreateLL( pszFilename, pszCodePage , &sHooks );
648 : }
649 :
650 : /************************************************************************/
651 : /* DBFCreate() */
652 : /* */
653 : /* Create a new .dbf file. */
654 : /************************************************************************/
655 :
656 : DBFHandle SHPAPI_CALL
657 : DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHooks )
658 :
659 130 : {
660 : DBFHandle psDBF;
661 : SAFile fp;
662 : char *pszFullname, *pszBasename;
663 130 : int i, ldid = -1;
664 130 : 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 130 : pszBasename = (char *) malloc(strlen(pszFilename)+5);
671 130 : strcpy( pszBasename, pszFilename );
672 130 : for( i = strlen(pszBasename)-1;
673 650 : i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
674 : && pszBasename[i] != '\\';
675 390 : i-- ) {}
676 :
677 130 : if( pszBasename[i] == '.' )
678 130 : pszBasename[i] = '\0';
679 :
680 130 : pszFullname = (char *) malloc(strlen(pszBasename) + 5);
681 130 : sprintf( pszFullname, "%s.dbf", pszBasename );
682 :
683 : /* -------------------------------------------------------------------- */
684 : /* Create the file. */
685 : /* -------------------------------------------------------------------- */
686 130 : fp = psHooks->FOpen( pszFullname, "wb" );
687 130 : if( fp == NULL )
688 0 : return( NULL );
689 :
690 130 : psHooks->FWrite( &chZero, 1, 1, fp );
691 130 : psHooks->FClose( fp );
692 :
693 130 : fp = psHooks->FOpen( pszFullname, "rb+" );
694 130 : if( fp == NULL )
695 0 : return( NULL );
696 :
697 :
698 130 : sprintf( pszFullname, "%s.cpg", pszBasename );
699 130 : if( pszCodePage != NULL )
700 : {
701 128 : if( strncmp( pszCodePage, "LDID/", 5 ) == 0 )
702 : {
703 128 : ldid = atoi( pszCodePage + 5 );
704 128 : 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 128 : 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 130 : if( pszCodePage == NULL || ldid >= 0 )
715 : {
716 130 : psHooks->Remove( pszFullname );
717 : }
718 :
719 130 : free( pszBasename );
720 130 : free( pszFullname );
721 :
722 : /* -------------------------------------------------------------------- */
723 : /* Create the info structure. */
724 : /* -------------------------------------------------------------------- */
725 130 : psDBF = (DBFHandle) calloc(1,sizeof(DBFInfo));
726 :
727 130 : memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
728 130 : psDBF->fp = fp;
729 130 : psDBF->nRecords = 0;
730 130 : psDBF->nFields = 0;
731 130 : psDBF->nRecordLength = 1;
732 130 : psDBF->nHeaderLength = 33;
733 :
734 130 : psDBF->panFieldOffset = NULL;
735 130 : psDBF->panFieldSize = NULL;
736 130 : psDBF->panFieldDecimals = NULL;
737 130 : psDBF->pachFieldType = NULL;
738 130 : psDBF->pszHeader = NULL;
739 :
740 130 : psDBF->nCurrentRecord = -1;
741 130 : psDBF->bCurrentRecordModified = FALSE;
742 130 : psDBF->pszCurrentRecord = NULL;
743 :
744 130 : psDBF->bNoHeader = TRUE;
745 :
746 130 : psDBF->iLanguageDriver = ldid > 0 ? ldid : 0;
747 130 : psDBF->pszCodePage = NULL;
748 130 : if( pszCodePage )
749 : {
750 128 : psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 );
751 128 : strcpy( psDBF->pszCodePage, pszCodePage );
752 : }
753 :
754 130 : 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 : DBFAddField(DBFHandle psDBF, const char * pszFieldName,
765 : DBFFieldType eType, int nWidth, int nDecimals )
766 :
767 251 : {
768 251 : char chNativeType = 'C';
769 :
770 251 : if( eType == FTLogical )
771 0 : chNativeType = 'L';
772 251 : else if( eType == FTString )
773 64 : chNativeType = 'C';
774 : else
775 187 : chNativeType = 'N';
776 :
777 251 : 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 : DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName,
790 : char chType, int nWidth, int nDecimals )
791 :
792 252 : {
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 252 : if( nWidth < 1 )
804 0 : return -1;
805 :
806 252 : if( nWidth > 255 )
807 0 : nWidth = 255;
808 :
809 252 : nOldRecordLength = psDBF->nRecordLength;
810 252 : nOldHeaderLength = psDBF->nHeaderLength;
811 :
812 : /* -------------------------------------------------------------------- */
813 : /* SfRealloc all the arrays larger to hold the additional field */
814 : /* information. */
815 : /* -------------------------------------------------------------------- */
816 252 : psDBF->nFields++;
817 :
818 252 : psDBF->panFieldOffset = (int *)
819 : SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
820 :
821 252 : psDBF->panFieldSize = (int *)
822 : SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
823 :
824 252 : psDBF->panFieldDecimals = (int *)
825 : SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
826 :
827 252 : psDBF->pachFieldType = (char *)
828 : SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
829 :
830 : /* -------------------------------------------------------------------- */
831 : /* Assign the new field information fields. */
832 : /* -------------------------------------------------------------------- */
833 252 : psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength;
834 252 : psDBF->nRecordLength += nWidth;
835 252 : psDBF->panFieldSize[psDBF->nFields-1] = nWidth;
836 252 : psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals;
837 252 : psDBF->pachFieldType[psDBF->nFields-1] = chType;
838 :
839 : /* -------------------------------------------------------------------- */
840 : /* Extend the required header information. */
841 : /* -------------------------------------------------------------------- */
842 252 : psDBF->nHeaderLength += 32;
843 252 : psDBF->bUpdated = FALSE;
844 :
845 252 : psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
846 :
847 252 : pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1);
848 :
849 8316 : for( i = 0; i < 32; i++ )
850 8064 : pszFInfo[i] = '\0';
851 :
852 252 : if( (int) strlen(pszFieldName) < 10 )
853 227 : strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
854 : else
855 25 : strncpy( pszFInfo, pszFieldName, 10);
856 :
857 252 : pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1];
858 :
859 252 : if( chType == 'C' )
860 : {
861 64 : pszFInfo[16] = (unsigned char) (nWidth % 256);
862 64 : pszFInfo[17] = (unsigned char) (nWidth / 256);
863 : }
864 : else
865 : {
866 188 : pszFInfo[16] = (unsigned char) nWidth;
867 188 : pszFInfo[17] = (unsigned char) nDecimals;
868 : }
869 :
870 : /* -------------------------------------------------------------------- */
871 : /* Make the current record buffer appropriately larger. */
872 : /* -------------------------------------------------------------------- */
873 252 : psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
874 : psDBF->nRecordLength);
875 :
876 : /* we're done if dealing with new .dbf */
877 252 : if( psDBF->bNoHeader )
878 251 : 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 : static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField,
939 : char chReqType )
940 :
941 42098 : {
942 : unsigned char *pabyRec;
943 42098 : void *pReturnField = NULL;
944 :
945 : /* -------------------------------------------------------------------- */
946 : /* Verify selection. */
947 : /* -------------------------------------------------------------------- */
948 42098 : if( hEntity < 0 || hEntity >= psDBF->nRecords )
949 0 : return( NULL );
950 :
951 42098 : if( iField < 0 || iField >= psDBF->nFields )
952 0 : return( NULL );
953 :
954 : /* -------------------------------------------------------------------- */
955 : /* Have we read the record? */
956 : /* -------------------------------------------------------------------- */
957 42098 : if( !DBFLoadRecord( psDBF, hEntity ) )
958 0 : return NULL;
959 :
960 42098 : pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
961 :
962 : /* -------------------------------------------------------------------- */
963 : /* Ensure we have room to extract the target field. */
964 : /* -------------------------------------------------------------------- */
965 42098 : if( psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength )
966 : {
967 212 : psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100;
968 212 : if( psDBF->pszWorkField == NULL )
969 212 : psDBF->pszWorkField = (char *) malloc(psDBF->nWorkFieldLength);
970 : else
971 0 : psDBF->pszWorkField = (char *) realloc(psDBF->pszWorkField,
972 : psDBF->nWorkFieldLength);
973 : }
974 :
975 : /* -------------------------------------------------------------------- */
976 : /* Extract the requested field. */
977 : /* -------------------------------------------------------------------- */
978 42098 : strncpy( psDBF->pszWorkField,
979 : ((const char *) pabyRec) + psDBF->panFieldOffset[iField],
980 : psDBF->panFieldSize[iField] );
981 42098 : psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0';
982 :
983 42098 : pReturnField = psDBF->pszWorkField;
984 :
985 : /* -------------------------------------------------------------------- */
986 : /* Decode the field. */
987 : /* -------------------------------------------------------------------- */
988 42098 : if( chReqType == 'N' )
989 : {
990 19002 : psDBF->dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField);
991 :
992 19002 : 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 23096 : pchDst = pchSrc = psDBF->pszWorkField;
1004 207912 : while( *pchSrc == ' ' )
1005 161720 : pchSrc++;
1006 :
1007 246504 : while( *pchSrc != '\0' )
1008 200312 : *(pchDst++) = *(pchSrc++);
1009 23096 : *pchDst = '\0';
1010 :
1011 135860 : while( pchDst != psDBF->pszWorkField && *(--pchDst) == ' ' )
1012 89668 : *pchDst = '\0';
1013 : }
1014 : #endif
1015 :
1016 42098 : return( pReturnField );
1017 : }
1018 :
1019 : /************************************************************************/
1020 : /* DBFReadIntAttribute() */
1021 : /* */
1022 : /* Read an integer attribute. */
1023 : /************************************************************************/
1024 :
1025 : int SHPAPI_CALL
1026 : DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField )
1027 :
1028 1684 : {
1029 : double *pdValue;
1030 :
1031 1684 : pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
1032 :
1033 1684 : if( pdValue == NULL )
1034 0 : return 0;
1035 : else
1036 1684 : return( (int) *pdValue );
1037 : }
1038 :
1039 : /************************************************************************/
1040 : /* DBFReadDoubleAttribute() */
1041 : /* */
1042 : /* Read a double attribute. */
1043 : /************************************************************************/
1044 :
1045 : double SHPAPI_CALL
1046 : DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField )
1047 :
1048 17318 : {
1049 : double *pdValue;
1050 :
1051 17318 : pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
1052 :
1053 17318 : if( pdValue == NULL )
1054 0 : return 0.0;
1055 : else
1056 17318 : return( *pdValue );
1057 : }
1058 :
1059 : /************************************************************************/
1060 : /* DBFReadStringAttribute() */
1061 : /* */
1062 : /* Read a string attribute. */
1063 : /************************************************************************/
1064 :
1065 : const char SHPAPI_CALL1(*)
1066 : DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField )
1067 :
1068 23096 : {
1069 23096 : 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 : DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField )
1080 :
1081 0 : {
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 : DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField )
1095 :
1096 21304 : {
1097 : const char *pszValue;
1098 : int i;
1099 :
1100 21304 : pszValue = DBFReadStringAttribute( psDBF, iRecord, iField );
1101 :
1102 21304 : if( pszValue == NULL )
1103 0 : return TRUE;
1104 :
1105 21304 : 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 19018 : if( pszValue[0] == '*' )
1115 16 : return TRUE;
1116 :
1117 19002 : for( i = 0; pszValue[i] != '\0'; i++ )
1118 : {
1119 19002 : if( pszValue[i] != ' ' )
1120 19002 : 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 2284 : 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 : DBFGetFieldCount( DBFHandle psDBF )
1146 :
1147 21496 : {
1148 21496 : 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 : DBFGetRecordCount( DBFHandle psDBF )
1159 :
1160 31737 : {
1161 31737 : return( psDBF->nRecords );
1162 : }
1163 :
1164 : /************************************************************************/
1165 : /* DBFGetFieldInfo() */
1166 : /* */
1167 : /* Return any requested information about the field. */
1168 : /************************************************************************/
1169 :
1170 : DBFFieldType SHPAPI_CALL
1171 : DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName,
1172 : int * pnWidth, int * pnDecimals )
1173 :
1174 3918 : {
1175 3918 : if( iField < 0 || iField >= psDBF->nFields )
1176 0 : return( FTInvalid );
1177 :
1178 3918 : if( pnWidth != NULL )
1179 1459 : *pnWidth = psDBF->panFieldSize[iField];
1180 :
1181 3918 : if( pnDecimals != NULL )
1182 1459 : *pnDecimals = psDBF->panFieldDecimals[iField];
1183 :
1184 3918 : if( pszFieldName != NULL )
1185 : {
1186 : int i;
1187 :
1188 3918 : strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 );
1189 3918 : pszFieldName[11] = '\0';
1190 3918 : for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- )
1191 0 : pszFieldName[i] = '\0';
1192 : }
1193 :
1194 3918 : if ( psDBF->pachFieldType[iField] == 'L' )
1195 0 : return( FTLogical);
1196 :
1197 3918 : else if( psDBF->pachFieldType[iField] == 'N'
1198 : || psDBF->pachFieldType[iField] == 'F' )
1199 : {
1200 2788 : if( psDBF->panFieldDecimals[iField] > 0
1201 : || psDBF->panFieldSize[iField] > 10 )
1202 2714 : return( FTDouble );
1203 : else
1204 74 : return( FTInteger );
1205 : }
1206 : else
1207 : {
1208 1130 : return( FTString );
1209 : }
1210 : }
1211 :
1212 : /************************************************************************/
1213 : /* DBFWriteAttribute() */
1214 : /* */
1215 : /* Write an attribute record to the file. */
1216 : /************************************************************************/
1217 :
1218 : static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
1219 : void * pValue )
1220 :
1221 16676 : {
1222 16676 : 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 16676 : if( hEntity < 0 || hEntity > psDBF->nRecords )
1230 0 : return( FALSE );
1231 :
1232 16676 : if( psDBF->bNoHeader )
1233 124 : DBFWriteHeader(psDBF);
1234 :
1235 : /* -------------------------------------------------------------------- */
1236 : /* Is this a brand new record? */
1237 : /* -------------------------------------------------------------------- */
1238 16676 : if( hEntity == psDBF->nRecords )
1239 : {
1240 15867 : if( !DBFFlushRecord( psDBF ) )
1241 0 : return FALSE;
1242 :
1243 15867 : psDBF->nRecords++;
1244 220448 : for( i = 0; i < psDBF->nRecordLength; i++ )
1245 204581 : psDBF->pszCurrentRecord[i] = ' ';
1246 :
1247 15867 : psDBF->nCurrentRecord = hEntity;
1248 : }
1249 :
1250 : /* -------------------------------------------------------------------- */
1251 : /* Is this an existing record, but different than the last one */
1252 : /* we accessed? */
1253 : /* -------------------------------------------------------------------- */
1254 16676 : if( !DBFLoadRecord( psDBF, hEntity ) )
1255 0 : return FALSE;
1256 :
1257 16676 : pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1258 :
1259 16676 : psDBF->bCurrentRecordModified = TRUE;
1260 16676 : psDBF->bUpdated = TRUE;
1261 :
1262 : /* -------------------------------------------------------------------- */
1263 : /* Translate NULL value to valid DBF file representation. */
1264 : /* */
1265 : /* Contributed by Jim Matthews. */
1266 : /* -------------------------------------------------------------------- */
1267 16676 : 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 : 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 : 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 : 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 : psDBF->panFieldSize[iField] );
1294 : break;
1295 : }
1296 2 : return TRUE;
1297 : }
1298 :
1299 : /* -------------------------------------------------------------------- */
1300 : /* Assign all the record fields. */
1301 : /* -------------------------------------------------------------------- */
1302 16674 : switch( psDBF->pachFieldType[iField] )
1303 : {
1304 : case 'D':
1305 : case 'N':
1306 : case 'F':
1307 16306 : if( psDBF->panFieldDecimals[iField] == 0 )
1308 : {
1309 15832 : int nWidth = psDBF->panFieldSize[iField];
1310 :
1311 15832 : if( (int) sizeof(szSField)-2 < nWidth )
1312 0 : nWidth = sizeof(szSField)-2;
1313 :
1314 15832 : sprintf( szFormat, "%%%dd", nWidth );
1315 15832 : sprintf(szSField, szFormat, (int) *((double *) pValue) );
1316 15832 : if( (int)strlen(szSField) > psDBF->panFieldSize[iField] )
1317 : {
1318 0 : szSField[psDBF->panFieldSize[iField]] = '\0';
1319 0 : nRetResult = FALSE;
1320 : }
1321 :
1322 15832 : strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1323 : szSField, strlen(szSField) );
1324 : }
1325 : else
1326 : {
1327 474 : int nWidth = psDBF->panFieldSize[iField];
1328 :
1329 474 : if( (int) sizeof(szSField)-2 < nWidth )
1330 0 : nWidth = sizeof(szSField)-2;
1331 :
1332 474 : sprintf( szFormat, "%%%d.%df",
1333 : nWidth, psDBF->panFieldDecimals[iField] );
1334 474 : sprintf(szSField, szFormat, *((double *) pValue) );
1335 474 : if( (int) strlen(szSField) > psDBF->panFieldSize[iField] )
1336 : {
1337 0 : szSField[psDBF->panFieldSize[iField]] = '\0';
1338 0 : nRetResult = FALSE;
1339 : }
1340 474 : strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1341 : szSField, strlen(szSField) );
1342 : }
1343 16306 : break;
1344 :
1345 : case 'L':
1346 0 : if (psDBF->panFieldSize[iField] >= 1 &&
1347 : (*(char*)pValue == 'F' || *(char*)pValue == 'T'))
1348 0 : *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue;
1349 0 : break;
1350 :
1351 : default:
1352 368 : if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] )
1353 : {
1354 0 : j = psDBF->panFieldSize[iField];
1355 0 : nRetResult = FALSE;
1356 : }
1357 : else
1358 : {
1359 368 : memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
1360 : psDBF->panFieldSize[iField] );
1361 368 : j = strlen((char *) pValue);
1362 : }
1363 :
1364 368 : strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1365 : (char *) pValue, j );
1366 : break;
1367 : }
1368 :
1369 16674 : 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 : DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField,
1382 : void * pValue )
1383 :
1384 0 : {
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 : 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 : DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField,
1450 : double dValue )
1451 :
1452 795 : {
1453 795 : 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 : DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField,
1464 : int nValue )
1465 :
1466 15511 : {
1467 15511 : double dValue = nValue;
1468 :
1469 15511 : 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 : DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField,
1480 : const char * pszValue )
1481 :
1482 368 : {
1483 368 : 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 : DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField )
1494 :
1495 2 : {
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 : DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField,
1507 : const char lValue)
1508 :
1509 0 : {
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 : DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple )
1521 :
1522 28 : {
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 : DBFReadTuple(DBFHandle psDBF, int hEntity )
1576 :
1577 28 : {
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 : DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename )
1595 3 : {
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 : DBFGetNativeFieldType( DBFHandle psDBF, int iField )
1641 :
1642 1459 : {
1643 1459 : if( iField >=0 && iField < psDBF->nFields )
1644 1459 : return psDBF->pachFieldType[iField];
1645 :
1646 0 : return ' ';
1647 : }
1648 :
1649 : /************************************************************************/
1650 : /* str_to_upper() */
1651 : /************************************************************************/
1652 :
1653 : static void str_to_upper (char *string)
1654 3167 : {
1655 : int len;
1656 3167 : short i = -1;
1657 :
1658 3167 : len = strlen (string);
1659 :
1660 27642 : while (++i < len)
1661 21308 : if (isalpha(string[i]) && islower(string[i]))
1662 7164 : string[i] = (char) toupper ((int)string[i]);
1663 3167 : }
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 : DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
1675 :
1676 708 : {
1677 : char name[12], name1[12], name2[12];
1678 : int i;
1679 :
1680 708 : strncpy(name1, pszFieldName,11);
1681 708 : name1[11] = '\0';
1682 708 : str_to_upper(name1);
1683 :
1684 3037 : for( i = 0; i < DBFGetFieldCount(psDBF); i++ )
1685 : {
1686 2459 : DBFGetFieldInfo( psDBF, i, name, NULL, NULL );
1687 2459 : strncpy(name2,name,11);
1688 2459 : str_to_upper(name2);
1689 :
1690 2459 : if(!strncmp(name1,name2,10))
1691 130 : return(i);
1692 : }
1693 578 : 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 : int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape )
1704 :
1705 35593 : {
1706 : /* -------------------------------------------------------------------- */
1707 : /* Verify selection. */
1708 : /* -------------------------------------------------------------------- */
1709 35593 : if( iShape < 0 || iShape >= psDBF->nRecords )
1710 0 : return TRUE;
1711 :
1712 : /* -------------------------------------------------------------------- */
1713 : /* Have we read the record? */
1714 : /* -------------------------------------------------------------------- */
1715 35593 : if( !DBFLoadRecord( psDBF, iShape ) )
1716 0 : return FALSE;
1717 :
1718 : /* -------------------------------------------------------------------- */
1719 : /* '*' means deleted. */
1720 : /* -------------------------------------------------------------------- */
1721 35593 : return psDBF->pszCurrentRecord[0] == '*';
1722 : }
1723 :
1724 : /************************************************************************/
1725 : /* DBFMarkRecordDeleted() */
1726 : /************************************************************************/
1727 :
1728 : int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape,
1729 : int bIsDeleted )
1730 :
1731 3 : {
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 : DBFGetCodePage(DBFHandle psDBF )
1771 0 : {
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 : DBFDeleteField(DBFHandle psDBF, int iField)
1785 0 : {
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 : SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
1819 :
1820 0 : psDBF->panFieldSize = (int *)
1821 : SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
1822 :
1823 0 : psDBF->panFieldDecimals = (int *)
1824 : SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
1825 :
1826 0 : psDBF->pachFieldType = (char *)
1827 : 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 : psDBF->pszHeader + (iField+1)*32,
1836 : 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 : 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 : 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 : 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 : }
|