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

Generated by: LCOV version 1.14