Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MapServer/src/mapxbase.c
Line
Count
Source
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  .dbf access API.  Derived from shapelib, and relicensed with
6
 *           permission of Frank Warmerdam (shapelib author).
7
 * Author:   Steve Lime and the MapServer team.
8
 *
9
 ******************************************************************************
10
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a
13
 * copy of this software and associated documentation files (the "Software"),
14
 * to deal in the Software without restriction, including without limitation
15
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16
 * and/or sell copies of the Software, and to permit persons to whom the
17
 * Software is furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in
20
 * all copies of this Software or works derived from this Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
 * DEALINGS IN THE SOFTWARE.
29
 ****************************************************************************/
30
31
#include "mapserver.h"
32
#include <stdlib.h> /* for atof() and atoi() */
33
#include <math.h>
34
35
#include "cpl_vsi.h"
36
37
0
static inline void IGUR_sizet(size_t ignored) {
38
0
  (void)ignored;
39
0
} /* Ignore GCC Unused Result */
40
41
/************************************************************************/
42
/*                             SfRealloc()                              */
43
/*                                                                      */
44
/*      A realloc cover function that will access a NULL pointer as     */
45
/*      a valid input.                                                  */
46
/************************************************************************/
47
static void *SfRealloc(void *pMem, int nNewSize)
48
49
1.26k
{
50
1.26k
  return ((void *)realloc(pMem, nNewSize));
51
1.26k
}
52
53
/************************************************************************/
54
/*                           writeHeader()                              */
55
/*                                                                      */
56
/*      This is called to write out the file header, and field          */
57
/*      descriptions before writing any actual data records.  This      */
58
/*      also computes all the DBFDataSet field offset/size/decimals     */
59
/*      and so forth values.                                            */
60
/************************************************************************/
61
static void writeHeader(DBFHandle psDBF)
62
63
0
{
64
0
  uchar abyHeader[32];
65
0
  int i;
66
67
0
  if (!psDBF->bNoHeader)
68
0
    return;
69
70
0
  psDBF->bNoHeader = MS_FALSE;
71
72
  /* -------------------------------------------------------------------- */
73
  /*  Initialize the file header information.               */
74
  /* -------------------------------------------------------------------- */
75
0
  for (i = 0; i < 32; i++)
76
0
    abyHeader[i] = 0;
77
78
0
  abyHeader[0] = 0x03; /* memo field? - just copying   */
79
80
  /* date updated on close, record count preset at zero */
81
82
0
  abyHeader[8] = psDBF->nHeaderLength % 256;
83
0
  abyHeader[9] = psDBF->nHeaderLength / 256;
84
85
0
  abyHeader[10] = psDBF->nRecordLength % 256;
86
0
  abyHeader[11] = psDBF->nRecordLength / 256;
87
88
  /* -------------------------------------------------------------------- */
89
  /*      Write the initial 32 byte file header, and all the field        */
90
  /*      descriptions.                                             */
91
  /* -------------------------------------------------------------------- */
92
0
  VSIFSeekL(psDBF->fp, 0, 0);
93
0
  VSIFWriteL(abyHeader, 32, 1, psDBF->fp);
94
0
  VSIFWriteL(psDBF->pszHeader, 32, psDBF->nFields, psDBF->fp);
95
96
  /* -------------------------------------------------------------------- */
97
  /*      Write out the newline character if there is room for it.        */
98
  /* -------------------------------------------------------------------- */
99
0
  if (psDBF->nHeaderLength > 32 * psDBF->nFields + 32) {
100
0
    char cNewline;
101
102
0
    cNewline = 0x0d;
103
0
    VSIFWriteL(&cNewline, 1, 1, psDBF->fp);
104
0
  }
105
0
}
106
107
/************************************************************************/
108
/*                           flushRecord()                              */
109
/*                                                                      */
110
/*      Write out the current record if there is one.                   */
111
/************************************************************************/
112
static void flushRecord(DBFHandle psDBF)
113
114
1.22k
{
115
1.22k
  unsigned int nRecordOffset;
116
117
1.22k
  if (psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1) {
118
0
    psDBF->bCurrentRecordModified = MS_FALSE;
119
120
0
    nRecordOffset =
121
0
        psDBF->nRecordLength * psDBF->nCurrentRecord + psDBF->nHeaderLength;
122
123
0
    VSIFSeekL(psDBF->fp, nRecordOffset, 0);
124
0
    VSIFWriteL(psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp);
125
0
  }
126
1.22k
}
127
128
1.31k
DBFHandle msDBFOpenVirtualFile(VSILFILE *fp) {
129
1.31k
  DBFHandle psDBF;
130
1.31k
  uchar *pabyBuf;
131
1.31k
  int nFields, nHeadLen, nRecLen, iField;
132
133
  /* -------------------------------------------------------------------- */
134
  /*      Open the file.                                                  */
135
  /* -------------------------------------------------------------------- */
136
1.31k
  psDBF = (DBFHandle)calloc(1, sizeof(DBFInfo));
137
1.31k
  MS_CHECK_ALLOC(psDBF, sizeof(DBFInfo), NULL);
138
1.31k
  psDBF->fp = fp;
139
140
1.31k
  psDBF->bNoHeader = MS_FALSE;
141
1.31k
  psDBF->nCurrentRecord = -1;
142
1.31k
  psDBF->bCurrentRecordModified = MS_FALSE;
143
144
1.31k
  psDBF->pszStringField = NULL;
145
1.31k
  psDBF->nStringFieldLen = 0;
146
147
1.31k
  psDBF->pszEncoding = NULL;
148
149
  /* -------------------------------------------------------------------- */
150
  /*  Read Table Header info                                              */
151
  /* -------------------------------------------------------------------- */
152
1.31k
  pabyBuf = (uchar *)msSmallMalloc(500);
153
1.31k
  if (VSIFReadL(pabyBuf, 32, 1, psDBF->fp) != 1) {
154
42
    VSIFCloseL(psDBF->fp);
155
42
    msFree(psDBF);
156
42
    msFree(pabyBuf);
157
42
    return (NULL);
158
42
  }
159
160
1.27k
  if (pabyBuf[7] < 128)
161
1.08k
    psDBF->nRecords = pabyBuf[4] + pabyBuf[5] * 256 + pabyBuf[6] * 256 * 256 +
162
1.08k
                      pabyBuf[7] * 256 * 256 * 256;
163
187
  else
164
187
    psDBF->nRecords = 0;
165
166
1.27k
  psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9] * 256;
167
1.27k
  psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11] * 256;
168
169
1.27k
  if (nHeadLen <= 32) {
170
8
    VSIFCloseL(psDBF->fp);
171
8
    msFree(psDBF);
172
8
    msFree(pabyBuf);
173
8
    return (NULL);
174
8
  }
175
176
1.26k
  psDBF->nFields = nFields = (nHeadLen - 32) / 32;
177
178
1.26k
  psDBF->pszCurrentRecord = (char *)msSmallMalloc(nRecLen);
179
180
  /* -------------------------------------------------------------------- */
181
  /*  Read in Field Definitions                                           */
182
  /* -------------------------------------------------------------------- */
183
1.26k
  pabyBuf = (uchar *)SfRealloc(pabyBuf, nHeadLen);
184
1.26k
  psDBF->pszHeader = (char *)pabyBuf;
185
186
1.26k
  VSIFSeekL(psDBF->fp, 32, 0);
187
1.26k
  if (VSIFReadL(pabyBuf, nHeadLen - 32, 1, psDBF->fp) != 1) {
188
41
    msFree(psDBF->pszCurrentRecord);
189
41
    VSIFCloseL(psDBF->fp);
190
41
    msFree(psDBF);
191
41
    msFree(pabyBuf);
192
41
    return (NULL);
193
41
  }
194
195
1.22k
  psDBF->panFieldOffset = (int *)msSmallMalloc(sizeof(int) * nFields);
196
1.22k
  psDBF->panFieldSize = (int *)msSmallMalloc(sizeof(int) * nFields);
197
1.22k
  psDBF->panFieldDecimals = (int *)msSmallMalloc(sizeof(int) * nFields);
198
1.22k
  psDBF->pachFieldType = (char *)msSmallMalloc(sizeof(char) * nFields);
199
200
13.0k
  for (iField = 0; iField < nFields; iField++) {
201
11.8k
    uchar *pabyFInfo;
202
203
11.8k
    pabyFInfo = pabyBuf + iField * 32;
204
205
11.8k
    if (pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F') {
206
575
      psDBF->panFieldSize[iField] = pabyFInfo[16];
207
575
      psDBF->panFieldDecimals[iField] = pabyFInfo[17];
208
11.2k
    } else {
209
11.2k
      psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17] * 256;
210
11.2k
      psDBF->panFieldDecimals[iField] = 0;
211
11.2k
    }
212
213
11.8k
    psDBF->pachFieldType[iField] = (char)pabyFInfo[11];
214
11.8k
    if (iField == 0)
215
92
      psDBF->panFieldOffset[iField] = 1;
216
11.7k
    else
217
11.7k
      psDBF->panFieldOffset[iField] =
218
11.7k
          psDBF->panFieldOffset[iField - 1] + psDBF->panFieldSize[iField - 1];
219
11.8k
  }
220
221
  /* The field offsets and sizes come straight from the header and are trusted
222
     by msDBFReadAttribute() when it copies panFieldSize bytes out of the
223
     nRecLen-byte record buffer. Reject a header whose fields do not fit within
224
     the record length, otherwise that copy reads past pszCurrentRecord. */
225
1.22k
  if (nFields > 0 &&
226
92
      psDBF->panFieldOffset[nFields - 1] + psDBF->panFieldSize[nFields - 1] >
227
92
          nRecLen) {
228
65
    msDBFClose(psDBF);
229
65
    return (NULL);
230
65
  }
231
232
1.15k
  return (psDBF);
233
1.22k
}
234
235
/**
236
 * Attempt to read character encoding from a .CPG-file
237
 */
238
0
char *msReadCPGEncoding(char *cpgFilename) {
239
0
  VSILFILE *fpCPG = VSIFOpenL(cpgFilename, "rb");
240
0
  if (fpCPG == NULL)
241
0
    return NULL;
242
243
0
  char szEncoding[100] = "";
244
0
  size_t nRead = VSIFReadL(szEncoding, 1, sizeof(szEncoding) - 1, fpCPG);
245
0
  VSIFCloseL(fpCPG);
246
247
0
  if (nRead == 0)
248
0
    return NULL;
249
250
  // Null terminate and remove any line break
251
0
  szEncoding[nRead] = '\0';
252
0
  szEncoding[strcspn(szEncoding, "\r\n")] = 0;
253
254
0
  if (szEncoding[0] == '\0') {
255
0
    return NULL;
256
0
  }
257
258
  /* See
259
   * https://github.com/OSGeo/gdal/blob/release/3.11/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp#L503
260
   */
261
0
  const int nCPG = atoi(szEncoding);
262
0
  if ((nCPG >= 437 && nCPG <= 950) || (nCPG >= 1250 && nCPG <= 1258)) {
263
0
    char szResult[20];
264
0
    snprintf(szResult, sizeof(szResult), "CP%d", nCPG);
265
0
    return msStrdup(szResult);
266
0
  }
267
268
0
  if (strncasecmp(szEncoding, "8859", 4) == 0) {
269
0
    const char *suffix = szEncoding + 4;
270
0
    if (*suffix == '-' || *suffix == '_')
271
0
      suffix++;
272
273
0
    if (!isdigit((unsigned char)*suffix))
274
0
      return msStrdup(szEncoding);
275
276
0
    char szResult[40];
277
0
    snprintf(szResult, sizeof(szResult), "ISO-8859-%d", atoi(suffix));
278
0
    return msStrdup(szResult);
279
0
  }
280
281
0
  if (strncasecmp(szEncoding, "ANSI 1251", 9) == 0) {
282
0
    return msStrdup("CP1251");
283
0
  }
284
285
0
  return msStrdup(szEncoding);
286
0
}
287
288
/************************************************************************/
289
/*                              msDBFOpen()                             */
290
/*                                                                      */
291
/*      Open a .dbf file.                                               */
292
/************************************************************************/
293
DBFHandle msDBFOpen(const char *pszFilename, const char *pszAccess)
294
295
0
{
296
  /* -------------------------------------------------------------------- */
297
  /*      We only allow the access strings "rb" and "r+".                 */
298
  /* -------------------------------------------------------------------- */
299
0
  if (strcmp(pszAccess, "r") != 0 && strcmp(pszAccess, "r+") != 0 &&
300
0
      strcmp(pszAccess, "rb") != 0 && strcmp(pszAccess, "r+b") != 0)
301
0
    return (NULL);
302
303
  /* -------------------------------------------------------------------- */
304
  /*  Ensure the extension is converted to dbf or DBF if it is      */
305
  /*  currently .shp or .shx.               */
306
  /* -------------------------------------------------------------------- */
307
0
  char *pszDBFFilename = (char *)msSmallMalloc(strlen(pszFilename) + 1);
308
0
  strcpy(pszDBFFilename, pszFilename);
309
310
0
  if (strcmp(pszFilename + strlen(pszFilename) - 4, ".shp") == 0 ||
311
0
      strcmp(pszFilename + strlen(pszFilename) - 4, ".shx") == 0) {
312
0
    strcpy(pszDBFFilename + strlen(pszDBFFilename) - 4, ".dbf");
313
0
  } else if (strcmp(pszFilename + strlen(pszFilename) - 4, ".SHP") == 0 ||
314
0
             strcmp(pszFilename + strlen(pszFilename) - 4, ".SHX") == 0) {
315
0
    strcpy(pszDBFFilename + strlen(pszDBFFilename) - 4, ".DBF");
316
0
  }
317
318
  /* -------------------------------------------------------------------- */
319
  /*      Open the file.                                                  */
320
  /* -------------------------------------------------------------------- */
321
0
  VSILFILE *fp = VSIFOpenL(pszDBFFilename, pszAccess);
322
0
  if (fp == NULL) {
323
0
    if (strcmp(pszDBFFilename + strlen(pszDBFFilename) - 4, ".dbf") == 0) {
324
0
      strcpy(pszDBFFilename + strlen(pszDBFFilename) - 4, ".DBF");
325
0
      fp = VSIFOpenL(pszDBFFilename, pszAccess);
326
0
    }
327
0
  }
328
329
0
  if (fp == NULL) {
330
0
    msFree(pszDBFFilename);
331
0
    return (NULL);
332
0
  }
333
334
0
  DBFHandle dbfHandle = msDBFOpenVirtualFile(fp);
335
0
  if (dbfHandle) {
336
0
    char *pszCPGFilename = (char *)msSmallMalloc(strlen(pszDBFFilename) + 1);
337
0
    strcpy(pszCPGFilename, pszDBFFilename);
338
339
0
    if (strcmp(pszDBFFilename + strlen(pszDBFFilename) - 4, ".dbf") == 0) {
340
0
      strcpy(pszCPGFilename + strlen(pszCPGFilename) - 4, ".cpg");
341
0
    } else {
342
0
      strcpy(pszCPGFilename + strlen(pszCPGFilename) - 4, ".CPG");
343
0
    }
344
345
0
    dbfHandle->pszEncoding = msReadCPGEncoding(pszCPGFilename);
346
347
0
    msFree(pszCPGFilename);
348
0
  }
349
350
0
  msFree(pszDBFFilename);
351
0
  return dbfHandle;
352
0
}
353
354
/************************************************************************/
355
/*                              msDBFClose()                            */
356
/************************************************************************/
357
358
1.22k
void msDBFClose(DBFHandle psDBF) {
359
  /* -------------------------------------------------------------------- */
360
  /*      Write out header if not already written.                        */
361
  /* -------------------------------------------------------------------- */
362
1.22k
  if (psDBF->bNoHeader)
363
0
    writeHeader(psDBF);
364
365
1.22k
  flushRecord(psDBF);
366
367
  /* -------------------------------------------------------------------- */
368
  /*      Update last access date, and number of records if we have       */
369
  /*  write access.                             */
370
  /* -------------------------------------------------------------------- */
371
1.22k
  if (psDBF->bUpdated) {
372
0
    uchar abyFileHeader[32];
373
374
0
    VSIFSeekL(psDBF->fp, 0, 0);
375
0
    IGUR_sizet(VSIFReadL(abyFileHeader, 32, 1, psDBF->fp));
376
377
0
    abyFileHeader[1] = 95; /* YY */
378
0
    abyFileHeader[2] = 7;  /* MM */
379
0
    abyFileHeader[3] = 26; /* DD */
380
381
0
    abyFileHeader[4] = psDBF->nRecords % 256;
382
0
    abyFileHeader[5] = (psDBF->nRecords / 256) % 256;
383
0
    abyFileHeader[6] = (psDBF->nRecords / (256 * 256)) % 256;
384
0
    abyFileHeader[7] = (psDBF->nRecords / (256 * 256 * 256)) % 256;
385
386
0
    VSIFSeekL(psDBF->fp, 0, 0);
387
0
    VSIFWriteL(abyFileHeader, 32, 1, psDBF->fp);
388
0
  }
389
390
  /* -------------------------------------------------------------------- */
391
  /*      Close, and free resources.                                      */
392
  /* -------------------------------------------------------------------- */
393
1.22k
  VSIFCloseL(psDBF->fp);
394
395
1.22k
  if (psDBF->panFieldOffset != NULL) {
396
92
    free(psDBF->panFieldOffset);
397
92
    free(psDBF->panFieldSize);
398
92
    free(psDBF->panFieldDecimals);
399
92
    free(psDBF->pachFieldType);
400
92
  }
401
402
1.22k
  free(psDBF->pszHeader);
403
1.22k
  free(psDBF->pszCurrentRecord);
404
405
1.22k
  free(psDBF->pszStringField);
406
1.22k
  free(psDBF->pszEncoding);
407
408
1.22k
  free(psDBF);
409
1.22k
}
410
411
/************************************************************************/
412
/*                             msDBFCreate()                            */
413
/*                                                                      */
414
/*      Create a new .dbf file.                                         */
415
/************************************************************************/
416
DBFHandle msDBFCreate(const char *pszFilename)
417
418
0
{
419
0
  DBFHandle psDBF;
420
0
  VSILFILE *fp;
421
422
  /* -------------------------------------------------------------------- */
423
  /*      Create the file.                                                */
424
  /* -------------------------------------------------------------------- */
425
0
  fp = VSIFOpenL(pszFilename, "wb");
426
0
  if (fp == NULL)
427
0
    return (NULL);
428
429
0
  {
430
0
    char ch = 0;
431
0
    VSIFWriteL(&ch, 1, 1, fp);
432
0
  }
433
0
  VSIFCloseL(fp);
434
435
0
  fp = VSIFOpenL(pszFilename, "rb+");
436
0
  if (fp == NULL)
437
0
    return (NULL);
438
439
  /* -------------------------------------------------------------------- */
440
  /*  Create the info structure.                */
441
  /* -------------------------------------------------------------------- */
442
0
  psDBF = (DBFHandle)malloc(sizeof(DBFInfo));
443
0
  if (psDBF == NULL) {
444
0
    msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n",
445
0
               "msDBFCreate()", __FILE__, __LINE__,
446
0
               (unsigned int)sizeof(DBFInfo));
447
0
    VSIFCloseL(fp);
448
0
    return NULL;
449
0
  }
450
451
0
  psDBF->fp = fp;
452
0
  psDBF->nRecords = 0;
453
0
  psDBF->nFields = 0;
454
0
  psDBF->nRecordLength = 1;
455
0
  psDBF->nHeaderLength = 33;
456
457
0
  psDBF->panFieldOffset = NULL;
458
0
  psDBF->panFieldSize = NULL;
459
0
  psDBF->panFieldDecimals = NULL;
460
0
  psDBF->pachFieldType = NULL;
461
0
  psDBF->pszHeader = NULL;
462
463
0
  psDBF->nCurrentRecord = -1;
464
0
  psDBF->bCurrentRecordModified = MS_FALSE;
465
0
  psDBF->pszCurrentRecord = NULL;
466
467
0
  psDBF->pszStringField = NULL;
468
0
  psDBF->nStringFieldLen = 0;
469
470
0
  psDBF->bNoHeader = MS_TRUE;
471
0
  psDBF->bUpdated = MS_FALSE;
472
473
0
  return (psDBF);
474
0
}
475
476
/************************************************************************/
477
/*                            msDBFAddField()                           */
478
/*                                                                      */
479
/*      Add a field to a newly created .dbf file before any records     */
480
/*      are written.                                                    */
481
/************************************************************************/
482
int msDBFAddField(DBFHandle psDBF, const char *pszFieldName, DBFFieldType eType,
483
0
                  int nWidth, int nDecimals) {
484
0
  char *pszFInfo;
485
0
  int i;
486
487
  /* -------------------------------------------------------------------- */
488
  /*      Do some checking to ensure we can add records to this file.     */
489
  /* -------------------------------------------------------------------- */
490
0
  if (psDBF->nRecords > 0)
491
0
    return (MS_FALSE);
492
493
0
  if (!psDBF->bNoHeader)
494
0
    return (MS_FALSE);
495
496
0
  if (eType != FTDouble && nDecimals != 0)
497
0
    return (MS_FALSE);
498
499
  /* -------------------------------------------------------------------- */
500
  /*      SfRealloc all the arrays larger to hold the additional field    */
501
  /*      information.                                                    */
502
  /* -------------------------------------------------------------------- */
503
0
  psDBF->nFields++;
504
505
0
  psDBF->panFieldOffset =
506
0
      (int *)SfRealloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields);
507
508
0
  psDBF->panFieldSize =
509
0
      (int *)SfRealloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields);
510
511
0
  psDBF->panFieldDecimals =
512
0
      (int *)SfRealloc(psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields);
513
514
0
  psDBF->pachFieldType =
515
0
      (char *)SfRealloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields);
516
517
  /* -------------------------------------------------------------------- */
518
  /*      Assign the new field information fields.                        */
519
  /* -------------------------------------------------------------------- */
520
0
  psDBF->panFieldOffset[psDBF->nFields - 1] = psDBF->nRecordLength;
521
0
  psDBF->nRecordLength += nWidth;
522
0
  psDBF->panFieldSize[psDBF->nFields - 1] = nWidth;
523
0
  psDBF->panFieldDecimals[psDBF->nFields - 1] = nDecimals;
524
525
0
  if (eType == FTString)
526
0
    psDBF->pachFieldType[psDBF->nFields - 1] = 'C';
527
0
  else
528
0
    psDBF->pachFieldType[psDBF->nFields - 1] = 'N';
529
530
  /* -------------------------------------------------------------------- */
531
  /*      Extend the required header information.                         */
532
  /* -------------------------------------------------------------------- */
533
0
  psDBF->nHeaderLength += 32;
534
0
  psDBF->bUpdated = MS_FALSE;
535
536
0
  psDBF->pszHeader = (char *)SfRealloc(psDBF->pszHeader, psDBF->nFields * 32);
537
538
0
  pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields - 1);
539
540
0
  for (i = 0; i < 32; i++)
541
0
    pszFInfo[i] = '\0';
542
543
0
  strncpy(pszFInfo, pszFieldName, 10);
544
545
0
  pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields - 1];
546
547
0
  if (eType == FTString) {
548
0
    pszFInfo[16] = nWidth % 256;
549
0
    pszFInfo[17] = nWidth / 256;
550
0
  } else {
551
0
    pszFInfo[16] = nWidth;
552
0
    pszFInfo[17] = nDecimals;
553
0
  }
554
555
  /* -------------------------------------------------------------------- */
556
  /*      Make the current record buffer appropriately larger.            */
557
  /* -------------------------------------------------------------------- */
558
0
  psDBF->pszCurrentRecord =
559
0
      (char *)SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength);
560
561
0
  return (psDBF->nFields - 1);
562
0
}
563
564
/************************************************************************/
565
/*                         DBFIsValueNULL()                             */
566
/*                                                                      */
567
/*      Return TRUE if value is NULL (in DBF terms).                    */
568
/*                                                                      */
569
/*      Based on DBFIsAttributeNULL of shapelib                         */
570
/************************************************************************/
571
572
static int DBFIsValueNULL(const char *pszValue, char type)
573
574
0
{
575
0
  switch (type) {
576
0
  case 'N':
577
0
  case 'F':
578
    /* NULL numeric fields have value "****************" */
579
0
    return pszValue[0] == '*';
580
581
0
  case 'D':
582
    /* NULL date fields have value "00000000" */
583
0
    return strncmp(pszValue, "00000000", 8) == 0;
584
585
0
  case 'L':
586
    /* NULL boolean fields have value "?" */
587
0
    return pszValue[0] == '?';
588
589
0
  default:
590
    /* empty string fields are considered NULL */
591
0
    return strlen(pszValue) == 0;
592
0
  }
593
0
}
594
595
/************************************************************************/
596
/*                          msDBFReadAttribute()                        */
597
/*                                                                      */
598
/*      Read one of the attribute fields of a record.                   */
599
/************************************************************************/
600
static const char *msDBFReadAttribute(DBFHandle psDBF, int hEntity, int iField)
601
602
0
{
603
0
  int i;
604
0
  unsigned int nRecordOffset;
605
0
  const uchar *pabyRec;
606
0
  const char *pReturnField = NULL;
607
608
  /* -------------------------------------------------------------------- */
609
  /*  Is the request valid?                             */
610
  /* -------------------------------------------------------------------- */
611
0
  if (iField < 0 || iField >= psDBF->nFields) {
612
0
    msSetError(MS_DBFERR, "Invalid field index %d.", "msDBFReadAttribute()",
613
0
               iField);
614
0
    return (NULL);
615
0
  }
616
617
0
  if (hEntity < 0 || hEntity >= psDBF->nRecords) {
618
0
    msSetError(MS_DBFERR, "Invalid record number %d.", "msDBFReadAttribute()",
619
0
               hEntity);
620
0
    return (NULL);
621
0
  }
622
623
  /* -------------------------------------------------------------------- */
624
  /*  Have we read the record?              */
625
  /* -------------------------------------------------------------------- */
626
0
  if (psDBF->nCurrentRecord != hEntity) {
627
0
    flushRecord(psDBF);
628
629
0
    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
630
631
0
    VSIFSeekL(psDBF->fp, nRecordOffset, 0);
632
0
    if (VSIFReadL(psDBF->pszCurrentRecord, psDBF->nRecordLength, 1,
633
0
                  psDBF->fp) != 1) {
634
0
      msSetError(MS_DBFERR, "Cannot read record %d.", "msDBFReadAttribute()",
635
0
                 hEntity);
636
0
      return (NULL);
637
0
    }
638
639
0
    psDBF->nCurrentRecord = hEntity;
640
0
  }
641
642
0
  pabyRec = (const uchar *)psDBF->pszCurrentRecord;
643
  /* DEBUG */
644
  /* printf("CurrentRecord(%c):%s\n", psDBF->pachFieldType[iField], pabyRec); */
645
646
  /* -------------------------------------------------------------------- */
647
  /*  Ensure our field buffer is large enough to hold this buffer.      */
648
  /* -------------------------------------------------------------------- */
649
0
  if (psDBF->panFieldSize[iField] + 1 > psDBF->nStringFieldLen) {
650
0
    psDBF->nStringFieldLen = psDBF->panFieldSize[iField] * 2 + 10;
651
0
    psDBF->pszStringField =
652
0
        (char *)SfRealloc(psDBF->pszStringField, psDBF->nStringFieldLen);
653
0
  }
654
655
  /* -------------------------------------------------------------------- */
656
  /*  Extract the requested field.              */
657
  /* -------------------------------------------------------------------- */
658
0
  strncpy(psDBF->pszStringField,
659
0
          (const char *)pabyRec + psDBF->panFieldOffset[iField],
660
0
          psDBF->panFieldSize[iField]);
661
0
  psDBF->pszStringField[psDBF->panFieldSize[iField]] = '\0';
662
663
  /*
664
  ** Trim trailing blanks (SDL Modification)
665
  */
666
0
  for (i = strlen(psDBF->pszStringField) - 1; i >= 0; i--) {
667
0
    if (psDBF->pszStringField[i] != ' ') {
668
0
      psDBF->pszStringField[i + 1] = '\0';
669
0
      break;
670
0
    }
671
0
  }
672
673
0
  if (i == -1)
674
0
    psDBF->pszStringField[0] = '\0'; /* whole string is blank (SDL fix)       */
675
676
  /*
677
  ** Trim/skip leading blanks (SDL/DM Modification - only on numeric types)
678
  */
679
0
  if (psDBF->pachFieldType[iField] == 'N' ||
680
0
      psDBF->pachFieldType[iField] == 'F' ||
681
0
      psDBF->pachFieldType[iField] == 'D') {
682
0
    for (i = 0; psDBF->pszStringField[i] != '\0'; i++) {
683
0
      if (psDBF->pszStringField[i] != ' ')
684
0
        break;
685
0
    }
686
0
    pReturnField = psDBF->pszStringField + i;
687
0
  } else
688
0
    pReturnField = psDBF->pszStringField;
689
690
  /*  detect null values */
691
0
  if (DBFIsValueNULL(pReturnField, psDBF->pachFieldType[iField])) {
692
0
    if (psDBF->pachFieldType[iField] == 'N' ||
693
0
        psDBF->pachFieldType[iField] == 'F' ||
694
0
        psDBF->pachFieldType[iField] == 'D')
695
0
      pReturnField = "0";
696
0
  }
697
0
  return (pReturnField);
698
0
}
699
700
/************************************************************************/
701
/*                        msDBFReadIntAttribute()                       */
702
/*                                                                      */
703
/*      Read an integer attribute.                                      */
704
/************************************************************************/
705
int msDBFReadIntegerAttribute(DBFHandle psDBF, int iRecord, int iField)
706
707
0
{
708
0
  return (atoi(msDBFReadAttribute(psDBF, iRecord, iField)));
709
0
}
710
711
/************************************************************************/
712
/*                        msDBFReadDoubleAttribute()                    */
713
/*                                                                      */
714
/*      Read a double attribute.                                        */
715
/************************************************************************/
716
0
double msDBFReadDoubleAttribute(DBFHandle psDBF, int iRecord, int iField) {
717
0
  return (atof(msDBFReadAttribute(psDBF, iRecord, iField)));
718
0
}
719
720
/************************************************************************/
721
/*                        msDBFReadStringAttribute()                      */
722
/*                                                                      */
723
/*      Read a string attribute.                                        */
724
/************************************************************************/
725
0
const char *msDBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField) {
726
0
  return (msDBFReadAttribute(psDBF, iRecord, iField));
727
0
}
728
729
/************************************************************************/
730
/*                          msDBFGetFieldCount()                        */
731
/*                                                                      */
732
/*      Return the number of fields in this table.                      */
733
/************************************************************************/
734
0
int msDBFGetFieldCount(DBFHandle psDBF) { return (psDBF->nFields); }
735
736
/************************************************************************/
737
/*                         msDBFGetRecordCount()                        */
738
/*                                                                      */
739
/*      Return the number of records in this table.                     */
740
/************************************************************************/
741
0
int msDBFGetRecordCount(DBFHandle psDBF) { return (psDBF->nRecords); }
742
743
/************************************************************************/
744
/*                          msDBFGetFieldInfo()                         */
745
/*                                                                      */
746
/*      Return any requested information about the field.               */
747
/************************************************************************/
748
DBFFieldType msDBFGetFieldInfo(DBFHandle psDBF, int iField, char *pszFieldName,
749
0
                               int *pnWidth, int *pnDecimals) {
750
0
  if (iField < 0 || iField >= psDBF->nFields)
751
0
    return (FTInvalid);
752
753
0
  if (pnWidth != NULL)
754
0
    *pnWidth = psDBF->panFieldSize[iField];
755
756
0
  if (pnDecimals != NULL)
757
0
    *pnDecimals = psDBF->panFieldDecimals[iField];
758
759
0
  if (pszFieldName != NULL) {
760
0
    int i;
761
762
0
    strncpy(pszFieldName, (char *)psDBF->pszHeader + iField * 32, 11);
763
0
    pszFieldName[11] = '\0';
764
0
    for (i = 10; i > 0 && pszFieldName[i] == ' '; i--)
765
0
      pszFieldName[i] = '\0';
766
0
  }
767
768
0
  if (psDBF->pachFieldType[iField] == 'N' ||
769
0
      psDBF->pachFieldType[iField] == 'F' ||
770
0
      psDBF->pachFieldType[iField] == 'D') {
771
0
    if (psDBF->panFieldDecimals[iField] > 0)
772
0
      return (FTDouble);
773
0
    else
774
0
      return (FTInteger);
775
0
  } else {
776
0
    return (FTString);
777
0
  }
778
0
}
779
780
/************************************************************************/
781
/*                         msDBFWriteAttribute()                        */
782
/*                  */
783
/*  Write an attribute record to the file.        */
784
/************************************************************************/
785
static int msDBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
786
0
                               void *pValue) {
787
0
  unsigned int nRecordOffset;
788
0
  int len;
789
0
  uchar *pabyRec;
790
0
  char szSField[40];
791
792
  /* -------------------------------------------------------------------- */
793
  /*  Is this a valid record?             */
794
  /* -------------------------------------------------------------------- */
795
0
  if (hEntity < 0 || hEntity > psDBF->nRecords)
796
0
    return (MS_FALSE);
797
798
0
  if (psDBF->bNoHeader)
799
0
    writeHeader(psDBF);
800
801
  /* -------------------------------------------------------------------- */
802
  /*      Is this a brand new record?                                     */
803
  /* -------------------------------------------------------------------- */
804
0
  if (hEntity == psDBF->nRecords) {
805
0
    flushRecord(psDBF);
806
807
0
    psDBF->nRecords++;
808
0
    for (unsigned i = 0; i < psDBF->nRecordLength; i++)
809
0
      psDBF->pszCurrentRecord[i] = ' ';
810
811
0
    psDBF->nCurrentRecord = hEntity;
812
0
  }
813
814
  /* -------------------------------------------------------------------- */
815
  /*      Is this an existing record, but different than the last one     */
816
  /*      we accessed?                                                    */
817
  /* -------------------------------------------------------------------- */
818
0
  if (psDBF->nCurrentRecord != hEntity) {
819
0
    flushRecord(psDBF);
820
821
0
    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
822
823
0
    VSIFSeekL(psDBF->fp, nRecordOffset, 0);
824
0
    if (VSIFReadL(psDBF->pszCurrentRecord, psDBF->nRecordLength, 1,
825
0
                  psDBF->fp) != 1)
826
0
      return MS_FALSE;
827
828
0
    psDBF->nCurrentRecord = hEntity;
829
0
  }
830
831
0
  pabyRec = (uchar *)psDBF->pszCurrentRecord;
832
833
  /* -------------------------------------------------------------------- */
834
  /*      Assign all the record fields.                                   */
835
  /* -------------------------------------------------------------------- */
836
0
  switch (psDBF->pachFieldType[iField]) {
837
0
  case 'D':
838
0
  case 'N':
839
0
  case 'F':
840
0
    snprintf(szSField, sizeof(szSField), "%*.*f", psDBF->panFieldSize[iField],
841
0
             psDBF->panFieldDecimals[iField], *(double *)pValue);
842
0
    len = strlen((char *)szSField);
843
0
    memcpy(pabyRec + psDBF->panFieldOffset[iField], szSField,
844
0
           MS_MIN(len, psDBF->panFieldSize[iField]));
845
0
    break;
846
847
0
  default:
848
0
    len = strlen((char *)pValue);
849
0
    memcpy(pabyRec + psDBF->panFieldOffset[iField], pValue,
850
0
           MS_MIN(len, psDBF->panFieldSize[iField]));
851
0
    break;
852
0
  }
853
854
0
  psDBF->bCurrentRecordModified = MS_TRUE;
855
0
  psDBF->bUpdated = MS_TRUE;
856
857
0
  return (MS_TRUE);
858
0
}
859
860
/************************************************************************/
861
/*                      msDBFWriteDoubleAttribute()                     */
862
/*                                                                      */
863
/*      Write a double attribute.                                       */
864
/************************************************************************/
865
int msDBFWriteDoubleAttribute(DBFHandle psDBF, int iRecord, int iField,
866
0
                              double dValue) {
867
0
  return (msDBFWriteAttribute(psDBF, iRecord, iField, (void *)&dValue));
868
0
}
869
870
/************************************************************************/
871
/*                      msDBFWriteIntegerAttribute()                    */
872
/*                                                                      */
873
/*      Write a integer attribute.                                      */
874
/************************************************************************/
875
876
int msDBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord, int iField,
877
0
                               int nValue) {
878
0
  double dValue = nValue;
879
880
0
  return (msDBFWriteAttribute(psDBF, iRecord, iField, (void *)&dValue));
881
0
}
882
883
/************************************************************************/
884
/*                      msDBFWriteStringAttribute()                     */
885
/*                                                                      */
886
/*      Write a string attribute.                                       */
887
/************************************************************************/
888
int msDBFWriteStringAttribute(DBFHandle psDBF, int iRecord, int iField,
889
0
                              const char *pszValue) {
890
0
  return (msDBFWriteAttribute(psDBF, iRecord, iField, (void *)pszValue));
891
0
}
892
893
/*
894
** Which column number in the .DBF file does the item correspond to
895
*/
896
0
int msDBFGetItemIndex(DBFHandle dbffile, char *name) {
897
0
  int i;
898
0
  int fWidth, fnDecimals; /* field width and number of decimals */
899
0
  char fName[32];         /* field name */
900
901
0
  if (!name) {
902
0
    msSetError(MS_MISCERR, "NULL item name passed.", "msGetItemIndex()");
903
0
    return (-1);
904
0
  }
905
906
  /* does name exist as a field? */
907
0
  for (i = 0; i < msDBFGetFieldCount(dbffile); i++) {
908
0
    msDBFGetFieldInfo(dbffile, i, fName, &fWidth, &fnDecimals);
909
0
    if (strcasecmp(name, fName) == 0) /* found it */
910
0
      return (i);
911
0
  }
912
913
0
  msSetError(MS_DBFERR, "Item '%s' not found.", "msDBFGetItemIndex()", name);
914
0
  return (-1); /* item not found */
915
0
}
916
917
/*
918
** Load item names into a character array
919
*/
920
0
char **msDBFGetItems(DBFHandle dbffile) {
921
0
  char **items;
922
0
  int i, nFields;
923
0
  char fName[32];
924
925
0
  if ((nFields = msDBFGetFieldCount(dbffile)) == 0) {
926
0
    msSetError(MS_DBFERR, "File contains no data.", "msGetDBFItems()");
927
0
    return (NULL);
928
0
  }
929
930
0
  items = (char **)malloc(sizeof(char *) * nFields);
931
0
  MS_CHECK_ALLOC(items, sizeof(char *) * nFields, NULL);
932
933
0
  for (i = 0; i < nFields; i++) {
934
0
    msDBFGetFieldInfo(dbffile, i, fName, NULL, NULL);
935
0
    items[i] = msStrdup(fName);
936
0
  }
937
938
0
  return (items);
939
0
}
940
941
/*
942
** Load item values into a character array
943
*/
944
0
char **msDBFGetValues(DBFHandle dbffile, int record) {
945
0
  char **values;
946
0
  int i, nFields;
947
948
0
  if ((nFields = msDBFGetFieldCount(dbffile)) == 0) {
949
0
    msSetError(MS_DBFERR, "File contains no data.", "msGetDBFValues()");
950
0
    return (NULL);
951
0
  }
952
953
0
  values = (char **)malloc(sizeof(char *) * nFields);
954
0
  MS_CHECK_ALLOC(values, sizeof(char *) * nFields, NULL);
955
956
0
  for (i = 0; i < nFields; i++)
957
0
    values[i] = msStrdup(msDBFReadStringAttribute(dbffile, record, i));
958
959
0
  return (values);
960
0
}
961
962
0
int *msDBFGetItemIndexes(DBFHandle dbffile, char **items, int numitems) {
963
0
  int *itemindexes = NULL, i;
964
965
0
  if (numitems == 0)
966
0
    return (NULL);
967
968
0
  itemindexes = (int *)malloc(sizeof(int) * numitems);
969
0
  MS_CHECK_ALLOC(itemindexes, sizeof(int) * numitems, NULL);
970
971
0
  for (i = 0; i < numitems; i++) {
972
0
    itemindexes[i] = msDBFGetItemIndex(dbffile, items[i]);
973
0
    if (itemindexes[i] == -1) {
974
0
      free(itemindexes);
975
0
      return (NULL); /* item not found */
976
0
    }
977
0
  }
978
979
0
  return (itemindexes);
980
0
}
981
982
char **msDBFGetValueList(DBFHandle dbffile, int record, int *itemindexes,
983
0
                         int numitems) {
984
0
  const char *value;
985
0
  char **values = NULL;
986
0
  int i;
987
988
0
  if (numitems == 0)
989
0
    return (NULL);
990
991
0
  values = (char **)malloc(sizeof(char *) * numitems);
992
0
  MS_CHECK_ALLOC(values, sizeof(char *) * numitems, NULL);
993
994
0
  for (i = 0; i < numitems; i++) {
995
0
    value = msDBFReadStringAttribute(dbffile, record, itemindexes[i]);
996
0
    if (value == NULL) {
997
0
      free(values);
998
0
      return NULL; /* Error already reported by msDBFReadStringAttribute() */
999
0
    }
1000
0
    values[i] = msStrdup(value);
1001
0
  }
1002
1003
0
  return (values);
1004
0
}