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: 925 1234 75.0 %
Date: 2026-01-16 04:37:55 Functions: 40 46 87.0 %

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

Generated by: LCOV version 1.14