Coverage Report

Created: 2026-04-10 07:04

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