LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/shape - dbfopen.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 796 959 83.0 %
Date: 2024-05-04 12:52:34 Functions: 40 47 85.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Shapelib
       4             :  * Purpose:  Implementation of .dbf access API documented in dbf_api.html.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2012-2019, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
      12             :  ******************************************************************************/
      13             : 
      14             : #include "shapefil_private.h"
      15             : 
      16             : #include <math.h>
      17             : #include <stdbool.h>
      18             : #include <stdio.h>
      19             : #include <stdlib.h>
      20             : #include <ctype.h>
      21             : #include <string.h>
      22             : 
      23             : #ifdef USE_CPL
      24             : #include "cpl_string.h"
      25             : #else
      26             : 
      27             : #if defined(_MSC_VER)
      28             : #define STRCASECMP(a, b) (_stricmp(a, b))
      29             : #elif defined(_WIN32)
      30             : #define STRCASECMP(a, b) (stricmp(a, b))
      31             : #else
      32             : #include <strings.h>
      33             : #define STRCASECMP(a, b) (strcasecmp(a, b))
      34             : #endif
      35             : 
      36             : #if defined(_MSC_VER)
      37             : #if _MSC_VER < 1900
      38             : #define snprintf _snprintf
      39             : #endif
      40             : #elif defined(_WIN32)
      41             : #ifndef snprintf
      42             : #define snprintf _snprintf
      43             : #endif
      44             : #endif
      45             : 
      46             : #define CPLsprintf sprintf
      47             : #define CPLsnprintf snprintf
      48             : #endif
      49             : 
      50             : #ifndef FALSE
      51             : #define FALSE 0
      52             : #define TRUE 1
      53             : #endif
      54             : 
      55             : /* File header size */
      56             : #define XBASE_FILEHDR_SZ 32
      57             : 
      58             : #define HEADER_RECORD_TERMINATOR 0x0D
      59             : 
      60             : /* See http://www.manmrk.net/tutorials/database/xbase/dbf.html */
      61             : #define END_OF_FILE_CHARACTER 0x1A
      62             : 
      63             : #ifdef USE_CPL
      64        5046 : CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)
      65             : {
      66        5046 : }
      67             : #else
      68             : #define CPL_IGNORE_RET_VAL_INT(x) x
      69             : #endif
      70             : 
      71             : /************************************************************************/
      72             : /*                           DBFWriteHeader()                           */
      73             : /*                                                                      */
      74             : /*      This is called to write out the file header, and field          */
      75             : /*      descriptions before writing any actual data records.  This      */
      76             : /*      also computes all the DBFDataSet field offset/size/decimals     */
      77             : /*      and so forth values.                                            */
      78             : /************************************************************************/
      79             : 
      80        1661 : static void DBFWriteHeader(DBFHandle psDBF)
      81             : {
      82        1661 :     unsigned char abyHeader[XBASE_FILEHDR_SZ] = {0};
      83             : 
      84        1661 :     if (!psDBF->bNoHeader)
      85           0 :         return;
      86             : 
      87        1661 :     psDBF->bNoHeader = FALSE;
      88             : 
      89             :     /* -------------------------------------------------------------------- */
      90             :     /*      Initialize the file header information.                         */
      91             :     /* -------------------------------------------------------------------- */
      92        1661 :     abyHeader[0] = 0x03; /* memo field? - just copying */
      93             : 
      94             :     /* write out update date */
      95        1661 :     abyHeader[1] = STATIC_CAST(unsigned char, psDBF->nUpdateYearSince1900);
      96        1661 :     abyHeader[2] = STATIC_CAST(unsigned char, psDBF->nUpdateMonth);
      97        1661 :     abyHeader[3] = STATIC_CAST(unsigned char, psDBF->nUpdateDay);
      98             : 
      99             :     /* record count preset at zero */
     100             : 
     101        1661 :     abyHeader[8] = STATIC_CAST(unsigned char, psDBF->nHeaderLength % 256);
     102        1661 :     abyHeader[9] = STATIC_CAST(unsigned char, psDBF->nHeaderLength / 256);
     103             : 
     104        1661 :     abyHeader[10] = STATIC_CAST(unsigned char, psDBF->nRecordLength % 256);
     105        1661 :     abyHeader[11] = STATIC_CAST(unsigned char, psDBF->nRecordLength / 256);
     106             : 
     107        1661 :     abyHeader[29] = STATIC_CAST(unsigned char, psDBF->iLanguageDriver);
     108             : 
     109             :     /* -------------------------------------------------------------------- */
     110             :     /*      Write the initial 32 byte file header, and all the field        */
     111             :     /*      descriptions.                                                   */
     112             :     /* -------------------------------------------------------------------- */
     113        1661 :     psDBF->sHooks.FSeek(psDBF->fp, 0, 0);
     114        1661 :     psDBF->sHooks.FWrite(abyHeader, XBASE_FILEHDR_SZ, 1, psDBF->fp);
     115        1661 :     psDBF->sHooks.FWrite(psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields,
     116             :                          psDBF->fp);
     117             : 
     118             :     /* -------------------------------------------------------------------- */
     119             :     /*      Write out the newline character if there is room for it.        */
     120             :     /* -------------------------------------------------------------------- */
     121        1661 :     if (psDBF->nHeaderLength >
     122        1661 :         XBASE_FLDHDR_SZ * psDBF->nFields + XBASE_FLDHDR_SZ)
     123             :     {
     124        1661 :         char cNewline = HEADER_RECORD_TERMINATOR;
     125        1661 :         psDBF->sHooks.FWrite(&cNewline, 1, 1, psDBF->fp);
     126             :     }
     127             : 
     128             :     /* -------------------------------------------------------------------- */
     129             :     /*      If the file is new, add a EOF character.                        */
     130             :     /* -------------------------------------------------------------------- */
     131        1661 :     if (psDBF->nRecords == 0 && psDBF->bWriteEndOfFileChar)
     132             :     {
     133        1577 :         char ch = END_OF_FILE_CHARACTER;
     134             : 
     135        1577 :         psDBF->sHooks.FWrite(&ch, 1, 1, psDBF->fp);
     136             :     }
     137             : }
     138             : 
     139             : /************************************************************************/
     140             : /*                           DBFFlushRecord()                           */
     141             : /*                                                                      */
     142             : /*      Write out the current record if there is one.                   */
     143             : /************************************************************************/
     144             : 
     145      183806 : static bool DBFFlushRecord(DBFHandle psDBF)
     146             : {
     147      183806 :     if (psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1)
     148             :     {
     149       77604 :         psDBF->bCurrentRecordModified = FALSE;
     150             : 
     151       77604 :         const SAOffset nRecordOffset =
     152       77604 :             psDBF->nRecordLength *
     153       77604 :                 STATIC_CAST(SAOffset, psDBF->nCurrentRecord) +
     154       77604 :             psDBF->nHeaderLength;
     155             : 
     156             :         /* -------------------------------------------------------------------- */
     157             :         /*      Guard FSeek with check for whether we're already at position;   */
     158             :         /*      no-op FSeeks defeat network filesystems' write buffering.       */
     159             :         /* -------------------------------------------------------------------- */
     160      153342 :         if (psDBF->bRequireNextWriteSeek ||
     161       75738 :             psDBF->sHooks.FTell(psDBF->fp) != nRecordOffset)
     162             :         {
     163       77599 :             if (psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0) != 0)
     164             :             {
     165             :                 char szMessage[128];
     166           0 :                 snprintf(
     167             :                     szMessage, sizeof(szMessage),
     168             :                     "Failure seeking to position before writing DBF record %d.",
     169             :                     psDBF->nCurrentRecord);
     170           0 :                 psDBF->sHooks.Error(szMessage);
     171           0 :                 return false;
     172             :             }
     173             :         }
     174             : 
     175       77604 :         if (psDBF->sHooks.FWrite(psDBF->pszCurrentRecord, psDBF->nRecordLength,
     176       77604 :                                  1, psDBF->fp) != 1)
     177             :         {
     178             :             char szMessage[128];
     179           0 :             snprintf(szMessage, sizeof(szMessage),
     180             :                      "Failure writing DBF record %d.", psDBF->nCurrentRecord);
     181           0 :             psDBF->sHooks.Error(szMessage);
     182           0 :             return false;
     183             :         }
     184             : 
     185             :         /* -------------------------------------------------------------------- */
     186             :         /*      If next op is also a write, allow possible skipping of FSeek.   */
     187             :         /* -------------------------------------------------------------------- */
     188       77604 :         psDBF->bRequireNextWriteSeek = FALSE;
     189             : 
     190       77604 :         if (psDBF->nCurrentRecord == psDBF->nRecords - 1)
     191             :         {
     192       77382 :             if (psDBF->bWriteEndOfFileChar)
     193             :             {
     194       77369 :                 char ch = END_OF_FILE_CHARACTER;
     195       77369 :                 psDBF->sHooks.FWrite(&ch, 1, 1, psDBF->fp);
     196             :             }
     197             :         }
     198             :     }
     199             : 
     200      183806 :     return true;
     201             : }
     202             : 
     203             : /************************************************************************/
     204             : /*                           DBFLoadRecord()                            */
     205             : /************************************************************************/
     206             : 
     207      499835 : static bool DBFLoadRecord(DBFHandle psDBF, int iRecord)
     208             : {
     209      499835 :     if (psDBF->nCurrentRecord != iRecord)
     210             :     {
     211       94357 :         if (!DBFFlushRecord(psDBF))
     212           0 :             return false;
     213             : 
     214       94357 :         const SAOffset nRecordOffset =
     215       94357 :             psDBF->nRecordLength * STATIC_CAST(SAOffset, iRecord) +
     216       94357 :             psDBF->nHeaderLength;
     217             : 
     218       94357 :         if (psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, SEEK_SET) != 0)
     219             :         {
     220             :             char szMessage[128];
     221           0 :             snprintf(szMessage, sizeof(szMessage),
     222             :                      "fseek(%ld) failed on DBF file.",
     223             :                      STATIC_CAST(long, nRecordOffset));
     224           0 :             psDBF->sHooks.Error(szMessage);
     225           0 :             return false;
     226             :         }
     227             : 
     228       94357 :         if (psDBF->sHooks.FRead(psDBF->pszCurrentRecord, psDBF->nRecordLength,
     229       94357 :                                 1, psDBF->fp) != 1)
     230             :         {
     231             :             char szMessage[128];
     232           1 :             snprintf(szMessage, sizeof(szMessage),
     233             :                      "fread(%d) failed on DBF file.", psDBF->nRecordLength);
     234           1 :             psDBF->sHooks.Error(szMessage);
     235           1 :             return false;
     236             :         }
     237             : 
     238       94356 :         psDBF->nCurrentRecord = iRecord;
     239             :         /* -------------------------------------------------------------------- */
     240             :         /*      Require a seek for next write in case of mixed R/W operations.  */
     241             :         /* -------------------------------------------------------------------- */
     242       94356 :         psDBF->bRequireNextWriteSeek = TRUE;
     243             :     }
     244             : 
     245      499834 :     return true;
     246             : }
     247             : 
     248             : /************************************************************************/
     249             : /*                          DBFUpdateHeader()                           */
     250             : /************************************************************************/
     251             : 
     252        1968 : void SHPAPI_CALL DBFUpdateHeader(DBFHandle psDBF)
     253             : {
     254        1968 :     if (psDBF->bNoHeader)
     255          78 :         DBFWriteHeader(psDBF);
     256             : 
     257        1968 :     if (!DBFFlushRecord(psDBF))
     258           0 :         return;
     259             : 
     260        1968 :     psDBF->sHooks.FSeek(psDBF->fp, 0, 0);
     261             : 
     262        1968 :     unsigned char abyFileHeader[XBASE_FILEHDR_SZ] = {0};
     263        1968 :     psDBF->sHooks.FRead(abyFileHeader, 1, sizeof(abyFileHeader), psDBF->fp);
     264             : 
     265        1968 :     abyFileHeader[1] = STATIC_CAST(unsigned char, psDBF->nUpdateYearSince1900);
     266        1968 :     abyFileHeader[2] = STATIC_CAST(unsigned char, psDBF->nUpdateMonth);
     267        1968 :     abyFileHeader[3] = STATIC_CAST(unsigned char, psDBF->nUpdateDay);
     268        1968 :     abyFileHeader[4] = STATIC_CAST(unsigned char, psDBF->nRecords & 0xFF);
     269        1968 :     abyFileHeader[5] =
     270        1968 :         STATIC_CAST(unsigned char, (psDBF->nRecords >> 8) & 0xFF);
     271        1968 :     abyFileHeader[6] =
     272        1968 :         STATIC_CAST(unsigned char, (psDBF->nRecords >> 16) & 0xFF);
     273        1968 :     abyFileHeader[7] =
     274        1968 :         STATIC_CAST(unsigned char, (psDBF->nRecords >> 24) & 0xFF);
     275             : 
     276        1968 :     psDBF->sHooks.FSeek(psDBF->fp, 0, 0);
     277        1968 :     psDBF->sHooks.FWrite(abyFileHeader, sizeof(abyFileHeader), 1, psDBF->fp);
     278             : 
     279        1968 :     psDBF->sHooks.FFlush(psDBF->fp);
     280             : }
     281             : 
     282             : /************************************************************************/
     283             : /*                       DBFSetLastModifiedDate()                       */
     284             : /************************************************************************/
     285             : 
     286       10071 : void SHPAPI_CALL DBFSetLastModifiedDate(DBFHandle psDBF, int nYYSince1900,
     287             :                                         int nMM, int nDD)
     288             : {
     289       10071 :     psDBF->nUpdateYearSince1900 = nYYSince1900;
     290       10071 :     psDBF->nUpdateMonth = nMM;
     291       10071 :     psDBF->nUpdateDay = nDD;
     292       10071 : }
     293             : 
     294             : /************************************************************************/
     295             : /*                              DBFOpen()                               */
     296             : /*                                                                      */
     297             : /*      Open a .dbf file.                                               */
     298             : /************************************************************************/
     299             : 
     300          24 : DBFHandle SHPAPI_CALL DBFOpen(const char *pszFilename, const char *pszAccess)
     301             : {
     302             :     SAHooks sHooks;
     303             : 
     304          24 :     SASetupDefaultHooks(&sHooks);
     305             : 
     306          48 :     return DBFOpenLL(pszFilename, pszAccess, &sHooks);
     307             : }
     308             : 
     309             : /************************************************************************/
     310             : /*                      DBFGetLenWithoutExtension()                     */
     311             : /************************************************************************/
     312             : 
     313        5096 : static int DBFGetLenWithoutExtension(const char *pszBasename)
     314             : {
     315        5096 :     const int nLen = STATIC_CAST(int, strlen(pszBasename));
     316       20384 :     for (int i = nLen - 1;
     317       20384 :          i > 0 && pszBasename[i] != '/' && pszBasename[i] != '\\'; i--)
     318             :     {
     319       20384 :         if (pszBasename[i] == '.')
     320             :         {
     321        5096 :             return i;
     322             :         }
     323             :     }
     324           0 :     return nLen;
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                              DBFOpen()                               */
     329             : /*                                                                      */
     330             : /*      Open a .dbf file.                                               */
     331             : /************************************************************************/
     332             : 
     333        3512 : DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess,
     334             :                                 const SAHooks *psHooks)
     335             : {
     336             :     /* -------------------------------------------------------------------- */
     337             :     /*      We only allow the access strings "rb" and "r+".                  */
     338             :     /* -------------------------------------------------------------------- */
     339        3512 :     if (strcmp(pszAccess, "r") != 0 && strcmp(pszAccess, "r+") != 0 &&
     340          24 :         strcmp(pszAccess, "rb") != 0 && strcmp(pszAccess, "rb+") != 0 &&
     341           0 :         strcmp(pszAccess, "r+b") != 0)
     342           0 :         return SHPLIB_NULLPTR;
     343             : 
     344        3512 :     if (strcmp(pszAccess, "r") == 0)
     345        1827 :         pszAccess = "rb";
     346             : 
     347        3512 :     if (strcmp(pszAccess, "r+") == 0)
     348        1661 :         pszAccess = "rb+";
     349             : 
     350             :     /* -------------------------------------------------------------------- */
     351             :     /*      Compute the base (layer) name.  If there is any extension       */
     352             :     /*      on the passed in filename we will strip it off.                 */
     353             :     /* -------------------------------------------------------------------- */
     354        3512 :     const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
     355        3512 :     char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
     356        3512 :     memcpy(pszFullname, pszFilename, nLenWithoutExtension);
     357        3512 :     memcpy(pszFullname + nLenWithoutExtension, ".dbf", 5);
     358             : 
     359        3512 :     DBFHandle psDBF = STATIC_CAST(DBFHandle, calloc(1, sizeof(DBFInfo)));
     360        3512 :     psDBF->fp = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData);
     361        3512 :     memcpy(&(psDBF->sHooks), psHooks, sizeof(SAHooks));
     362             : 
     363        3512 :     if (psDBF->fp == SHPLIB_NULLPTR)
     364             :     {
     365          46 :         memcpy(pszFullname + nLenWithoutExtension, ".DBF", 5);
     366          46 :         psDBF->fp =
     367          46 :             psDBF->sHooks.FOpen(pszFullname, pszAccess, psHooks->pvUserData);
     368             :     }
     369             : 
     370        3512 :     memcpy(pszFullname + nLenWithoutExtension, ".cpg", 5);
     371        3512 :     SAFile pfCPG = psHooks->FOpen(pszFullname, "r", psHooks->pvUserData);
     372        3512 :     if (pfCPG == SHPLIB_NULLPTR)
     373             :     {
     374        3500 :         memcpy(pszFullname + nLenWithoutExtension, ".CPG", 5);
     375        3500 :         pfCPG = psHooks->FOpen(pszFullname, "r", psHooks->pvUserData);
     376             :     }
     377             : 
     378        3512 :     free(pszFullname);
     379             : 
     380        3512 :     if (psDBF->fp == SHPLIB_NULLPTR)
     381             :     {
     382          42 :         free(psDBF);
     383          42 :         if (pfCPG)
     384           0 :             psHooks->FClose(pfCPG);
     385          42 :         return SHPLIB_NULLPTR;
     386             :     }
     387             : 
     388        3470 :     psDBF->bNoHeader = FALSE;
     389        3470 :     psDBF->nCurrentRecord = -1;
     390        3470 :     psDBF->bCurrentRecordModified = FALSE;
     391             : 
     392             :     /* -------------------------------------------------------------------- */
     393             :     /*  Read Table Header info                                              */
     394             :     /* -------------------------------------------------------------------- */
     395        3470 :     const int nBufSize = 500;
     396        3470 :     unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(nBufSize));
     397        3470 :     if (psDBF->sHooks.FRead(pabyBuf, XBASE_FILEHDR_SZ, 1, psDBF->fp) != 1)
     398             :     {
     399           0 :         psDBF->sHooks.FClose(psDBF->fp);
     400           0 :         if (pfCPG)
     401           0 :             psDBF->sHooks.FClose(pfCPG);
     402           0 :         free(pabyBuf);
     403           0 :         free(psDBF);
     404           0 :         return SHPLIB_NULLPTR;
     405             :     }
     406             : 
     407        3470 :     DBFSetLastModifiedDate(psDBF, pabyBuf[1], pabyBuf[2], pabyBuf[3]);
     408             : 
     409        3470 :     psDBF->nRecords = pabyBuf[4] | (pabyBuf[5] << 8) | (pabyBuf[6] << 16) |
     410        3470 :                       ((pabyBuf[7] & 0x7f) << 24);
     411             : 
     412        3470 :     const int nHeadLen = pabyBuf[8] | (pabyBuf[9] << 8);
     413        3470 :     psDBF->nHeaderLength = nHeadLen;
     414        3470 :     psDBF->nRecordLength = pabyBuf[10] | (pabyBuf[11] << 8);
     415        3470 :     psDBF->iLanguageDriver = pabyBuf[29];
     416             : 
     417        3470 :     if (psDBF->nRecordLength == 0 || nHeadLen < XBASE_FILEHDR_SZ)
     418             :     {
     419           0 :         psDBF->sHooks.FClose(psDBF->fp);
     420           0 :         if (pfCPG)
     421           0 :             psDBF->sHooks.FClose(pfCPG);
     422           0 :         free(pabyBuf);
     423           0 :         free(psDBF);
     424           0 :         return SHPLIB_NULLPTR;
     425             :     }
     426             : 
     427        3470 :     const int nFields = (nHeadLen - XBASE_FILEHDR_SZ) / XBASE_FLDHDR_SZ;
     428        3470 :     psDBF->nFields = nFields;
     429             : 
     430             :     /* coverity[tainted_data] */
     431        3470 :     psDBF->pszCurrentRecord = STATIC_CAST(char *, malloc(psDBF->nRecordLength));
     432             : 
     433             :     /* -------------------------------------------------------------------- */
     434             :     /*  Figure out the code page from the LDID and CPG                      */
     435             :     /* -------------------------------------------------------------------- */
     436        3470 :     psDBF->pszCodePage = SHPLIB_NULLPTR;
     437        3470 :     if (pfCPG)
     438             :     {
     439          14 :         memset(pabyBuf, 0, nBufSize);
     440          14 :         psDBF->sHooks.FRead(pabyBuf, 1, nBufSize - 1, pfCPG);
     441          14 :         const size_t n = strcspn(REINTERPRET_CAST(char *, pabyBuf), "\n\r");
     442          14 :         if (n > 0)
     443             :         {
     444          14 :             pabyBuf[n] = '\0';
     445          14 :             psDBF->pszCodePage = STATIC_CAST(char *, malloc(n + 1));
     446          14 :             memcpy(psDBF->pszCodePage, pabyBuf, n + 1);
     447             :         }
     448          14 :         psDBF->sHooks.FClose(pfCPG);
     449             :     }
     450        3470 :     if (psDBF->pszCodePage == SHPLIB_NULLPTR && pabyBuf[29] != 0)
     451             :     {
     452        2771 :         snprintf(REINTERPRET_CAST(char *, pabyBuf), nBufSize, "LDID/%d",
     453             :                  psDBF->iLanguageDriver);
     454        2771 :         psDBF->pszCodePage = STATIC_CAST(
     455             :             char *, malloc(strlen(REINTERPRET_CAST(char *, pabyBuf)) + 1));
     456        2771 :         strcpy(psDBF->pszCodePage, REINTERPRET_CAST(char *, pabyBuf));
     457             :     }
     458             : 
     459             :     /* -------------------------------------------------------------------- */
     460             :     /*  Read in Field Definitions                                           */
     461             :     /* -------------------------------------------------------------------- */
     462        3470 :     pabyBuf = STATIC_CAST(unsigned char *, realloc(pabyBuf, nHeadLen));
     463        3470 :     psDBF->pszHeader = REINTERPRET_CAST(char *, pabyBuf);
     464             : 
     465        3470 :     psDBF->sHooks.FSeek(psDBF->fp, XBASE_FILEHDR_SZ, 0);
     466        3470 :     if (psDBF->sHooks.FRead(pabyBuf, nHeadLen - XBASE_FILEHDR_SZ, 1,
     467        3470 :                             psDBF->fp) != 1)
     468             :     {
     469           0 :         psDBF->sHooks.FClose(psDBF->fp);
     470           0 :         free(pabyBuf);
     471           0 :         free(psDBF->pszCurrentRecord);
     472           0 :         free(psDBF->pszCodePage);
     473           0 :         free(psDBF);
     474           0 :         return SHPLIB_NULLPTR;
     475             :     }
     476             : 
     477        3470 :     psDBF->panFieldOffset = STATIC_CAST(int *, malloc(sizeof(int) * nFields));
     478        3470 :     psDBF->panFieldSize = STATIC_CAST(int *, malloc(sizeof(int) * nFields));
     479        3470 :     psDBF->panFieldDecimals = STATIC_CAST(int *, malloc(sizeof(int) * nFields));
     480        3470 :     psDBF->pachFieldType = STATIC_CAST(char *, malloc(sizeof(char) * nFields));
     481             : 
     482        9938 :     for (int iField = 0; iField < nFields; iField++)
     483             :     {
     484        6469 :         unsigned char *pabyFInfo = pabyBuf + iField * XBASE_FLDHDR_SZ;
     485        6469 :         if (pabyFInfo[0] == HEADER_RECORD_TERMINATOR)
     486             :         {
     487           1 :             psDBF->nFields = iField;
     488           1 :             break;
     489             :         }
     490             : 
     491        6468 :         if (pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F')
     492             :         {
     493        3254 :             psDBF->panFieldSize[iField] = pabyFInfo[16];
     494        3254 :             psDBF->panFieldDecimals[iField] = pabyFInfo[17];
     495             :         }
     496             :         else
     497             :         {
     498        3214 :             psDBF->panFieldSize[iField] = pabyFInfo[16];
     499        3214 :             psDBF->panFieldDecimals[iField] = 0;
     500             : 
     501             :             /*
     502             :             ** The following seemed to be used sometimes to handle files with
     503             :             long
     504             :             ** string fields, but in other cases (such as bug 1202) the decimals
     505             :             field
     506             :             ** just seems to indicate some sort of preferred formatting, not
     507             :             very
     508             :             ** wide fields.  So I have disabled this code.  FrankW.
     509             :                     psDBF->panFieldSize[iField] = pabyFInfo[16] +
     510             :             pabyFInfo[17]*256; psDBF->panFieldDecimals[iField] = 0;
     511             :             */
     512             :         }
     513             : 
     514        6468 :         psDBF->pachFieldType[iField] = STATIC_CAST(char, pabyFInfo[11]);
     515        6468 :         if (iField == 0)
     516        3449 :             psDBF->panFieldOffset[iField] = 1;
     517             :         else
     518        3019 :             psDBF->panFieldOffset[iField] = psDBF->panFieldOffset[iField - 1] +
     519        3019 :                                             psDBF->panFieldSize[iField - 1];
     520             :     }
     521             : 
     522             :     /* Check that the total width of fields does not exceed the record width */
     523        3470 :     if (psDBF->nFields > 0 && psDBF->panFieldOffset[psDBF->nFields - 1] +
     524        3449 :                                       psDBF->panFieldSize[psDBF->nFields - 1] >
     525        3449 :                                   psDBF->nRecordLength)
     526             :     {
     527           0 :         DBFClose(psDBF);
     528           0 :         return SHPLIB_NULLPTR;
     529             :     }
     530             : 
     531        3470 :     DBFSetWriteEndOfFileChar(psDBF, TRUE);
     532             : 
     533        3470 :     psDBF->bRequireNextWriteSeek = TRUE;
     534             : 
     535        3470 :     return (psDBF);
     536             : }
     537             : 
     538             : /************************************************************************/
     539             : /*                              DBFClose()                              */
     540             : /************************************************************************/
     541             : 
     542        5046 : void SHPAPI_CALL DBFClose(DBFHandle psDBF)
     543             : {
     544        5046 :     if (psDBF == SHPLIB_NULLPTR)
     545           0 :         return;
     546             : 
     547             :     /* -------------------------------------------------------------------- */
     548             :     /*      Write out header if not already written.                        */
     549             :     /* -------------------------------------------------------------------- */
     550        5046 :     if (psDBF->bNoHeader)
     551          65 :         DBFWriteHeader(psDBF);
     552             : 
     553        5046 :     CPL_IGNORE_RET_VAL_INT(DBFFlushRecord(psDBF));
     554             : 
     555             :     /* -------------------------------------------------------------------- */
     556             :     /*      Update last access date, and number of records if we have       */
     557             :     /*      write access.                                                   */
     558             :     /* -------------------------------------------------------------------- */
     559        5046 :     if (psDBF->bUpdated)
     560        1638 :         DBFUpdateHeader(psDBF);
     561             : 
     562             :     /* -------------------------------------------------------------------- */
     563             :     /*      Close, and free resources.                                      */
     564             :     /* -------------------------------------------------------------------- */
     565        5046 :     psDBF->sHooks.FClose(psDBF->fp);
     566             : 
     567        5046 :     if (psDBF->panFieldOffset != SHPLIB_NULLPTR)
     568             :     {
     569        5000 :         free(psDBF->panFieldOffset);
     570        5000 :         free(psDBF->panFieldSize);
     571        5000 :         free(psDBF->panFieldDecimals);
     572        5000 :         free(psDBF->pachFieldType);
     573             :     }
     574             : 
     575        5046 :     if (psDBF->pszWorkField != SHPLIB_NULLPTR)
     576        1996 :         free(psDBF->pszWorkField);
     577             : 
     578        5046 :     free(psDBF->pszHeader);
     579        5046 :     free(psDBF->pszCurrentRecord);
     580        5046 :     free(psDBF->pszCodePage);
     581             : 
     582        5046 :     free(psDBF);
     583             : }
     584             : 
     585             : /************************************************************************/
     586             : /*                             DBFCreate()                              */
     587             : /*                                                                      */
     588             : /* Create a new .dbf file with default code page LDID/87 (0x57)         */
     589             : /************************************************************************/
     590             : 
     591           1 : DBFHandle SHPAPI_CALL DBFCreate(const char *pszFilename)
     592             : {
     593           1 :     return DBFCreateEx(pszFilename, "LDID/87");  // 0x57
     594             : }
     595             : 
     596             : /************************************************************************/
     597             : /*                            DBFCreateEx()                             */
     598             : /*                                                                      */
     599             : /*      Create a new .dbf file.                                         */
     600             : /************************************************************************/
     601             : 
     602           1 : DBFHandle SHPAPI_CALL DBFCreateEx(const char *pszFilename,
     603             :                                   const char *pszCodePage)
     604             : {
     605             :     SAHooks sHooks;
     606             : 
     607           1 :     SASetupDefaultHooks(&sHooks);
     608             : 
     609           2 :     return DBFCreateLL(pszFilename, pszCodePage, &sHooks);
     610             : }
     611             : 
     612             : /************************************************************************/
     613             : /*                             DBFCreate()                              */
     614             : /*                                                                      */
     615             : /*      Create a new .dbf file.                                         */
     616             : /************************************************************************/
     617             : 
     618        1584 : DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename,
     619             :                                   const char *pszCodePage,
     620             :                                   const SAHooks *psHooks)
     621             : {
     622             :     /* -------------------------------------------------------------------- */
     623             :     /*      Compute the base (layer) name.  If there is any extension       */
     624             :     /*      on the passed in filename we will strip it off.                 */
     625             :     /* -------------------------------------------------------------------- */
     626        1584 :     const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
     627        1584 :     char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
     628        1584 :     memcpy(pszFullname, pszFilename, nLenWithoutExtension);
     629        1584 :     memcpy(pszFullname + nLenWithoutExtension, ".dbf", 5);
     630             : 
     631             :     /* -------------------------------------------------------------------- */
     632             :     /*      Create the file.                                                */
     633             :     /* -------------------------------------------------------------------- */
     634        1584 :     SAFile fp = psHooks->FOpen(pszFullname, "wb+", psHooks->pvUserData);
     635        1584 :     if (fp == SHPLIB_NULLPTR)
     636             :     {
     637           1 :         free(pszFullname);
     638           1 :         return SHPLIB_NULLPTR;
     639             :     }
     640             : 
     641        1583 :     memcpy(pszFullname + nLenWithoutExtension, ".cpg", 5);
     642        1583 :     int ldid = -1;
     643        1583 :     if (pszCodePage != SHPLIB_NULLPTR)
     644             :     {
     645        1579 :         if (strncmp(pszCodePage, "LDID/", 5) == 0)
     646             :         {
     647        1573 :             ldid = atoi(pszCodePage + 5);
     648        1573 :             if (ldid > 255)
     649           0 :                 ldid = -1;  // don't use 0 to indicate out of range as LDID/0 is
     650             :                             // a valid one
     651             :         }
     652        1579 :         if (ldid < 0)
     653             :         {
     654             :             SAFile fpCPG =
     655           6 :                 psHooks->FOpen(pszFullname, "w", psHooks->pvUserData);
     656           6 :             psHooks->FWrite(
     657             :                 CONST_CAST(void *, STATIC_CAST(const void *, pszCodePage)),
     658           6 :                 strlen(pszCodePage), 1, fpCPG);
     659           6 :             psHooks->FClose(fpCPG);
     660             :         }
     661             :     }
     662        1583 :     if (pszCodePage == SHPLIB_NULLPTR || ldid >= 0)
     663             :     {
     664        1577 :         psHooks->Remove(pszFullname, psHooks->pvUserData);
     665             :     }
     666             : 
     667        1583 :     free(pszFullname);
     668             : 
     669             :     /* -------------------------------------------------------------------- */
     670             :     /*      Create the info structure.                                      */
     671             :     /* -------------------------------------------------------------------- */
     672        1583 :     DBFHandle psDBF = STATIC_CAST(DBFHandle, calloc(1, sizeof(DBFInfo)));
     673             : 
     674        1583 :     memcpy(&(psDBF->sHooks), psHooks, sizeof(SAHooks));
     675        1583 :     psDBF->fp = fp;
     676        1583 :     psDBF->nRecords = 0;
     677        1583 :     psDBF->nFields = 0;
     678        1583 :     psDBF->nRecordLength = 1;
     679        1583 :     psDBF->nHeaderLength =
     680             :         XBASE_FILEHDR_SZ + 1; /* + 1 for HEADER_RECORD_TERMINATOR */
     681             : 
     682        1583 :     psDBF->panFieldOffset = SHPLIB_NULLPTR;
     683        1583 :     psDBF->panFieldSize = SHPLIB_NULLPTR;
     684        1583 :     psDBF->panFieldDecimals = SHPLIB_NULLPTR;
     685        1583 :     psDBF->pachFieldType = SHPLIB_NULLPTR;
     686        1583 :     psDBF->pszHeader = SHPLIB_NULLPTR;
     687             : 
     688        1583 :     psDBF->nCurrentRecord = -1;
     689        1583 :     psDBF->bCurrentRecordModified = FALSE;
     690        1583 :     psDBF->pszCurrentRecord = SHPLIB_NULLPTR;
     691             : 
     692        1583 :     psDBF->bNoHeader = TRUE;
     693             : 
     694        1583 :     psDBF->iLanguageDriver = ldid > 0 ? ldid : 0;
     695        1583 :     psDBF->pszCodePage = SHPLIB_NULLPTR;
     696        1583 :     if (pszCodePage)
     697             :     {
     698        1579 :         psDBF->pszCodePage =
     699        1579 :             STATIC_CAST(char *, malloc(strlen(pszCodePage) + 1));
     700        1579 :         strcpy(psDBF->pszCodePage, pszCodePage);
     701             :     }
     702        1583 :     DBFSetLastModifiedDate(psDBF, 95, 7, 26); /* dummy date */
     703             : 
     704        1583 :     DBFSetWriteEndOfFileChar(psDBF, TRUE);
     705             : 
     706        1583 :     psDBF->bRequireNextWriteSeek = TRUE;
     707             : 
     708        1583 :     return (psDBF);
     709             : }
     710             : 
     711             : /************************************************************************/
     712             : /*                            DBFAddField()                             */
     713             : /*                                                                      */
     714             : /*      Add a field to a newly created .dbf or to an existing one       */
     715             : /************************************************************************/
     716             : 
     717         230 : int SHPAPI_CALL DBFAddField(DBFHandle psDBF, const char *pszFieldName,
     718             :                             DBFFieldType eType, int nWidth, int nDecimals)
     719             : {
     720             :     char chNativeType;
     721             : 
     722         230 :     if (eType == FTLogical)
     723           0 :         chNativeType = 'L';
     724         230 :     else if (eType == FTDate)
     725           0 :         chNativeType = 'D';
     726         230 :     else if (eType == FTString)
     727           0 :         chNativeType = 'C';
     728             :     else
     729         230 :         chNativeType = 'N';
     730             : 
     731         230 :     return DBFAddNativeFieldType(psDBF, pszFieldName, chNativeType, nWidth,
     732         230 :                                  nDecimals);
     733             : }
     734             : 
     735             : /************************************************************************/
     736             : /*                        DBFGetNullCharacter()                         */
     737             : /************************************************************************/
     738             : 
     739         642 : static char DBFGetNullCharacter(char chType)
     740             : {
     741         642 :     switch (chType)
     742             :     {
     743          77 :         case 'N':
     744             :         case 'F':
     745          77 :             return '*';
     746          29 :         case 'D':
     747          29 :             return '0';
     748           1 :         case 'L':
     749           1 :             return '?';
     750         535 :         default:
     751         535 :             return ' ';
     752             :     }
     753             : }
     754             : 
     755             : /************************************************************************/
     756             : /*                            DBFAddField()                             */
     757             : /*                                                                      */
     758             : /*      Add a field to a newly created .dbf file before any records     */
     759             : /*      are written.                                                    */
     760             : /************************************************************************/
     761             : 
     762        5023 : int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName,
     763             :                                       char chType, int nWidth, int nDecimals)
     764             : {
     765             :     /* make sure that everything is written in .dbf */
     766        5023 :     if (!DBFFlushRecord(psDBF))
     767           0 :         return -1;
     768             : 
     769        5023 :     if (psDBF->nHeaderLength + XBASE_FLDHDR_SZ > 65535)
     770             :     {
     771             :         char szMessage[128];
     772           0 :         snprintf(szMessage, sizeof(szMessage),
     773             :                  "Cannot add field %s. Header length limit reached "
     774             :                  "(max 65535 bytes, 2046 fields).",
     775             :                  pszFieldName);
     776           0 :         psDBF->sHooks.Error(szMessage);
     777           0 :         return -1;
     778             :     }
     779             : 
     780             :     /* -------------------------------------------------------------------- */
     781             :     /*      Do some checking to ensure we can add records to this file.     */
     782             :     /* -------------------------------------------------------------------- */
     783        5023 :     if (nWidth < 1)
     784           0 :         return -1;
     785             : 
     786        5023 :     if (nWidth > XBASE_FLD_MAX_WIDTH)
     787           0 :         nWidth = XBASE_FLD_MAX_WIDTH;
     788             : 
     789        5023 :     if (psDBF->nRecordLength + nWidth > 65535)
     790             :     {
     791             :         char szMessage[128];
     792           1 :         snprintf(szMessage, sizeof(szMessage),
     793             :                  "Cannot add field %s. Record length limit reached "
     794             :                  "(max 65535 bytes).",
     795             :                  pszFieldName);
     796           1 :         psDBF->sHooks.Error(szMessage);
     797           1 :         return -1;
     798             :     }
     799             : 
     800        5022 :     const int nOldRecordLength = psDBF->nRecordLength;
     801        5022 :     const int nOldHeaderLength = psDBF->nHeaderLength;
     802             : 
     803             :     /* -------------------------------------------------------------------- */
     804             :     /*      realloc all the arrays larger to hold the additional field      */
     805             :     /*      information.                                                    */
     806             :     /* -------------------------------------------------------------------- */
     807        5022 :     psDBF->nFields++;
     808             : 
     809        5022 :     psDBF->panFieldOffset = STATIC_CAST(
     810             :         int *, realloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields));
     811             : 
     812        5022 :     psDBF->panFieldSize = STATIC_CAST(
     813             :         int *, realloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields));
     814             : 
     815        5022 :     psDBF->panFieldDecimals = STATIC_CAST(
     816             :         int *, realloc(psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields));
     817             : 
     818        5022 :     psDBF->pachFieldType = STATIC_CAST(
     819             :         char *, realloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields));
     820             : 
     821             :     /* -------------------------------------------------------------------- */
     822             :     /*      Assign the new field information fields.                        */
     823             :     /* -------------------------------------------------------------------- */
     824        5022 :     psDBF->panFieldOffset[psDBF->nFields - 1] = psDBF->nRecordLength;
     825        5022 :     psDBF->nRecordLength += nWidth;
     826        5022 :     psDBF->panFieldSize[psDBF->nFields - 1] = nWidth;
     827        5022 :     psDBF->panFieldDecimals[psDBF->nFields - 1] = nDecimals;
     828        5022 :     psDBF->pachFieldType[psDBF->nFields - 1] = chType;
     829             : 
     830             :     /* -------------------------------------------------------------------- */
     831             :     /*      Extend the required header information.                         */
     832             :     /* -------------------------------------------------------------------- */
     833        5022 :     psDBF->nHeaderLength += XBASE_FLDHDR_SZ;
     834        5022 :     psDBF->bUpdated = FALSE;
     835             : 
     836        5022 :     psDBF->pszHeader = STATIC_CAST(
     837             :         char *, realloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ));
     838             : 
     839        5022 :     char *pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * (psDBF->nFields - 1);
     840             : 
     841      165726 :     for (int i = 0; i < XBASE_FLDHDR_SZ; i++)
     842      160704 :         pszFInfo[i] = '\0';
     843             : 
     844        5022 :     strncpy(pszFInfo, pszFieldName, XBASE_FLDNAME_LEN_WRITE);
     845             : 
     846        5022 :     pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields - 1];
     847             : 
     848        5022 :     if (chType == 'C')
     849             :     {
     850        2364 :         pszFInfo[16] = STATIC_CAST(unsigned char, nWidth % 256);
     851        2364 :         pszFInfo[17] = STATIC_CAST(unsigned char, nWidth / 256);
     852             :     }
     853             :     else
     854             :     {
     855        2658 :         pszFInfo[16] = STATIC_CAST(unsigned char, nWidth);
     856        2658 :         pszFInfo[17] = STATIC_CAST(unsigned char, nDecimals);
     857             :     }
     858             : 
     859             :     /* -------------------------------------------------------------------- */
     860             :     /*      Make the current record buffer appropriately larger.            */
     861             :     /* -------------------------------------------------------------------- */
     862        5022 :     psDBF->pszCurrentRecord = STATIC_CAST(
     863             :         char *, realloc(psDBF->pszCurrentRecord, psDBF->nRecordLength));
     864             : 
     865             :     /* we're done if dealing with new .dbf */
     866        5022 :     if (psDBF->bNoHeader)
     867        4993 :         return (psDBF->nFields - 1);
     868             : 
     869             :     /* -------------------------------------------------------------------- */
     870             :     /*      For existing .dbf file, shift records                           */
     871             :     /* -------------------------------------------------------------------- */
     872             : 
     873             :     /* alloc record */
     874             :     char *pszRecord =
     875          29 :         STATIC_CAST(char *, malloc(sizeof(char) * psDBF->nRecordLength));
     876             : 
     877          29 :     const char chFieldFill = DBFGetNullCharacter(chType);
     878             : 
     879             :     SAOffset nRecordOffset;
     880          78 :     for (int i = psDBF->nRecords - 1; i >= 0; --i)
     881             :     {
     882          49 :         nRecordOffset =
     883          49 :             nOldRecordLength * STATIC_CAST(SAOffset, i) + nOldHeaderLength;
     884             : 
     885             :         /* load record */
     886          49 :         psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
     887          49 :         if (psDBF->sHooks.FRead(pszRecord, nOldRecordLength, 1, psDBF->fp) != 1)
     888             :         {
     889           0 :             free(pszRecord);
     890           0 :             return -1;
     891             :         }
     892             : 
     893             :         /* set new field's value to NULL */
     894          49 :         memset(pszRecord + nOldRecordLength, chFieldFill, nWidth);
     895             : 
     896          49 :         nRecordOffset = psDBF->nRecordLength * STATIC_CAST(SAOffset, i) +
     897          49 :                         psDBF->nHeaderLength;
     898             : 
     899             :         /* move record to the new place*/
     900          49 :         psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
     901          49 :         psDBF->sHooks.FWrite(pszRecord, psDBF->nRecordLength, 1, psDBF->fp);
     902             :     }
     903             : 
     904          29 :     if (psDBF->bWriteEndOfFileChar)
     905             :     {
     906          27 :         char ch = END_OF_FILE_CHARACTER;
     907             : 
     908          27 :         nRecordOffset =
     909          27 :             psDBF->nRecordLength * STATIC_CAST(SAOffset, psDBF->nRecords) +
     910          27 :             psDBF->nHeaderLength;
     911             : 
     912          27 :         psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
     913          27 :         psDBF->sHooks.FWrite(&ch, 1, 1, psDBF->fp);
     914             :     }
     915             : 
     916             :     /* free record */
     917          29 :     free(pszRecord);
     918             : 
     919             :     /* force update of header with new header, record length and new field */
     920          29 :     psDBF->bNoHeader = TRUE;
     921          29 :     DBFUpdateHeader(psDBF);
     922             : 
     923          29 :     psDBF->nCurrentRecord = -1;
     924          29 :     psDBF->bCurrentRecordModified = FALSE;
     925          29 :     psDBF->bUpdated = TRUE;
     926             : 
     927          29 :     return (psDBF->nFields - 1);
     928             : }
     929             : 
     930             : /************************************************************************/
     931             : /*                          DBFReadAttribute()                          */
     932             : /*                                                                      */
     933             : /*      Read one of the attribute fields of a record.                   */
     934             : /************************************************************************/
     935             : 
     936      240557 : static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField,
     937             :                               char chReqType)
     938             : {
     939             :     /* -------------------------------------------------------------------- */
     940             :     /*      Verify selection.                                               */
     941             :     /* -------------------------------------------------------------------- */
     942      240557 :     if (hEntity < 0 || hEntity >= psDBF->nRecords)
     943           0 :         return SHPLIB_NULLPTR;
     944             : 
     945      240557 :     if (iField < 0 || iField >= psDBF->nFields)
     946           0 :         return SHPLIB_NULLPTR;
     947             : 
     948             :     /* -------------------------------------------------------------------- */
     949             :     /*     Have we read the record?                                         */
     950             :     /* -------------------------------------------------------------------- */
     951      240557 :     if (!DBFLoadRecord(psDBF, hEntity))
     952           0 :         return SHPLIB_NULLPTR;
     953             : 
     954      240557 :     const unsigned char *pabyRec =
     955             :         REINTERPRET_CAST(const unsigned char *, psDBF->pszCurrentRecord);
     956             : 
     957             :     /* -------------------------------------------------------------------- */
     958             :     /*      Ensure we have room to extract the target field.                */
     959             :     /* -------------------------------------------------------------------- */
     960      240557 :     if (psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength)
     961             :     {
     962        2014 :         psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100;
     963        2014 :         if (psDBF->pszWorkField == SHPLIB_NULLPTR)
     964        2001 :             psDBF->pszWorkField =
     965        2001 :                 STATIC_CAST(char *, malloc(psDBF->nWorkFieldLength));
     966             :         else
     967          13 :             psDBF->pszWorkField = STATIC_CAST(
     968             :                 char *, realloc(psDBF->pszWorkField, psDBF->nWorkFieldLength));
     969             :     }
     970             : 
     971             :     /* -------------------------------------------------------------------- */
     972             :     /*      Extract the requested field.                                    */
     973             :     /* -------------------------------------------------------------------- */
     974      240557 :     memcpy(psDBF->pszWorkField,
     975             :            REINTERPRET_CAST(const char *, pabyRec) +
     976      240557 :                psDBF->panFieldOffset[iField],
     977      240557 :            psDBF->panFieldSize[iField]);
     978      240557 :     psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0';
     979             : 
     980      240557 :     void *pReturnField = psDBF->pszWorkField;
     981             : 
     982             :     /* -------------------------------------------------------------------- */
     983             :     /*      Decode the field.                                               */
     984             :     /* -------------------------------------------------------------------- */
     985      240557 :     if (chReqType == 'I')
     986             :     {
     987           0 :         psDBF->fieldValue.nIntField = atoi(psDBF->pszWorkField);
     988             : 
     989           0 :         pReturnField = &(psDBF->fieldValue.nIntField);
     990             :     }
     991      240557 :     else if (chReqType == 'N')
     992             :     {
     993           0 :         psDBF->fieldValue.dfDoubleField =
     994           0 :             psDBF->sHooks.Atof(psDBF->pszWorkField);
     995             : 
     996           0 :         pReturnField = &(psDBF->fieldValue.dfDoubleField);
     997             :     }
     998             : 
     999             : /* -------------------------------------------------------------------- */
    1000             : /*      Should we trim white space off the string attribute value?      */
    1001             : /* -------------------------------------------------------------------- */
    1002             : #ifdef TRIM_DBF_WHITESPACE
    1003             :     else
    1004             :     {
    1005      240557 :         char *pchSrc = psDBF->pszWorkField;
    1006      240557 :         char *pchDst = pchSrc;
    1007             : 
    1008     2087140 :         while (*pchSrc == ' ')
    1009     1846590 :             pchSrc++;
    1010             : 
    1011     1652920 :         while (*pchSrc != '\0')
    1012     1412360 :             *(pchDst++) = *(pchSrc++);
    1013      240557 :         *pchDst = '\0';
    1014             : 
    1015      690690 :         while (pchDst != psDBF->pszWorkField && *(--pchDst) == ' ')
    1016      450133 :             *pchDst = '\0';
    1017             :     }
    1018             : #endif
    1019             : 
    1020      240557 :     return pReturnField;
    1021             : }
    1022             : 
    1023             : /************************************************************************/
    1024             : /*                        DBFReadIntAttribute()                         */
    1025             : /*                                                                      */
    1026             : /*      Read an integer attribute.                                      */
    1027             : /************************************************************************/
    1028             : 
    1029           0 : int SHPAPI_CALL DBFReadIntegerAttribute(DBFHandle psDBF, int iRecord,
    1030             :                                         int iField)
    1031             : {
    1032             :     int *pnValue =
    1033           0 :         STATIC_CAST(int *, DBFReadAttribute(psDBF, iRecord, iField, 'I'));
    1034             : 
    1035           0 :     if (pnValue == SHPLIB_NULLPTR)
    1036           0 :         return 0;
    1037             :     else
    1038           0 :         return *pnValue;
    1039             : }
    1040             : 
    1041             : /************************************************************************/
    1042             : /*                        DBFReadDoubleAttribute()                      */
    1043             : /*                                                                      */
    1044             : /*      Read a double attribute.                                        */
    1045             : /************************************************************************/
    1046             : 
    1047           0 : double SHPAPI_CALL DBFReadDoubleAttribute(DBFHandle psDBF, int iRecord,
    1048             :                                           int iField)
    1049             : {
    1050             :     double *pdValue =
    1051           0 :         STATIC_CAST(double *, DBFReadAttribute(psDBF, iRecord, iField, 'N'));
    1052             : 
    1053           0 :     if (pdValue == SHPLIB_NULLPTR)
    1054           0 :         return 0.0;
    1055             :     else
    1056           0 :         return *pdValue;
    1057             : }
    1058             : 
    1059             : /************************************************************************/
    1060             : /*                        DBFReadStringAttribute()                      */
    1061             : /*                                                                      */
    1062             : /*      Read a string attribute.                                        */
    1063             : /************************************************************************/
    1064             : 
    1065             : const char SHPAPI_CALL1(*)
    1066      240555 :     DBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField)
    1067             : {
    1068      240555 :     return STATIC_CAST(const char *,
    1069             :                        DBFReadAttribute(psDBF, iRecord, iField, 'C'));
    1070             : }
    1071             : 
    1072             : /************************************************************************/
    1073             : /*                        DBFReadLogicalAttribute()                     */
    1074             : /*                                                                      */
    1075             : /*      Read a logical attribute.                                       */
    1076             : /************************************************************************/
    1077             : 
    1078             : const char SHPAPI_CALL1(*)
    1079           2 :     DBFReadLogicalAttribute(DBFHandle psDBF, int iRecord, int iField)
    1080             : {
    1081           2 :     return STATIC_CAST(const char *,
    1082             :                        DBFReadAttribute(psDBF, iRecord, iField, 'L'));
    1083             : }
    1084             : 
    1085             : /************************************************************************/
    1086             : /*                        DBFReadDateAttribute()                        */
    1087             : /*                                                                      */
    1088             : /*      Read a date attribute.                                          */
    1089             : /************************************************************************/
    1090             : 
    1091           0 : SHPDate SHPAPI_CALL DBFReadDateAttribute(DBFHandle psDBF, int iRecord,
    1092             :                                          int iField)
    1093             : {
    1094           0 :     const char *pdateValue = STATIC_CAST(
    1095             :         const char *, DBFReadAttribute(psDBF, iRecord, iField, 'D'));
    1096             : 
    1097             :     SHPDate date;
    1098             : 
    1099           0 :     if (pdateValue == SHPLIB_NULLPTR)
    1100             :     {
    1101           0 :         date.year = 0;
    1102           0 :         date.month = 0;
    1103           0 :         date.day = 0;
    1104             :     }
    1105           0 :     else if (3 != sscanf(pdateValue, "%4d%2d%2d", &date.year, &date.month,
    1106             :                          &date.day))
    1107             :     {
    1108           0 :         date.year = 0;
    1109           0 :         date.month = 0;
    1110           0 :         date.day = 0;
    1111             :     }
    1112             : 
    1113           0 :     return date;
    1114             : }
    1115             : 
    1116             : /************************************************************************/
    1117             : /*                         DBFIsValueNULL()                             */
    1118             : /*                                                                      */
    1119             : /*      Return TRUE if the passed string is NULL.                       */
    1120             : /************************************************************************/
    1121             : 
    1122      115127 : static bool DBFIsValueNULL(char chType, const char *pszValue)
    1123             : {
    1124      115127 :     if (pszValue == SHPLIB_NULLPTR)
    1125           0 :         return true;
    1126             : 
    1127      115127 :     switch (chType)
    1128             :     {
    1129      115069 :         case 'N':
    1130             :         case 'F':
    1131             :             /*
    1132             :             ** We accept all asterisks or all blanks as NULL
    1133             :             ** though according to the spec I think it should be all
    1134             :             ** asterisks.
    1135             :             */
    1136      115069 :             if (pszValue[0] == '*')
    1137          83 :                 return true;
    1138             : 
    1139      115017 :             for (int i = 0; pszValue[i] != '\0'; i++)
    1140             :             {
    1141      115016 :                 if (pszValue[i] != ' ')
    1142      114985 :                     return false;
    1143             :             }
    1144           1 :             return true;
    1145             : 
    1146          38 :         case 'D':
    1147             :             /* NULL date fields have value "00000000" */
    1148             :             /* Some DBF files have fields filled with spaces */
    1149             :             /* (trimmed by DBFReadStringAttribute) to indicate null */
    1150             :             /* values for dates (#4265). */
    1151             :             /* And others have '       0': https://lists.osgeo.org/pipermail/gdal-dev/2023-November/058010.html */
    1152          47 :             return strncmp(pszValue, "00000000", 8) == 0 ||
    1153          38 :                    strcmp(pszValue, " ") == 0 || strcmp(pszValue, "0") == 0;
    1154             : 
    1155           3 :         case 'L':
    1156             :             /* NULL boolean fields have value "?" */
    1157           3 :             return pszValue[0] == '?';
    1158             : 
    1159          17 :         default:
    1160             :             /* empty string fields are considered NULL */
    1161          17 :             return strlen(pszValue) == 0;
    1162             :     }
    1163             : }
    1164             : 
    1165             : /************************************************************************/
    1166             : /*                         DBFIsAttributeNULL()                         */
    1167             : /*                                                                      */
    1168             : /*      Return TRUE if value for field is NULL.                         */
    1169             : /*                                                                      */
    1170             : /*      Contributed by Jim Matthews.                                    */
    1171             : /************************************************************************/
    1172             : 
    1173      115096 : int SHPAPI_CALL DBFIsAttributeNULL(const DBFHandle psDBF, int iRecord,
    1174             :                                    int iField)
    1175             : {
    1176      115096 :     const char *pszValue = DBFReadStringAttribute(psDBF, iRecord, iField);
    1177             : 
    1178      115096 :     if (pszValue == SHPLIB_NULLPTR)
    1179           0 :         return TRUE;
    1180             : 
    1181      115096 :     return DBFIsValueNULL(psDBF->pachFieldType[iField], pszValue);
    1182             : }
    1183             : 
    1184             : /************************************************************************/
    1185             : /*                          DBFGetFieldCount()                          */
    1186             : /*                                                                      */
    1187             : /*      Return the number of fields in this table.                      */
    1188             : /************************************************************************/
    1189             : 
    1190       75292 : int SHPAPI_CALL DBFGetFieldCount(const DBFHandle psDBF)
    1191             : {
    1192       75292 :     return (psDBF->nFields);
    1193             : }
    1194             : 
    1195             : /************************************************************************/
    1196             : /*                         DBFGetRecordCount()                          */
    1197             : /*                                                                      */
    1198             : /*      Return the number of records in this table.                     */
    1199             : /************************************************************************/
    1200             : 
    1201      154433 : int SHPAPI_CALL DBFGetRecordCount(const DBFHandle psDBF)
    1202             : {
    1203      154433 :     return (psDBF->nRecords);
    1204             : }
    1205             : 
    1206             : /************************************************************************/
    1207             : /*                          DBFGetFieldInfo()                           */
    1208             : /*                                                                      */
    1209             : /*      Return any requested information about the field.               */
    1210             : /*      pszFieldName must be at least XBASE_FLDNAME_LEN_READ+1 (=12)    */
    1211             : /*      bytes long.                                                     */
    1212             : /************************************************************************/
    1213             : 
    1214       11676 : DBFFieldType SHPAPI_CALL DBFGetFieldInfo(const DBFHandle psDBF, int iField,
    1215             :                                          char *pszFieldName, int *pnWidth,
    1216             :                                          int *pnDecimals)
    1217             : {
    1218       11676 :     if (iField < 0 || iField >= psDBF->nFields)
    1219           0 :         return (FTInvalid);
    1220             : 
    1221       11676 :     if (pnWidth != SHPLIB_NULLPTR)
    1222       11676 :         *pnWidth = psDBF->panFieldSize[iField];
    1223             : 
    1224       11676 :     if (pnDecimals != SHPLIB_NULLPTR)
    1225       11676 :         *pnDecimals = psDBF->panFieldDecimals[iField];
    1226             : 
    1227       11676 :     if (pszFieldName != SHPLIB_NULLPTR)
    1228             :     {
    1229       11676 :         strncpy(pszFieldName,
    1230       11676 :                 STATIC_CAST(char *, psDBF->pszHeader) +
    1231       11676 :                     iField * XBASE_FLDHDR_SZ,
    1232             :                 XBASE_FLDNAME_LEN_READ);
    1233       11676 :         pszFieldName[XBASE_FLDNAME_LEN_READ] = '\0';
    1234       11676 :         for (int i = XBASE_FLDNAME_LEN_READ - 1;
    1235       11676 :              i > 0 && pszFieldName[i] == ' '; i--)
    1236           0 :             pszFieldName[i] = '\0';
    1237             :     }
    1238             : 
    1239       11676 :     if (psDBF->pachFieldType[iField] == 'L')
    1240           2 :         return (FTLogical);
    1241             : 
    1242       11674 :     else if (psDBF->pachFieldType[iField] == 'D')
    1243         119 :         return (FTDate);
    1244             : 
    1245       11555 :     else if (psDBF->pachFieldType[iField] == 'N' ||
    1246        5788 :              psDBF->pachFieldType[iField] == 'F')
    1247             :     {
    1248        5767 :         if (psDBF->panFieldDecimals[iField] > 0 ||
    1249        4399 :             psDBF->panFieldSize[iField] >= 10)
    1250        3833 :             return (FTDouble);
    1251             :         else
    1252        1934 :             return (FTInteger);
    1253             :     }
    1254             :     else
    1255             :     {
    1256        5788 :         return (FTString);
    1257             :     }
    1258             : }
    1259             : 
    1260             : /************************************************************************/
    1261             : /*                         DBFWriteAttribute()                          */
    1262             : /*                                                                      */
    1263             : /*      Write an attribute record to the file.                          */
    1264             : /************************************************************************/
    1265             : 
    1266       62353 : static bool DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
    1267             :                               void *pValue)
    1268             : {
    1269             :     /* -------------------------------------------------------------------- */
    1270             :     /*      Is this a valid record?                                         */
    1271             :     /* -------------------------------------------------------------------- */
    1272       62353 :     if (hEntity < 0 || hEntity > psDBF->nRecords)
    1273           0 :         return false;
    1274             : 
    1275       62353 :     if (psDBF->bNoHeader)
    1276        1428 :         DBFWriteHeader(psDBF);
    1277             : 
    1278             :     /* -------------------------------------------------------------------- */
    1279             :     /*      Is this a brand new record?                                     */
    1280             :     /* -------------------------------------------------------------------- */
    1281       62353 :     if (hEntity == psDBF->nRecords)
    1282             :     {
    1283       58469 :         if (!DBFFlushRecord(psDBF))
    1284           0 :             return false;
    1285             : 
    1286       58469 :         psDBF->nRecords++;
    1287     1074530 :         for (int i = 0; i < psDBF->nRecordLength; i++)
    1288     1016060 :             psDBF->pszCurrentRecord[i] = ' ';
    1289             : 
    1290       58469 :         psDBF->nCurrentRecord = hEntity;
    1291             :     }
    1292             : 
    1293             :     /* -------------------------------------------------------------------- */
    1294             :     /*      Is this an existing record, but different than the last one     */
    1295             :     /*      we accessed?                                                    */
    1296             :     /* -------------------------------------------------------------------- */
    1297       62353 :     if (!DBFLoadRecord(psDBF, hEntity))
    1298           0 :         return false;
    1299             : 
    1300       62353 :     unsigned char *pabyRec =
    1301             :         REINTERPRET_CAST(unsigned char *, psDBF->pszCurrentRecord);
    1302             : 
    1303       62353 :     psDBF->bCurrentRecordModified = TRUE;
    1304       62353 :     psDBF->bUpdated = TRUE;
    1305             : 
    1306             :     /* -------------------------------------------------------------------- */
    1307             :     /*      Translate NULL value to valid DBF file representation.          */
    1308             :     /*                                                                      */
    1309             :     /*      Contributed by Jim Matthews.                                    */
    1310             :     /* -------------------------------------------------------------------- */
    1311       62353 :     if (pValue == SHPLIB_NULLPTR)
    1312             :     {
    1313        1178 :         memset(pabyRec + psDBF->panFieldOffset[iField],
    1314         589 :                DBFGetNullCharacter(psDBF->pachFieldType[iField]),
    1315         589 :                psDBF->panFieldSize[iField]);
    1316         589 :         return true;
    1317             :     }
    1318             : 
    1319             :     /* -------------------------------------------------------------------- */
    1320             :     /*      Assign all the record fields.                                   */
    1321             :     /* -------------------------------------------------------------------- */
    1322       61764 :     bool nRetResult = true;
    1323             : 
    1324       61764 :     switch (psDBF->pachFieldType[iField])
    1325             :     {
    1326       57677 :         case 'D':
    1327             :         case 'N':
    1328             :         case 'F':
    1329             :         {
    1330       57677 :             int nWidth = psDBF->panFieldSize[iField];
    1331             : 
    1332             :             char szSField[XBASE_FLD_MAX_WIDTH + 1];
    1333       57677 :             if (STATIC_CAST(int, sizeof(szSField)) - 2 < nWidth)
    1334           0 :                 nWidth = sizeof(szSField) - 2;
    1335             : 
    1336             :             char szFormat[20];
    1337       57677 :             snprintf(szFormat, sizeof(szFormat), "%%%d.%df", nWidth,
    1338       57677 :                      psDBF->panFieldDecimals[iField]);
    1339       57677 :             CPLsnprintf(szSField, sizeof(szSField), szFormat,
    1340             :                         *STATIC_CAST(double *, pValue));
    1341       57677 :             szSField[sizeof(szSField) - 1] = '\0';
    1342       57677 :             if (STATIC_CAST(int, strlen(szSField)) >
    1343       57677 :                 psDBF->panFieldSize[iField])
    1344             :             {
    1345           7 :                 szSField[psDBF->panFieldSize[iField]] = '\0';
    1346           7 :                 nRetResult = false;
    1347             :             }
    1348       57677 :             memcpy(REINTERPRET_CAST(char *,
    1349             :                                     pabyRec + psDBF->panFieldOffset[iField]),
    1350             :                    szSField, strlen(szSField));
    1351       57677 :             break;
    1352             :         }
    1353             : 
    1354           0 :         case 'L':
    1355           0 :             if (psDBF->panFieldSize[iField] >= 1 &&
    1356           0 :                 (*STATIC_CAST(char *, pValue) == 'F' ||
    1357           0 :                  *STATIC_CAST(char *, pValue) == 'T'))
    1358             :             {
    1359           0 :                 *(pabyRec + psDBF->panFieldOffset[iField]) =
    1360           0 :                     *STATIC_CAST(char *, pValue);
    1361             :             }
    1362             :             else
    1363             :             {
    1364           0 :                 nRetResult = false;
    1365             :             }
    1366           0 :             break;
    1367             : 
    1368        4087 :         default:
    1369             :         {
    1370             :             int j;
    1371        4087 :             if (STATIC_CAST(int, strlen(STATIC_CAST(char *, pValue))) >
    1372        4087 :                 psDBF->panFieldSize[iField])
    1373             :             {
    1374           1 :                 j = psDBF->panFieldSize[iField];
    1375           1 :                 nRetResult = false;
    1376             :             }
    1377             :             else
    1378             :             {
    1379        4086 :                 memset(pabyRec + psDBF->panFieldOffset[iField], ' ',
    1380        4086 :                        psDBF->panFieldSize[iField]);
    1381        4086 :                 j = STATIC_CAST(int, strlen(STATIC_CAST(char *, pValue)));
    1382             :             }
    1383             : 
    1384        4087 :             strncpy(REINTERPRET_CAST(char *,
    1385             :                                      pabyRec + psDBF->panFieldOffset[iField]),
    1386             :                     STATIC_CAST(const char *, pValue), j);
    1387        4087 :             break;
    1388             :         }
    1389             :     }
    1390             : 
    1391       61764 :     return nRetResult;
    1392             : }
    1393             : 
    1394             : /************************************************************************/
    1395             : /*                     DBFWriteAttributeDirectly()                      */
    1396             : /*                                                                      */
    1397             : /*      Write an attribute record to the file, but without any          */
    1398             : /*      reformatting based on type.  The provided buffer is written     */
    1399             : /*      as is to the field position in the record.                      */
    1400             : /************************************************************************/
    1401             : 
    1402       24296 : int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity,
    1403             :                                           int iField, const void *pValue)
    1404             : {
    1405             :     /* -------------------------------------------------------------------- */
    1406             :     /*      Is this a valid record?                                         */
    1407             :     /* -------------------------------------------------------------------- */
    1408       24296 :     if (hEntity < 0 || hEntity > psDBF->nRecords)
    1409           0 :         return (FALSE);
    1410             : 
    1411       24296 :     if (psDBF->bNoHeader)
    1412          66 :         DBFWriteHeader(psDBF);
    1413             : 
    1414             :     /* -------------------------------------------------------------------- */
    1415             :     /*      Is this a brand new record?                                     */
    1416             :     /* -------------------------------------------------------------------- */
    1417       24296 :     if (hEntity == psDBF->nRecords)
    1418             :     {
    1419       18727 :         if (!DBFFlushRecord(psDBF))
    1420           0 :             return FALSE;
    1421             : 
    1422       18727 :         psDBF->nRecords++;
    1423      294275 :         for (int i = 0; i < psDBF->nRecordLength; i++)
    1424      275548 :             psDBF->pszCurrentRecord[i] = ' ';
    1425             : 
    1426       18727 :         psDBF->nCurrentRecord = hEntity;
    1427             :     }
    1428             : 
    1429             :     /* -------------------------------------------------------------------- */
    1430             :     /*      Is this an existing record, but different than the last one     */
    1431             :     /*      we accessed?                                                    */
    1432             :     /* -------------------------------------------------------------------- */
    1433       24296 :     if (!DBFLoadRecord(psDBF, hEntity))
    1434           0 :         return FALSE;
    1435             : 
    1436       24296 :     if (iField >= 0)
    1437             :     {
    1438       24295 :         unsigned char *pabyRec =
    1439             :             REINTERPRET_CAST(unsigned char *, psDBF->pszCurrentRecord);
    1440             : 
    1441             :         /* -------------------------------------------------------------------- */
    1442             :         /*      Assign all the record fields.                                   */
    1443             :         /* -------------------------------------------------------------------- */
    1444             :         int j;
    1445       24295 :         if (STATIC_CAST(int, strlen(STATIC_CAST(const char *, pValue))) >
    1446       24295 :             psDBF->panFieldSize[iField])
    1447           0 :             j = psDBF->panFieldSize[iField];
    1448             :         else
    1449             :         {
    1450       24295 :             memset(pabyRec + psDBF->panFieldOffset[iField], ' ',
    1451       24295 :                    psDBF->panFieldSize[iField]);
    1452       24295 :             j = STATIC_CAST(int, strlen(STATIC_CAST(const char *, pValue)));
    1453             :         }
    1454             : 
    1455       24295 :         memcpy(
    1456       24295 :             REINTERPRET_CAST(char *, pabyRec + psDBF->panFieldOffset[iField]),
    1457             :             STATIC_CAST(const char *, pValue), j);
    1458             :     }
    1459             : 
    1460       24296 :     psDBF->bCurrentRecordModified = TRUE;
    1461       24296 :     psDBF->bUpdated = TRUE;
    1462             : 
    1463       24296 :     return (TRUE);
    1464             : }
    1465             : 
    1466             : /************************************************************************/
    1467             : /*                      DBFWriteDoubleAttribute()                       */
    1468             : /*                                                                      */
    1469             : /*      Write a double attribute.                                       */
    1470             : /************************************************************************/
    1471             : 
    1472        1129 : int SHPAPI_CALL DBFWriteDoubleAttribute(DBFHandle psDBF, int iRecord,
    1473             :                                         int iField, double dValue)
    1474             : {
    1475        1129 :     return (DBFWriteAttribute(psDBF, iRecord, iField,
    1476        1129 :                               STATIC_CAST(void *, &dValue)));
    1477             : }
    1478             : 
    1479             : /************************************************************************/
    1480             : /*                      DBFWriteIntegerAttribute()                      */
    1481             : /*                                                                      */
    1482             : /*      Write an integer attribute.                                     */
    1483             : /************************************************************************/
    1484             : 
    1485       56548 : int SHPAPI_CALL DBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord,
    1486             :                                          int iField, int nValue)
    1487             : {
    1488       56548 :     double dValue = nValue;
    1489             : 
    1490       56548 :     return (DBFWriteAttribute(psDBF, iRecord, iField,
    1491       56548 :                               STATIC_CAST(void *, &dValue)));
    1492             : }
    1493             : 
    1494             : /************************************************************************/
    1495             : /*                      DBFWriteStringAttribute()                       */
    1496             : /*                                                                      */
    1497             : /*      Write a string attribute.                                       */
    1498             : /************************************************************************/
    1499             : 
    1500        4087 : int SHPAPI_CALL DBFWriteStringAttribute(DBFHandle psDBF, int iRecord,
    1501             :                                         int iField, const char *pszValue)
    1502             : {
    1503             :     return (
    1504        4087 :         DBFWriteAttribute(psDBF, iRecord, iField,
    1505        4087 :                           STATIC_CAST(void *, CONST_CAST(char *, pszValue))));
    1506             : }
    1507             : 
    1508             : /************************************************************************/
    1509             : /*                      DBFWriteNULLAttribute()                         */
    1510             : /*                                                                      */
    1511             : /*      Write a NULL attribute.                                         */
    1512             : /************************************************************************/
    1513             : 
    1514         589 : int SHPAPI_CALL DBFWriteNULLAttribute(DBFHandle psDBF, int iRecord, int iField)
    1515             : {
    1516         589 :     return (DBFWriteAttribute(psDBF, iRecord, iField, SHPLIB_NULLPTR));
    1517             : }
    1518             : 
    1519             : /************************************************************************/
    1520             : /*                      DBFWriteLogicalAttribute()                      */
    1521             : /*                                                                      */
    1522             : /*      Write a logical attribute.                                      */
    1523             : /************************************************************************/
    1524             : 
    1525           0 : int SHPAPI_CALL DBFWriteLogicalAttribute(DBFHandle psDBF, int iRecord,
    1526             :                                          int iField, const char lValue)
    1527             : {
    1528             :     return (
    1529           0 :         DBFWriteAttribute(psDBF, iRecord, iField,
    1530           0 :                           STATIC_CAST(void *, CONST_CAST(char *, &lValue))));
    1531             : }
    1532             : 
    1533             : /************************************************************************/
    1534             : /*                      DBFWriteDateAttribute()                         */
    1535             : /*                                                                      */
    1536             : /*      Write a date attribute.                                         */
    1537             : /************************************************************************/
    1538             : 
    1539           0 : int SHPAPI_CALL DBFWriteDateAttribute(DBFHandle psDBF, int iRecord, int iField,
    1540             :                                       const SHPDate *lValue)
    1541             : {
    1542           0 :     if (SHPLIB_NULLPTR == lValue)
    1543           0 :         return false;
    1544             :     /* check for supported digit range, but do not check for valid date */
    1545           0 :     if (lValue->year < 0 || lValue->year > 9999)
    1546           0 :         return false;
    1547           0 :     if (lValue->month < 0 || lValue->month > 99)
    1548           0 :         return false;
    1549           0 :     if (lValue->day < 0 || lValue->day > 99)
    1550           0 :         return false;
    1551             :     char dateValue[9]; /* "yyyyMMdd\0" */
    1552           0 :     snprintf(dateValue, sizeof(dateValue), "%04d%02d%02d", lValue->year,
    1553           0 :              lValue->month, lValue->day);
    1554           0 :     return (DBFWriteAttributeDirectly(psDBF, iRecord, iField, dateValue));
    1555             : }
    1556             : 
    1557             : /************************************************************************/
    1558             : /*                         DBFWriteTuple()                              */
    1559             : /*                                                                      */
    1560             : /*      Write an attribute record to the file.                          */
    1561             : /************************************************************************/
    1562             : 
    1563         164 : int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity,
    1564             :                               const void *pRawTuple)
    1565             : {
    1566             :     /* -------------------------------------------------------------------- */
    1567             :     /*      Is this a valid record?                                         */
    1568             :     /* -------------------------------------------------------------------- */
    1569         164 :     if (hEntity < 0 || hEntity > psDBF->nRecords)
    1570           0 :         return (FALSE);
    1571             : 
    1572         164 :     if (psDBF->bNoHeader)
    1573           0 :         DBFWriteHeader(psDBF);
    1574             : 
    1575             :     /* -------------------------------------------------------------------- */
    1576             :     /*      Is this a brand new record?                                     */
    1577             :     /* -------------------------------------------------------------------- */
    1578         164 :     if (hEntity == psDBF->nRecords)
    1579             :     {
    1580         164 :         if (!DBFFlushRecord(psDBF))
    1581           0 :             return FALSE;
    1582             : 
    1583         164 :         psDBF->nRecords++;
    1584       21437 :         for (int i = 0; i < psDBF->nRecordLength; i++)
    1585       21273 :             psDBF->pszCurrentRecord[i] = ' ';
    1586             : 
    1587         164 :         psDBF->nCurrentRecord = hEntity;
    1588             :     }
    1589             : 
    1590             :     /* -------------------------------------------------------------------- */
    1591             :     /*      Is this an existing record, but different than the last one     */
    1592             :     /*      we accessed?                                                    */
    1593             :     /* -------------------------------------------------------------------- */
    1594         164 :     if (!DBFLoadRecord(psDBF, hEntity))
    1595           0 :         return FALSE;
    1596             : 
    1597         164 :     unsigned char *pabyRec =
    1598             :         REINTERPRET_CAST(unsigned char *, psDBF->pszCurrentRecord);
    1599             : 
    1600         164 :     memcpy(pabyRec, pRawTuple, psDBF->nRecordLength);
    1601             : 
    1602         164 :     psDBF->bCurrentRecordModified = TRUE;
    1603         164 :     psDBF->bUpdated = TRUE;
    1604             : 
    1605         164 :     return (TRUE);
    1606             : }
    1607             : 
    1608             : /************************************************************************/
    1609             : /*                            DBFReadTuple()                            */
    1610             : /*                                                                      */
    1611             : /*      Read a complete record.  Note that the result is only valid     */
    1612             : /*      till the next record read for any reason.                       */
    1613             : /************************************************************************/
    1614             : 
    1615         164 : const char SHPAPI_CALL1(*) DBFReadTuple(DBFHandle psDBF, int hEntity)
    1616             : {
    1617         164 :     if (hEntity < 0 || hEntity >= psDBF->nRecords)
    1618           0 :         return SHPLIB_NULLPTR;
    1619             : 
    1620         164 :     if (!DBFLoadRecord(psDBF, hEntity))
    1621           0 :         return SHPLIB_NULLPTR;
    1622             : 
    1623         164 :     return STATIC_CAST(const char *, psDBF->pszCurrentRecord);
    1624             : }
    1625             : 
    1626             : /************************************************************************/
    1627             : /*                          DBFCloneEmpty()                             */
    1628             : /*                                                                      */
    1629             : /*      Create a new .dbf file with same code page and field            */
    1630             : /*      definitions as the given handle.                                */
    1631             : /************************************************************************/
    1632             : 
    1633          24 : DBFHandle SHPAPI_CALL DBFCloneEmpty(const DBFHandle psDBF,
    1634             :                                     const char *pszFilename)
    1635             : {
    1636             :     DBFHandle newDBF =
    1637          24 :         DBFCreateLL(pszFilename, psDBF->pszCodePage, &psDBF->sHooks);
    1638          24 :     if (newDBF == SHPLIB_NULLPTR)
    1639           0 :         return SHPLIB_NULLPTR;
    1640             : 
    1641          24 :     newDBF->nFields = psDBF->nFields;
    1642          24 :     newDBF->nRecordLength = psDBF->nRecordLength;
    1643          24 :     newDBF->nHeaderLength = psDBF->nHeaderLength;
    1644             : 
    1645          24 :     if (psDBF->pszHeader)
    1646             :     {
    1647          24 :         newDBF->pszHeader =
    1648          24 :             STATIC_CAST(char *, malloc(XBASE_FLDHDR_SZ * psDBF->nFields));
    1649          24 :         memcpy(newDBF->pszHeader, psDBF->pszHeader,
    1650          24 :                XBASE_FLDHDR_SZ * psDBF->nFields);
    1651             :     }
    1652             : 
    1653          24 :     newDBF->panFieldOffset =
    1654          24 :         STATIC_CAST(int *, malloc(sizeof(int) * psDBF->nFields));
    1655          24 :     memcpy(newDBF->panFieldOffset, psDBF->panFieldOffset,
    1656          24 :            sizeof(int) * psDBF->nFields);
    1657          24 :     newDBF->panFieldSize =
    1658          24 :         STATIC_CAST(int *, malloc(sizeof(int) * psDBF->nFields));
    1659          24 :     memcpy(newDBF->panFieldSize, psDBF->panFieldSize,
    1660          24 :            sizeof(int) * psDBF->nFields);
    1661          24 :     newDBF->panFieldDecimals =
    1662          24 :         STATIC_CAST(int *, malloc(sizeof(int) * psDBF->nFields));
    1663          24 :     memcpy(newDBF->panFieldDecimals, psDBF->panFieldDecimals,
    1664          24 :            sizeof(int) * psDBF->nFields);
    1665          24 :     newDBF->pachFieldType =
    1666          24 :         STATIC_CAST(char *, malloc(sizeof(char) * psDBF->nFields));
    1667          24 :     memcpy(newDBF->pachFieldType, psDBF->pachFieldType,
    1668          24 :            sizeof(char) * psDBF->nFields);
    1669             : 
    1670          24 :     newDBF->bNoHeader = TRUE;
    1671          24 :     newDBF->bUpdated = TRUE;
    1672          24 :     newDBF->bWriteEndOfFileChar = psDBF->bWriteEndOfFileChar;
    1673             : 
    1674          24 :     DBFWriteHeader(newDBF);
    1675          24 :     DBFClose(newDBF);
    1676             : 
    1677          24 :     newDBF = DBFOpen(pszFilename, "rb+");
    1678          24 :     newDBF->bWriteEndOfFileChar = psDBF->bWriteEndOfFileChar;
    1679             : 
    1680          24 :     return (newDBF);
    1681             : }
    1682             : 
    1683             : /************************************************************************/
    1684             : /*                       DBFGetNativeFieldType()                        */
    1685             : /*                                                                      */
    1686             : /*      Return the DBase field type for the specified field.            */
    1687             : /*                                                                      */
    1688             : /*      Value can be one of: 'C' (String), 'D' (Date), 'F' (Float),     */
    1689             : /*                           'N' (Numeric, with or without decimal),    */
    1690             : /*                           'L' (Logical),                             */
    1691             : /*                           'M' (Memo: 10 digits .DBT block ptr)       */
    1692             : /************************************************************************/
    1693             : 
    1694          29 : char SHPAPI_CALL DBFGetNativeFieldType(const DBFHandle psDBF, int iField)
    1695             : {
    1696          29 :     if (iField >= 0 && iField < psDBF->nFields)
    1697          29 :         return psDBF->pachFieldType[iField];
    1698             : 
    1699           0 :     return ' ';
    1700             : }
    1701             : 
    1702             : /************************************************************************/
    1703             : /*                          DBFGetFieldIndex()                          */
    1704             : /*                                                                      */
    1705             : /*      Get the index number for a field in a .dbf file.                */
    1706             : /*                                                                      */
    1707             : /*      Contributed by Jim Matthews.                                    */
    1708             : /************************************************************************/
    1709             : 
    1710           0 : int SHPAPI_CALL DBFGetFieldIndex(const DBFHandle psDBF,
    1711             :                                  const char *pszFieldName)
    1712             : {
    1713             :     char name[XBASE_FLDNAME_LEN_READ + 1];
    1714             : 
    1715           0 :     for (int i = 0; i < DBFGetFieldCount(psDBF); i++)
    1716             :     {
    1717           0 :         DBFGetFieldInfo(psDBF, i, name, SHPLIB_NULLPTR, SHPLIB_NULLPTR);
    1718           0 :         if (!STRCASECMP(pszFieldName, name))
    1719           0 :             return (i);
    1720             :     }
    1721           0 :     return (-1);
    1722             : }
    1723             : 
    1724             : /************************************************************************/
    1725             : /*                         DBFIsRecordDeleted()                         */
    1726             : /*                                                                      */
    1727             : /*      Returns TRUE if the indicated record is deleted, otherwise      */
    1728             : /*      it returns FALSE.                                               */
    1729             : /************************************************************************/
    1730             : 
    1731      172087 : int SHPAPI_CALL DBFIsRecordDeleted(const DBFHandle psDBF, int iShape)
    1732             : {
    1733             :     /* -------------------------------------------------------------------- */
    1734             :     /*      Verify selection.                                               */
    1735             :     /* -------------------------------------------------------------------- */
    1736      172087 :     if (iShape < 0 || iShape >= psDBF->nRecords)
    1737           0 :         return TRUE;
    1738             : 
    1739             :     /* -------------------------------------------------------------------- */
    1740             :     /*      Have we read the record?                                        */
    1741             :     /* -------------------------------------------------------------------- */
    1742      172087 :     if (!DBFLoadRecord(psDBF, iShape))
    1743           1 :         return FALSE;
    1744             : 
    1745             :     /* -------------------------------------------------------------------- */
    1746             :     /*      '*' means deleted.                                              */
    1747             :     /* -------------------------------------------------------------------- */
    1748      172086 :     return psDBF->pszCurrentRecord[0] == '*';
    1749             : }
    1750             : 
    1751             : /************************************************************************/
    1752             : /*                        DBFMarkRecordDeleted()                        */
    1753             : /************************************************************************/
    1754             : 
    1755         214 : int SHPAPI_CALL DBFMarkRecordDeleted(DBFHandle psDBF, int iShape,
    1756             :                                      int bIsDeleted)
    1757             : {
    1758             :     /* -------------------------------------------------------------------- */
    1759             :     /*      Verify selection.                                               */
    1760             :     /* -------------------------------------------------------------------- */
    1761         214 :     if (iShape < 0 || iShape >= psDBF->nRecords)
    1762           0 :         return FALSE;
    1763             : 
    1764             :     /* -------------------------------------------------------------------- */
    1765             :     /*      Is this an existing record, but different than the last one     */
    1766             :     /*      we accessed?                                                    */
    1767             :     /* -------------------------------------------------------------------- */
    1768         214 :     if (!DBFLoadRecord(psDBF, iShape))
    1769           0 :         return FALSE;
    1770             : 
    1771             :     /* -------------------------------------------------------------------- */
    1772             :     /*      Assign value, marking record as dirty if it changes.            */
    1773             :     /* -------------------------------------------------------------------- */
    1774             :     char chNewFlag;
    1775         214 :     if (bIsDeleted)
    1776         214 :         chNewFlag = '*';
    1777             :     else
    1778           0 :         chNewFlag = ' ';
    1779             : 
    1780         214 :     if (psDBF->pszCurrentRecord[0] != chNewFlag)
    1781             :     {
    1782         214 :         psDBF->bCurrentRecordModified = TRUE;
    1783         214 :         psDBF->bUpdated = TRUE;
    1784         214 :         psDBF->pszCurrentRecord[0] = chNewFlag;
    1785             :     }
    1786             : 
    1787         214 :     return TRUE;
    1788             : }
    1789             : 
    1790             : /************************************************************************/
    1791             : /*                            DBFGetCodePage                            */
    1792             : /************************************************************************/
    1793             : 
    1794           0 : const char SHPAPI_CALL1(*) DBFGetCodePage(const DBFHandle psDBF)
    1795             : {
    1796           0 :     if (psDBF == SHPLIB_NULLPTR)
    1797           0 :         return SHPLIB_NULLPTR;
    1798           0 :     return psDBF->pszCodePage;
    1799             : }
    1800             : 
    1801             : /************************************************************************/
    1802             : /*                          DBFDeleteField()                            */
    1803             : /*                                                                      */
    1804             : /*      Remove a field from a .dbf file                                 */
    1805             : /************************************************************************/
    1806             : 
    1807          11 : int SHPAPI_CALL DBFDeleteField(DBFHandle psDBF, int iField)
    1808             : {
    1809          11 :     if (iField < 0 || iField >= psDBF->nFields)
    1810           0 :         return FALSE;
    1811             : 
    1812             :     /* make sure that everything is written in .dbf */
    1813          11 :     if (!DBFFlushRecord(psDBF))
    1814           0 :         return FALSE;
    1815             : 
    1816             :     /* get information about field to be deleted */
    1817          11 :     int nOldRecordLength = psDBF->nRecordLength;
    1818          11 :     int nOldHeaderLength = psDBF->nHeaderLength;
    1819          11 :     int nDeletedFieldOffset = psDBF->panFieldOffset[iField];
    1820          11 :     int nDeletedFieldSize = psDBF->panFieldSize[iField];
    1821             : 
    1822             :     /* update fields info */
    1823          16 :     for (int i = iField + 1; i < psDBF->nFields; i++)
    1824             :     {
    1825           5 :         psDBF->panFieldOffset[i - 1] =
    1826           5 :             psDBF->panFieldOffset[i] - nDeletedFieldSize;
    1827           5 :         psDBF->panFieldSize[i - 1] = psDBF->panFieldSize[i];
    1828           5 :         psDBF->panFieldDecimals[i - 1] = psDBF->panFieldDecimals[i];
    1829           5 :         psDBF->pachFieldType[i - 1] = psDBF->pachFieldType[i];
    1830             :     }
    1831             : 
    1832             :     /* resize fields arrays */
    1833          11 :     psDBF->nFields--;
    1834             : 
    1835          11 :     psDBF->panFieldOffset = STATIC_CAST(
    1836             :         int *, realloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields));
    1837             : 
    1838          11 :     psDBF->panFieldSize = STATIC_CAST(
    1839             :         int *, realloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields));
    1840             : 
    1841          11 :     psDBF->panFieldDecimals = STATIC_CAST(
    1842             :         int *, realloc(psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields));
    1843             : 
    1844          11 :     psDBF->pachFieldType = STATIC_CAST(
    1845             :         char *, realloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields));
    1846             : 
    1847             :     /* update header information */
    1848          11 :     psDBF->nHeaderLength -= XBASE_FLDHDR_SZ;
    1849          11 :     psDBF->nRecordLength -= nDeletedFieldSize;
    1850             : 
    1851             :     /* overwrite field information in header */
    1852          11 :     memmove(psDBF->pszHeader + iField * XBASE_FLDHDR_SZ,
    1853          11 :             psDBF->pszHeader + (iField + 1) * XBASE_FLDHDR_SZ,
    1854          11 :             sizeof(char) * (psDBF->nFields - iField) * XBASE_FLDHDR_SZ);
    1855             : 
    1856          11 :     psDBF->pszHeader = STATIC_CAST(
    1857             :         char *, realloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ));
    1858             : 
    1859             :     /* update size of current record appropriately */
    1860          11 :     psDBF->pszCurrentRecord = STATIC_CAST(
    1861             :         char *, realloc(psDBF->pszCurrentRecord, psDBF->nRecordLength));
    1862             : 
    1863             :     /* we're done if we're dealing with not yet created .dbf */
    1864          11 :     if (psDBF->bNoHeader && psDBF->nRecords == 0)
    1865           0 :         return TRUE;
    1866             : 
    1867             :     /* force update of header with new header and record length */
    1868          11 :     psDBF->bNoHeader = TRUE;
    1869          11 :     DBFUpdateHeader(psDBF);
    1870             : 
    1871             :     /* alloc record */
    1872             :     char *pszRecord =
    1873          11 :         STATIC_CAST(char *, malloc(sizeof(char) * nOldRecordLength));
    1874             : 
    1875             :     /* shift records to their new positions */
    1876          25 :     for (int iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
    1877             :     {
    1878          14 :         SAOffset nRecordOffset =
    1879          14 :             nOldRecordLength * STATIC_CAST(SAOffset, iRecord) +
    1880          14 :             nOldHeaderLength;
    1881             : 
    1882             :         /* load record */
    1883          14 :         psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    1884          14 :         if (psDBF->sHooks.FRead(pszRecord, nOldRecordLength, 1, psDBF->fp) != 1)
    1885             :         {
    1886           0 :             free(pszRecord);
    1887           0 :             return FALSE;
    1888             :         }
    1889             : 
    1890          14 :         nRecordOffset = psDBF->nRecordLength * STATIC_CAST(SAOffset, iRecord) +
    1891          14 :                         psDBF->nHeaderLength;
    1892             : 
    1893             :         /* move record in two steps */
    1894          14 :         psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    1895          14 :         psDBF->sHooks.FWrite(pszRecord, nDeletedFieldOffset, 1, psDBF->fp);
    1896          14 :         psDBF->sHooks.FWrite(
    1897          14 :             pszRecord + nDeletedFieldOffset + nDeletedFieldSize,
    1898          14 :             nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize, 1,
    1899             :             psDBF->fp);
    1900             :     }
    1901             : 
    1902          11 :     if (psDBF->bWriteEndOfFileChar)
    1903             :     {
    1904          10 :         char ch = END_OF_FILE_CHARACTER;
    1905          10 :         SAOffset nEOFOffset =
    1906          10 :             psDBF->nRecordLength * STATIC_CAST(SAOffset, psDBF->nRecords) +
    1907          10 :             psDBF->nHeaderLength;
    1908             : 
    1909          10 :         psDBF->sHooks.FSeek(psDBF->fp, nEOFOffset, 0);
    1910          10 :         psDBF->sHooks.FWrite(&ch, 1, 1, psDBF->fp);
    1911             :     }
    1912             : 
    1913             :     /* TODO: truncate file */
    1914             : 
    1915             :     /* free record */
    1916          11 :     free(pszRecord);
    1917             : 
    1918          11 :     psDBF->nCurrentRecord = -1;
    1919          11 :     psDBF->bCurrentRecordModified = FALSE;
    1920          11 :     psDBF->bUpdated = TRUE;
    1921             : 
    1922          11 :     return TRUE;
    1923             : }
    1924             : 
    1925             : /************************************************************************/
    1926             : /*                          DBFReorderFields()                          */
    1927             : /*                                                                      */
    1928             : /*      Reorder the fields of a .dbf file                               */
    1929             : /*                                                                      */
    1930             : /* panMap must be exactly psDBF->nFields long and be a permutation      */
    1931             : /* of [0, psDBF->nFields-1]. This assumption will not be asserted in the*/
    1932             : /* code of DBFReorderFields.                                            */
    1933             : /************************************************************************/
    1934             : 
    1935          17 : int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, const int *panMap)
    1936             : {
    1937          17 :     if (psDBF->nFields == 0)
    1938           0 :         return TRUE;
    1939             : 
    1940             :     /* make sure that everything is written in .dbf */
    1941          17 :     if (!DBFFlushRecord(psDBF))
    1942           0 :         return FALSE;
    1943             : 
    1944             :     /* a simple malloc() would be enough, but calloc() helps clang static
    1945             :      * analyzer */
    1946             :     int *panFieldOffsetNew =
    1947          17 :         STATIC_CAST(int *, calloc(sizeof(int), psDBF->nFields));
    1948             :     int *panFieldSizeNew =
    1949          17 :         STATIC_CAST(int *, calloc(sizeof(int), psDBF->nFields));
    1950             :     int *panFieldDecimalsNew =
    1951          17 :         STATIC_CAST(int *, calloc(sizeof(int), psDBF->nFields));
    1952             :     char *pachFieldTypeNew =
    1953          17 :         STATIC_CAST(char *, calloc(sizeof(char), psDBF->nFields));
    1954          17 :     char *pszHeaderNew = STATIC_CAST(
    1955             :         char *, malloc(sizeof(char) * XBASE_FLDHDR_SZ * psDBF->nFields));
    1956             : 
    1957             :     /* shuffle fields definitions */
    1958          74 :     for (int i = 0; i < psDBF->nFields; i++)
    1959             :     {
    1960          57 :         panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]];
    1961          57 :         panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]];
    1962          57 :         pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]];
    1963          57 :         memcpy(pszHeaderNew + i * XBASE_FLDHDR_SZ,
    1964          57 :                psDBF->pszHeader + panMap[i] * XBASE_FLDHDR_SZ, XBASE_FLDHDR_SZ);
    1965             :     }
    1966          17 :     panFieldOffsetNew[0] = 1;
    1967          57 :     for (int i = 1; i < psDBF->nFields; i++)
    1968             :     {
    1969          40 :         panFieldOffsetNew[i] =
    1970          40 :             panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
    1971             :     }
    1972             : 
    1973          17 :     free(psDBF->pszHeader);
    1974          17 :     psDBF->pszHeader = pszHeaderNew;
    1975             : 
    1976          17 :     bool errorAbort = false;
    1977             : 
    1978             :     /* we're done if we're dealing with not yet created .dbf */
    1979          17 :     if (!(psDBF->bNoHeader && psDBF->nRecords == 0))
    1980             :     {
    1981             :         /* force update of header with new header and record length */
    1982          17 :         psDBF->bNoHeader = TRUE;
    1983          17 :         DBFUpdateHeader(psDBF);
    1984             : 
    1985             :         /* alloc record */
    1986             :         char *pszRecord =
    1987          17 :             STATIC_CAST(char *, malloc(sizeof(char) * psDBF->nRecordLength));
    1988             :         char *pszRecordNew =
    1989          17 :             STATIC_CAST(char *, malloc(sizeof(char) * psDBF->nRecordLength));
    1990             : 
    1991             :         /* shuffle fields in records */
    1992          64 :         for (int iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
    1993             :         {
    1994          47 :             const SAOffset nRecordOffset =
    1995          47 :                 psDBF->nRecordLength * STATIC_CAST(SAOffset, iRecord) +
    1996          47 :                 psDBF->nHeaderLength;
    1997             : 
    1998             :             /* load record */
    1999          47 :             psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    2000          47 :             if (psDBF->sHooks.FRead(pszRecord, psDBF->nRecordLength, 1,
    2001          47 :                                     psDBF->fp) != 1)
    2002             :             {
    2003           0 :                 errorAbort = true;
    2004           0 :                 break;
    2005             :             }
    2006             : 
    2007          47 :             pszRecordNew[0] = pszRecord[0];
    2008             : 
    2009         222 :             for (int i = 0; i < psDBF->nFields; i++)
    2010             :             {
    2011         175 :                 memcpy(pszRecordNew + panFieldOffsetNew[i],
    2012         175 :                        pszRecord + psDBF->panFieldOffset[panMap[i]],
    2013         175 :                        psDBF->panFieldSize[panMap[i]]);
    2014             :             }
    2015             : 
    2016             :             /* write record */
    2017          47 :             psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    2018          47 :             psDBF->sHooks.FWrite(pszRecordNew, psDBF->nRecordLength, 1,
    2019             :                                  psDBF->fp);
    2020             :         }
    2021             : 
    2022             :         /* free record */
    2023          17 :         free(pszRecord);
    2024          17 :         free(pszRecordNew);
    2025             :     }
    2026             : 
    2027          17 :     if (errorAbort)
    2028             :     {
    2029           0 :         free(panFieldOffsetNew);
    2030           0 :         free(panFieldSizeNew);
    2031           0 :         free(panFieldDecimalsNew);
    2032           0 :         free(pachFieldTypeNew);
    2033           0 :         psDBF->nCurrentRecord = -1;
    2034           0 :         psDBF->bCurrentRecordModified = FALSE;
    2035           0 :         psDBF->bUpdated = FALSE;
    2036           0 :         return FALSE;
    2037             :     }
    2038             : 
    2039          17 :     free(psDBF->panFieldOffset);
    2040          17 :     free(psDBF->panFieldSize);
    2041          17 :     free(psDBF->panFieldDecimals);
    2042          17 :     free(psDBF->pachFieldType);
    2043             : 
    2044          17 :     psDBF->panFieldOffset = panFieldOffsetNew;
    2045          17 :     psDBF->panFieldSize = panFieldSizeNew;
    2046          17 :     psDBF->panFieldDecimals = panFieldDecimalsNew;
    2047          17 :     psDBF->pachFieldType = pachFieldTypeNew;
    2048             : 
    2049          17 :     psDBF->nCurrentRecord = -1;
    2050          17 :     psDBF->bCurrentRecordModified = FALSE;
    2051          17 :     psDBF->bUpdated = TRUE;
    2052             : 
    2053          17 :     return TRUE;
    2054             : }
    2055             : 
    2056             : /************************************************************************/
    2057             : /*                          DBFAlterFieldDefn()                         */
    2058             : /*                                                                      */
    2059             : /*      Alter a field definition in a .dbf file                         */
    2060             : /************************************************************************/
    2061             : 
    2062          24 : int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField,
    2063             :                                   const char *pszFieldName, char chType,
    2064             :                                   int nWidth, int nDecimals)
    2065             : {
    2066          24 :     if (iField < 0 || iField >= psDBF->nFields)
    2067           0 :         return FALSE;
    2068             : 
    2069             :     /* make sure that everything is written in .dbf */
    2070          24 :     if (!DBFFlushRecord(psDBF))
    2071           0 :         return FALSE;
    2072             : 
    2073          24 :     const char chFieldFill = DBFGetNullCharacter(chType);
    2074             : 
    2075          24 :     const char chOldType = psDBF->pachFieldType[iField];
    2076          24 :     const int nOffset = psDBF->panFieldOffset[iField];
    2077          24 :     const int nOldWidth = psDBF->panFieldSize[iField];
    2078          24 :     const int nOldRecordLength = psDBF->nRecordLength;
    2079             : 
    2080             :     /* -------------------------------------------------------------------- */
    2081             :     /*      Do some checking to ensure we can add records to this file.     */
    2082             :     /* -------------------------------------------------------------------- */
    2083          24 :     if (nWidth < 1)
    2084           2 :         return -1;
    2085             : 
    2086          22 :     if (nWidth > XBASE_FLD_MAX_WIDTH)
    2087           0 :         nWidth = XBASE_FLD_MAX_WIDTH;
    2088             : 
    2089             :     /* -------------------------------------------------------------------- */
    2090             :     /*      Assign the new field information fields.                        */
    2091             :     /* -------------------------------------------------------------------- */
    2092          22 :     psDBF->panFieldSize[iField] = nWidth;
    2093          22 :     psDBF->panFieldDecimals[iField] = nDecimals;
    2094          22 :     psDBF->pachFieldType[iField] = chType;
    2095             : 
    2096             :     /* -------------------------------------------------------------------- */
    2097             :     /*      Update the header information.                                  */
    2098             :     /* -------------------------------------------------------------------- */
    2099          22 :     char *pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * iField;
    2100             : 
    2101         726 :     for (int i = 0; i < XBASE_FLDHDR_SZ; i++)
    2102         704 :         pszFInfo[i] = '\0';
    2103             : 
    2104          22 :     strncpy(pszFInfo, pszFieldName, XBASE_FLDNAME_LEN_WRITE);
    2105             : 
    2106          22 :     pszFInfo[11] = psDBF->pachFieldType[iField];
    2107             : 
    2108          22 :     if (chType == 'C')
    2109             :     {
    2110          12 :         pszFInfo[16] = STATIC_CAST(unsigned char, nWidth % 256);
    2111          12 :         pszFInfo[17] = STATIC_CAST(unsigned char, nWidth / 256);
    2112             :     }
    2113             :     else
    2114             :     {
    2115          10 :         pszFInfo[16] = STATIC_CAST(unsigned char, nWidth);
    2116          10 :         pszFInfo[17] = STATIC_CAST(unsigned char, nDecimals);
    2117             :     }
    2118             : 
    2119             :     /* -------------------------------------------------------------------- */
    2120             :     /*      Update offsets                                                  */
    2121             :     /* -------------------------------------------------------------------- */
    2122          22 :     if (nWidth != nOldWidth)
    2123             :     {
    2124          33 :         for (int i = iField + 1; i < psDBF->nFields; i++)
    2125          19 :             psDBF->panFieldOffset[i] += nWidth - nOldWidth;
    2126          14 :         psDBF->nRecordLength += nWidth - nOldWidth;
    2127             : 
    2128          14 :         psDBF->pszCurrentRecord = STATIC_CAST(
    2129             :             char *, realloc(psDBF->pszCurrentRecord, psDBF->nRecordLength));
    2130             :     }
    2131             : 
    2132             :     /* we're done if we're dealing with not yet created .dbf */
    2133          22 :     if (psDBF->bNoHeader && psDBF->nRecords == 0)
    2134           1 :         return TRUE;
    2135             : 
    2136             :     /* force update of header with new header and record length */
    2137          21 :     psDBF->bNoHeader = TRUE;
    2138          21 :     DBFUpdateHeader(psDBF);
    2139             : 
    2140          21 :     bool errorAbort = false;
    2141             : 
    2142          21 :     if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType))
    2143             :     {
    2144             :         char *pszRecord =
    2145           6 :             STATIC_CAST(char *, malloc(sizeof(char) * nOldRecordLength));
    2146             :         char *pszOldField =
    2147           6 :             STATIC_CAST(char *, malloc(sizeof(char) * (nOldWidth + 1)));
    2148             : 
    2149           6 :         pszOldField[nOldWidth] = 0;
    2150             : 
    2151             :         /* move records to their new positions */
    2152          24 :         for (int iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
    2153             :         {
    2154          18 :             SAOffset nRecordOffset =
    2155          18 :                 nOldRecordLength * STATIC_CAST(SAOffset, iRecord) +
    2156          18 :                 psDBF->nHeaderLength;
    2157             : 
    2158             :             /* load record */
    2159          18 :             psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    2160          18 :             if (psDBF->sHooks.FRead(pszRecord, nOldRecordLength, 1,
    2161          18 :                                     psDBF->fp) != 1)
    2162             :             {
    2163           0 :                 errorAbort = true;
    2164           0 :                 break;
    2165             :             }
    2166             : 
    2167          18 :             memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
    2168          18 :             const bool bIsNULL = DBFIsValueNULL(chOldType, pszOldField);
    2169             : 
    2170          18 :             if (nWidth != nOldWidth)
    2171             :             {
    2172          18 :                 if ((chOldType == 'N' || chOldType == 'F' ||
    2173          12 :                      chOldType == 'D') &&
    2174          12 :                     pszOldField[0] == ' ')
    2175             :                 {
    2176             :                     /* Strip leading spaces when truncating a numeric field */
    2177           4 :                     memmove(pszRecord + nOffset,
    2178           4 :                             pszRecord + nOffset + nOldWidth - nWidth, nWidth);
    2179             :                 }
    2180          18 :                 if (nOffset + nOldWidth < nOldRecordLength)
    2181             :                 {
    2182          15 :                     memmove(pszRecord + nOffset + nWidth,
    2183          15 :                             pszRecord + nOffset + nOldWidth,
    2184          15 :                             nOldRecordLength - (nOffset + nOldWidth));
    2185             :                 }
    2186             :             }
    2187             : 
    2188             :             /* Convert null value to the appropriate value of the new type */
    2189          18 :             if (bIsNULL)
    2190             :             {
    2191           7 :                 memset(pszRecord + nOffset, chFieldFill, nWidth);
    2192             :             }
    2193             : 
    2194          18 :             nRecordOffset =
    2195          18 :                 psDBF->nRecordLength * STATIC_CAST(SAOffset, iRecord) +
    2196          18 :                 psDBF->nHeaderLength;
    2197             : 
    2198             :             /* write record */
    2199          18 :             psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    2200          18 :             psDBF->sHooks.FWrite(pszRecord, psDBF->nRecordLength, 1, psDBF->fp);
    2201             :         }
    2202             : 
    2203           6 :         if (!errorAbort && psDBF->bWriteEndOfFileChar)
    2204             :         {
    2205           6 :             char ch = END_OF_FILE_CHARACTER;
    2206             : 
    2207           6 :             SAOffset nRecordOffset =
    2208           6 :                 psDBF->nRecordLength * STATIC_CAST(SAOffset, psDBF->nRecords) +
    2209           6 :                 psDBF->nHeaderLength;
    2210             : 
    2211           6 :             psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    2212           6 :             psDBF->sHooks.FWrite(&ch, 1, 1, psDBF->fp);
    2213             :         }
    2214             :         /* TODO: truncate file */
    2215             : 
    2216           6 :         free(pszRecord);
    2217           6 :         free(pszOldField);
    2218             :     }
    2219          15 :     else if (nWidth > nOldWidth)
    2220             :     {
    2221             :         char *pszRecord =
    2222           8 :             STATIC_CAST(char *, malloc(sizeof(char) * psDBF->nRecordLength));
    2223             :         char *pszOldField =
    2224           8 :             STATIC_CAST(char *, malloc(sizeof(char) * (nOldWidth + 1)));
    2225             : 
    2226           8 :         pszOldField[nOldWidth] = 0;
    2227             : 
    2228             :         /* move records to their new positions */
    2229          21 :         for (int iRecord = psDBF->nRecords - 1; iRecord >= 0; iRecord--)
    2230             :         {
    2231          13 :             SAOffset nRecordOffset =
    2232          13 :                 nOldRecordLength * STATIC_CAST(SAOffset, iRecord) +
    2233          13 :                 psDBF->nHeaderLength;
    2234             : 
    2235             :             /* load record */
    2236          13 :             psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    2237          13 :             if (psDBF->sHooks.FRead(pszRecord, nOldRecordLength, 1,
    2238          13 :                                     psDBF->fp) != 1)
    2239             :             {
    2240           0 :                 errorAbort = true;
    2241           0 :                 break;
    2242             :             }
    2243             : 
    2244          13 :             memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
    2245          13 :             const bool bIsNULL = DBFIsValueNULL(chOldType, pszOldField);
    2246             : 
    2247          13 :             if (nOffset + nOldWidth < nOldRecordLength)
    2248             :             {
    2249           8 :                 memmove(pszRecord + nOffset + nWidth,
    2250           8 :                         pszRecord + nOffset + nOldWidth,
    2251           8 :                         nOldRecordLength - (nOffset + nOldWidth));
    2252             :             }
    2253             : 
    2254             :             /* Convert null value to the appropriate value of the new type */
    2255          13 :             if (bIsNULL)
    2256             :             {
    2257           3 :                 memset(pszRecord + nOffset, chFieldFill, nWidth);
    2258             :             }
    2259             :             else
    2260             :             {
    2261          10 :                 if ((chOldType == 'N' || chOldType == 'F'))
    2262             :                 {
    2263             :                     /* Add leading spaces when expanding a numeric field */
    2264           5 :                     memmove(pszRecord + nOffset + nWidth - nOldWidth,
    2265           5 :                             pszRecord + nOffset, nOldWidth);
    2266           5 :                     memset(pszRecord + nOffset, ' ', nWidth - nOldWidth);
    2267             :                 }
    2268             :                 else
    2269             :                 {
    2270             :                     /* Add trailing spaces */
    2271           5 :                     memset(pszRecord + nOffset + nOldWidth, ' ',
    2272           5 :                            nWidth - nOldWidth);
    2273             :                 }
    2274             :             }
    2275             : 
    2276          13 :             nRecordOffset =
    2277          13 :                 psDBF->nRecordLength * STATIC_CAST(SAOffset, iRecord) +
    2278          13 :                 psDBF->nHeaderLength;
    2279             : 
    2280             :             /* write record */
    2281          13 :             psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    2282          13 :             psDBF->sHooks.FWrite(pszRecord, psDBF->nRecordLength, 1, psDBF->fp);
    2283             :         }
    2284             : 
    2285           8 :         if (!errorAbort && psDBF->bWriteEndOfFileChar)
    2286             :         {
    2287           8 :             char ch = END_OF_FILE_CHARACTER;
    2288             : 
    2289           8 :             SAOffset nRecordOffset =
    2290           8 :                 psDBF->nRecordLength * STATIC_CAST(SAOffset, psDBF->nRecords) +
    2291           8 :                 psDBF->nHeaderLength;
    2292             : 
    2293           8 :             psDBF->sHooks.FSeek(psDBF->fp, nRecordOffset, 0);
    2294           8 :             psDBF->sHooks.FWrite(&ch, 1, 1, psDBF->fp);
    2295             :         }
    2296             : 
    2297           8 :         free(pszRecord);
    2298           8 :         free(pszOldField);
    2299             :     }
    2300             : 
    2301          21 :     if (errorAbort)
    2302             :     {
    2303           0 :         psDBF->nCurrentRecord = -1;
    2304           0 :         psDBF->bCurrentRecordModified = TRUE;
    2305           0 :         psDBF->bUpdated = FALSE;
    2306             : 
    2307           0 :         return FALSE;
    2308             :     }
    2309          21 :     psDBF->nCurrentRecord = -1;
    2310          21 :     psDBF->bCurrentRecordModified = FALSE;
    2311          21 :     psDBF->bUpdated = TRUE;
    2312             : 
    2313          21 :     return TRUE;
    2314             : }
    2315             : 
    2316             : /************************************************************************/
    2317             : /*                    DBFSetWriteEndOfFileChar()                        */
    2318             : /************************************************************************/
    2319             : 
    2320       10022 : void SHPAPI_CALL DBFSetWriteEndOfFileChar(DBFHandle psDBF, int bWriteFlag)
    2321             : {
    2322       10022 :     psDBF->bWriteEndOfFileChar = bWriteFlag;
    2323       10022 : }

Generated by: LCOV version 1.14