LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/shape - dbfopen.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 802 966 83.0 %
Date: 2024-11-21 22:18:42 Functions: 40 47 85.1 %

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

Generated by: LCOV version 1.14