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

Generated by: LCOV version 1.14