~librecad-dev/librecad/librecad

« back to all changes in this revision

Viewing changes to plugins/importshp/shapelib/dbfopen.c

  • Committer: Scott Howard
  • Date: 2014-02-21 19:07:55 UTC
  • Revision ID: showard@debian.org-20140221190755-csjax9wb146hgdq4
first commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}