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