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 56100 : static void * SfRealloc( void * pMem, int nNewSize )
181 :
182 : {
183 56100 : if( pMem == NULL )
184 14256 : return( (void *) malloc(nNewSize) );
185 : else
186 41844 : 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 2458 : static void DBFWriteHeader(DBFHandle psDBF)
199 :
200 : {
201 : unsigned char abyHeader[XBASE_FLDHDR_SZ];
202 : int i;
203 :
204 2458 : if( !psDBF->bNoHeader )
205 0 : return;
206 :
207 2458 : psDBF->bNoHeader = FALSE;
208 :
209 : /* -------------------------------------------------------------------- */
210 : /* Initialize the file header information. */
211 : /* -------------------------------------------------------------------- */
212 81114 : for( i = 0; i < XBASE_FLDHDR_SZ; i++ )
213 78656 : abyHeader[i] = 0;
214 :
215 2458 : abyHeader[0] = 0x03; /* memo field? - just copying */
216 :
217 : /* write out a dummy date */
218 2458 : abyHeader[1] = 95; /* YY */
219 2458 : abyHeader[2] = 7; /* MM */
220 2458 : abyHeader[3] = 26; /* DD */
221 :
222 : /* record count preset at zero */
223 :
224 2458 : abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256);
225 2458 : abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256);
226 :
227 2458 : abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256);
228 2458 : abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256);
229 :
230 2458 : abyHeader[29] = (unsigned char) (psDBF->iLanguageDriver);
231 :
232 : /* -------------------------------------------------------------------- */
233 : /* Write the initial 32 byte file header, and all the field */
234 : /* descriptions. */
235 : /* -------------------------------------------------------------------- */
236 2458 : psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
237 2458 : psDBF->sHooks.FWrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp );
238 2458 : 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 2458 : if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 )
245 : {
246 : char cNewline;
247 :
248 2458 : cNewline = 0x0d;
249 2458 : 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 173409 : static int DBFFlushRecord( DBFHandle psDBF )
260 :
261 : {
262 : SAOffset nRecordOffset;
263 :
264 173409 : if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 )
265 : {
266 59114 : psDBF->bCurrentRecordModified = FALSE;
267 :
268 59114 : nRecordOffset =
269 59114 : psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord
270 59114 : + psDBF->nHeaderLength;
271 :
272 118228 : if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0
273 118228 : || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord,
274 59114 : psDBF->nRecordLength,
275 177342 : 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 173409 : return TRUE;
286 : }
287 :
288 : /************************************************************************/
289 : /* DBFLoadRecord() */
290 : /************************************************************************/
291 :
292 423052 : static int DBFLoadRecord( DBFHandle psDBF, int iRecord )
293 :
294 : {
295 423052 : if( psDBF->nCurrentRecord != iRecord )
296 : {
297 : SAOffset nRecordOffset;
298 :
299 95843 : if( !DBFFlushRecord( psDBF ) )
300 0 : return FALSE;
301 :
302 95843 : nRecordOffset =
303 95843 : psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
304 :
305 95843 : 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 191686 : if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord,
315 95843 : 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 95843 : psDBF->nCurrentRecord = iRecord;
325 : }
326 :
327 423052 : return TRUE;
328 : }
329 :
330 : /************************************************************************/
331 : /* DBFUpdateHeader() */
332 : /************************************************************************/
333 :
334 : void SHPAPI_CALL
335 2532 : DBFUpdateHeader( DBFHandle psDBF )
336 :
337 : {
338 : unsigned char abyFileHeader[32];
339 :
340 2532 : if( psDBF->bNoHeader )
341 68 : DBFWriteHeader( psDBF );
342 :
343 2532 : DBFFlushRecord( psDBF );
344 :
345 2532 : psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
346 2532 : psDBF->sHooks.FRead( abyFileHeader, 32, 1, psDBF->fp );
347 :
348 2532 : abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256);
349 2532 : abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256);
350 2532 : abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256);
351 2532 : abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256);
352 :
353 2532 : psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
354 2532 : psDBF->sHooks.FWrite( abyFileHeader, 32, 1, psDBF->fp );
355 :
356 2532 : psDBF->sHooks.FFlush( psDBF->fp );
357 2532 : }
358 :
359 : /************************************************************************/
360 : /* DBFOpen() */
361 : /* */
362 : /* Open a .dbf file. */
363 : /************************************************************************/
364 :
365 : DBFHandle SHPAPI_CALL
366 5030 : DBFOpen( const char * pszFilename, const char * pszAccess )
367 :
368 : {
369 : SAHooks sHooks;
370 :
371 5030 : SASetupDefaultHooks( &sHooks );
372 :
373 5030 : return DBFOpenLL( pszFilename, pszAccess, &sHooks );
374 : }
375 :
376 : /************************************************************************/
377 : /* DBFOpen() */
378 : /* */
379 : /* Open a .dbf file. */
380 : /************************************************************************/
381 :
382 : DBFHandle SHPAPI_CALL
383 5030 : 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 5030 : int nBufSize = 500;
392 :
393 : /* -------------------------------------------------------------------- */
394 : /* We only allow the access strings "rb" and "r+". */
395 : /* -------------------------------------------------------------------- */
396 8406 : if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0
397 3370 : && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0
398 6 : && strcmp(pszAccess,"r+b") != 0 )
399 0 : return( NULL );
400 :
401 5030 : if( strcmp(pszAccess,"r") == 0 )
402 1666 : pszAccess = "rb";
403 :
404 5030 : if( strcmp(pszAccess,"r+") == 0 )
405 3358 : 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 5030 : pszBasename = (char *) malloc(strlen(pszFilename)+5);
412 5030 : strcpy( pszBasename, pszFilename );
413 75450 : for( i = strlen(pszBasename)-1;
414 35210 : i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
415 15090 : && pszBasename[i] != '\\';
416 15090 : i-- ) {}
417 :
418 5030 : if( pszBasename[i] == '.' )
419 5030 : pszBasename[i] = '\0';
420 :
421 5030 : pszFullname = (char *) malloc(strlen(pszBasename) + 5);
422 5030 : sprintf( pszFullname, "%s.dbf", pszBasename );
423 :
424 5030 : psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) );
425 5030 : psDBF->fp = psHooks->FOpen( pszFullname, pszAccess );
426 5030 : memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
427 :
428 5030 : if( psDBF->fp == NULL )
429 : {
430 28 : sprintf( pszFullname, "%s.DBF", pszBasename );
431 28 : psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess );
432 : }
433 :
434 5030 : sprintf( pszFullname, "%s.cpg", pszBasename );
435 5030 : pfCPG = psHooks->FOpen( pszFullname, "r" );
436 5030 : if( pfCPG == NULL )
437 : {
438 5030 : sprintf( pszFullname, "%s.CPG", pszBasename );
439 5030 : pfCPG = psHooks->FOpen( pszFullname, "r" );
440 : }
441 :
442 5030 : free( pszBasename );
443 5030 : free( pszFullname );
444 :
445 5030 : if( psDBF->fp == NULL )
446 : {
447 24 : free( psDBF );
448 24 : if( pfCPG ) psHooks->FClose( pfCPG );
449 24 : return( NULL );
450 : }
451 :
452 5006 : psDBF->bNoHeader = FALSE;
453 5006 : psDBF->nCurrentRecord = -1;
454 5006 : psDBF->bCurrentRecordModified = FALSE;
455 :
456 : /* -------------------------------------------------------------------- */
457 : /* Read Table Header info */
458 : /* -------------------------------------------------------------------- */
459 5006 : pabyBuf = (unsigned char *) malloc(nBufSize);
460 5006 : 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 5006 : psDBF->nRecords =
470 5006 : pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256;
471 :
472 5006 : psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256;
473 5006 : psDBF->nRecordLength = pabyBuf[10] + pabyBuf[11]*256;
474 5006 : psDBF->iLanguageDriver = pabyBuf[29];
475 :
476 5006 : 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 5006 : psDBF->nFields = nFields = (nHeadLen - 32) / 32;
486 :
487 5006 : psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength);
488 :
489 : /* -------------------------------------------------------------------- */
490 : /* Figure out the code page from the LDID and CPG */
491 : /* -------------------------------------------------------------------- */
492 :
493 5006 : psDBF->pszCodePage = NULL;
494 5006 : if( pfCPG )
495 : {
496 : size_t n;
497 0 : memset( pabyBuf, 0, nBufSize);
498 0 : psDBF->sHooks.FRead( pabyBuf, nBufSize - 1, 1, pfCPG );
499 0 : n = strcspn( (char *) pabyBuf, "\n\r" );
500 0 : if( n > 0 )
501 : {
502 0 : pabyBuf[n] = '\0';
503 0 : psDBF->pszCodePage = (char *) malloc(n + 1);
504 0 : memcpy( psDBF->pszCodePage, pabyBuf, n + 1 );
505 : }
506 0 : psDBF->sHooks.FClose( pfCPG );
507 : }
508 5006 : if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 )
509 : {
510 4598 : sprintf( (char *) pabyBuf, "LDID/%d", psDBF->iLanguageDriver );
511 4598 : psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1);
512 4598 : strcpy( psDBF->pszCodePage, (char *) pabyBuf );
513 : }
514 :
515 : /* -------------------------------------------------------------------- */
516 : /* Read in Field Definitions */
517 : /* -------------------------------------------------------------------- */
518 :
519 5006 : pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen);
520 5006 : psDBF->pszHeader = (char *) pabyBuf;
521 :
522 5006 : psDBF->sHooks.FSeek( psDBF->fp, 32, 0 );
523 5006 : 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 5006 : psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields);
533 5006 : psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields);
534 5006 : psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields);
535 5006 : psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields);
536 :
537 11590 : for( iField = 0; iField < nFields; iField++ )
538 : {
539 : unsigned char *pabyFInfo;
540 :
541 6584 : pabyFInfo = pabyBuf+iField*32;
542 :
543 9126 : if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' )
544 : {
545 2542 : psDBF->panFieldSize[iField] = pabyFInfo[16];
546 2542 : psDBF->panFieldDecimals[iField] = pabyFInfo[17];
547 : }
548 : else
549 : {
550 4042 : psDBF->panFieldSize[iField] = pabyFInfo[16];
551 4042 : 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 6584 : psDBF->pachFieldType[iField] = (char) pabyFInfo[11];
564 6584 : if( iField == 0 )
565 4996 : psDBF->panFieldOffset[iField] = 1;
566 : else
567 3176 : psDBF->panFieldOffset[iField] =
568 1588 : psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1];
569 : }
570 :
571 5006 : return( psDBF );
572 : }
573 :
574 : /************************************************************************/
575 : /* DBFClose() */
576 : /************************************************************************/
577 :
578 : void SHPAPI_CALL
579 7396 : DBFClose(DBFHandle psDBF)
580 : {
581 7396 : if( psDBF == NULL )
582 0 : return;
583 :
584 : /* -------------------------------------------------------------------- */
585 : /* Write out header if not already written. */
586 : /* -------------------------------------------------------------------- */
587 7396 : if( psDBF->bNoHeader )
588 14 : DBFWriteHeader( psDBF );
589 :
590 7396 : DBFFlushRecord( psDBF );
591 :
592 : /* -------------------------------------------------------------------- */
593 : /* Update last access date, and number of records if we have */
594 : /* write access. */
595 : /* -------------------------------------------------------------------- */
596 7396 : if( psDBF->bUpdated )
597 2424 : DBFUpdateHeader( psDBF );
598 :
599 : /* -------------------------------------------------------------------- */
600 : /* Close, and free resources. */
601 : /* -------------------------------------------------------------------- */
602 7396 : psDBF->sHooks.FClose( psDBF->fp );
603 :
604 7396 : if( psDBF->panFieldOffset != NULL )
605 : {
606 7386 : free( psDBF->panFieldOffset );
607 7386 : free( psDBF->panFieldSize );
608 7386 : free( psDBF->panFieldDecimals );
609 7386 : free( psDBF->pachFieldType );
610 : }
611 :
612 7396 : if( psDBF->pszWorkField != NULL )
613 2670 : free( psDBF->pszWorkField );
614 :
615 7396 : free( psDBF->pszHeader );
616 7396 : free( psDBF->pszCurrentRecord );
617 7396 : free( psDBF->pszCodePage );
618 :
619 7396 : 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 2384 : DBFCreate( const char * pszFilename )
630 :
631 : {
632 2384 : 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 2390 : DBFCreateEx( const char * pszFilename, const char* pszCodePage )
643 :
644 : {
645 : SAHooks sHooks;
646 :
647 2390 : SASetupDefaultHooks( &sHooks );
648 :
649 2390 : return DBFCreateLL( pszFilename, pszCodePage , &sHooks );
650 : }
651 :
652 : /************************************************************************/
653 : /* DBFCreate() */
654 : /* */
655 : /* Create a new .dbf file. */
656 : /************************************************************************/
657 :
658 : DBFHandle SHPAPI_CALL
659 2390 : DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHooks )
660 :
661 : {
662 : DBFHandle psDBF;
663 : SAFile fp;
664 : char *pszFullname, *pszBasename;
665 2390 : int i, ldid = -1;
666 2390 : 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 2390 : pszBasename = (char *) malloc(strlen(pszFilename)+5);
673 2390 : strcpy( pszBasename, pszFilename );
674 35850 : for( i = strlen(pszBasename)-1;
675 16730 : i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
676 7170 : && pszBasename[i] != '\\';
677 7170 : i-- ) {}
678 :
679 2390 : if( pszBasename[i] == '.' )
680 2390 : pszBasename[i] = '\0';
681 :
682 2390 : pszFullname = (char *) malloc(strlen(pszBasename) + 5);
683 2390 : sprintf( pszFullname, "%s.dbf", pszBasename );
684 :
685 : /* -------------------------------------------------------------------- */
686 : /* Create the file. */
687 : /* -------------------------------------------------------------------- */
688 2390 : fp = psHooks->FOpen( pszFullname, "wb" );
689 2390 : if( fp == NULL )
690 0 : return( NULL );
691 :
692 2390 : psHooks->FWrite( &chZero, 1, 1, fp );
693 2390 : psHooks->FClose( fp );
694 :
695 2390 : fp = psHooks->FOpen( pszFullname, "rb+" );
696 2390 : if( fp == NULL )
697 0 : return( NULL );
698 :
699 :
700 2390 : sprintf( pszFullname, "%s.cpg", pszBasename );
701 2390 : if( pszCodePage != NULL )
702 : {
703 2386 : if( strncmp( pszCodePage, "LDID/", 5 ) == 0 )
704 : {
705 2386 : ldid = atoi( pszCodePage + 5 );
706 2386 : if( ldid > 255 )
707 0 : ldid = -1; // don't use 0 to indicate out of range as LDID/0 is a valid one
708 : }
709 2386 : if( ldid < 0 )
710 : {
711 0 : SAFile fpCPG = psHooks->FOpen( pszFullname, "w" );
712 0 : psHooks->FWrite( (char*) pszCodePage, strlen(pszCodePage), 1, fpCPG );
713 0 : psHooks->FClose( fpCPG );
714 : }
715 : }
716 2390 : if( pszCodePage == NULL || ldid >= 0 )
717 : {
718 2390 : psHooks->Remove( pszFullname );
719 : }
720 :
721 2390 : free( pszBasename );
722 2390 : free( pszFullname );
723 :
724 : /* -------------------------------------------------------------------- */
725 : /* Create the info structure. */
726 : /* -------------------------------------------------------------------- */
727 2390 : psDBF = (DBFHandle) calloc(1,sizeof(DBFInfo));
728 :
729 2390 : memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
730 2390 : psDBF->fp = fp;
731 2390 : psDBF->nRecords = 0;
732 2390 : psDBF->nFields = 0;
733 2390 : psDBF->nRecordLength = 1;
734 2390 : psDBF->nHeaderLength = 33;
735 :
736 2390 : psDBF->panFieldOffset = NULL;
737 2390 : psDBF->panFieldSize = NULL;
738 2390 : psDBF->panFieldDecimals = NULL;
739 2390 : psDBF->pachFieldType = NULL;
740 2390 : psDBF->pszHeader = NULL;
741 :
742 2390 : psDBF->nCurrentRecord = -1;
743 2390 : psDBF->bCurrentRecordModified = FALSE;
744 2390 : psDBF->pszCurrentRecord = NULL;
745 :
746 2390 : psDBF->bNoHeader = TRUE;
747 :
748 2390 : psDBF->iLanguageDriver = ldid > 0 ? ldid : 0;
749 2390 : psDBF->pszCodePage = NULL;
750 2390 : if( pszCodePage )
751 : {
752 2386 : psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 );
753 2386 : strcpy( psDBF->pszCodePage, pszCodePage );
754 : }
755 :
756 2390 : return( psDBF );
757 : }
758 :
759 : /************************************************************************/
760 : /* DBFAddField() */
761 : /* */
762 : /* Add a field to a newly created .dbf or to an existing one */
763 : /************************************************************************/
764 :
765 : int SHPAPI_CALL
766 164 : DBFAddField(DBFHandle psDBF, const char * pszFieldName,
767 : DBFFieldType eType, int nWidth, int nDecimals )
768 :
769 : {
770 164 : char chNativeType = 'C';
771 :
772 164 : if( eType == FTLogical )
773 0 : chNativeType = 'L';
774 164 : else if( eType == FTString )
775 0 : chNativeType = 'C';
776 : else
777 164 : chNativeType = 'N';
778 :
779 164 : return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType,
780 : nWidth, nDecimals );
781 : }
782 :
783 : /************************************************************************/
784 : /* DBFGetNullCharacter() */
785 : /************************************************************************/
786 :
787 70 : static char DBFGetNullCharacter(char chType)
788 : {
789 70 : switch (chType)
790 : {
791 : case 'N':
792 : case 'F':
793 30 : return '*';
794 : case 'D':
795 0 : return '0';
796 : case 'L':
797 0 : return '?';
798 : default:
799 40 : return ' ';
800 : }
801 : }
802 :
803 : /************************************************************************/
804 : /* DBFAddField() */
805 : /* */
806 : /* Add a field to a newly created .dbf file before any records */
807 : /* are written. */
808 : /************************************************************************/
809 :
810 : int SHPAPI_CALL
811 8500 : DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName,
812 : char chType, int nWidth, int nDecimals )
813 :
814 : {
815 : char *pszFInfo;
816 : int i;
817 : int nOldRecordLength, nOldHeaderLength;
818 : char *pszRecord;
819 : char chFieldFill;
820 : SAOffset nRecordOffset;
821 :
822 : /* make sure that everything is written in .dbf */
823 8500 : if( !DBFFlushRecord( psDBF ) )
824 0 : return -1;
825 :
826 : /* -------------------------------------------------------------------- */
827 : /* Do some checking to ensure we can add records to this file. */
828 : /* -------------------------------------------------------------------- */
829 8500 : if( nWidth < 1 )
830 0 : return -1;
831 :
832 8500 : if( nWidth > 255 )
833 0 : nWidth = 255;
834 :
835 8500 : nOldRecordLength = psDBF->nRecordLength;
836 8500 : nOldHeaderLength = psDBF->nHeaderLength;
837 :
838 : /* -------------------------------------------------------------------- */
839 : /* SfRealloc all the arrays larger to hold the additional field */
840 : /* information. */
841 : /* -------------------------------------------------------------------- */
842 8500 : psDBF->nFields++;
843 :
844 17000 : psDBF->panFieldOffset = (int *)
845 8500 : SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
846 :
847 17000 : psDBF->panFieldSize = (int *)
848 8500 : SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
849 :
850 17000 : psDBF->panFieldDecimals = (int *)
851 8500 : SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
852 :
853 17000 : psDBF->pachFieldType = (char *)
854 8500 : SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
855 :
856 : /* -------------------------------------------------------------------- */
857 : /* Assign the new field information fields. */
858 : /* -------------------------------------------------------------------- */
859 8500 : psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength;
860 8500 : psDBF->nRecordLength += nWidth;
861 8500 : psDBF->panFieldSize[psDBF->nFields-1] = nWidth;
862 8500 : psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals;
863 8500 : psDBF->pachFieldType[psDBF->nFields-1] = chType;
864 :
865 : /* -------------------------------------------------------------------- */
866 : /* Extend the required header information. */
867 : /* -------------------------------------------------------------------- */
868 8500 : psDBF->nHeaderLength += 32;
869 8500 : psDBF->bUpdated = FALSE;
870 :
871 8500 : psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
872 :
873 8500 : pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1);
874 :
875 280500 : for( i = 0; i < 32; i++ )
876 272000 : pszFInfo[i] = '\0';
877 :
878 8500 : if( (int) strlen(pszFieldName) < 10 )
879 8426 : strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
880 : else
881 74 : strncpy( pszFInfo, pszFieldName, 10);
882 :
883 8500 : pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1];
884 :
885 8500 : if( chType == 'C' )
886 : {
887 3878 : pszFInfo[16] = (unsigned char) (nWidth % 256);
888 3878 : pszFInfo[17] = (unsigned char) (nWidth / 256);
889 : }
890 : else
891 : {
892 4622 : pszFInfo[16] = (unsigned char) nWidth;
893 4622 : pszFInfo[17] = (unsigned char) nDecimals;
894 : }
895 :
896 : /* -------------------------------------------------------------------- */
897 : /* Make the current record buffer appropriately larger. */
898 : /* -------------------------------------------------------------------- */
899 8500 : psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
900 : psDBF->nRecordLength);
901 :
902 : /* we're done if dealing with new .dbf */
903 8500 : if( psDBF->bNoHeader )
904 8488 : return( psDBF->nFields - 1 );
905 :
906 : /* -------------------------------------------------------------------- */
907 : /* For existing .dbf file, shift records */
908 : /* -------------------------------------------------------------------- */
909 :
910 : /* alloc record */
911 12 : pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
912 :
913 12 : chFieldFill = DBFGetNullCharacter(chType);
914 :
915 60 : for (i = psDBF->nRecords-1; i >= 0; --i)
916 : {
917 48 : nRecordOffset = nOldRecordLength * (SAOffset) i + nOldHeaderLength;
918 :
919 : /* load record */
920 48 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
921 48 : psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
922 :
923 : /* set new field's value to NULL */
924 48 : memset(pszRecord + nOldRecordLength, chFieldFill, nWidth);
925 :
926 48 : nRecordOffset = psDBF->nRecordLength * (SAOffset) i + psDBF->nHeaderLength;
927 :
928 : /* move record to the new place*/
929 48 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
930 48 : psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
931 : }
932 :
933 : /* free record */
934 12 : free(pszRecord);
935 :
936 : /* force update of header with new header, record length and new field */
937 12 : psDBF->bNoHeader = TRUE;
938 12 : DBFUpdateHeader( psDBF );
939 :
940 12 : psDBF->nCurrentRecord = -1;
941 12 : psDBF->bCurrentRecordModified = FALSE;
942 :
943 12 : return( psDBF->nFields-1 );
944 : }
945 :
946 : /************************************************************************/
947 : /* DBFReadAttribute() */
948 : /* */
949 : /* Read one of the attribute fields of a record. */
950 : /************************************************************************/
951 :
952 186460 : static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField,
953 : char chReqType )
954 :
955 : {
956 : unsigned char *pabyRec;
957 186460 : void *pReturnField = NULL;
958 :
959 : /* -------------------------------------------------------------------- */
960 : /* Verify selection. */
961 : /* -------------------------------------------------------------------- */
962 186460 : if( hEntity < 0 || hEntity >= psDBF->nRecords )
963 0 : return( NULL );
964 :
965 186460 : if( iField < 0 || iField >= psDBF->nFields )
966 0 : return( NULL );
967 :
968 : /* -------------------------------------------------------------------- */
969 : /* Have we read the record? */
970 : /* -------------------------------------------------------------------- */
971 186460 : if( !DBFLoadRecord( psDBF, hEntity ) )
972 0 : return NULL;
973 :
974 186460 : pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
975 :
976 : /* -------------------------------------------------------------------- */
977 : /* Ensure we have room to extract the target field. */
978 : /* -------------------------------------------------------------------- */
979 186460 : if( psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength )
980 : {
981 2670 : psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100;
982 2670 : if( psDBF->pszWorkField == NULL )
983 2670 : psDBF->pszWorkField = (char *) malloc(psDBF->nWorkFieldLength);
984 : else
985 0 : psDBF->pszWorkField = (char *) realloc(psDBF->pszWorkField,
986 0 : psDBF->nWorkFieldLength);
987 : }
988 :
989 : /* -------------------------------------------------------------------- */
990 : /* Extract the requested field. */
991 : /* -------------------------------------------------------------------- */
992 372920 : strncpy( psDBF->pszWorkField,
993 186460 : ((const char *) pabyRec) + psDBF->panFieldOffset[iField],
994 186460 : psDBF->panFieldSize[iField] );
995 186460 : psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0';
996 :
997 186460 : pReturnField = psDBF->pszWorkField;
998 :
999 : /* -------------------------------------------------------------------- */
1000 : /* Decode the field. */
1001 : /* -------------------------------------------------------------------- */
1002 186460 : if( chReqType == 'N' )
1003 : {
1004 87056 : psDBF->dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField);
1005 :
1006 87056 : pReturnField = &(psDBF->dfDoubleField);
1007 : }
1008 :
1009 : /* -------------------------------------------------------------------- */
1010 : /* Should we trim white space off the string attribute value? */
1011 : /* -------------------------------------------------------------------- */
1012 : #ifdef TRIM_DBF_WHITESPACE
1013 : else
1014 : {
1015 : char *pchSrc, *pchDst;
1016 :
1017 99404 : pchDst = pchSrc = psDBF->pszWorkField;
1018 909638 : while( *pchSrc == ' ' )
1019 710830 : pchSrc++;
1020 :
1021 942342 : while( *pchSrc != '\0' )
1022 743534 : *(pchDst++) = *(pchSrc++);
1023 99404 : *pchDst = '\0';
1024 :
1025 492338 : while( pchDst != psDBF->pszWorkField && *(--pchDst) == ' ' )
1026 293530 : *pchDst = '\0';
1027 : }
1028 : #endif
1029 :
1030 186460 : return( pReturnField );
1031 : }
1032 :
1033 : /************************************************************************/
1034 : /* DBFReadIntAttribute() */
1035 : /* */
1036 : /* Read an integer attribute. */
1037 : /************************************************************************/
1038 :
1039 : int SHPAPI_CALL
1040 3836 : DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField )
1041 :
1042 : {
1043 : double *pdValue;
1044 :
1045 3836 : pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
1046 :
1047 3836 : if( pdValue == NULL )
1048 0 : return 0;
1049 : else
1050 3836 : return( (int) *pdValue );
1051 : }
1052 :
1053 : /************************************************************************/
1054 : /* DBFReadDoubleAttribute() */
1055 : /* */
1056 : /* Read a double attribute. */
1057 : /************************************************************************/
1058 :
1059 : double SHPAPI_CALL
1060 83220 : DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField )
1061 :
1062 : {
1063 : double *pdValue;
1064 :
1065 83220 : pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
1066 :
1067 83220 : if( pdValue == NULL )
1068 0 : return 0.0;
1069 : else
1070 83220 : return( *pdValue );
1071 : }
1072 :
1073 : /************************************************************************/
1074 : /* DBFReadStringAttribute() */
1075 : /* */
1076 : /* Read a string attribute. */
1077 : /************************************************************************/
1078 :
1079 : const char SHPAPI_CALL1(*)
1080 99404 : DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField )
1081 :
1082 : {
1083 99404 : return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) );
1084 : }
1085 :
1086 : /************************************************************************/
1087 : /* DBFReadLogicalAttribute() */
1088 : /* */
1089 : /* Read a logical attribute. */
1090 : /************************************************************************/
1091 :
1092 : const char SHPAPI_CALL1(*)
1093 0 : DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField )
1094 :
1095 : {
1096 0 : return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) );
1097 : }
1098 :
1099 :
1100 : /************************************************************************/
1101 : /* DBFIsValueNULL() */
1102 : /* */
1103 : /* Return TRUE if the passed string is NULL. */
1104 : /************************************************************************/
1105 :
1106 87232 : static int DBFIsValueNULL( char chType, const char* pszValue )
1107 : {
1108 : int i;
1109 :
1110 87232 : if( pszValue == NULL )
1111 0 : return TRUE;
1112 :
1113 87232 : switch(chType)
1114 : {
1115 : case 'N':
1116 : case 'F':
1117 : /*
1118 : ** We accept all asterisks or all blanks as NULL
1119 : ** though according to the spec I think it should be all
1120 : ** asterisks.
1121 : */
1122 87190 : if( pszValue[0] == '*' )
1123 104 : return TRUE;
1124 :
1125 87156 : for( i = 0; pszValue[i] != '\0'; i++ )
1126 : {
1127 87154 : if( pszValue[i] != ' ' )
1128 87084 : return FALSE;
1129 : }
1130 2 : return TRUE;
1131 :
1132 : case 'D':
1133 : /* NULL date fields have value "00000000" */
1134 4 : return strncmp(pszValue,"00000000",8) == 0;
1135 :
1136 : case 'L':
1137 : /* NULL boolean fields have value "?" */
1138 0 : return pszValue[0] == '?';
1139 :
1140 : default:
1141 : /* empty string fields are considered NULL */
1142 38 : return strlen(pszValue) == 0;
1143 : }
1144 : }
1145 :
1146 : /************************************************************************/
1147 : /* DBFIsAttributeNULL() */
1148 : /* */
1149 : /* Return TRUE if value for field is NULL. */
1150 : /* */
1151 : /* Contributed by Jim Matthews. */
1152 : /************************************************************************/
1153 :
1154 : int SHPAPI_CALL
1155 87154 : DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField )
1156 :
1157 : {
1158 : const char *pszValue;
1159 :
1160 87154 : pszValue = DBFReadStringAttribute( psDBF, iRecord, iField );
1161 :
1162 87154 : if( pszValue == NULL )
1163 0 : return TRUE;
1164 :
1165 87154 : return DBFIsValueNULL( psDBF->pachFieldType[iField], pszValue );
1166 : }
1167 :
1168 : /************************************************************************/
1169 : /* DBFGetFieldCount() */
1170 : /* */
1171 : /* Return the number of fields in this table. */
1172 : /************************************************************************/
1173 :
1174 : int SHPAPI_CALL
1175 14674398 : DBFGetFieldCount( DBFHandle psDBF )
1176 :
1177 : {
1178 14674398 : return( psDBF->nFields );
1179 : }
1180 :
1181 : /************************************************************************/
1182 : /* DBFGetRecordCount() */
1183 : /* */
1184 : /* Return the number of records in this table. */
1185 : /************************************************************************/
1186 :
1187 : int SHPAPI_CALL
1188 118072 : DBFGetRecordCount( DBFHandle psDBF )
1189 :
1190 : {
1191 118072 : return( psDBF->nRecords );
1192 : }
1193 :
1194 : /************************************************************************/
1195 : /* DBFGetFieldInfo() */
1196 : /* */
1197 : /* Return any requested information about the field. */
1198 : /************************************************************************/
1199 :
1200 : DBFFieldType SHPAPI_CALL
1201 14580618 : DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName,
1202 : int * pnWidth, int * pnDecimals )
1203 :
1204 : {
1205 14580618 : if( iField < 0 || iField >= psDBF->nFields )
1206 0 : return( FTInvalid );
1207 :
1208 14580618 : if( pnWidth != NULL )
1209 6570 : *pnWidth = psDBF->panFieldSize[iField];
1210 :
1211 14580618 : if( pnDecimals != NULL )
1212 6570 : *pnDecimals = psDBF->panFieldDecimals[iField];
1213 :
1214 14580618 : if( pszFieldName != NULL )
1215 : {
1216 : int i;
1217 :
1218 14580618 : strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 );
1219 14580618 : pszFieldName[11] = '\0';
1220 14580618 : for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- )
1221 0 : pszFieldName[i] = '\0';
1222 : }
1223 :
1224 14580618 : if ( psDBF->pachFieldType[iField] == 'L' )
1225 0 : return( FTLogical);
1226 :
1227 16602666 : else if( psDBF->pachFieldType[iField] == 'N'
1228 2022048 : || psDBF->pachFieldType[iField] == 'F' )
1229 : {
1230 25115522 : if( psDBF->panFieldDecimals[iField] > 0
1231 12556952 : || psDBF->panFieldSize[iField] > 10 )
1232 3710 : return( FTDouble );
1233 : else
1234 12554860 : return( FTInteger );
1235 : }
1236 : else
1237 : {
1238 2022048 : return( FTString );
1239 : }
1240 : }
1241 :
1242 : /************************************************************************/
1243 : /* DBFWriteAttribute() */
1244 : /* */
1245 : /* Write an attribute record to the file. */
1246 : /************************************************************************/
1247 :
1248 63412 : static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
1249 : void * pValue )
1250 :
1251 : {
1252 63412 : int i, j, nRetResult = TRUE;
1253 : unsigned char *pabyRec;
1254 : char szSField[400], szFormat[20];
1255 :
1256 : /* -------------------------------------------------------------------- */
1257 : /* Is this a valid record? */
1258 : /* -------------------------------------------------------------------- */
1259 63412 : if( hEntity < 0 || hEntity > psDBF->nRecords )
1260 0 : return( FALSE );
1261 :
1262 63412 : if( psDBF->bNoHeader )
1263 2340 : DBFWriteHeader(psDBF);
1264 :
1265 : /* -------------------------------------------------------------------- */
1266 : /* Is this a brand new record? */
1267 : /* -------------------------------------------------------------------- */
1268 63412 : if( hEntity == psDBF->nRecords )
1269 : {
1270 57340 : if( !DBFFlushRecord( psDBF ) )
1271 0 : return FALSE;
1272 :
1273 57340 : psDBF->nRecords++;
1274 1195362 : for( i = 0; i < psDBF->nRecordLength; i++ )
1275 1138022 : psDBF->pszCurrentRecord[i] = ' ';
1276 :
1277 57340 : psDBF->nCurrentRecord = hEntity;
1278 : }
1279 :
1280 : /* -------------------------------------------------------------------- */
1281 : /* Is this an existing record, but different than the last one */
1282 : /* we accessed? */
1283 : /* -------------------------------------------------------------------- */
1284 63412 : if( !DBFLoadRecord( psDBF, hEntity ) )
1285 0 : return FALSE;
1286 :
1287 63412 : pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1288 :
1289 63412 : psDBF->bCurrentRecordModified = TRUE;
1290 63412 : psDBF->bUpdated = TRUE;
1291 :
1292 : /* -------------------------------------------------------------------- */
1293 : /* Translate NULL value to valid DBF file representation. */
1294 : /* */
1295 : /* Contributed by Jim Matthews. */
1296 : /* -------------------------------------------------------------------- */
1297 63412 : if( pValue == NULL )
1298 : {
1299 68 : memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]),
1300 34 : DBFGetNullCharacter(psDBF->pachFieldType[iField]),
1301 34 : psDBF->panFieldSize[iField] );
1302 34 : return TRUE;
1303 : }
1304 :
1305 : /* -------------------------------------------------------------------- */
1306 : /* Assign all the record fields. */
1307 : /* -------------------------------------------------------------------- */
1308 63378 : switch( psDBF->pachFieldType[iField] )
1309 : {
1310 : case 'D':
1311 : case 'N':
1312 : case 'F':
1313 56688 : if( psDBF->panFieldDecimals[iField] == 0 )
1314 : {
1315 55238 : int nWidth = psDBF->panFieldSize[iField];
1316 :
1317 55238 : if( (int) sizeof(szSField)-2 < nWidth )
1318 0 : nWidth = sizeof(szSField)-2;
1319 :
1320 55238 : sprintf( szFormat, "%%%dd", nWidth );
1321 55238 : sprintf(szSField, szFormat, (int) *((double *) pValue) );
1322 55238 : if( (int)strlen(szSField) > psDBF->panFieldSize[iField] )
1323 : {
1324 0 : szSField[psDBF->panFieldSize[iField]] = '\0';
1325 0 : nRetResult = FALSE;
1326 : }
1327 :
1328 55238 : strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1329 : szSField, strlen(szSField) );
1330 : }
1331 : else
1332 : {
1333 1450 : int nWidth = psDBF->panFieldSize[iField];
1334 :
1335 1450 : if( (int) sizeof(szSField)-2 < nWidth )
1336 0 : nWidth = sizeof(szSField)-2;
1337 :
1338 1450 : sprintf( szFormat, "%%%d.%df",
1339 1450 : nWidth, psDBF->panFieldDecimals[iField] );
1340 1450 : sprintf(szSField, szFormat, *((double *) pValue) );
1341 1450 : if( (int) strlen(szSField) > psDBF->panFieldSize[iField] )
1342 : {
1343 0 : szSField[psDBF->panFieldSize[iField]] = '\0';
1344 0 : nRetResult = FALSE;
1345 : }
1346 1450 : strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1347 : szSField, strlen(szSField) );
1348 : }
1349 56688 : break;
1350 :
1351 : case 'L':
1352 0 : if (psDBF->panFieldSize[iField] >= 1 &&
1353 0 : (*(char*)pValue == 'F' || *(char*)pValue == 'T'))
1354 0 : *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue;
1355 0 : break;
1356 :
1357 : default:
1358 6690 : if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] )
1359 : {
1360 2 : j = psDBF->panFieldSize[iField];
1361 2 : nRetResult = FALSE;
1362 : }
1363 : else
1364 : {
1365 6688 : memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
1366 6688 : psDBF->panFieldSize[iField] );
1367 6688 : j = strlen((char *) pValue);
1368 : }
1369 :
1370 6690 : strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1371 : (char *) pValue, j );
1372 : break;
1373 : }
1374 :
1375 63378 : return( nRetResult );
1376 : }
1377 :
1378 : /************************************************************************/
1379 : /* DBFWriteAttributeDirectly() */
1380 : /* */
1381 : /* Write an attribute record to the file, but without any */
1382 : /* reformatting based on type. The provided buffer is written */
1383 : /* as is to the field position in the record. */
1384 : /************************************************************************/
1385 :
1386 : int SHPAPI_CALL
1387 9910 : DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField,
1388 : void * pValue )
1389 :
1390 : {
1391 : int i, j;
1392 : unsigned char *pabyRec;
1393 :
1394 : /* -------------------------------------------------------------------- */
1395 : /* Is this a valid record? */
1396 : /* -------------------------------------------------------------------- */
1397 9910 : if( hEntity < 0 || hEntity > psDBF->nRecords )
1398 0 : return( FALSE );
1399 :
1400 9910 : if( psDBF->bNoHeader )
1401 30 : DBFWriteHeader(psDBF);
1402 :
1403 : /* -------------------------------------------------------------------- */
1404 : /* Is this a brand new record? */
1405 : /* -------------------------------------------------------------------- */
1406 9910 : if( hEntity == psDBF->nRecords )
1407 : {
1408 1686 : if( !DBFFlushRecord( psDBF ) )
1409 0 : return FALSE;
1410 :
1411 1686 : psDBF->nRecords++;
1412 104128 : for( i = 0; i < psDBF->nRecordLength; i++ )
1413 102442 : psDBF->pszCurrentRecord[i] = ' ';
1414 :
1415 1686 : psDBF->nCurrentRecord = hEntity;
1416 : }
1417 :
1418 : /* -------------------------------------------------------------------- */
1419 : /* Is this an existing record, but different than the last one */
1420 : /* we accessed? */
1421 : /* -------------------------------------------------------------------- */
1422 9910 : if( !DBFLoadRecord( psDBF, hEntity ) )
1423 0 : return FALSE;
1424 :
1425 9910 : pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1426 :
1427 : /* -------------------------------------------------------------------- */
1428 : /* Assign all the record fields. */
1429 : /* -------------------------------------------------------------------- */
1430 9910 : if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] )
1431 0 : j = psDBF->panFieldSize[iField];
1432 : else
1433 : {
1434 9910 : memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
1435 9910 : psDBF->panFieldSize[iField] );
1436 9910 : j = strlen((char *) pValue);
1437 : }
1438 :
1439 9910 : strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1440 : (char *) pValue, j );
1441 :
1442 9910 : psDBF->bCurrentRecordModified = TRUE;
1443 9910 : psDBF->bUpdated = TRUE;
1444 :
1445 9910 : return( TRUE );
1446 : }
1447 :
1448 : /************************************************************************/
1449 : /* DBFWriteDoubleAttribute() */
1450 : /* */
1451 : /* Write a double attribute. */
1452 : /************************************************************************/
1453 :
1454 : int SHPAPI_CALL
1455 2636 : DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField,
1456 : double dValue )
1457 :
1458 : {
1459 2636 : return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
1460 : }
1461 :
1462 : /************************************************************************/
1463 : /* DBFWriteIntegerAttribute() */
1464 : /* */
1465 : /* Write a integer attribute. */
1466 : /************************************************************************/
1467 :
1468 : int SHPAPI_CALL
1469 54052 : DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField,
1470 : int nValue )
1471 :
1472 : {
1473 54052 : double dValue = nValue;
1474 :
1475 54052 : return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
1476 : }
1477 :
1478 : /************************************************************************/
1479 : /* DBFWriteStringAttribute() */
1480 : /* */
1481 : /* Write a string attribute. */
1482 : /************************************************************************/
1483 :
1484 : int SHPAPI_CALL
1485 6690 : DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField,
1486 : const char * pszValue )
1487 :
1488 : {
1489 6690 : return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) );
1490 : }
1491 :
1492 : /************************************************************************/
1493 : /* DBFWriteNULLAttribute() */
1494 : /* */
1495 : /* Write a string attribute. */
1496 : /************************************************************************/
1497 :
1498 : int SHPAPI_CALL
1499 34 : DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField )
1500 :
1501 : {
1502 34 : return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) );
1503 : }
1504 :
1505 : /************************************************************************/
1506 : /* DBFWriteLogicalAttribute() */
1507 : /* */
1508 : /* Write a logical attribute. */
1509 : /************************************************************************/
1510 :
1511 : int SHPAPI_CALL
1512 0 : DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField,
1513 : const char lValue)
1514 :
1515 : {
1516 0 : return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) (&lValue) ) );
1517 : }
1518 :
1519 : /************************************************************************/
1520 : /* DBFWriteTuple() */
1521 : /* */
1522 : /* Write an attribute record to the file. */
1523 : /************************************************************************/
1524 :
1525 : int SHPAPI_CALL
1526 56 : DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple )
1527 :
1528 : {
1529 : int i;
1530 : unsigned char *pabyRec;
1531 :
1532 : /* -------------------------------------------------------------------- */
1533 : /* Is this a valid record? */
1534 : /* -------------------------------------------------------------------- */
1535 56 : if( hEntity < 0 || hEntity > psDBF->nRecords )
1536 0 : return( FALSE );
1537 :
1538 56 : if( psDBF->bNoHeader )
1539 0 : DBFWriteHeader(psDBF);
1540 :
1541 : /* -------------------------------------------------------------------- */
1542 : /* Is this a brand new record? */
1543 : /* -------------------------------------------------------------------- */
1544 56 : if( hEntity == psDBF->nRecords )
1545 : {
1546 56 : if( !DBFFlushRecord( psDBF ) )
1547 0 : return FALSE;
1548 :
1549 56 : psDBF->nRecords++;
1550 3796 : for( i = 0; i < psDBF->nRecordLength; i++ )
1551 3740 : psDBF->pszCurrentRecord[i] = ' ';
1552 :
1553 56 : psDBF->nCurrentRecord = hEntity;
1554 : }
1555 :
1556 : /* -------------------------------------------------------------------- */
1557 : /* Is this an existing record, but different than the last one */
1558 : /* we accessed? */
1559 : /* -------------------------------------------------------------------- */
1560 56 : if( !DBFLoadRecord( psDBF, hEntity ) )
1561 0 : return FALSE;
1562 :
1563 56 : pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1564 :
1565 56 : memcpy ( pabyRec, pRawTuple, psDBF->nRecordLength );
1566 :
1567 56 : psDBF->bCurrentRecordModified = TRUE;
1568 56 : psDBF->bUpdated = TRUE;
1569 :
1570 56 : return( TRUE );
1571 : }
1572 :
1573 : /************************************************************************/
1574 : /* DBFReadTuple() */
1575 : /* */
1576 : /* Read a complete record. Note that the result is only valid */
1577 : /* till the next record read for any reason. */
1578 : /************************************************************************/
1579 :
1580 : const char SHPAPI_CALL1(*)
1581 56 : DBFReadTuple(DBFHandle psDBF, int hEntity )
1582 :
1583 : {
1584 56 : if( hEntity < 0 || hEntity >= psDBF->nRecords )
1585 0 : return( NULL );
1586 :
1587 56 : if( !DBFLoadRecord( psDBF, hEntity ) )
1588 0 : return NULL;
1589 :
1590 56 : return (const char *) psDBF->pszCurrentRecord;
1591 : }
1592 :
1593 : /************************************************************************/
1594 : /* DBFCloneEmpty() */
1595 : /* */
1596 : /* Read one of the attribute fields of a record. */
1597 : /************************************************************************/
1598 :
1599 : DBFHandle SHPAPI_CALL
1600 6 : DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename )
1601 : {
1602 : DBFHandle newDBF;
1603 :
1604 6 : newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage );
1605 6 : if ( newDBF == NULL ) return ( NULL );
1606 :
1607 6 : newDBF->nFields = psDBF->nFields;
1608 6 : newDBF->nRecordLength = psDBF->nRecordLength;
1609 6 : newDBF->nHeaderLength = psDBF->nHeaderLength;
1610 :
1611 6 : newDBF->pszHeader = (char *) malloc ( newDBF->nHeaderLength );
1612 6 : memcpy ( newDBF->pszHeader, psDBF->pszHeader, newDBF->nHeaderLength );
1613 :
1614 6 : newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields );
1615 6 : memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
1616 6 : newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields );
1617 6 : memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
1618 6 : newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields );
1619 6 : memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
1620 6 : newDBF->pachFieldType = (char *) malloc ( sizeof(char) * psDBF->nFields );
1621 6 : memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(char)*psDBF->nFields );
1622 :
1623 6 : newDBF->bNoHeader = TRUE;
1624 6 : newDBF->bUpdated = TRUE;
1625 :
1626 6 : DBFWriteHeader ( newDBF );
1627 6 : DBFClose ( newDBF );
1628 :
1629 6 : newDBF = DBFOpen ( pszFilename, "rb+" );
1630 :
1631 6 : return ( newDBF );
1632 : }
1633 :
1634 : /************************************************************************/
1635 : /* DBFGetNativeFieldType() */
1636 : /* */
1637 : /* Return the DBase field type for the specified field. */
1638 : /* */
1639 : /* Value can be one of: 'C' (String), 'D' (Date), 'F' (Float), */
1640 : /* 'N' (Numeric, with or without decimal), */
1641 : /* 'L' (Logical), */
1642 : /* 'M' (Memo: 10 digits .DBT block ptr) */
1643 : /************************************************************************/
1644 :
1645 : char SHPAPI_CALL
1646 6570 : DBFGetNativeFieldType( DBFHandle psDBF, int iField )
1647 :
1648 : {
1649 6570 : if( iField >=0 && iField < psDBF->nFields )
1650 6570 : return psDBF->pachFieldType[iField];
1651 :
1652 0 : return ' ';
1653 : }
1654 :
1655 : /************************************************************************/
1656 : /* str_to_upper() */
1657 : /************************************************************************/
1658 :
1659 14599448 : static void str_to_upper (char *string)
1660 : {
1661 : int len;
1662 14599448 : short i = -1;
1663 :
1664 14599448 : len = strlen (string);
1665 :
1666 118268256 : while (++i < len)
1667 89069360 : if (isalpha(string[i]) && islower(string[i]))
1668 43823098 : string[i] = (char) toupper ((int)string[i]);
1669 14599448 : }
1670 :
1671 : /************************************************************************/
1672 : /* DBFGetFieldIndex() */
1673 : /* */
1674 : /* Get the index number for a field in a .dbf file. */
1675 : /* */
1676 : /* Contributed by Jim Matthews. */
1677 : /************************************************************************/
1678 :
1679 : int SHPAPI_CALL
1680 25400 : DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
1681 :
1682 : {
1683 : char name[12], name1[12], name2[12];
1684 : int i;
1685 :
1686 25400 : strncpy(name1, pszFieldName,11);
1687 25400 : name1[11] = '\0';
1688 25400 : str_to_upper(name1);
1689 :
1690 14599076 : for( i = 0; i < DBFGetFieldCount(psDBF); i++ )
1691 : {
1692 14574048 : DBFGetFieldInfo( psDBF, i, name, NULL, NULL );
1693 14574048 : strncpy(name2,name,11);
1694 14574048 : str_to_upper(name2);
1695 :
1696 14574048 : if(!strncmp(name1,name2,10))
1697 372 : return(i);
1698 : }
1699 25028 : return(-1);
1700 : }
1701 :
1702 : /************************************************************************/
1703 : /* DBFIsRecordDeleted() */
1704 : /* */
1705 : /* Returns TRUE if the indicated record is deleted, otherwise */
1706 : /* it returns FALSE. */
1707 : /************************************************************************/
1708 :
1709 163144 : int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape )
1710 :
1711 : {
1712 : /* -------------------------------------------------------------------- */
1713 : /* Verify selection. */
1714 : /* -------------------------------------------------------------------- */
1715 163144 : if( iShape < 0 || iShape >= psDBF->nRecords )
1716 0 : return TRUE;
1717 :
1718 : /* -------------------------------------------------------------------- */
1719 : /* Have we read the record? */
1720 : /* -------------------------------------------------------------------- */
1721 163144 : if( !DBFLoadRecord( psDBF, iShape ) )
1722 0 : return FALSE;
1723 :
1724 : /* -------------------------------------------------------------------- */
1725 : /* '*' means deleted. */
1726 : /* -------------------------------------------------------------------- */
1727 163144 : return psDBF->pszCurrentRecord[0] == '*';
1728 : }
1729 :
1730 : /************************************************************************/
1731 : /* DBFMarkRecordDeleted() */
1732 : /************************************************************************/
1733 :
1734 14 : int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape,
1735 : int bIsDeleted )
1736 :
1737 : {
1738 : char chNewFlag;
1739 :
1740 : /* -------------------------------------------------------------------- */
1741 : /* Verify selection. */
1742 : /* -------------------------------------------------------------------- */
1743 14 : if( iShape < 0 || iShape >= psDBF->nRecords )
1744 0 : return FALSE;
1745 :
1746 : /* -------------------------------------------------------------------- */
1747 : /* Is this an existing record, but different than the last one */
1748 : /* we accessed? */
1749 : /* -------------------------------------------------------------------- */
1750 14 : if( !DBFLoadRecord( psDBF, iShape ) )
1751 0 : return FALSE;
1752 :
1753 : /* -------------------------------------------------------------------- */
1754 : /* Assign value, marking record as dirty if it changes. */
1755 : /* -------------------------------------------------------------------- */
1756 14 : if( bIsDeleted )
1757 14 : chNewFlag = '*';
1758 : else
1759 0 : chNewFlag = ' ';
1760 :
1761 14 : if( psDBF->pszCurrentRecord[0] != chNewFlag )
1762 : {
1763 14 : psDBF->bCurrentRecordModified = TRUE;
1764 14 : psDBF->bUpdated = TRUE;
1765 14 : psDBF->pszCurrentRecord[0] = chNewFlag;
1766 : }
1767 :
1768 14 : return TRUE;
1769 : }
1770 :
1771 : /************************************************************************/
1772 : /* DBFGetCodePage */
1773 : /************************************************************************/
1774 :
1775 : const char SHPAPI_CALL1(*)
1776 0 : DBFGetCodePage(DBFHandle psDBF )
1777 : {
1778 0 : if( psDBF == NULL )
1779 0 : return NULL;
1780 0 : return psDBF->pszCodePage;
1781 : }
1782 :
1783 : /************************************************************************/
1784 : /* DBFDeleteField() */
1785 : /* */
1786 : /* Remove a field from a .dbf file */
1787 : /************************************************************************/
1788 :
1789 : int SHPAPI_CALL
1790 12 : DBFDeleteField(DBFHandle psDBF, int iField)
1791 : {
1792 : int nOldRecordLength, nOldHeaderLength;
1793 : int nDeletedFieldOffset, nDeletedFieldSize;
1794 : SAOffset nRecordOffset;
1795 : char* pszRecord;
1796 : int i, iRecord;
1797 :
1798 12 : if (iField < 0 || iField >= psDBF->nFields)
1799 0 : return FALSE;
1800 :
1801 : /* make sure that everything is written in .dbf */
1802 12 : if( !DBFFlushRecord( psDBF ) )
1803 0 : return FALSE;
1804 :
1805 : /* get information about field to be deleted */
1806 12 : nOldRecordLength = psDBF->nRecordLength;
1807 12 : nOldHeaderLength = psDBF->nHeaderLength;
1808 12 : nDeletedFieldOffset = psDBF->panFieldOffset[iField];
1809 12 : nDeletedFieldSize = psDBF->panFieldSize[iField];
1810 :
1811 : /* update fields info */
1812 30 : for (i = iField + 1; i < psDBF->nFields; i++)
1813 : {
1814 18 : psDBF->panFieldOffset[i-1] = psDBF->panFieldOffset[i] - nDeletedFieldSize;
1815 18 : psDBF->panFieldSize[i-1] = psDBF->panFieldSize[i];
1816 18 : psDBF->panFieldDecimals[i-1] = psDBF->panFieldDecimals[i];
1817 18 : psDBF->pachFieldType[i-1] = psDBF->pachFieldType[i];
1818 : }
1819 :
1820 : /* resize fields arrays */
1821 12 : psDBF->nFields--;
1822 :
1823 24 : psDBF->panFieldOffset = (int *)
1824 12 : SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
1825 :
1826 24 : psDBF->panFieldSize = (int *)
1827 12 : SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
1828 :
1829 24 : psDBF->panFieldDecimals = (int *)
1830 12 : SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
1831 :
1832 24 : psDBF->pachFieldType = (char *)
1833 12 : SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
1834 :
1835 : /* update header information */
1836 12 : psDBF->nHeaderLength -= 32;
1837 12 : psDBF->nRecordLength -= nDeletedFieldSize;
1838 :
1839 : /* overwrite field information in header */
1840 24 : memmove(psDBF->pszHeader + iField*32,
1841 12 : psDBF->pszHeader + (iField+1)*32,
1842 12 : sizeof(char) * (psDBF->nFields - iField)*32);
1843 :
1844 12 : psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
1845 :
1846 : /* update size of current record appropriately */
1847 12 : psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
1848 : psDBF->nRecordLength);
1849 :
1850 : /* we're done if we're dealing with not yet created .dbf */
1851 12 : if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
1852 0 : return TRUE;
1853 :
1854 : /* force update of header with new header and record length */
1855 12 : psDBF->bNoHeader = TRUE;
1856 12 : DBFUpdateHeader( psDBF );
1857 :
1858 : /* alloc record */
1859 12 : pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
1860 :
1861 : /* shift records to their new positions */
1862 60 : for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
1863 : {
1864 48 : nRecordOffset =
1865 48 : nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength;
1866 :
1867 : /* load record */
1868 48 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1869 48 : psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
1870 :
1871 48 : nRecordOffset =
1872 48 : psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
1873 :
1874 : /* move record in two steps */
1875 48 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1876 48 : psDBF->sHooks.FWrite( pszRecord, nDeletedFieldOffset, 1, psDBF->fp );
1877 96 : psDBF->sHooks.FWrite( pszRecord + nDeletedFieldOffset + nDeletedFieldSize,
1878 48 : nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize,
1879 : 1, psDBF->fp );
1880 :
1881 : }
1882 :
1883 : /* TODO: truncate file */
1884 :
1885 : /* free record */
1886 12 : free(pszRecord);
1887 :
1888 12 : psDBF->nCurrentRecord = -1;
1889 12 : psDBF->bCurrentRecordModified = FALSE;
1890 :
1891 12 : return TRUE;
1892 : }
1893 :
1894 : /************************************************************************/
1895 : /* DBFReorderFields() */
1896 : /* */
1897 : /* Reorder the fields of a .dbf file */
1898 : /* */
1899 : /* panMap must be exactly psDBF->nFields long and be a permutation */
1900 : /* of [0, psDBF->nFields-1]. This assumption will not be asserted in the*/
1901 : /* code of DBFReorderFields. */
1902 : /************************************************************************/
1903 :
1904 : int SHPAPI_CALL
1905 20 : DBFReorderFields( DBFHandle psDBF, int* panMap )
1906 : {
1907 : SAOffset nRecordOffset;
1908 : int i, iRecord;
1909 : int *panFieldOffsetNew;
1910 : int *panFieldSizeNew;
1911 : int *panFieldDecimalsNew;
1912 : char *pachFieldTypeNew;
1913 : char *pszHeaderNew;
1914 : char *pszRecord;
1915 : char *pszRecordNew;
1916 :
1917 20 : if ( psDBF->nFields == 0 )
1918 0 : return TRUE;
1919 :
1920 : /* make sure that everything is written in .dbf */
1921 20 : if( !DBFFlushRecord( psDBF ) )
1922 0 : return FALSE;
1923 :
1924 20 : panFieldOffsetNew = (int *) malloc(sizeof(int) * psDBF->nFields);
1925 20 : panFieldSizeNew = (int *) malloc(sizeof(int) * psDBF->nFields);
1926 20 : panFieldDecimalsNew = (int *) malloc(sizeof(int) * psDBF->nFields);
1927 20 : pachFieldTypeNew = (char *) malloc(sizeof(char) * psDBF->nFields);
1928 20 : pszHeaderNew = (char*) malloc(sizeof(char) * 32 * psDBF->nFields);
1929 :
1930 : /* shuffle fields definitions */
1931 104 : for(i=0; i < psDBF->nFields; i++)
1932 : {
1933 84 : panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]];
1934 84 : panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]];
1935 84 : pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]];
1936 168 : memcpy(pszHeaderNew + i * 32,
1937 84 : psDBF->pszHeader + panMap[i] * 32, 32);
1938 : }
1939 20 : panFieldOffsetNew[0] = 1;
1940 84 : for(i=1; i < psDBF->nFields; i++)
1941 : {
1942 64 : panFieldOffsetNew[i] = panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
1943 : }
1944 :
1945 20 : free(psDBF->pszHeader);
1946 20 : psDBF->pszHeader = pszHeaderNew;
1947 :
1948 : /* we're done if we're dealing with not yet created .dbf */
1949 20 : if ( !(psDBF->bNoHeader && psDBF->nRecords == 0) )
1950 : {
1951 : /* force update of header with new header and record length */
1952 20 : psDBF->bNoHeader = TRUE;
1953 20 : DBFUpdateHeader( psDBF );
1954 :
1955 : /* alloc record */
1956 20 : pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
1957 20 : pszRecordNew = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
1958 :
1959 : /* shuffle fields in records */
1960 100 : for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
1961 : {
1962 80 : nRecordOffset =
1963 80 : psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
1964 :
1965 : /* load record */
1966 80 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1967 80 : psDBF->sHooks.FRead( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
1968 :
1969 80 : pszRecordNew[0] = pszRecord[0];
1970 :
1971 416 : for(i=0; i < psDBF->nFields; i++)
1972 : {
1973 672 : memcpy(pszRecordNew + panFieldOffsetNew[i],
1974 336 : pszRecord + psDBF->panFieldOffset[panMap[i]],
1975 336 : psDBF->panFieldSize[panMap[i]]);
1976 : }
1977 :
1978 : /* write record */
1979 80 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1980 80 : psDBF->sHooks.FWrite( pszRecordNew, psDBF->nRecordLength, 1, psDBF->fp );
1981 : }
1982 :
1983 : /* free record */
1984 20 : free(pszRecord);
1985 20 : free(pszRecordNew);
1986 : }
1987 :
1988 20 : free(psDBF->panFieldOffset);
1989 20 : free(psDBF->panFieldSize);
1990 20 : free(psDBF->panFieldDecimals);
1991 20 : free(psDBF->pachFieldType);
1992 :
1993 20 : psDBF->panFieldOffset = panFieldOffsetNew;
1994 20 : psDBF->panFieldSize = panFieldSizeNew;
1995 20 : psDBF->panFieldDecimals =panFieldDecimalsNew;
1996 20 : psDBF->pachFieldType = pachFieldTypeNew;
1997 :
1998 20 : psDBF->nCurrentRecord = -1;
1999 20 : psDBF->bCurrentRecordModified = FALSE;
2000 :
2001 20 : return TRUE;
2002 : }
2003 :
2004 :
2005 : /************************************************************************/
2006 : /* DBFAlterFieldDefn() */
2007 : /* */
2008 : /* Alter a field definition in a .dbf file */
2009 : /************************************************************************/
2010 :
2011 : int SHPAPI_CALL
2012 24 : DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName,
2013 : char chType, int nWidth, int nDecimals )
2014 : {
2015 : int i;
2016 : int iRecord;
2017 : int nOffset;
2018 : int nOldWidth;
2019 : int nOldRecordLength;
2020 : int nRecordOffset;
2021 : char* pszFInfo;
2022 : char chOldType;
2023 : int bIsNULL;
2024 : char chFieldFill;
2025 :
2026 24 : if (iField < 0 || iField >= psDBF->nFields)
2027 0 : return FALSE;
2028 :
2029 : /* make sure that everything is written in .dbf */
2030 24 : if( !DBFFlushRecord( psDBF ) )
2031 0 : return FALSE;
2032 :
2033 24 : chFieldFill = DBFGetNullCharacter(chType);
2034 :
2035 24 : chOldType = psDBF->pachFieldType[iField];
2036 24 : nOffset = psDBF->panFieldOffset[iField];
2037 24 : nOldWidth = psDBF->panFieldSize[iField];
2038 24 : nOldRecordLength = psDBF->nRecordLength;
2039 :
2040 : /* -------------------------------------------------------------------- */
2041 : /* Do some checking to ensure we can add records to this file. */
2042 : /* -------------------------------------------------------------------- */
2043 24 : if( nWidth < 1 )
2044 0 : return -1;
2045 :
2046 24 : if( nWidth > 255 )
2047 0 : nWidth = 255;
2048 :
2049 : /* -------------------------------------------------------------------- */
2050 : /* Assign the new field information fields. */
2051 : /* -------------------------------------------------------------------- */
2052 24 : psDBF->panFieldSize[iField] = nWidth;
2053 24 : psDBF->panFieldDecimals[iField] = nDecimals;
2054 24 : psDBF->pachFieldType[iField] = chType;
2055 :
2056 : /* -------------------------------------------------------------------- */
2057 : /* Update the header information. */
2058 : /* -------------------------------------------------------------------- */
2059 24 : pszFInfo = psDBF->pszHeader + 32 * iField;
2060 :
2061 792 : for( i = 0; i < 32; i++ )
2062 768 : pszFInfo[i] = '\0';
2063 :
2064 24 : if( (int) strlen(pszFieldName) < 10 )
2065 24 : strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
2066 : else
2067 0 : strncpy( pszFInfo, pszFieldName, 10);
2068 :
2069 24 : pszFInfo[11] = psDBF->pachFieldType[iField];
2070 :
2071 24 : if( chType == 'C' )
2072 : {
2073 12 : pszFInfo[16] = (unsigned char) (nWidth % 256);
2074 12 : pszFInfo[17] = (unsigned char) (nWidth / 256);
2075 : }
2076 : else
2077 : {
2078 12 : pszFInfo[16] = (unsigned char) nWidth;
2079 12 : pszFInfo[17] = (unsigned char) nDecimals;
2080 : }
2081 :
2082 : /* -------------------------------------------------------------------- */
2083 : /* Update offsets */
2084 : /* -------------------------------------------------------------------- */
2085 24 : if (nWidth != nOldWidth)
2086 : {
2087 66 : for (i = iField + 1; i < psDBF->nFields; i++)
2088 44 : psDBF->panFieldOffset[i] += nWidth - nOldWidth;
2089 22 : psDBF->nRecordLength += nWidth - nOldWidth;
2090 :
2091 22 : psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
2092 : psDBF->nRecordLength);
2093 : }
2094 :
2095 : /* we're done if we're dealing with not yet created .dbf */
2096 24 : if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
2097 0 : return TRUE;
2098 :
2099 : /* force update of header with new header and record length */
2100 24 : psDBF->bNoHeader = TRUE;
2101 24 : DBFUpdateHeader( psDBF );
2102 :
2103 38 : if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType))
2104 : {
2105 14 : char* pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
2106 14 : char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
2107 :
2108 14 : pszOldField[nOldWidth] = 0;
2109 :
2110 : /* move records to their new positions */
2111 70 : for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
2112 : {
2113 56 : nRecordOffset =
2114 : nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2115 :
2116 : /* load record */
2117 56 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2118 56 : psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
2119 :
2120 56 : memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2121 56 : bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
2122 :
2123 56 : if (nWidth != nOldWidth)
2124 : {
2125 56 : if ((chOldType == 'N' || chOldType == 'F') && pszOldField[0] == ' ')
2126 : {
2127 : /* Strip leading spaces when truncating a numeric field */
2128 40 : memmove( pszRecord + nOffset,
2129 30 : pszRecord + nOffset + nOldWidth - nWidth,
2130 : nWidth );
2131 : }
2132 56 : if (nOffset + nOldWidth < nOldRecordLength)
2133 : {
2134 144 : memmove( pszRecord + nOffset + nWidth,
2135 96 : pszRecord + nOffset + nOldWidth,
2136 48 : nOldRecordLength - (nOffset + nOldWidth));
2137 : }
2138 : }
2139 :
2140 : /* Convert null value to the appropriate value of the new type */
2141 56 : if (bIsNULL)
2142 : {
2143 28 : memset( pszRecord + nOffset, chFieldFill, nWidth);
2144 : }
2145 :
2146 56 : nRecordOffset =
2147 : psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2148 :
2149 : /* write record */
2150 56 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2151 56 : psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
2152 : }
2153 :
2154 14 : free(pszRecord);
2155 14 : free(pszOldField);
2156 : }
2157 10 : else if (nWidth > nOldWidth)
2158 : {
2159 8 : char* pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
2160 8 : char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
2161 :
2162 8 : pszOldField[nOldWidth] = 0;
2163 :
2164 : /* move records to their new positions */
2165 30 : for (iRecord = psDBF->nRecords - 1; iRecord >= 0; iRecord--)
2166 : {
2167 22 : nRecordOffset =
2168 : nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2169 :
2170 : /* load record */
2171 22 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2172 22 : psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
2173 :
2174 22 : memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2175 22 : bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
2176 :
2177 22 : if (nOffset + nOldWidth < nOldRecordLength)
2178 : {
2179 54 : memmove( pszRecord + nOffset + nWidth,
2180 36 : pszRecord + nOffset + nOldWidth,
2181 18 : nOldRecordLength - (nOffset + nOldWidth));
2182 : }
2183 :
2184 : /* Convert null value to the appropriate value of the new type */
2185 22 : if (bIsNULL)
2186 : {
2187 8 : memset( pszRecord + nOffset, chFieldFill, nWidth);
2188 : }
2189 : else
2190 : {
2191 18 : if ((chOldType == 'N' || chOldType == 'F'))
2192 : {
2193 : /* Add leading spaces when expanding a numeric field */
2194 8 : memmove( pszRecord + nOffset + nWidth - nOldWidth,
2195 4 : pszRecord + nOffset, nOldWidth );
2196 4 : memset( pszRecord + nOffset, ' ', nWidth - nOldWidth );
2197 : }
2198 : else
2199 : {
2200 : /* Add trailing spaces */
2201 10 : memset(pszRecord + nOffset + nOldWidth, ' ', nWidth - nOldWidth);
2202 : }
2203 : }
2204 :
2205 22 : nRecordOffset =
2206 : psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2207 :
2208 : /* write record */
2209 22 : psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2210 22 : psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
2211 : }
2212 :
2213 8 : free(pszRecord);
2214 8 : free(pszOldField);
2215 : }
2216 :
2217 24 : psDBF->nCurrentRecord = -1;
2218 24 : psDBF->bCurrentRecordModified = FALSE;
2219 :
2220 24 : return TRUE;
2221 : }
|