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: 897 1224 73.3 %
Date: 2024-11-21 22:18:42 Functions: 39 46 84.8 %

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

Generated by: LCOV version 1.14