LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_datfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 658 1158 56.8 %
Date: 2024-05-05 20:23:34 Functions: 42 52 80.8 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_datfile.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABIDFile class used to handle
       7             :  *           reading/writing of the .DAT file
       8             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999-2001, Daniel Morissette
      12             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      13             :  *
      14             :  * Permission is hereby granted, free of charge, to any person obtaining a
      15             :  * copy of this software and associated documentation files (the "Software"),
      16             :  * to deal in the Software without restriction, including without limitation
      17             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      18             :  * and/or sell copies of the Software, and to permit persons to whom the
      19             :  * Software is furnished to do so, subject to the following conditions:
      20             :  *
      21             :  * The above copyright notice and this permission notice shall be included
      22             :  * in all copies or substantial portions of the Software.
      23             :  *
      24             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      25             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      26             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      27             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      28             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      29             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      30             :  * DEALINGS IN THE SOFTWARE.
      31             :  **********************************************************************/
      32             : 
      33             : #include "cpl_port.h"
      34             : #include "mitab.h"
      35             : 
      36             : #include <climits>
      37             : #include <cstdio>
      38             : #include <cstdlib>
      39             : #include <cstring>
      40             : #if HAVE_FCNTL_H
      41             : #include <fcntl.h>
      42             : #endif
      43             : #include <algorithm>
      44             : #include <string>
      45             : 
      46             : #include "cpl_conv.h"
      47             : #include "cpl_error.h"
      48             : #include "cpl_string.h"
      49             : #include "cpl_vsi.h"
      50             : #include "mitab_priv.h"
      51             : #include "ogr_core.h"
      52             : #include "ogr_feature.h"
      53             : #include "ogr_p.h"
      54             : 
      55             : /*=====================================================================
      56             :  *                      class TABDATFile
      57             :  *
      58             :  * Note that the .DAT files are .DBF files with some exceptions:
      59             :  *
      60             :  * All fields in the DBF header are defined as 'C' type (strings),
      61             :  * even for binary integers.  So we have to look in the associated .TAB
      62             :  * file to find the real field definition.
      63             :  *
      64             :  * Even though binary integers are defined as 'C' type, they are stored
      65             :  * in binary form inside a 4 bytes string field.
      66             :  *====================================================================*/
      67             : 
      68             : /**********************************************************************
      69             :  *                   TABDATFile::TABDATFile()
      70             :  *
      71             :  * Constructor.
      72             :  **********************************************************************/
      73        1448 : TABDATFile::TABDATFile(const char *pszEncoding)
      74             :     : m_pszFname(nullptr), m_fp(nullptr), m_eAccessMode(TABRead),
      75             :       m_eTableType(TABTableNative), m_poHeaderBlock(nullptr), m_numFields(-1),
      76             :       m_pasFieldDef(nullptr), m_poRecordBlock(nullptr), m_nBlockSize(0),
      77             :       m_nRecordSize(-1), m_nCurRecordId(-1), m_bCurRecordDeletedFlag(FALSE),
      78             :       m_numRecords(-1), m_nFirstRecordPtr(0), m_bWriteHeaderInitialized(FALSE),
      79             :       m_bWriteEOF(FALSE), m_bUpdated(FALSE),
      80        1448 :       m_osEncoding(pszEncoding), m_szBuffer{}
      81             : {
      82        1448 : }
      83             : 
      84             : /**********************************************************************
      85             :  *                   TABDATFile::~TABDATFile()
      86             :  *
      87             :  * Destructor.
      88             :  **********************************************************************/
      89        1448 : TABDATFile::~TABDATFile()
      90             : {
      91        1448 :     Close();
      92        1448 : }
      93             : 
      94             : /**********************************************************************
      95             :  *                   TABDATFile::Open()
      96             :  *
      97             :  * Compatibility layer with new interface.
      98             :  * Return 0 on success, -1 in case of failure.
      99             :  **********************************************************************/
     100             : 
     101           0 : int TABDATFile::Open(const char *pszFname, const char *pszAccess,
     102             :                      TABTableType eTableType)
     103             : {
     104             :     // cppcheck-suppress nullPointer
     105           0 :     if (STARTS_WITH_CI(pszAccess, "r"))
     106             :     {
     107           0 :         return Open(pszFname, TABRead, eTableType);
     108             :     }
     109           0 :     else if (STARTS_WITH_CI(pszAccess, "w"))
     110             :     {
     111           0 :         return Open(pszFname, TABWrite, eTableType);
     112             :     }
     113             :     else
     114             :     {
     115           0 :         CPLError(CE_Failure, CPLE_FileIO,
     116             :                  "Open() failed: access mode \"%s\" not supported", pszAccess);
     117           0 :         return -1;
     118             :     }
     119             : }
     120             : 
     121             : /**********************************************************************
     122             :  *                   TABDATFile::Open()
     123             :  *
     124             :  * Open a .DAT file, and initialize the structures to be ready to read
     125             :  * records from it.
     126             :  *
     127             :  * We currently support NATIVE and DBF tables for reading, and only
     128             :  * NATIVE tables for writing.
     129             :  *
     130             :  * Returns 0 on success, -1 on error.
     131             :  **********************************************************************/
     132        1478 : int TABDATFile::Open(const char *pszFname, TABAccess eAccess,
     133             :                      TABTableType eTableType /*=TABNativeTable*/)
     134             : {
     135        1478 :     if (m_fp)
     136             :     {
     137           0 :         CPLError(CE_Failure, CPLE_FileIO,
     138             :                  "Open() failed: object already contains an open file");
     139           0 :         return -1;
     140             :     }
     141             : 
     142             :     // Validate access mode and make sure we use binary access.
     143        1478 :     const char *pszAccess = nullptr;
     144        1478 :     if (eAccess == TABRead &&
     145           0 :         (eTableType == TABTableNative || eTableType == TABTableDBF))
     146             :     {
     147         254 :         pszAccess = "rb";
     148             :     }
     149        1224 :     else if (eAccess == TABWrite && eTableType == TABTableNative)
     150             :     {
     151         147 :         pszAccess = "wb+";
     152             :     }
     153        1077 :     else if (eAccess == TABReadWrite && eTableType == TABTableNative)
     154             :     {
     155        1077 :         pszAccess = "rb+";
     156             :     }
     157             :     else
     158             :     {
     159           0 :         CPLError(CE_Failure, CPLE_FileIO,
     160             :                  "Open() failed: access mode \"%d\" "
     161             :                  "not supported with eTableType=%d",
     162             :                  eAccess, eTableType);
     163           0 :         return -1;
     164             :     }
     165        1478 :     m_eAccessMode = eAccess;
     166             : 
     167             :     // Open file for reading.
     168        1478 :     m_pszFname = CPLStrdup(pszFname);
     169        1478 :     m_fp = VSIFOpenL(m_pszFname, pszAccess);
     170        1478 :     m_eTableType = eTableType;
     171             : 
     172        1478 :     if (m_fp == nullptr)
     173             :     {
     174           0 :         CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s", m_pszFname);
     175           0 :         CPLFree(m_pszFname);
     176           0 :         m_pszFname = nullptr;
     177           0 :         return -1;
     178             :     }
     179             : 
     180        1478 :     if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)
     181             :     {
     182             :         // READ ACCESS:
     183             :         // Read .DAT file header (record size, num records, etc...)
     184             :         // m_poHeaderBlock will be reused later to read field definition
     185        1331 :         m_poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE);
     186        1331 :         CPL_IGNORE_RET_VAL(m_poHeaderBlock->ReadFromFile(m_fp, 0, 32));
     187             : 
     188        1331 :         m_poHeaderBlock->ReadByte();  // Table type ??? 0x03
     189        1331 :         m_poHeaderBlock->ReadByte();  // Last update year
     190        1331 :         m_poHeaderBlock->ReadByte();  // Last update month
     191        1331 :         m_poHeaderBlock->ReadByte();  // Last update day
     192             : 
     193        1331 :         m_numRecords = m_poHeaderBlock->ReadInt32();
     194        1331 :         m_nFirstRecordPtr = m_poHeaderBlock->ReadInt16();
     195        1331 :         m_nRecordSize = m_poHeaderBlock->ReadInt16();
     196        1331 :         if (m_nFirstRecordPtr < 32 || m_nRecordSize <= 0 || m_numRecords < 0)
     197             :         {
     198           0 :             VSIFCloseL(m_fp);
     199           0 :             m_fp = nullptr;
     200           0 :             CPLFree(m_pszFname);
     201           0 :             m_pszFname = nullptr;
     202           0 :             delete m_poHeaderBlock;
     203           0 :             m_poHeaderBlock = nullptr;
     204           0 :             return -1;
     205             :         }
     206             : 
     207             :         // Limit number of records to avoid int overflow
     208        1331 :         if (m_numRecords > INT_MAX / m_nRecordSize ||
     209        1331 :             m_nFirstRecordPtr > INT_MAX - m_numRecords * m_nRecordSize)
     210             :         {
     211           0 :             m_numRecords = (INT_MAX - m_nFirstRecordPtr) / m_nRecordSize;
     212             :         }
     213             : 
     214        1331 :         m_numFields = m_nFirstRecordPtr / 32 - 1;
     215             : 
     216             :         // Read the field definitions.
     217             :         // First 32 bytes field definition starts at byte 32 in file.
     218        1331 :         m_pasFieldDef = static_cast<TABDATFieldDef *>(
     219        1331 :             CPLCalloc(m_numFields, sizeof(TABDATFieldDef)));
     220             : 
     221        2948 :         for (int i = 0; i < m_numFields; i++)
     222             :         {
     223        1617 :             m_poHeaderBlock->GotoByteInFile((i + 1) * 32);
     224        1617 :             m_poHeaderBlock->ReadBytes(
     225        1617 :                 11, reinterpret_cast<GByte *>(m_pasFieldDef[i].szName));
     226        1617 :             m_pasFieldDef[i].szName[10] = '\0';
     227        1617 :             m_pasFieldDef[i].cType =
     228        1617 :                 static_cast<char>(m_poHeaderBlock->ReadByte());
     229             : 
     230        1617 :             m_poHeaderBlock->ReadInt32();  // Skip Bytes 12-15
     231        1617 :             m_pasFieldDef[i].byLength = m_poHeaderBlock->ReadByte();
     232        1617 :             m_pasFieldDef[i].byDecimals = m_poHeaderBlock->ReadByte();
     233             : 
     234        1617 :             m_pasFieldDef[i].eTABType = TABFUnknown;
     235             :         }
     236             : 
     237             :         // Establish a good record block size to use based on record size, and
     238             :         // then create m_poRecordBlock.
     239             :         // Record block size has to be a multiple of record size.
     240        1331 :         m_nBlockSize = ((1024 / m_nRecordSize) + 1) * m_nRecordSize;
     241        1331 :         m_nBlockSize = std::min(m_nBlockSize, (m_numRecords * m_nRecordSize));
     242             : 
     243        1331 :         CPLAssert(m_poRecordBlock == nullptr);
     244        1331 :         m_poRecordBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
     245        1331 :         m_poRecordBlock->InitNewBlock(m_fp, m_nBlockSize);
     246        1331 :         m_poRecordBlock->SetFirstBlockPtr(m_nFirstRecordPtr);
     247             : 
     248        1331 :         m_bWriteHeaderInitialized = TRUE;
     249             :     }
     250             :     else
     251             :     {
     252             :         // WRITE ACCESS:
     253             :         // Set acceptable defaults for all class members.
     254             :         // The real header initialization will be done when the first
     255             :         // record is written.
     256         147 :         m_poHeaderBlock = nullptr;
     257             : 
     258         147 :         m_numRecords = 0;
     259         147 :         m_nFirstRecordPtr = 0;
     260         147 :         m_nRecordSize = 0;
     261         147 :         m_numFields = 0;
     262         147 :         m_pasFieldDef = nullptr;
     263         147 :         m_bWriteHeaderInitialized = FALSE;
     264             :     }
     265             : 
     266        1478 :     return 0;
     267             : }
     268             : 
     269             : /**********************************************************************
     270             :  *                   TABDATFile::Close()
     271             :  *
     272             :  * Close current file, and release all memory used.
     273             :  *
     274             :  * Returns 0 on success, -1 on error.
     275             :  **********************************************************************/
     276        2926 : int TABDATFile::Close()
     277             : {
     278        2926 :     if (m_fp == nullptr)
     279        1448 :         return 0;
     280             : 
     281             :     // Write access: Update the header with number of records, etc.
     282             :     // and add a CTRL-Z char at the end of the file.
     283        1478 :     if (m_eAccessMode != TABRead)
     284             :     {
     285        1224 :         SyncToDisk();
     286             :     }
     287             : 
     288             :     // Delete all structures
     289        1478 :     if (m_poHeaderBlock)
     290             :     {
     291        1478 :         delete m_poHeaderBlock;
     292        1478 :         m_poHeaderBlock = nullptr;
     293             :     }
     294             : 
     295        1478 :     if (m_poRecordBlock)
     296             :     {
     297        1478 :         delete m_poRecordBlock;
     298        1478 :         m_poRecordBlock = nullptr;
     299             :     }
     300             : 
     301             :     // Close file
     302        1478 :     VSIFCloseL(m_fp);
     303        1478 :     m_fp = nullptr;
     304             : 
     305        1478 :     CPLFree(m_pszFname);
     306        1478 :     m_pszFname = nullptr;
     307             : 
     308        1478 :     CPLFree(m_pasFieldDef);
     309        1478 :     m_pasFieldDef = nullptr;
     310             : 
     311        1478 :     m_numFields = -1;
     312        1478 :     m_numRecords = -1;
     313        1478 :     m_nFirstRecordPtr = 0;
     314        1478 :     m_nBlockSize = 0;
     315        1478 :     m_nRecordSize = -1;
     316        1478 :     m_nCurRecordId = -1;
     317        1478 :     m_bWriteHeaderInitialized = FALSE;
     318        1478 :     m_bWriteEOF = FALSE;
     319        1478 :     m_bUpdated = FALSE;
     320             : 
     321        1478 :     return 0;
     322             : }
     323             : 
     324             : /************************************************************************/
     325             : /*                            SyncToDisk()                             */
     326             : /************************************************************************/
     327             : 
     328        1343 : int TABDATFile::SyncToDisk()
     329             : {
     330        1343 :     if (m_fp == nullptr)
     331           0 :         return 0;
     332             : 
     333        1343 :     if (m_eAccessMode == TABRead)
     334             :     {
     335           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     336             :                  "SyncToDisk() can be used only with Write access.");
     337           0 :         return -1;
     338             :     }
     339             : 
     340        1343 :     if (!m_bUpdated && m_bWriteHeaderInitialized)
     341         111 :         return 0;
     342             : 
     343             :     // No need to call. CommitRecordToFile(). It is normally called by
     344             :     // TABFeature::WriteRecordToDATFile()
     345        1232 :     if (WriteHeader() != 0)
     346           0 :         return -1;
     347             : 
     348        1232 :     m_bUpdated = FALSE;
     349        1232 :     return 0;
     350             : }
     351             : 
     352             : /**********************************************************************
     353             :  *                   TABDATFile::InitWriteHeader()
     354             :  *
     355             :  * Init the header members to be ready to write the header and data records
     356             :  * to a newly created data file.
     357             :  *
     358             :  * Returns 0 on success, -1 on error.
     359             :  **********************************************************************/
     360         147 : int TABDATFile::InitWriteHeader()
     361             : {
     362         147 :     if (m_eAccessMode == TABRead || m_bWriteHeaderInitialized)
     363           0 :         return 0;
     364             : 
     365             :     // Compute values for Record size, header size, etc.
     366         147 :     m_nFirstRecordPtr = (m_numFields + 1) * 32 + 1;
     367             : 
     368         147 :     m_nRecordSize = 1;
     369         455 :     for (int i = 0; i < m_numFields; i++)
     370             :     {
     371         308 :         m_nRecordSize += m_pasFieldDef[i].byLength;
     372             :     }
     373             : 
     374             :     // Create m_poRecordBlock the size of a data record.
     375         147 :     m_nBlockSize = m_nRecordSize;
     376             : 
     377         147 :     CPLAssert(m_poRecordBlock == nullptr);
     378         147 :     m_poRecordBlock = new TABRawBinBlock(TABReadWrite, FALSE);
     379         147 :     m_poRecordBlock->InitNewBlock(m_fp, m_nBlockSize);
     380         147 :     m_poRecordBlock->SetFirstBlockPtr(m_nFirstRecordPtr);
     381             : 
     382             :     // Make sure this init. will be performed only once.
     383         147 :     m_bWriteHeaderInitialized = TRUE;
     384             : 
     385         147 :     return 0;
     386             : }
     387             : 
     388             : /**********************************************************************
     389             :  *                   TABDATFile::WriteHeader()
     390             :  *
     391             :  * Init the header members to be ready to write the header and data records
     392             :  * to a newly created data file.
     393             :  *
     394             :  * Returns 0 on success, -1 on error.
     395             :  **********************************************************************/
     396        1355 : int TABDATFile::WriteHeader()
     397             : {
     398        1355 :     if (m_eAccessMode == TABRead)
     399             :     {
     400           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     401             :                  "WriteHeader() can be used only with Write access.");
     402           0 :         return -1;
     403             :     }
     404             : 
     405        1355 :     if (!m_bWriteHeaderInitialized)
     406         147 :         InitWriteHeader();
     407             : 
     408             :     // Create a single block that will be used to generate the whole header.
     409        1355 :     if (m_poHeaderBlock == nullptr)
     410         147 :         m_poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE);
     411        1355 :     m_poHeaderBlock->InitNewBlock(m_fp, m_nFirstRecordPtr, 0);
     412             : 
     413             :     // First 32 bytes: main header block.
     414        1355 :     m_poHeaderBlock->WriteByte(0x03);  // Table type ??? 0x03
     415             : 
     416             :     // __TODO__ Write the correct update date value
     417        1355 :     m_poHeaderBlock->WriteByte(99);  // Last update year
     418        1355 :     m_poHeaderBlock->WriteByte(9);   // Last update month
     419        1355 :     m_poHeaderBlock->WriteByte(9);   // Last update day
     420             : 
     421        1355 :     m_poHeaderBlock->WriteInt32(m_numRecords);
     422        1355 :     m_poHeaderBlock->WriteInt16(static_cast<GInt16>(m_nFirstRecordPtr));
     423        1355 :     m_poHeaderBlock->WriteInt16(static_cast<GInt16>(m_nRecordSize));
     424             : 
     425        1355 :     m_poHeaderBlock->WriteZeros(20);  // Pad rest with zeros.
     426             : 
     427             :     // Field definitions follow.  Each field def is 32 bytes.
     428        3098 :     for (int i = 0; i < m_numFields; i++)
     429             :     {
     430        1743 :         m_poHeaderBlock->WriteBytes(
     431        1743 :             11, reinterpret_cast<GByte *>(m_pasFieldDef[i].szName));
     432        1743 :         m_poHeaderBlock->WriteByte(m_pasFieldDef[i].cType);
     433             : 
     434        1743 :         m_poHeaderBlock->WriteInt32(0);  // Skip Bytes 12-15
     435             : 
     436        1743 :         m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byLength);
     437        1743 :         m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byDecimals);
     438             : 
     439        1743 :         m_poHeaderBlock->WriteZeros(14);  // Pad rest with zeros
     440             :     }
     441             : 
     442             :     // Header ends with a 0x0d character.
     443        1355 :     m_poHeaderBlock->WriteByte(0x0d);
     444             : 
     445             :     // Write the block to the file and return.
     446        1355 :     return m_poHeaderBlock->CommitToFile();
     447             : }
     448             : 
     449             : /**********************************************************************
     450             :  *                   TABDATFile::GetNumFields()
     451             :  *
     452             :  * Return the number of fields in this table.
     453             :  *
     454             :  * Returns a value >= 0 on success, -1 on error.
     455             :  **********************************************************************/
     456      550495 : int TABDATFile::GetNumFields()
     457             : {
     458      550495 :     return m_numFields;
     459             : }
     460             : 
     461             : /**********************************************************************
     462             :  *                   TABDATFile::GetNumRecords()
     463             :  *
     464             :  * Return the number of records in this table.
     465             :  *
     466             :  * Returns a value >= 0 on success, -1 on error.
     467             :  **********************************************************************/
     468        1418 : int TABDATFile::GetNumRecords()
     469             : {
     470        1418 :     return m_numRecords;
     471             : }
     472             : 
     473             : /**********************************************************************
     474             :  *                   TABDATFile::GetRecordBlock()
     475             :  *
     476             :  * Return a TABRawBinBlock reference positioned at the beginning of the
     477             :  * specified record and ready to read (or write) field values from/to it.
     478             :  * In read access, the returned block is guaranteed to contain at least one
     479             :  * full record of data, and in write access, it is at least big enough to
     480             :  * hold one full record.
     481             :  *
     482             :  * Note that record ids are positive and start at 1.
     483             :  *
     484             :  * In Write access, CommitRecordToFile() MUST be called after the
     485             :  * data items have been written to the record, otherwise the record
     486             :  * will never make it to the file.
     487             :  *
     488             :  * Returns a reference to the TABRawBinBlock on success or NULL on error.
     489             :  * The returned pointer is a reference to a block object owned by this
     490             :  * TABDATFile object and should not be freed by the caller.
     491             :  **********************************************************************/
     492      703995 : TABRawBinBlock *TABDATFile::GetRecordBlock(int nRecordId)
     493             : {
     494      703995 :     if (m_fp == nullptr)
     495             :     {
     496           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     497             :                  "Operation not supported on closed table.");
     498           0 :         return nullptr;
     499             :     }
     500             : 
     501      703995 :     m_bCurRecordDeletedFlag = FALSE;
     502      703995 :     m_bWriteEOF = FALSE;
     503             : 
     504      703995 :     if (m_eAccessMode == TABRead || nRecordId <= m_numRecords)
     505             :     {
     506             :         // READ ACCESS
     507      689045 :         const int nFileOffset =
     508      689045 :             m_nFirstRecordPtr + (nRecordId - 1) * m_nRecordSize;
     509             : 
     510             :         // Move record block pointer to the right location.
     511      689045 :         if (m_poRecordBlock == nullptr || nRecordId < 1 ||
     512     2067140 :             nRecordId > m_numRecords ||
     513      689045 :             m_poRecordBlock->GotoByteInFile(nFileOffset) != 0)
     514             :         {
     515           0 :             CPLError(CE_Failure, CPLE_FileIO,
     516             :                      "Failed reading .DAT record block for record #%d in %s",
     517             :                      nRecordId, m_pszFname);
     518           0 :             return nullptr;
     519             :         }
     520             : 
     521             :         // The first char of the record is a ' ' for an active record, or
     522             :         // '*' for a deleted one.
     523             :         // In the case of a deleted record, we simply return default
     524             :         // values for each attribute... this is what MapInfo seems to do
     525             :         // when it takes a .TAB with deleted records and exports it to .MIF
     526      689045 :         if (m_poRecordBlock->ReadByte() != ' ')
     527             :         {
     528        1320 :             m_bCurRecordDeletedFlag = TRUE;
     529      689045 :         }
     530             :     }
     531       14950 :     else if (nRecordId > 0)
     532             :     {
     533             :         // WRITE ACCESS
     534             : 
     535             :         // Before writing the first record, we must generate the file
     536             :         // header.  We will also initialize class members such as record
     537             :         // size, etc. and will create m_poRecordBlock.
     538       14950 :         if (!m_bWriteHeaderInitialized)
     539             :         {
     540         123 :             WriteHeader();
     541             :         }
     542             : 
     543       14950 :         m_bUpdated = TRUE;
     544             : 
     545       14950 :         m_numRecords = std::max(nRecordId, m_numRecords);
     546       14950 :         if (nRecordId == m_numRecords)
     547       14950 :             m_bWriteEOF = TRUE;
     548             : 
     549       14950 :         const int nFileOffset =
     550       14950 :             m_nFirstRecordPtr + (nRecordId - 1) * m_nRecordSize;
     551             : 
     552       14950 :         m_poRecordBlock->InitNewBlock(m_fp, m_nRecordSize, nFileOffset);
     553             : 
     554             :         // The first char of the record is the active/deleted flag.
     555             :         // Automatically set it to ' ' (active).
     556       14950 :         m_poRecordBlock->WriteByte(' ');
     557             :     }
     558             : 
     559      703995 :     m_nCurRecordId = nRecordId;
     560             : 
     561      703995 :     return m_poRecordBlock;
     562             : }
     563             : 
     564             : /**********************************************************************
     565             :  *                   TABDATFile::CommitRecordToFile()
     566             :  *
     567             :  * Commit the data record previously initialized with GetRecordBlock()
     568             :  * to the file.  This function must be called after writing the data
     569             :  * values to a record otherwise the record will never make it to the
     570             :  * file.
     571             :  *
     572             :  * Returns 0 on success, -1 on error.
     573             :  **********************************************************************/
     574       15163 : int TABDATFile::CommitRecordToFile()
     575             : {
     576       15163 :     if (m_eAccessMode == TABRead || m_poRecordBlock == nullptr)
     577           0 :         return -1;
     578             : 
     579       15163 :     if (m_poRecordBlock->CommitToFile() != 0)
     580           0 :         return -1;
     581             : 
     582             :     // If this is the end of file, write EOF character.
     583       15163 :     if (m_bWriteEOF)
     584             :     {
     585       14949 :         m_bWriteEOF = FALSE;
     586       14949 :         char cEOF = 26;
     587       14949 :         if (VSIFSeekL(m_fp, 0L, SEEK_END) == 0)
     588       14949 :             VSIFWriteL(&cEOF, 1, 1, m_fp);
     589             :     }
     590             : 
     591       15163 :     return 0;
     592             : }
     593             : 
     594             : /**********************************************************************
     595             :  *                   TABDATFile::MarkAsDeleted()
     596             :  *
     597             :  * Returns 0 on success, -1 on error.
     598             :  **********************************************************************/
     599         715 : int TABDATFile::MarkAsDeleted()
     600             : {
     601         715 :     if (m_eAccessMode == TABRead || m_poRecordBlock == nullptr)
     602           0 :         return -1;
     603             : 
     604         715 :     const int nFileOffset =
     605         715 :         m_nFirstRecordPtr + (m_nCurRecordId - 1) * m_nRecordSize;
     606             : 
     607         715 :     if (m_poRecordBlock->GotoByteInFile(nFileOffset) != 0)
     608           0 :         return -1;
     609             : 
     610         715 :     m_poRecordBlock->WriteByte('*');
     611             : 
     612         715 :     if (m_poRecordBlock->CommitToFile() != 0)
     613           0 :         return -1;
     614             : 
     615         715 :     m_bCurRecordDeletedFlag = TRUE;
     616         715 :     m_bUpdated = TRUE;
     617             : 
     618         715 :     return 0;
     619             : }
     620             : 
     621             : /**********************************************************************
     622             :  *                   TABDATFile::MarkRecordAsExisting()
     623             :  *
     624             :  * Returns 0 on success, -1 on error.
     625             :  **********************************************************************/
     626       15078 : int TABDATFile::MarkRecordAsExisting()
     627             : {
     628       15078 :     if (m_eAccessMode == TABRead || m_poRecordBlock == nullptr)
     629           0 :         return -1;
     630             : 
     631       15078 :     const int nFileOffset =
     632       15078 :         m_nFirstRecordPtr + (m_nCurRecordId - 1) * m_nRecordSize;
     633             : 
     634       15078 :     if (m_poRecordBlock->GotoByteInFile(nFileOffset) != 0)
     635           0 :         return -1;
     636             : 
     637       15078 :     m_poRecordBlock->WriteByte(' ');
     638             : 
     639       15078 :     m_bCurRecordDeletedFlag = FALSE;
     640       15078 :     m_bUpdated = TRUE;
     641             : 
     642       15078 :     return 0;
     643             : }
     644             : 
     645             : /**********************************************************************
     646             :  *                   TABDATFile::ValidateFieldInfoFromTAB()
     647             :  *
     648             :  * Check that the value read from the .TAB file by the caller are
     649             :  * consistent with what is found in the .DAT header.
     650             :  *
     651             :  * Note that field ids are positive and start at 0.
     652             :  *
     653             :  * We have to use this function when opening a file for reading since
     654             :  * the .DAT file does not contain the full field types information...
     655             :  * a .DAT file is actually a .DBF file in which the .DBF types are
     656             :  * handled in a special way... type 'C' fields are used to store binary
     657             :  * values for most MapInfo types.
     658             :  *
     659             :  * For TABTableDBF, we actually have no validation to do since all types
     660             :  * are stored as strings internally, so we'll just convert from string.
     661             :  *
     662             :  * Returns a value >= 0 if OK, -1 on error.
     663             :  **********************************************************************/
     664        1520 : int TABDATFile::ValidateFieldInfoFromTAB(int iField, const char *pszName,
     665             :                                          TABFieldType eType, int nWidth,
     666             :                                          int nPrecision)
     667             : {
     668        1520 :     int i = iField;  // Just to make things shorter
     669             : 
     670        1520 :     if (m_pasFieldDef == nullptr || iField < 0 || iField >= m_numFields)
     671             :     {
     672          16 :         CPLError(
     673             :             CE_Failure, CPLE_FileIO,
     674             :             "Invalid field %d (%s) in .TAB header. %s contains only %d fields.",
     675          16 :             iField + 1, pszName, m_pszFname, m_pasFieldDef ? m_numFields : 0);
     676          16 :         return -1;
     677             :     }
     678             : 
     679             :     // We used to check that the .TAB field name matched the .DAT
     680             :     // name stored internally, but apparently some tools that rename table
     681             :     // field names only update the .TAB file and not the .DAT, so we won't
     682             :     // do that name validation any more... we'll just check the type.
     683             :     //
     684             :     // With TABTableNative, we have to validate the field sizes as well
     685             :     // because .DAT files use char fields to store binary values.
     686             :     // With TABTableDBF, no need to validate field type since all
     687             :     // fields are stored as strings internally.
     688             : 
     689        1504 :     if ((m_eTableType == TABTableNative &&
     690         198 :          ((eType == TABFChar && (m_pasFieldDef[i].cType != 'C' ||
     691        1504 :                                  m_pasFieldDef[i].byLength != nWidth)) ||
     692           7 :           (eType == TABFDecimal &&
     693           7 :            (m_pasFieldDef[i].cType != 'N' ||
     694           7 :             m_pasFieldDef[i].byLength != nWidth ||
     695        1504 :             m_pasFieldDef[i].byDecimals != nPrecision)) ||
     696        1226 :           (eType == TABFInteger &&
     697        1504 :            (m_pasFieldDef[i].cType != 'C' || m_pasFieldDef[i].byLength != 4)) ||
     698           1 :           (eType == TABFSmallInt &&
     699        1504 :            (m_pasFieldDef[i].cType != 'C' || m_pasFieldDef[i].byLength != 2)) ||
     700           1 :           (eType == TABFLargeInt &&
     701        1504 :            (m_pasFieldDef[i].cType != 'C' || m_pasFieldDef[i].byLength != 8)) ||
     702          30 :           (eType == TABFFloat &&
     703        1504 :            (m_pasFieldDef[i].cType != 'C' || m_pasFieldDef[i].byLength != 8)) ||
     704          20 :           (eType == TABFDate &&
     705        1504 :            (m_pasFieldDef[i].cType != 'C' || m_pasFieldDef[i].byLength != 4)) ||
     706           2 :           (eType == TABFTime &&
     707        1504 :            (m_pasFieldDef[i].cType != 'C' || m_pasFieldDef[i].byLength != 4)) ||
     708          18 :           (eType == TABFDateTime &&
     709        1504 :            (m_pasFieldDef[i].cType != 'C' || m_pasFieldDef[i].byLength != 8)) ||
     710           1 :           (eType == TABFLogical &&
     711           1 :            (m_pasFieldDef[i].cType != 'L' || m_pasFieldDef[i].byLength != 1)))))
     712             :     {
     713           0 :         CPLError(CE_Failure, CPLE_FileIO,
     714             :                  "Definition of field %d (%s) from .TAB file does not match "
     715             :                  "what is found in %s (name=%s, type=%c, width=%d, prec=%d)",
     716           0 :                  iField + 1, pszName, m_pszFname, m_pasFieldDef[i].szName,
     717           0 :                  m_pasFieldDef[i].cType, m_pasFieldDef[i].byLength,
     718           0 :                  m_pasFieldDef[i].byDecimals);
     719           0 :         return -1;
     720             :     }
     721             : 
     722        1504 :     m_pasFieldDef[i].eTABType = eType;
     723             : 
     724        1504 :     return 0;
     725             : }
     726             : 
     727             : /**********************************************************************
     728             :  *                  TABDATFileSetFieldDefinition()
     729             :  *
     730             :  **********************************************************************/
     731         326 : static int TABDATFileSetFieldDefinition(TABDATFieldDef *psFieldDef,
     732             :                                         const char *pszName, TABFieldType eType,
     733             :                                         int nWidth, int nPrecision)
     734             : {
     735             :     // Validate field width.
     736         326 :     if (nWidth > 254)
     737             :     {
     738           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     739             :                  "Invalid size (%d) for field '%s'.  "
     740             :                  "Size must be 254 or less.",
     741             :                  nWidth, pszName);
     742           0 :         return -1;
     743             :     }
     744             : 
     745             :     // Map fields with width=0 (variable length in OGR) to a valid default.
     746         326 :     if (eType == TABFDecimal && nWidth == 0)
     747           0 :         nWidth = 20;
     748         326 :     else if (nWidth == 0)
     749           0 :         nWidth = 254;  // char fields.
     750             : 
     751         326 :     strncpy(psFieldDef->szName, pszName, sizeof(psFieldDef->szName) - 1);
     752         326 :     psFieldDef->szName[sizeof(psFieldDef->szName) - 1] = '\0';
     753         326 :     psFieldDef->eTABType = eType;
     754         326 :     psFieldDef->byLength = static_cast<GByte>(nWidth);
     755         326 :     psFieldDef->byDecimals = static_cast<GByte>(nPrecision);
     756             : 
     757         326 :     switch (eType)
     758             :     {
     759         187 :         case TABFChar:
     760         187 :             psFieldDef->cType = 'C';
     761         187 :             break;
     762           3 :         case TABFDecimal:
     763           3 :             psFieldDef->cType = 'N';
     764           3 :             break;
     765          73 :         case TABFInteger:
     766          73 :             psFieldDef->cType = 'C';
     767          73 :             psFieldDef->byLength = 4;
     768          73 :             break;
     769           0 :         case TABFSmallInt:
     770           0 :             psFieldDef->cType = 'C';
     771           0 :             psFieldDef->byLength = 2;
     772           0 :             break;
     773           1 :         case TABFLargeInt:
     774           1 :             psFieldDef->cType = 'C';
     775           1 :             psFieldDef->byLength = 8;
     776           1 :             break;
     777          24 :         case TABFFloat:
     778          24 :             psFieldDef->cType = 'C';
     779          24 :             psFieldDef->byLength = 8;
     780          24 :             break;
     781          18 :         case TABFDate:
     782          18 :             psFieldDef->cType = 'C';
     783          18 :             psFieldDef->byLength = 4;
     784          18 :             break;
     785           2 :         case TABFTime:
     786           2 :             psFieldDef->cType = 'C';
     787           2 :             psFieldDef->byLength = 4;
     788           2 :             break;
     789          18 :         case TABFDateTime:
     790          18 :             psFieldDef->cType = 'C';
     791          18 :             psFieldDef->byLength = 8;
     792          18 :             break;
     793           0 :         case TABFLogical:
     794           0 :             psFieldDef->cType = 'L';
     795           0 :             psFieldDef->byLength = 1;
     796           0 :             break;
     797           0 :         default:
     798           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     799             :                      "Unsupported field type for field `%s'", pszName);
     800           0 :             return -1;
     801             :     }
     802             : 
     803         326 :     return 0;
     804             : }
     805             : 
     806             : /**********************************************************************
     807             :  *                   TABDATFile::AddField()
     808             :  *
     809             :  * Create a new field (column) in a newly created table.  This function
     810             :  * must be called after the file has been opened, but before writing the
     811             :  * first record.
     812             :  *
     813             :  * Returns the new field index (a value >= 0) if OK, -1 on error.
     814             :  **********************************************************************/
     815         322 : int TABDATFile::AddField(const char *pszName, TABFieldType eType, int nWidth,
     816             :                          int nPrecision /* =0 */)
     817             : {
     818         322 :     if (m_fp == nullptr)
     819             :     {
     820           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     821             :                  "Operation not supported on closed table.");
     822           0 :         return -1;
     823             :     }
     824         322 :     if (m_eAccessMode == TABRead || m_eTableType != TABTableNative)
     825             :     {
     826           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     827             :                  "Operation not supported on read-only files or "
     828             :                  "on non-native table.");
     829           0 :         return -1;
     830             :     }
     831             : 
     832             :     TABDATFieldDef sFieldDef;
     833         322 :     if (TABDATFileSetFieldDefinition(&sFieldDef, pszName, eType, nWidth,
     834         322 :                                      nPrecision) < 0)
     835           0 :         return -1;
     836             : 
     837         322 :     if (m_numFields < 0)
     838           0 :         m_numFields = 0;
     839             : 
     840         322 :     m_numFields++;
     841         322 :     m_pasFieldDef = static_cast<TABDATFieldDef *>(
     842         322 :         CPLRealloc(m_pasFieldDef, m_numFields * sizeof(TABDATFieldDef)));
     843         322 :     memcpy(&m_pasFieldDef[m_numFields - 1], &sFieldDef, sizeof(sFieldDef));
     844             : 
     845             :     // If there are already records, we cannot update in place.
     846             :     // Create a temporary .dat.tmp in which we create the new structure
     847             :     // and then copy the widen records.
     848         322 :     if (m_numRecords > 0)
     849             :     {
     850          14 :         TABDATFile oTempFile(GetEncoding());
     851          14 :         CPLString osOriginalFile(m_pszFname);
     852          14 :         CPLString osTmpFile(m_pszFname);
     853          14 :         osTmpFile += ".tmp";
     854          14 :         if (oTempFile.Open(osTmpFile.c_str(), TABWrite) != 0)
     855           0 :             return -1;
     856             : 
     857             :         // Create field structure.
     858          60 :         for (int i = 0; i < m_numFields; i++)
     859             :         {
     860          46 :             oTempFile.AddField(
     861          46 :                 m_pasFieldDef[i].szName, m_pasFieldDef[i].eTABType,
     862          46 :                 m_pasFieldDef[i].byLength, m_pasFieldDef[i].byDecimals);
     863             :         }
     864             : 
     865          14 :         GByte *pabyRecord = static_cast<GByte *>(CPLMalloc(m_nRecordSize));
     866             : 
     867             :         // Copy records.
     868          44 :         for (int j = 0; j < m_numRecords; j++)
     869             :         {
     870          60 :             if (GetRecordBlock(1 + j) == nullptr ||
     871          30 :                 oTempFile.GetRecordBlock(1 + j) == nullptr)
     872             :             {
     873           0 :                 CPLFree(pabyRecord);
     874           0 :                 oTempFile.Close();
     875           0 :                 VSIUnlink(osTmpFile);
     876           0 :                 return -1;
     877             :             }
     878          30 :             if (m_bCurRecordDeletedFlag)
     879             :             {
     880           0 :                 oTempFile.MarkAsDeleted();
     881             :             }
     882             :             else
     883             :             {
     884          30 :                 if (m_poRecordBlock->ReadBytes(m_nRecordSize - 1, pabyRecord) !=
     885          30 :                         0 ||
     886          30 :                     oTempFile.m_poRecordBlock->WriteBytes(m_nRecordSize - 1,
     887          60 :                                                           pabyRecord) != 0 ||
     888          30 :                     oTempFile.m_poRecordBlock->WriteZeros(
     889          30 :                         m_pasFieldDef[m_numFields - 1].byLength) != 0)
     890             :                 {
     891           0 :                     CPLFree(pabyRecord);
     892           0 :                     oTempFile.Close();
     893           0 :                     VSIUnlink(osTmpFile);
     894           0 :                     return -1;
     895             :                 }
     896          30 :                 oTempFile.CommitRecordToFile();
     897             :             }
     898             :         }
     899             : 
     900          14 :         CPLFree(pabyRecord);
     901             : 
     902             :         // Close temporary file.
     903          14 :         oTempFile.Close();
     904             : 
     905             :         // Backup field definitions as we will need to set the TABFieldType.
     906             :         TABDATFieldDef *pasFieldDefTmp = static_cast<TABDATFieldDef *>(
     907          14 :             CPLMalloc(m_numFields * sizeof(TABDATFieldDef)));
     908          14 :         memcpy(pasFieldDefTmp, m_pasFieldDef,
     909          14 :                m_numFields * sizeof(TABDATFieldDef));
     910             : 
     911          14 :         m_numFields--;  // So that Close() doesn't see the new field.
     912          14 :         Close();
     913             : 
     914             :         // Move temporary file as main .data file and reopen it.
     915          14 :         VSIUnlink(osOriginalFile);
     916          14 :         VSIRename(osTmpFile, osOriginalFile);
     917          14 :         if (Open(osOriginalFile, TABReadWrite) < 0)
     918             :         {
     919           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen %s",
     920             :                      osOriginalFile.c_str());
     921           0 :             CPLFree(pasFieldDefTmp);
     922           0 :             return -1;
     923             :         }
     924             : 
     925             :         // Restore saved TABFieldType.
     926          60 :         for (int i = 0; i < m_numFields; i++)
     927             :         {
     928          46 :             m_pasFieldDef[i].eTABType = pasFieldDefTmp[i].eTABType;
     929             :         }
     930          14 :         CPLFree(pasFieldDefTmp);
     931             :     }
     932             : 
     933         322 :     return 0;
     934             : }
     935             : 
     936             : /************************************************************************/
     937             : /*                            DeleteField()                             */
     938             : /************************************************************************/
     939             : 
     940           2 : int TABDATFile::DeleteField(int iField)
     941             : {
     942           2 :     if (m_fp == nullptr)
     943             :     {
     944           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     945             :                  "Operation not supported on closed table.");
     946           0 :         return -1;
     947             :     }
     948           2 :     if (m_eAccessMode == TABRead || m_eTableType != TABTableNative)
     949             :     {
     950           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     951             :                  "Operation not supported on read-only files or "
     952             :                  "on non-native table.");
     953           0 :         return -1;
     954             :     }
     955             : 
     956           2 :     if (iField < 0 || iField >= m_numFields)
     957             :     {
     958           0 :         CPLError(CE_Failure, CPLE_IllegalArg, "Invalid field index: %d",
     959             :                  iField);
     960           0 :         return -1;
     961             :     }
     962             : 
     963             :     // If no records have been written, then just remove from the field
     964             :     // definition array.
     965           2 :     if (m_numRecords <= 0)
     966             :     {
     967           0 :         if (iField < m_numFields - 1)
     968             :         {
     969           0 :             memmove(m_pasFieldDef + iField, m_pasFieldDef + iField + 1,
     970           0 :                     (m_numFields - 1 - iField) * sizeof(TABDATFieldDef));
     971             :         }
     972           0 :         m_numFields--;
     973           0 :         return 0;
     974             :     }
     975             : 
     976           2 :     if (m_numFields == 1)
     977             :     {
     978           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     979             :                  "Cannot delete the single remaining field.");
     980           0 :         return -1;
     981             :     }
     982             : 
     983             :     // Otherwise we need to do a temporary file.
     984           4 :     TABDATFile oTempFile(GetEncoding());
     985           4 :     CPLString osOriginalFile(m_pszFname);
     986           4 :     CPLString osTmpFile(m_pszFname);
     987           2 :     osTmpFile += ".tmp";
     988           2 :     if (oTempFile.Open(osTmpFile.c_str(), TABWrite) != 0)
     989           0 :         return -1;
     990             : 
     991             :     // Create field structure.
     992           2 :     int nRecordSizeBefore = 0;
     993           2 :     int nRecordSizeAfter = 0;
     994          11 :     for (int i = 0; i < m_numFields; i++)
     995             :     {
     996           9 :         if (i != iField)
     997             :         {
     998           7 :             if (i < iField)
     999           3 :                 nRecordSizeBefore += m_pasFieldDef[i].byLength;
    1000             :             else /* if( i > iField ) */
    1001           4 :                 nRecordSizeAfter += m_pasFieldDef[i].byLength;
    1002           7 :             oTempFile.AddField(
    1003           7 :                 m_pasFieldDef[i].szName, m_pasFieldDef[i].eTABType,
    1004           7 :                 m_pasFieldDef[i].byLength, m_pasFieldDef[i].byDecimals);
    1005             :         }
    1006             :     }
    1007             : 
    1008           2 :     CPLAssert(nRecordSizeBefore + m_pasFieldDef[iField].byLength +
    1009             :                   nRecordSizeAfter ==
    1010             :               m_nRecordSize - 1);
    1011             : 
    1012           2 :     GByte *pabyRecord = static_cast<GByte *>(CPLMalloc(m_nRecordSize));
    1013             : 
    1014             :     // Copy records.
    1015           8 :     for (int j = 0; j < m_numRecords; j++)
    1016             :     {
    1017          12 :         if (GetRecordBlock(1 + j) == nullptr ||
    1018           6 :             oTempFile.GetRecordBlock(1 + j) == nullptr)
    1019             :         {
    1020           0 :             CPLFree(pabyRecord);
    1021           0 :             oTempFile.Close();
    1022           0 :             VSIUnlink(osTmpFile);
    1023           0 :             return -1;
    1024             :         }
    1025           6 :         if (m_bCurRecordDeletedFlag)
    1026             :         {
    1027           0 :             oTempFile.MarkAsDeleted();
    1028             :         }
    1029             :         else
    1030             :         {
    1031           6 :             if (m_poRecordBlock->ReadBytes(m_nRecordSize - 1, pabyRecord) !=
    1032           6 :                     0 ||
    1033           3 :                 (nRecordSizeBefore > 0 &&
    1034           3 :                  oTempFile.m_poRecordBlock->WriteBytes(nRecordSizeBefore,
    1035          18 :                                                        pabyRecord) != 0) ||
    1036           3 :                 (nRecordSizeAfter > 0 &&
    1037           3 :                  oTempFile.m_poRecordBlock->WriteBytes(
    1038           3 :                      nRecordSizeAfter, pabyRecord + nRecordSizeBefore +
    1039           3 :                                            m_pasFieldDef[iField].byLength) !=
    1040             :                      0))
    1041             :             {
    1042           0 :                 CPLFree(pabyRecord);
    1043           0 :                 oTempFile.Close();
    1044           0 :                 VSIUnlink(osTmpFile);
    1045           0 :                 return -1;
    1046             :             }
    1047           6 :             oTempFile.CommitRecordToFile();
    1048             :         }
    1049             :     }
    1050             : 
    1051           2 :     CPLFree(pabyRecord);
    1052             : 
    1053             :     // Close temporary file.
    1054           2 :     oTempFile.Close();
    1055             : 
    1056             :     // Backup field definitions as we will need to set the TABFieldType.
    1057             :     TABDATFieldDef *pasFieldDefTmp = static_cast<TABDATFieldDef *>(
    1058           2 :         CPLMalloc(m_numFields * sizeof(TABDATFieldDef)));
    1059           2 :     memcpy(pasFieldDefTmp, m_pasFieldDef, m_numFields * sizeof(TABDATFieldDef));
    1060             : 
    1061           2 :     Close();
    1062             : 
    1063             :     // Move temporary file as main .data file and reopen it.
    1064           2 :     VSIUnlink(osOriginalFile);
    1065           2 :     VSIRename(osTmpFile, osOriginalFile);
    1066           2 :     if (Open(osOriginalFile, TABReadWrite) < 0)
    1067             :     {
    1068           0 :         CPLFree(pasFieldDefTmp);
    1069           0 :         return -1;
    1070             :     }
    1071             : 
    1072             :     // Restore saved TABFieldType.
    1073           9 :     for (int i = 0; i < m_numFields; i++)
    1074             :     {
    1075           7 :         if (i < iField)
    1076           3 :             m_pasFieldDef[i].eTABType = pasFieldDefTmp[i].eTABType;
    1077             :         else
    1078           4 :             m_pasFieldDef[i].eTABType = pasFieldDefTmp[i + 1].eTABType;
    1079             :     }
    1080           2 :     CPLFree(pasFieldDefTmp);
    1081             : 
    1082           2 :     return 0;
    1083             : }
    1084             : 
    1085             : /************************************************************************/
    1086             : /*                           ReorderFields()                            */
    1087             : /************************************************************************/
    1088             : 
    1089          10 : int TABDATFile::ReorderFields(int *panMap)
    1090             : {
    1091          10 :     if (m_fp == nullptr)
    1092             :     {
    1093           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1094             :                  "Operation not supported on closed table.");
    1095           0 :         return -1;
    1096             :     }
    1097          10 :     if (m_eAccessMode == TABRead || m_eTableType != TABTableNative)
    1098             :     {
    1099           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1100             :                  "Operation not supported on read-only files or "
    1101             :                  "on non-native table.");
    1102           0 :         return -1;
    1103             :     }
    1104             : 
    1105          10 :     if (m_numFields == 0)
    1106           0 :         return 0;
    1107             : 
    1108          10 :     OGRErr eErr = OGRCheckPermutation(panMap, m_numFields);
    1109          10 :     if (eErr != OGRERR_NONE)
    1110           0 :         return -1;
    1111             : 
    1112             :     // If no records have been written, then just reorder the field
    1113             :     // definition array.
    1114          10 :     if (m_numRecords <= 0)
    1115             :     {
    1116             :         TABDATFieldDef *pasFieldDefTmp = static_cast<TABDATFieldDef *>(
    1117           0 :             CPLMalloc(m_numFields * sizeof(TABDATFieldDef)));
    1118           0 :         memcpy(pasFieldDefTmp, m_pasFieldDef,
    1119           0 :                m_numFields * sizeof(TABDATFieldDef));
    1120           0 :         for (int i = 0; i < m_numFields; i++)
    1121             :         {
    1122           0 :             memcpy(m_pasFieldDef + i, pasFieldDefTmp + panMap[i],
    1123             :                    sizeof(TABDATFieldDef));
    1124             :         }
    1125           0 :         CPLFree(pasFieldDefTmp);
    1126           0 :         return 0;
    1127             :     }
    1128             : 
    1129             :     // We could theoretically update in place, but a sudden interruption
    1130             :     // would leave the file in a undefined state.
    1131             : 
    1132          20 :     TABDATFile oTempFile(GetEncoding());
    1133          20 :     CPLString osOriginalFile(m_pszFname);
    1134          20 :     CPLString osTmpFile(m_pszFname);
    1135          10 :     osTmpFile += ".tmp";
    1136          10 :     if (oTempFile.Open(osTmpFile.c_str(), TABWrite) != 0)
    1137           0 :         return -1;
    1138             : 
    1139             :     // Create field structure.
    1140             :     int *panOldOffset =
    1141          10 :         static_cast<int *>(CPLMalloc(m_numFields * sizeof(int)));
    1142          52 :     for (int i = 0; i < m_numFields; i++)
    1143             :     {
    1144          42 :         int iBefore = panMap[i];
    1145          42 :         if (i == 0)
    1146          10 :             panOldOffset[i] = 0;
    1147             :         else
    1148          32 :             panOldOffset[i] =
    1149          32 :                 panOldOffset[i - 1] + m_pasFieldDef[i - 1].byLength;
    1150          42 :         oTempFile.AddField(
    1151          42 :             m_pasFieldDef[iBefore].szName, m_pasFieldDef[iBefore].eTABType,
    1152          42 :             m_pasFieldDef[iBefore].byLength, m_pasFieldDef[iBefore].byDecimals);
    1153             :     }
    1154             : 
    1155          10 :     GByte *pabyRecord = static_cast<GByte *>(CPLMalloc(m_nRecordSize));
    1156             : 
    1157             :     // Copy records.
    1158          48 :     for (int j = 0; j < m_numRecords; j++)
    1159             :     {
    1160          76 :         if (GetRecordBlock(1 + j) == nullptr ||
    1161          38 :             oTempFile.GetRecordBlock(1 + j) == nullptr)
    1162             :         {
    1163           0 :             CPLFree(pabyRecord);
    1164           0 :             CPLFree(panOldOffset);
    1165           0 :             oTempFile.Close();
    1166           0 :             VSIUnlink(osTmpFile);
    1167           0 :             return -1;
    1168             :         }
    1169          38 :         if (m_bCurRecordDeletedFlag)
    1170             :         {
    1171           0 :             oTempFile.MarkAsDeleted();
    1172             :         }
    1173             :         else
    1174             :         {
    1175          38 :             if (m_poRecordBlock->ReadBytes(m_nRecordSize - 1, pabyRecord) != 0)
    1176             :             {
    1177           0 :                 CPLFree(pabyRecord);
    1178           0 :                 CPLFree(panOldOffset);
    1179           0 :                 oTempFile.Close();
    1180           0 :                 VSIUnlink(osTmpFile);
    1181           0 :                 return -1;
    1182             :             }
    1183         196 :             for (int i = 0; i < m_numFields; i++)
    1184             :             {
    1185         158 :                 int iBefore = panMap[i];
    1186         316 :                 if (oTempFile.m_poRecordBlock->WriteBytes(
    1187         158 :                         m_pasFieldDef[iBefore].byLength,
    1188         158 :                         pabyRecord + panOldOffset[iBefore]) != 0)
    1189             :                 {
    1190           0 :                     CPLFree(pabyRecord);
    1191           0 :                     CPLFree(panOldOffset);
    1192           0 :                     oTempFile.Close();
    1193           0 :                     VSIUnlink(osTmpFile);
    1194           0 :                     return -1;
    1195             :                 }
    1196             :             }
    1197             : 
    1198          38 :             oTempFile.CommitRecordToFile();
    1199             :         }
    1200             :     }
    1201             : 
    1202          10 :     CPLFree(pabyRecord);
    1203          10 :     CPLFree(panOldOffset);
    1204             : 
    1205          10 :     oTempFile.Close();
    1206             : 
    1207             :     // Backup field definitions as we will need to set the TABFieldType.
    1208             :     TABDATFieldDef *pasFieldDefTmp = static_cast<TABDATFieldDef *>(
    1209          10 :         CPLMalloc(m_numFields * sizeof(TABDATFieldDef)));
    1210          10 :     memcpy(pasFieldDefTmp, m_pasFieldDef, m_numFields * sizeof(TABDATFieldDef));
    1211             : 
    1212             :     // Close ourselves.
    1213          10 :     Close();
    1214             : 
    1215             :     // Move temporary file as main .data file and reopen it.
    1216          10 :     VSIUnlink(osOriginalFile);
    1217          10 :     VSIRename(osTmpFile, osOriginalFile);
    1218          10 :     if (Open(osOriginalFile, TABReadWrite) < 0)
    1219             :     {
    1220           0 :         CPLFree(pasFieldDefTmp);
    1221           0 :         return -1;
    1222             :     }
    1223             : 
    1224             :     // Restore saved TABFieldType.
    1225          52 :     for (int i = 0; i < m_numFields; i++)
    1226             :     {
    1227          42 :         int iBefore = panMap[i];
    1228          42 :         m_pasFieldDef[i].eTABType = pasFieldDefTmp[iBefore].eTABType;
    1229             :     }
    1230          10 :     CPLFree(pasFieldDefTmp);
    1231             : 
    1232          10 :     return 0;
    1233             : }
    1234             : 
    1235             : /************************************************************************/
    1236             : /*                           AlterFieldDefn()                           */
    1237             : /************************************************************************/
    1238             : 
    1239           5 : int TABDATFile::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
    1240             :                                int nFlags)
    1241             : {
    1242           5 :     if (m_fp == nullptr)
    1243             :     {
    1244           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1245             :                  "Operation not supported on closed table.");
    1246           0 :         return -1;
    1247             :     }
    1248           5 :     if (m_eAccessMode == TABRead || m_eTableType != TABTableNative)
    1249             :     {
    1250           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1251             :                  "Operation not supported on read-only files or "
    1252             :                  "on non-native table.");
    1253           0 :         return -1;
    1254             :     }
    1255             : 
    1256           5 :     if (iField < 0 || iField >= m_numFields)
    1257             :     {
    1258           0 :         CPLError(CE_Failure, CPLE_IllegalArg, "Invalid field index: %d",
    1259             :                  iField);
    1260           0 :         return -1;
    1261             :     }
    1262             : 
    1263           5 :     TABFieldType eTABType = m_pasFieldDef[iField].eTABType;
    1264           5 :     int nWidth = m_pasFieldDef[iField].byLength;
    1265           5 :     int nPrecision = m_pasFieldDef[iField].byDecimals;
    1266           5 :     int nWidthDummy = 0;
    1267           5 :     int nPrecisionDummy = 0;
    1268           5 :     if (nFlags & ALTER_TYPE_FLAG)
    1269             :     {
    1270           5 :         if (IMapInfoFile::GetTABType(poNewFieldDefn, &eTABType, &nWidthDummy,
    1271           5 :                                      &nPrecisionDummy) < 0)
    1272           0 :             return -1;
    1273             :     }
    1274           5 :     if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
    1275             :     {
    1276             :         TABFieldType eTABTypeDummy;
    1277           5 :         if (IMapInfoFile::GetTABType(poNewFieldDefn, &eTABTypeDummy, &nWidth,
    1278           5 :                                      &nPrecision) < 0)
    1279           0 :             return -1;
    1280             :     }
    1281             : 
    1282           5 :     if ((nFlags & ALTER_TYPE_FLAG) &&
    1283           5 :         eTABType != m_pasFieldDef[iField].eTABType)
    1284             :     {
    1285           2 :         if (eTABType != TABFChar)
    1286             :         {
    1287           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1288             :                      "Can only convert to OFTString");
    1289           0 :             return -1;
    1290             :         }
    1291           2 :         if ((nFlags & ALTER_WIDTH_PRECISION_FLAG) == 0)
    1292           0 :             nWidth = 254;
    1293             :     }
    1294             : 
    1295           5 :     if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
    1296             :     {
    1297           5 :         if (eTABType != TABFChar && nWidth != m_pasFieldDef[iField].byLength)
    1298             :         {
    1299           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    1300             :                      "Resizing only supported on String fields");
    1301           1 :             return -1;
    1302             :         }
    1303             :     }
    1304             : 
    1305           4 :     if (nFlags & ALTER_NAME_FLAG)
    1306             :     {
    1307           4 :         strncpy(m_pasFieldDef[iField].szName, poNewFieldDefn->GetNameRef(),
    1308             :                 sizeof(m_pasFieldDef[iField].szName) - 1);
    1309           4 :         m_pasFieldDef[iField].szName[sizeof(m_pasFieldDef[iField].szName) - 1] =
    1310             :             '\0';
    1311             :         // If renaming is the only operation, then nothing more to do.
    1312           4 :         if (nFlags == ALTER_NAME_FLAG)
    1313             :         {
    1314           0 :             m_bUpdated = TRUE;
    1315           0 :             return 0;
    1316             :         }
    1317             :     }
    1318             : 
    1319           4 :     if (m_numRecords <= 0)
    1320             :     {
    1321           0 :         if ((nFlags & ALTER_TYPE_FLAG) &&
    1322           0 :             eTABType != m_pasFieldDef[iField].eTABType)
    1323             :         {
    1324             :             TABDATFieldDef sFieldDef;
    1325           0 :             TABDATFileSetFieldDefinition(&sFieldDef,
    1326           0 :                                          m_pasFieldDef[iField].szName, eTABType,
    1327           0 :                                          m_pasFieldDef[iField].byLength,
    1328           0 :                                          m_pasFieldDef[iField].byDecimals);
    1329           0 :             memcpy(&m_pasFieldDef[iField], &sFieldDef, sizeof(sFieldDef));
    1330             :         }
    1331           0 :         if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
    1332             :         {
    1333           0 :             m_pasFieldDef[iField].byLength = static_cast<GByte>(nWidth);
    1334           0 :             m_pasFieldDef[iField].byDecimals = static_cast<GByte>(nPrecision);
    1335             :         }
    1336           0 :         return 0;
    1337             :     }
    1338             : 
    1339             :     // Otherwise we need to do a temporary file.
    1340           8 :     TABDATFile oTempFile(GetEncoding());
    1341           8 :     CPLString osOriginalFile(m_pszFname);
    1342           8 :     CPLString osTmpFile(m_pszFname);
    1343           4 :     osTmpFile += ".tmp";
    1344           4 :     if (oTempFile.Open(osTmpFile.c_str(), TABWrite) != 0)
    1345           0 :         return -1;
    1346             : 
    1347             :     // Create field structure.
    1348           4 :     int nRecordSizeBefore = 0;
    1349           4 :     int nRecordSizeAfter = 0;
    1350             :     TABDATFieldDef sFieldDef;
    1351           4 :     sFieldDef.eTABType = TABFUnknown;
    1352           4 :     sFieldDef.byLength = 0;
    1353           4 :     sFieldDef.byDecimals = 0;
    1354           4 :     TABDATFileSetFieldDefinition(&sFieldDef, m_pasFieldDef[iField].szName,
    1355             :                                  eTABType, nWidth, nPrecision);
    1356             : 
    1357          22 :     for (int i = 0; i < m_numFields; i++)
    1358             :     {
    1359          18 :         if (i != iField)
    1360             :         {
    1361          14 :             if (i < iField)
    1362           4 :                 nRecordSizeBefore += m_pasFieldDef[i].byLength;
    1363             :             else /*if( i > iField )*/
    1364          10 :                 nRecordSizeAfter += m_pasFieldDef[i].byLength;
    1365          14 :             oTempFile.AddField(
    1366          14 :                 m_pasFieldDef[i].szName, m_pasFieldDef[i].eTABType,
    1367          14 :                 m_pasFieldDef[i].byLength, m_pasFieldDef[i].byDecimals);
    1368             :         }
    1369             :         else
    1370             :         {
    1371           4 :             oTempFile.AddField(sFieldDef.szName, sFieldDef.eTABType,
    1372           4 :                                sFieldDef.byLength, sFieldDef.byDecimals);
    1373             :         }
    1374             :     }
    1375             : 
    1376           4 :     GByte *pabyRecord = static_cast<GByte *>(CPLMalloc(m_nRecordSize));
    1377           4 :     char *pabyNewField = static_cast<char *>(CPLMalloc(sFieldDef.byLength + 1));
    1378             : 
    1379             :     // Copy records.
    1380          16 :     for (int j = 0; j < m_numRecords; j++)
    1381             :     {
    1382          24 :         if (GetRecordBlock(1 + j) == nullptr ||
    1383          12 :             oTempFile.GetRecordBlock(1 + j) == nullptr)
    1384             :         {
    1385           0 :             CPLFree(pabyRecord);
    1386           0 :             CPLFree(pabyNewField);
    1387           0 :             oTempFile.Close();
    1388           0 :             VSIUnlink(osTmpFile);
    1389           0 :             return -1;
    1390             :         }
    1391          12 :         if (m_bCurRecordDeletedFlag)
    1392             :         {
    1393           0 :             oTempFile.MarkAsDeleted();
    1394             :         }
    1395             :         else
    1396             :         {
    1397          18 :             if (nRecordSizeBefore > 0 &&
    1398           6 :                 (m_poRecordBlock->ReadBytes(nRecordSizeBefore, pabyRecord) !=
    1399           6 :                      0 ||
    1400           6 :                  oTempFile.m_poRecordBlock->WriteBytes(nRecordSizeBefore,
    1401           6 :                                                        pabyRecord) != 0))
    1402             :             {
    1403           0 :                 CPLFree(pabyRecord);
    1404           0 :                 CPLFree(pabyNewField);
    1405           0 :                 oTempFile.Close();
    1406           0 :                 VSIUnlink(osTmpFile);
    1407           0 :                 return -1;
    1408             :             }
    1409             : 
    1410          12 :             memset(pabyNewField, 0, sFieldDef.byLength + 1);
    1411          12 :             if (m_pasFieldDef[iField].eTABType == TABFChar)
    1412             :             {
    1413           6 :                 strncpy(pabyNewField,
    1414           6 :                         ReadCharField(m_pasFieldDef[iField].byLength),
    1415           6 :                         sFieldDef.byLength);
    1416             :             }
    1417           6 :             else if (m_pasFieldDef[iField].eTABType == TABFInteger)
    1418             :             {
    1419           6 :                 snprintf(pabyNewField, sFieldDef.byLength, "%d",
    1420           6 :                          ReadIntegerField(m_pasFieldDef[iField].byLength));
    1421             :             }
    1422           0 :             else if (m_pasFieldDef[iField].eTABType == TABFSmallInt)
    1423             :             {
    1424           0 :                 snprintf(pabyNewField, sFieldDef.byLength, "%d",
    1425           0 :                          ReadSmallIntField(m_pasFieldDef[iField].byLength));
    1426             :             }
    1427           0 :             else if (m_pasFieldDef[iField].eTABType == TABFLargeInt)
    1428             :             {
    1429           0 :                 snprintf(pabyNewField, sFieldDef.byLength, CPL_FRMT_GIB,
    1430           0 :                          ReadLargeIntField(m_pasFieldDef[iField].byLength));
    1431             :             }
    1432           0 :             else if (m_pasFieldDef[iField].eTABType == TABFFloat)
    1433             :             {
    1434           0 :                 CPLsnprintf(pabyNewField, sFieldDef.byLength, "%.18f",
    1435           0 :                             ReadFloatField(m_pasFieldDef[iField].byLength));
    1436             :             }
    1437           0 :             else if (m_pasFieldDef[iField].eTABType == TABFDecimal)
    1438             :             {
    1439           0 :                 CPLsnprintf(pabyNewField, sFieldDef.byLength, "%.18f",
    1440           0 :                             ReadFloatField(m_pasFieldDef[iField].byLength));
    1441             :             }
    1442           0 :             else if (m_pasFieldDef[iField].eTABType == TABFLogical)
    1443             :             {
    1444           0 :                 strncpy(pabyNewField,
    1445           0 :                         ReadLogicalField(m_pasFieldDef[iField].byLength),
    1446           0 :                         sFieldDef.byLength);
    1447             :             }
    1448           0 :             else if (m_pasFieldDef[iField].eTABType == TABFDate)
    1449             :             {
    1450           0 :                 strncpy(pabyNewField,
    1451           0 :                         ReadDateField(m_pasFieldDef[iField].byLength),
    1452           0 :                         sFieldDef.byLength);
    1453             :             }
    1454           0 :             else if (m_pasFieldDef[iField].eTABType == TABFTime)
    1455             :             {
    1456           0 :                 strncpy(pabyNewField,
    1457           0 :                         ReadTimeField(m_pasFieldDef[iField].byLength),
    1458           0 :                         sFieldDef.byLength);
    1459             :             }
    1460           0 :             else if (m_pasFieldDef[iField].eTABType == TABFDateTime)
    1461             :             {
    1462           0 :                 strncpy(pabyNewField,
    1463           0 :                         ReadDateTimeField(m_pasFieldDef[iField].byLength),
    1464           0 :                         sFieldDef.byLength);
    1465             :             }
    1466             : 
    1467          36 :             if (oTempFile.m_poRecordBlock->WriteBytes(
    1468          12 :                     sFieldDef.byLength,
    1469          24 :                     reinterpret_cast<GByte *>(pabyNewField)) != 0 ||
    1470          12 :                 (nRecordSizeAfter > 0 &&
    1471          12 :                  (m_poRecordBlock->ReadBytes(nRecordSizeAfter, pabyRecord) !=
    1472          12 :                       0 ||
    1473          12 :                   oTempFile.m_poRecordBlock->WriteBytes(nRecordSizeAfter,
    1474          12 :                                                         pabyRecord) != 0)))
    1475             :             {
    1476           0 :                 CPLFree(pabyRecord);
    1477           0 :                 CPLFree(pabyNewField);
    1478           0 :                 oTempFile.Close();
    1479           0 :                 VSIUnlink(osTmpFile);
    1480           0 :                 return -1;
    1481             :             }
    1482          12 :             oTempFile.CommitRecordToFile();
    1483             :         }
    1484             :     }
    1485             : 
    1486           4 :     CPLFree(pabyRecord);
    1487           4 :     CPLFree(pabyNewField);
    1488             : 
    1489           4 :     oTempFile.Close();
    1490             : 
    1491             :     // Backup field definitions as we will need to set the TABFieldType.
    1492             :     TABDATFieldDef *pasFieldDefTmp = static_cast<TABDATFieldDef *>(
    1493           4 :         CPLMalloc(m_numFields * sizeof(TABDATFieldDef)));
    1494           4 :     memcpy(pasFieldDefTmp, m_pasFieldDef, m_numFields * sizeof(TABDATFieldDef));
    1495             : 
    1496           4 :     Close();
    1497             : 
    1498             :     // Move temporary file as main .data file and reopen it.
    1499           4 :     VSIUnlink(osOriginalFile);
    1500           4 :     VSIRename(osTmpFile, osOriginalFile);
    1501           4 :     if (Open(osOriginalFile, TABReadWrite) < 0)
    1502             :     {
    1503           0 :         CPLFree(pasFieldDefTmp);
    1504           0 :         return -1;
    1505             :     }
    1506             : 
    1507             :     // Restore saved TABFieldType.
    1508          22 :     for (int i = 0; i < m_numFields; i++)
    1509             :     {
    1510          18 :         if (i != iField)
    1511          14 :             m_pasFieldDef[i].eTABType = pasFieldDefTmp[i].eTABType;
    1512             :         else
    1513           4 :             m_pasFieldDef[i].eTABType = eTABType;
    1514             :     }
    1515           4 :     CPLFree(pasFieldDefTmp);
    1516             : 
    1517           4 :     return 0;
    1518             : }
    1519             : 
    1520             : /**********************************************************************
    1521             :  *                   TABDATFile::GetFieldType()
    1522             :  *
    1523             :  * Returns the native field type for field # nFieldId as previously set
    1524             :  * by ValidateFieldInfoFromTAB().
    1525             :  *
    1526             :  * Note that field ids are positive and start at 0.
    1527             :  **********************************************************************/
    1528      553083 : TABFieldType TABDATFile::GetFieldType(int nFieldId)
    1529             : {
    1530      553083 :     if (m_pasFieldDef == nullptr || nFieldId < 0 || nFieldId >= m_numFields)
    1531           0 :         return TABFUnknown;
    1532             : 
    1533      553083 :     return m_pasFieldDef[nFieldId].eTABType;
    1534             : }
    1535             : 
    1536             : /**********************************************************************
    1537             :  *                   TABDATFile::GetFieldWidth()
    1538             :  *
    1539             :  * Returns the width for field # nFieldId as previously read from the
    1540             :  * .DAT header.
    1541             :  *
    1542             :  * Note that field ids are positive and start at 0.
    1543             :  **********************************************************************/
    1544      537424 : int TABDATFile::GetFieldWidth(int nFieldId)
    1545             : {
    1546      537424 :     if (m_pasFieldDef == nullptr || nFieldId < 0 || nFieldId >= m_numFields)
    1547           0 :         return 0;
    1548             : 
    1549      537424 :     return m_pasFieldDef[nFieldId].byLength;
    1550             : }
    1551             : 
    1552             : /**********************************************************************
    1553             :  *                   TABDATFile::GetFieldPrecision()
    1554             :  *
    1555             :  * Returns the precision for field # nFieldId as previously read from the
    1556             :  * .DAT header.
    1557             :  *
    1558             :  * Note that field ids are positive and start at 0.
    1559             :  **********************************************************************/
    1560           4 : int TABDATFile::GetFieldPrecision(int nFieldId)
    1561             : {
    1562           4 :     if (m_pasFieldDef == nullptr || nFieldId < 0 || nFieldId >= m_numFields)
    1563           0 :         return 0;
    1564             : 
    1565           4 :     return m_pasFieldDef[nFieldId].byDecimals;
    1566             : }
    1567             : 
    1568             : /**********************************************************************
    1569             :  *                   TABDATFile::ReadCharField()
    1570             :  *
    1571             :  * Read the character field value at the current position in the data
    1572             :  * block.
    1573             :  *
    1574             :  * Use GetRecordBlock() to position the data block to the beginning of
    1575             :  * a record before attempting to read values.
    1576             :  *
    1577             :  * nWidth is the field length, as defined in the .DAT header.
    1578             :  *
    1579             :  * Returns a reference to an internal buffer that will be valid only until
    1580             :  * the next field is read, or "" if the operation failed, in which case
    1581             :  * CPLError() will have been called.
    1582             :  **********************************************************************/
    1583        1320 : const char *TABDATFile::ReadCharField(int nWidth)
    1584             : {
    1585             :     // If current record has been deleted, then return an acceptable
    1586             :     // default value.
    1587        1320 :     if (m_bCurRecordDeletedFlag)
    1588           0 :         return "";
    1589             : 
    1590        1320 :     if (m_poRecordBlock == nullptr)
    1591             :     {
    1592           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1593             :                  "Can't read field value: file is not opened.");
    1594           0 :         return "";
    1595             :     }
    1596             : 
    1597        1320 :     if (nWidth < 1 || nWidth > 255)
    1598             :     {
    1599           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1600             :                  "Illegal width for a char field: %d", nWidth);
    1601           0 :         return "";
    1602             :     }
    1603             : 
    1604        2640 :     if (m_poRecordBlock->ReadBytes(nWidth,
    1605        1320 :                                    reinterpret_cast<GByte *>(m_szBuffer)) != 0)
    1606           0 :         return "";
    1607             : 
    1608        1320 :     m_szBuffer[nWidth] = '\0';
    1609             : 
    1610             :     // NATIVE tables are padded with '\0' chars, but DBF tables are padded
    1611             :     // with spaces... get rid of the trailing spaces.
    1612        1320 :     if (m_eTableType == TABTableDBF)
    1613             :     {
    1614           0 :         int nLen = static_cast<int>(strlen(m_szBuffer)) - 1;
    1615           0 :         while (nLen >= 0 && m_szBuffer[nLen] == ' ')
    1616           0 :             m_szBuffer[nLen--] = '\0';
    1617             :     }
    1618             : 
    1619        1320 :     return m_szBuffer;
    1620             : }
    1621             : 
    1622             : /**********************************************************************
    1623             :  *                   TABDATFile::ReadIntegerField()
    1624             :  *
    1625             :  * Read the integer field value at the current position in the data
    1626             :  * block.
    1627             :  *
    1628             :  * Note: nWidth is used only with TABTableDBF types.
    1629             :  *
    1630             :  * CPLError() will have been called if something fails.
    1631             :  **********************************************************************/
    1632      535105 : GInt32 TABDATFile::ReadIntegerField(int nWidth)
    1633             : {
    1634             :     // If current record has been deleted, then return an acceptable
    1635             :     // default value.
    1636      535105 :     if (m_bCurRecordDeletedFlag)
    1637           0 :         return 0;
    1638             : 
    1639      535105 :     if (m_poRecordBlock == nullptr)
    1640             :     {
    1641           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1642             :                  "Can't read field value: file is not opened.");
    1643           0 :         return 0;
    1644             :     }
    1645             : 
    1646      535105 :     if (m_eTableType == TABTableDBF)
    1647           0 :         return atoi(ReadCharField(nWidth));
    1648             : 
    1649      535105 :     return m_poRecordBlock->ReadInt32();
    1650             : }
    1651             : 
    1652             : /**********************************************************************
    1653             :  *                   TABDATFile::ReadSmallIntField()
    1654             :  *
    1655             :  * Read the smallint field value at the current position in the data
    1656             :  * block.
    1657             :  *
    1658             :  * Note: nWidth is used only with TABTableDBF types.
    1659             :  *
    1660             :  * CPLError() will have been called if something fails.
    1661             :  **********************************************************************/
    1662           1 : GInt16 TABDATFile::ReadSmallIntField(int nWidth)
    1663             : {
    1664             :     // If current record has been deleted, then return an acceptable
    1665             :     // default value.
    1666           1 :     if (m_bCurRecordDeletedFlag)
    1667           0 :         return 0;
    1668             : 
    1669           1 :     if (m_poRecordBlock == nullptr)
    1670             :     {
    1671           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1672             :                  "Can't read field value: file is not opened.");
    1673           0 :         return 0;
    1674             :     }
    1675             : 
    1676           1 :     if (m_eTableType == TABTableDBF)
    1677           0 :         return static_cast<GInt16>(atoi(ReadCharField(nWidth)));
    1678             : 
    1679           1 :     return m_poRecordBlock->ReadInt16();
    1680             : }
    1681             : 
    1682             : /**********************************************************************
    1683             :  *                   TABDATFile::ReadLargeIntField()
    1684             :  *
    1685             :  * Read the largeint field value at the current position in the data
    1686             :  * block.
    1687             :  *
    1688             :  * Note: nWidth is used only with TABTableDBF types.
    1689             :  *
    1690             :  * CPLError() will have been called if something fails.
    1691             :  **********************************************************************/
    1692           1 : GInt64 TABDATFile::ReadLargeIntField(int nWidth)
    1693             : {
    1694             :     // If current record has been deleted, then return an acceptable
    1695             :     // default value.
    1696           1 :     if (m_bCurRecordDeletedFlag)
    1697           0 :         return 0;
    1698             : 
    1699           1 :     if (m_poRecordBlock == nullptr)
    1700             :     {
    1701           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1702             :                  "Can't read field value: file is not opened.");
    1703           0 :         return 0;
    1704             :     }
    1705             : 
    1706           1 :     if (m_eTableType == TABTableDBF)
    1707           0 :         return static_cast<GIntBig>(CPLAtoGIntBig(ReadCharField(nWidth)));
    1708             : 
    1709           1 :     return m_poRecordBlock->ReadInt64();
    1710             : }
    1711             : 
    1712             : /**********************************************************************
    1713             :  *                   TABDATFile::ReadFloatField()
    1714             :  *
    1715             :  * Read the float field value at the current position in the data
    1716             :  * block.
    1717             :  *
    1718             :  * Note: nWidth is used only with TABTableDBF types.
    1719             :  *
    1720             :  * CPLError() will have been called if something fails.
    1721             :  **********************************************************************/
    1722         582 : double TABDATFile::ReadFloatField(int nWidth)
    1723             : {
    1724             :     // If current record has been deleted, then return an acceptable
    1725             :     // default value.
    1726         582 :     if (m_bCurRecordDeletedFlag)
    1727           0 :         return 0.0;
    1728             : 
    1729         582 :     if (m_poRecordBlock == nullptr)
    1730             :     {
    1731           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1732             :                  "Can't read field value: file is not opened.");
    1733           0 :         return 0.0;
    1734             :     }
    1735             : 
    1736         582 :     if (m_eTableType == TABTableDBF)
    1737           0 :         return CPLAtof(ReadCharField(nWidth));
    1738             : 
    1739         582 :     return m_poRecordBlock->ReadDouble();
    1740             : }
    1741             : 
    1742             : /**********************************************************************
    1743             :  *                   TABDATFile::ReadLogicalField()
    1744             :  *
    1745             :  * Read the logical field value at the current position in the data
    1746             :  * block.
    1747             :  *
    1748             :  * The file contains either 0 or 1, and we return a string with
    1749             :  * "F" (false) or "T" (true)
    1750             :  *
    1751             :  * Note: nWidth is used only with TABTableDBF types.
    1752             :  *
    1753             :  * CPLError() will have been called if something fails.
    1754             :  **********************************************************************/
    1755           1 : const char *TABDATFile::ReadLogicalField(int nWidth)
    1756             : {
    1757             :     // If current record has been deleted, then return an acceptable
    1758             :     // default value.
    1759           1 :     if (m_bCurRecordDeletedFlag)
    1760           0 :         return "F";
    1761             : 
    1762           1 :     if (m_poRecordBlock == nullptr)
    1763             :     {
    1764           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1765             :                  "Can't read field value: file is not opened.");
    1766           0 :         return "";
    1767             :     }
    1768             : 
    1769           1 :     bool bValue = false;
    1770           1 :     if (m_eTableType == TABTableDBF)
    1771             :     {
    1772           0 :         const char *pszVal = ReadCharField(nWidth);
    1773           0 :         bValue = pszVal && strchr("1YyTt", pszVal[0]) != nullptr;
    1774             :     }
    1775             :     else
    1776             :     {
    1777             :         // In Native tables, we are guaranteed it is 1 byte with 0/1 value
    1778           1 :         bValue = CPL_TO_BOOL(m_poRecordBlock->ReadByte());
    1779             :     }
    1780             : 
    1781           1 :     return bValue ? "T" : "F";
    1782             : }
    1783             : 
    1784             : /**********************************************************************
    1785             :  *                   TABDATFile::ReadDateField()
    1786             :  *
    1787             :  * Read the logical field value at the current position in the data
    1788             :  * block.
    1789             :  *
    1790             :  * A date field is a 4 bytes binary value in which the first byte is
    1791             :  * the day, followed by 1 byte for the month, and 2 bytes for the year.
    1792             :  *
    1793             :  * We return an 8 chars string in the format "YYYYMMDD"
    1794             :  *
    1795             :  * Note: nWidth is used only with TABTableDBF types.
    1796             :  *
    1797             :  * Returns a reference to an internal buffer that will be valid only until
    1798             :  * the next field is read, or "" if the operation failed, in which case
    1799             :  * CPLError() will have been called.
    1800             :  **********************************************************************/
    1801           0 : const char *TABDATFile::ReadDateField(int nWidth)
    1802             : {
    1803           0 :     int nDay = 0;
    1804           0 :     int nMonth = 0;
    1805           0 :     int nYear = 0;
    1806           0 :     int status = ReadDateField(nWidth, &nYear, &nMonth, &nDay);
    1807             : 
    1808           0 :     if (status == -1)
    1809           0 :         return "";
    1810             : 
    1811           0 :     snprintf(m_szBuffer, sizeof(m_szBuffer), "%4.4d%2.2d%2.2d", nYear, nMonth,
    1812             :              nDay);
    1813             : 
    1814           0 :     return m_szBuffer;
    1815             : }
    1816             : 
    1817          24 : int TABDATFile::ReadDateField(int nWidth, int *nYear, int *nMonth, int *nDay)
    1818             : {
    1819             :     // If current record has been deleted, then return an acceptable
    1820             :     // default value.
    1821          24 :     if (m_bCurRecordDeletedFlag)
    1822           0 :         return -1;
    1823             : 
    1824          24 :     if (m_poRecordBlock == nullptr)
    1825             :     {
    1826           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1827             :                  "Can't read field value: file is not opened.");
    1828           0 :         return -1;
    1829             :     }
    1830             : 
    1831             :     // With .DBF files, the value should already be
    1832             :     // stored in YYYYMMDD format according to DBF specs.
    1833          24 :     if (m_eTableType == TABTableDBF)
    1834             :     {
    1835           0 :         strcpy(m_szBuffer, ReadCharField(nWidth));
    1836           0 :         sscanf(m_szBuffer, "%4d%2d%2d", nYear, nMonth, nDay);
    1837             :     }
    1838             :     else
    1839             :     {
    1840          24 :         *nYear = m_poRecordBlock->ReadInt16();
    1841          24 :         *nMonth = m_poRecordBlock->ReadByte();
    1842          24 :         *nDay = m_poRecordBlock->ReadByte();
    1843             :     }
    1844             : 
    1845          48 :     if (CPLGetLastErrorType() == CE_Failure ||
    1846          24 :         (*nYear == 0 && *nMonth == 0 && *nDay == 0))
    1847          17 :         return -1;
    1848             : 
    1849           7 :     return 0;
    1850             : }
    1851             : 
    1852             : /**********************************************************************
    1853             :  *                   TABDATFile::ReadTimeField()
    1854             :  *
    1855             :  * Read the Time field value at the current position in the data
    1856             :  * block.
    1857             :  *
    1858             :  * A time field is a 4 bytes binary value which represents the number
    1859             :  * of milliseconds since midnight.
    1860             :  *
    1861             :  * We return a 9 char string in the format "HHMMSSMMM"
    1862             :  *
    1863             :  * Note: nWidth is used only with TABTableDBF types.
    1864             :  *
    1865             :  * Returns a reference to an internal buffer that will be valid only until
    1866             :  * the next field is read, or "" if the operation failed, in which case
    1867             :  * CPLError() will have been called.
    1868             :  **********************************************************************/
    1869           0 : const char *TABDATFile::ReadTimeField(int nWidth)
    1870             : {
    1871           0 :     int nHour = 0;
    1872           0 :     int nMinute = 0;
    1873           0 :     int nSecond = 0;
    1874           0 :     int nMS = 0;
    1875           0 :     int status = ReadTimeField(nWidth, &nHour, &nMinute, &nSecond, &nMS);
    1876             : 
    1877           0 :     if (status == -1)
    1878           0 :         return "";
    1879             : 
    1880           0 :     snprintf(m_szBuffer, sizeof(m_szBuffer), "%2.2d%2.2d%2.2d%3.3d", nHour,
    1881             :              nMinute, nSecond, nMS);
    1882             : 
    1883           0 :     return m_szBuffer;
    1884             : }
    1885             : 
    1886           2 : int TABDATFile::ReadTimeField(int nWidth, int *nHour, int *nMinute,
    1887             :                               int *nSecond, int *nMS)
    1888             : {
    1889           2 :     GInt32 nS = 0;
    1890             :     // If current record has been deleted, then return an acceptable
    1891             :     // default value.
    1892           2 :     if (m_bCurRecordDeletedFlag)
    1893           0 :         return -1;
    1894             : 
    1895           2 :     if (m_poRecordBlock == nullptr)
    1896             :     {
    1897           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1898             :                  "Can't read field value: file is not opened.");
    1899           0 :         return -1;
    1900             :     }
    1901             : 
    1902             :     // With .DBF files, the value should already be stored in
    1903             :     // HHMMSSMMM format according to DBF specs.
    1904           2 :     if (m_eTableType == TABTableDBF)
    1905             :     {
    1906           0 :         strcpy(m_szBuffer, ReadCharField(nWidth));
    1907           0 :         sscanf(m_szBuffer, "%2d%2d%2d%3d", nHour, nMinute, nSecond, nMS);
    1908             :     }
    1909             :     else
    1910             :     {
    1911           2 :         nS = m_poRecordBlock->ReadInt32();  // Convert time from ms to sec
    1912             :     }
    1913             : 
    1914             :     // nS is set to -1 when the value is 'not set'
    1915           2 :     if (CPLGetLastErrorType() == CE_Failure || nS < 0 || (nS > 86400000))
    1916           1 :         return -1;
    1917             : 
    1918           1 :     *nHour = int(nS / 3600000);
    1919           1 :     *nMinute = int((nS / 1000 - *nHour * 3600) / 60);
    1920           1 :     *nSecond = int(nS / 1000 - *nHour * 3600 - *nMinute * 60);
    1921           1 :     *nMS = int(nS - *nHour * 3600000 - *nMinute * 60000 - *nSecond * 1000);
    1922             : 
    1923           1 :     return 0;
    1924             : }
    1925             : 
    1926             : /**********************************************************************
    1927             :  *                   TABDATFile::ReadDateTimeField()
    1928             :  *
    1929             :  * Read the DateTime field value at the current position in the data
    1930             :  * block.
    1931             :  *
    1932             :  * A datetime field is an 8 bytes binary value in which the first byte is
    1933             :  * the day, followed by 1 byte for the month, and 2 bytes for the year. After
    1934             :  * this is 4 bytes which represents the number of milliseconds since midnight.
    1935             :  *
    1936             :  * We return an 17 chars string in the format "YYYYMMDDhhmmssmmm"
    1937             :  *
    1938             :  * Note: nWidth is used only with TABTableDBF types.
    1939             :  *
    1940             :  * Returns a reference to an internal buffer that will be valid only until
    1941             :  * the next field is read, or "" if the operation failed, in which case
    1942             :  * CPLError() will have been called.
    1943             :  **********************************************************************/
    1944           0 : const char *TABDATFile::ReadDateTimeField(int nWidth)
    1945             : {
    1946           0 :     int nDay = 0;
    1947           0 :     int nMonth = 0;
    1948           0 :     int nYear = 0;
    1949           0 :     int nHour = 0;
    1950           0 :     int nMinute = 0;
    1951           0 :     int nSecond = 0;
    1952           0 :     int nMS = 0;
    1953           0 :     int status = ReadDateTimeField(nWidth, &nYear, &nMonth, &nDay, &nHour,
    1954             :                                    &nMinute, &nSecond, &nMS);
    1955             : 
    1956           0 :     if (status == -1)
    1957           0 :         return "";
    1958             : 
    1959           0 :     snprintf(m_szBuffer, sizeof(m_szBuffer),
    1960             :              "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d%3.3d", nYear, nMonth, nDay, nHour,
    1961             :              nMinute, nSecond, nMS);
    1962             : 
    1963           0 :     return m_szBuffer;
    1964             : }
    1965             : 
    1966          18 : int TABDATFile::ReadDateTimeField(int nWidth, int *nYear, int *nMonth,
    1967             :                                   int *nDay, int *nHour, int *nMinute,
    1968             :                                   int *nSecond, int *nMS)
    1969             : {
    1970          18 :     GInt32 nS = 0;
    1971             :     // If current record has been deleted, then return an acceptable
    1972             :     // default value.
    1973          18 :     if (m_bCurRecordDeletedFlag)
    1974           0 :         return -1;
    1975             : 
    1976          18 :     if (m_poRecordBlock == nullptr)
    1977             :     {
    1978           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1979             :                  "Can't read field value: file is not opened.");
    1980           0 :         return -1;
    1981             :     }
    1982             : 
    1983             :     // With .DBF files, the value should already be stored in
    1984             :     // YYYYMMDD format according to DBF specs.
    1985          18 :     if (m_eTableType == TABTableDBF)
    1986             :     {
    1987           0 :         strcpy(m_szBuffer, ReadCharField(nWidth));
    1988           0 :         sscanf(m_szBuffer, "%4d%2d%2d%2d%2d%2d%3d", nYear, nMonth, nDay, nHour,
    1989             :                nMinute, nSecond, nMS);
    1990             :     }
    1991             :     else
    1992             :     {
    1993          18 :         *nYear = m_poRecordBlock->ReadInt16();
    1994          18 :         *nMonth = m_poRecordBlock->ReadByte();
    1995          18 :         *nDay = m_poRecordBlock->ReadByte();
    1996          18 :         nS = m_poRecordBlock->ReadInt32();
    1997             :     }
    1998             : 
    1999          18 :     if (CPLGetLastErrorType() == CE_Failure ||
    2000          18 :         (*nYear == 0 && *nMonth == 0 && *nDay == 0) || (nS > 86400000))
    2001          17 :         return -1;
    2002             : 
    2003           1 :     *nHour = int(nS / 3600000);
    2004           1 :     *nMinute = int((nS / 1000 - *nHour * 3600) / 60);
    2005           1 :     *nSecond = int(nS / 1000 - *nHour * 3600 - *nMinute * 60);
    2006           1 :     *nMS = int(nS - *nHour * 3600000 - *nMinute * 60000 - *nSecond * 1000);
    2007             : 
    2008           1 :     return 0;
    2009             : }
    2010             : 
    2011             : /**********************************************************************
    2012             :  *                   TABDATFile::ReadDecimalField()
    2013             :  *
    2014             :  * Read the decimal field value at the current position in the data
    2015             :  * block.
    2016             :  *
    2017             :  * A decimal field is a floating point value with a fixed number of digits
    2018             :  * stored as a character string.
    2019             :  *
    2020             :  * nWidth is the field length, as defined in the .DAT header.
    2021             :  *
    2022             :  * We return the value as a binary double.
    2023             :  *
    2024             :  * CPLError() will have been called if something fails.
    2025             :  **********************************************************************/
    2026          23 : double TABDATFile::ReadDecimalField(int nWidth)
    2027             : {
    2028             :     // If current record has been deleted, then return an acceptable
    2029             :     // default value.
    2030          23 :     if (m_bCurRecordDeletedFlag)
    2031           0 :         return 0.0;
    2032             : 
    2033          23 :     const char *pszVal = ReadCharField(nWidth);
    2034             : 
    2035          23 :     return CPLAtof(pszVal);
    2036             : }
    2037             : 
    2038             : /**********************************************************************
    2039             :  *                   TABDATFile::WriteCharField()
    2040             :  *
    2041             :  * Write the character field value at the current position in the data
    2042             :  * block.
    2043             :  *
    2044             :  * Use GetRecordBlock() to position the data block to the beginning of
    2045             :  * a record before attempting to write values.
    2046             :  *
    2047             :  * nWidth is the field length, as defined in the .DAT header.
    2048             :  *
    2049             :  * Returns 0 on success, or -1 if the operation failed, in which case
    2050             :  * CPLError() will have been called.
    2051             :  **********************************************************************/
    2052         374 : int TABDATFile::WriteCharField(const char *pszStr, int nWidth,
    2053             :                                TABINDFile *poINDFile, int nIndexNo)
    2054             : {
    2055         374 :     if (m_poRecordBlock == nullptr)
    2056             :     {
    2057           0 :         CPLError(
    2058             :             CE_Failure, CPLE_AssertionFailed,
    2059             :             "Can't write field value: GetRecordBlock() has not been called.");
    2060           0 :         return -1;
    2061             :     }
    2062             : 
    2063         374 :     if (nWidth < 1 || nWidth > 255)
    2064             :     {
    2065           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2066             :                  "Illegal width for a char field: %d", nWidth);
    2067           0 :         return -1;
    2068             :     }
    2069             : 
    2070             :     //
    2071             :     // Write the buffer after making sure that we don't try to read
    2072             :     // past the end of the source buffer.  The rest of the field will
    2073             :     // be padded with zeros if source string is shorter than specified
    2074             :     // field width.
    2075             :     //
    2076         374 :     const int nLen = std::min(static_cast<int>(strlen(pszStr)), nWidth);
    2077             : 
    2078         275 :     if ((nLen > 0 && m_poRecordBlock->WriteBytes(
    2079         748 :                          nLen, reinterpret_cast<const GByte *>(pszStr)) != 0) ||
    2080         374 :         (nWidth - nLen > 0 && m_poRecordBlock->WriteZeros(nWidth - nLen) != 0))
    2081           0 :         return -1;
    2082             : 
    2083             :     // Update Index
    2084         374 :     if (poINDFile && nIndexNo > 0)
    2085             :     {
    2086           0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, pszStr);
    2087           0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2088           0 :             return -1;
    2089             :     }
    2090             : 
    2091         374 :     return 0;
    2092             : }
    2093             : 
    2094             : /**********************************************************************
    2095             :  *                   TABDATFile::WriteIntegerField()
    2096             :  *
    2097             :  * Write the integer field value at the current position in the data
    2098             :  * block.
    2099             :  *
    2100             :  * CPLError() will have been called if something fails.
    2101             :  **********************************************************************/
    2102       14985 : int TABDATFile::WriteIntegerField(GInt32 nValue, TABINDFile *poINDFile,
    2103             :                                   int nIndexNo)
    2104             : {
    2105       14985 :     if (m_poRecordBlock == nullptr)
    2106             :     {
    2107           0 :         CPLError(
    2108             :             CE_Failure, CPLE_AssertionFailed,
    2109             :             "Can't write field value: GetRecordBlock() has not been called.");
    2110           0 :         return -1;
    2111             :     }
    2112             : 
    2113             :     // Update Index
    2114       14985 :     if (poINDFile && nIndexNo > 0)
    2115             :     {
    2116           2 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, nValue);
    2117           2 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2118           0 :             return -1;
    2119             :     }
    2120             : 
    2121       14985 :     return m_poRecordBlock->WriteInt32(nValue);
    2122             : }
    2123             : 
    2124             : /**********************************************************************
    2125             :  *                   TABDATFile::WriteSmallIntField()
    2126             :  *
    2127             :  * Write the smallint field value at the current position in the data
    2128             :  * block.
    2129             :  *
    2130             :  * CPLError() will have been called if something fails.
    2131             :  **********************************************************************/
    2132           0 : int TABDATFile::WriteSmallIntField(GInt16 nValue, TABINDFile *poINDFile,
    2133             :                                    int nIndexNo)
    2134             : {
    2135           0 :     if (m_poRecordBlock == nullptr)
    2136             :     {
    2137           0 :         CPLError(
    2138             :             CE_Failure, CPLE_AssertionFailed,
    2139             :             "Can't write field value: GetRecordBlock() has not been called.");
    2140           0 :         return -1;
    2141             :     }
    2142             : 
    2143             :     // Update Index
    2144           0 :     if (poINDFile && nIndexNo > 0)
    2145             :     {
    2146           0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, nValue);
    2147           0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2148           0 :             return -1;
    2149             :     }
    2150             : 
    2151           0 :     return m_poRecordBlock->WriteInt16(nValue);
    2152             : }
    2153             : 
    2154             : /**********************************************************************
    2155             :  *                   TABDATFile::WriteLargeIntField()
    2156             :  *
    2157             :  * Write the smallint field value at the current position in the data
    2158             :  * block.
    2159             :  *
    2160             :  * CPLError() will have been called if something fails.
    2161             :  **********************************************************************/
    2162           1 : int TABDATFile::WriteLargeIntField(GInt64 nValue, TABINDFile *poINDFile,
    2163             :                                    int nIndexNo)
    2164             : {
    2165           1 :     if (m_poRecordBlock == nullptr)
    2166             :     {
    2167           0 :         CPLError(
    2168             :             CE_Failure, CPLE_AssertionFailed,
    2169             :             "Can't write field value: GetRecordBlock() has not been called.");
    2170           0 :         return -1;
    2171             :     }
    2172             : 
    2173             :     // Update Index
    2174           1 :     if (poINDFile && nIndexNo > 0)
    2175             :     {
    2176           0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, nValue);
    2177           0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2178           0 :             return -1;
    2179             :     }
    2180             : 
    2181           1 :     return m_poRecordBlock->WriteInt64(nValue);
    2182             : }
    2183             : 
    2184             : /**********************************************************************
    2185             :  *                   TABDATFile::WriteFloatField()
    2186             :  *
    2187             :  * Write the float field value at the current position in the data
    2188             :  * block.
    2189             :  *
    2190             :  * CPLError() will have been called if something fails.
    2191             :  **********************************************************************/
    2192         234 : int TABDATFile::WriteFloatField(double dValue, TABINDFile *poINDFile,
    2193             :                                 int nIndexNo)
    2194             : {
    2195         234 :     if (m_poRecordBlock == nullptr)
    2196             :     {
    2197           0 :         CPLError(
    2198             :             CE_Failure, CPLE_AssertionFailed,
    2199             :             "Can't write field value: GetRecordBlock() has not been called.");
    2200           0 :         return -1;
    2201             :     }
    2202             : 
    2203             :     // Update Index
    2204         234 :     if (poINDFile && nIndexNo > 0)
    2205             :     {
    2206           0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, dValue);
    2207           0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2208           0 :             return -1;
    2209             :     }
    2210             : 
    2211         234 :     return m_poRecordBlock->WriteDouble(dValue);
    2212             : }
    2213             : 
    2214             : /**********************************************************************
    2215             :  *                   TABDATFile::WriteLogicalField()
    2216             :  *
    2217             :  * Write the logical field value at the current position in the data
    2218             :  * block.
    2219             :  *
    2220             :  * The value written to the file is either 0 or 1, but this function
    2221             :  * takes as input a string with "F" (false) or "T" (true)
    2222             :  *
    2223             :  * CPLError() will have been called if something fails.
    2224             :  **********************************************************************/
    2225           0 : int TABDATFile::WriteLogicalField(const char *pszValue, TABINDFile *poINDFile,
    2226             :                                   int nIndexNo)
    2227             : {
    2228           0 :     if (m_poRecordBlock == nullptr)
    2229             :     {
    2230           0 :         CPLError(
    2231             :             CE_Failure, CPLE_AssertionFailed,
    2232             :             "Can't write field value: GetRecordBlock() has not been called.");
    2233           0 :         return -1;
    2234             :     }
    2235             : 
    2236             :     // TODO(schwehr): bValue should be a bool.
    2237           0 :     const GByte bValue = STARTS_WITH_CI(pszValue, "T") ? 1 : 0;
    2238             : 
    2239             :     // Update Index
    2240           0 :     if (poINDFile && nIndexNo > 0)
    2241             :     {
    2242           0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, static_cast<int>(bValue));
    2243           0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2244           0 :             return -1;
    2245             :     }
    2246             : 
    2247           0 :     return m_poRecordBlock->WriteByte(bValue);
    2248             : }
    2249             : 
    2250             : /**********************************************************************
    2251             :  *                   TABDATFile::WriteDateField()
    2252             :  *
    2253             :  * Write the date field value at the current position in the data
    2254             :  * block.
    2255             :  *
    2256             :  * A date field is a 4 bytes binary value in which the first byte is
    2257             :  * the day, followed by 1 byte for the month, and 2 bytes for the year.
    2258             :  *
    2259             :  * The expected input is a 10 chars string in the format "YYYY/MM/DD"
    2260             :  * or "DD/MM/YYYY" or "YYYYMMDD"
    2261             :  *
    2262             :  * Returns 0 on success, or -1 if the operation failed, in which case
    2263             :  * CPLError() will have been called.
    2264             :  **********************************************************************/
    2265           0 : int TABDATFile::WriteDateField(const char *pszValue, TABINDFile *poINDFile,
    2266             :                                int nIndexNo)
    2267             : {
    2268           0 :     char **papszTok = nullptr;
    2269             : 
    2270             :     // Get rid of leading spaces.
    2271           0 :     while (*pszValue == ' ')
    2272             :     {
    2273           0 :         pszValue++;
    2274             :     }
    2275             : 
    2276             :     // Try to automagically detect date format, one of:
    2277             :     // "YYYY/MM/DD", "DD/MM/YYYY", or "YYYYMMDD"
    2278           0 :     int nDay = 0;
    2279           0 :     int nMonth = 0;
    2280           0 :     int nYear = 0;
    2281             : 
    2282           0 :     if (strlen(pszValue) == 8)
    2283             :     {
    2284             :         // "YYYYMMDD"
    2285           0 :         char szBuf[9] = {};
    2286           0 :         strcpy(szBuf, pszValue);
    2287           0 :         nDay = atoi(szBuf + 6);
    2288           0 :         szBuf[6] = '\0';
    2289           0 :         nMonth = atoi(szBuf + 4);
    2290           0 :         szBuf[4] = '\0';
    2291           0 :         nYear = atoi(szBuf);
    2292             :     }
    2293           0 :     else if (strlen(pszValue) == 10 &&
    2294           0 :              (papszTok = CSLTokenizeStringComplex(pszValue, "/", FALSE,
    2295           0 :                                                   FALSE)) != nullptr &&
    2296           0 :              CSLCount(papszTok) == 3 &&
    2297           0 :              (strlen(papszTok[0]) == 4 || strlen(papszTok[2]) == 4))
    2298             :     {
    2299             :         // Either "YYYY/MM/DD" or "DD/MM/YYYY"
    2300           0 :         if (strlen(papszTok[0]) == 4)
    2301             :         {
    2302           0 :             nYear = atoi(papszTok[0]);
    2303           0 :             nMonth = atoi(papszTok[1]);
    2304           0 :             nDay = atoi(papszTok[2]);
    2305             :         }
    2306             :         else
    2307             :         {
    2308           0 :             nYear = atoi(papszTok[2]);
    2309           0 :             nMonth = atoi(papszTok[1]);
    2310           0 :             nDay = atoi(papszTok[0]);
    2311             :         }
    2312             :     }
    2313           0 :     else if (strlen(pszValue) == 0)
    2314             :     {
    2315           0 :         nYear = 0;
    2316           0 :         nMonth = 0;
    2317           0 :         nDay = 0;
    2318             :     }
    2319             :     else
    2320             :     {
    2321           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2322             :                  "Invalid date field value `%s'.  Date field values must "
    2323             :                  "be in the format `YYYY/MM/DD', `MM/DD/YYYY' or `YYYYMMDD'",
    2324             :                  pszValue);
    2325           0 :         CSLDestroy(papszTok);
    2326           0 :         return -1;
    2327             :     }
    2328           0 :     CSLDestroy(papszTok);
    2329             : 
    2330           0 :     return WriteDateField(nYear, nMonth, nDay, poINDFile, nIndexNo);
    2331             : }
    2332             : 
    2333          75 : int TABDATFile::WriteDateField(int nYear, int nMonth, int nDay,
    2334             :                                TABINDFile *poINDFile, int nIndexNo)
    2335             : {
    2336          75 :     if (m_poRecordBlock == nullptr)
    2337             :     {
    2338           0 :         CPLError(
    2339             :             CE_Failure, CPLE_AssertionFailed,
    2340             :             "Can't write field value: GetRecordBlock() has not been called.");
    2341           0 :         return -1;
    2342             :     }
    2343             : 
    2344          75 :     m_poRecordBlock->WriteInt16(static_cast<GInt16>(nYear));
    2345          75 :     m_poRecordBlock->WriteByte(static_cast<GByte>(nMonth));
    2346          75 :     m_poRecordBlock->WriteByte(static_cast<GByte>(nDay));
    2347             : 
    2348          75 :     if (CPLGetLastErrorType() == CE_Failure)
    2349           0 :         return -1;
    2350             : 
    2351             :     // Update Index
    2352          75 :     if (poINDFile && nIndexNo > 0)
    2353             :     {
    2354           0 :         GByte *pKey = poINDFile->BuildKey(
    2355           0 :             nIndexNo, (nYear * 0x10000 + nMonth * 0x100 + nDay));
    2356           0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2357           0 :             return -1;
    2358             :     }
    2359             : 
    2360          75 :     return 0;
    2361             : }
    2362             : 
    2363             : /**********************************************************************
    2364             :  *                   TABDATFile::WriteTimeField()
    2365             :  *
    2366             :  * Write the date field value at the current position in the data
    2367             :  * block.
    2368             :  *
    2369             :  * A time field is a 4 byte binary value which represents the number
    2370             :  * of milliseconds since midnight.
    2371             :  *
    2372             :  * The expected input is a 10 chars string in the format "HH:MM:SS"
    2373             :  * or "HHMMSSmmm"
    2374             :  *
    2375             :  * Returns 0 on success, or -1 if the operation failed, in which case
    2376             :  * CPLError() will have been called.
    2377             :  **********************************************************************/
    2378           0 : int TABDATFile::WriteTimeField(const char *pszValue, TABINDFile *poINDFile,
    2379             :                                int nIndexNo)
    2380             : {
    2381             :     // Get rid of leading spaces.
    2382           0 :     while (*pszValue == ' ')
    2383             :     {
    2384           0 :         pszValue++;
    2385             :     }
    2386             : 
    2387             :     // Try to automagically detect time format, one of:
    2388             :     // "HH:MM:SS", or "HHMMSSmmm"
    2389           0 :     int nHour = 0;
    2390           0 :     int nMin = 0;
    2391           0 :     int nSec = 0;
    2392           0 :     int nMS = 0;
    2393             : 
    2394           0 :     if (strlen(pszValue) == 8)
    2395             :     {
    2396             :         // "HH:MM:SS"
    2397           0 :         char szBuf[9] = {};
    2398           0 :         strcpy(szBuf, pszValue);
    2399           0 :         szBuf[2] = 0;
    2400           0 :         szBuf[5] = 0;
    2401           0 :         nHour = atoi(szBuf);
    2402           0 :         nMin = atoi(szBuf + 3);
    2403           0 :         nSec = atoi(szBuf + 6);
    2404           0 :         nMS = 0;
    2405             :     }
    2406           0 :     else if (strlen(pszValue) == 9)
    2407             :     {
    2408             :         // "HHMMSSmmm"
    2409           0 :         char szBuf[4] = {};
    2410           0 :         const int HHLength = 2;
    2411           0 :         strncpy(szBuf, pszValue, HHLength);
    2412           0 :         szBuf[HHLength] = 0;
    2413           0 :         nHour = atoi(szBuf);
    2414             : 
    2415           0 :         const int MMLength = 2;
    2416           0 :         strncpy(szBuf, pszValue + HHLength, MMLength);
    2417           0 :         szBuf[MMLength] = 0;
    2418           0 :         nMin = atoi(szBuf);
    2419             : 
    2420           0 :         const int SSLength = 2;
    2421           0 :         strncpy(szBuf, pszValue + HHLength + MMLength, SSLength);
    2422           0 :         szBuf[SSLength] = 0;
    2423           0 :         nSec = atoi(szBuf);
    2424             : 
    2425           0 :         const int mmmLength = 3;
    2426           0 :         strncpy(szBuf, pszValue + HHLength + MMLength + SSLength, mmmLength);
    2427           0 :         szBuf[mmmLength] = 0;
    2428           0 :         nMS = atoi(szBuf);
    2429             :     }
    2430           0 :     else if (strlen(pszValue) == 0)
    2431             :     {
    2432             :         // Write -1 to .DAT file if value is not set
    2433           0 :         nHour = -1;
    2434           0 :         nMin = -1;
    2435           0 :         nSec = -1;
    2436           0 :         nMS = -1;
    2437             :     }
    2438             :     else
    2439             :     {
    2440           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2441             :                  "Invalid time field value `%s'.  Time field values must "
    2442             :                  "be in the format `HH:MM:SS', or `HHMMSSmmm'",
    2443             :                  pszValue);
    2444           0 :         return -1;
    2445             :     }
    2446             : 
    2447           0 :     return WriteTimeField(nHour, nMin, nSec, nMS, poINDFile, nIndexNo);
    2448             : }
    2449             : 
    2450           2 : int TABDATFile::WriteTimeField(int nHour, int nMinute, int nSecond, int nMS,
    2451             :                                TABINDFile *poINDFile, int nIndexNo)
    2452             : {
    2453           2 :     GInt32 nS = -1;
    2454             : 
    2455           2 :     if (m_poRecordBlock == nullptr)
    2456             :     {
    2457           0 :         CPLError(
    2458             :             CE_Failure, CPLE_AssertionFailed,
    2459             :             "Can't write field value: GetRecordBlock() has not been called.");
    2460           0 :         return -1;
    2461             :     }
    2462             : 
    2463           2 :     nS = (nHour * 3600 + nMinute * 60 + nSecond) * 1000 + nMS;
    2464           2 :     if (nS < 0)
    2465           1 :         nS = -1;
    2466           2 :     m_poRecordBlock->WriteInt32(nS);
    2467             : 
    2468           2 :     if (CPLGetLastErrorType() == CE_Failure)
    2469           0 :         return -1;
    2470             : 
    2471             :     // Update Index
    2472           2 :     if (poINDFile && nIndexNo > 0)
    2473             :     {
    2474           0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, (nS));
    2475           0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2476           0 :             return -1;
    2477             :     }
    2478             : 
    2479           2 :     return 0;
    2480             : }
    2481             : 
    2482             : /**********************************************************************
    2483             :  *                   TABDATFile::WriteDateTimeField()
    2484             :  *
    2485             :  * Write the DateTime field value at the current position in the data
    2486             :  * block.
    2487             :  *
    2488             :  * A datetime field is a 8 bytes binary value in which the first byte is
    2489             :  * the day, followe
    2490             : d by 1 byte for the month, and 2 bytes for the year.
    2491             :  * After this the time value is stored as a 4 byte integer
    2492             :  * (milliseconds since midnight)
    2493             :  *
    2494             :  * The expected input is a 10 chars string in the format "YYYY/MM/DD HH:MM:SS"
    2495             :  * or "DD/MM/YYYY HH:MM:SS" or "YYYYMMDDhhmmssmmm"
    2496             :  *
    2497             :  * Returns 0 on success, or -1 if the operation failed, in which case
    2498             :  * CPLError() will have been called.
    2499             :  **********************************************************************/
    2500           0 : int TABDATFile::WriteDateTimeField(const char *pszValue, TABINDFile *poINDFile,
    2501             :                                    int nIndexNo)
    2502             : {
    2503             :     // Get rid of leading spaces.
    2504           0 :     while (*pszValue == ' ')
    2505             :     {
    2506           0 :         pszValue++;
    2507             :     }
    2508             : 
    2509             :     /*-----------------------------------------------------------------
    2510             :      * Try to automagically detect date format, one of:
    2511             :      * "YYYY/MM/DD HH:MM:SS", "DD/MM/YYYY HH:MM:SS", or "YYYYMMDDhhmmssmmm"
    2512             :      *----------------------------------------------------------------*/
    2513           0 :     int nDay = 0;
    2514           0 :     int nMonth = 0;
    2515           0 :     int nYear = 0;
    2516           0 :     int nHour = 0;
    2517           0 :     int nMin = 0;
    2518           0 :     int nSec = 0;
    2519           0 :     int nMS = 0;
    2520           0 :     char **papszTok = nullptr;
    2521             : 
    2522           0 :     if (strlen(pszValue) == 17)
    2523             :     {
    2524             :         // "YYYYMMDDhhmmssmmm"
    2525           0 :         char szBuf[18] = {};
    2526           0 :         strcpy(szBuf, pszValue);
    2527           0 :         nMS = atoi(szBuf + 14);
    2528           0 :         szBuf[14] = 0;
    2529           0 :         nSec = atoi(szBuf + 12);
    2530           0 :         szBuf[12] = 0;
    2531           0 :         nMin = atoi(szBuf + 10);
    2532           0 :         szBuf[10] = 0;
    2533           0 :         nHour = atoi(szBuf + 8);
    2534           0 :         szBuf[8] = 0;
    2535           0 :         nDay = atoi(szBuf + 6);
    2536           0 :         szBuf[6] = 0;
    2537           0 :         nMonth = atoi(szBuf + 4);
    2538           0 :         szBuf[4] = 0;
    2539           0 :         nYear = atoi(szBuf);
    2540             :     }
    2541           0 :     else if (strlen(pszValue) == 19 &&
    2542           0 :              (papszTok = CSLTokenizeStringComplex(pszValue, "/ :", FALSE,
    2543           0 :                                                   FALSE)) != nullptr &&
    2544           0 :              CSLCount(papszTok) == 6 &&
    2545           0 :              (strlen(papszTok[0]) == 4 || strlen(papszTok[2]) == 4))
    2546             :     {
    2547             :         // Either "YYYY/MM/DD HH:MM:SS" or "DD/MM/YYYY HH:MM:SS"
    2548           0 :         if (strlen(papszTok[0]) == 4)
    2549             :         {
    2550           0 :             nYear = atoi(papszTok[0]);
    2551           0 :             nMonth = atoi(papszTok[1]);
    2552           0 :             nDay = atoi(papszTok[2]);
    2553           0 :             nHour = atoi(papszTok[3]);
    2554           0 :             nMin = atoi(papszTok[4]);
    2555           0 :             nSec = atoi(papszTok[5]);
    2556           0 :             nMS = 0;
    2557             :         }
    2558             :         else
    2559             :         {
    2560           0 :             nYear = atoi(papszTok[2]);
    2561           0 :             nMonth = atoi(papszTok[1]);
    2562           0 :             nDay = atoi(papszTok[0]);
    2563           0 :             nHour = atoi(papszTok[3]);
    2564           0 :             nMin = atoi(papszTok[4]);
    2565           0 :             nSec = atoi(papszTok[5]);
    2566           0 :             nMS = 0;
    2567             :         }
    2568             :     }
    2569           0 :     else if (strlen(pszValue) == 0)
    2570             :     {
    2571           0 :         nYear = 0;
    2572           0 :         nMonth = 0;
    2573           0 :         nDay = 0;
    2574           0 :         nHour = 0;
    2575           0 :         nMin = 0;
    2576           0 :         nSec = 0;
    2577           0 :         nMS = 0;
    2578             :     }
    2579             :     else
    2580             :     {
    2581           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2582             :                  "Invalid date field value `%s'.  Date field values must "
    2583             :                  "be in the format `YYYY/MM/DD HH:MM:SS', "
    2584             :                  "`MM/DD/YYYY HH:MM:SS' or `YYYYMMDDhhmmssmmm'",
    2585             :                  pszValue);
    2586           0 :         CSLDestroy(papszTok);
    2587           0 :         return -1;
    2588             :     }
    2589           0 :     CSLDestroy(papszTok);
    2590             : 
    2591           0 :     return WriteDateTimeField(nYear, nMonth, nDay, nHour, nMin, nSec, nMS,
    2592           0 :                               poINDFile, nIndexNo);
    2593             : }
    2594             : 
    2595          74 : int TABDATFile::WriteDateTimeField(int nYear, int nMonth, int nDay, int nHour,
    2596             :                                    int nMinute, int nSecond, int nMS,
    2597             :                                    TABINDFile *poINDFile, int nIndexNo)
    2598             : {
    2599          74 :     GInt32 nS = (nHour * 3600 + nMinute * 60 + nSecond) * 1000 + nMS;
    2600             : 
    2601          74 :     if (m_poRecordBlock == nullptr)
    2602             :     {
    2603           0 :         CPLError(
    2604             :             CE_Failure, CPLE_AssertionFailed,
    2605             :             "Can't write field value: GetRecordBlock() has not been called.");
    2606           0 :         return -1;
    2607             :     }
    2608             : 
    2609          74 :     m_poRecordBlock->WriteInt16(static_cast<GInt16>(nYear));
    2610          74 :     m_poRecordBlock->WriteByte(static_cast<GByte>(nMonth));
    2611          74 :     m_poRecordBlock->WriteByte(static_cast<GByte>(nDay));
    2612          74 :     m_poRecordBlock->WriteInt32(nS);
    2613             : 
    2614          74 :     if (CPLGetLastErrorType() == CE_Failure)
    2615           0 :         return -1;
    2616             : 
    2617             :     // Update Index
    2618          74 :     if (poINDFile && nIndexNo > 0)
    2619             :     {
    2620             :         // __TODO__  (see bug #1844)
    2621             :         // Indexing on DateTime Fields not currently supported, that will
    2622             :         // require passing the 8 bytes datetime value to BuildKey() here...
    2623           0 :         CPLAssert(false);
    2624             :         GByte *pKey = poINDFile->BuildKey(
    2625             :             nIndexNo, (nYear * 0x10000 + nMonth * 0x100 + nDay));
    2626             :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2627             :             return -1;
    2628             :     }
    2629             : 
    2630          74 :     return 0;
    2631             : }
    2632             : 
    2633             : /**********************************************************************
    2634             :  *                   TABDATFile::WriteDecimalField()
    2635             :  *
    2636             :  * Write the decimal field value at the current position in the data
    2637             :  * block.
    2638             :  *
    2639             :  * A decimal field is a floating point value with a fixed number of digits
    2640             :  * stored as a character string.
    2641             :  *
    2642             :  * nWidth is the field length, as defined in the .DAT header.
    2643             :  *
    2644             :  * CPLError() will have been called if something fails.
    2645             :  **********************************************************************/
    2646           4 : int TABDATFile::WriteDecimalField(double dValue, int nWidth, int nPrec,
    2647             :                                   TABINDFile *poINDFile, int nIndexNo)
    2648             : {
    2649           4 :     char szFormat[10] = {};
    2650             : 
    2651           4 :     snprintf(szFormat, sizeof(szFormat), "%%%d.%df", nWidth, nPrec);
    2652           4 :     const char *pszVal = CPLSPrintf(szFormat, dValue);
    2653           4 :     if (static_cast<int>(strlen(pszVal)) > nWidth)
    2654             :     {
    2655           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2656             :                  "Cannot format %g as a %d.%d field", dValue, nWidth, nPrec);
    2657           1 :         return -1;
    2658             :     }
    2659             : 
    2660             :     // Update Index
    2661           3 :     if (poINDFile && nIndexNo > 0)
    2662             :     {
    2663           0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, dValue);
    2664           0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    2665           0 :             return -1;
    2666             :     }
    2667             : 
    2668           3 :     return m_poRecordBlock->WriteBytes(nWidth,
    2669           3 :                                        reinterpret_cast<const GByte *>(pszVal));
    2670             : }
    2671             : 
    2672        1832 : const CPLString &TABDATFile::GetEncoding() const
    2673             : {
    2674        1832 :     return m_osEncoding;
    2675             : }
    2676             : 
    2677           2 : void TABDATFile::SetEncoding(const CPLString &osEncoding)
    2678             : {
    2679           2 :     m_osEncoding = osEncoding;
    2680           2 : }
    2681             : 
    2682             : /**********************************************************************
    2683             :  *                   TABDATFile::Dump()
    2684             :  *
    2685             :  * Dump block contents... available only in DEBUG mode.
    2686             :  **********************************************************************/
    2687             : #ifdef DEBUG
    2688             : 
    2689           0 : void TABDATFile::Dump(FILE *fpOut /* =NULL */)
    2690             : {
    2691           0 :     if (fpOut == nullptr)
    2692           0 :         fpOut = stdout;
    2693             : 
    2694           0 :     fprintf(fpOut, "----- TABDATFile::Dump() -----\n");
    2695             : 
    2696           0 :     if (m_fp == nullptr)
    2697             :     {
    2698           0 :         fprintf(fpOut, "File is not opened.\n");
    2699             :     }
    2700             :     else
    2701             :     {
    2702           0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
    2703           0 :         fprintf(fpOut, "m_numFields  = %d\n", m_numFields);
    2704           0 :         fprintf(fpOut, "m_numRecords = %d\n", m_numRecords);
    2705             :     }
    2706             : 
    2707           0 :     fflush(fpOut);
    2708           0 : }
    2709             : 
    2710             : #endif  // DEBUG

Generated by: LCOV version 1.14