• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geographika / mapserver / 17709373190

14 Sep 2025 09:32AM UTC coverage: 41.466% (+0.09%) from 41.375%
17709373190

push

github

geographika
Add index templates

62086 of 149729 relevant lines covered (41.47%)

25036.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.87
/src/mapxbase.c
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
static inline void IGUR_sizet(size_t ignored) {
38
  (void)ignored;
39
} /* 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
{
50
  return ((void *)realloc(pMem, nNewSize));
4,546✔
51
}
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
{
64
  uchar abyHeader[32];
65
  int i;
66

67
  if (!psDBF->bNoHeader)
×
68
    return;
×
69

70
  psDBF->bNoHeader = MS_FALSE;
×
71

72
  /* -------------------------------------------------------------------- */
73
  /*  Initialize the file header information.               */
74
  /* -------------------------------------------------------------------- */
75
  for (i = 0; i < 32; i++)
×
76
    abyHeader[i] = 0;
×
77

78
  abyHeader[0] = 0x03; /* memo field? - just copying   */
×
79

80
  /* date updated on close, record count preset at zero */
81

82
  abyHeader[8] = psDBF->nHeaderLength % 256;
×
83
  abyHeader[9] = psDBF->nHeaderLength / 256;
×
84

85
  abyHeader[10] = psDBF->nRecordLength % 256;
×
86
  abyHeader[11] = psDBF->nRecordLength / 256;
×
87

88
  /* -------------------------------------------------------------------- */
89
  /*      Write the initial 32 byte file header, and all the field        */
90
  /*      descriptions.                                             */
91
  /* -------------------------------------------------------------------- */
92
  VSIFSeekL(psDBF->fp, 0, 0);
×
93
  VSIFWriteL(abyHeader, 32, 1, psDBF->fp);
×
94
  VSIFWriteL(psDBF->pszHeader, 32, psDBF->nFields, psDBF->fp);
×
95

96
  /* -------------------------------------------------------------------- */
97
  /*      Write out the newline character if there is room for it.        */
98
  /* -------------------------------------------------------------------- */
99
  if (psDBF->nHeaderLength > 32 * psDBF->nFields + 32) {
×
100
    char cNewline;
101

102
    cNewline = 0x0d;
×
103
    VSIFWriteL(&cNewline, 1, 1, psDBF->fp);
×
104
  }
105
}
106

107
/************************************************************************/
108
/*                           flushRecord()                              */
109
/*                                                                      */
110
/*      Write out the current record if there is one.                   */
111
/************************************************************************/
112
static void flushRecord(DBFHandle psDBF)
29,086✔
113

114
{
115
  unsigned int nRecordOffset;
116

117
  if (psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1) {
29,086✔
118
    psDBF->bCurrentRecordModified = MS_FALSE;
×
119

120
    nRecordOffset =
×
121
        psDBF->nRecordLength * psDBF->nCurrentRecord + psDBF->nHeaderLength;
×
122

123
    VSIFSeekL(psDBF->fp, nRecordOffset, 0);
×
124
    VSIFWriteL(psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp);
×
125
  }
126
}
29,086✔
127

128
DBFHandle msDBFOpenVirtualFile(VSILFILE *fp) {
2,935✔
129
  DBFHandle psDBF;
130
  uchar *pabyBuf;
131
  int nFields, nHeadLen, nRecLen, iField;
132

133
  /* -------------------------------------------------------------------- */
134
  /*      Open the file.                                                  */
135
  /* -------------------------------------------------------------------- */
136
  psDBF = (DBFHandle)calloc(1, sizeof(DBFInfo));
2,935✔
137
  MS_CHECK_ALLOC(psDBF, sizeof(DBFInfo), NULL);
2,935✔
138
  psDBF->fp = fp;
2,935✔
139

140
  psDBF->bNoHeader = MS_FALSE;
2,935✔
141
  psDBF->nCurrentRecord = -1;
2,935✔
142
  psDBF->bCurrentRecordModified = MS_FALSE;
2,935✔
143

144
  psDBF->pszStringField = NULL;
2,935✔
145
  psDBF->nStringFieldLen = 0;
2,935✔
146

147
  psDBF->pszEncoding = NULL;
2,935✔
148

149
  /* -------------------------------------------------------------------- */
150
  /*  Read Table Header info                                              */
151
  /* -------------------------------------------------------------------- */
152
  pabyBuf = (uchar *)msSmallMalloc(500);
2,935✔
153
  if (VSIFReadL(pabyBuf, 32, 1, psDBF->fp) != 1) {
2,935✔
154
    VSIFCloseL(psDBF->fp);
×
155
    msFree(psDBF);
×
156
    msFree(pabyBuf);
×
157
    return (NULL);
×
158
  }
159

160
  if (pabyBuf[7] < 128)
2,935✔
161
    psDBF->nRecords = pabyBuf[4] + pabyBuf[5] * 256 + pabyBuf[6] * 256 * 256 +
2,935✔
162
                      pabyBuf[7] * 256 * 256 * 256;
2,935✔
163
  else
164
    psDBF->nRecords = 0;
×
165

166
  psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9] * 256;
2,935✔
167
  psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11] * 256;
2,935✔
168

169
  if (nHeadLen <= 32) {
2,935✔
170
    VSIFCloseL(psDBF->fp);
×
171
    msFree(psDBF);
×
172
    msFree(pabyBuf);
×
173
    return (NULL);
×
174
  }
175

176
  psDBF->nFields = nFields = (nHeadLen - 32) / 32;
2,935✔
177

178
  psDBF->pszCurrentRecord = (char *)msSmallMalloc(nRecLen);
2,935✔
179

180
  /* -------------------------------------------------------------------- */
181
  /*  Read in Field Definitions                                           */
182
  /* -------------------------------------------------------------------- */
183
  pabyBuf = (uchar *)SfRealloc(pabyBuf, nHeadLen);
184
  psDBF->pszHeader = (char *)pabyBuf;
2,935✔
185

186
  VSIFSeekL(psDBF->fp, 32, 0);
2,935✔
187
  if (VSIFReadL(pabyBuf, nHeadLen - 32, 1, psDBF->fp) != 1) {
2,935✔
188
    msFree(psDBF->pszCurrentRecord);
×
189
    VSIFCloseL(psDBF->fp);
×
190
    msFree(psDBF);
×
191
    msFree(pabyBuf);
×
192
    return (NULL);
×
193
  }
194

195
  psDBF->panFieldOffset = (int *)msSmallMalloc(sizeof(int) * nFields);
2,935✔
196
  psDBF->panFieldSize = (int *)msSmallMalloc(sizeof(int) * nFields);
2,935✔
197
  psDBF->panFieldDecimals = (int *)msSmallMalloc(sizeof(int) * nFields);
2,935✔
198
  psDBF->pachFieldType = (char *)msSmallMalloc(sizeof(char) * nFields);
2,935✔
199

200
  for (iField = 0; iField < nFields; iField++) {
26,787✔
201
    uchar *pabyFInfo;
202

203
    pabyFInfo = pabyBuf + iField * 32;
23,852✔
204

205
    if (pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F') {
23,852✔
206
      psDBF->panFieldSize[iField] = pabyFInfo[16];
12,334✔
207
      psDBF->panFieldDecimals[iField] = pabyFInfo[17];
12,334✔
208
    } else {
209
      psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17] * 256;
11,518✔
210
      psDBF->panFieldDecimals[iField] = 0;
11,518✔
211
    }
212

213
    psDBF->pachFieldType[iField] = (char)pabyFInfo[11];
23,852✔
214
    if (iField == 0)
23,852✔
215
      psDBF->panFieldOffset[iField] = 1;
2,935✔
216
    else
217
      psDBF->panFieldOffset[iField] =
20,917✔
218
          psDBF->panFieldOffset[iField - 1] + psDBF->panFieldSize[iField - 1];
20,917✔
219
  }
220

221
  return (psDBF);
222
}
223

224
/**
225
 * Attempt to read character encoding from a .CPG-file
226
 */
227
char *msReadCPGEncoding(char *cpgFilename) {
2,935✔
228
  VSILFILE *fpCPG = VSIFOpenL(cpgFilename, "rb");
2,935✔
229
  if (fpCPG == NULL)
2,935✔
230
    return NULL;
231

232
  char szEncoding[100] = "";
47✔
233
  size_t nRead = VSIFReadL(szEncoding, 1, sizeof(szEncoding) - 1, fpCPG);
47✔
234
  VSIFCloseL(fpCPG);
47✔
235

236
  if (nRead == 0)
47✔
237
    return NULL;
238

239
  // Null terminate and remove any line break
240
  szEncoding[nRead] = '\0';
47✔
241
  szEncoding[strcspn(szEncoding, "\r\n")] = 0;
47✔
242

243
  if (szEncoding[0] == '\0') {
47✔
244
    return NULL;
245
  }
246

247
  /* See
248
   * https://github.com/OSGeo/gdal/blob/release/3.11/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp#L503
249
   */
250
  const int nCPG = atoi(szEncoding);
251
  if ((nCPG >= 437 && nCPG <= 950) || (nCPG >= 1250 && nCPG <= 1258)) {
47✔
252
    char szResult[20];
253
    snprintf(szResult, sizeof(szResult), "CP%d", nCPG);
254
    return msStrdup(szResult);
5✔
255
  }
256

257
  if (strncasecmp(szEncoding, "8859", 4) == 0) {
42✔
258
    const char *suffix = szEncoding + 4;
259
    if (*suffix == '-' || *suffix == '_')
13✔
260
      suffix++;
261

262
    if (!isdigit((unsigned char)*suffix))
13✔
263
      return msStrdup(szEncoding);
×
264

265
    char szResult[40];
266
    snprintf(szResult, sizeof(szResult), "ISO-8859-%d", atoi(suffix));
267
    return msStrdup(szResult);
13✔
268
  }
269

270
  if (strncasecmp(szEncoding, "ANSI 1251", 9) == 0) {
29✔
271
    return msStrdup("CP1251");
×
272
  }
273

274
  return msStrdup(szEncoding);
29✔
275
}
276

277
/************************************************************************/
278
/*                              msDBFOpen()                             */
279
/*                                                                      */
280
/*      Open a .dbf file.                                               */
281
/************************************************************************/
282
DBFHandle msDBFOpen(const char *pszFilename, const char *pszAccess)
2,935✔
283

284
{
285
  /* -------------------------------------------------------------------- */
286
  /*      We only allow the access strings "rb" and "r+".                 */
287
  /* -------------------------------------------------------------------- */
288
  if (strcmp(pszAccess, "r") != 0 && strcmp(pszAccess, "r+") != 0 &&
2,935✔
289
      strcmp(pszAccess, "rb") != 0 && strcmp(pszAccess, "r+b") != 0)
2,935✔
290
    return (NULL);
291

292
  /* -------------------------------------------------------------------- */
293
  /*  Ensure the extension is converted to dbf or DBF if it is      */
294
  /*  currently .shp or .shx.               */
295
  /* -------------------------------------------------------------------- */
296
  char *pszDBFFilename = (char *)msSmallMalloc(strlen(pszFilename) + 1);
2,935✔
297
  strcpy(pszDBFFilename, pszFilename);
298

299
  if (strcmp(pszFilename + strlen(pszFilename) - 4, ".shp") == 0 ||
2,935✔
300
      strcmp(pszFilename + strlen(pszFilename) - 4, ".shx") == 0) {
2,935✔
301
    strcpy(pszDBFFilename + strlen(pszDBFFilename) - 4, ".dbf");
×
302
  } else if (strcmp(pszFilename + strlen(pszFilename) - 4, ".SHP") == 0 ||
2,935✔
303
             strcmp(pszFilename + strlen(pszFilename) - 4, ".SHX") == 0) {
2,935✔
304
    strcpy(pszDBFFilename + strlen(pszDBFFilename) - 4, ".DBF");
×
305
  }
306

307
  /* -------------------------------------------------------------------- */
308
  /*      Open the file.                                                  */
309
  /* -------------------------------------------------------------------- */
310
  VSILFILE *fp = VSIFOpenL(pszDBFFilename, pszAccess);
2,935✔
311
  if (fp == NULL) {
2,935✔
312
    if (strcmp(pszDBFFilename + strlen(pszDBFFilename) - 4, ".dbf") == 0) {
1✔
313
      strcpy(pszDBFFilename + strlen(pszDBFFilename) - 4, ".DBF");
314
      fp = VSIFOpenL(pszDBFFilename, pszAccess);
1✔
315
    }
316
  }
317

318
  if (fp == NULL) {
1✔
319
    msFree(pszDBFFilename);
×
320
    return (NULL);
×
321
  }
322

323
  DBFHandle dbfHandle = msDBFOpenVirtualFile(fp);
2,935✔
324
  if (dbfHandle) {
2,935✔
325
    char *pszCPGFilename = (char *)msSmallMalloc(strlen(pszDBFFilename) + 1);
2,935✔
326
    strcpy(pszCPGFilename, pszDBFFilename);
327

328
    if (strcmp(pszDBFFilename + strlen(pszDBFFilename) - 4, ".dbf") == 0) {
2,935✔
329
      strcpy(pszCPGFilename + strlen(pszCPGFilename) - 4, ".cpg");
2,934✔
330
    } else {
331
      strcpy(pszCPGFilename + strlen(pszCPGFilename) - 4, ".CPG");
1✔
332
    }
333

334
    dbfHandle->pszEncoding = msReadCPGEncoding(pszCPGFilename);
2,935✔
335

336
    msFree(pszCPGFilename);
2,935✔
337
  }
338

339
  msFree(pszDBFFilename);
2,935✔
340
  return dbfHandle;
2,935✔
341
}
342

343
/************************************************************************/
344
/*                              msDBFClose()                            */
345
/************************************************************************/
346

347
void msDBFClose(DBFHandle psDBF) {
2,935✔
348
  /* -------------------------------------------------------------------- */
349
  /*      Write out header if not already written.                        */
350
  /* -------------------------------------------------------------------- */
351
  if (psDBF->bNoHeader)
2,935✔
352
    writeHeader(psDBF);
×
353

354
  flushRecord(psDBF);
2,935✔
355

356
  /* -------------------------------------------------------------------- */
357
  /*      Update last access date, and number of records if we have       */
358
  /*  write access.                             */
359
  /* -------------------------------------------------------------------- */
360
  if (psDBF->bUpdated) {
2,935✔
361
    uchar abyFileHeader[32];
362

363
    VSIFSeekL(psDBF->fp, 0, 0);
×
364
    IGUR_sizet(VSIFReadL(abyFileHeader, 32, 1, psDBF->fp));
×
365

366
    abyFileHeader[1] = 95; /* YY */
×
367
    abyFileHeader[2] = 7;  /* MM */
×
368
    abyFileHeader[3] = 26; /* DD */
×
369

370
    abyFileHeader[4] = psDBF->nRecords % 256;
×
371
    abyFileHeader[5] = (psDBF->nRecords / 256) % 256;
×
372
    abyFileHeader[6] = (psDBF->nRecords / (256 * 256)) % 256;
×
373
    abyFileHeader[7] = (psDBF->nRecords / (256 * 256 * 256)) % 256;
×
374

375
    VSIFSeekL(psDBF->fp, 0, 0);
×
376
    VSIFWriteL(abyFileHeader, 32, 1, psDBF->fp);
×
377
  }
378

379
  /* -------------------------------------------------------------------- */
380
  /*      Close, and free resources.                                      */
381
  /* -------------------------------------------------------------------- */
382
  VSIFCloseL(psDBF->fp);
2,935✔
383

384
  if (psDBF->panFieldOffset != NULL) {
2,935✔
385
    free(psDBF->panFieldOffset);
2,935✔
386
    free(psDBF->panFieldSize);
2,935✔
387
    free(psDBF->panFieldDecimals);
2,935✔
388
    free(psDBF->pachFieldType);
2,935✔
389
  }
390

391
  free(psDBF->pszHeader);
2,935✔
392
  free(psDBF->pszCurrentRecord);
2,935✔
393

394
  free(psDBF->pszStringField);
2,935✔
395
  free(psDBF->pszEncoding);
2,935✔
396

397
  free(psDBF);
2,935✔
398
}
2,935✔
399

400
/************************************************************************/
401
/*                             msDBFCreate()                            */
402
/*                                                                      */
403
/*      Create a new .dbf file.                                         */
404
/************************************************************************/
405
DBFHandle msDBFCreate(const char *pszFilename)
×
406

407
{
408
  DBFHandle psDBF;
409
  VSILFILE *fp;
410

411
  /* -------------------------------------------------------------------- */
412
  /*      Create the file.                                                */
413
  /* -------------------------------------------------------------------- */
414
  fp = VSIFOpenL(pszFilename, "wb");
×
415
  if (fp == NULL)
×
416
    return (NULL);
417

418
  {
419
    char ch = 0;
×
420
    VSIFWriteL(&ch, 1, 1, fp);
×
421
  }
422
  VSIFCloseL(fp);
×
423

424
  fp = VSIFOpenL(pszFilename, "rb+");
×
425
  if (fp == NULL)
×
426
    return (NULL);
427

428
  /* -------------------------------------------------------------------- */
429
  /*  Create the info structure.                */
430
  /* -------------------------------------------------------------------- */
431
  psDBF = (DBFHandle)malloc(sizeof(DBFInfo));
×
432
  if (psDBF == NULL) {
×
433
    msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n",
×
434
               "msDBFCreate()", __FILE__, __LINE__,
435
               (unsigned int)sizeof(DBFInfo));
436
    VSIFCloseL(fp);
×
437
    return NULL;
×
438
  }
439

440
  psDBF->fp = fp;
×
441
  psDBF->nRecords = 0;
×
442
  psDBF->nFields = 0;
×
443
  psDBF->nRecordLength = 1;
×
444
  psDBF->nHeaderLength = 33;
×
445

446
  psDBF->panFieldOffset = NULL;
×
447
  psDBF->panFieldSize = NULL;
×
448
  psDBF->panFieldDecimals = NULL;
×
449
  psDBF->pachFieldType = NULL;
×
450
  psDBF->pszHeader = NULL;
×
451

452
  psDBF->nCurrentRecord = -1;
×
453
  psDBF->bCurrentRecordModified = MS_FALSE;
×
454
  psDBF->pszCurrentRecord = NULL;
×
455

456
  psDBF->pszStringField = NULL;
×
457
  psDBF->nStringFieldLen = 0;
×
458

459
  psDBF->bNoHeader = MS_TRUE;
×
460
  psDBF->bUpdated = MS_FALSE;
×
461

462
  return (psDBF);
×
463
}
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
                  int nWidth, int nDecimals) {
473
  char *pszFInfo;
474
  int i;
475

476
  /* -------------------------------------------------------------------- */
477
  /*      Do some checking to ensure we can add records to this file.     */
478
  /* -------------------------------------------------------------------- */
479
  if (psDBF->nRecords > 0)
×
480
    return (MS_FALSE);
481

482
  if (!psDBF->bNoHeader)
×
483
    return (MS_FALSE);
484

485
  if (eType != FTDouble && nDecimals != 0)
×
486
    return (MS_FALSE);
487

488
  /* -------------------------------------------------------------------- */
489
  /*      SfRealloc all the arrays larger to hold the additional field    */
490
  /*      information.                                                    */
491
  /* -------------------------------------------------------------------- */
492
  psDBF->nFields++;
×
493

494
  psDBF->panFieldOffset =
×
495
      (int *)SfRealloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields);
×
496

497
  psDBF->panFieldSize =
×
498
      (int *)SfRealloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields);
×
499

500
  psDBF->panFieldDecimals =
×
501
      (int *)SfRealloc(psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields);
×
502

503
  psDBF->pachFieldType =
×
504
      (char *)SfRealloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields);
×
505

506
  /* -------------------------------------------------------------------- */
507
  /*      Assign the new field information fields.                        */
508
  /* -------------------------------------------------------------------- */
509
  psDBF->panFieldOffset[psDBF->nFields - 1] = psDBF->nRecordLength;
×
510
  psDBF->nRecordLength += nWidth;
×
511
  psDBF->panFieldSize[psDBF->nFields - 1] = nWidth;
×
512
  psDBF->panFieldDecimals[psDBF->nFields - 1] = nDecimals;
×
513

514
  if (eType == FTString)
×
515
    psDBF->pachFieldType[psDBF->nFields - 1] = 'C';
×
516
  else
517
    psDBF->pachFieldType[psDBF->nFields - 1] = 'N';
×
518

519
  /* -------------------------------------------------------------------- */
520
  /*      Extend the required header information.                         */
521
  /* -------------------------------------------------------------------- */
522
  psDBF->nHeaderLength += 32;
×
523
  psDBF->bUpdated = MS_FALSE;
×
524

525
  psDBF->pszHeader = (char *)SfRealloc(psDBF->pszHeader, psDBF->nFields * 32);
×
526

527
  pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields - 1);
×
528

529
  for (i = 0; i < 32; i++)
×
530
    pszFInfo[i] = '\0';
×
531

532
  strncpy(pszFInfo, pszFieldName, 10);
533

534
  pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields - 1];
×
535

536
  if (eType == FTString) {
×
537
    pszFInfo[16] = nWidth % 256;
×
538
    pszFInfo[17] = nWidth / 256;
×
539
  } else {
540
    pszFInfo[16] = nWidth;
×
541
    pszFInfo[17] = nDecimals;
×
542
  }
543

544
  /* -------------------------------------------------------------------- */
545
  /*      Make the current record buffer appropriately larger.            */
546
  /* -------------------------------------------------------------------- */
547
  psDBF->pszCurrentRecord =
×
548
      (char *)SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength);
×
549

550
  return (psDBF->nFields - 1);
×
551
}
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)
158,610✔
562

563
{
564
  switch (type) {
158,610✔
565
  case 'N':
90,298✔
566
  case 'F':
567
    /* NULL numeric fields have value "****************" */
568
    return pszValue[0] == '*';
90,298✔
569

570
  case 'D':
×
571
    /* NULL date fields have value "00000000" */
572
    return strncmp(pszValue, "00000000", 8) == 0;
×
573

574
  case 'L':
44✔
575
    /* NULL boolean fields have value "?" */
576
    return pszValue[0] == '?';
44✔
577

578
  default:
68,268✔
579
    /* empty string fields are considered NULL */
580
    return strlen(pszValue) == 0;
68,268✔
581
  }
582
}
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)
158,610✔
590

591
{
592
  int i;
593
  unsigned int nRecordOffset;
594
  const uchar *pabyRec;
595
  const char *pReturnField = NULL;
596

597
  /* -------------------------------------------------------------------- */
598
  /*  Is the request valid?                             */
599
  /* -------------------------------------------------------------------- */
600
  if (iField < 0 || iField >= psDBF->nFields) {
158,610✔
601
    msSetError(MS_DBFERR, "Invalid field index %d.", "msDBFReadAttribute()",
×
602
               iField);
603
    return (NULL);
×
604
  }
605

606
  if (hEntity < 0 || hEntity >= psDBF->nRecords) {
158,610✔
607
    msSetError(MS_DBFERR, "Invalid record number %d.", "msDBFReadAttribute()",
×
608
               hEntity);
609
    return (NULL);
×
610
  }
611

612
  /* -------------------------------------------------------------------- */
613
  /*  Have we read the record?              */
614
  /* -------------------------------------------------------------------- */
615
  if (psDBF->nCurrentRecord != hEntity) {
158,610✔
616
    flushRecord(psDBF);
26,151✔
617

618
    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
26,151✔
619

620
    VSIFSeekL(psDBF->fp, nRecordOffset, 0);
26,151✔
621
    if (VSIFReadL(psDBF->pszCurrentRecord, psDBF->nRecordLength, 1,
26,151✔
622
                  psDBF->fp) != 1) {
623
      msSetError(MS_DBFERR, "Cannot read record %d.", "msDBFReadAttribute()",
×
624
                 hEntity);
625
      return (NULL);
×
626
    }
627

628
    psDBF->nCurrentRecord = hEntity;
26,151✔
629
  }
630

631
  pabyRec = (const uchar *)psDBF->pszCurrentRecord;
158,610✔
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
  if (psDBF->panFieldSize[iField] + 1 > psDBF->nStringFieldLen) {
158,610✔
639
    psDBF->nStringFieldLen = psDBF->panFieldSize[iField] * 2 + 10;
1,611✔
640
    psDBF->pszStringField =
1,611✔
641
        (char *)SfRealloc(psDBF->pszStringField, psDBF->nStringFieldLen);
1,611✔
642
  }
643

644
  /* -------------------------------------------------------------------- */
645
  /*  Extract the requested field.              */
646
  /* -------------------------------------------------------------------- */
647
  strncpy(psDBF->pszStringField,
158,610✔
648
          (const char *)pabyRec + psDBF->panFieldOffset[iField],
158,610✔
649
          psDBF->panFieldSize[iField]);
158,610✔
650
  psDBF->pszStringField[psDBF->panFieldSize[iField]] = '\0';
158,610✔
651

652
  /*
653
  ** Trim trailing blanks (SDL Modification)
654
  */
655
  for (i = strlen(psDBF->pszStringField) - 1; i >= 0; i--) {
1,600,472✔
656
    if (psDBF->pszStringField[i] != ' ') {
1,575,820✔
657
      psDBF->pszStringField[i + 1] = '\0';
133,958✔
658
      break;
133,958✔
659
    }
660
  }
661

662
  if (i == -1)
158,610✔
663
    psDBF->pszStringField[0] = '\0'; /* whole string is blank (SDL fix)       */
24,652✔
664

665
  /*
666
  ** Trim/skip leading blanks (SDL/DM Modification - only on numeric types)
667
  */
668
  if (psDBF->pachFieldType[iField] == 'N' ||
158,610✔
669
      psDBF->pachFieldType[iField] == 'F' ||
670
      psDBF->pachFieldType[iField] == 'D') {
671
    for (i = 0; psDBF->pszStringField[i] != '\0'; i++) {
588,718✔
672
      if (psDBF->pszStringField[i] != ' ')
588,718✔
673
        break;
674
    }
675
    pReturnField = psDBF->pszStringField + i;
676
  } else
677
    pReturnField = psDBF->pszStringField;
68,312✔
678

679
  /*  detect null values */
680
  if (DBFIsValueNULL(pReturnField, psDBF->pachFieldType[iField])) {
158,610✔
681
    if (psDBF->pachFieldType[iField] == 'N' ||
682
        psDBF->pachFieldType[iField] == 'F' ||
683
        psDBF->pachFieldType[iField] == 'D')
684
      pReturnField = "0";
685
  }
686
  return (pReturnField);
687
}
688

689
/************************************************************************/
690
/*                        msDBFReadIntAttribute()                       */
691
/*                                                                      */
692
/*      Read an integer attribute.                                      */
693
/************************************************************************/
694
int msDBFReadIntegerAttribute(DBFHandle psDBF, int iRecord, int iField)
×
695

696
{
697
  return (atoi(msDBFReadAttribute(psDBF, iRecord, iField)));
×
698
}
699

700
/************************************************************************/
701
/*                        msDBFReadDoubleAttribute()                    */
702
/*                                                                      */
703
/*      Read a double attribute.                                        */
704
/************************************************************************/
705
double msDBFReadDoubleAttribute(DBFHandle psDBF, int iRecord, int iField) {
×
706
  return (atof(msDBFReadAttribute(psDBF, iRecord, iField)));
×
707
}
708

709
/************************************************************************/
710
/*                        msDBFReadStringAttribute()                      */
711
/*                                                                      */
712
/*      Read a string attribute.                                        */
713
/************************************************************************/
714
const char *msDBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField) {
158,610✔
715
  return (msDBFReadAttribute(psDBF, iRecord, iField));
158,610✔
716
}
717

718
/************************************************************************/
719
/*                          msDBFGetFieldCount()                        */
720
/*                                                                      */
721
/*      Return the number of fields in this table.                      */
722
/************************************************************************/
723
int msDBFGetFieldCount(DBFHandle psDBF) { return (psDBF->nFields); }
149,642✔
724

725
/************************************************************************/
726
/*                         msDBFGetRecordCount()                        */
727
/*                                                                      */
728
/*      Return the number of records in this table.                     */
729
/************************************************************************/
730
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,
160,623✔
738
                               int *pnWidth, int *pnDecimals) {
739
  if (iField < 0 || iField >= psDBF->nFields)
160,623✔
740
    return (FTInvalid);
741

742
  if (pnWidth != NULL)
160,623✔
743
    *pnWidth = psDBF->panFieldSize[iField];
146,819✔
744

745
  if (pnDecimals != NULL)
160,623✔
746
    *pnDecimals = psDBF->panFieldDecimals[iField];
146,819✔
747

748
  if (pszFieldName != NULL) {
160,623✔
749
    int i;
750

751
    strncpy(pszFieldName, (char *)psDBF->pszHeader + iField * 32, 11);
160,621✔
752
    pszFieldName[11] = '\0';
160,621✔
753
    for (i = 10; i > 0 && pszFieldName[i] == ' '; i--)
160,621✔
754
      pszFieldName[i] = '\0';
×
755
  }
756

757
  if (psDBF->pachFieldType[iField] == 'N' ||
160,623✔
758
      psDBF->pachFieldType[iField] == 'F' ||
759
      psDBF->pachFieldType[iField] == 'D') {
760
    if (psDBF->panFieldDecimals[iField] > 0)
88,067✔
761
      return (FTDouble);
762
    else
763
      return (FTInteger);
57,228✔
764
  } else {
765
    return (FTString);
766
  }
767
}
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
                               void *pValue) {
776
  unsigned int nRecordOffset;
777
  int len;
778
  uchar *pabyRec;
779
  char szSField[40];
780

781
  /* -------------------------------------------------------------------- */
782
  /*  Is this a valid record?             */
783
  /* -------------------------------------------------------------------- */
784
  if (hEntity < 0 || hEntity > psDBF->nRecords)
×
785
    return (MS_FALSE);
786

787
  if (psDBF->bNoHeader)
×
788
    writeHeader(psDBF);
×
789

790
  /* -------------------------------------------------------------------- */
791
  /*      Is this a brand new record?                                     */
792
  /* -------------------------------------------------------------------- */
793
  if (hEntity == psDBF->nRecords) {
×
794
    flushRecord(psDBF);
×
795

796
    psDBF->nRecords++;
×
797
    for (unsigned i = 0; i < psDBF->nRecordLength; i++)
×
798
      psDBF->pszCurrentRecord[i] = ' ';
×
799

800
    psDBF->nCurrentRecord = hEntity;
×
801
  }
802

803
  /* -------------------------------------------------------------------- */
804
  /*      Is this an existing record, but different than the last one     */
805
  /*      we accessed?                                                    */
806
  /* -------------------------------------------------------------------- */
807
  if (psDBF->nCurrentRecord != hEntity) {
×
808
    flushRecord(psDBF);
×
809

810
    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
×
811

812
    VSIFSeekL(psDBF->fp, nRecordOffset, 0);
×
813
    if (VSIFReadL(psDBF->pszCurrentRecord, psDBF->nRecordLength, 1,
×
814
                  psDBF->fp) != 1)
815
      return MS_FALSE;
816

817
    psDBF->nCurrentRecord = hEntity;
×
818
  }
819

820
  pabyRec = (uchar *)psDBF->pszCurrentRecord;
×
821

822
  /* -------------------------------------------------------------------- */
823
  /*      Assign all the record fields.                                   */
824
  /* -------------------------------------------------------------------- */
825
  switch (psDBF->pachFieldType[iField]) {
×
826
  case 'D':
×
827
  case 'N':
828
  case 'F':
829
    snprintf(szSField, sizeof(szSField), "%*.*f", psDBF->panFieldSize[iField],
×
830
             psDBF->panFieldDecimals[iField], *(double *)pValue);
×
831
    len = strlen((char *)szSField);
×
832
    memcpy(pabyRec + psDBF->panFieldOffset[iField], szSField,
×
833
           MS_MIN(len, psDBF->panFieldSize[iField]));
×
834
    break;
835

836
  default:
×
837
    len = strlen((char *)pValue);
×
838
    memcpy(pabyRec + psDBF->panFieldOffset[iField], pValue,
×
839
           MS_MIN(len, psDBF->panFieldSize[iField]));
×
840
    break;
841
  }
842

843
  psDBF->bCurrentRecordModified = MS_TRUE;
×
844
  psDBF->bUpdated = MS_TRUE;
×
845

846
  return (MS_TRUE);
×
847
}
848

849
/************************************************************************/
850
/*                      msDBFWriteDoubleAttribute()                     */
851
/*                                                                      */
852
/*      Write a double attribute.                                       */
853
/************************************************************************/
854
int msDBFWriteDoubleAttribute(DBFHandle psDBF, int iRecord, int iField,
×
855
                              double dValue) {
856
  return (msDBFWriteAttribute(psDBF, iRecord, iField, (void *)&dValue));
×
857
}
858

859
/************************************************************************/
860
/*                      msDBFWriteIntegerAttribute()                    */
861
/*                                                                      */
862
/*      Write a integer attribute.                                      */
863
/************************************************************************/
864

865
int msDBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord, int iField,
×
866
                               int nValue) {
867
  double dValue = nValue;
×
868

869
  return (msDBFWriteAttribute(psDBF, iRecord, iField, (void *)&dValue));
×
870
}
871

872
/************************************************************************/
873
/*                      msDBFWriteStringAttribute()                     */
874
/*                                                                      */
875
/*      Write a string attribute.                                       */
876
/************************************************************************/
877
int msDBFWriteStringAttribute(DBFHandle psDBF, int iRecord, int iField,
×
878
                              const char *pszValue) {
879
  return (msDBFWriteAttribute(psDBF, iRecord, iField, (void *)pszValue));
×
880
}
881

882
/*
883
** Which column number in the .DBF file does the item correspond to
884
*/
885
int msDBFGetItemIndex(DBFHandle dbffile, char *name) {
20,212✔
886
  int i;
887
  int fWidth, fnDecimals; /* field width and number of decimals */
888
  char fName[32];         /* field name */
889

890
  if (!name) {
20,212✔
891
    msSetError(MS_MISCERR, "NULL item name passed.", "msGetItemIndex()");
×
892
    return (-1);
×
893
  }
894

895
  /* does name exist as a field? */
896
  for (i = 0; i < msDBFGetFieldCount(dbffile); i++) {
146,375✔
897
    msDBFGetFieldInfo(dbffile, i, fName, &fWidth, &fnDecimals);
146,373✔
898
    if (strcasecmp(name, fName) == 0) /* found it */
146,373✔
899
      return (i);
20,210✔
900
  }
901

902
  msSetError(MS_DBFERR, "Item '%s' not found.", "msDBFGetItemIndex()", name);
2✔
903
  return (-1); /* item not found */
2✔
904
}
905

906
/*
907
** Load item names into a character array
908
*/
909
char **msDBFGetItems(DBFHandle dbffile) {
1,604✔
910
  char **items;
911
  int i, nFields;
912
  char fName[32];
913

914
  if ((nFields = msDBFGetFieldCount(dbffile)) == 0) {
1,604✔
915
    msSetError(MS_DBFERR, "File contains no data.", "msGetDBFItems()");
×
916
    return (NULL);
×
917
  }
918

919
  items = (char **)malloc(sizeof(char *) * nFields);
1,604✔
920
  MS_CHECK_ALLOC(items, sizeof(char *) * nFields, NULL);
1,604✔
921

922
  for (i = 0; i < nFields; i++) {
15,406✔
923
    msDBFGetFieldInfo(dbffile, i, fName, NULL, NULL);
13,802✔
924
    items[i] = msStrdup(fName);
13,802✔
925
  }
926

927
  return (items);
928
}
929

930
/*
931
** Load item values into a character array
932
*/
933
char **msDBFGetValues(DBFHandle dbffile, int record) {
×
934
  char **values;
935
  int i, nFields;
936

937
  if ((nFields = msDBFGetFieldCount(dbffile)) == 0) {
×
938
    msSetError(MS_DBFERR, "File contains no data.", "msGetDBFValues()");
×
939
    return (NULL);
×
940
  }
941

942
  values = (char **)malloc(sizeof(char *) * nFields);
×
943
  MS_CHECK_ALLOC(values, sizeof(char *) * nFields, NULL);
×
944

945
  for (i = 0; i < nFields; i++)
×
946
    values[i] = msStrdup(msDBFReadStringAttribute(dbffile, record, i));
×
947

948
  return (values);
949
}
950

951
int *msDBFGetItemIndexes(DBFHandle dbffile, char **items, int numitems) {
3,036✔
952
  int *itemindexes = NULL, i;
953

954
  if (numitems == 0)
3,036✔
955
    return (NULL);
956

957
  itemindexes = (int *)malloc(sizeof(int) * numitems);
3,036✔
958
  MS_CHECK_ALLOC(itemindexes, sizeof(int) * numitems, NULL);
3,036✔
959

960
  for (i = 0; i < numitems; i++) {
23,233✔
961
    itemindexes[i] = msDBFGetItemIndex(dbffile, items[i]);
20,199✔
962
    if (itemindexes[i] == -1) {
20,199✔
963
      free(itemindexes);
2✔
964
      return (NULL); /* item not found */
2✔
965
    }
966
  }
967

968
  return (itemindexes);
969
}
970

971
char **msDBFGetValueList(DBFHandle dbffile, int record, int *itemindexes,
31,916✔
972
                         int numitems) {
973
  const char *value;
974
  char **values = NULL;
975
  int i;
976

977
  if (numitems == 0)
31,916✔
978
    return (NULL);
979

980
  values = (char **)malloc(sizeof(char *) * numitems);
26,264✔
981
  MS_CHECK_ALLOC(values, sizeof(char *) * numitems, NULL);
26,264✔
982

983
  for (i = 0; i < numitems; i++) {
184,857✔
984
    value = msDBFReadStringAttribute(dbffile, record, itemindexes[i]);
158,593✔
985
    if (value == NULL) {
158,593✔
986
      free(values);
×
987
      return NULL; /* Error already reported by msDBFReadStringAttribute() */
×
988
    }
989
    values[i] = msStrdup(value);
158,593✔
990
  }
991

992
  return (values);
993
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc