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