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

Generated by: LCOV version 1.14