LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_tabfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 852 1173 72.6 %
Date: 2024-05-05 22:37:24 Functions: 36 43 83.7 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_tabfile.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABFile class, the main class of the lib.
       7             :  *           To be used by external programs to handle reading/writing of
       8             :  *           features from/to TAB datasets.
       9             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
      10             :  *
      11             :  **********************************************************************
      12             :  * Copyright (c) 1999-2003, Daniel Morissette
      13             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      14             :  *
      15             :  * Permission is hereby granted, free of charge, to any person obtaining a
      16             :  * copy of this software and associated documentation files (the "Software"),
      17             :  * to deal in the Software without restriction, including without limitation
      18             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      19             :  * and/or sell copies of the Software, and to permit persons to whom the
      20             :  * Software is furnished to do so, subject to the following conditions:
      21             :  *
      22             :  * The above copyright notice and this permission notice shall be included
      23             :  * in all copies or substantial portions of the Software.
      24             :  *
      25             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      26             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      27             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      28             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      29             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      30             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      31             :  * DEALINGS IN THE SOFTWARE.
      32             :  **********************************************************************/
      33             : 
      34             : #include "cpl_port.h"
      35             : #include "mitab.h"
      36             : 
      37             : #include <cctype>
      38             : #include <climits>
      39             : #include <cstdio>
      40             : #include <cstdlib>
      41             : #include <cstring>
      42             : #include <algorithm>
      43             : #include <memory>
      44             : 
      45             : #include "cpl_conv.h"
      46             : #include "cpl_error.h"
      47             : #include "cpl_minixml.h"
      48             : #include "cpl_string.h"
      49             : #include "cpl_vsi.h"
      50             : #include "mitab_priv.h"
      51             : #include "mitab_utils.h"
      52             : #include "ogr_core.h"
      53             : #include "ogr_feature.h"
      54             : #include "ogr_geometry.h"
      55             : #include "ogr_p.h"
      56             : #include "ogr_spatialref.h"
      57             : #include "ogrsf_frmts.h"
      58             : 
      59             : static const char UNSUPPORTED_OP_READ_ONLY[] =
      60             :     "%s : unsupported operation on a read-only datasource.";
      61             : 
      62             : constexpr const char *DESCRIPTION_KEY = "DESCRIPTION";
      63             : // Tab file allow to store description longer than 255 characters.
      64             : // But only 255 will shown in MapInfo layer list.
      65             : constexpr int MAX_DESCRIPTION_LEN = 254 * 2;
      66             : 
      67         121 : static char *EscapeString(const char *pszInput,
      68             :                           bool bEscapeDoubleQuotes = false)
      69             : {
      70         121 :     if (nullptr == pszInput)
      71             :     {
      72         115 :         return nullptr;
      73             :     }
      74             : 
      75           6 :     auto nLength = CPLStrnlen(pszInput, MAX_DESCRIPTION_LEN);
      76           6 :     char *pszOutput = static_cast<char *>(CPLMalloc(nLength * 2 + 1));
      77           6 :     int iOut = 0;
      78           6 :     int nDoubleQuotesCount = 0;
      79        1394 :     for (int iIn = 0; iIn < static_cast<int>(nLength + 1); ++iIn)
      80             :     {
      81        1390 :         if (pszInput[iIn] == '"')
      82             :         {
      83          12 :             if (bEscapeDoubleQuotes)
      84             :             {
      85           6 :                 pszOutput[iOut++] = '"';
      86           6 :                 pszOutput[iOut++] = '"';
      87             :             }
      88             :             else
      89             :             {
      90           6 :                 nDoubleQuotesCount++;
      91           6 :                 pszOutput[iOut++] = pszInput[iIn];
      92             :             }
      93             :         }
      94        1378 :         else if (pszInput[iIn] == '\n' || pszInput[iIn] == '\r')
      95             :         {
      96           6 :             pszOutput[iOut++] = ' ';
      97             :         }
      98             :         else
      99             :         {
     100        1372 :             if ((pszInput[iIn] & 0xc0) != 0x80)
     101             :             {
     102             :                 // Stop at the start of the character just beyond the maximum
     103             :                 // accepted
     104         844 :                 if (iOut >= MAX_DESCRIPTION_LEN - nDoubleQuotesCount)
     105             :                 {
     106           2 :                     break;
     107             :                 }
     108             :             }
     109        1370 :             pszOutput[iOut++] = pszInput[iIn];
     110             :         }
     111             :     }
     112             : 
     113           6 :     pszOutput[iOut] = '\0';
     114           6 :     return pszOutput;
     115             : }
     116             : 
     117           4 : static char *UnescapeString(const char *pszInput)
     118             : {
     119           4 :     if (nullptr == pszInput)
     120             :     {
     121           0 :         return nullptr;
     122             :     }
     123             : 
     124           4 :     auto nLength = CPLStrnlen(pszInput, MAX_DESCRIPTION_LEN);
     125           4 :     char *pszOutput = static_cast<char *>(CPLMalloc(nLength * 2 + 1));
     126           4 :     int iOut = 0;
     127         786 :     for (int iIn = 0; iIn < static_cast<int>(nLength + 1); ++iIn)
     128             :     {
     129         782 :         if (pszInput[iIn] == '"' && pszInput[iIn + 1] == '"')
     130             :         {
     131           6 :             ++iIn;
     132           6 :             pszOutput[iOut++] = pszInput[iIn];
     133             :         }
     134             :         else
     135             :         {
     136         776 :             if ((pszInput[iIn] & 0xc0) != 0x80)
     137             :             {
     138             :                 // Stop at the start of the character just beyond the maximum
     139             :                 // accepted
     140         482 :                 if (iOut >= MAX_DESCRIPTION_LEN)
     141             :                 {
     142           0 :                     break;
     143             :                 }
     144             :             }
     145         776 :             pszOutput[iOut++] = pszInput[iIn];
     146             :         }
     147             :     }
     148           4 :     pszOutput[iOut] = '\0';
     149           4 :     return pszOutput;
     150             : }
     151             : 
     152           4 : static std::string GetTabDescription(const char *pszLine)
     153             : {
     154           8 :     CPLString osDescriptionLine(pszLine);
     155           4 :     auto nStart = osDescriptionLine.find_first_of('"') + 1;
     156           4 :     if (nStart != std::string::npos)
     157             :     {
     158           4 :         auto nEnd = osDescriptionLine.find_last_of('"');
     159           4 :         auto nLen = nEnd == std::string::npos ? nEnd : nEnd - nStart;
     160           4 :         return osDescriptionLine.substr(nStart, nLen);
     161             :     }
     162           0 :     return "";
     163             : }
     164             : 
     165             : /*=====================================================================
     166             :  *                      class TABFile
     167             :  *====================================================================*/
     168             : 
     169             : /**********************************************************************
     170             :  *                   TABFile::TABFile()
     171             :  *
     172             :  * Constructor.
     173             :  **********************************************************************/
     174        1418 : TABFile::TABFile(GDALDataset *poDS)
     175             :     : IMapInfoFile(poDS), m_pszFname(nullptr), m_eAccessMode(TABRead),
     176             :       m_papszTABFile(nullptr), m_nVersion(300), m_panIndexNo(nullptr),
     177             :       m_eTableType(TABTableNative), m_poDATFile(nullptr), m_poMAPFile(nullptr),
     178             :       m_poINDFile(nullptr), m_poDefn(nullptr), m_poSpatialRef(nullptr),
     179             :       bUseSpatialTraversal(FALSE), m_nLastFeatureId(0),
     180             :       m_panMatchingFIDs(nullptr), m_iMatchingFID(0), m_bNeedTABRewrite(FALSE),
     181        1418 :       m_bLastOpWasRead(FALSE), m_bLastOpWasWrite(FALSE)
     182             : {
     183        1418 :     m_poCurFeature = nullptr;
     184        1418 :     m_nCurFeatureId = 0;
     185        1418 : }
     186             : 
     187             : /**********************************************************************
     188             :  *                   TABFile::~TABFile()
     189             :  *
     190             :  * Destructor.
     191             :  **********************************************************************/
     192        2836 : TABFile::~TABFile()
     193             : {
     194        1418 :     TABFile::Close();
     195        2836 : }
     196             : 
     197             : /************************************************************************/
     198             : /*                         GetFeatureCount()                          */
     199             : /************************************************************************/
     200             : 
     201       10278 : GIntBig TABFile::GetFeatureCount(int bForce)
     202             : {
     203             : 
     204       10278 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr || bForce)
     205       10278 :         return OGRLayer::GetFeatureCount(bForce);
     206             :     else
     207           0 :         return m_nLastFeatureId;
     208             : }
     209             : 
     210             : /************************************************************************/
     211             : /*                            ResetReading()                            */
     212             : /************************************************************************/
     213       33818 : void TABFile::ResetReading()
     214             : {
     215       33818 :     CPLFree(m_panMatchingFIDs);
     216       33818 :     m_panMatchingFIDs = nullptr;
     217       33818 :     m_iMatchingFID = 0;
     218             : 
     219       33818 :     m_nCurFeatureId = 0;
     220       33818 :     if (m_poMAPFile != nullptr)
     221       33818 :         m_poMAPFile->ResetReading();
     222             : 
     223             :     /* -------------------------------------------------------------------- */
     224             :     /*      Decide whether to operate in spatial traversal mode or not,     */
     225             :     /*      and ensure the current spatial filter is applied to the map     */
     226             :     /*      file object.                                                    */
     227             :     /* -------------------------------------------------------------------- */
     228       33818 :     if (m_poMAPFile)
     229             :     {
     230       33818 :         bUseSpatialTraversal = FALSE;
     231             : 
     232       33818 :         m_poMAPFile->ResetCoordFilter();
     233             : 
     234       33818 :         if (m_poFilterGeom != nullptr)
     235             :         {
     236             :             // TABMAPHeaderBlock *poHeader = m_poMAPFile->GetHeaderBlock();
     237             : 
     238       31399 :             OGREnvelope sEnvelope;
     239       31399 :             m_poFilterGeom->getEnvelope(&sEnvelope);
     240             : 
     241       31399 :             TABVertex sMin;
     242       31399 :             TABVertex sMax;
     243       31399 :             m_poMAPFile->GetCoordFilter(sMin, sMax);
     244             : 
     245       31399 :             if (sEnvelope.MinX > sMin.x || sEnvelope.MinY > sMin.y ||
     246         279 :                 sEnvelope.MaxX < sMax.x || sEnvelope.MaxY < sMax.y)
     247             :             {
     248       31364 :                 bUseSpatialTraversal = TRUE;
     249       31364 :                 sMin.x = sEnvelope.MinX;
     250       31364 :                 sMin.y = sEnvelope.MinY;
     251       31364 :                 sMax.x = sEnvelope.MaxX;
     252       31364 :                 sMax.y = sEnvelope.MaxY;
     253       31364 :                 m_poMAPFile->SetCoordFilter(sMin, sMax);
     254             :             }
     255             :         }
     256             :     }
     257             : 
     258       33818 :     m_bLastOpWasRead = FALSE;
     259       33818 :     m_bLastOpWasWrite = FALSE;
     260       33818 : }
     261             : 
     262             : /**********************************************************************
     263             :  *                   TABFile::Open()
     264             :  *
     265             :  * Open a .TAB dataset and the associated files, and initialize the
     266             :  * structures to be ready to read features from (or write to) it.
     267             :  *
     268             :  * Supported access modes are "r" (read-only) and "w" (create new dataset or
     269             :  * update).
     270             :  *
     271             :  * Set bTestOpenNoError=TRUE to silently return -1 with no error message
     272             :  * if the file cannot be opened.  This is intended to be used in the
     273             :  * context of a TestOpen() function.  The default value is FALSE which
     274             :  * means that an error is reported if the file cannot be opened.
     275             :  *
     276             :  * Note that dataset extents will have to be set using SetBounds() before
     277             :  * any feature can be written to a newly created dataset.
     278             :  *
     279             :  * In read mode, a valid dataset must have at least a .TAB and a .DAT file.
     280             :  * The .MAP and .ID files are optional and if they do not exist then
     281             :  * all features will be returned with NONE geometry.
     282             :  *
     283             :  * Returns 0 on success, -1 on error.
     284             :  **********************************************************************/
     285        1418 : int TABFile::Open(const char *pszFname, TABAccess eAccess,
     286             :                   GBool bTestOpenNoError /*=FALSE*/, int nBlockSizeForCreate,
     287             :                   const char *pszCharset /* = NULL */)
     288             : {
     289        1418 :     char *pszTmpFname = nullptr;
     290        1418 :     int nFnameLen = 0;
     291             : 
     292        1418 :     CPLErrorReset();
     293             : 
     294        1418 :     if (m_poMAPFile)
     295             :     {
     296           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     297             :                  "Open() failed: object already contains an open file");
     298             : 
     299           0 :         return -1;
     300             :     }
     301             : 
     302        1418 :     m_eAccessMode = eAccess;
     303             : 
     304             :     /*-----------------------------------------------------------------
     305             :      * Make sure filename has a .TAB extension...
     306             :      *----------------------------------------------------------------*/
     307        1418 :     m_pszFname = CPLStrdup(pszFname);
     308        1418 :     nFnameLen = static_cast<int>(strlen(m_pszFname));
     309             : 
     310        1418 :     if (nFnameLen > 4 && (strcmp(m_pszFname + nFnameLen - 4, ".TAB") == 0 ||
     311        1415 :                           strcmp(m_pszFname + nFnameLen - 4, ".MAP") == 0 ||
     312        1415 :                           strcmp(m_pszFname + nFnameLen - 4, ".DAT") == 0))
     313           3 :         strcpy(m_pszFname + nFnameLen - 4, ".TAB");
     314        1415 :     else if (nFnameLen > 4 && (EQUAL(m_pszFname + nFnameLen - 4, ".tab") ||
     315           0 :                                EQUAL(m_pszFname + nFnameLen - 4, ".map") ||
     316           0 :                                EQUAL(m_pszFname + nFnameLen - 4, ".dat")))
     317        1415 :         strcpy(m_pszFname + nFnameLen - 4, ".tab");
     318             :     else
     319             :     {
     320           0 :         if (!bTestOpenNoError)
     321           0 :             CPLError(CE_Failure, CPLE_FileIO,
     322             :                      "Open() failed for %s: invalid filename extension",
     323             :                      m_pszFname);
     324             :         else
     325           0 :             CPLErrorReset();
     326             : 
     327           0 :         CPLFree(m_pszFname);
     328           0 :         m_pszFname = nullptr;
     329           0 :         return -1;
     330             :     }
     331             : 
     332        1418 :     pszTmpFname = CPLStrdup(m_pszFname);
     333             : 
     334             : #ifndef _WIN32
     335             :     /*-----------------------------------------------------------------
     336             :      * On Unix, make sure extension uses the right cases
     337             :      * We do it even for write access because if a file with the same
     338             :      * extension already exists we want to overwrite it.
     339             :      *----------------------------------------------------------------*/
     340        1418 :     TABAdjustFilenameExtension(m_pszFname);
     341             : #endif
     342             : 
     343             :     /*-----------------------------------------------------------------
     344             :      * Handle .TAB file... depends on access mode.
     345             :      *----------------------------------------------------------------*/
     346        1418 :     if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)
     347             :     {
     348             :         /*-------------------------------------------------------------
     349             :          * Open .TAB file... since it is a small text file, we will just load
     350             :          * it as a stringlist in memory.
     351             :          *------------------------------------------------------------*/
     352        1301 :         m_papszTABFile = TAB_CSLLoad(m_pszFname);
     353        1301 :         if (m_papszTABFile == nullptr)
     354             :         {
     355           0 :             if (!bTestOpenNoError)
     356             :             {
     357           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Failed opening %s.",
     358             :                          m_pszFname);
     359             :             }
     360           0 :             CPLFree(m_pszFname);
     361           0 :             m_pszFname = nullptr;
     362           0 :             CSLDestroy(m_papszTABFile);
     363           0 :             m_papszTABFile = nullptr;
     364           0 :             CPLFree(pszTmpFname);
     365           0 :             return -1;
     366             :         }
     367             : 
     368             :         /*-------------------------------------------------------------
     369             :          * Do a first pass on the TAB header to establish the type of
     370             :          * dataset we have (NATIVE, DBF, etc.)... and also to know if
     371             :          * it is a supported type.
     372             :          *------------------------------------------------------------*/
     373        1301 :         if (ParseTABFileFirstPass(bTestOpenNoError) != 0)
     374             :         {
     375             :             // No need to produce an error... it is already been done if
     376             :             // necessary... just cleanup and exit.
     377             : 
     378           0 :             CPLFree(m_pszFname);
     379           0 :             m_pszFname = nullptr;
     380           0 :             CSLDestroy(m_papszTABFile);
     381           0 :             m_papszTABFile = nullptr;
     382           0 :             CPLFree(pszTmpFname);
     383             : 
     384           0 :             return -1;
     385             :         }
     386             :     }
     387             :     else
     388             :     {
     389             :         /*-------------------------------------------------------------
     390             :          * In Write access mode, the .TAB file will be written during the
     391             :          * Close() call... we will just set some defaults here.
     392             :          *------------------------------------------------------------*/
     393         117 :         m_nVersion = 300;
     394         117 :         if (pszCharset != nullptr)
     395         117 :             SetCharset(pszCharset);
     396             :         else
     397           0 :             SetCharset("Neutral");
     398         117 :         m_eTableType = TABTableNative;
     399             : 
     400             :         /*-------------------------------------------------------------
     401             :          * Do initial setup of feature definition.
     402             :          *------------------------------------------------------------*/
     403         117 :         char *pszFeatureClassName = TABGetBasename(m_pszFname);
     404         117 :         m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
     405         117 :         m_poDefn->Reference();
     406         117 :         CPLFree(pszFeatureClassName);
     407             : 
     408         117 :         m_bNeedTABRewrite = TRUE;
     409             :     }
     410             : 
     411             :     /*-----------------------------------------------------------------
     412             :      * Open .DAT file (or .DBF)
     413             :      *----------------------------------------------------------------*/
     414        1418 :     if (nFnameLen > 4 && strcmp(pszTmpFname + nFnameLen - 4, ".TAB") == 0)
     415             :     {
     416           3 :         if (m_eTableType == TABTableDBF)
     417           0 :             strcpy(pszTmpFname + nFnameLen - 4, ".DBF");
     418             :         else  // Default is NATIVE
     419           3 :             strcpy(pszTmpFname + nFnameLen - 4, ".DAT");
     420             :     }
     421             :     else
     422             :     {
     423        1415 :         if (m_eTableType == TABTableDBF)
     424           0 :             strcpy(pszTmpFname + nFnameLen - 4, ".dbf");
     425             :         else  // Default is NATIVE
     426        1415 :             strcpy(pszTmpFname + nFnameLen - 4, ".dat");
     427             :     }
     428             : 
     429             : #ifndef _WIN32
     430        1418 :     TABAdjustFilenameExtension(pszTmpFname);
     431             : #endif
     432             : 
     433        2836 :     CPLString oEncoding;
     434             : 
     435        1418 :     if (eAccess == TABRead || eAccess == TABReadWrite)
     436             :     {
     437        1301 :         oEncoding = CharsetToEncoding(GetCharset());
     438             :     }
     439         117 :     else if (eAccess == TABWrite)
     440             :     {
     441         117 :         oEncoding = CharsetToEncoding(pszCharset);
     442             :     }
     443             : 
     444        1418 :     m_poDATFile = new TABDATFile(oEncoding);
     445             : 
     446        1418 :     if (m_poDATFile->Open(pszTmpFname, eAccess, m_eTableType) != 0)
     447             :     {
     448             :         // Open Failed... an error has already been reported, just return.
     449           0 :         CPLFree(pszTmpFname);
     450           0 :         Close();
     451           0 :         if (bTestOpenNoError)
     452           0 :             CPLErrorReset();
     453             : 
     454           0 :         return -1;
     455             :     }
     456             : 
     457        1418 :     m_nLastFeatureId = m_poDATFile->GetNumRecords();
     458             : 
     459             :     /*-----------------------------------------------------------------
     460             :      * Parse .TAB file field defs and build FeatureDefn (only in read access)
     461             :      *----------------------------------------------------------------*/
     462        2719 :     if ((m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) &&
     463        1301 :         ParseTABFileFields() != 0)
     464             :     {
     465             :         // Failed... an error has already been reported, just return.
     466          16 :         CPLFree(pszTmpFname);
     467          16 :         Close();
     468          16 :         if (bTestOpenNoError)
     469          16 :             CPLErrorReset();
     470             : 
     471          16 :         return -1;
     472             :     }
     473             : 
     474             :     /*-----------------------------------------------------------------
     475             :      * Open .MAP (and .ID) file
     476             :      * Note that the .MAP and .ID files are optional.  Failure to open them
     477             :      * is not an error... it simply means that all features will be returned
     478             :      * with NONE geometry.
     479             :      *----------------------------------------------------------------*/
     480        1402 :     bool bUpperCase = false;
     481        1402 :     if (nFnameLen > 4 && strcmp(pszTmpFname + nFnameLen - 4, ".DAT") == 0)
     482             :     {
     483           3 :         bUpperCase = true;
     484           3 :         strcpy(pszTmpFname + nFnameLen - 4, ".MAP");
     485             :     }
     486             :     else
     487        1399 :         strcpy(pszTmpFname + nFnameLen - 4, ".map");
     488             : 
     489             : #ifndef _WIN32
     490        1402 :     TABAdjustFilenameExtension(pszTmpFname);
     491             : #endif
     492             : 
     493        1402 :     m_poMAPFile = new TABMAPFile(oEncoding);
     494        1402 :     if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)
     495             :     {
     496             :         /*-------------------------------------------------------------
     497             :          * Read access: .MAP/.ID are optional... try to open but return
     498             :          * no error if files do not exist.
     499             :          *------------------------------------------------------------*/
     500        1285 :         if (m_poMAPFile->Open(pszTmpFname, eAccess, TRUE) < 0)
     501             :         {
     502             :             // File exists, but Open Failed...
     503             :             // we have to produce an error message
     504           0 :             if (!bTestOpenNoError)
     505           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s",
     506             :                          pszTmpFname);
     507             :             else
     508           0 :                 CPLErrorReset();
     509             : 
     510           0 :             CPLFree(pszTmpFname);
     511           0 :             Close();
     512           0 :             return -1;
     513             :         }
     514             : 
     515             :         /*-------------------------------------------------------------
     516             :          * Set geometry type if the geometry objects are uniform.
     517             :          *------------------------------------------------------------*/
     518        1285 :         int numPoints = 0, numRegions = 0, numTexts = 0, numLines = 0;
     519             : 
     520        1285 :         GetFeatureCountByType(numPoints, numLines, numRegions, numTexts);
     521             : 
     522        1285 :         if (numPoints >= 0 && numTexts >= 0 && numPoints < INT_MAX - numTexts)
     523        1285 :             numPoints += numTexts;
     524        1285 :         if (numPoints > 0 && numLines == 0 && numRegions == 0)
     525        1191 :             m_poDefn->SetGeomType(wkbPoint);
     526          94 :         else if (numPoints == 0 && numLines > 0 && numRegions == 0)
     527           8 :             m_poDefn->SetGeomType(wkbLineString);
     528          86 :         else if (m_eAccessMode == TABRead && numPoints == 0 && numLines == 0 &&
     529          34 :                  numRegions == 0)
     530             :             /* No geometries present; this is an aspatial dataset */
     531          20 :             m_poDefn->SetGeomType(wkbNone);
     532             :         else
     533             :         {
     534             :             /* we leave it unknown indicating a mixture */
     535        1285 :         }
     536             :     }
     537         117 :     else if (m_poMAPFile->Open(pszTmpFname, eAccess, FALSE,
     538         117 :                                nBlockSizeForCreate) != 0)
     539             :     {
     540             :         // Open Failed for write...
     541             :         // an error has already been reported, just return.
     542             : 
     543           1 :         m_poMAPFile->Close();
     544           1 :         delete m_poMAPFile;
     545           1 :         m_poMAPFile = nullptr;
     546             : 
     547           1 :         CPLFree(pszTmpFname);
     548           1 :         Close();
     549           1 :         if (bTestOpenNoError)
     550           0 :             CPLErrorReset();
     551             : 
     552           1 :         return -1;
     553             :     }
     554             : 
     555             :     /*-----------------------------------------------------------------
     556             :      * Initializing the attribute index (.IND) support
     557             :      *----------------------------------------------------------------*/
     558        1401 :     bool bHasIndex = false;
     559             : 
     560             :     CPLXMLNode *psRoot =
     561        1401 :         CPLCreateXMLNode(nullptr, CXT_Element, "OGRMILayerAttrIndex");
     562        1401 :     OGRFeatureDefn *poLayerDefn = GetLayerDefn();
     563        2902 :     for (int iField = 0; iField < poLayerDefn->GetFieldCount(); iField++)
     564             :     {
     565        1502 :         int iIndexIndex = GetFieldIndexNumber(iField);
     566        1502 :         if (iIndexIndex > 0)
     567             :         {
     568           8 :             if (!bHasIndex)
     569             :             {
     570           7 :                 const char *pszIndFilename = CPLFormCIFilename(
     571             :                     CPLGetPath(pszFname), CPLGetBasename(pszFname),
     572             :                     (bUpperCase) ? "IND" : "ind");
     573             :                 VSIStatBufL sStat;
     574           7 :                 if (VSIStatL(pszIndFilename, &sStat) == 0)
     575             :                 {
     576           6 :                     CPLCreateXMLElementAndValue(psRoot, "MIIDFilename",
     577             :                                                 pszIndFilename);
     578             :                 }
     579             :                 else
     580             :                 {
     581           1 :                     CPLDebug("MITAB",
     582             :                              "At least one field is supposed to be indexed, "
     583             :                              "but index file is missing");
     584           1 :                     break;
     585             :                 }
     586             :             }
     587             : 
     588             :             CPLXMLNode *psIndex =
     589           7 :                 CPLCreateXMLNode(psRoot, CXT_Element, "OGRMIAttrIndex");
     590           7 :             CPLCreateXMLElementAndValue(psIndex, "FieldIndex",
     591             :                                         CPLSPrintf("%d", iField));
     592           7 :             CPLCreateXMLElementAndValue(
     593             :                 psIndex, "FieldName",
     594           7 :                 poLayerDefn->GetFieldDefn(iField)->GetNameRef());
     595           7 :             CPLCreateXMLElementAndValue(psIndex, "IndexIndex",
     596             :                                         CPLSPrintf("%d", iIndexIndex));
     597           7 :             bHasIndex = true;
     598             :         }
     599             :     }
     600             : 
     601        1401 :     if (bHasIndex)
     602             :     {
     603           6 :         char *pszRawXML = CPLSerializeXMLTree(psRoot);
     604           6 :         InitializeIndexSupport(pszRawXML);
     605           6 :         CPLFree(pszRawXML);
     606             :     }
     607             : 
     608        1401 :     CPLDestroyXMLNode(psRoot);
     609             : 
     610        1401 :     CPLFree(pszTmpFname);
     611        1401 :     pszTmpFname = nullptr;
     612             : 
     613        2686 :     if (m_poDefn != nullptr && m_eAccessMode != TABWrite &&
     614        1285 :         m_poDefn->GetGeomFieldCount() != 0)
     615        1265 :         m_poDefn->GetGeomFieldDefn(0)->SetSpatialRef(GetSpatialRef());
     616             : 
     617        1401 :     if (m_poDefn)
     618        1401 :         m_poDefn->Seal(/* bSealFields = */ true);
     619             : 
     620        1401 :     return 0;
     621             : }
     622             : 
     623             : /**********************************************************************
     624             :  *                   TABFile::ParseTABFileFirstPass()
     625             :  *
     626             :  * Do a first pass in the TAB header file to establish the table type, etc.
     627             :  * and store any useful information into class members.
     628             :  *
     629             :  * This private method should be used only during the Open() call.
     630             :  *
     631             :  * Returns 0 on success, -1 on error.
     632             :  **********************************************************************/
     633        1301 : int TABFile::ParseTABFileFirstPass(GBool bTestOpenNoError)
     634             : {
     635        1301 :     int iLine, numLines, numFields = 0;
     636        1301 :     char **papszTok = nullptr;
     637        1301 :     GBool bInsideTableDef = FALSE, bFoundTableFields = FALSE;
     638             : 
     639        1301 :     if (m_eAccessMode == TABWrite)
     640             :     {
     641           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     642             :                  "ParseTABFile() can be used only with Read access.");
     643           0 :         return -1;
     644             :     }
     645             : 
     646        1301 :     numLines = CSLCount(m_papszTABFile);
     647             : 
     648       12013 :     for (iLine = 0; iLine < numLines; iLine++)
     649             :     {
     650             :         /*-------------------------------------------------------------
     651             :          * Tokenize the next .TAB line, and check first keyword
     652             :          *------------------------------------------------------------*/
     653       10712 :         CSLDestroy(papszTok);
     654       10712 :         papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
     655             :                                             TRUE, FALSE);
     656       10712 :         if (CSLCount(papszTok) < 2)
     657        2634 :             continue;  // All interesting lines have at least 2 tokens
     658             : 
     659        8078 :         if (EQUAL(papszTok[0], "!version"))
     660             :         {
     661        1301 :             m_nVersion = atoi(papszTok[1]);
     662        1301 :             if (m_nVersion == 100)
     663             :             {
     664             :                 /* Version 100 files contain only the fields definition,
     665             :                  * so we set default values for the other params.
     666             :                  */
     667           0 :                 bInsideTableDef = TRUE;
     668           0 :                 SetCharset("Neutral");
     669           0 :                 m_eTableType = TABTableNative;
     670             :             }
     671             :         }
     672        6777 :         else if (EQUAL(papszTok[0], "!edit_version"))
     673             :         {
     674             :             /* Sometimes, V450 files have version 300 + edit_version 450
     675             :              * for us version and edit_version are the same
     676             :              */
     677           0 :             m_nVersion = atoi(papszTok[1]);
     678             :         }
     679        6777 :         else if (EQUAL(papszTok[0], "!charset"))
     680             :         {
     681        1301 :             SetCharset(papszTok[1]);
     682             :         }
     683        5476 :         else if (EQUAL(papszTok[0], "Definition") &&
     684        1301 :                  EQUAL(papszTok[1], "Table"))
     685             :         {
     686        1301 :             bInsideTableDef = TRUE;
     687             :         }
     688        4175 :         else if (bInsideTableDef && !bFoundTableFields &&
     689        2606 :                  (EQUAL(papszTok[0], "Type") || EQUAL(papszTok[0], "FORMAT:")))
     690             :         {
     691        1301 :             if (EQUAL(papszTok[1], "NATIVE") || EQUAL(papszTok[1], "LINKED"))
     692        1301 :                 m_eTableType = TABTableNative;
     693           0 :             else if (EQUAL(papszTok[1], "DBF"))
     694           0 :                 m_eTableType = TABTableDBF;
     695             :             else
     696             :             {
     697             :                 // Type=ACCESS, or other unsupported type... cannot open!
     698           0 :                 if (!bTestOpenNoError)
     699           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     700             :                              "Unsupported table type '%s' in file %s.  "
     701             :                              "This type of .TAB file cannot be read by this "
     702             :                              "library.",
     703           0 :                              papszTok[1], m_pszFname);
     704           0 :                 CSLDestroy(papszTok);
     705           0 :                 return -1;
     706             :             }
     707             :         }
     708        2874 :         else if (bInsideTableDef && !bFoundTableFields &&
     709        1305 :                  EQUAL(papszTok[0], "Description"))
     710             :         {
     711           8 :             auto osDescription = GetTabDescription(m_papszTABFile[iLine]);
     712           4 :             if (!osDescription.empty())
     713             :             {
     714           4 :                 const char *pszEncoding = GetEncoding();
     715           4 :                 if (pszEncoding == nullptr || EQUAL(pszEncoding, ""))
     716             :                 {
     717             :                     std::shared_ptr<char> oUnescapedDescription(
     718           0 :                         UnescapeString(osDescription.c_str()), CPLFree);
     719           0 :                     IMapInfoFile::SetMetadataItem(DESCRIPTION_KEY,
     720           0 :                                                   oUnescapedDescription.get());
     721             :                 }
     722             :                 else
     723             :                 {
     724             :                     std::shared_ptr<char> oEncodedDescription(
     725             :                         CPLRecode(osDescription.c_str(), pszEncoding,
     726             :                                   CPL_ENC_UTF8),
     727           8 :                         CPLFree);
     728             : 
     729             :                     std::shared_ptr<char> oUnescapedDescription(
     730           8 :                         UnescapeString(oEncodedDescription.get()), CPLFree);
     731           4 :                     IMapInfoFile::SetMetadataItem(DESCRIPTION_KEY,
     732           4 :                                                   oUnescapedDescription.get());
     733             :                 }
     734           4 :             }
     735             :         }
     736        2870 :         else if (bInsideTableDef && !bFoundTableFields &&
     737        1301 :                  (EQUAL(papszTok[0], "Fields") ||
     738           0 :                   EQUAL(papszTok[0], "FIELDS:")))
     739             :         {
     740             :             /*---------------------------------------------------------
     741             :              * We found the list of table fields
     742             :              * Just remember number of fields... the field types will be
     743             :              * parsed inside ParseTABFileFields() later...
     744             :              *--------------------------------------------------------*/
     745        1301 :             bFoundTableFields = TRUE;
     746        1301 :             numFields = atoi(papszTok[1]);
     747             : 
     748        1301 :             if (numFields < 1 || numFields > 2048 ||
     749        1301 :                 iLine + numFields >= numLines)
     750             :             {
     751           0 :                 if (!bTestOpenNoError)
     752           0 :                     CPLError(
     753             :                         CE_Failure, CPLE_FileIO,
     754             :                         "Invalid number of fields (%s) at line %d in file %s",
     755           0 :                         papszTok[1], iLine + 1, m_pszFname);
     756             : 
     757           0 :                 CSLDestroy(papszTok);
     758           0 :                 return -1;
     759             :             }
     760             : 
     761        1301 :             bInsideTableDef = FALSE;
     762             :         } /* end of fields section*/
     763             :         else
     764             :         {
     765             :             // Simply Ignore unrecognized lines
     766             :         }
     767             :     }
     768             : 
     769        1301 :     CSLDestroy(papszTok);
     770             : 
     771        1301 :     if (m_pszCharset == nullptr)
     772           0 :         SetCharset("Neutral");
     773             : 
     774        1301 :     if (numFields == 0)
     775             :     {
     776           0 :         if (!bTestOpenNoError)
     777           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     778             :                      "%s contains no table field definition.  "
     779             :                      "This type of .TAB file cannot be read by this library.",
     780             :                      m_pszFname);
     781           0 :         return -1;
     782             :     }
     783             : 
     784        1301 :     return 0;
     785             : }
     786             : 
     787             : /**********************************************************************
     788             :  *                   TABFile::ParseTABFileFields()
     789             :  *
     790             :  * Extract the field definition from the TAB header file, validate
     791             :  * with what we have in the previously opened .DAT or .DBF file, and
     792             :  * finally build the m_poDefn OGRFeatureDefn for this dataset.
     793             :  *
     794             :  * This private method should be used only during the Open() call and after
     795             :  * ParseTABFileFirstPass() has been called.
     796             :  *
     797             :  * Returns 0 on success, -1 on error.
     798             :  **********************************************************************/
     799        1301 : int TABFile::ParseTABFileFields()
     800             : {
     801             : 
     802        1301 :     if (m_eAccessMode == TABWrite)
     803             :     {
     804           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     805             :                  "ParseTABFile() can be used only with Read access.");
     806           0 :         return -1;
     807             :     }
     808             : 
     809        1301 :     char *pszFeatureClassName = TABGetBasename(m_pszFname);
     810        1301 :     m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
     811        1301 :     CPLFree(pszFeatureClassName);
     812             :     // Ref count defaults to 0... set it to 1
     813        1301 :     m_poDefn->Reference();
     814             : 
     815             :     /*-------------------------------------------------------------
     816             :      * Scan for fields.
     817             :      *------------------------------------------------------------*/
     818        1301 :     OGRFieldDefn *poFieldDefn = nullptr;
     819        1301 :     char **papszTok = nullptr;
     820             : 
     821        1301 :     const int numLines = CSLCount(m_papszTABFile);
     822        9111 :     for (int iLine = 0; iLine < numLines; iLine++)
     823             :     {
     824             :         /*-------------------------------------------------------------
     825             :          * Tokenize the next .TAB line, and check first keyword
     826             :          *------------------------------------------------------------*/
     827        9111 :         const char *pszStr = m_papszTABFile[iLine];
     828       14323 :         while (*pszStr != '\0' && isspace(static_cast<unsigned char>(*pszStr)))
     829        5212 :             pszStr++;
     830             : 
     831        9111 :         if (STARTS_WITH_CI(pszStr, "Fields") && CPLStrnlen(pszStr, 7) >= 7)
     832             :         {
     833             :             /*---------------------------------------------------------
     834             :              * We found the list of table fields
     835             :              *--------------------------------------------------------*/
     836        1301 :             int iField = 0;
     837        1301 :             int numFields = atoi(pszStr + 7);
     838        1301 :             if (numFields < 1 || numFields > 2048 ||
     839        1301 :                 iLine + numFields >= numLines)
     840             :             {
     841           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     842             :                          "Invalid number of fields (%s) at line %d in file %s",
     843             :                          pszStr + 7, iLine + 1, m_pszFname);
     844           0 :                 CSLDestroy(papszTok);
     845           0 :                 return -1;
     846             :             }
     847             : 
     848             :             // Alloc the array to keep track of indexed fields
     849        1301 :             m_panIndexNo =
     850        1301 :                 static_cast<int *>(CPLCalloc(numFields, sizeof(int)));
     851             : 
     852        1301 :             iLine++;
     853        1301 :             poFieldDefn = nullptr;
     854        2805 :             for (iField = 0; iField < numFields; iField++, iLine++)
     855             :             {
     856             :                 /*-----------------------------------------------------
     857             :                  * For each field definition found in the .TAB:
     858             :                  * Pass the info to the DAT file object.  It will validate
     859             :                  * the info with what is found in the .DAT header, and will
     860             :                  * also use this info later to interpret field values.
     861             :                  *
     862             :                  * We also create the OGRFieldDefn at the same time to
     863             :                  * initialize the OGRFeatureDefn
     864             :                  *----------------------------------------------------*/
     865        1520 :                 CSLDestroy(papszTok);
     866        1520 :                 papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine],
     867             :                                                     " \t(),;", TRUE, FALSE);
     868        1520 :                 const int numTok = CSLCount(papszTok);
     869             : 
     870        1520 :                 CPLAssert(m_poDefn);
     871        1520 :                 poFieldDefn = nullptr;
     872             : 
     873        1520 :                 CPLString osFieldName;
     874        1520 :                 if (numTok > 0)
     875             :                 {
     876        1520 :                     osFieldName = papszTok[0];
     877        1520 :                     if (strlen(GetEncoding()) > 0)
     878          73 :                         osFieldName.Recode(GetEncoding(), CPL_ENC_UTF8);
     879             :                 }
     880             : 
     881        1520 :                 int nStatus = -1;
     882        1520 :                 if (numTok >= 3 && EQUAL(papszTok[1], "char"))
     883             :                 {
     884             :                     /*-------------------------------------------------
     885             :                      * CHAR type
     886             :                      *------------------------------------------------*/
     887         198 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     888         198 :                         iField, osFieldName, TABFChar, atoi(papszTok[2]), 0);
     889         198 :                     poFieldDefn = new OGRFieldDefn(osFieldName, OFTString);
     890         198 :                     poFieldDefn->SetWidth(atoi(papszTok[2]));
     891             :                 }
     892        1322 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "integer"))
     893             :                 {
     894             :                     /*-------------------------------------------------
     895             :                      * INTEGER type
     896             :                      *------------------------------------------------*/
     897        1242 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     898             :                         iField, osFieldName, TABFInteger, 0, 0);
     899        1242 :                     poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger);
     900        1242 :                     if (numTok > 2 && atoi(papszTok[2]) > 0)
     901           2 :                         poFieldDefn->SetWidth(atoi(papszTok[2]));
     902             :                 }
     903          80 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "smallint"))
     904             :                 {
     905             :                     /*-------------------------------------------------
     906             :                      * SMALLINT type
     907             :                      *------------------------------------------------*/
     908           1 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     909             :                         iField, osFieldName, TABFSmallInt, 0, 0);
     910           1 :                     poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger);
     911           1 :                     if (numTok > 2 && atoi(papszTok[2]) > 0)
     912           0 :                         poFieldDefn->SetWidth(atoi(papszTok[2]));
     913             :                 }
     914          79 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "largeint"))
     915             :                 {
     916             :                     /*-------------------------------------------------
     917             :                      * LargeInt type
     918             :                      *------------------------------------------------*/
     919           1 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     920             :                         iField, osFieldName, TABFLargeInt, 0, 0);
     921           1 :                     poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger64);
     922           1 :                     if (numTok > 2 && atoi(papszTok[2]) > 0)
     923           0 :                         poFieldDefn->SetWidth(atoi(papszTok[2]));
     924             :                 }
     925          78 :                 else if (numTok >= 4 && EQUAL(papszTok[1], "decimal"))
     926             :                 {
     927             :                     /*-------------------------------------------------
     928             :                      * DECIMAL type
     929             :                      *------------------------------------------------*/
     930           7 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     931           7 :                         iField, osFieldName, TABFDecimal, atoi(papszTok[2]),
     932           7 :                         atoi(papszTok[3]));
     933           7 :                     poFieldDefn = new OGRFieldDefn(osFieldName, OFTReal);
     934           7 :                     poFieldDefn->SetWidth(atoi(papszTok[2]));
     935           7 :                     poFieldDefn->SetPrecision(atoi(papszTok[3]));
     936             :                 }
     937          71 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "float"))
     938             :                 {
     939             :                     /*-------------------------------------------------
     940             :                      * FLOAT type
     941             :                      *------------------------------------------------*/
     942          30 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     943             :                         iField, osFieldName, TABFFloat, 0, 0);
     944          30 :                     poFieldDefn = new OGRFieldDefn(osFieldName, OFTReal);
     945             :                 }
     946          41 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "date"))
     947             :                 {
     948             :                     /*-------------------------------------------------
     949             :                      * DATE type (returned as a string: "DD/MM/YYYY")
     950             :                      *------------------------------------------------*/
     951          20 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     952             :                         iField, osFieldName, TABFDate, 0, 0);
     953          20 :                     poFieldDefn = new OGRFieldDefn(osFieldName,
     954             : #ifdef MITAB_USE_OFTDATETIME
     955          20 :                                                    OFTDate);
     956             : #else
     957             :                                                    OFTString);
     958             : #endif
     959          20 :                     poFieldDefn->SetWidth(10);
     960             :                 }
     961          21 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "time"))
     962             :                 {
     963             :                     /*-------------------------------------------------
     964             :                      * TIME type (returned as a string: "HH:MM:SS")
     965             :                      *------------------------------------------------*/
     966           2 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     967             :                         iField, osFieldName, TABFTime, 0, 0);
     968           2 :                     poFieldDefn = new OGRFieldDefn(osFieldName,
     969             : #ifdef MITAB_USE_OFTDATETIME
     970           2 :                                                    OFTTime);
     971             : #else
     972             :                                                    OFTString);
     973             : #endif
     974           2 :                     poFieldDefn->SetWidth(9);
     975             :                 }
     976          19 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "datetime"))
     977             :                 {
     978             :                     /*-------------------------------------------------
     979             :                      * DATETIME type (returned as a string: "DD/MM/YYYY
     980             :                      *HH:MM:SS")
     981             :                      *------------------------------------------------*/
     982          18 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     983             :                         iField, osFieldName, TABFDateTime, 0, 0);
     984          18 :                     poFieldDefn = new OGRFieldDefn(osFieldName,
     985             : #ifdef MITAB_USE_OFTDATETIME
     986          18 :                                                    OFTDateTime);
     987             : #else
     988             :                                                    OFTString);
     989             : #endif
     990          18 :                     poFieldDefn->SetWidth(19);
     991             :                 }
     992           1 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "logical"))
     993             :                 {
     994             :                     /*-------------------------------------------------
     995             :                      * LOGICAL type (value "T" or "F")
     996             :                      *------------------------------------------------*/
     997           1 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
     998             :                         iField, osFieldName, TABFLogical, 0, 0);
     999           1 :                     poFieldDefn = new OGRFieldDefn(osFieldName, OFTString);
    1000           1 :                     poFieldDefn->SetWidth(1);
    1001             :                 }
    1002             :                 else
    1003             :                 {
    1004             :                     // Unrecognized field type or line corrupt
    1005             :                 }
    1006             : 
    1007        1520 :                 if (nStatus != 0)
    1008             :                 {
    1009          16 :                     CPLError(CE_Failure, CPLE_FileIO,
    1010             :                              "Failed to parse field definition at line %d in "
    1011             :                              "file %s",
    1012             :                              iLine + 1, m_pszFname);
    1013          16 :                     CSLDestroy(papszTok);
    1014          16 :                     delete poFieldDefn;
    1015          16 :                     return -1;
    1016             :                 }
    1017             :                 /*-----------------------------------------------------
    1018             :                  * Keep track of index number if present
    1019             :                  *----------------------------------------------------*/
    1020        1504 :                 if (numTok >= 4 && EQUAL(papszTok[numTok - 2], "index"))
    1021             :                 {
    1022           9 :                     m_panIndexNo[iField] = atoi(papszTok[numTok - 1]);
    1023             :                 }
    1024             :                 else
    1025             :                 {
    1026        1495 :                     m_panIndexNo[iField] = 0;
    1027             :                 }
    1028             : 
    1029             :                 /*-----------------------------------------------------
    1030             :                  * Add the FieldDefn to the FeatureDefn and continue with
    1031             :                  * the next one.
    1032             :                  *----------------------------------------------------*/
    1033        1504 :                 m_poDefn->AddFieldDefn(poFieldDefn);
    1034             :                 m_oSetFields.insert(
    1035        1504 :                     CPLString(poFieldDefn->GetNameRef()).toupper());
    1036             :                 // AddFieldDenf() takes a copy, so we delete the original
    1037        1504 :                 delete poFieldDefn;
    1038        1504 :                 poFieldDefn = nullptr;
    1039             :             }
    1040             : 
    1041             :             /*---------------------------------------------------------
    1042             :              * OK, we're done... end the loop now.
    1043             :              *--------------------------------------------------------*/
    1044        1285 :             break;
    1045             :         } /* end of fields section*/
    1046             :         else
    1047             :         {
    1048             :             // Simply Ignore unrecognized lines
    1049             :         }
    1050             :     }
    1051             : 
    1052        1285 :     CSLDestroy(papszTok);
    1053             : 
    1054        1285 :     if (m_poDefn->GetFieldCount() == 0)
    1055             :     {
    1056           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1057             :                  "%s contains no table field definition.  "
    1058             :                  "This type of .TAB file cannot be read by this library.",
    1059             :                  m_pszFname);
    1060           0 :         return -1;
    1061             :     }
    1062             : 
    1063        1285 :     return 0;
    1064             : }
    1065             : 
    1066             : /**********************************************************************
    1067             :  *                   TABFile::WriteTABFile()
    1068             :  *
    1069             :  * Generate the .TAB file using mainly the attribute fields definition.
    1070             :  *
    1071             :  *
    1072             :  * Returns 0 on success, -1 on error.
    1073             :  **********************************************************************/
    1074        1300 : int TABFile::WriteTABFile()
    1075             : {
    1076        1300 :     if (!m_bNeedTABRewrite)
    1077             :     {
    1078        1164 :         return 0;
    1079             :     }
    1080             : 
    1081         136 :     if (m_poMAPFile == nullptr || m_eAccessMode == TABRead)
    1082             :     {
    1083           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1084             :                  "WriteTABFile() can be used only with Write access.");
    1085           0 :         return -1;
    1086             :     }
    1087             : 
    1088             :     // First update file version number...
    1089         136 :     int nMapObjVersion = m_poMAPFile->GetMinTABFileVersion();
    1090         136 :     m_nVersion = std::max(m_nVersion, nMapObjVersion);
    1091             : 
    1092         136 :     VSILFILE *fp = VSIFOpenL(m_pszFname, "wt");
    1093         136 :     if (fp != nullptr)
    1094             :     {
    1095         136 :         VSIFPrintfL(fp, "!table\n");
    1096         136 :         VSIFPrintfL(fp, "!version %d\n", m_nVersion);
    1097         136 :         VSIFPrintfL(fp, "!charset %s\n", m_pszCharset);
    1098         136 :         VSIFPrintfL(fp, "\n");
    1099             : 
    1100         136 :         if (m_poDefn && m_poDefn->GetFieldCount() > 0)
    1101             :         {
    1102         119 :             VSIFPrintfL(fp, "Definition Table\n");
    1103         119 :             VSIFPrintfL(fp, "  Type NATIVE Charset \"%s\"\n", m_pszCharset);
    1104         119 :             const char *pszDescription = GetMetadataItem(DESCRIPTION_KEY);
    1105         119 :             if (nullptr != pszDescription)
    1106             :             {
    1107             :                 std::shared_ptr<char> oEscapedDescription(
    1108           6 :                     EscapeString(pszDescription, true), CPLFree);
    1109           3 :                 const char *pszEncoding = GetEncoding();
    1110           3 :                 if (nullptr == pszEncoding || EQUAL(pszEncoding, ""))
    1111             :                 {
    1112           0 :                     VSIFPrintfL(fp, "  Description \"%s\"\n",
    1113             :                                 oEscapedDescription.get());
    1114             :                 }
    1115             :                 else
    1116             :                 {
    1117             :                     std::shared_ptr<char> oEncodedDescription(
    1118           3 :                         CPLRecode(oEscapedDescription.get(), CPL_ENC_UTF8,
    1119             :                                   pszEncoding),
    1120           6 :                         CPLFree);
    1121           3 :                     VSIFPrintfL(fp, "  Description \"%s\"\n",
    1122             :                                 oEncodedDescription.get());
    1123             :                 }
    1124             :             }
    1125         119 :             VSIFPrintfL(fp, "  Fields %d\n", m_poDefn->GetFieldCount());
    1126             : 
    1127         406 :             for (int iField = 0; iField < m_poDefn->GetFieldCount(); iField++)
    1128             :             {
    1129         287 :                 const char *pszFieldType = nullptr;
    1130         287 :                 OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField);
    1131         287 :                 switch (GetNativeFieldType(iField))
    1132             :                 {
    1133         147 :                     case TABFChar:
    1134             :                         pszFieldType =
    1135         147 :                             CPLSPrintf("Char (%d)", poFieldDefn->GetWidth());
    1136         147 :                         break;
    1137           3 :                     case TABFDecimal:
    1138           3 :                         pszFieldType = CPLSPrintf("Decimal (%d,%d)",
    1139             :                                                   poFieldDefn->GetWidth(),
    1140             :                                                   poFieldDefn->GetPrecision());
    1141           3 :                         break;
    1142          74 :                     case TABFInteger:
    1143          74 :                         if (poFieldDefn->GetWidth() == 0)
    1144          70 :                             pszFieldType = "Integer";
    1145             :                         else
    1146           4 :                             pszFieldType = CPLSPrintf("Integer (%d)",
    1147             :                                                       poFieldDefn->GetWidth());
    1148          74 :                         break;
    1149           0 :                     case TABFSmallInt:
    1150           0 :                         if (poFieldDefn->GetWidth() == 0)
    1151           0 :                             pszFieldType = "SmallInt";
    1152             :                         else
    1153           0 :                             pszFieldType = CPLSPrintf("SmallInt (%d)",
    1154             :                                                       poFieldDefn->GetWidth());
    1155           0 :                         break;
    1156           1 :                     case TABFLargeInt:
    1157           1 :                         if (poFieldDefn->GetWidth() == 0)
    1158           1 :                             pszFieldType = "LargeInt";
    1159             :                         else
    1160           0 :                             pszFieldType = CPLSPrintf("LargeInt (%d)",
    1161             :                                                       poFieldDefn->GetWidth());
    1162           1 :                         break;
    1163          24 :                     case TABFFloat:
    1164          24 :                         pszFieldType = "Float";
    1165          24 :                         break;
    1166           0 :                     case TABFLogical:
    1167           0 :                         pszFieldType = "Logical";
    1168           0 :                         break;
    1169          18 :                     case TABFDate:
    1170          18 :                         pszFieldType = "Date";
    1171          18 :                         break;
    1172           2 :                     case TABFTime:
    1173           2 :                         pszFieldType = "Time";
    1174           2 :                         break;
    1175          18 :                     case TABFDateTime:
    1176          18 :                         pszFieldType = "DateTime";
    1177          18 :                         break;
    1178           0 :                     default:
    1179             :                         // Unsupported field type!!!  This should never happen.
    1180           0 :                         CPLError(CE_Failure, CPLE_AssertionFailed,
    1181             :                                  "WriteTABFile(): Unsupported field type");
    1182           0 :                         VSIFCloseL(fp);
    1183           0 :                         return -1;
    1184             :                 }
    1185             : 
    1186         574 :                 CPLString osFieldName(poFieldDefn->GetNameRef());
    1187             : 
    1188         287 :                 if (strlen(GetEncoding()) > 0)
    1189          12 :                     osFieldName.Recode(CPL_ENC_UTF8, GetEncoding());
    1190             : 
    1191         287 :                 char *pszCleanName = TABCleanFieldName(osFieldName);
    1192         287 :                 osFieldName = pszCleanName;
    1193         287 :                 CPLFree(pszCleanName);
    1194             : 
    1195         287 :                 if (GetFieldIndexNumber(iField) == 0)
    1196             :                 {
    1197         286 :                     VSIFPrintfL(fp, "    %s %s ;\n", osFieldName.c_str(),
    1198             :                                 pszFieldType);
    1199             :                 }
    1200             :                 else
    1201             :                 {
    1202           1 :                     VSIFPrintfL(fp, "    %s %s Index %d ;\n",
    1203             :                                 osFieldName.c_str(), pszFieldType,
    1204             :                                 GetFieldIndexNumber(iField));
    1205             :                 }
    1206             :             }
    1207             :         }
    1208             :         else
    1209             :         {
    1210          17 :             VSIFPrintfL(fp, "Definition Table\n");
    1211          17 :             VSIFPrintfL(fp, "  Type NATIVE Charset \"%s\"\n", m_pszCharset);
    1212          17 :             VSIFPrintfL(fp, "  Fields 1\n");
    1213          17 :             VSIFPrintfL(fp, "    FID Integer ;\n");
    1214             :         }
    1215             : 
    1216         136 :         VSIFCloseL(fp);
    1217             : 
    1218         136 :         m_bNeedTABRewrite = FALSE;
    1219             :     }
    1220             :     else
    1221             :     {
    1222           0 :         CPLError(CE_Failure, CPLE_FileIO, "Failed to create file `%s'",
    1223             :                  m_pszFname);
    1224           0 :         return -1;
    1225             :     }
    1226             : 
    1227         136 :     return 0;
    1228             : }
    1229             : 
    1230             : /**********************************************************************
    1231             :  *                   TABFile::Close()
    1232             :  *
    1233             :  * Close current file, and release all memory used.
    1234             :  *
    1235             :  * Returns 0 on success, -1 on error.
    1236             :  **********************************************************************/
    1237        1435 : int TABFile::Close()
    1238             : {
    1239        1435 :     CPLErrorReset();
    1240             : 
    1241             :     // Commit the latest changes to the file...
    1242             : 
    1243        1435 :     if (m_poMAPFile)
    1244             :     {
    1245             :         // In Write access, it is time to write the .TAB file.
    1246        1401 :         if (m_eAccessMode != TABRead)
    1247             :         {
    1248        1163 :             WriteTABFile();
    1249             :         }
    1250             : 
    1251        1401 :         m_poMAPFile->Close();
    1252        1401 :         delete m_poMAPFile;
    1253        1401 :         m_poMAPFile = nullptr;
    1254             :     }
    1255             : 
    1256        1435 :     if (m_poDATFile)
    1257             :     {
    1258        1418 :         m_poDATFile->Close();
    1259        1418 :         delete m_poDATFile;
    1260        1418 :         m_poDATFile = nullptr;
    1261             :     }
    1262             : 
    1263        1435 :     if (m_poINDFile)
    1264             :     {
    1265           3 :         m_poINDFile->Close();
    1266           3 :         delete m_poINDFile;
    1267           3 :         m_poINDFile = nullptr;
    1268             :     }
    1269             : 
    1270        1435 :     if (m_poCurFeature)
    1271             :     {
    1272          12 :         delete m_poCurFeature;
    1273          12 :         m_poCurFeature = nullptr;
    1274             :     }
    1275             : 
    1276        1435 :     if (m_poDefn)
    1277        1418 :         m_poDefn->Release();
    1278        1435 :     m_poDefn = nullptr;
    1279             : 
    1280        1435 :     if (m_poSpatialRef)
    1281        1300 :         m_poSpatialRef->Release();
    1282        1435 :     m_poSpatialRef = nullptr;
    1283             : 
    1284        1435 :     CSLDestroy(m_papszTABFile);
    1285        1435 :     m_papszTABFile = nullptr;
    1286             : 
    1287        1435 :     CPLFree(m_pszFname);
    1288        1435 :     m_pszFname = nullptr;
    1289             : 
    1290        1435 :     CPLFree(m_pszCharset);
    1291        1435 :     m_pszCharset = nullptr;
    1292             : 
    1293        1435 :     CPLFree(m_panIndexNo);
    1294        1435 :     m_panIndexNo = nullptr;
    1295             : 
    1296        1435 :     CPLFree(m_panMatchingFIDs);
    1297        1435 :     m_panMatchingFIDs = nullptr;
    1298             : 
    1299        1435 :     return 0;
    1300             : }
    1301             : 
    1302             : /**********************************************************************
    1303             :  *                   TABFile::SetQuickSpatialIndexMode()
    1304             :  *
    1305             :  * Select "quick spatial index mode".
    1306             :  *
    1307             :  * The default behavior of MITAB is to generate an optimized spatial index,
    1308             :  * but this results in slower write speed.
    1309             :  *
    1310             :  * Applications that want faster write speed and do not care
    1311             :  * about the performance of spatial queries on the resulting file can
    1312             :  * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
    1313             :  * spatial index (actually emulating the type of spatial index produced
    1314             :  * by MITAB before version 1.6.0). In this mode writing files can be
    1315             :  * about 5 times faster, but spatial queries can be up to 30 times slower.
    1316             :  *
    1317             :  * Returns 0 on success, -1 on error.
    1318             :  **********************************************************************/
    1319           0 : int TABFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode /*=TRUE*/)
    1320             : {
    1321           0 :     if (m_eAccessMode != TABWrite || m_poMAPFile == nullptr)
    1322             :     {
    1323           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1324             :                  "SetQuickSpatialIndexMode() failed: file not opened for write "
    1325             :                  "access.");
    1326           0 :         return -1;
    1327             :     }
    1328             : 
    1329           0 :     return m_poMAPFile->SetQuickSpatialIndexMode(bQuickSpatialIndexMode);
    1330             : }
    1331             : 
    1332             : /**********************************************************************
    1333             :  *                   TABFile::GetNextFeatureId()
    1334             :  *
    1335             :  * Returns feature id that follows nPrevId, or -1 if it is the
    1336             :  * last feature id.  Pass nPrevId=-1 to fetch the first valid feature id.
    1337             :  **********************************************************************/
    1338      526817 : GIntBig TABFile::GetNextFeatureId(GIntBig nPrevId)
    1339             : {
    1340      526817 :     if (m_bLastOpWasWrite)
    1341           0 :         ResetReading();
    1342      526817 :     m_bLastOpWasRead = TRUE;
    1343             : 
    1344      526817 :     if (!CPL_INT64_FITS_ON_INT32(nPrevId))
    1345           0 :         return -1;
    1346             : 
    1347             :     /*-----------------------------------------------------------------
    1348             :      * Are we using spatial rather than .ID based traversal?
    1349             :      *----------------------------------------------------------------*/
    1350      526817 :     if (bUseSpatialTraversal)
    1351      375057 :         return m_poMAPFile->GetNextFeatureId(static_cast<int>(nPrevId));
    1352             : 
    1353             :     /*-----------------------------------------------------------------
    1354             :      * Should we use an attribute index traversal?
    1355             :      *----------------------------------------------------------------*/
    1356      151760 :     if (m_poAttrQuery != nullptr)
    1357             :     {
    1358        4682 :         if (m_panMatchingFIDs == nullptr)
    1359             :         {
    1360        4680 :             m_iMatchingFID = 0;
    1361        4680 :             m_panMatchingFIDs =
    1362        4680 :                 m_poAttrQuery->EvaluateAgainstIndices(this, nullptr);
    1363             :         }
    1364        4682 :         if (m_panMatchingFIDs != nullptr)
    1365             :         {
    1366           4 :             if (m_panMatchingFIDs[m_iMatchingFID] == OGRNullFID)
    1367           2 :                 return OGRNullFID;
    1368             : 
    1369           2 :             return m_panMatchingFIDs[m_iMatchingFID++] + 1;
    1370             :         }
    1371             :     }
    1372             : 
    1373             :     /*-----------------------------------------------------------------
    1374             :      * Establish what the next logical feature ID should be
    1375             :      *----------------------------------------------------------------*/
    1376      151756 :     int nFeatureId = -1;
    1377             : 
    1378      151756 :     if (nPrevId <= 0 && m_nLastFeatureId > 0)
    1379         980 :         nFeatureId = 1;  // Feature Ids start at 1
    1380      150776 :     else if (nPrevId > 0 && nPrevId < m_nLastFeatureId)
    1381      150511 :         nFeatureId = static_cast<int>(nPrevId) + 1;
    1382             :     else
    1383             :     {
    1384             :         // This was the last feature
    1385         265 :         return OGRNullFID;
    1386             :     }
    1387             : 
    1388             :     /*-----------------------------------------------------------------
    1389             :      * Skip any feature with NONE geometry and a deleted attribute record
    1390             :      *----------------------------------------------------------------*/
    1391      152495 :     while (nFeatureId <= m_nLastFeatureId)
    1392             :     {
    1393      304990 :         if (m_poMAPFile->MoveToObjId(nFeatureId) != 0 ||
    1394      152495 :             m_poDATFile->GetRecordBlock(nFeatureId) == nullptr)
    1395             :         {
    1396           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1397             :                      "GetNextFeatureId() failed: unable to set read pointer "
    1398             :                      "to feature id %d",
    1399             :                      nFeatureId);
    1400           0 :             return -1;
    1401             :         }
    1402             : 
    1403             :         // __TODO__ Add a test here to check if object is deleted,
    1404             :         // i.e. 0x40 set on object_id in object block
    1405      153636 :         if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE ||
    1406        1141 :             m_poDATFile->IsCurrentRecordDeleted() == FALSE)
    1407             :         {
    1408             :             // This feature contains at least a geometry or some attributes...
    1409             :             // return its id.
    1410      151491 :             return nFeatureId;
    1411             :         }
    1412             : 
    1413        1004 :         nFeatureId++;
    1414             :     }
    1415             : 
    1416             :     // If we reached this point, then we kept skipping deleted features
    1417             :     // and stopped when EOF was reached.
    1418           0 :     return -1;
    1419             : }
    1420             : 
    1421             : /**********************************************************************
    1422             :  *                   TABFile::GetNextFeatureId_Spatial()
    1423             :  *
    1424             :  * Returns feature id that follows nPrevId, or -1 if it is the
    1425             :  * last feature id, but by traversing the spatial tree instead of the
    1426             :  * direct object index.  Generally speaking the feature id's will be
    1427             :  * returned in an unordered fashion.
    1428             :  **********************************************************************/
    1429           0 : int TABFile::GetNextFeatureId_Spatial(int nPrevId)
    1430             : {
    1431           0 :     if (m_eAccessMode != TABRead)
    1432             :     {
    1433           0 :         CPLError(
    1434             :             CE_Failure, CPLE_NotSupported,
    1435             :             "GetNextFeatureId_Spatial() can be used only with Read access.");
    1436           0 :         return -1;
    1437             :     }
    1438             : 
    1439           0 :     if (m_poMAPFile == nullptr)
    1440             :     {
    1441           0 :         CPLError(
    1442             :             CE_Failure, CPLE_NotSupported,
    1443             :             "GetNextFeatureId_Spatial() requires availability of .MAP file.");
    1444           0 :         return -1;
    1445             :     }
    1446             : 
    1447           0 :     return m_poMAPFile->GetNextFeatureId(nPrevId);
    1448             : }
    1449             : 
    1450             : /**********************************************************************
    1451             :  *                   TABFile::GetFeatureRef()
    1452             :  *
    1453             :  * Fill and return a TABFeature object for the specified feature id.
    1454             :  *
    1455             :  * The returned pointer is a reference to an object owned and maintained
    1456             :  * by this TABFile object.  It should not be altered or freed by the
    1457             :  * caller and its contents is guaranteed to be valid only until the next
    1458             :  * call to GetFeatureRef() or Close().
    1459             :  *
    1460             :  * Returns NULL if the specified feature id does not exist of if an
    1461             :  * error happened.  In any case, CPLError() will have been called to
    1462             :  * report the reason of the failure.
    1463             :  *
    1464             :  * If an unsupported object type is encountered (likely from a newer version
    1465             :  * of MapInfo) then a valid feature will be returned with a NONE geometry,
    1466             :  * and a warning will be produced with code TAB_WarningFeatureTypeNotSupported
    1467             :  * CPLGetLastErrorNo() should be used to detect that case.
    1468             :  **********************************************************************/
    1469      516691 : TABFeature *TABFile::GetFeatureRef(GIntBig nFeatureId)
    1470             : {
    1471      516691 :     CPLErrorReset();
    1472             : 
    1473             :     /*-----------------------------------------------------------------
    1474             :      * Make sure file is opened and Validate feature id by positioning
    1475             :      * the read pointers for the .MAP and .DAT files to this feature id.
    1476             :      *----------------------------------------------------------------*/
    1477      516691 :     if (m_poMAPFile == nullptr)
    1478             :     {
    1479           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1480             :                  "GetFeatureRef() failed: file is not opened!");
    1481           0 :         return nullptr;
    1482             :     }
    1483             : 
    1484      516691 :     if (m_bLastOpWasWrite)
    1485           3 :         ResetReading();
    1486      516691 :     m_bLastOpWasRead = TRUE;
    1487             : 
    1488      516688 :     if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId ||
    1489     1550060 :         m_poMAPFile->MoveToObjId(static_cast<int>(nFeatureId)) != 0 ||
    1490      516680 :         m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == nullptr)
    1491             :     {
    1492             :         //     CPLError(CE_Failure, CPLE_IllegalArg,
    1493             :         //    "GetFeatureRef() failed: invalid feature id %d",
    1494             :         //    nFeatureId);
    1495          11 :         return nullptr;
    1496             :     }
    1497             : 
    1498      516680 :     if (m_poDATFile->IsCurrentRecordDeleted())
    1499             :     {
    1500           2 :         if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE)
    1501             :         {
    1502           0 :             CPLError(
    1503             :                 CE_Failure, CPLE_AppDefined,
    1504             :                 "Valid .MAP record " CPL_FRMT_GIB
    1505             :                 " found, but .DAT is marked as deleted. File likely corrupt",
    1506             :                 nFeatureId);
    1507             :         }
    1508           2 :         return nullptr;
    1509             :     }
    1510             : 
    1511             :     /*-----------------------------------------------------------------
    1512             :      * Flush current feature object
    1513             :      * __TODO__ try to reuse if it is already of the right type
    1514             :      *----------------------------------------------------------------*/
    1515      516678 :     if (m_poCurFeature)
    1516             :     {
    1517      311610 :         delete m_poCurFeature;
    1518      311610 :         m_poCurFeature = nullptr;
    1519             :     }
    1520             : 
    1521             :     /*-----------------------------------------------------------------
    1522             :      * Create new feature object of the right type
    1523             :      * Unsupported object types are returned as raw TABFeature (i.e. NONE
    1524             :      * geometry)
    1525             :      *----------------------------------------------------------------*/
    1526     1550030 :     m_poCurFeature = TABFeature::CreateFromMapInfoType(
    1527      516678 :         m_poMAPFile->GetCurObjType(), m_poDefn);
    1528             : 
    1529             :     /*-----------------------------------------------------------------
    1530             :      * Read fields from the .DAT file
    1531             :      * GetRecordBlock() has already been called above...
    1532             :      *----------------------------------------------------------------*/
    1533      516678 :     if (m_poCurFeature->ReadRecordFromDATFile(m_poDATFile) != 0)
    1534             :     {
    1535           0 :         delete m_poCurFeature;
    1536           0 :         m_poCurFeature = nullptr;
    1537           0 :         return nullptr;
    1538             :     }
    1539             : 
    1540             :     /*-----------------------------------------------------------------
    1541             :      * Read geometry from the .MAP file
    1542             :      * MoveToObjId() has already been called above...
    1543             :      *----------------------------------------------------------------*/
    1544      516678 :     TABMAPObjHdr *poObjHdr = TABMAPObjHdr::NewObj(m_poMAPFile->GetCurObjType(),
    1545      516678 :                                                   m_poMAPFile->GetCurObjId());
    1546             :     // Note that poObjHdr==NULL is a valid case if geometry type is NONE
    1547             : 
    1548     1033360 :     if ((poObjHdr && poObjHdr->ReadObj(m_poMAPFile->GetCurObjBlock()) != 0) ||
    1549      516678 :         m_poCurFeature->ReadGeometryFromMAPFile(m_poMAPFile, poObjHdr) != 0)
    1550             :     {
    1551           0 :         delete m_poCurFeature;
    1552           0 :         m_poCurFeature = nullptr;
    1553           0 :         if (poObjHdr)
    1554           0 :             delete poObjHdr;
    1555           0 :         return nullptr;
    1556             :     }
    1557      516678 :     if (poObjHdr)  // May be NULL if feature geometry type is NONE
    1558      516678 :         delete poObjHdr;
    1559             : 
    1560      516678 :     m_nCurFeatureId = nFeatureId;
    1561      516678 :     m_poCurFeature->SetFID(m_nCurFeatureId);
    1562             : 
    1563      516678 :     m_poCurFeature->SetRecordDeleted(m_poDATFile->IsCurrentRecordDeleted());
    1564             : 
    1565      516678 :     return m_poCurFeature;
    1566             : }
    1567             : 
    1568             : /**********************************************************************
    1569             :  *                   TABFile::DeleteFeature()
    1570             :  *
    1571             :  * Standard OGR DeleteFeature implementation.
    1572             :  **********************************************************************/
    1573         723 : OGRErr TABFile::DeleteFeature(GIntBig nFeatureId)
    1574             : {
    1575         723 :     CPLErrorReset();
    1576             : 
    1577         723 :     if (m_eAccessMode == TABRead)
    1578             :     {
    1579           4 :         CPLError(CE_Failure, CPLE_NotSupported,
    1580             :                  "DeleteFeature() cannot be used in read-only access.");
    1581           4 :         return OGRERR_FAILURE;
    1582             :     }
    1583             : 
    1584             :     /*-----------------------------------------------------------------
    1585             :      * Make sure file is opened and establish new feature id.
    1586             :      *----------------------------------------------------------------*/
    1587         719 :     if (m_poMAPFile == nullptr)
    1588             :     {
    1589           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1590             :                  "DeleteFeature() failed: file is not opened!");
    1591           0 :         return OGRERR_FAILURE;
    1592             :     }
    1593             : 
    1594         719 :     if (m_bLastOpWasWrite)
    1595           4 :         ResetReading();
    1596             : 
    1597         718 :     if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId ||
    1598        2153 :         m_poMAPFile->MoveToObjId(static_cast<int>(nFeatureId)) != 0 ||
    1599         716 :         m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == nullptr)
    1600             :     {
    1601             :         /*CPLError(CE_Failure, CPLE_IllegalArg,
    1602             :                  "DeleteFeature() failed: invalid feature id " CPL_FRMT_GIB,
    1603             :                  nFeatureId);*/
    1604           3 :         return OGRERR_NON_EXISTING_FEATURE;
    1605             :     }
    1606             : 
    1607         716 :     if (m_poDATFile->IsCurrentRecordDeleted())
    1608             :     {
    1609             :         /*CPLError(CE_Failure, CPLE_IllegalArg,
    1610             :                  "DeleteFeature() failed: record is already deleted!");*/
    1611           1 :         return OGRERR_NON_EXISTING_FEATURE;
    1612             :     }
    1613             : 
    1614         715 :     if (m_poCurFeature)
    1615             :     {
    1616           0 :         delete m_poCurFeature;
    1617           0 :         m_poCurFeature = nullptr;
    1618             :     }
    1619             : 
    1620         715 :     if (m_poMAPFile->MarkAsDeleted() != 0 || m_poDATFile->MarkAsDeleted() != 0)
    1621             :     {
    1622           0 :         return OGRERR_FAILURE;
    1623             :     }
    1624             : 
    1625         715 :     return OGRERR_NONE;
    1626             : }
    1627             : 
    1628             : /**********************************************************************
    1629             :  *                   TABFile::WriteFeature()
    1630             :  *
    1631             :  * Write a feature to this dataset.
    1632             :  *
    1633             :  * Returns 0 on success, or -1 if an error happened in which case,
    1634             :  * CPLError() will have been called to
    1635             :  * report the reason of the failure.
    1636             :  **********************************************************************/
    1637       15075 : int TABFile::WriteFeature(TABFeature *poFeature)
    1638             : {
    1639             : 
    1640       15075 :     m_bLastOpWasWrite = TRUE;
    1641             : 
    1642             :     /*-----------------------------------------------------------------
    1643             :      * Make sure file is opened and establish new feature id.
    1644             :      *----------------------------------------------------------------*/
    1645       15075 :     if (m_poMAPFile == nullptr)
    1646             :     {
    1647           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1648             :                  "WriteFeature() failed: file is not opened!");
    1649           0 :         return -1;
    1650             :     }
    1651             : 
    1652       15075 :     int nFeatureId = 0;
    1653       15075 :     if (poFeature->GetFID() >= 0)
    1654             :     {
    1655         211 :         nFeatureId = static_cast<int>(poFeature->GetFID());
    1656             :     }
    1657       14864 :     else if (m_nLastFeatureId < 1)
    1658             :     {
    1659             :         /*-------------------------------------------------------------
    1660             :          * Special hack to write out at least one field if none are in
    1661             :          * OGRFeatureDefn.
    1662             :          *------------------------------------------------------------*/
    1663          97 :         if (m_poDATFile->GetNumFields() == 0)
    1664             :         {
    1665           1 :             CPLError(CE_Warning, CPLE_IllegalArg,
    1666             :                      "MapInfo tables must contain at least 1 column, adding "
    1667             :                      "dummy FID column.");
    1668           1 :             CPLErrorReset();
    1669           1 :             m_poDATFile->AddField("FID", TABFInteger, 10, 0);
    1670             :         }
    1671             : 
    1672          97 :         nFeatureId = 1;
    1673             :     }
    1674             :     else
    1675             :     {
    1676       14767 :         nFeatureId = m_nLastFeatureId + 1;
    1677             :     }
    1678             : 
    1679       15075 :     poFeature->SetFID(nFeatureId);
    1680             : 
    1681             :     /*-----------------------------------------------------------------
    1682             :      * Write fields to the .DAT file and update .IND if necessary
    1683             :      *----------------------------------------------------------------*/
    1684       30150 :     if (m_poDATFile->GetRecordBlock(nFeatureId) == nullptr ||
    1685       15075 :         poFeature->WriteRecordToDATFile(m_poDATFile, m_poINDFile,
    1686       15075 :                                         m_panIndexNo) != 0)
    1687             :     {
    1688           1 :         CPLError(CE_Failure, CPLE_FileIO,
    1689             :                  "Failed writing attributes for feature id %d in %s",
    1690             :                  nFeatureId, m_pszFname);
    1691           1 :         return -1;
    1692             :     }
    1693             : 
    1694             :     /*-----------------------------------------------------------------
    1695             :      * Write geometry to the .MAP file
    1696             :      * The call to PrepareNewObj() takes care of the .ID file.
    1697             :      *----------------------------------------------------------------*/
    1698             :     std::unique_ptr<TABMAPObjHdr> poObjHdr(TABMAPObjHdr::NewObj(
    1699       30148 :         poFeature->ValidateMapInfoType(m_poMAPFile), nFeatureId));
    1700             : 
    1701       15074 :     if (poObjHdr == nullptr)
    1702             :     {
    1703           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1704             :                  "Failed writing geometry for feature id %d in %s", nFeatureId,
    1705             :                  m_pszFname);
    1706           0 :         return -1;
    1707             :     }
    1708             : 
    1709             :     /*-----------------------------------------------------------------
    1710             :      * ValidateMapInfoType() may have returned TAB_GEOM_NONE if feature
    1711             :      * contained an invalid geometry for its class. Need to catch that
    1712             :      * case and return the error.
    1713             :      *----------------------------------------------------------------*/
    1714       15131 :     if (poObjHdr->m_nType == TAB_GEOM_NONE &&
    1715          57 :         poFeature->GetFeatureClass() != TABFCNoGeomFeature)
    1716             :     {
    1717           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1718             :                  "Invalid geometry for feature id %d in %s", nFeatureId,
    1719             :                  m_pszFname);
    1720           0 :         return -1;
    1721             :     }
    1722             : 
    1723             :     /*-----------------------------------------------------------------
    1724             :      * The ValidateMapInfoType() call above has forced calculation of the
    1725             :      * feature's IntMBR. Store that value in the ObjHdr for use by
    1726             :      * PrepareNewObj() to search the best node to insert the feature.
    1727             :      *----------------------------------------------------------------*/
    1728       15074 :     if (poObjHdr->m_nType != TAB_GEOM_NONE)
    1729             :     {
    1730       15017 :         poFeature->GetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY,
    1731       15017 :                              poObjHdr->m_nMaxX, poObjHdr->m_nMaxY);
    1732             :     }
    1733             : 
    1734             :     /*
    1735             :         if( m_nCurFeatureId < m_nLastFeatureId )
    1736             :         {
    1737             :             delete GetFeatureRef(m_nLastFeatureId);
    1738             :             m_poCurFeature = NULL;
    1739             :         }*/
    1740             : 
    1741       15074 :     if (m_poMAPFile->PrepareNewObj(poObjHdr.get()) != 0 ||
    1742       30148 :         poFeature->WriteGeometryToMAPFile(m_poMAPFile, poObjHdr.get()) != 0 ||
    1743       15074 :         m_poMAPFile->CommitNewObj(poObjHdr.get()) != 0)
    1744             :     {
    1745           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1746             :                  "Failed writing geometry for feature id %d in %s", nFeatureId,
    1747             :                  m_pszFname);
    1748           0 :         return -1;
    1749             :     }
    1750             : 
    1751       15074 :     m_nLastFeatureId = std::max(m_nLastFeatureId, nFeatureId);
    1752       15074 :     m_nCurFeatureId = nFeatureId;
    1753             : 
    1754       15074 :     return 0;
    1755             : }
    1756             : 
    1757        1420 : int TABFile::SetCharset(const char *pszCharset)
    1758             : {
    1759        1420 :     if (0 != IMapInfoFile::SetCharset(pszCharset))
    1760             :     {
    1761           0 :         return -1;
    1762             :     }
    1763        1420 :     if (m_poDATFile != nullptr)
    1764             :     {
    1765           2 :         m_poDATFile->SetEncoding(CharsetToEncoding(pszCharset));
    1766             :     }
    1767        1420 :     if (m_poMAPFile != nullptr)
    1768             :     {
    1769           2 :         m_poMAPFile->SetEncoding(CharsetToEncoding(pszCharset));
    1770             :     }
    1771        1420 :     return 0;
    1772             : }
    1773             : 
    1774             : /**********************************************************************
    1775             :  *                   TABFile::CreateFeature()
    1776             :  *
    1777             :  * Write a new feature to this dataset. The passed in feature is updated
    1778             :  * with the new feature id.
    1779             :  *
    1780             :  * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
    1781             :  * error happened in which case, CPLError() will have been called to
    1782             :  * report the reason of the failure.
    1783             :  **********************************************************************/
    1784       14865 : OGRErr TABFile::CreateFeature(TABFeature *poFeature)
    1785             : {
    1786       14865 :     CPLErrorReset();
    1787             : 
    1788       14865 :     if (m_eAccessMode == TABRead)
    1789             :     {
    1790           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1791             :                  "CreateFeature() cannot be used in read-only access.");
    1792           0 :         return OGRERR_FAILURE;
    1793             :     }
    1794             : 
    1795       14865 :     GIntBig nFeatureId = poFeature->GetFID();
    1796       14865 :     if (nFeatureId != OGRNullFID)
    1797             :     {
    1798           1 :         if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId)
    1799             :         {
    1800           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1801             :                      "CreateFeature() failed: invalid feature id " CPL_FRMT_GIB,
    1802             :                      nFeatureId);
    1803           0 :             return OGRERR_FAILURE;
    1804             :         }
    1805             : 
    1806           1 :         if (m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) ==
    1807           2 :                 nullptr ||
    1808           1 :             !m_poDATFile->IsCurrentRecordDeleted())
    1809             :         {
    1810           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1811             :                      "CreateFeature() failed: cannot re-write already existing "
    1812             :                      "feature " CPL_FRMT_GIB,
    1813             :                      nFeatureId);
    1814           0 :             return OGRERR_FAILURE;
    1815             :         }
    1816             :     }
    1817             : 
    1818       14865 :     if (WriteFeature(poFeature) < 0)
    1819           1 :         return OGRERR_FAILURE;
    1820             : 
    1821       14864 :     return OGRERR_NONE;
    1822             : }
    1823             : 
    1824             : /**********************************************************************
    1825             :  *                   TABFile::ISetFeature()
    1826             :  *
    1827             :  * Implementation of OGRLayer's SetFeature()
    1828             :  **********************************************************************/
    1829         220 : OGRErr TABFile::ISetFeature(OGRFeature *poFeature)
    1830             : 
    1831             : {
    1832         220 :     CPLErrorReset();
    1833             : 
    1834         220 :     if (m_eAccessMode == TABRead)
    1835             :     {
    1836           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    1837             :                  "SetFeature() cannot be used in read-only access.");
    1838           2 :         return OGRERR_FAILURE;
    1839             :     }
    1840             : 
    1841             :     /*-----------------------------------------------------------------
    1842             :      * Make sure file is opened.
    1843             :      *----------------------------------------------------------------*/
    1844         218 :     if (m_poMAPFile == nullptr)
    1845             :     {
    1846           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1847             :                  "SetFeature() failed: file is not opened!");
    1848           0 :         return OGRERR_FAILURE;
    1849             :     }
    1850             : 
    1851         218 :     GIntBig nFeatureId = poFeature->GetFID();
    1852         218 :     if (nFeatureId == OGRNullFID)
    1853             :     {
    1854           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1855             :                  "SetFeature() must be used on a feature with a FID.");
    1856           1 :         return OGRERR_FAILURE;
    1857             :     }
    1858         217 :     if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId)
    1859             :     {
    1860             :         /*CPLError(CE_Failure, CPLE_IllegalArg,
    1861             :                     "SetFeature() failed: invalid feature id " CPL_FRMT_GIB,
    1862             :                     nFeatureId);*/
    1863           3 :         return OGRERR_NON_EXISTING_FEATURE;
    1864             :     }
    1865             : 
    1866         214 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
    1867         425 :     if (poGeom != nullptr &&
    1868         211 :         ((wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) ||
    1869         211 :          (wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)))
    1870             :     {
    1871           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1872             :                  "SetFeature() failed: setting MultiPoint or "
    1873             :                  "GeometryCollection not supported");
    1874           0 :         return OGRERR_FAILURE;
    1875             :     }
    1876             : 
    1877         214 :     TABFeature *poTABFeature = CreateTABFeature(poFeature);
    1878         214 :     if (poTABFeature == nullptr)
    1879           0 :         return OGRERR_FAILURE;
    1880             : 
    1881         214 :     if (m_bLastOpWasWrite)
    1882         201 :         ResetReading();
    1883             : 
    1884         214 :     if (m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == nullptr)
    1885             :     {
    1886             :         /*CPLError(CE_Failure, CPLE_IllegalArg,
    1887             :                  "SetFeature() failed: invalid feature id " CPL_FRMT_GIB,
    1888             :                  nFeatureId);*/
    1889           0 :         delete poTABFeature;
    1890           0 :         return OGRERR_NON_EXISTING_FEATURE;
    1891             :     }
    1892             : 
    1893             :     /* If the object is not already deleted, delete it */
    1894         214 :     if (!(m_poDATFile->IsCurrentRecordDeleted()))
    1895             :     {
    1896         113 :         OGRFeature *poOldFeature = GetFeature(nFeatureId);
    1897         113 :         if (poOldFeature != nullptr)
    1898             :         {
    1899             :             /* Optimization: if old and new features are the same, do nothing */
    1900         113 :             if (poOldFeature->Equal(poFeature))
    1901             :             {
    1902           1 :                 CPLDebug("MITAB", "Un-modified object " CPL_FRMT_GIB,
    1903             :                          nFeatureId);
    1904           1 :                 delete poTABFeature;
    1905           1 :                 delete poOldFeature;
    1906           1 :                 return OGRERR_NONE;
    1907             :             }
    1908             : 
    1909             :             /* Optimization: if old and new geometries are the same, just */
    1910             :             /* rewrite the attributes */
    1911         112 :             OGRGeometry *poOldGeom = poOldFeature->GetGeometryRef();
    1912         112 :             OGRGeometry *poNewGeom = poFeature->GetGeometryRef();
    1913         221 :             if ((poOldGeom == nullptr && poNewGeom == nullptr) ||
    1914         109 :                 (poOldGeom != nullptr && poNewGeom != nullptr &&
    1915         108 :                  poOldGeom->Equals(poNewGeom)))
    1916             :             {
    1917           3 :                 const char *pszOldStyle = poOldFeature->GetStyleString();
    1918           3 :                 const char *pszNewStyle = poFeature->GetStyleString();
    1919           3 :                 if ((pszOldStyle == nullptr && pszNewStyle == nullptr) ||
    1920           1 :                     (pszOldStyle != nullptr && pszNewStyle != nullptr &&
    1921           1 :                      EQUAL(pszOldStyle, pszNewStyle)))
    1922             :                 {
    1923           3 :                     CPLDebug("MITAB",
    1924             :                              "Rewrite only attributes for object " CPL_FRMT_GIB,
    1925             :                              nFeatureId);
    1926           6 :                     if (poTABFeature->WriteRecordToDATFile(
    1927           3 :                             m_poDATFile, m_poINDFile, m_panIndexNo) != 0)
    1928             :                     {
    1929           0 :                         CPLError(CE_Failure, CPLE_FileIO,
    1930             :                                  "Failed writing attributes for feature "
    1931             :                                  "id " CPL_FRMT_GIB " in %s",
    1932             :                                  nFeatureId, m_pszFname);
    1933           0 :                         delete poTABFeature;
    1934           0 :                         delete poOldFeature;
    1935           0 :                         return OGRERR_FAILURE;
    1936             :                     }
    1937             : 
    1938           3 :                     delete poTABFeature;
    1939           3 :                     delete poOldFeature;
    1940           3 :                     return OGRERR_NONE;
    1941             :                 }
    1942             :             }
    1943             : 
    1944         109 :             delete poOldFeature;
    1945             :         }
    1946             : 
    1947         109 :         if (DeleteFeature(nFeatureId) != OGRERR_NONE)
    1948             :         {
    1949           0 :             delete poTABFeature;
    1950           0 :             return OGRERR_FAILURE;
    1951             :         }
    1952             :     }
    1953             : 
    1954         210 :     int nStatus = WriteFeature(poTABFeature);
    1955             : 
    1956         210 :     delete poTABFeature;
    1957             : 
    1958         210 :     if (nStatus < 0)
    1959           0 :         return OGRERR_FAILURE;
    1960             : 
    1961         210 :     return OGRERR_NONE;
    1962             : }
    1963             : 
    1964             : /**********************************************************************
    1965             :  *                   TABFile::GetLayerDefn()
    1966             :  *
    1967             :  * Returns a reference to the OGRFeatureDefn that will be used to create
    1968             :  * features in this dataset.
    1969             :  *
    1970             :  * Returns a reference to an object that is maintained by this TABFile
    1971             :  * object (and thus should not be modified or freed by the caller) or
    1972             :  * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
    1973             :  * opened yet)
    1974             :  **********************************************************************/
    1975      250911 : OGRFeatureDefn *TABFile::GetLayerDefn()
    1976             : {
    1977      250911 :     return m_poDefn;
    1978             : }
    1979             : 
    1980             : /**********************************************************************
    1981             :  *                   TABFile::SetFeatureDefn()
    1982             :  *
    1983             :  * Pass a reference to the OGRFeatureDefn that will be used to create
    1984             :  * features in this dataset.  This function should be called after
    1985             :  * creating a new dataset, but before writing the first feature.
    1986             :  * All features that will be written to this dataset must share this same
    1987             :  * OGRFeatureDefn.
    1988             :  *
    1989             :  * A reference to the OGRFeatureDefn will be kept and will be used to
    1990             :  * build the .DAT file, etc.
    1991             :  *
    1992             :  * Returns 0 on success, -1 on error.
    1993             :  **********************************************************************/
    1994           0 : int TABFile::SetFeatureDefn(
    1995             :     OGRFeatureDefn *poFeatureDefn,
    1996             :     TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
    1997             : {
    1998           0 :     if (m_eAccessMode != TABWrite)
    1999             :     {
    2000           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2001             :                  "SetFeatureDefn() can be used only with Write access.");
    2002           0 :         return -1;
    2003             :     }
    2004             : 
    2005             :     /*-----------------------------------------------------------------
    2006             :      * Keep a reference to the OGRFeatureDefn... we'll have to take the
    2007             :      * reference count into account when we are done with it.
    2008             :      *----------------------------------------------------------------*/
    2009           0 :     if (m_poDefn && m_poDefn->Dereference() == 0)
    2010           0 :         delete m_poDefn;
    2011             : 
    2012           0 :     m_poDefn = poFeatureDefn;
    2013           0 :     m_poDefn->Reference();
    2014             : 
    2015             :     /*-----------------------------------------------------------------
    2016             :      * Pass field information to the .DAT file, after making sure that
    2017             :      * it has been created and that it does not contain any field
    2018             :      * definition yet.
    2019             :      *----------------------------------------------------------------*/
    2020           0 :     if (m_poDATFile == nullptr || m_poDATFile->GetNumFields() > 0)
    2021             :     {
    2022           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2023             :                  "SetFeatureDefn() can be called only once in a newly "
    2024             :                  "created dataset.");
    2025           0 :         return -1;
    2026             :     }
    2027             : 
    2028           0 :     const int numFields = poFeatureDefn->GetFieldCount();
    2029           0 :     TABFieldType eMapInfoType = TABFUnknown;
    2030           0 :     int nStatus = 0;
    2031           0 :     for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
    2032             :     {
    2033           0 :         OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField);
    2034             : 
    2035           0 :         if (paeMapInfoNativeFieldTypes)
    2036             :         {
    2037           0 :             eMapInfoType = paeMapInfoNativeFieldTypes[iField];
    2038             :         }
    2039             :         else
    2040             :         {
    2041             :             /*---------------------------------------------------------
    2042             :              * Map OGRFieldTypes to MapInfo native types
    2043             :              *--------------------------------------------------------*/
    2044           0 :             switch (poFieldDefn->GetType())
    2045             :             {
    2046           0 :                 case OFTInteger:
    2047           0 :                     eMapInfoType = TABFInteger;
    2048           0 :                     break;
    2049           0 :                 case OFTReal:
    2050           0 :                     if (poFieldDefn->GetWidth() > 0 ||
    2051           0 :                         poFieldDefn->GetPrecision() > 0)
    2052           0 :                         eMapInfoType = TABFDecimal;
    2053             :                     else
    2054           0 :                         eMapInfoType = TABFFloat;
    2055           0 :                     break;
    2056           0 :                 case OFTDateTime:
    2057           0 :                     eMapInfoType = TABFDateTime;
    2058           0 :                     break;
    2059           0 :                 case OFTDate:
    2060           0 :                     eMapInfoType = TABFDate;
    2061           0 :                     break;
    2062           0 :                 case OFTTime:
    2063           0 :                     eMapInfoType = TABFTime;
    2064           0 :                     break;
    2065           0 :                 case OFTString:
    2066             :                 default:
    2067           0 :                     eMapInfoType = TABFChar;
    2068             :             }
    2069             :         }
    2070             : 
    2071           0 :         nStatus = m_poDATFile->AddField(poFieldDefn->GetNameRef(), eMapInfoType,
    2072             :                                         poFieldDefn->GetWidth(),
    2073             :                                         poFieldDefn->GetPrecision());
    2074             :     }
    2075             : 
    2076             :     /*-----------------------------------------------------------------
    2077             :      * Alloc the array to keep track of indexed fields (default=NOT indexed)
    2078             :      *----------------------------------------------------------------*/
    2079           0 :     m_panIndexNo = static_cast<int *>(CPLCalloc(numFields, sizeof(int)));
    2080             : 
    2081           0 :     return nStatus;
    2082             : }
    2083             : 
    2084             : /**********************************************************************
    2085             :  *                   TABFile::AddFieldNative()
    2086             :  *
    2087             :  * Create a new field using a native mapinfo data type... this is an
    2088             :  * alternative to defining fields through the OGR interface.
    2089             :  * This function should be called after creating a new dataset.
    2090             :  *
    2091             :  * This function will build/update the OGRFeatureDefn that will have to be
    2092             :  * used when writing features to this dataset.
    2093             :  *
    2094             :  * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
    2095             :  *
    2096             :  * Note: The bUnique flag has no effect on TABFiles.  See the TABView class.
    2097             :  *
    2098             :  * Returns 0 on success, -1 on error.
    2099             :  **********************************************************************/
    2100         208 : int TABFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
    2101             :                             int nWidth /*=0*/, int nPrecision /*=0*/,
    2102             :                             GBool bIndexed /*=FALSE*/, GBool /*bUnique=FALSE*/,
    2103             :                             int /*bApproxOK*/)
    2104             : {
    2105         208 :     if (m_eAccessMode == TABRead || m_poDATFile == nullptr)
    2106             :     {
    2107           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2108             :                  "AddFieldNative() cannot be used only with Read access.");
    2109           0 :         return -1;
    2110             :     }
    2111             : 
    2112         208 :     m_bNeedTABRewrite = TRUE;
    2113             : 
    2114             :     /*-----------------------------------------------------------------
    2115             :      * Validate field width... must be <= 254
    2116             :      *----------------------------------------------------------------*/
    2117         208 :     if (nWidth > 254)
    2118             :     {
    2119           0 :         CPLError(CE_Warning, CPLE_IllegalArg,
    2120             :                  "Invalid size (%d) for field '%s'.  "
    2121             :                  "Size must be 254 or less.",
    2122             :                  nWidth, pszName);
    2123           0 :         nWidth = 254;
    2124             :     }
    2125             : 
    2126             :     /*-----------------------------------------------------------------
    2127             :      * Map fields with width=0 (variable length in OGR) to a valid default
    2128             :      *----------------------------------------------------------------*/
    2129         208 :     if (eMapInfoType == TABFDecimal && nWidth == 0)
    2130           0 :         nWidth = 20;
    2131         208 :     else if (nWidth == 0)
    2132           0 :         nWidth = 254; /* char fields */
    2133             : 
    2134         416 :     CPLString osName(NormalizeFieldName(pszName));
    2135             : 
    2136             :     /*-----------------------------------------------------------------
    2137             :      * Map MapInfo native types to OGR types
    2138             :      *----------------------------------------------------------------*/
    2139         208 :     OGRFieldDefn *poFieldDefn = nullptr;
    2140             : 
    2141         208 :     switch (eMapInfoType)
    2142             :     {
    2143          74 :         case TABFChar:
    2144             :             /*-------------------------------------------------
    2145             :              * CHAR type
    2146             :              *------------------------------------------------*/
    2147          74 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTString);
    2148          74 :             poFieldDefn->SetWidth(nWidth);
    2149          74 :             break;
    2150          68 :         case TABFInteger:
    2151             :             /*-------------------------------------------------
    2152             :              * INTEGER type
    2153             :              *------------------------------------------------*/
    2154          68 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
    2155          68 :             if (nWidth <= 10)
    2156           3 :                 poFieldDefn->SetWidth(nWidth);
    2157          68 :             break;
    2158           0 :         case TABFSmallInt:
    2159             :             /*-------------------------------------------------
    2160             :              * SMALLINT type
    2161             :              *------------------------------------------------*/
    2162           0 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
    2163           0 :             if (nWidth <= 5)
    2164           0 :                 poFieldDefn->SetWidth(nWidth);
    2165           0 :             break;
    2166           1 :         case TABFLargeInt:
    2167             :             /*-------------------------------------------------
    2168             :              * SMALLINT type
    2169             :              *------------------------------------------------*/
    2170           1 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger64);
    2171           1 :             break;
    2172           3 :         case TABFDecimal:
    2173             :             /*-------------------------------------------------
    2174             :              * DECIMAL type
    2175             :              *------------------------------------------------*/
    2176           3 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal);
    2177           3 :             poFieldDefn->SetWidth(nWidth);
    2178           3 :             poFieldDefn->SetPrecision(nPrecision);
    2179           3 :             break;
    2180          24 :         case TABFFloat:
    2181             :             /*-------------------------------------------------
    2182             :              * FLOAT type
    2183             :              *------------------------------------------------*/
    2184          24 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal);
    2185          24 :             break;
    2186          18 :         case TABFDate:
    2187             :             /*-------------------------------------------------
    2188             :              * DATE type (V450, returned as a string: "DD/MM/YYYY")
    2189             :              *------------------------------------------------*/
    2190          18 :             poFieldDefn = new OGRFieldDefn(osName.c_str(),
    2191             : #ifdef MITAB_USE_OFTDATETIME
    2192          18 :                                            OFTDate);
    2193             : #else
    2194             :                                            OFTString);
    2195             : #endif
    2196          18 :             poFieldDefn->SetWidth(10);
    2197          18 :             m_nVersion = std::max(m_nVersion, 450);
    2198          18 :             break;
    2199           2 :         case TABFTime:
    2200             :             /*-------------------------------------------------
    2201             :              * TIME type (V900, returned as a string: "HH:MM:SS")
    2202             :              *------------------------------------------------*/
    2203           2 :             poFieldDefn = new OGRFieldDefn(osName.c_str(),
    2204             : #ifdef MITAB_USE_OFTDATETIME
    2205           2 :                                            OFTTime);
    2206             : #else
    2207             :                                            OFTString);
    2208             : #endif
    2209           2 :             poFieldDefn->SetWidth(8);
    2210           2 :             m_nVersion = std::max(m_nVersion, 900);
    2211           2 :             break;
    2212          18 :         case TABFDateTime:
    2213             :             /*-------------------------------------------------
    2214             :              * DATETIME type (V900, returned as a string: "DD/MM/YYYY HH:MM:SS")
    2215             :              *------------------------------------------------*/
    2216          18 :             poFieldDefn = new OGRFieldDefn(osName.c_str(),
    2217             : #ifdef MITAB_USE_OFTDATETIME
    2218          18 :                                            OFTDateTime);
    2219             : #else
    2220             :                                            OFTString);
    2221             : #endif
    2222          18 :             poFieldDefn->SetWidth(19);
    2223          18 :             m_nVersion = std::max(m_nVersion, 900);
    2224          18 :             break;
    2225           0 :         case TABFLogical:
    2226             :             /*-------------------------------------------------
    2227             :              * LOGICAL type (value "T" or "F")
    2228             :              *------------------------------------------------*/
    2229           0 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTString);
    2230           0 :             poFieldDefn->SetWidth(1);
    2231           0 :             break;
    2232           0 :         default:
    2233           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2234             :                      "Unsupported type for field %s", osName.c_str());
    2235           0 :             return -1;
    2236             :     }
    2237             : 
    2238             :     /*-----------------------------------------------------
    2239             :      * Add the FieldDefn to the FeatureDefn
    2240             :      *----------------------------------------------------*/
    2241         208 :     whileUnsealing(m_poDefn)->AddFieldDefn(poFieldDefn);
    2242         208 :     m_oSetFields.insert(CPLString(poFieldDefn->GetNameRef()).toupper());
    2243         208 :     delete poFieldDefn;
    2244             : 
    2245             :     /*-----------------------------------------------------
    2246             :      * ... and pass field info to the .DAT file.
    2247             :      *----------------------------------------------------*/
    2248             :     int nStatus =
    2249         208 :         m_poDATFile->AddField(osName.c_str(), eMapInfoType, nWidth, nPrecision);
    2250             : 
    2251             :     /*-----------------------------------------------------------------
    2252             :      * Extend the array to keep track of indexed fields (default=NOT indexed)
    2253             :      *----------------------------------------------------------------*/
    2254         208 :     m_panIndexNo = static_cast<int *>(
    2255         208 :         CPLRealloc(m_panIndexNo, m_poDefn->GetFieldCount() * sizeof(int)));
    2256         208 :     m_panIndexNo[m_poDefn->GetFieldCount() - 1] = 0;
    2257             : 
    2258             :     /*-----------------------------------------------------------------
    2259             :      * Index the field if requested
    2260             :      *----------------------------------------------------------------*/
    2261         208 :     if (nStatus == 0 && bIndexed)
    2262           0 :         nStatus = SetFieldIndexed(m_poDefn->GetFieldCount() - 1);
    2263             : 
    2264         208 :     if (nStatus == 0 && m_eAccessMode == TABReadWrite)
    2265           2 :         nStatus = WriteTABFile();
    2266             : 
    2267         208 :     return nStatus;
    2268             : }
    2269             : 
    2270             : /**********************************************************************
    2271             :  *                   TABFile::GetNativeFieldType()
    2272             :  *
    2273             :  * Returns the native MapInfo field type for the specified field.
    2274             :  *
    2275             :  * Returns TABFUnknown if file is not opened, or if specified field index is
    2276             :  * invalid.
    2277             :  *
    2278             :  * Note that field ids are positive and start at 0.
    2279             :  **********************************************************************/
    2280         292 : TABFieldType TABFile::GetNativeFieldType(int nFieldId)
    2281             : {
    2282         292 :     if (m_poDATFile)
    2283             :     {
    2284         292 :         return m_poDATFile->GetFieldType(nFieldId);
    2285             :     }
    2286           0 :     return TABFUnknown;
    2287             : }
    2288             : 
    2289             : /**********************************************************************
    2290             :  *                   TABFile::GetFieldIndexNumber()
    2291             :  *
    2292             :  * Returns the field's index number that was specified in the .TAB header
    2293             :  * or 0 if the specified field is not indexed.
    2294             :  *
    2295             :  * Note that field ids are positive and start at 0
    2296             :  * and valid index ids are positive and start at 1.
    2297             :  **********************************************************************/
    2298        1792 : int TABFile::GetFieldIndexNumber(int nFieldId)
    2299             : {
    2300        3584 :     if (m_panIndexNo == nullptr || nFieldId < 0 || m_poDATFile == nullptr ||
    2301        1792 :         nFieldId >= m_poDefn->GetFieldCount())
    2302           0 :         return 0;  // no index
    2303             : 
    2304        1792 :     return m_panIndexNo[nFieldId];
    2305             : }
    2306             : 
    2307             : /************************************************************************
    2308             :  *                       TABFile::SetFieldIndexed()
    2309             :  *
    2310             :  * Request that a field be indexed.  This will create the .IND file if
    2311             :  * necessary, etc.
    2312             :  *
    2313             :  * Note that field ids are positive and start at 0.
    2314             :  *
    2315             :  * Returns 0 on success, -1 on error.
    2316             :  ************************************************************************/
    2317           3 : int TABFile::SetFieldIndexed(int nFieldId)
    2318             : {
    2319             :     /*-----------------------------------------------------------------
    2320             :      * Make sure things are OK
    2321             :      *----------------------------------------------------------------*/
    2322           3 :     if (m_pszFname == nullptr || m_eAccessMode != TABWrite ||
    2323           2 :         m_poDefn == nullptr)
    2324             :     {
    2325           1 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2326             :                  "SetFieldIndexed() must be called after opening a new "
    2327             :                  "dataset, but before writing the first feature to it.");
    2328           1 :         return -1;
    2329             :     }
    2330             : 
    2331           4 :     if (m_panIndexNo == nullptr || nFieldId < 0 || m_poDATFile == nullptr ||
    2332           2 :         nFieldId >= m_poDefn->GetFieldCount())
    2333             :     {
    2334           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2335             :                  "Invalid field number in SetFieldIndexed().");
    2336           0 :         return -1;
    2337             :     }
    2338             : 
    2339             :     /*-----------------------------------------------------------------
    2340             :      * If field is already indexed then just return
    2341             :      *----------------------------------------------------------------*/
    2342           2 :     if (m_panIndexNo[nFieldId] != 0)
    2343           1 :         return 0;  // Nothing to do
    2344             : 
    2345             :     /*-----------------------------------------------------------------
    2346             :      * Create .IND file if it is not done yet.
    2347             :      *
    2348             :      * Note: We can pass the .TAB's filename directly and the
    2349             :      * TABINDFile class will automagically adjust the extension.
    2350             :      *----------------------------------------------------------------*/
    2351           1 :     if (m_poINDFile == nullptr)
    2352             :     {
    2353           1 :         m_poINDFile = new TABINDFile;
    2354             : 
    2355           1 :         if (m_poINDFile->Open(m_pszFname, "w", TRUE) != 0)
    2356             :         {
    2357             :             // File could not be opened...
    2358           0 :             delete m_poINDFile;
    2359           0 :             m_poINDFile = nullptr;
    2360           0 :             return -1;
    2361             :         }
    2362             :     }
    2363             : 
    2364             :     /*-----------------------------------------------------------------
    2365             :      * Init new index.
    2366             :      *----------------------------------------------------------------*/
    2367           1 :     OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(nFieldId);
    2368             : 
    2369           1 :     if (poFieldDefn == nullptr)
    2370           0 :         return -1;
    2371           1 :     const int nNewIndexNo = m_poINDFile->CreateIndex(
    2372             :         GetNativeFieldType(nFieldId), poFieldDefn->GetWidth());
    2373           1 :     if (nNewIndexNo < 1)
    2374             :     {
    2375             :         // Failed... an error has already been reported.
    2376           0 :         return -1;
    2377             :     }
    2378             : 
    2379           1 :     m_panIndexNo[nFieldId] = nNewIndexNo;
    2380             : 
    2381           1 :     return 0;
    2382             : }
    2383             : 
    2384             : /************************************************************************
    2385             :  *                       TABFile::IsFieldIndexed()
    2386             :  *
    2387             :  * Returns TRUE if field is indexed, or FALSE otherwise.
    2388             :  ************************************************************************/
    2389           0 : GBool TABFile::IsFieldIndexed(int nFieldId)
    2390             : {
    2391           0 :     return GetFieldIndexNumber(nFieldId) > 0 ? TRUE : FALSE;
    2392             : }
    2393             : 
    2394             : /**********************************************************************
    2395             :  *                   TABFile::GetINDFileRef()
    2396             :  *
    2397             :  * Opens the .IND file for this dataset and returns a reference to
    2398             :  * the handle.
    2399             :  * If the .IND file has already been opened then the same handle is
    2400             :  * returned directly.
    2401             :  * If the .IND file does not exist then the function silently returns NULL.
    2402             :  *
    2403             :  * Note that the returned TABINDFile handle is only a reference to an
    2404             :  * object that is owned by this class.  Callers can use it but cannot
    2405             :  * destroy the object.  The object will remain valid for as long as
    2406             :  * the TABFile will remain open.
    2407             :  **********************************************************************/
    2408           2 : TABINDFile *TABFile::GetINDFileRef()
    2409             : {
    2410           2 :     if (m_pszFname == nullptr)
    2411           0 :         return nullptr;
    2412             : 
    2413           2 :     if (m_eAccessMode == TABRead && m_poINDFile == nullptr)
    2414             :     {
    2415             :         /*-------------------------------------------------------------
    2416             :          * File is not opened yet... do it now.
    2417             :          *
    2418             :          * Note: We can pass the .TAB's filename directly and the
    2419             :          * TABINDFile class will automagically adjust the extension.
    2420             :          *------------------------------------------------------------*/
    2421           2 :         m_poINDFile = new TABINDFile;
    2422             : 
    2423           2 :         if (m_poINDFile->Open(m_pszFname, "r", TRUE) != 0)
    2424             :         {
    2425             :             // File could not be opened... probably does not exist
    2426           0 :             delete m_poINDFile;
    2427           0 :             m_poINDFile = nullptr;
    2428             :         }
    2429           2 :         else if (m_panIndexNo && m_poDATFile)
    2430             :         {
    2431             :             /*---------------------------------------------------------
    2432             :              * Pass type information for each indexed field.
    2433             :              *--------------------------------------------------------*/
    2434           6 :             for (int i = 0; i < m_poDefn->GetFieldCount(); i++)
    2435             :             {
    2436           4 :                 if (m_panIndexNo[i] > 0)
    2437             :                 {
    2438           2 :                     m_poINDFile->SetIndexFieldType(m_panIndexNo[i],
    2439             :                                                    GetNativeFieldType(i));
    2440             :                 }
    2441             :             }
    2442             :         }
    2443             :     }
    2444             : 
    2445           2 :     return m_poINDFile;
    2446             : }
    2447             : 
    2448             : /**********************************************************************
    2449             :  *                   TABFile::SetBounds()
    2450             :  *
    2451             :  * Set projection coordinates bounds of the newly created dataset.
    2452             :  *
    2453             :  * This function must be called after creating a new dataset and before any
    2454             :  * feature can be written to it.
    2455             :  *
    2456             :  * Returns 0 on success, -1 on error.
    2457             :  **********************************************************************/
    2458         116 : int TABFile::SetBounds(double dXMin, double dYMin, double dXMax, double dYMax)
    2459             : {
    2460         116 :     if (m_eAccessMode != TABWrite)
    2461             :     {
    2462           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2463             :                  "SetBounds() can be used only with Write access.");
    2464           0 :         return -1;
    2465             :     }
    2466             : 
    2467             :     /*-----------------------------------------------------------------
    2468             :      * Check that dataset has been created but no feature set yet.
    2469             :      *----------------------------------------------------------------*/
    2470         116 :     if (m_poMAPFile && m_nLastFeatureId < 1)
    2471             :     {
    2472         116 :         m_poMAPFile->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax);
    2473             : 
    2474         116 :         m_bBoundsSet = TRUE;
    2475             :     }
    2476             :     else
    2477             :     {
    2478           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2479             :                  "SetBounds() can be called only after dataset has been "
    2480             :                  "created and before any feature is set.");
    2481           0 :         return -1;
    2482             :     }
    2483             : 
    2484         116 :     return 0;
    2485             : }
    2486             : 
    2487             : /**********************************************************************
    2488             :  *                   TABFile::GetBounds()
    2489             :  *
    2490             :  * Fetch projection coordinates bounds of a dataset.
    2491             :  *
    2492             :  * The bForce flag has no effect on TAB files since the bounds are
    2493             :  * always in the header.
    2494             :  *
    2495             :  * Returns 0 on success, -1 on error.
    2496             :  **********************************************************************/
    2497           0 : int TABFile::GetBounds(double &dXMin, double &dYMin, double &dXMax,
    2498             :                        double &dYMax, GBool /*bForce = TRUE*/)
    2499             : {
    2500           0 :     if (m_poMAPFile)
    2501             :     {
    2502           0 :         TABMAPHeaderBlock *poHeader = m_poMAPFile->GetHeaderBlock();
    2503           0 :         if (poHeader != nullptr)
    2504             :         {
    2505             :             /*-------------------------------------------------------------
    2506             :              * Projection bounds correspond to the +/- 1e9 integer coord. limits
    2507             :              *------------------------------------------------------------*/
    2508           0 :             double dX0 = 0.0;
    2509           0 :             double dX1 = 0.0;
    2510           0 :             double dY0 = 0.0;
    2511           0 :             double dY1 = 0.0;
    2512           0 :             m_poMAPFile->Int2Coordsys(-1000000000, -1000000000, dX0, dY0);
    2513           0 :             m_poMAPFile->Int2Coordsys(1000000000, 1000000000, dX1, dY1);
    2514             :             /*-------------------------------------------------------------
    2515             :              * ... and make sure that Min < Max
    2516             :              *------------------------------------------------------------*/
    2517           0 :             dXMin = std::min(dX0, dX1);
    2518           0 :             dXMax = std::max(dX0, dX1);
    2519           0 :             dYMin = std::min(dY0, dY1);
    2520           0 :             dYMax = std::max(dY0, dY1);
    2521           0 :             return 0;
    2522             :         }
    2523             :     }
    2524             : 
    2525           0 :     CPLError(CE_Failure, CPLE_AppDefined,
    2526             :              "GetBounds() can be called only after dataset has been opened.");
    2527           0 :     return -1;
    2528             : }
    2529             : 
    2530             : /**********************************************************************
    2531             :  *                   TABFile::GetExtent()
    2532             :  *
    2533             :  * Fetch extent of the data currently stored in the dataset.
    2534             :  *
    2535             :  * The bForce flag has no effect on TAB files since that value is
    2536             :  * always in the header.
    2537             :  *
    2538             :  * Returns OGRERR_NONE/OGRRERR_FAILURE.
    2539             :  **********************************************************************/
    2540          15 : OGRErr TABFile::GetExtent(OGREnvelope *psExtent, CPL_UNUSED int bForce)
    2541             : {
    2542          15 :     TABMAPHeaderBlock *poHeader = nullptr;
    2543             : 
    2544          30 :     if (m_poMAPFile && (poHeader = m_poMAPFile->GetHeaderBlock()) != nullptr &&
    2545          15 :         GetGeomType() != wkbNone)
    2546             :     {
    2547          14 :         double dX0 = 0.0;
    2548          14 :         double dX1 = 0.0;
    2549          14 :         double dY0 = 0.0;
    2550          14 :         double dY1 = 0.0;
    2551             :         /*-------------------------------------------------------------
    2552             :          * Fetch extent of the data from the .map header block
    2553             :          * this value is different from the projection bounds.
    2554             :          *------------------------------------------------------------*/
    2555          14 :         m_poMAPFile->Int2Coordsys(poHeader->m_nXMin, poHeader->m_nYMin, dX0,
    2556             :                                   dY0);
    2557          14 :         m_poMAPFile->Int2Coordsys(poHeader->m_nXMax, poHeader->m_nYMax, dX1,
    2558             :                                   dY1);
    2559             : 
    2560             :         /*-------------------------------------------------------------
    2561             :          * ... and make sure that Min < Max
    2562             :          *------------------------------------------------------------*/
    2563          14 :         psExtent->MinX = std::min(dX0, dX1);
    2564          14 :         psExtent->MaxX = std::max(dX0, dX1);
    2565          14 :         psExtent->MinY = std::min(dY0, dY1);
    2566          14 :         psExtent->MaxY = std::max(dY0, dY1);
    2567             : 
    2568          14 :         return OGRERR_NONE;
    2569             :     }
    2570             : 
    2571           1 :     return OGRERR_FAILURE;
    2572             : }
    2573             : 
    2574             : /**********************************************************************
    2575             :  *                   TABFile::GetFeatureCountByType()
    2576             :  *
    2577             :  * Return number of features of each type.
    2578             :  *
    2579             :  * Note that the sum of the 4 returned values may be different from
    2580             :  * the total number of features since features with NONE geometry
    2581             :  * are not taken into account here.
    2582             :  *
    2583             :  * Note: the bForce flag has nmo effect on .TAB files since the info
    2584             :  * is always in the header.
    2585             :  *
    2586             :  * Returns 0 on success, or silently returns -1 (with no error) if this
    2587             :  * information is not available.
    2588             :  **********************************************************************/
    2589        1285 : int TABFile::GetFeatureCountByType(int &numPoints, int &numLines,
    2590             :                                    int &numRegions, int &numTexts,
    2591             :                                    GBool /* bForce = TRUE*/)
    2592             : {
    2593        1285 :     TABMAPHeaderBlock *poHeader = nullptr;
    2594             : 
    2595        1285 :     if (m_poMAPFile && (poHeader = m_poMAPFile->GetHeaderBlock()) != nullptr)
    2596             :     {
    2597        1285 :         numPoints = poHeader->m_numPointObjects;
    2598        1285 :         numLines = poHeader->m_numLineObjects;
    2599        1285 :         numRegions = poHeader->m_numRegionObjects;
    2600        1285 :         numTexts = poHeader->m_numTextObjects;
    2601             :     }
    2602             :     else
    2603             :     {
    2604           0 :         numPoints = numLines = numRegions = numTexts = 0;
    2605           0 :         return -1;
    2606             :     }
    2607             : 
    2608        1285 :     return 0;
    2609             : }
    2610             : 
    2611             : /**********************************************************************
    2612             :  *                   TABFile::SetMIFCoordSys()
    2613             :  *
    2614             :  * Set projection for a new file using a MIF coordsys string.
    2615             :  *
    2616             :  * This function must be called after creating a new dataset and before any
    2617             :  * feature can be written to it.
    2618             :  *
    2619             :  * Returns 0 on success, -1 on error.
    2620             :  **********************************************************************/
    2621           0 : int TABFile::SetMIFCoordSys(const char *pszMIFCoordSys)
    2622             : {
    2623           0 :     if (m_eAccessMode != TABWrite)
    2624             :     {
    2625           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2626             :                  "SetMIFCoordSys() can be used only with Write access.");
    2627           0 :         return -1;
    2628             :     }
    2629             : 
    2630             :     /*-----------------------------------------------------------------
    2631             :      * Check that dataset has been created but no feature set yet.
    2632             :      *----------------------------------------------------------------*/
    2633           0 :     if (m_poMAPFile && m_nLastFeatureId < 1)
    2634             :     {
    2635             :         OGRSpatialReference *poSpatialRef =
    2636           0 :             MITABCoordSys2SpatialRef(pszMIFCoordSys);
    2637             : 
    2638           0 :         if (poSpatialRef)
    2639             :         {
    2640           0 :             double dXMin = 0.0;
    2641           0 :             double dYMin = 0.0;
    2642           0 :             double dXMax = 0.0;
    2643           0 :             double dYMax = 0.0;
    2644           0 :             if (SetSpatialRef(poSpatialRef) == 0)
    2645             :             {
    2646           0 :                 if (MITABExtractCoordSysBounds(pszMIFCoordSys, dXMin, dYMin,
    2647             :                                                dXMax, dYMax))
    2648             :                 {
    2649             :                     // If the coordsys string contains bounds, then use them
    2650           0 :                     if (SetBounds(dXMin, dYMin, dXMax, dYMax) != 0)
    2651             :                     {
    2652             :                         // Failed Setting Bounds... an error should have
    2653             :                         // been already reported.
    2654           0 :                         return -1;
    2655             :                     }
    2656             :                 }
    2657             :             }
    2658             :             else
    2659             :             {
    2660             :                 // Failed setting poSpatialRef... and error should have
    2661             :                 // been reported.
    2662           0 :                 return -1;
    2663             :             }
    2664             : 
    2665             :             // Release our handle on poSpatialRef
    2666           0 :             if (poSpatialRef->Dereference() == 0)
    2667           0 :                 delete poSpatialRef;
    2668           0 :         }
    2669             :     }
    2670             :     else
    2671             :     {
    2672           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2673             :                  "SetMIFCoordSys() can be called only after dataset has been "
    2674             :                  "created and before any feature is set.");
    2675           0 :         return -1;
    2676             :     }
    2677             : 
    2678           0 :     return 0;
    2679             : }
    2680             : 
    2681             : /**********************************************************************
    2682             :  *                   TABFile::SetProjInfo()
    2683             :  *
    2684             :  * Set projection for a new file using a TABProjInfo structure.
    2685             :  *
    2686             :  * This function must be called after creating a new dataset and before any
    2687             :  * feature can be written to it.
    2688             :  *
    2689             :  * This call will also trigger a lookup of default bounds for the specified
    2690             :  * projection (except nonearth), and reset the m_bBoundsValid flag.
    2691             :  *
    2692             :  * Returns 0 on success, -1 on error.
    2693             :  **********************************************************************/
    2694          28 : int TABFile::SetProjInfo(TABProjInfo *poPI)
    2695             : {
    2696          28 :     if (m_eAccessMode != TABWrite)
    2697             :     {
    2698           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2699             :                  "SetProjInfo() can be used only with Write access.");
    2700           0 :         return -1;
    2701             :     }
    2702             : 
    2703             :     /*-----------------------------------------------------------------
    2704             :      * Lookup default bounds and reset m_bBoundsSet flag
    2705             :      *----------------------------------------------------------------*/
    2706             :     double dXMin;
    2707             :     double dYMin;
    2708             :     double dXMax;
    2709             :     double dYMax;
    2710             : 
    2711          28 :     m_bBoundsSet = FALSE;
    2712          28 :     if (MITABLookupCoordSysBounds(poPI, dXMin, dYMin, dXMax, dYMax))
    2713             :     {
    2714          15 :         SetBounds(dXMin, dYMin, dXMax, dYMax);
    2715             :     }
    2716             : 
    2717             :     /*-----------------------------------------------------------------
    2718             :      * Check that dataset has been created but no feature set yet.
    2719             :      *----------------------------------------------------------------*/
    2720          28 :     if (m_poMAPFile && m_nLastFeatureId < 1)
    2721             :     {
    2722          28 :         if (m_poMAPFile->GetHeaderBlock()->SetProjInfo(poPI) != 0)
    2723           0 :             return -1;
    2724             :     }
    2725             :     else
    2726             :     {
    2727           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2728             :                  "SetProjInfo() can be called only after dataset has been "
    2729             :                  "created and before any feature is set.");
    2730           0 :         return -1;
    2731             :     }
    2732             : 
    2733          28 :     return 0;
    2734             : }
    2735             : 
    2736             : /************************************************************************/
    2737             : /*                            DeleteField()                             */
    2738             : /************************************************************************/
    2739             : 
    2740           4 : OGRErr TABFile::DeleteField(int iField)
    2741             : {
    2742           4 :     if (m_poDATFile == nullptr || !TestCapability(OLCDeleteField))
    2743             :     {
    2744           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2745             :                  "DeleteField");
    2746           0 :         return OGRERR_FAILURE;
    2747             :     }
    2748             : 
    2749           4 :     if (iField < 0 || iField >= m_poDefn->GetFieldCount())
    2750             :     {
    2751           2 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    2752           2 :         return OGRERR_FAILURE;
    2753             :     }
    2754             : 
    2755           2 :     if (m_poDATFile->DeleteField(iField) == 0)
    2756             :     {
    2757           2 :         m_bNeedTABRewrite = TRUE;
    2758           4 :         m_oSetFields.erase(
    2759           2 :             CPLString(m_poDefn->GetFieldDefn(iField)->GetNameRef()).toupper());
    2760             : 
    2761             :         /* Delete from the array of indexed fields */
    2762           2 :         if (iField < m_poDefn->GetFieldCount() - 1)
    2763             :         {
    2764           1 :             memmove(m_panIndexNo + iField, m_panIndexNo + iField + 1,
    2765           1 :                     (m_poDefn->GetFieldCount() - 1 - iField) * sizeof(int));
    2766             :         }
    2767             : 
    2768           2 :         whileUnsealing(m_poDefn)->DeleteFieldDefn(iField);
    2769             : 
    2770           2 :         if (m_eAccessMode == TABReadWrite)
    2771           2 :             WriteTABFile();
    2772             : 
    2773           2 :         return OGRERR_NONE;
    2774             :     }
    2775             :     else
    2776           0 :         return OGRERR_FAILURE;
    2777             : }
    2778             : 
    2779             : /************************************************************************/
    2780             : /*                           ReorderFields()                            */
    2781             : /************************************************************************/
    2782             : 
    2783          15 : OGRErr TABFile::ReorderFields(int *panMap)
    2784             : {
    2785          15 :     if (m_poDATFile == nullptr || !TestCapability(OLCDeleteField))
    2786             :     {
    2787           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2788             :                  "ReorderFields");
    2789           0 :         return OGRERR_FAILURE;
    2790             :     }
    2791          15 :     if (m_poDefn->GetFieldCount() == 0)
    2792           4 :         return OGRERR_NONE;
    2793             : 
    2794          11 :     OGRErr eErr = OGRCheckPermutation(panMap, m_poDefn->GetFieldCount());
    2795          11 :     if (eErr != OGRERR_NONE)
    2796           1 :         return eErr;
    2797             : 
    2798          10 :     if (m_poDATFile->ReorderFields(panMap) == 0)
    2799             :     {
    2800          10 :         m_bNeedTABRewrite = TRUE;
    2801             : 
    2802             :         int *panNewIndexedField = static_cast<int *>(
    2803          10 :             CPLMalloc(sizeof(int) * m_poDefn->GetFieldCount()));
    2804          52 :         for (int i = 0; i < m_poDefn->GetFieldCount(); i++)
    2805             :         {
    2806          42 :             panNewIndexedField[i] = m_panIndexNo[panMap[i]];
    2807             :         }
    2808          10 :         CPLFree(m_panIndexNo);
    2809          10 :         m_panIndexNo = panNewIndexedField;
    2810             : 
    2811          10 :         whileUnsealing(m_poDefn)->ReorderFieldDefns(panMap);
    2812             : 
    2813          10 :         if (m_eAccessMode == TABReadWrite)
    2814          10 :             WriteTABFile();
    2815             : 
    2816          10 :         return OGRERR_NONE;
    2817             :     }
    2818             :     else
    2819           0 :         return OGRERR_FAILURE;
    2820             : }
    2821             : 
    2822             : /************************************************************************/
    2823             : /*                           AlterFieldDefn()                           */
    2824             : /************************************************************************/
    2825             : 
    2826           7 : OGRErr TABFile::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
    2827             :                                int nFlagsIn)
    2828             : {
    2829           7 :     if (m_poDATFile == nullptr || !TestCapability(OLCDeleteField))
    2830             :     {
    2831           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2832             :                  "AlterFieldDefn");
    2833           0 :         return OGRERR_FAILURE;
    2834             :     }
    2835             : 
    2836           7 :     if (iField < 0 || iField >= m_poDefn->GetFieldCount())
    2837             :     {
    2838           2 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    2839           2 :         return OGRERR_FAILURE;
    2840             :     }
    2841             : 
    2842           5 :     if (m_poDATFile->AlterFieldDefn(iField, poNewFieldDefn, nFlagsIn) == 0)
    2843             :     {
    2844           4 :         m_bNeedTABRewrite = TRUE;
    2845             : 
    2846           4 :         OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField);
    2847           4 :         auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
    2848           8 :         if ((nFlagsIn & ALTER_TYPE_FLAG) &&
    2849           4 :             poNewFieldDefn->GetType() != poFieldDefn->GetType())
    2850             :         {
    2851           2 :             poFieldDefn->SetType(poNewFieldDefn->GetType());
    2852           2 :             if ((nFlagsIn & ALTER_WIDTH_PRECISION_FLAG) == 0)
    2853           0 :                 poFieldDefn->SetWidth(254);
    2854             :         }
    2855           4 :         if (nFlagsIn & ALTER_NAME_FLAG)
    2856             :         {
    2857           4 :             m_oSetFields.erase(CPLString(poFieldDefn->GetNameRef()).toupper());
    2858           4 :             poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
    2859             :             m_oSetFields.insert(
    2860           4 :                 CPLString(poNewFieldDefn->GetNameRef()).toupper());
    2861             :         }
    2862           8 :         if ((nFlagsIn & ALTER_WIDTH_PRECISION_FLAG) &&
    2863           4 :             poFieldDefn->GetType() == OFTString)
    2864             :         {
    2865           4 :             poFieldDefn->SetWidth(m_poDATFile->GetFieldWidth(iField));
    2866             :         }
    2867             : 
    2868           4 :         if (m_eAccessMode == TABReadWrite)
    2869           4 :             WriteTABFile();
    2870             : 
    2871           4 :         return OGRERR_NONE;
    2872             :     }
    2873             :     else
    2874           1 :         return OGRERR_FAILURE;
    2875             : }
    2876             : 
    2877             : /************************************************************************/
    2878             : /*                            SyncToDisk()                             */
    2879             : /************************************************************************/
    2880             : 
    2881         119 : OGRErr TABFile::SyncToDisk()
    2882             : {
    2883             :     /* Silently return */
    2884         119 :     if (m_eAccessMode == TABRead)
    2885           0 :         return OGRERR_NONE;
    2886             : 
    2887         119 :     OGRErr eErr = OGRERR_NONE;
    2888             : 
    2889             :     // This is a hack for Windows and VSIFFlushL() issue. See
    2890             :     // http://trac.osgeo.org/gdal/ticket/5556
    2891         119 :     CPLSetConfigOption("VSI_FLUSH", "TRUE");
    2892             : 
    2893         119 :     if (WriteTABFile() != 0)
    2894           0 :         eErr = OGRERR_FAILURE;
    2895             : 
    2896         119 :     if (m_poMAPFile->SyncToDisk() != 0)
    2897           0 :         eErr = OGRERR_FAILURE;
    2898             : 
    2899         119 :     if (m_poDATFile->SyncToDisk() != 0)
    2900           0 :         eErr = OGRERR_FAILURE;
    2901             : 
    2902         119 :     CPLSetConfigOption("VSI_FLUSH", nullptr);
    2903             : 
    2904         119 :     return eErr;
    2905             : }
    2906             : 
    2907             : /************************************************************************/
    2908             : /*                           TestCapability()                           */
    2909             : /************************************************************************/
    2910             : 
    2911        2414 : int TABFile::TestCapability(const char *pszCap)
    2912             : 
    2913             : {
    2914        2414 :     if (EQUAL(pszCap, OLCRandomRead))
    2915           2 :         return TRUE;
    2916             : 
    2917        2412 :     else if (EQUAL(pszCap, OLCSequentialWrite))
    2918          19 :         return m_eAccessMode != TABRead;
    2919             : 
    2920        2393 :     else if (EQUAL(pszCap, OLCRandomWrite))
    2921           3 :         return m_eAccessMode != TABRead;
    2922             : 
    2923        2390 :     else if (EQUAL(pszCap, OLCDeleteFeature))
    2924           3 :         return m_eAccessMode != TABRead;
    2925             : 
    2926        2387 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
    2927           0 :         return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
    2928             : 
    2929        2387 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
    2930           0 :         return TRUE;
    2931             : 
    2932        2387 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    2933           8 :         return TRUE;
    2934             : 
    2935        2379 :     else if (EQUAL(pszCap, OLCCreateField))
    2936          22 :         return m_eAccessMode != TABRead;
    2937             : 
    2938        2357 :     else if (EQUAL(pszCap, OLCDeleteField))
    2939          31 :         return m_eAccessMode != TABRead;
    2940             : 
    2941        2326 :     else if (EQUAL(pszCap, OLCReorderFields))
    2942           5 :         return m_eAccessMode != TABRead;
    2943             : 
    2944        2321 :     else if (EQUAL(pszCap, OLCAlterFieldDefn))
    2945           5 :         return m_eAccessMode != TABRead;
    2946             : 
    2947        2316 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    2948          48 :         return TestUtf8Capability();
    2949             : 
    2950             :     else
    2951        2268 :         return FALSE;
    2952             : }
    2953             : 
    2954             : /**********************************************************************
    2955             :  *                   TABFile::Dump()
    2956             :  *
    2957             :  * Dump block contents... available only in DEBUG mode.
    2958             :  **********************************************************************/
    2959             : #ifdef DEBUG
    2960             : 
    2961           0 : void TABFile::Dump(FILE *fpOut /*=NULL*/)
    2962             : {
    2963           0 :     if (fpOut == nullptr)
    2964           0 :         fpOut = stdout;
    2965             : 
    2966           0 :     fprintf(fpOut, "----- TABFile::Dump() -----\n");
    2967             : 
    2968           0 :     if (m_poMAPFile == nullptr)
    2969             :     {
    2970           0 :         fprintf(fpOut, "File is not opened.\n");
    2971             :     }
    2972             :     else
    2973             :     {
    2974           0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
    2975           0 :         fprintf(fpOut, "Associated TABLE file ...\n\n");
    2976           0 :         m_poDATFile->Dump(fpOut);
    2977           0 :         fprintf(fpOut, "... end of TABLE file dump.\n\n");
    2978           0 :         if (GetSpatialRef() != nullptr)
    2979             :         {
    2980           0 :             char *pszWKT = nullptr;
    2981             : 
    2982           0 :             GetSpatialRef()->exportToWkt(&pszWKT);
    2983           0 :             fprintf(fpOut, "SRS = %s\n", pszWKT);
    2984           0 :             CPLFree(pszWKT);
    2985             :         }
    2986           0 :         fprintf(fpOut, "Associated .MAP file ...\n\n");
    2987           0 :         m_poMAPFile->Dump(fpOut);
    2988           0 :         fprintf(fpOut, "... end of .MAP file dump.\n\n");
    2989             :     }
    2990             : 
    2991           0 :     fflush(fpOut);
    2992           0 : }
    2993             : 
    2994             : #endif  // DEBUG
    2995             : 
    2996             : /*
    2997             :  * SetMetadataItem()
    2998             :  */
    2999         118 : CPLErr TABFile::SetMetadataItem(const char *pszName, const char *pszValue,
    3000             :                                 const char *pszDomain)
    3001             : {
    3002         118 :     if (EQUAL(DESCRIPTION_KEY, pszName) && EQUAL(pszDomain, ""))
    3003             :     {
    3004         118 :         if (m_eAccessMode == TABRead)
    3005             :         {
    3006           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    3007             :                      "Description will not save in TAB file in readonly mode.");
    3008             :         }
    3009             : 
    3010         118 :         m_bNeedTABRewrite = TRUE;
    3011         118 :         std::shared_ptr<char> oEscapedString(EscapeString(pszValue), CPLFree);
    3012         118 :         auto result = IMapInfoFile::SetMetadataItem(DESCRIPTION_KEY,
    3013         118 :                                                     oEscapedString.get());
    3014         118 :         if (oEscapedString)
    3015             :         {
    3016           3 :             CPLDebug("MITAB", "Set description to '%s'", oEscapedString.get());
    3017             :         }
    3018         118 :         return result;
    3019             :     }
    3020           0 :     return IMapInfoFile::SetMetadataItem(pszName, pszValue, pszDomain);
    3021             : }

Generated by: LCOV version 1.14