Salome HOME
debug of the land cover maps
[modules/hydro.git] / src / shapelib / dbfopen.c
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 static void * SfRealloc( void * pMem, int nNewSize )
181
182 {
183     if( pMem == NULL )
184         return( (void *) malloc(nNewSize) );
185     else
186         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 static void DBFWriteHeader(DBFHandle psDBF)
199
200 {
201     unsigned char       abyHeader[XBASE_FLDHDR_SZ];
202     int         i;
203
204     if( !psDBF->bNoHeader )
205         return;
206
207     psDBF->bNoHeader = FALSE;
208
209 /* -------------------------------------------------------------------- */
210 /*      Initialize the file header information.                         */
211 /* -------------------------------------------------------------------- */
212     for( i = 0; i < XBASE_FLDHDR_SZ; i++ )
213         abyHeader[i] = 0;
214
215     abyHeader[0] = 0x03;                /* memo field? - just copying   */
216
217     /* write out a dummy date */
218     abyHeader[1] = 95;                  /* YY */
219     abyHeader[2] = 7;                   /* MM */
220     abyHeader[3] = 26;                  /* DD */
221
222     /* record count preset at zero */
223
224     abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256);
225     abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256);
226     
227     abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256);
228     abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256);
229
230     abyHeader[29] = (unsigned char) (psDBF->iLanguageDriver);
231
232 /* -------------------------------------------------------------------- */
233 /*      Write the initial 32 byte file header, and all the field        */
234 /*      descriptions.                                                   */
235 /* -------------------------------------------------------------------- */
236     psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
237     psDBF->sHooks.FWrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp );
238     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     if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 )
245     {
246         char    cNewline;
247
248         cNewline = 0x0d;
249         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 static int DBFFlushRecord( DBFHandle psDBF )
260
261 {
262     SAOffset    nRecordOffset;
263
264     if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 )
265     {
266         psDBF->bCurrentRecordModified = FALSE;
267
268         nRecordOffset = 
269             psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord 
270             + psDBF->nHeaderLength;
271
272         if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0 
273             || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord, 
274                                      psDBF->nRecordLength, 
275                                      1, psDBF->fp ) != 1 )
276         {
277             char szMessage[128];
278             sprintf( szMessage, "Failure writing DBF record %d.", 
279                      psDBF->nCurrentRecord );
280             psDBF->sHooks.Error( szMessage );
281             return FALSE;
282         }
283     }
284
285     return TRUE;
286 }
287
288 /************************************************************************/
289 /*                           DBFLoadRecord()                            */
290 /************************************************************************/
291
292 static int DBFLoadRecord( DBFHandle psDBF, int iRecord )
293
294 {
295     if( psDBF->nCurrentRecord != iRecord )
296     {
297         SAOffset nRecordOffset;
298
299         if( !DBFFlushRecord( psDBF ) )
300             return FALSE;
301
302         nRecordOffset = 
303             psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
304
305         if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, SEEK_SET ) != 0 )
306         {
307             char szMessage[128];
308             sprintf( szMessage, "fseek(%ld) failed on DBF file.\n",
309                      (long) nRecordOffset );
310             psDBF->sHooks.Error( szMessage );
311             return FALSE;
312         }
313
314         if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord, 
315                                  psDBF->nRecordLength, 1, psDBF->fp ) != 1 )
316         {
317             char szMessage[128];
318             sprintf( szMessage, "fread(%d) failed on DBF file.\n",
319                      psDBF->nRecordLength );
320             psDBF->sHooks.Error( szMessage );
321             return FALSE;
322         }
323
324         psDBF->nCurrentRecord = iRecord;
325     }
326
327     return TRUE;
328 }
329
330 /************************************************************************/
331 /*                          DBFUpdateHeader()                           */
332 /************************************************************************/
333
334 void SHPAPI_CALL
335 DBFUpdateHeader( DBFHandle psDBF )
336
337 {
338     unsigned char               abyFileHeader[32];
339
340     if( psDBF->bNoHeader )
341         DBFWriteHeader( psDBF );
342
343     DBFFlushRecord( psDBF );
344
345     psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
346     psDBF->sHooks.FRead( abyFileHeader, 32, 1, psDBF->fp );
347     
348     abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256);
349     abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256);
350     abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256);
351     abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256);
352     
353     psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
354     psDBF->sHooks.FWrite( abyFileHeader, 32, 1, psDBF->fp );
355
356     psDBF->sHooks.FFlush( psDBF->fp );
357 }
358
359 /************************************************************************/
360 /*                              DBFOpen()                               */
361 /*                                                                      */
362 /*      Open a .dbf file.                                               */
363 /************************************************************************/
364    
365 DBFHandle SHPAPI_CALL
366 DBFOpen( const char * pszFilename, const char * pszAccess )
367
368 {
369     SAHooks sHooks;
370
371     SASetupDefaultHooks( &sHooks );
372
373     return DBFOpenLL( pszFilename, pszAccess, &sHooks );
374 }
375
376 /************************************************************************/
377 /*                              DBFOpen()                               */
378 /*                                                                      */
379 /*      Open a .dbf file.                                               */
380 /************************************************************************/
381    
382 DBFHandle SHPAPI_CALL
383 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     int                 nBufSize = 500;
392
393 /* -------------------------------------------------------------------- */
394 /*      We only allow the access strings "rb" and "r+".                  */
395 /* -------------------------------------------------------------------- */
396     if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 
397         && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0
398         && strcmp(pszAccess,"r+b") != 0 )
399         return( NULL );
400
401     if( strcmp(pszAccess,"r") == 0 )
402         pszAccess = "rb";
403  
404     if( strcmp(pszAccess,"r+") == 0 )
405         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     pszBasename = (char *) malloc(strlen(pszFilename)+5);
412     strcpy( pszBasename, pszFilename );
413     for( i = strlen(pszBasename)-1; 
414          i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
415                && pszBasename[i] != '\\';
416          i-- ) {}
417
418     if( pszBasename[i] == '.' )
419         pszBasename[i] = '\0';
420
421     pszFullname = (char *) malloc(strlen(pszBasename) + 5);
422     sprintf( pszFullname, "%s.dbf", pszBasename );
423         
424     psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) );
425     psDBF->fp = psHooks->FOpen( pszFullname, pszAccess );
426     memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
427
428     if( psDBF->fp == NULL )
429     {
430         sprintf( pszFullname, "%s.DBF", pszBasename );
431         psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess );
432     }
433
434     sprintf( pszFullname, "%s.cpg", pszBasename );
435     pfCPG = psHooks->FOpen( pszFullname, "r" );
436     if( pfCPG == NULL )
437     {
438         sprintf( pszFullname, "%s.CPG", pszBasename );
439         pfCPG = psHooks->FOpen( pszFullname, "r" );
440     }
441
442     free( pszBasename );
443     free( pszFullname );
444     
445     if( psDBF->fp == NULL )
446     {
447         free( psDBF );
448         if( pfCPG ) psHooks->FClose( pfCPG );
449         return( NULL );
450     }
451
452     psDBF->bNoHeader = FALSE;
453     psDBF->nCurrentRecord = -1;
454     psDBF->bCurrentRecordModified = FALSE;
455
456 /* -------------------------------------------------------------------- */
457 /*  Read Table Header info                                              */
458 /* -------------------------------------------------------------------- */
459     pabyBuf = (unsigned char *) malloc(nBufSize);
460     if( psDBF->sHooks.FRead( pabyBuf, 32, 1, psDBF->fp ) != 1 )
461     {
462         psDBF->sHooks.FClose( psDBF->fp );
463         if( pfCPG ) psDBF->sHooks.FClose( pfCPG );
464         free( pabyBuf );
465         free( psDBF );
466         return NULL;
467     }
468
469     psDBF->nRecords = 
470      pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256;
471
472     psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256;
473     psDBF->nRecordLength = pabyBuf[10] + pabyBuf[11]*256;
474     psDBF->iLanguageDriver = pabyBuf[29];
475
476     if (nHeadLen < 32)
477     {
478         psDBF->sHooks.FClose( psDBF->fp );
479         if( pfCPG ) psDBF->sHooks.FClose( pfCPG );
480         free( pabyBuf );
481         free( psDBF );
482         return NULL;
483     }
484
485     psDBF->nFields = nFields = (nHeadLen - 32) / 32;
486
487     psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength);
488
489 /* -------------------------------------------------------------------- */
490 /*  Figure out the code page from the LDID and CPG                      */
491 /* -------------------------------------------------------------------- */
492
493     psDBF->pszCodePage = NULL;
494     if( pfCPG )
495     {
496         size_t n;
497         memset( pabyBuf, 0, nBufSize);
498         psDBF->sHooks.FRead( pabyBuf, nBufSize - 1, 1, pfCPG );
499         n = strcspn( (char *) pabyBuf, "\n\r" );
500         if( n > 0 )
501         {
502             pabyBuf[n] = '\0';
503             psDBF->pszCodePage = (char *) malloc(n + 1);
504             memcpy( psDBF->pszCodePage, pabyBuf, n + 1 );
505         }
506                 psDBF->sHooks.FClose( pfCPG );
507     }
508     if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 )
509     {
510         sprintf( (char *) pabyBuf, "LDID/%d", psDBF->iLanguageDriver );
511         psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1);
512         strcpy( psDBF->pszCodePage, (char *) pabyBuf );
513     }
514
515 /* -------------------------------------------------------------------- */
516 /*  Read in Field Definitions                                           */
517 /* -------------------------------------------------------------------- */
518     
519     pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen);
520     psDBF->pszHeader = (char *) pabyBuf;
521
522     psDBF->sHooks.FSeek( psDBF->fp, 32, 0 );
523     if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 )
524     {
525         psDBF->sHooks.FClose( psDBF->fp );
526         free( pabyBuf );
527         free( psDBF->pszCurrentRecord );
528         free( psDBF );
529         return NULL;
530     }
531
532     psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields);
533     psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields);
534     psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields);
535     psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields);
536
537     for( iField = 0; iField < nFields; iField++ )
538     {
539         unsigned char           *pabyFInfo;
540
541         pabyFInfo = pabyBuf+iField*32;
542
543         if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' )
544         {
545             psDBF->panFieldSize[iField] = pabyFInfo[16];
546             psDBF->panFieldDecimals[iField] = pabyFInfo[17];
547         }
548         else
549         {
550             psDBF->panFieldSize[iField] = pabyFInfo[16];
551             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         psDBF->pachFieldType[iField] = (char) pabyFInfo[11];
564         if( iField == 0 )
565             psDBF->panFieldOffset[iField] = 1;
566         else
567             psDBF->panFieldOffset[iField] = 
568               psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1];
569     }
570
571     return( psDBF );
572 }
573
574 /************************************************************************/
575 /*                              DBFClose()                              */
576 /************************************************************************/
577
578 void SHPAPI_CALL
579 DBFClose(DBFHandle psDBF)
580 {
581     if( psDBF == NULL )
582         return;
583
584 /* -------------------------------------------------------------------- */
585 /*      Write out header if not already written.                        */
586 /* -------------------------------------------------------------------- */
587     if( psDBF->bNoHeader )
588         DBFWriteHeader( psDBF );
589
590     DBFFlushRecord( psDBF );
591
592 /* -------------------------------------------------------------------- */
593 /*      Update last access date, and number of records if we have       */
594 /*      write access.                                                   */
595 /* -------------------------------------------------------------------- */
596     if( psDBF->bUpdated )
597         DBFUpdateHeader( psDBF );
598
599 /* -------------------------------------------------------------------- */
600 /*      Close, and free resources.                                      */
601 /* -------------------------------------------------------------------- */
602     psDBF->sHooks.FClose( psDBF->fp );
603
604     if( psDBF->panFieldOffset != NULL )
605     {
606         free( psDBF->panFieldOffset );
607         free( psDBF->panFieldSize );
608         free( psDBF->panFieldDecimals );
609         free( psDBF->pachFieldType );
610     }
611
612     if( psDBF->pszWorkField != NULL )
613         free( psDBF->pszWorkField );
614
615     free( psDBF->pszHeader );
616     free( psDBF->pszCurrentRecord );
617     free( psDBF->pszCodePage );
618
619     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 DBFCreate( const char * pszFilename )
630
631 {
632     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 DBFCreateEx( const char * pszFilename, const char* pszCodePage )
643
644 {
645     SAHooks sHooks;
646
647     SASetupDefaultHooks( &sHooks );
648
649     return DBFCreateLL( pszFilename, pszCodePage , &sHooks );
650 }
651
652 /************************************************************************/
653 /*                             DBFCreate()                              */
654 /*                                                                      */
655 /*      Create a new .dbf file.                                         */
656 /************************************************************************/
657
658 DBFHandle SHPAPI_CALL
659 DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHooks )
660
661 {
662     DBFHandle   psDBF;
663     SAFile      fp;
664     char        *pszFullname, *pszBasename;
665     int         i, ldid = -1;
666     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     pszBasename = (char *) malloc(strlen(pszFilename)+5);
673     strcpy( pszBasename, pszFilename );
674     for( i = strlen(pszBasename)-1; 
675          i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
676                && pszBasename[i] != '\\';
677          i-- ) {}
678
679     if( pszBasename[i] == '.' )
680         pszBasename[i] = '\0';
681
682     pszFullname = (char *) malloc(strlen(pszBasename) + 5);
683     sprintf( pszFullname, "%s.dbf", pszBasename );
684
685 /* -------------------------------------------------------------------- */
686 /*      Create the file.                                                */
687 /* -------------------------------------------------------------------- */
688     fp = psHooks->FOpen( pszFullname, "wb" );
689     if( fp == NULL )
690         return( NULL );
691     
692     psHooks->FWrite( &chZero, 1, 1, fp );
693     psHooks->FClose( fp );
694
695     fp = psHooks->FOpen( pszFullname, "rb+" );
696     if( fp == NULL )
697         return( NULL );
698
699
700     sprintf( pszFullname, "%s.cpg", pszBasename );
701     if( pszCodePage != NULL )
702     {
703         if( strncmp( pszCodePage, "LDID/", 5 ) == 0 )
704         {
705             ldid = atoi( pszCodePage + 5 );
706             if( ldid > 255 )
707                 ldid = -1; // don't use 0 to indicate out of range as LDID/0 is a valid one
708         }
709         if( ldid < 0 )
710         {
711             SAFile fpCPG = psHooks->FOpen( pszFullname, "w" );
712             psHooks->FWrite( (char*) pszCodePage, strlen(pszCodePage), 1, fpCPG );
713             psHooks->FClose( fpCPG );
714         }
715     }
716     if( pszCodePage == NULL || ldid >= 0 )
717     {
718         psHooks->Remove( pszFullname );
719     }
720
721     free( pszBasename );
722     free( pszFullname );
723
724 /* -------------------------------------------------------------------- */
725 /*      Create the info structure.                                      */
726 /* -------------------------------------------------------------------- */
727     psDBF = (DBFHandle) calloc(1,sizeof(DBFInfo));
728
729     memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
730     psDBF->fp = fp;
731     psDBF->nRecords = 0;
732     psDBF->nFields = 0;
733     psDBF->nRecordLength = 1;
734     psDBF->nHeaderLength = 33;
735     
736     psDBF->panFieldOffset = NULL;
737     psDBF->panFieldSize = NULL;
738     psDBF->panFieldDecimals = NULL;
739     psDBF->pachFieldType = NULL;
740     psDBF->pszHeader = NULL;
741
742     psDBF->nCurrentRecord = -1;
743     psDBF->bCurrentRecordModified = FALSE;
744     psDBF->pszCurrentRecord = NULL;
745
746     psDBF->bNoHeader = TRUE;
747
748     psDBF->iLanguageDriver = ldid > 0 ? ldid : 0;
749     psDBF->pszCodePage = NULL;
750     if( pszCodePage )
751     {
752         psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 );
753         strcpy( psDBF->pszCodePage, pszCodePage );
754     }
755
756     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 DBFAddField(DBFHandle psDBF, const char * pszFieldName, 
767             DBFFieldType eType, int nWidth, int nDecimals )
768
769 {
770     char chNativeType = 'C';
771
772     if( eType == FTLogical )
773         chNativeType = 'L';
774     else if( eType == FTString )
775         chNativeType = 'C';
776     else
777         chNativeType = 'N';
778
779     return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType, 
780                                   nWidth, nDecimals );
781 }
782
783 /************************************************************************/
784 /*                        DBFGetNullCharacter()                         */
785 /************************************************************************/
786
787 static char DBFGetNullCharacter(char chType)
788 {
789     switch (chType)
790     {
791       case 'N':
792       case 'F':
793         return '*';
794       case 'D':
795         return '0';
796       case 'L':
797        return '?';
798       default:
799        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 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     if( !DBFFlushRecord( psDBF ) )
824         return -1;
825
826 /* -------------------------------------------------------------------- */
827 /*      Do some checking to ensure we can add records to this file.     */
828 /* -------------------------------------------------------------------- */
829     if( nWidth < 1 )
830         return -1;
831
832     if( nWidth > 255 )
833         nWidth = 255;
834
835     nOldRecordLength = psDBF->nRecordLength;
836     nOldHeaderLength = psDBF->nHeaderLength;
837
838 /* -------------------------------------------------------------------- */
839 /*      SfRealloc all the arrays larger to hold the additional field      */
840 /*      information.                                                    */
841 /* -------------------------------------------------------------------- */
842     psDBF->nFields++;
843
844     psDBF->panFieldOffset = (int *) 
845         SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
846
847     psDBF->panFieldSize = (int *) 
848         SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
849
850     psDBF->panFieldDecimals = (int *) 
851         SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
852
853     psDBF->pachFieldType = (char *) 
854         SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
855
856 /* -------------------------------------------------------------------- */
857 /*      Assign the new field information fields.                        */
858 /* -------------------------------------------------------------------- */
859     psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength;
860     psDBF->nRecordLength += nWidth;
861     psDBF->panFieldSize[psDBF->nFields-1] = nWidth;
862     psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals;
863     psDBF->pachFieldType[psDBF->nFields-1] = chType;
864
865 /* -------------------------------------------------------------------- */
866 /*      Extend the required header information.                         */
867 /* -------------------------------------------------------------------- */
868     psDBF->nHeaderLength += 32;
869     psDBF->bUpdated = FALSE;
870
871     psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
872
873     pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1);
874
875     for( i = 0; i < 32; i++ )
876         pszFInfo[i] = '\0';
877
878     if( (int) strlen(pszFieldName) < 10 )
879         strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
880     else
881         strncpy( pszFInfo, pszFieldName, 10);
882
883     pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1];
884
885     if( chType == 'C' )
886     {
887         pszFInfo[16] = (unsigned char) (nWidth % 256);
888         pszFInfo[17] = (unsigned char) (nWidth / 256);
889     }
890     else
891     {
892         pszFInfo[16] = (unsigned char) nWidth;
893         pszFInfo[17] = (unsigned char) nDecimals;
894     }
895     
896 /* -------------------------------------------------------------------- */
897 /*      Make the current record buffer appropriately larger.            */
898 /* -------------------------------------------------------------------- */
899     psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
900                                                  psDBF->nRecordLength);
901
902     /* we're done if dealing with new .dbf */
903     if( psDBF->bNoHeader )
904         return( psDBF->nFields - 1 );
905
906 /* -------------------------------------------------------------------- */
907 /*      For existing .dbf file, shift records                           */
908 /* -------------------------------------------------------------------- */
909
910     /* alloc record */
911     pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
912
913     chFieldFill = DBFGetNullCharacter(chType);
914
915     for (i = psDBF->nRecords-1; i >= 0; --i)
916     {
917         nRecordOffset = nOldRecordLength * (SAOffset) i + nOldHeaderLength;
918
919         /* load record */
920         psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
921         psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
922
923         /* set new field's value to NULL */
924         memset(pszRecord + nOldRecordLength, chFieldFill, nWidth);
925
926         nRecordOffset = psDBF->nRecordLength * (SAOffset) i + psDBF->nHeaderLength;
927
928         /* move record to the new place*/
929         psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
930         psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
931     }
932
933     /* free record */
934     free(pszRecord);
935
936     /* force update of header with new header, record length and new field */
937     psDBF->bNoHeader = TRUE;
938     DBFUpdateHeader( psDBF );
939
940     psDBF->nCurrentRecord = -1;
941     psDBF->bCurrentRecordModified = FALSE;
942
943     return( psDBF->nFields-1 );
944 }
945
946 /************************************************************************/
947 /*                          DBFReadAttribute()                          */
948 /*                                                                      */
949 /*      Read one of the attribute fields of a record.                   */
950 /************************************************************************/
951
952 static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField,
953                               char chReqType )
954
955 {
956     unsigned char       *pabyRec;
957     void        *pReturnField = NULL;
958
959 /* -------------------------------------------------------------------- */
960 /*      Verify selection.                                               */
961 /* -------------------------------------------------------------------- */
962     if( hEntity < 0 || hEntity >= psDBF->nRecords )
963         return( NULL );
964
965     if( iField < 0 || iField >= psDBF->nFields )
966         return( NULL );
967
968 /* -------------------------------------------------------------------- */
969 /*      Have we read the record?                                        */
970 /* -------------------------------------------------------------------- */
971     if( !DBFLoadRecord( psDBF, hEntity ) )
972         return NULL;
973
974     pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
975
976 /* -------------------------------------------------------------------- */
977 /*      Ensure we have room to extract the target field.                */
978 /* -------------------------------------------------------------------- */
979     if( psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength )
980     {
981         psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100;
982         if( psDBF->pszWorkField == NULL )
983             psDBF->pszWorkField = (char *) malloc(psDBF->nWorkFieldLength);
984         else
985             psDBF->pszWorkField = (char *) realloc(psDBF->pszWorkField,
986                                                    psDBF->nWorkFieldLength);
987     }
988
989 /* -------------------------------------------------------------------- */
990 /*      Extract the requested field.                                    */
991 /* -------------------------------------------------------------------- */
992     strncpy( psDBF->pszWorkField,
993              ((const char *) pabyRec) + psDBF->panFieldOffset[iField],
994              psDBF->panFieldSize[iField] );
995     psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0';
996
997     pReturnField = psDBF->pszWorkField;
998
999 /* -------------------------------------------------------------------- */
1000 /*      Decode the field.                                               */
1001 /* -------------------------------------------------------------------- */
1002     if( chReqType == 'N' )
1003     {
1004         psDBF->dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField);
1005
1006         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         pchDst = pchSrc = psDBF->pszWorkField;
1018         while( *pchSrc == ' ' )
1019             pchSrc++;
1020
1021         while( *pchSrc != '\0' )
1022             *(pchDst++) = *(pchSrc++);
1023         *pchDst = '\0';
1024
1025         while( pchDst != psDBF->pszWorkField && *(--pchDst) == ' ' )
1026             *pchDst = '\0';
1027     }
1028 #endif
1029     
1030     return( pReturnField );
1031 }
1032
1033 /************************************************************************/
1034 /*                        DBFReadIntAttribute()                         */
1035 /*                                                                      */
1036 /*      Read an integer attribute.                                      */
1037 /************************************************************************/
1038
1039 int SHPAPI_CALL
1040 DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField )
1041
1042 {
1043     double      *pdValue;
1044
1045     pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
1046
1047     if( pdValue == NULL )
1048         return 0;
1049     else
1050         return( (int) *pdValue );
1051 }
1052
1053 /************************************************************************/
1054 /*                        DBFReadDoubleAttribute()                      */
1055 /*                                                                      */
1056 /*      Read a double attribute.                                        */
1057 /************************************************************************/
1058
1059 double SHPAPI_CALL
1060 DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField )
1061
1062 {
1063     double      *pdValue;
1064
1065     pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
1066
1067     if( pdValue == NULL )
1068         return 0.0;
1069     else
1070         return( *pdValue );
1071 }
1072
1073 /************************************************************************/
1074 /*                        DBFReadStringAttribute()                      */
1075 /*                                                                      */
1076 /*      Read a string attribute.                                        */
1077 /************************************************************************/
1078
1079 const char SHPAPI_CALL1(*)
1080 DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField )
1081
1082 {
1083     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 DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField )
1094
1095 {
1096     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 static int DBFIsValueNULL( char chType, const char* pszValue )
1107 {
1108     int i;
1109
1110     if( pszValue == NULL )
1111         return TRUE;
1112
1113     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         if( pszValue[0] == '*' )
1123             return TRUE;
1124
1125         for( i = 0; pszValue[i] != '\0'; i++ )
1126         {
1127             if( pszValue[i] != ' ' )
1128                 return FALSE;
1129         }
1130         return TRUE;
1131
1132       case 'D':
1133         /* NULL date fields have value "00000000" */
1134         return strncmp(pszValue,"00000000",8) == 0;
1135
1136       case 'L':
1137         /* NULL boolean fields have value "?" */
1138         return pszValue[0] == '?';
1139
1140       default:
1141         /* empty string fields are considered NULL */
1142         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 DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField )
1156
1157 {
1158     const char  *pszValue;
1159
1160     pszValue = DBFReadStringAttribute( psDBF, iRecord, iField );
1161
1162     if( pszValue == NULL )
1163         return TRUE;
1164
1165     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 DBFGetFieldCount( DBFHandle psDBF )
1176
1177 {
1178     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 DBFGetRecordCount( DBFHandle psDBF )
1189
1190 {
1191     return( psDBF->nRecords );
1192 }
1193
1194 /************************************************************************/
1195 /*                          DBFGetFieldInfo()                           */
1196 /*                                                                      */
1197 /*      Return any requested information about the field.               */
1198 /************************************************************************/
1199
1200 DBFFieldType SHPAPI_CALL
1201 DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName,
1202                  int * pnWidth, int * pnDecimals )
1203
1204 {
1205     if( iField < 0 || iField >= psDBF->nFields )
1206         return( FTInvalid );
1207
1208     if( pnWidth != NULL )
1209         *pnWidth = psDBF->panFieldSize[iField];
1210
1211     if( pnDecimals != NULL )
1212         *pnDecimals = psDBF->panFieldDecimals[iField];
1213
1214     if( pszFieldName != NULL )
1215     {
1216         int     i;
1217
1218         strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 );
1219         pszFieldName[11] = '\0';
1220         for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- )
1221             pszFieldName[i] = '\0';
1222     }
1223
1224     if ( psDBF->pachFieldType[iField] == 'L' )
1225         return( FTLogical);
1226
1227     else if( psDBF->pachFieldType[iField] == 'N' 
1228              || psDBF->pachFieldType[iField] == 'F' )
1229     {
1230         if( psDBF->panFieldDecimals[iField] > 0 
1231             || psDBF->panFieldSize[iField] > 10 )
1232             return( FTDouble );
1233         else
1234             return( FTInteger );
1235     }
1236     else
1237     {
1238         return( FTString );
1239     }
1240 }
1241
1242 /************************************************************************/
1243 /*                         DBFWriteAttribute()                          */
1244 /*                                                                      */
1245 /*      Write an attribute record to the file.                          */
1246 /************************************************************************/
1247
1248 static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
1249                              void * pValue )
1250
1251 {
1252     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     if( hEntity < 0 || hEntity > psDBF->nRecords )
1260         return( FALSE );
1261
1262     if( psDBF->bNoHeader )
1263         DBFWriteHeader(psDBF);
1264
1265 /* -------------------------------------------------------------------- */
1266 /*      Is this a brand new record?                                     */
1267 /* -------------------------------------------------------------------- */
1268     if( hEntity == psDBF->nRecords )
1269     {
1270         if( !DBFFlushRecord( psDBF ) )
1271             return FALSE;
1272
1273         psDBF->nRecords++;
1274         for( i = 0; i < psDBF->nRecordLength; i++ )
1275             psDBF->pszCurrentRecord[i] = ' ';
1276
1277         psDBF->nCurrentRecord = hEntity;
1278     }
1279
1280 /* -------------------------------------------------------------------- */
1281 /*      Is this an existing record, but different than the last one     */
1282 /*      we accessed?                                                    */
1283 /* -------------------------------------------------------------------- */
1284     if( !DBFLoadRecord( psDBF, hEntity ) )
1285         return FALSE;
1286
1287     pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1288
1289     psDBF->bCurrentRecordModified = TRUE;
1290     psDBF->bUpdated = TRUE;
1291
1292 /* -------------------------------------------------------------------- */
1293 /*      Translate NULL value to valid DBF file representation.          */
1294 /*                                                                      */
1295 /*      Contributed by Jim Matthews.                                    */
1296 /* -------------------------------------------------------------------- */
1297     if( pValue == NULL )
1298     {
1299         memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]),
1300                 DBFGetNullCharacter(psDBF->pachFieldType[iField]),
1301                 psDBF->panFieldSize[iField] );
1302         return TRUE;
1303     }
1304
1305 /* -------------------------------------------------------------------- */
1306 /*      Assign all the record fields.                                   */
1307 /* -------------------------------------------------------------------- */
1308     switch( psDBF->pachFieldType[iField] )
1309     {
1310       case 'D':
1311       case 'N':
1312       case 'F':
1313         if( psDBF->panFieldDecimals[iField] == 0 )
1314         {
1315             int         nWidth = psDBF->panFieldSize[iField];
1316
1317             if( (int) sizeof(szSField)-2 < nWidth )
1318                 nWidth = sizeof(szSField)-2;
1319
1320             sprintf( szFormat, "%%%dd", nWidth );
1321             sprintf(szSField, szFormat, (int) *((double *) pValue) );
1322             if( (int)strlen(szSField) > psDBF->panFieldSize[iField] )
1323             {
1324                 szSField[psDBF->panFieldSize[iField]] = '\0';
1325                 nRetResult = FALSE;
1326             }
1327
1328             strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1329                     szSField, strlen(szSField) );
1330         }
1331         else
1332         {
1333             int         nWidth = psDBF->panFieldSize[iField];
1334
1335             if( (int) sizeof(szSField)-2 < nWidth )
1336                 nWidth = sizeof(szSField)-2;
1337
1338             sprintf( szFormat, "%%%d.%df", 
1339                      nWidth, psDBF->panFieldDecimals[iField] );
1340             sprintf(szSField, szFormat, *((double *) pValue) );
1341             if( (int) strlen(szSField) > psDBF->panFieldSize[iField] )
1342             {
1343                 szSField[psDBF->panFieldSize[iField]] = '\0';
1344                 nRetResult = FALSE;
1345             }
1346             strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1347                     szSField, strlen(szSField) );
1348         }
1349         break;
1350
1351       case 'L':
1352         if (psDBF->panFieldSize[iField] >= 1  && 
1353             (*(char*)pValue == 'F' || *(char*)pValue == 'T'))
1354             *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue;
1355         break;
1356
1357       default:
1358         if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] )
1359         {
1360             j = psDBF->panFieldSize[iField];
1361             nRetResult = FALSE;
1362         }
1363         else
1364         {
1365             memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
1366                     psDBF->panFieldSize[iField] );
1367             j = strlen((char *) pValue);
1368         }
1369
1370         strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1371                 (char *) pValue, j );
1372         break;
1373     }
1374
1375     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 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     if( hEntity < 0 || hEntity > psDBF->nRecords )
1398         return( FALSE );
1399
1400     if( psDBF->bNoHeader )
1401         DBFWriteHeader(psDBF);
1402
1403 /* -------------------------------------------------------------------- */
1404 /*      Is this a brand new record?                                     */
1405 /* -------------------------------------------------------------------- */
1406     if( hEntity == psDBF->nRecords )
1407     {
1408         if( !DBFFlushRecord( psDBF ) )
1409             return FALSE;
1410
1411         psDBF->nRecords++;
1412         for( i = 0; i < psDBF->nRecordLength; i++ )
1413             psDBF->pszCurrentRecord[i] = ' ';
1414
1415         psDBF->nCurrentRecord = hEntity;
1416     }
1417
1418 /* -------------------------------------------------------------------- */
1419 /*      Is this an existing record, but different than the last one     */
1420 /*      we accessed?                                                    */
1421 /* -------------------------------------------------------------------- */
1422     if( !DBFLoadRecord( psDBF, hEntity ) )
1423         return FALSE;
1424
1425     pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1426
1427 /* -------------------------------------------------------------------- */
1428 /*      Assign all the record fields.                                   */
1429 /* -------------------------------------------------------------------- */
1430     if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] )
1431         j = psDBF->panFieldSize[iField];
1432     else
1433     {
1434         memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
1435                 psDBF->panFieldSize[iField] );
1436         j = strlen((char *) pValue);
1437     }
1438
1439     strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
1440             (char *) pValue, j );
1441
1442     psDBF->bCurrentRecordModified = TRUE;
1443     psDBF->bUpdated = TRUE;
1444
1445     return( TRUE );
1446 }
1447
1448 /************************************************************************/
1449 /*                      DBFWriteDoubleAttribute()                       */
1450 /*                                                                      */
1451 /*      Write a double attribute.                                       */
1452 /************************************************************************/
1453
1454 int SHPAPI_CALL
1455 DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField,
1456                          double dValue )
1457
1458 {
1459     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 DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField,
1470                           int nValue )
1471
1472 {
1473     double      dValue = nValue;
1474
1475     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 DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField,
1486                          const char * pszValue )
1487
1488 {
1489     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 DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField )
1500
1501 {
1502     return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) );
1503 }
1504
1505 /************************************************************************/
1506 /*                      DBFWriteLogicalAttribute()                      */
1507 /*                                                                      */
1508 /*      Write a logical attribute.                                      */
1509 /************************************************************************/
1510
1511 int SHPAPI_CALL
1512 DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField,
1513                        const char lValue)
1514
1515 {
1516     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 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     if( hEntity < 0 || hEntity > psDBF->nRecords )
1536         return( FALSE );
1537
1538     if( psDBF->bNoHeader )
1539         DBFWriteHeader(psDBF);
1540
1541 /* -------------------------------------------------------------------- */
1542 /*      Is this a brand new record?                                     */
1543 /* -------------------------------------------------------------------- */
1544     if( hEntity == psDBF->nRecords )
1545     {
1546         if( !DBFFlushRecord( psDBF ) )
1547             return FALSE;
1548
1549         psDBF->nRecords++;
1550         for( i = 0; i < psDBF->nRecordLength; i++ )
1551             psDBF->pszCurrentRecord[i] = ' ';
1552
1553         psDBF->nCurrentRecord = hEntity;
1554     }
1555
1556 /* -------------------------------------------------------------------- */
1557 /*      Is this an existing record, but different than the last one     */
1558 /*      we accessed?                                                    */
1559 /* -------------------------------------------------------------------- */
1560     if( !DBFLoadRecord( psDBF, hEntity ) )
1561         return FALSE;
1562
1563     pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
1564
1565     memcpy ( pabyRec, pRawTuple,  psDBF->nRecordLength );
1566
1567     psDBF->bCurrentRecordModified = TRUE;
1568     psDBF->bUpdated = TRUE;
1569
1570     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 DBFReadTuple(DBFHandle psDBF, int hEntity )
1582
1583 {
1584     if( hEntity < 0 || hEntity >= psDBF->nRecords )
1585         return( NULL );
1586
1587     if( !DBFLoadRecord( psDBF, hEntity ) )
1588         return NULL;
1589
1590     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 DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) 
1601 {
1602     DBFHandle   newDBF;
1603
1604    newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage );
1605    if ( newDBF == NULL ) return ( NULL ); 
1606    
1607    newDBF->nFields = psDBF->nFields;
1608    newDBF->nRecordLength = psDBF->nRecordLength;
1609    newDBF->nHeaderLength = psDBF->nHeaderLength;
1610     
1611    newDBF->pszHeader = (char *) malloc ( newDBF->nHeaderLength );
1612    memcpy ( newDBF->pszHeader, psDBF->pszHeader, newDBF->nHeaderLength );
1613    
1614    newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); 
1615    memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
1616    newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields );
1617    memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
1618    newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields );
1619    memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
1620    newDBF->pachFieldType = (char *) malloc ( sizeof(char) * psDBF->nFields );
1621    memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(char)*psDBF->nFields );
1622
1623    newDBF->bNoHeader = TRUE;
1624    newDBF->bUpdated = TRUE;
1625    
1626    DBFWriteHeader ( newDBF );
1627    DBFClose ( newDBF );
1628    
1629    newDBF = DBFOpen ( pszFilename, "rb+" );
1630
1631    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 DBFGetNativeFieldType( DBFHandle psDBF, int iField )
1647
1648 {
1649     if( iField >=0 && iField < psDBF->nFields )
1650         return psDBF->pachFieldType[iField];
1651
1652     return  ' ';
1653 }
1654
1655 /************************************************************************/
1656 /*                            str_to_upper()                            */
1657 /************************************************************************/
1658
1659 static void str_to_upper (char *string)
1660 {
1661     int len;
1662     short i = -1;
1663
1664     len = strlen (string);
1665
1666     while (++i < len)
1667         if (isalpha(string[i]) && islower(string[i]))
1668             string[i] = (char) toupper ((int)string[i]);
1669 }
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 DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
1681
1682 {
1683     char          name[12], name1[12], name2[12];
1684     int           i;
1685
1686     strncpy(name1, pszFieldName,11);
1687     name1[11] = '\0';
1688     str_to_upper(name1);
1689
1690     for( i = 0; i < DBFGetFieldCount(psDBF); i++ )
1691     {
1692         DBFGetFieldInfo( psDBF, i, name, NULL, NULL );
1693         strncpy(name2,name,11);
1694         str_to_upper(name2);
1695
1696         if(!strncmp(name1,name2,10))
1697             return(i);
1698     }
1699     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 int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape )
1710
1711 {
1712 /* -------------------------------------------------------------------- */
1713 /*      Verify selection.                                               */
1714 /* -------------------------------------------------------------------- */
1715     if( iShape < 0 || iShape >= psDBF->nRecords )
1716         return TRUE;
1717
1718 /* -------------------------------------------------------------------- */
1719 /*      Have we read the record?                                        */
1720 /* -------------------------------------------------------------------- */
1721     if( !DBFLoadRecord( psDBF, iShape ) )
1722         return FALSE;
1723
1724 /* -------------------------------------------------------------------- */
1725 /*      '*' means deleted.                                              */
1726 /* -------------------------------------------------------------------- */
1727     return psDBF->pszCurrentRecord[0] == '*';
1728 }
1729
1730 /************************************************************************/
1731 /*                        DBFMarkRecordDeleted()                        */
1732 /************************************************************************/
1733
1734 int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, 
1735                                       int bIsDeleted )
1736
1737 {
1738     char chNewFlag;
1739
1740 /* -------------------------------------------------------------------- */
1741 /*      Verify selection.                                               */
1742 /* -------------------------------------------------------------------- */
1743     if( iShape < 0 || iShape >= psDBF->nRecords )
1744         return FALSE;
1745
1746 /* -------------------------------------------------------------------- */
1747 /*      Is this an existing record, but different than the last one     */
1748 /*      we accessed?                                                    */
1749 /* -------------------------------------------------------------------- */
1750     if( !DBFLoadRecord( psDBF, iShape ) )
1751         return FALSE;
1752
1753 /* -------------------------------------------------------------------- */
1754 /*      Assign value, marking record as dirty if it changes.            */
1755 /* -------------------------------------------------------------------- */
1756     if( bIsDeleted )
1757         chNewFlag = '*';
1758     else 
1759         chNewFlag = ' ';
1760
1761     if( psDBF->pszCurrentRecord[0] != chNewFlag )
1762     {
1763         psDBF->bCurrentRecordModified = TRUE;
1764         psDBF->bUpdated = TRUE;
1765         psDBF->pszCurrentRecord[0] = chNewFlag;
1766     }
1767
1768     return TRUE;
1769 }
1770
1771 /************************************************************************/
1772 /*                            DBFGetCodePage                            */
1773 /************************************************************************/
1774
1775 const char SHPAPI_CALL1(*)
1776 DBFGetCodePage(DBFHandle psDBF )
1777 {
1778     if( psDBF == NULL )
1779         return NULL;
1780     return psDBF->pszCodePage;
1781 }
1782
1783 /************************************************************************/
1784 /*                          DBFDeleteField()                            */
1785 /*                                                                      */
1786 /*      Remove a field from a .dbf file                                 */
1787 /************************************************************************/
1788
1789 int SHPAPI_CALL
1790 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     if (iField < 0 || iField >= psDBF->nFields)
1799         return FALSE;
1800
1801     /* make sure that everything is written in .dbf */
1802     if( !DBFFlushRecord( psDBF ) )
1803         return FALSE;
1804
1805     /* get information about field to be deleted */
1806     nOldRecordLength = psDBF->nRecordLength;
1807     nOldHeaderLength = psDBF->nHeaderLength;
1808     nDeletedFieldOffset = psDBF->panFieldOffset[iField];
1809     nDeletedFieldSize = psDBF->panFieldSize[iField];
1810
1811     /* update fields info */
1812     for (i = iField + 1; i < psDBF->nFields; i++)
1813     {
1814         psDBF->panFieldOffset[i-1] = psDBF->panFieldOffset[i] - nDeletedFieldSize;
1815         psDBF->panFieldSize[i-1] = psDBF->panFieldSize[i];
1816         psDBF->panFieldDecimals[i-1] = psDBF->panFieldDecimals[i];
1817         psDBF->pachFieldType[i-1] = psDBF->pachFieldType[i];
1818     }
1819
1820     /* resize fields arrays */
1821     psDBF->nFields--;
1822
1823     psDBF->panFieldOffset = (int *) 
1824         SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
1825
1826     psDBF->panFieldSize = (int *) 
1827         SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
1828
1829     psDBF->panFieldDecimals = (int *) 
1830         SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
1831
1832     psDBF->pachFieldType = (char *) 
1833         SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
1834
1835     /* update header information */
1836     psDBF->nHeaderLength -= 32;
1837     psDBF->nRecordLength -= nDeletedFieldSize;
1838
1839     /* overwrite field information in header */
1840     memmove(psDBF->pszHeader + iField*32,
1841            psDBF->pszHeader + (iField+1)*32,
1842            sizeof(char) * (psDBF->nFields - iField)*32);
1843
1844     psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
1845
1846     /* update size of current record appropriately */
1847     psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
1848                                                  psDBF->nRecordLength);
1849
1850     /* we're done if we're dealing with not yet created .dbf */
1851     if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
1852         return TRUE;
1853
1854     /* force update of header with new header and record length */
1855     psDBF->bNoHeader = TRUE;
1856     DBFUpdateHeader( psDBF );
1857
1858     /* alloc record */
1859     pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
1860
1861     /* shift records to their new positions */
1862     for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
1863     {
1864         nRecordOffset = 
1865             nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength;
1866
1867         /* load record */
1868         psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1869         psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
1870
1871         nRecordOffset = 
1872             psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
1873
1874         /* move record in two steps */
1875         psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1876         psDBF->sHooks.FWrite( pszRecord, nDeletedFieldOffset, 1, psDBF->fp );
1877         psDBF->sHooks.FWrite( pszRecord + nDeletedFieldOffset + nDeletedFieldSize,
1878                               nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize,
1879                               1, psDBF->fp );
1880
1881     }
1882
1883     /* TODO: truncate file */
1884
1885     /* free record */
1886     free(pszRecord);
1887
1888     psDBF->nCurrentRecord = -1;
1889     psDBF->bCurrentRecordModified = FALSE;
1890
1891     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 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     if ( psDBF->nFields == 0 )
1918         return TRUE;
1919
1920     /* make sure that everything is written in .dbf */
1921     if( !DBFFlushRecord( psDBF ) )
1922         return FALSE;
1923
1924     panFieldOffsetNew = (int *) malloc(sizeof(int) * psDBF->nFields);
1925     panFieldSizeNew = (int *) malloc(sizeof(int) *  psDBF->nFields);
1926     panFieldDecimalsNew = (int *) malloc(sizeof(int) *  psDBF->nFields);
1927     pachFieldTypeNew = (char *) malloc(sizeof(char) *  psDBF->nFields);
1928     pszHeaderNew = (char*) malloc(sizeof(char) * 32 *  psDBF->nFields);
1929
1930     /* shuffle fields definitions */
1931     for(i=0; i < psDBF->nFields; i++)
1932     {
1933         panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]];
1934         panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]];
1935         pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]];
1936         memcpy(pszHeaderNew + i * 32,
1937                psDBF->pszHeader + panMap[i] * 32, 32);
1938     }
1939     panFieldOffsetNew[0] = 1;
1940     for(i=1; i < psDBF->nFields; i++)
1941     {
1942         panFieldOffsetNew[i] = panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
1943     }
1944
1945     free(psDBF->pszHeader);
1946     psDBF->pszHeader = pszHeaderNew;
1947
1948     /* we're done if we're dealing with not yet created .dbf */
1949     if ( !(psDBF->bNoHeader && psDBF->nRecords == 0) )
1950     {
1951         /* force update of header with new header and record length */
1952         psDBF->bNoHeader = TRUE;
1953         DBFUpdateHeader( psDBF );
1954
1955         /* alloc record */
1956         pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
1957         pszRecordNew = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
1958
1959         /* shuffle fields in records */
1960         for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
1961         {
1962             nRecordOffset =
1963                 psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
1964
1965             /* load record */
1966             psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1967             psDBF->sHooks.FRead( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
1968
1969             pszRecordNew[0] = pszRecord[0];
1970
1971             for(i=0; i < psDBF->nFields; i++)
1972             {
1973                 memcpy(pszRecordNew + panFieldOffsetNew[i],
1974                        pszRecord + psDBF->panFieldOffset[panMap[i]],
1975                        psDBF->panFieldSize[panMap[i]]);
1976             }
1977
1978             /* write record */
1979             psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
1980             psDBF->sHooks.FWrite( pszRecordNew, psDBF->nRecordLength, 1, psDBF->fp );
1981         }
1982
1983         /* free record */
1984         free(pszRecord);
1985         free(pszRecordNew);
1986     }
1987
1988     free(psDBF->panFieldOffset);
1989     free(psDBF->panFieldSize);
1990     free(psDBF->panFieldDecimals);
1991     free(psDBF->pachFieldType);
1992
1993     psDBF->panFieldOffset = panFieldOffsetNew;
1994     psDBF->panFieldSize = panFieldSizeNew;
1995     psDBF->panFieldDecimals =panFieldDecimalsNew;
1996     psDBF->pachFieldType = pachFieldTypeNew;
1997
1998     psDBF->nCurrentRecord = -1;
1999     psDBF->bCurrentRecordModified = FALSE;
2000
2001     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 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     if (iField < 0 || iField >= psDBF->nFields)
2027         return FALSE;
2028
2029     /* make sure that everything is written in .dbf */
2030     if( !DBFFlushRecord( psDBF ) )
2031         return FALSE;
2032
2033     chFieldFill = DBFGetNullCharacter(chType);
2034
2035     chOldType = psDBF->pachFieldType[iField];
2036     nOffset = psDBF->panFieldOffset[iField];
2037     nOldWidth = psDBF->panFieldSize[iField];
2038     nOldRecordLength = psDBF->nRecordLength;
2039
2040 /* -------------------------------------------------------------------- */
2041 /*      Do some checking to ensure we can add records to this file.     */
2042 /* -------------------------------------------------------------------- */
2043     if( nWidth < 1 )
2044         return -1;
2045
2046     if( nWidth > 255 )
2047         nWidth = 255;
2048
2049 /* -------------------------------------------------------------------- */
2050 /*      Assign the new field information fields.                        */
2051 /* -------------------------------------------------------------------- */
2052     psDBF->panFieldSize[iField] = nWidth;
2053     psDBF->panFieldDecimals[iField] = nDecimals;
2054     psDBF->pachFieldType[iField] = chType;
2055
2056 /* -------------------------------------------------------------------- */
2057 /*      Update the header information.                                  */
2058 /* -------------------------------------------------------------------- */
2059     pszFInfo = psDBF->pszHeader + 32 * iField;
2060
2061     for( i = 0; i < 32; i++ )
2062         pszFInfo[i] = '\0';
2063
2064     if( (int) strlen(pszFieldName) < 10 )
2065         strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
2066     else
2067         strncpy( pszFInfo, pszFieldName, 10);
2068
2069     pszFInfo[11] = psDBF->pachFieldType[iField];
2070
2071     if( chType == 'C' )
2072     {
2073         pszFInfo[16] = (unsigned char) (nWidth % 256);
2074         pszFInfo[17] = (unsigned char) (nWidth / 256);
2075     }
2076     else
2077     {
2078         pszFInfo[16] = (unsigned char) nWidth;
2079         pszFInfo[17] = (unsigned char) nDecimals;
2080     }
2081
2082 /* -------------------------------------------------------------------- */
2083 /*      Update offsets                                                  */
2084 /* -------------------------------------------------------------------- */
2085     if (nWidth != nOldWidth)
2086     {
2087         for (i = iField + 1; i < psDBF->nFields; i++)
2088              psDBF->panFieldOffset[i] += nWidth - nOldWidth;
2089         psDBF->nRecordLength += nWidth - nOldWidth;
2090
2091         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     if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
2097         return TRUE;
2098
2099     /* force update of header with new header and record length */
2100     psDBF->bNoHeader = TRUE;
2101     DBFUpdateHeader( psDBF );
2102
2103     if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType))
2104     {
2105         char* pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
2106         char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
2107
2108         pszOldField[nOldWidth] = 0;
2109
2110         /* move records to their new positions */
2111         for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
2112         {
2113             nRecordOffset =
2114                 nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2115
2116             /* load record */
2117             psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2118             psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
2119
2120             memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2121             bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
2122
2123             if (nWidth != nOldWidth)
2124             {
2125                 if ((chOldType == 'N' || chOldType == 'F') && pszOldField[0] == ' ')
2126                 {
2127                     /* Strip leading spaces when truncating a numeric field */
2128                     memmove( pszRecord + nOffset,
2129                             pszRecord + nOffset + nOldWidth - nWidth,
2130                             nWidth );
2131                 }
2132                 if (nOffset + nOldWidth < nOldRecordLength)
2133                 {
2134                     memmove( pszRecord + nOffset + nWidth,
2135                             pszRecord + nOffset + nOldWidth,
2136                             nOldRecordLength - (nOffset + nOldWidth));
2137                 }
2138             }
2139
2140             /* Convert null value to the appropriate value of the new type */
2141             if (bIsNULL)
2142             {
2143                 memset( pszRecord + nOffset, chFieldFill, nWidth);
2144             }
2145
2146             nRecordOffset =
2147                 psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2148
2149             /* write record */
2150             psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2151             psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
2152         }
2153
2154         free(pszRecord);
2155         free(pszOldField);
2156     }
2157     else if (nWidth > nOldWidth)
2158     {
2159         char* pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
2160         char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
2161
2162         pszOldField[nOldWidth] = 0;
2163
2164         /* move records to their new positions */
2165         for (iRecord = psDBF->nRecords - 1; iRecord >= 0; iRecord--)
2166         {
2167             nRecordOffset =
2168                 nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2169
2170             /* load record */
2171             psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2172             psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
2173
2174             memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2175             bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
2176
2177             if (nOffset + nOldWidth < nOldRecordLength)
2178             {
2179                 memmove( pszRecord + nOffset + nWidth,
2180                          pszRecord + nOffset + nOldWidth,
2181                          nOldRecordLength - (nOffset + nOldWidth));
2182             }
2183
2184             /* Convert null value to the appropriate value of the new type */
2185             if (bIsNULL)
2186             {
2187                 memset( pszRecord + nOffset, chFieldFill, nWidth);
2188             }
2189             else
2190             {
2191                 if ((chOldType == 'N' || chOldType == 'F'))
2192                 {
2193                     /* Add leading spaces when expanding a numeric field */
2194                     memmove( pszRecord + nOffset + nWidth - nOldWidth,
2195                              pszRecord + nOffset, nOldWidth );
2196                     memset( pszRecord + nOffset, ' ', nWidth - nOldWidth );
2197                 }
2198                 else
2199                 {
2200                     /* Add trailing spaces */
2201                     memset(pszRecord + nOffset + nOldWidth, ' ', nWidth - nOldWidth);
2202                 }
2203             }
2204
2205             nRecordOffset =
2206                 psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
2207
2208             /* write record */
2209             psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
2210             psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
2211         }
2212
2213         free(pszRecord);
2214         free(pszOldField);
2215     }
2216
2217     psDBF->nCurrentRecord = -1;
2218     psDBF->bCurrentRecordModified = FALSE;
2219
2220     return TRUE;
2221 }