LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_miffile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 663 895 74.1 %
Date: 2024-05-05 22:37:24 Functions: 26 32 81.2 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_miffile.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the MIDFile class.
       7             :  *           To be used by external programs to handle reading/writing of
       8             :  *           features from/to MID/MIF datasets.
       9             :  * Author:   Stephane Villeneuve, stephane.v@videotron.ca
      10             :  *
      11             :  **********************************************************************
      12             :  * Copyright (c) 1999-2003, Stephane Villeneuve
      13             :  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
      14             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      15             :  *
      16             :  * Permission is hereby granted, free of charge, to any person obtaining a
      17             :  * copy of this software and associated documentation files (the "Software"),
      18             :  * to deal in the Software without restriction, including without limitation
      19             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      20             :  * and/or sell copies of the Software, and to permit persons to whom the
      21             :  * Software is furnished to do so, subject to the following conditions:
      22             :  *
      23             :  * The above copyright notice and this permission notice shall be included
      24             :  * in all copies or substantial portions of the Software.
      25             :  *
      26             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      27             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      28             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      29             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      30             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      31             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      32             :  * DEALINGS IN THE SOFTWARE.
      33             :  **********************************************************************/
      34             : 
      35             : #include "cpl_port.h"
      36             : #include "mitab.h"
      37             : 
      38             : #include <cctype>
      39             : #include <cstdio>
      40             : #include <cstdlib>
      41             : #include <cstring>
      42             : #include <algorithm>
      43             : 
      44             : #include "cpl_conv.h"
      45             : #include "cpl_error.h"
      46             : #include "cpl_string.h"
      47             : #include "mitab_priv.h"
      48             : #include "mitab_utils.h"
      49             : #include "ogr_core.h"
      50             : #include "ogr_feature.h"
      51             : #include "ogr_spatialref.h"
      52             : #include "ogrsf_frmts.h"
      53             : 
      54             : /*=====================================================================
      55             :  *                      class MIFFile
      56             :  *====================================================================*/
      57             : 
      58             : /**********************************************************************
      59             :  *                   MIFFile::MIFFile()
      60             :  *
      61             :  * Constructor.
      62             :  **********************************************************************/
      63         954 : MIFFile::MIFFile(GDALDataset *poDS)
      64             :     : IMapInfoFile(poDS), m_pszFname(nullptr), m_eAccessMode(TABRead),
      65             :       m_nVersion(300),
      66             :       // Tab is default delimiter in MIF spec if not explicitly specified.  Use
      67             :       // that by default for read mode. In write mode, we will use "," as
      68             :       // delimiter since it is more common than tab (we do this in Open())
      69        1908 :       m_pszDelimiter(CPLStrdup("\t")), m_pszUnique(nullptr),
      70             :       m_pszIndex(nullptr), m_pszCoordSys(nullptr), m_paeFieldType(nullptr),
      71             :       m_pabFieldIndexed(nullptr), m_pabFieldUnique(nullptr),
      72             :       m_dfXMultiplier(1.0), m_dfYMultiplier(1.0), m_dfXDisplacement(0.0),
      73             :       m_dfYDisplacement(0.0), m_dXMin(0), m_dYMin(0), m_dXMax(0), m_dYMax(0),
      74             :       m_bExtentsSet(FALSE), m_nPoints(0), m_nLines(0), m_nRegions(0),
      75             :       m_nTexts(0), m_nPreloadedId(0), m_poMIDFile(nullptr),
      76             :       m_poMIFFile(nullptr), m_poDefn(nullptr), m_poSpatialRef(nullptr),
      77             :       m_nFeatureCount(0), m_nWriteFeatureId(-1), m_nAttribute(0),
      78         954 :       m_bPreParsed(FALSE), m_bHeaderWrote(FALSE)
      79             : {
      80         954 :     m_nCurFeatureId = 0;
      81         954 :     m_poCurFeature = nullptr;
      82         954 : }
      83             : 
      84             : /**********************************************************************
      85             :  *                   MIFFile::~MIFFile()
      86             :  *
      87             :  * Destructor.
      88             :  **********************************************************************/
      89        1908 : MIFFile::~MIFFile()
      90             : {
      91         954 :     MIFFile::Close();
      92        1908 : }
      93             : 
      94             : /**********************************************************************
      95             :  *                   MIFFile::Open()
      96             :  *
      97             :  * Returns 0 on success, -1 on error.
      98             :  **********************************************************************/
      99         954 : int MIFFile::Open(const char *pszFname, TABAccess eAccess,
     100             :                   GBool bTestOpenNoError /*=FALSE*/,
     101             :                   const char *pszCharset /* = NULL */)
     102             : {
     103         954 :     char *pszTmpFname = nullptr;
     104         954 :     int nFnameLen = 0;
     105             : 
     106         954 :     CPLErrorReset();
     107             : 
     108         954 :     if (m_poMIFFile)
     109             :     {
     110           0 :         CPLError(CE_Failure, CPLE_FileIO,
     111             :                  "Open() failed: object already contains an open file");
     112             : 
     113           0 :         return -1;
     114             :     }
     115             : 
     116             :     /*-----------------------------------------------------------------
     117             :      * Validate access mode
     118             :      *----------------------------------------------------------------*/
     119         954 :     const char *pszAccess = nullptr;
     120         954 :     if (eAccess == TABRead)
     121             :     {
     122         869 :         m_eAccessMode = TABRead;
     123         869 :         pszAccess = "rt";
     124             :     }
     125          85 :     else if (eAccess == TABWrite)
     126             :     {
     127          84 :         m_eAccessMode = TABWrite;
     128          84 :         pszAccess = "wt";
     129             : 
     130             :         // In write mode, use "," as delimiter since it is more common than tab
     131          84 :         CPLFree(m_pszDelimiter);
     132          84 :         m_pszDelimiter = CPLStrdup(",");
     133             :     }
     134             :     else
     135             :     {
     136           1 :         if (!bTestOpenNoError)
     137           0 :             CPLError(CE_Failure, CPLE_FileIO,
     138             :                      "Open() failed: access mode \"%d\" not supported",
     139             :                      eAccess);
     140             :         else
     141           1 :             CPLErrorReset();
     142             : 
     143           1 :         return -1;
     144             :     }
     145             : 
     146             :     /*-----------------------------------------------------------------
     147             :      * Make sure filename has a .MIF or .MID extension...
     148             :      *----------------------------------------------------------------*/
     149         953 :     m_pszFname = CPLStrdup(pszFname);
     150         953 :     nFnameLen = static_cast<int>(strlen(m_pszFname));
     151         953 :     if (nFnameLen > 4 && (strcmp(m_pszFname + nFnameLen - 4, ".MID") == 0 ||
     152         953 :                           strcmp(m_pszFname + nFnameLen - 4, ".MIF") == 0))
     153           0 :         strcpy(m_pszFname + nFnameLen - 4, ".MIF");
     154         953 :     else if (nFnameLen > 4 && (EQUAL(m_pszFname + nFnameLen - 4, ".mid") ||
     155         953 :                                EQUAL(m_pszFname + nFnameLen - 4, ".mif")))
     156         953 :         strcpy(m_pszFname + nFnameLen - 4, ".mif");
     157             :     else
     158             :     {
     159           0 :         if (!bTestOpenNoError)
     160           0 :             CPLError(CE_Failure, CPLE_FileIO,
     161             :                      "Open() failed for %s: invalid filename extension",
     162             :                      m_pszFname);
     163             :         else
     164           0 :             CPLErrorReset();
     165             : 
     166           0 :         return -1;
     167             :     }
     168             : 
     169         953 :     pszTmpFname = CPLStrdup(m_pszFname);
     170             : 
     171             :     /*-----------------------------------------------------------------
     172             :      * Open .MIF file
     173             :      *----------------------------------------------------------------*/
     174             : 
     175             : #ifndef _WIN32
     176             :     /*-----------------------------------------------------------------
     177             :      * On Unix, make sure extension uses the right cases
     178             :      * We do it even for write access because if a file with the same
     179             :      * extension already exists we want to overwrite it.
     180             :      *----------------------------------------------------------------*/
     181         953 :     TABAdjustFilenameExtension(pszTmpFname);
     182             : #endif
     183             : 
     184         953 :     m_poMIFFile = new MIDDATAFile(CharsetToEncoding(pszCharset));
     185             : 
     186         953 :     if (m_poMIFFile->Open(pszTmpFname, pszAccess) != 0)
     187             :     {
     188           0 :         if (!bTestOpenNoError)
     189           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Unable to open %s.",
     190             :                      pszTmpFname);
     191             :         else
     192           0 :             CPLErrorReset();
     193             : 
     194           0 :         CPLFree(pszTmpFname);
     195           0 :         Close();
     196             : 
     197           0 :         return -1;
     198             :     }
     199             : 
     200             :     /*-----------------------------------------------------------------
     201             :      * Read MIF File Header
     202             :      *----------------------------------------------------------------*/
     203         953 :     int bIsEmpty = FALSE;
     204         953 :     if (m_eAccessMode == TABRead && ParseMIFHeader(&bIsEmpty) != 0)
     205             :     {
     206          58 :         Close();
     207             : 
     208          58 :         if (!bTestOpenNoError)
     209           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     210             :                      "Failed parsing header in %s.", m_pszFname);
     211             :         else
     212          58 :             CPLErrorReset();
     213             : 
     214          58 :         CPLFree(pszTmpFname);
     215             : 
     216          58 :         return -1;
     217             :     }
     218             : 
     219         895 :     if (m_nAttribute > 0 || m_eAccessMode == TABWrite)
     220             :     {
     221             :         /*-----------------------------------------------------------------
     222             :          * Open .MID file
     223             :          *----------------------------------------------------------------*/
     224         177 :         if (nFnameLen > 4 && strcmp(pszTmpFname + nFnameLen - 4, ".MIF") == 0)
     225           0 :             strcpy(pszTmpFname + nFnameLen - 4, ".MID");
     226             :         else
     227         177 :             strcpy(pszTmpFname + nFnameLen - 4, ".mid");
     228             : 
     229             : #ifndef _WIN32
     230         177 :         TABAdjustFilenameExtension(pszTmpFname);
     231             : #endif
     232             : 
     233         177 :         m_poMIDFile = new MIDDATAFile("");
     234         177 :         if (eAccess == TABRead || eAccess == TABReadWrite)
     235             :         {
     236          93 :             m_poMIDFile->SetEncoding(CharsetToEncoding(GetCharset()));
     237             :         }
     238          84 :         else if (eAccess == TABWrite)
     239             :         {
     240          84 :             m_poMIDFile->SetEncoding(CharsetToEncoding(pszCharset));
     241             :         }
     242             : 
     243         177 :         if (m_poMIDFile->Open(pszTmpFname, pszAccess) != 0)
     244             :         {
     245           4 :             if (m_eAccessMode == TABWrite)
     246             :             {
     247           0 :                 if (!bTestOpenNoError)
     248           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     249             :                              "Unable to open %s.", pszTmpFname);
     250             :                 else
     251           0 :                     CPLErrorReset();
     252             : 
     253           0 :                 CPLFree(pszTmpFname);
     254           0 :                 Close();
     255             : 
     256           0 :                 return -1;
     257             :             }
     258             :             else
     259             :             {
     260           4 :                 CPLDebug("MITAB",
     261             :                          "%s is not found, although %d attributes are declared",
     262             :                          pszTmpFname, m_nAttribute);
     263           4 :                 delete m_poMIDFile;
     264           4 :                 m_poMIDFile = nullptr;
     265             :             }
     266             :         }
     267             :     }
     268             : 
     269         895 :     CPLFree(pszTmpFname);
     270         895 :     pszTmpFname = nullptr;
     271             : 
     272             :     /*-----------------------------------------------------------------
     273             :      * In write access, set some defaults
     274             :      *----------------------------------------------------------------*/
     275         895 :     if (m_eAccessMode == TABWrite)
     276             :     {
     277          84 :         m_nVersion = 300;
     278          84 :         if (pszCharset != nullptr)
     279          84 :             SetCharset(pszCharset);
     280             :         else
     281           0 :             SetCharset("Neutral");
     282             :     }
     283             : 
     284         895 :     m_poMIFFile->SetTranslation(m_dfXMultiplier, m_dfYMultiplier,
     285             :                                 m_dfXDisplacement, m_dfYDisplacement);
     286         895 :     if (m_poMIDFile != nullptr)
     287         173 :         m_poMIDFile->SetTranslation(m_dfXMultiplier, m_dfYMultiplier,
     288             :                                     m_dfXDisplacement, m_dfYDisplacement);
     289         895 :     m_poMIFFile->SetDelimiter(m_pszDelimiter);
     290         895 :     if (m_poMIDFile != nullptr)
     291         173 :         m_poMIDFile->SetDelimiter(m_pszDelimiter);
     292             : 
     293             :     /*-------------------------------------------------------------
     294             :      * Set geometry type if the geometry objects are uniform.
     295             :      *------------------------------------------------------------*/
     296         895 :     int numPoints = 0, numRegions = 0, numTexts = 0, numLines = 0;
     297             : 
     298         895 :     if (GetFeatureCountByType(numPoints, numLines, numRegions, numTexts,
     299         895 :                               FALSE) == 0)
     300             :     {
     301           0 :         numPoints += numTexts;
     302           0 :         if (numPoints > 0 && numLines == 0 && numRegions == 0)
     303           0 :             m_poDefn->SetGeomType(wkbPoint);
     304           0 :         else if (numPoints == 0 && numLines > 0 && numRegions == 0)
     305           0 :             m_poDefn->SetGeomType(wkbLineString);
     306             :         else
     307             :         {
     308             :             /* we leave it unknown indicating a mixture */
     309             :         }
     310             :     }
     311             : 
     312             :     /* A newly created layer should have OGRFeatureDefn */
     313         895 :     if (m_poDefn == nullptr)
     314             :     {
     315          84 :         char *pszFeatureClassName = TABGetBasename(m_pszFname);
     316          84 :         m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
     317          84 :         CPLFree(pszFeatureClassName);
     318             :         // Ref count defaults to 0... set it to 1
     319          84 :         m_poDefn->Reference();
     320          84 :         m_poDefn->Seal(/* bSealFields = */ true);
     321             :     }
     322             : 
     323         895 :     return 0;
     324             : }
     325             : 
     326             : /**********************************************************************
     327             :  *                   MIFFile::ParseMIFHeader()
     328             :  *
     329             :  * Scan the header of a MIF file, and store any useful information into
     330             :  * class members.  The main piece of information being the fields
     331             :  * definition that we use to build the OGRFeatureDefn for this file.
     332             :  *
     333             :  * This private method should be used only during the Open() call.
     334             :  *
     335             :  * Returns 0 on success, -1 on error.
     336             :  **********************************************************************/
     337         869 : int MIFFile::ParseMIFHeader(int *pbIsEmpty)
     338             : {
     339         869 :     *pbIsEmpty = FALSE;
     340             : 
     341         869 :     char *pszFeatureClassName = TABGetBasename(m_pszFname);
     342         869 :     m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
     343         869 :     CPLFree(pszFeatureClassName);
     344             :     // Ref count defaults to 0... set it to 1
     345         869 :     m_poDefn->Reference();
     346         869 :     m_poDefn->Seal(/* bSealFields = */ true);
     347             : 
     348         869 :     if (m_eAccessMode != TABRead)
     349             :     {
     350           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     351             :                  "ParseMIDFile() can be used only with Read access.");
     352           0 :         return -1;
     353             :     }
     354             : 
     355             :     /*-----------------------------------------------------------------
     356             :      * Parse header until we find the "Data" line
     357             :      *----------------------------------------------------------------*/
     358         869 :     char **papszToken = nullptr;
     359         869 :     GBool bColumns = FALSE;
     360         869 :     GBool bAllColumnsRead = FALSE;
     361         869 :     int nColumns = 0;
     362         869 :     GBool bCoordSys = FALSE;
     363        1738 :     CPLString osCoordSys;
     364         869 :     int nLineCount = 0;
     365             : 
     366         869 :     const char *pszLine = nullptr;
     367        5311 :     while (((pszLine = m_poMIFFile->GetLine()) != nullptr) &&
     368         819 :            ((bAllColumnsRead == FALSE) || !STARTS_WITH_CI(pszLine, "Data")))
     369             :     {
     370        3623 :         nLineCount++;
     371        3623 :         if (nLineCount == 100000)
     372             :         {
     373             :             // Arbitrary threshold. The number of lines must be at least as big
     374             :             // as the number of fields we want to support.
     375           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     376             :                      "Too many lines in MIF header");
     377           0 :             return -1;
     378             :         }
     379             : 
     380        3623 :         if (bColumns == TRUE && nColumns > 0)
     381             :         {
     382         153 :             if (AddFields(pszLine) == 0)
     383             :             {
     384         153 :                 nColumns--;
     385         153 :                 if (nColumns == 0)
     386             :                 {
     387          93 :                     bAllColumnsRead = TRUE;
     388          93 :                     bColumns = FALSE;
     389             :                 }
     390             :             }
     391             :             else
     392             :             {
     393           0 :                 bColumns = FALSE;
     394             :             }
     395             :         }
     396        3470 :         else if (STARTS_WITH_CI(pszLine, "VERSION"))
     397             :         {
     398             :             papszToken =
     399         862 :                 CSLTokenizeStringComplex(pszLine, " ()\t", TRUE, FALSE);
     400         862 :             bColumns = FALSE;
     401         862 :             bCoordSys = FALSE;
     402         862 :             if (CSLCount(papszToken) == 2)
     403         860 :                 m_nVersion = atoi(papszToken[1]);
     404             : 
     405         862 :             CSLDestroy(papszToken);
     406             :         }
     407        2608 :         else if (STARTS_WITH_CI(pszLine, "CHARSET"))
     408             :         {
     409             :             papszToken =
     410         850 :                 CSLTokenizeStringComplex(pszLine, " ()\t", TRUE, FALSE);
     411         850 :             bColumns = FALSE;
     412         850 :             bCoordSys = FALSE;
     413             : 
     414         850 :             if (CSLCount(papszToken) == 2)
     415             :             {
     416         847 :                 SetCharset(papszToken[1]);
     417             :             }
     418         850 :             CSLDestroy(papszToken);
     419             :         }
     420        1758 :         else if (STARTS_WITH_CI(pszLine, "DELIMITER"))
     421             :         {
     422             :             papszToken =
     423         828 :                 CSLTokenizeStringComplex(pszLine, " ()\t", TRUE, FALSE);
     424         828 :             bColumns = FALSE;
     425         828 :             bCoordSys = FALSE;
     426             : 
     427         828 :             if (CSLCount(papszToken) == 2)
     428             :             {
     429         825 :                 CPLFree(m_pszDelimiter);
     430         825 :                 m_pszDelimiter = CPLStrdup(papszToken[1]);
     431             :             }
     432         828 :             CSLDestroy(papszToken);
     433             :         }
     434         930 :         else if (m_pszUnique == nullptr && STARTS_WITH_CI(pszLine, "UNIQUE"))
     435             :         {
     436           0 :             bColumns = FALSE;
     437           0 :             bCoordSys = FALSE;
     438             : 
     439           0 :             m_pszUnique = CPLStrdup(pszLine + 6);
     440             :         }
     441         930 :         else if (m_pszIndex == nullptr && STARTS_WITH_CI(pszLine, "INDEX"))
     442             :         {
     443           1 :             bColumns = FALSE;
     444           1 :             bCoordSys = FALSE;
     445             : 
     446           1 :             m_pszIndex = CPLStrdup(pszLine + 5);
     447             :         }
     448        1006 :         else if (osCoordSys.empty() && STARTS_WITH_CI(pszLine, "COORDSYS") &&
     449          77 :                  CPLStrnlen(pszLine, 9) >= 9)
     450             :         {
     451          77 :             bCoordSys = TRUE;
     452          77 :             osCoordSys = pszLine + 9;
     453             :         }
     454         852 :         else if (STARTS_WITH_CI(pszLine, "TRANSFORM"))
     455             :         {
     456           0 :             papszToken = CSLTokenizeStringComplex(pszLine, " ,\t", TRUE, FALSE);
     457           0 :             bColumns = FALSE;
     458           0 :             bCoordSys = FALSE;
     459             : 
     460           0 :             if (CSLCount(papszToken) == 5)
     461             :             {
     462           0 :                 m_dfXMultiplier = CPLAtof(papszToken[1]);
     463           0 :                 m_dfYMultiplier = CPLAtof(papszToken[2]);
     464           0 :                 m_dfXDisplacement = CPLAtof(papszToken[3]);
     465           0 :                 m_dfYDisplacement = CPLAtof(papszToken[4]);
     466             : 
     467           0 :                 if (m_dfXMultiplier == 0.0)
     468           0 :                     m_dfXMultiplier = 1.0;
     469           0 :                 if (m_dfYMultiplier == 0.0)
     470           0 :                     m_dfYMultiplier = 1.0;
     471             :             }
     472           0 :             CSLDestroy(papszToken);
     473             :         }
     474         852 :         else if (STARTS_WITH_CI(pszLine, "COLUMNS"))
     475             :         {
     476             :             papszToken =
     477         818 :                 CSLTokenizeStringComplex(pszLine, " ()\t", TRUE, FALSE);
     478         818 :             bCoordSys = FALSE;
     479         818 :             bColumns = TRUE;
     480         818 :             if (CSLCount(papszToken) == 2)
     481             :             {
     482         816 :                 nColumns = atoi(papszToken[1]);
     483         816 :                 m_nAttribute = nColumns;
     484         816 :                 if (nColumns == 0)
     485             :                 {
     486             :                     // Permit to 0 columns
     487         723 :                     bAllColumnsRead = TRUE;
     488         723 :                     bColumns = FALSE;
     489             :                 }
     490             :             }
     491             :             else
     492             :             {
     493           2 :                 bColumns = FALSE;
     494           2 :                 m_nAttribute = 0;
     495             :             }
     496         818 :             CSLDestroy(papszToken);
     497             :         }
     498          34 :         else if (bCoordSys == TRUE)
     499             :         {
     500           0 :             if (osCoordSys.size() > 10000)  // Arbitrary threshold
     501             :             {
     502           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
     503             :                          "COORDSYS value too long");
     504           0 :                 return -1;
     505             :             }
     506           0 :             osCoordSys += ' ';
     507           0 :             osCoordSys += pszLine;
     508             :         }
     509             :     }
     510             : 
     511         869 :     if (!osCoordSys.empty())
     512             :     {
     513          77 :         m_pszCoordSys = CPLStrdup(osCoordSys);
     514             : 
     515             :         // Extract bounds if present
     516             :         char **papszFields =
     517          77 :             CSLTokenizeStringComplex(osCoordSys, " ,()\t", TRUE, FALSE);
     518          77 :         int iBounds = CSLFindString(papszFields, "Bounds");
     519          77 :         if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields))
     520             :         {
     521          15 :             m_dXMin = CPLAtof(papszFields[++iBounds]);
     522          15 :             m_dYMin = CPLAtof(papszFields[++iBounds]);
     523          15 :             m_dXMax = CPLAtof(papszFields[++iBounds]);
     524          15 :             m_dYMax = CPLAtof(papszFields[++iBounds]);
     525          15 :             m_bBoundsSet = TRUE;
     526             :         }
     527          77 :         CSLDestroy(papszFields);
     528             :     }
     529             : 
     530         869 :     if (!bAllColumnsRead)
     531             :     {
     532          53 :         CPLError(CE_Failure, CPLE_NotSupported,
     533             :                  "COLUMNS keyword not found or invalid number of columns read "
     534             :                  "in %s.  File may be corrupt.",
     535             :                  m_pszFname);
     536          53 :         return -1;
     537             :     }
     538             : 
     539        1627 :     if (m_poMIFFile->GetLastLine() == nullptr ||
     540         811 :         STARTS_WITH_CI(m_poMIFFile->GetLastLine(), "DATA") == FALSE)
     541             :     {
     542           5 :         CPLError(CE_Failure, CPLE_NotSupported,
     543             :                  "DATA keyword not found in %s.  File may be corrupt.",
     544             :                  m_pszFname);
     545           5 :         return -1;
     546             :     }
     547             : 
     548             :     /*-----------------------------------------------------------------
     549             :      * Move pointer to first line of first object
     550             :      *----------------------------------------------------------------*/
     551        2428 :     while (((pszLine = m_poMIFFile->GetLine()) != nullptr) &&
     552        1607 :            m_poMIFFile->IsValidFeature(pszLine) == FALSE)
     553             :         ;
     554             : 
     555         811 :     *pbIsEmpty = (pszLine == nullptr);
     556             : 
     557             :     /*-----------------------------------------------------------------
     558             :      * Check for Unique and Indexed flags
     559             :      *----------------------------------------------------------------*/
     560         811 :     if (m_pszIndex)
     561             :     {
     562           1 :         papszToken = CSLTokenizeStringComplex(m_pszIndex, " ,\t", TRUE, FALSE);
     563           3 :         for (int i = 0; papszToken && papszToken[i]; i++)
     564             :         {
     565           2 :             int nVal = atoi(papszToken[i]);
     566           2 :             if (nVal > 0 && nVal <= m_poDefn->GetFieldCount())
     567           2 :                 m_pabFieldIndexed[nVal - 1] = TRUE;
     568             :         }
     569           1 :         CSLDestroy(papszToken);
     570             :     }
     571             : 
     572         811 :     if (m_pszUnique)
     573             :     {
     574           0 :         papszToken = CSLTokenizeStringComplex(m_pszUnique, " ,\t", TRUE, FALSE);
     575           0 :         for (int i = 0; papszToken && papszToken[i]; i++)
     576             :         {
     577           0 :             int nVal = atoi(papszToken[i]);
     578           0 :             if (nVal > 0 && nVal <= m_poDefn->GetFieldCount())
     579           0 :                 m_pabFieldUnique[nVal - 1] = TRUE;
     580             :         }
     581           0 :         CSLDestroy(papszToken);
     582             :     }
     583             : 
     584         811 :     return 0;
     585             : }
     586             : 
     587             : /************************************************************************/
     588             : /*                             AddFields()                              */
     589             : /************************************************************************/
     590             : 
     591         153 : int MIFFile::AddFields(const char *pszLine)
     592             : {
     593         153 :     int nStatus = 0;
     594             : 
     595         153 :     CPLAssert(m_bHeaderWrote == FALSE);
     596             :     char **papszToken =
     597         153 :         CSLTokenizeStringComplex(pszLine, " (,)\t", TRUE, FALSE);
     598         153 :     int numTok = CSLCount(papszToken);
     599             : 
     600         306 :     CPLString osFieldName;
     601         153 :     if (numTok > 0)
     602             :     {
     603         153 :         osFieldName = papszToken[0];
     604         153 :         if (strlen(GetEncoding()) > 0)
     605             :         {
     606          62 :             osFieldName.Recode(GetEncoding(), CPL_ENC_UTF8);
     607             :         }
     608             :     }
     609             : 
     610         153 :     if (numTok >= 3 && EQUAL(papszToken[1], "char"))
     611             :     {
     612             :         /*-------------------------------------------------
     613             :          * CHAR type
     614             :          *------------------------------------------------*/
     615          98 :         nStatus = AddFieldNative(osFieldName, TABFChar, atoi(papszToken[2]));
     616             :     }
     617          55 :     else if (numTok >= 2 && EQUAL(papszToken[1], "integer"))
     618             :     {
     619          30 :         if (numTok == 2)
     620             :         {
     621             :             /*-------------------------------------------------
     622             :              * INTEGER type without a specified width
     623             :              *------------------------------------------------*/
     624          30 :             nStatus = AddFieldNative(osFieldName, TABFInteger);
     625             :         }
     626             :         else /* if (numTok > 2) */
     627             :         {
     628             :             /*-------------------------------------------------
     629             :              * INTEGER type with a specified width
     630             :              *------------------------------------------------*/
     631             :             nStatus =
     632           0 :                 AddFieldNative(osFieldName, TABFInteger, atoi(papszToken[2]));
     633             :         }
     634             :     }
     635          25 :     else if (numTok >= 2 && EQUAL(papszToken[1], "smallint"))
     636             :     {
     637           1 :         if (numTok == 2)
     638             :         {
     639             :             /*-------------------------------------------------
     640             :              * SMALLINT type without a specified width
     641             :              *------------------------------------------------*/
     642           1 :             nStatus = AddFieldNative(osFieldName, TABFSmallInt);
     643             :         }
     644             :         else /* if (numTok > 2) */
     645             :         {
     646             :             /*-------------------------------------------------
     647             :              * SMALLINT type with a specified width
     648             :              *------------------------------------------------*/
     649             :             nStatus =
     650           0 :                 AddFieldNative(osFieldName, TABFSmallInt, atoi(papszToken[2]));
     651             :         }
     652             :     }
     653          24 :     else if (numTok >= 2 && EQUAL(papszToken[1], "largeint"))
     654             :     {
     655           1 :         if (numTok == 2)
     656             :         {
     657             :             /*-------------------------------------------------
     658             :              * LargeInt type without a specified width
     659             :              *------------------------------------------------*/
     660           1 :             nStatus = AddFieldNative(osFieldName, TABFLargeInt);
     661             :         }
     662             :         else /* if (numTok > 2) */
     663             :         {
     664             :             /*-------------------------------------------------
     665             :              * LargeInt type with a specified width
     666             :              *------------------------------------------------*/
     667             :             nStatus =
     668           0 :                 AddFieldNative(osFieldName, TABFLargeInt, atoi(papszToken[2]));
     669             :         }
     670             :     }
     671          23 :     else if (numTok >= 4 && EQUAL(papszToken[1], "decimal"))
     672             :     {
     673             :         /*-------------------------------------------------
     674             :          * DECIMAL type
     675             :          *------------------------------------------------*/
     676           3 :         nStatus = AddFieldNative(osFieldName, TABFDecimal, atoi(papszToken[2]),
     677           3 :                                  atoi(papszToken[3]));
     678             :     }
     679          20 :     else if (numTok >= 2 && EQUAL(papszToken[1], "float"))
     680             :     {
     681             :         /*-------------------------------------------------
     682             :          * FLOAT type
     683             :          *------------------------------------------------*/
     684          15 :         nStatus = AddFieldNative(osFieldName, TABFFloat);
     685             :     }
     686           5 :     else if (numTok >= 2 && EQUAL(papszToken[1], "date"))
     687             :     {
     688             :         /*-------------------------------------------------
     689             :          * DATE type (returned as a string: "DD/MM/YYYY" or "YYYYMMDD")
     690             :          *------------------------------------------------*/
     691           1 :         nStatus = AddFieldNative(osFieldName, TABFDate);
     692             :     }
     693           4 :     else if (numTok >= 2 && EQUAL(papszToken[1], "time"))
     694             :     {
     695             :         /*-------------------------------------------------
     696             :          *  TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm")
     697             :          *------------------------------------------------*/
     698           1 :         nStatus = AddFieldNative(osFieldName, TABFTime);
     699             :     }
     700           3 :     else if (numTok >= 2 && EQUAL(papszToken[1], "datetime"))
     701             :     {
     702             :         /*-------------------------------------------------
     703             :          * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS",
     704             :          * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm")
     705             :          *------------------------------------------------*/
     706           1 :         nStatus = AddFieldNative(osFieldName, TABFDateTime);
     707             :     }
     708           2 :     else if (numTok >= 2 && EQUAL(papszToken[1], "logical"))
     709             :     {
     710             :         /*-------------------------------------------------
     711             :          * LOGICAL type (value "T" or "F")
     712             :          *------------------------------------------------*/
     713           2 :         nStatus = AddFieldNative(osFieldName, TABFLogical);
     714             :     }
     715             :     else
     716           0 :         nStatus = -1;  // Unrecognized field type or line corrupt
     717             : 
     718         153 :     CSLDestroy(papszToken);
     719         153 :     papszToken = nullptr;
     720             : 
     721         153 :     if (nStatus != 0)
     722             :     {
     723           0 :         CPLError(CE_Failure, CPLE_FileIO,
     724             :                  "Failed to parse field definition in file %s", m_pszFname);
     725           0 :         return -1;
     726             :     }
     727             : 
     728         153 :     return 0;
     729             : }
     730             : 
     731             : /************************************************************************/
     732             : /*                          GetFeatureCount()                           */
     733             : /************************************************************************/
     734             : 
     735          15 : GIntBig MIFFile::GetFeatureCount(int bForce)
     736             : {
     737             : 
     738          15 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
     739           6 :         return OGRLayer::GetFeatureCount(bForce);
     740             :     else
     741             :     {
     742           9 :         if (bForce == TRUE)
     743           9 :             PreParseFile();
     744             : 
     745           9 :         if (m_bPreParsed)
     746           9 :             return m_nFeatureCount;
     747             :         else
     748           0 :             return -1;
     749             :     }
     750             : }
     751             : 
     752             : /************************************************************************/
     753             : /*                            ResetReading()                            */
     754             : /************************************************************************/
     755             : 
     756         842 : void MIFFile::ResetReading()
     757             : 
     758             : {
     759         842 :     m_poMIFFile->Rewind();
     760             : 
     761         842 :     const char *pszLine = nullptr;
     762        4647 :     while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
     763        4647 :         if (STARTS_WITH_CI(pszLine, "DATA"))
     764         842 :             break;
     765             : 
     766        1750 :     while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
     767             :     {
     768        1743 :         if (m_poMIFFile->IsValidFeature(pszLine))
     769         835 :             break;
     770             :     }
     771             : 
     772         842 :     if (m_poMIDFile != nullptr)
     773             :     {
     774         123 :         m_poMIDFile->Rewind();
     775             :     }
     776             : 
     777             :     // We're positioned on first feature.  Feature Ids start at 1.
     778         842 :     if (m_poCurFeature)
     779             :     {
     780          10 :         delete m_poCurFeature;
     781          10 :         m_poCurFeature = nullptr;
     782             :     }
     783             : 
     784         842 :     m_nCurFeatureId = 0;
     785         842 :     m_nPreloadedId = 1;
     786         842 : }
     787             : 
     788             : /************************************************************************/
     789             : /*                            PreParseFile()                            */
     790             : /************************************************************************/
     791             : 
     792          13 : void MIFFile::PreParseFile()
     793             : {
     794          13 :     char **papszToken = nullptr;
     795             : 
     796          13 :     GBool bPLine = FALSE;
     797          13 :     GBool bText = FALSE;
     798             : 
     799          13 :     if (m_bPreParsed == TRUE)
     800          11 :         return;
     801             : 
     802           2 :     m_poMIFFile->Rewind();
     803             : 
     804           2 :     const char *pszLine = nullptr;
     805          16 :     while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
     806          16 :         if (STARTS_WITH_CI(pszLine, "DATA"))
     807           2 :             break;
     808             : 
     809           2 :     m_nPoints = m_nLines = m_nRegions = m_nTexts = 0;
     810             : 
     811         298 :     while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
     812             :     {
     813         296 :         if (m_poMIFFile->IsValidFeature(pszLine))
     814             :         {
     815          11 :             bPLine = FALSE;
     816          11 :             bText = FALSE;
     817          11 :             m_nFeatureCount++;
     818             :         }
     819             : 
     820         296 :         CSLDestroy(papszToken);
     821         296 :         papszToken = CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS);
     822             : 
     823         296 :         if (STARTS_WITH_CI(pszLine, "POINT"))
     824             :         {
     825           0 :             m_nPoints++;
     826           0 :             if (CSLCount(papszToken) == 3)
     827             :             {
     828           0 :                 UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[1])),
     829           0 :                               m_poMIFFile->GetYTrans(CPLAtof(papszToken[2])));
     830             :             }
     831             :         }
     832         296 :         else if (STARTS_WITH_CI(pszLine, "LINE") ||
     833         296 :                  STARTS_WITH_CI(pszLine, "RECT") ||
     834         296 :                  STARTS_WITH_CI(pszLine, "ROUNDRECT") ||
     835         296 :                  STARTS_WITH_CI(pszLine, "ARC") ||
     836         296 :                  STARTS_WITH_CI(pszLine, "ELLIPSE"))
     837             :         {
     838           0 :             if (CSLCount(papszToken) == 5)
     839             :             {
     840           0 :                 m_nLines++;
     841           0 :                 UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[1])),
     842           0 :                               m_poMIFFile->GetYTrans(CPLAtof(papszToken[2])));
     843           0 :                 UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[3])),
     844           0 :                               m_poMIFFile->GetYTrans(CPLAtof(papszToken[4])));
     845             :             }
     846             :         }
     847         296 :         else if (STARTS_WITH_CI(pszLine, "REGION"))
     848             :         {
     849          11 :             m_nRegions++;
     850          11 :             bPLine = TRUE;
     851             :         }
     852         285 :         else if (STARTS_WITH_CI(pszLine, "PLINE"))
     853             :         {
     854           0 :             m_nLines++;
     855           0 :             bPLine = TRUE;
     856             :         }
     857         285 :         else if (STARTS_WITH_CI(pszLine, "TEXT"))
     858             :         {
     859           0 :             m_nTexts++;
     860           0 :             bText = TRUE;
     861             :         }
     862         285 :         else if (bPLine == TRUE)
     863             :         {
     864         555 :             if (CSLCount(papszToken) == 2 &&
     865         272 :                 strchr("-.0123456789", papszToken[0][0]) != nullptr)
     866             :             {
     867         250 :                 UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[0])),
     868         250 :                               m_poMIFFile->GetYTrans(CPLAtof(papszToken[1])));
     869             :             }
     870             :         }
     871           2 :         else if (bText == TRUE)
     872             :         {
     873           0 :             if (CSLCount(papszToken) == 4 &&
     874           0 :                 strchr("-.0123456789", papszToken[0][0]) != nullptr)
     875             :             {
     876           0 :                 UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[0])),
     877           0 :                               m_poMIFFile->GetYTrans(CPLAtof(papszToken[1])));
     878           0 :                 UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[2])),
     879           0 :                               m_poMIFFile->GetYTrans(CPLAtof(papszToken[3])));
     880             :             }
     881             :         }
     882             :     }
     883             : 
     884           2 :     CSLDestroy(papszToken);
     885             : 
     886           2 :     m_poMIFFile->Rewind();
     887             : 
     888          16 :     while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
     889          16 :         if (STARTS_WITH_CI(pszLine, "DATA"))
     890           2 :             break;
     891             : 
     892           4 :     while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
     893             :     {
     894           4 :         if (m_poMIFFile->IsValidFeature(pszLine))
     895           2 :             break;
     896             :     }
     897             : 
     898           2 :     if (m_poMIDFile != nullptr)
     899             :     {
     900           2 :         m_poMIDFile->Rewind();
     901             :     }
     902             : 
     903           2 :     m_bPreParsed = TRUE;
     904             : }
     905             : 
     906             : /**********************************************************************
     907             :  *                   MIFFile::WriteMIFHeader()
     908             :  *
     909             :  * Generate the .MIF header.
     910             :  *
     911             :  * Returns 0 on success, -1 on error.
     912             :  **********************************************************************/
     913          84 : int MIFFile::WriteMIFHeader()
     914             : {
     915             :     GBool bFound;
     916             : 
     917          84 :     if (m_eAccessMode != TABWrite)
     918             :     {
     919           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     920             :                  "WriteMIFHeader() can be used only with Write access.");
     921           0 :         return -1;
     922             :     }
     923             : 
     924          84 :     if (m_poDefn == nullptr || m_poDefn->GetFieldCount() == 0)
     925             :     {
     926           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     927             :                  "File %s must contain at least 1 attribute field.",
     928             :                  m_pszFname);
     929           0 :         return -1;
     930             :     }
     931             : 
     932             :     /*-----------------------------------------------------------------
     933             :      * Start writing header.
     934             :      *----------------------------------------------------------------*/
     935          84 :     m_bHeaderWrote = TRUE;
     936          84 :     m_poMIFFile->WriteLine("Version %d\n", m_nVersion);
     937          84 :     m_poMIFFile->WriteLine("Charset \"%s\"\n", m_pszCharset);
     938             : 
     939             :     // Delimiter is not required if you use \t as delimiter
     940          84 :     if (!EQUAL(m_pszDelimiter, "\t"))
     941          84 :         m_poMIFFile->WriteLine("Delimiter \"%s\"\n", m_pszDelimiter);
     942             : 
     943          84 :     bFound = FALSE;
     944             : 
     945         189 :     for (int iField = 0; iField < m_poDefn->GetFieldCount(); iField++)
     946             :     {
     947         105 :         if (m_pabFieldUnique[iField])
     948             :         {
     949           0 :             if (!bFound)
     950           0 :                 m_poMIFFile->WriteLine("Unique %d", iField + 1);
     951             :             else
     952           0 :                 m_poMIFFile->WriteLine(",%d", iField + 1);
     953           0 :             bFound = TRUE;
     954             :         }
     955             :     }
     956          84 :     if (bFound)
     957           0 :         m_poMIFFile->WriteLine("\n");
     958             : 
     959          84 :     bFound = FALSE;
     960         189 :     for (int iField = 0; iField < m_poDefn->GetFieldCount(); iField++)
     961             :     {
     962         105 :         if (m_pabFieldIndexed[iField])
     963             :         {
     964           0 :             if (!bFound)
     965           0 :                 m_poMIFFile->WriteLine("Index  %d", iField + 1);
     966             :             else
     967           0 :                 m_poMIFFile->WriteLine(",%d", iField + 1);
     968           0 :             bFound = TRUE;
     969             :         }
     970             :     }
     971          84 :     if (bFound)
     972           0 :         m_poMIFFile->WriteLine("\n");
     973             : 
     974          84 :     if (m_pszCoordSys && m_bBoundsSet)
     975             :     {
     976           6 :         m_poMIFFile->WriteLine("CoordSys %s "
     977             :                                "Bounds (%.15g, %.15g) (%.15g, %.15g)\n",
     978             :                                m_pszCoordSys, m_dXMin, m_dYMin, m_dXMax,
     979             :                                m_dYMax);
     980             :     }
     981          78 :     else if (m_pszCoordSys)
     982             :     {
     983          65 :         m_poMIFFile->WriteLine("CoordSys %s\n", m_pszCoordSys);
     984             :     }
     985             : 
     986             :     /*-----------------------------------------------------------------
     987             :      * Column definitions
     988             :      *----------------------------------------------------------------*/
     989          84 :     CPLAssert(m_paeFieldType);
     990             : 
     991          84 :     m_poMIFFile->WriteLine("Columns %d\n", m_poDefn->GetFieldCount());
     992             : 
     993         189 :     for (int iField = 0; iField < m_poDefn->GetFieldCount(); iField++)
     994             :     {
     995         105 :         OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField);
     996         210 :         CPLString osFieldName(poFieldDefn->GetNameRef());
     997             : 
     998         105 :         if (strlen(GetEncoding()) > 0)
     999           6 :             osFieldName.Recode(CPL_ENC_UTF8, GetEncoding());
    1000             : 
    1001         105 :         char *pszCleanName = TABCleanFieldName(osFieldName);
    1002         105 :         osFieldName = pszCleanName;
    1003         105 :         CPLFree(pszCleanName);
    1004             : 
    1005         105 :         switch (m_paeFieldType[iField])
    1006             :         {
    1007          17 :             case TABFInteger:
    1008          17 :                 m_poMIFFile->WriteLine("  %s Integer\n", osFieldName.c_str());
    1009          17 :                 break;
    1010           0 :             case TABFSmallInt:
    1011           0 :                 m_poMIFFile->WriteLine("  %s SmallInt\n", osFieldName.c_str());
    1012           0 :                 break;
    1013           1 :             case TABFLargeInt:
    1014           1 :                 m_poMIFFile->WriteLine("  %s LargeInt\n", osFieldName.c_str());
    1015           1 :                 break;
    1016           5 :             case TABFFloat:
    1017           5 :                 m_poMIFFile->WriteLine("  %s Float\n", osFieldName.c_str());
    1018           5 :                 break;
    1019           3 :             case TABFDecimal:
    1020           3 :                 m_poMIFFile->WriteLine(
    1021             :                     "  %s Decimal(%d,%d)\n", osFieldName.c_str(),
    1022             :                     poFieldDefn->GetWidth(), poFieldDefn->GetPrecision());
    1023           3 :                 break;
    1024           0 :             case TABFLogical:
    1025           0 :                 m_poMIFFile->WriteLine("  %s Logical\n", osFieldName.c_str());
    1026           0 :                 break;
    1027           1 :             case TABFDate:
    1028           1 :                 m_poMIFFile->WriteLine("  %s Date\n", osFieldName.c_str());
    1029           1 :                 break;
    1030           1 :             case TABFTime:
    1031           1 :                 m_poMIFFile->WriteLine("  %s Time\n", osFieldName.c_str());
    1032           1 :                 break;
    1033           1 :             case TABFDateTime:
    1034           1 :                 m_poMIFFile->WriteLine("  %s DateTime\n", osFieldName.c_str());
    1035           1 :                 break;
    1036          76 :             case TABFChar:
    1037             :             default:
    1038          76 :                 m_poMIFFile->WriteLine("  %s Char(%d)\n", osFieldName.c_str(),
    1039             :                                        poFieldDefn->GetWidth());
    1040             :         }
    1041             :     }
    1042             : 
    1043             :     /*-----------------------------------------------------------------
    1044             :      * Ready to write objects
    1045             :      *----------------------------------------------------------------*/
    1046          84 :     m_poMIFFile->WriteLine("Data\n\n");
    1047             : 
    1048          84 :     return 0;
    1049             : }
    1050             : 
    1051             : /**********************************************************************
    1052             :  *                   MIFFile::Close()
    1053             :  *
    1054             :  * Close current file, and release all memory used.
    1055             :  *
    1056             :  * Returns 0 on success, -1 on error.
    1057             :  **********************************************************************/
    1058        1012 : int MIFFile::Close()
    1059             : {
    1060             :     /* flush .mif header if not already written */
    1061        1012 :     if (m_poDefn != nullptr && m_bHeaderWrote == FALSE &&
    1062         873 :         m_eAccessMode != TABRead)
    1063             :     {
    1064           4 :         WriteMIFHeader();
    1065             :     }
    1066             : 
    1067        1012 :     if (m_poMIDFile)
    1068             :     {
    1069         173 :         m_poMIDFile->Close();
    1070         173 :         delete m_poMIDFile;
    1071         173 :         m_poMIDFile = nullptr;
    1072             :     }
    1073             : 
    1074        1012 :     if (m_poMIFFile)
    1075             :     {
    1076         953 :         m_poMIFFile->Close();
    1077         953 :         delete m_poMIFFile;
    1078         953 :         m_poMIFFile = nullptr;
    1079             :     }
    1080             : 
    1081        1012 :     if (m_poCurFeature)
    1082             :     {
    1083           0 :         delete m_poCurFeature;
    1084           0 :         m_poCurFeature = nullptr;
    1085             :     }
    1086             : 
    1087             :     /*-----------------------------------------------------------------
    1088             :      * Note: we have to check the reference count before deleting
    1089             :      * m_poSpatialRef and m_poDefn
    1090             :      *----------------------------------------------------------------*/
    1091        1012 :     if (m_poDefn && m_poDefn->Dereference() == 0)
    1092         157 :         delete m_poDefn;
    1093        1012 :     m_poDefn = nullptr;
    1094             : 
    1095        1012 :     if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0)
    1096          59 :         delete m_poSpatialRef;
    1097        1012 :     m_poSpatialRef = nullptr;
    1098             : 
    1099        1012 :     CPLFree(m_pszCoordSys);
    1100        1012 :     m_pszCoordSys = nullptr;
    1101             : 
    1102        1012 :     CPLFree(m_pszDelimiter);
    1103        1012 :     m_pszDelimiter = nullptr;
    1104             : 
    1105        1012 :     CPLFree(m_pszUnique);
    1106        1012 :     m_pszUnique = nullptr;
    1107             : 
    1108        1012 :     CPLFree(m_pszFname);
    1109        1012 :     m_pszFname = nullptr;
    1110             : 
    1111        1012 :     m_nVersion = 0;
    1112             : 
    1113        1012 :     CPLFree(m_pszCharset);
    1114        1012 :     m_pszCharset = nullptr;
    1115             : 
    1116        1012 :     CPLFree(m_pabFieldIndexed);
    1117        1012 :     m_pabFieldIndexed = nullptr;
    1118        1012 :     CPLFree(m_pabFieldUnique);
    1119        1012 :     m_pabFieldUnique = nullptr;
    1120             : 
    1121        1012 :     CPLFree(m_pszIndex);
    1122        1012 :     m_pszIndex = nullptr;
    1123             : 
    1124        1012 :     CPLFree(m_paeFieldType);
    1125        1012 :     m_paeFieldType = nullptr;
    1126             : 
    1127        1012 :     m_nCurFeatureId = 0;
    1128        1012 :     m_nPreloadedId = 0;
    1129        1012 :     m_nFeatureCount = 0;
    1130             : 
    1131        1012 :     m_bBoundsSet = FALSE;
    1132             : 
    1133        1012 :     return 0;
    1134             : }
    1135             : 
    1136             : /**********************************************************************
    1137             :  *                   MIFFile::GetNextFeatureId()
    1138             :  *
    1139             :  * Returns feature id that follows nPrevId, or -1 if it is the
    1140             :  * last feature id.  Pass nPrevId=-1 to fetch the first valid feature id.
    1141             :  **********************************************************************/
    1142        7938 : GIntBig MIFFile::GetNextFeatureId(GIntBig nPrevId)
    1143             : {
    1144        7938 :     if (m_eAccessMode != TABRead)
    1145             :     {
    1146           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1147             :                  "GetNextFeatureId() can be used only with Read access.");
    1148           0 :         return -1;
    1149             :     }
    1150             : 
    1151        7938 :     if (nPrevId <= 0 && m_poMIFFile->GetLastLine() != nullptr)
    1152         776 :         return 1;  // Feature Ids start at 1
    1153        7162 :     else if (nPrevId > 0 && m_poMIFFile->GetLastLine() != nullptr)
    1154        6742 :         return nPrevId + 1;
    1155             :     else
    1156         420 :         return -1;
    1157             : }
    1158             : 
    1159             : /**********************************************************************
    1160             :  *                   MIFFile::GotoFeature()
    1161             :  *
    1162             :  * Private method to move MIF and MID pointers ready to read specified
    1163             :  * feature.  Note that Feature Ids start at 1.
    1164             :  *
    1165             :  * Returns 0 on success, -1 on error (likely request for invalid feature id)
    1166             :  **********************************************************************/
    1167        7525 : int MIFFile::GotoFeature(int nFeatureId)
    1168             : {
    1169             : 
    1170        7525 :     if (nFeatureId < 1)
    1171           1 :         return -1;
    1172             : 
    1173        7524 :     if (nFeatureId == m_nPreloadedId)  // CorrectPosition
    1174             :     {
    1175        7498 :         return 0;
    1176             :     }
    1177             :     else
    1178             :     {
    1179          26 :         if (nFeatureId < m_nPreloadedId || m_nCurFeatureId == 0)
    1180          24 :             ResetReading();
    1181             : 
    1182          31 :         while (m_nPreloadedId < nFeatureId)
    1183             :         {
    1184             :             const char *pszLine;
    1185         136 :             while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
    1186             :             {
    1187         135 :                 if (m_poMIFFile->IsValidFeature(pszLine))
    1188             :                 {
    1189           5 :                     m_nPreloadedId++;
    1190           5 :                     if (m_poMIDFile != nullptr)
    1191           5 :                         CSLDestroy(m_poMIDFile->GetTokenizedNextLine());
    1192           5 :                     break;
    1193             :                 }
    1194             :             }
    1195           6 :             if (pszLine == nullptr)
    1196           1 :                 return -1;
    1197             :         }
    1198             : 
    1199          25 :         CPLAssert(m_nPreloadedId == nFeatureId);
    1200             : 
    1201          25 :         return 0;
    1202             :     }
    1203             : }
    1204             : 
    1205             : /**********************************************************************
    1206             :  *                   MIFFile::GetFeatureRef()
    1207             :  *
    1208             :  * Fill and return a TABFeature object for the specified feature id.
    1209             :  *
    1210             :  * The returned pointer is a reference to an object owned and maintained
    1211             :  * by this MIFFile object.  It should not be altered or freed by the
    1212             :  * caller and its contents is guaranteed to be valid only until the next
    1213             :  * call to GetFeatureRef() or Close().
    1214             :  *
    1215             :  * Returns NULL if the specified feature id does not exist of if an
    1216             :  * error happened.  In any case, CPLError() will have been called to
    1217             :  * report the reason of the failure.
    1218             :  **********************************************************************/
    1219        7526 : TABFeature *MIFFile::GetFeatureRef(GIntBig nFeatureId)
    1220             : {
    1221        7526 :     if (m_eAccessMode != TABRead)
    1222             :     {
    1223           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1224             :                  "GetFeatureRef() can be used only with Read access.");
    1225           0 :         return nullptr;
    1226             :     }
    1227             : 
    1228             :     /*-----------------------------------------------------------------
    1229             :      * Make sure file is opened and Validate feature id by positioning
    1230             :      * the read pointers for the .MAP and .DAT files to this feature id.
    1231             :      *----------------------------------------------------------------*/
    1232        7526 :     if (m_poMIFFile == nullptr)
    1233             :     {
    1234           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1235             :                  "GetFeatureRef() failed: file is not opened!");
    1236           0 :         return nullptr;
    1237             :     }
    1238             : 
    1239       15051 :     if (!CPL_INT64_FITS_ON_INT32(nFeatureId) ||
    1240        7525 :         GotoFeature(static_cast<int>(nFeatureId)) != 0)
    1241             :     {
    1242           3 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1243             :                  "GetFeatureRef() failed: invalid feature id " CPL_FRMT_GIB,
    1244             :                  nFeatureId);
    1245           3 :         return nullptr;
    1246             :     }
    1247             : 
    1248             :     /*-----------------------------------------------------------------
    1249             :      * Create new feature object of the right type
    1250             :      *----------------------------------------------------------------*/
    1251        7523 :     const char *pszLine = nullptr;
    1252        7523 :     if ((pszLine = m_poMIFFile->GetLastLine()) != nullptr)
    1253             :     {
    1254             :         // Delete previous feature... we'll start we a clean one.
    1255        7523 :         if (m_poCurFeature)
    1256          79 :             delete m_poCurFeature;
    1257        7523 :         m_poCurFeature = nullptr;
    1258             : 
    1259        7523 :         m_nCurFeatureId = m_nPreloadedId;
    1260             : 
    1261        7523 :         if (STARTS_WITH_CI(pszLine, "NONE"))
    1262             :         {
    1263          17 :             m_poCurFeature = new TABFeature(m_poDefn);
    1264             :         }
    1265        7506 :         else if (STARTS_WITH_CI(pszLine, "POINT"))
    1266             :         {
    1267             :             // Special case, we need to know two lines to decide the type
    1268             :             char **papszToken =
    1269        2092 :                 CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS);
    1270             : 
    1271        2092 :             if (CSLCount(papszToken) != 3)
    1272             :             {
    1273          12 :                 CSLDestroy(papszToken);
    1274          12 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1275             :                          "GetFeatureRef() failed: invalid point line: '%s'",
    1276             :                          pszLine);
    1277          12 :                 return nullptr;
    1278             :             }
    1279             : 
    1280        2080 :             m_poMIFFile->SaveLine(pszLine);
    1281             : 
    1282        2080 :             if ((pszLine = m_poMIFFile->GetLine()) != nullptr)
    1283             :             {
    1284        2074 :                 CSLDestroy(papszToken);
    1285             :                 papszToken =
    1286        2074 :                     CSLTokenizeStringComplex(pszLine, " ,()\t", TRUE, FALSE);
    1287        3441 :                 if (CSLCount(papszToken) > 0 &&
    1288        1367 :                     STARTS_WITH_CI(papszToken[0], "SYMBOL"))
    1289             :                 {
    1290        1357 :                     switch (CSLCount(papszToken))
    1291             :                     {
    1292          25 :                         case 4:
    1293          25 :                             m_poCurFeature = new TABPoint(m_poDefn);
    1294          25 :                             break;
    1295         635 :                         case 7:
    1296         635 :                             m_poCurFeature = new TABFontPoint(m_poDefn);
    1297         635 :                             break;
    1298         678 :                         case 5:
    1299         678 :                             m_poCurFeature = new TABCustomPoint(m_poDefn);
    1300         678 :                             break;
    1301          19 :                         default:
    1302          19 :                             CSLDestroy(papszToken);
    1303          19 :                             CPLError(CE_Failure, CPLE_NotSupported,
    1304             :                                      "GetFeatureRef() failed: invalid symbol "
    1305             :                                      "line: '%s'",
    1306             :                                      pszLine);
    1307          19 :                             return nullptr;
    1308             :                             break;
    1309             :                     }
    1310             :                 }
    1311             :             }
    1312        2061 :             CSLDestroy(papszToken);
    1313             : 
    1314        2061 :             if (m_poCurFeature == nullptr)
    1315             :             {
    1316             :                 // No symbol clause... default to TABPoint
    1317         723 :                 m_poCurFeature = new TABPoint(m_poDefn);
    1318             :             }
    1319             :         }
    1320        5414 :         else if (STARTS_WITH_CI(pszLine, "LINE") ||
    1321        4789 :                  STARTS_WITH_CI(pszLine, "PLINE"))
    1322             :         {
    1323        2391 :             m_poCurFeature = new TABPolyline(m_poDefn);
    1324             :         }
    1325        3023 :         else if (STARTS_WITH_CI(pszLine, "REGION"))
    1326             :         {
    1327         454 :             m_poCurFeature = new TABRegion(m_poDefn);
    1328             :         }
    1329        2569 :         else if (STARTS_WITH_CI(pszLine, "ARC"))
    1330             :         {
    1331         672 :             m_poCurFeature = new TABArc(m_poDefn);
    1332             :         }
    1333        1897 :         else if (STARTS_WITH_CI(pszLine, "TEXT"))
    1334             :         {
    1335         297 :             m_poCurFeature = new TABText(m_poDefn);
    1336             :         }
    1337        1600 :         else if (STARTS_WITH_CI(pszLine, "RECT") ||
    1338        1105 :                  STARTS_WITH_CI(pszLine, "ROUNDRECT"))
    1339             :         {
    1340         941 :             m_poCurFeature = new TABRectangle(m_poDefn);
    1341             :         }
    1342         659 :         else if (STARTS_WITH_CI(pszLine, "ELLIPSE"))
    1343             :         {
    1344         397 :             m_poCurFeature = new TABEllipse(m_poDefn);
    1345             :         }
    1346         262 :         else if (STARTS_WITH_CI(pszLine, "MULTIPOINT"))
    1347             :         {
    1348         163 :             m_poCurFeature = new TABMultiPoint(m_poDefn);
    1349             :         }
    1350          99 :         else if (STARTS_WITH_CI(pszLine, "COLLECTION"))
    1351             :         {
    1352          99 :             m_poCurFeature = new TABCollection(m_poDefn);
    1353             :         }
    1354             :         else
    1355             :         {
    1356           0 :             if (!EQUAL(pszLine, ""))
    1357           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1358             :                          "Error during reading, unknown type %s.", pszLine);
    1359             : 
    1360             :             // m_poCurFeature = new TABDebugFeature(m_poDefn);
    1361           0 :             return nullptr;
    1362             :         }
    1363             :     }
    1364             : 
    1365        7492 :     CPLAssert(m_poCurFeature);
    1366        7492 :     if (m_poCurFeature == nullptr)
    1367           0 :         return nullptr;
    1368             : 
    1369             :     /*-----------------------------------------------------------------
    1370             :      * Read fields from the .DAT file
    1371             :      * GetRecordBlock() has already been called above...
    1372             :      *----------------------------------------------------------------*/
    1373        7843 :     if (m_poMIDFile != nullptr &&
    1374         351 :         m_poCurFeature->ReadRecordFromMIDFile(m_poMIDFile) != 0)
    1375             :     {
    1376           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Error during reading Record.");
    1377             : 
    1378           0 :         delete m_poCurFeature;
    1379           0 :         m_poCurFeature = nullptr;
    1380           0 :         return nullptr;
    1381             :     }
    1382             : 
    1383             :     /*-----------------------------------------------------------------
    1384             :      * Read geometry from the .MAP file
    1385             :      * MoveToObjId() has already been called above...
    1386             :      *----------------------------------------------------------------*/
    1387        7492 :     if (m_poCurFeature->ReadGeometryFromMIFFile(m_poMIFFile) != 0)
    1388             :     {
    1389         297 :         CPLError(CE_Failure, CPLE_NotSupported,
    1390             :                  "Error during reading Geometry.");
    1391             : 
    1392         297 :         delete m_poCurFeature;
    1393         297 :         m_poCurFeature = nullptr;
    1394         297 :         return nullptr;
    1395             :     }
    1396             : 
    1397             :     /* If the feature geometry is Text, and the value is empty(""), transform
    1398             :        it to a geometry none */
    1399        7195 :     if (m_poCurFeature->GetFeatureClass() == TABFCText)
    1400             :     {
    1401         280 :         TABText *poTextFeature = cpl::down_cast<TABText *>(m_poCurFeature);
    1402         280 :         if (strlen(poTextFeature->GetTextString()) == 0)
    1403             :         {
    1404           0 :             TABFeature *poTmpFeature = new TABFeature(m_poDefn);
    1405           0 :             for (int i = 0; i < m_poDefn->GetFieldCount(); i++)
    1406             :             {
    1407           0 :                 poTmpFeature->SetField(i, m_poCurFeature->GetRawFieldRef(i));
    1408             :             }
    1409           0 :             delete m_poCurFeature;
    1410           0 :             m_poCurFeature = poTmpFeature;
    1411             :         }
    1412             :     }
    1413             : 
    1414             :     /*---------------------------------------------------------------------
    1415             :      * The act of reading the geometry causes the first line of the
    1416             :      * next object to be preloaded.  Set the preloaded id appropriately.
    1417             :      *--------------------------------------------------------------------- */
    1418        7195 :     if (m_poMIFFile->GetLastLine() != nullptr)
    1419        6762 :         m_nPreloadedId++;
    1420             :     else
    1421         433 :         m_nPreloadedId = 0;
    1422             : 
    1423             :     /* Update the Current Feature ID */
    1424        7195 :     m_poCurFeature->SetFID(m_nCurFeatureId);
    1425             : 
    1426        7195 :     return m_poCurFeature;
    1427             : }
    1428             : 
    1429             : /**********************************************************************
    1430             :  *                   MIFFile::CreateFeature()
    1431             :  *
    1432             :  * Write a new feature to this dataset. The passed in feature is updated
    1433             :  * with the new feature id.
    1434             :  *
    1435             :  * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
    1436             :  * error happened in which case, CPLError() will have been called to
    1437             :  * report the reason of the failure.
    1438             :  **********************************************************************/
    1439         114 : OGRErr MIFFile::CreateFeature(TABFeature *poFeature)
    1440             : {
    1441         114 :     int nFeatureId = -1;
    1442             : 
    1443         114 :     if (m_eAccessMode != TABWrite)
    1444             :     {
    1445           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1446             :                  "CreateFeature() can be used only with Write access.");
    1447           0 :         return OGRERR_UNSUPPORTED_OPERATION;
    1448             :     }
    1449             : 
    1450             :     /*-----------------------------------------------------------------
    1451             :      * Make sure file is opened and establish new feature id.
    1452             :      *----------------------------------------------------------------*/
    1453         114 :     if (m_poMIDFile == nullptr)
    1454             :     {
    1455           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1456             :                  "CreateFeature() failed: file is not opened!");
    1457           0 :         return OGRERR_FAILURE;
    1458             :     }
    1459             : 
    1460         114 :     if (m_bHeaderWrote == FALSE)
    1461             :     {
    1462             :         /*-------------------------------------------------------------
    1463             :          * OK, this is the first feature in the dataset... make sure the
    1464             :          * .MID schema has been initialized.
    1465             :          *------------------------------------------------------------*/
    1466          80 :         if (m_poDefn == nullptr)
    1467           0 :             SetFeatureDefn(poFeature->GetDefnRef(), nullptr);
    1468             : 
    1469          80 :         WriteMIFHeader();
    1470          80 :         nFeatureId = 1;
    1471             :     }
    1472             :     else
    1473             :     {
    1474          34 :         nFeatureId = ++m_nWriteFeatureId;
    1475             :     }
    1476             : 
    1477             :     /*-----------------------------------------------------------------
    1478             :      * Write geometry to the .Mif file
    1479             :      *----------------------------------------------------------------*/
    1480         228 :     if (m_poMIFFile == nullptr ||
    1481         114 :         poFeature->WriteGeometryToMIFFile(m_poMIFFile) != 0)
    1482             :     {
    1483           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1484             :                  "Failed writing geometry for feature id %d in %s", nFeatureId,
    1485             :                  m_pszFname);
    1486           0 :         return OGRERR_FAILURE;
    1487             :     }
    1488             : 
    1489         228 :     if (m_poMIDFile == nullptr ||
    1490         114 :         poFeature->WriteRecordToMIDFile(m_poMIDFile) != 0)
    1491             :     {
    1492           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1493             :                  "Failed writing attributes for feature id %d in %s",
    1494             :                  nFeatureId, m_pszFname);
    1495           0 :         return OGRERR_FAILURE;
    1496             :     }
    1497             : 
    1498         114 :     poFeature->SetFID(nFeatureId);
    1499             : 
    1500         114 :     return OGRERR_NONE;
    1501             : }
    1502             : 
    1503             : /**********************************************************************
    1504             :  *                   MIFFile::GetLayerDefn()
    1505             :  *
    1506             :  * Returns a reference to the OGRFeatureDefn that will be used to create
    1507             :  * features in this dataset.
    1508             :  *
    1509             :  * Returns a reference to an object that is maintained by this MIFFile
    1510             :  * object (and thus should not be modified or freed by the caller) or
    1511             :  * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
    1512             :  * opened yet)
    1513             :  **********************************************************************/
    1514        1677 : OGRFeatureDefn *MIFFile::GetLayerDefn()
    1515             : {
    1516        1677 :     return m_poDefn;
    1517             : }
    1518             : 
    1519             : /**********************************************************************
    1520             :  *                   MIFFile::SetFeatureDefn()
    1521             :  *
    1522             :  * Pass a reference to the OGRFeatureDefn that will be used to create
    1523             :  * features in this dataset.  This function should be called after
    1524             :  * creating a new dataset, but before writing the first feature.
    1525             :  * All features that will be written to this dataset must share this same
    1526             :  * OGRFeatureDefn.
    1527             :  *
    1528             :  * This function will use poFeatureDefn to create a local copy that
    1529             :  * will be used to build the .MID file, etc.
    1530             :  *
    1531             :  * Returns 0 on success, -1 on error.
    1532             :  **********************************************************************/
    1533           0 : int MIFFile::SetFeatureDefn(
    1534             :     OGRFeatureDefn *poFeatureDefn,
    1535             :     TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
    1536             : {
    1537             :     /*-----------------------------------------------------------------
    1538             :      * Check that call happens at the right time in dataset's life.
    1539             :      *----------------------------------------------------------------*/
    1540           0 :     if (m_eAccessMode == TABWrite && m_bHeaderWrote)
    1541             :     {
    1542           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1543             :                  "SetFeatureDefn() must be called after opening a new "
    1544             :                  "dataset, but before writing the first feature to it.");
    1545           0 :         return -1;
    1546             :     }
    1547             : 
    1548             :     /*-----------------------------------------------------------------
    1549             :      * Delete current feature defn if there is already one.
    1550             :      * AddFieldNative() will take care of creating a new one for us.
    1551             :      *----------------------------------------------------------------*/
    1552           0 :     if (m_poDefn && m_poDefn->Dereference() == 0)
    1553           0 :         delete m_poDefn;
    1554           0 :     m_poDefn = nullptr;
    1555             : 
    1556             :     /*-----------------------------------------------------------------
    1557             :      * Copy field information
    1558             :      *----------------------------------------------------------------*/
    1559           0 :     const int numFields = poFeatureDefn->GetFieldCount();
    1560           0 :     int nStatus = 0;
    1561             : 
    1562           0 :     for (int iField = 0; iField < numFields; iField++)
    1563             :     {
    1564             :         TABFieldType eMapInfoType;
    1565           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1566             : 
    1567           0 :         if (paeMapInfoNativeFieldTypes)
    1568             :         {
    1569           0 :             eMapInfoType = paeMapInfoNativeFieldTypes[iField];
    1570             :         }
    1571             :         else
    1572             :         {
    1573             :             /*---------------------------------------------------------
    1574             :              * Map OGRFieldTypes to MapInfo native types
    1575             :              *--------------------------------------------------------*/
    1576           0 :             switch (poFieldDefn->GetType())
    1577             :             {
    1578           0 :                 case OFTInteger:
    1579           0 :                     eMapInfoType = TABFInteger;
    1580           0 :                     break;
    1581           0 :                 case OFTReal:
    1582           0 :                     eMapInfoType = TABFFloat;
    1583           0 :                     break;
    1584           0 :                 case OFTDateTime:
    1585           0 :                     eMapInfoType = TABFDateTime;
    1586           0 :                     break;
    1587           0 :                 case OFTDate:
    1588           0 :                     eMapInfoType = TABFDate;
    1589           0 :                     break;
    1590           0 :                 case OFTTime:
    1591           0 :                     eMapInfoType = TABFTime;
    1592           0 :                     break;
    1593           0 :                 case OFTString:
    1594             :                 default:
    1595           0 :                     eMapInfoType = TABFChar;
    1596             :             }
    1597             :         }
    1598             : 
    1599           0 :         nStatus = AddFieldNative(poFieldDefn->GetNameRef(), eMapInfoType,
    1600             :                                  poFieldDefn->GetWidth(),
    1601             :                                  poFieldDefn->GetPrecision(), FALSE, FALSE);
    1602             :     }
    1603             : 
    1604           0 :     return nStatus;
    1605             : }
    1606             : 
    1607             : /**********************************************************************
    1608             :  *                   MIFFile::AddFieldNative()
    1609             :  *
    1610             :  * Create a new field using a native mapinfo data type... this is an
    1611             :  * alternative to defining fields through the OGR interface.
    1612             :  * This function should be called after creating a new dataset, but before
    1613             :  * writing the first feature.
    1614             :  *
    1615             :  * This function will build/update the OGRFeatureDefn that will have to be
    1616             :  * used when writing features to this dataset.
    1617             :  *
    1618             :  * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
    1619             :  *
    1620             :  * Returns 0 on success, -1 on error.
    1621             :  **********************************************************************/
    1622         258 : int MIFFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
    1623             :                             int nWidth /*=0*/, int nPrecision /*=0*/,
    1624             :                             GBool bIndexed /*=FALSE*/, GBool bUnique /*=FALSE*/,
    1625             :                             int /*bApproxOK*/)
    1626             : {
    1627             :     /*-----------------------------------------------------------------
    1628             :      * Check that call happens at the right time in dataset's life.
    1629             :      *----------------------------------------------------------------*/
    1630         258 :     if (m_eAccessMode == TABWrite && m_bHeaderWrote)
    1631             :     {
    1632           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1633             :                  "AddFieldNative() must be called after opening a new "
    1634             :                  "dataset, but before writing the first feature to it.");
    1635           0 :         return -1;
    1636             :     }
    1637             : 
    1638             :     /*-----------------------------------------------------------------
    1639             :      * Validate field width... must be <= 254
    1640             :      *----------------------------------------------------------------*/
    1641         258 :     if (nWidth > 254)
    1642             :     {
    1643           0 :         CPLError(CE_Warning, CPLE_IllegalArg,
    1644             :                  "Invalid size (%d) for field '%s'.  "
    1645             :                  "Size must be 254 or less.",
    1646             :                  nWidth, pszName);
    1647           0 :         nWidth = 254;
    1648             :     }
    1649             : 
    1650             :     /*-----------------------------------------------------------------
    1651             :      * Map fields with width=0 (variable length in OGR) to a valid default
    1652             :      *----------------------------------------------------------------*/
    1653         258 :     if (eMapInfoType == TABFDecimal && nWidth == 0)
    1654           0 :         nWidth = 20;
    1655         258 :     else if (eMapInfoType == TABFChar && nWidth == 0)
    1656           0 :         nWidth = 254; /* char fields */
    1657             : 
    1658             :     /*-----------------------------------------------------------------
    1659             :      * Create new OGRFeatureDefn if not done yet...
    1660             :      *----------------------------------------------------------------*/
    1661         258 :     if (m_poDefn == nullptr)
    1662             :     {
    1663           0 :         char *pszFeatureClassName = TABGetBasename(m_pszFname);
    1664           0 :         m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
    1665           0 :         CPLFree(pszFeatureClassName);
    1666             :         // Ref count defaults to 0... set it to 1
    1667           0 :         m_poDefn->Reference();
    1668           0 :         m_poDefn->Seal(/* bSealFields = */ true);
    1669             :     }
    1670             : 
    1671         516 :     CPLString osName(NormalizeFieldName(pszName));
    1672             : 
    1673             :     /*-----------------------------------------------------------------
    1674             :      * Map MapInfo native types to OGR types
    1675             :      *----------------------------------------------------------------*/
    1676         258 :     OGRFieldDefn *poFieldDefn = nullptr;
    1677             : 
    1678         258 :     switch (eMapInfoType)
    1679             :     {
    1680         174 :         case TABFChar:
    1681             :             /*-------------------------------------------------
    1682             :              * CHAR type
    1683             :              *------------------------------------------------*/
    1684         174 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTString);
    1685         174 :             poFieldDefn->SetWidth(nWidth);
    1686         174 :             break;
    1687          47 :         case TABFInteger:
    1688             :             /*-------------------------------------------------
    1689             :              * INTEGER type
    1690             :              *------------------------------------------------*/
    1691          47 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
    1692          47 :             poFieldDefn->SetWidth(nWidth);
    1693          47 :             break;
    1694           1 :         case TABFSmallInt:
    1695             :             /*-------------------------------------------------
    1696             :              * SMALLINT type
    1697             :              *------------------------------------------------*/
    1698           1 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
    1699           1 :             poFieldDefn->SetWidth(nWidth);
    1700           1 :             break;
    1701           2 :         case TABFLargeInt:
    1702             :             /*-------------------------------------------------
    1703             :              * LargeInt type
    1704             :              *------------------------------------------------*/
    1705           2 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger64);
    1706           2 :             poFieldDefn->SetWidth(nWidth);
    1707           2 :             break;
    1708           6 :         case TABFDecimal:
    1709             :             /*-------------------------------------------------
    1710             :              * DECIMAL type
    1711             :              *------------------------------------------------*/
    1712           6 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal);
    1713           6 :             poFieldDefn->SetWidth(nWidth);
    1714           6 :             poFieldDefn->SetPrecision(nPrecision);
    1715           6 :             break;
    1716          20 :         case TABFFloat:
    1717             :             /*-------------------------------------------------
    1718             :              * FLOAT type
    1719             :              *------------------------------------------------*/
    1720          20 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal);
    1721          20 :             break;
    1722           2 :         case TABFDate:
    1723             :             /*-------------------------------------------------
    1724             :              * DATE type (V450, returned as a string: "DD/MM/YYYY" or
    1725             :              *"YYYYMMDD")
    1726             :              *------------------------------------------------*/
    1727           2 :             poFieldDefn = new OGRFieldDefn(osName.c_str(),
    1728             : #ifdef MITAB_USE_OFTDATETIME
    1729           2 :                                            OFTDate);
    1730             : #else
    1731             :                                            OFTString);
    1732             : #endif
    1733           2 :             poFieldDefn->SetWidth(10);
    1734           2 :             m_nVersion = std::max(m_nVersion, 450);
    1735           2 :             break;
    1736           2 :         case TABFTime:
    1737             :             /*-------------------------------------------------
    1738             :              * TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm")
    1739             :              *------------------------------------------------*/
    1740           2 :             poFieldDefn = new OGRFieldDefn(osName.c_str(),
    1741             : #ifdef MITAB_USE_OFTDATETIME
    1742           2 :                                            OFTTime);
    1743             : #else
    1744             :                                            OFTString);
    1745             : #endif
    1746           2 :             poFieldDefn->SetWidth(9);
    1747           2 :             m_nVersion = std::max(m_nVersion, 900);
    1748           2 :             break;
    1749           2 :         case TABFDateTime:
    1750             :             /*-------------------------------------------------
    1751             :              * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS",
    1752             :              * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm")
    1753             :              *------------------------------------------------*/
    1754           2 :             poFieldDefn = new OGRFieldDefn(osName.c_str(),
    1755             : #ifdef MITAB_USE_OFTDATETIME
    1756           2 :                                            OFTDateTime);
    1757             : #else
    1758             :                                            OFTString);
    1759             : #endif
    1760           2 :             poFieldDefn->SetWidth(19);
    1761           2 :             m_nVersion = std::max(m_nVersion, 900);
    1762           2 :             break;
    1763           2 :         case TABFLogical:
    1764             :             /*-------------------------------------------------
    1765             :              * LOGICAL type (value "T" or "F")
    1766             :              *------------------------------------------------*/
    1767           2 :             poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTString);
    1768           2 :             poFieldDefn->SetWidth(1);
    1769           2 :             break;
    1770           0 :         default:
    1771           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1772             :                      "Unsupported type for field %s", pszName);
    1773           0 :             return -1;
    1774             :     }
    1775             : 
    1776             :     /*-----------------------------------------------------
    1777             :      * Add the FieldDefn to the FeatureDefn
    1778             :      *----------------------------------------------------*/
    1779         258 :     whileUnsealing(m_poDefn)->AddFieldDefn(poFieldDefn);
    1780         258 :     m_oSetFields.insert(CPLString(poFieldDefn->GetNameRef()).toupper());
    1781         258 :     delete poFieldDefn;
    1782             : 
    1783             :     /*-----------------------------------------------------------------
    1784             :      * Keep track of native field type
    1785             :      *----------------------------------------------------------------*/
    1786         516 :     m_paeFieldType = static_cast<TABFieldType *>(CPLRealloc(
    1787         258 :         m_paeFieldType, m_poDefn->GetFieldCount() * sizeof(TABFieldType)));
    1788         258 :     m_paeFieldType[m_poDefn->GetFieldCount() - 1] = eMapInfoType;
    1789             : 
    1790             :     /*-----------------------------------------------------------------
    1791             :      * Extend array of Indexed/Unique flags
    1792             :      *----------------------------------------------------------------*/
    1793         516 :     m_pabFieldIndexed = static_cast<GBool *>(CPLRealloc(
    1794         258 :         m_pabFieldIndexed, m_poDefn->GetFieldCount() * sizeof(GBool)));
    1795         516 :     m_pabFieldUnique = static_cast<GBool *>(CPLRealloc(
    1796         258 :         m_pabFieldUnique, m_poDefn->GetFieldCount() * sizeof(GBool)));
    1797         258 :     m_pabFieldIndexed[m_poDefn->GetFieldCount() - 1] = bIndexed;
    1798         258 :     m_pabFieldUnique[m_poDefn->GetFieldCount() - 1] = bUnique;
    1799             : 
    1800         258 :     return 0;
    1801             : }
    1802             : 
    1803             : /**********************************************************************
    1804             :  *                   MIFFile::GetNativeFieldType()
    1805             :  *
    1806             :  * Returns the native MapInfo field type for the specified field.
    1807             :  *
    1808             :  * Returns TABFUnknown if file is not opened, or if specified field index is
    1809             :  * invalid.
    1810             :  **********************************************************************/
    1811           0 : TABFieldType MIFFile::GetNativeFieldType(int nFieldId)
    1812             : {
    1813           0 :     if (m_poDefn == nullptr || m_paeFieldType == nullptr || nFieldId < 0 ||
    1814           0 :         nFieldId >= m_poDefn->GetFieldCount())
    1815           0 :         return TABFUnknown;
    1816             : 
    1817           0 :     return m_paeFieldType[nFieldId];
    1818             : }
    1819             : 
    1820             : /************************************************************************
    1821             :  *                       MIFFile::SetFieldIndexed()
    1822             :  ************************************************************************/
    1823             : 
    1824           0 : int MIFFile::SetFieldIndexed(int nFieldId)
    1825             : 
    1826             : {
    1827           0 :     if (m_poDefn == nullptr || m_pabFieldIndexed == nullptr || nFieldId < 0 ||
    1828           0 :         nFieldId >= m_poDefn->GetFieldCount())
    1829           0 :         return -1;
    1830             : 
    1831           0 :     m_pabFieldIndexed[nFieldId] = TRUE;
    1832             : 
    1833           0 :     return 0;
    1834             : }
    1835             : 
    1836             : /************************************************************************
    1837             :  *                       MIFFile::IsFieldIndexed()
    1838             :  ************************************************************************/
    1839             : 
    1840           0 : GBool MIFFile::IsFieldIndexed(int nFieldId)
    1841             : 
    1842             : {
    1843           0 :     if (m_poDefn == nullptr || m_pabFieldIndexed == nullptr || nFieldId < 0 ||
    1844           0 :         nFieldId >= m_poDefn->GetFieldCount())
    1845           0 :         return FALSE;
    1846             : 
    1847           0 :     return m_pabFieldIndexed[nFieldId];
    1848             : }
    1849             : 
    1850             : /************************************************************************
    1851             :  *                       MIFFile::IsFieldUnique()
    1852             :  ************************************************************************/
    1853             : 
    1854           0 : GBool MIFFile::IsFieldUnique(int nFieldId)
    1855             : 
    1856             : {
    1857           0 :     if (m_poDefn == nullptr || m_pabFieldUnique == nullptr || nFieldId < 0 ||
    1858           0 :         nFieldId >= m_poDefn->GetFieldCount())
    1859           0 :         return FALSE;
    1860             : 
    1861           0 :     return m_pabFieldUnique[nFieldId];
    1862             : }
    1863             : 
    1864             : /************************************************************************/
    1865             : /*                       MIFFile::SetSpatialRef()                       */
    1866             : /************************************************************************/
    1867             : 
    1868          71 : int MIFFile::SetSpatialRef(OGRSpatialReference *poSpatialRef)
    1869             : 
    1870             : {
    1871          71 :     CPLFree(m_pszCoordSys);
    1872          71 :     m_pszCoordSys = nullptr;
    1873             : 
    1874          71 :     char *pszCoordSys = MITABSpatialRef2CoordSys(poSpatialRef);
    1875          71 :     if (pszCoordSys)
    1876             :     {
    1877          71 :         SetMIFCoordSys(pszCoordSys);
    1878          71 :         CPLFree(pszCoordSys);
    1879             :     }
    1880             : 
    1881          71 :     return m_pszCoordSys != nullptr;
    1882             : }
    1883             : 
    1884             : /************************************************************************/
    1885             : /*                      MIFFile::SetMIFCoordSys()                       */
    1886             : /************************************************************************/
    1887             : 
    1888          71 : int MIFFile::SetMIFCoordSys(const char *pszMIFCoordSys)
    1889             : 
    1890             : {
    1891          71 :     char *pszCoordSys = nullptr;
    1892             : 
    1893             :     // Extract the word 'COORDSYS' if present
    1894          71 :     if (STARTS_WITH_CI(pszMIFCoordSys, "COORDSYS"))
    1895             :     {
    1896           0 :         pszCoordSys = CPLStrdup(pszMIFCoordSys + 9);
    1897             :     }
    1898             :     else
    1899             :     {
    1900          71 :         pszCoordSys = CPLStrdup(pszMIFCoordSys);
    1901             :     }
    1902             : 
    1903             :     // Extract bounds if present
    1904             :     char **papszFields =
    1905          71 :         CSLTokenizeStringComplex(pszCoordSys, " ,()\t", TRUE, FALSE);
    1906          71 :     int iBounds = CSLFindString(papszFields, "Bounds");
    1907          71 :     if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields))
    1908             :     {
    1909           5 :         m_dXMin = CPLAtof(papszFields[++iBounds]);
    1910           5 :         m_dYMin = CPLAtof(papszFields[++iBounds]);
    1911           5 :         m_dXMax = CPLAtof(papszFields[++iBounds]);
    1912           5 :         m_dYMax = CPLAtof(papszFields[++iBounds]);
    1913           5 :         m_bBoundsSet = TRUE;
    1914             : 
    1915           5 :         char *pszBounds = strstr(pszCoordSys, " Bounds");
    1916           5 :         if (pszBounds == nullptr)
    1917           0 :             pszBounds = strstr(pszCoordSys, "Bounds");
    1918           5 :         pszCoordSys[pszBounds - pszCoordSys] = '\0';
    1919             :     }
    1920          71 :     CSLDestroy(papszFields);
    1921             : 
    1922             :     // Assign the CoordSys
    1923          71 :     CPLFree(m_pszCoordSys);
    1924             : 
    1925          71 :     m_pszCoordSys = CPLStrdup(pszCoordSys);
    1926          71 :     CPLFree(pszCoordSys);
    1927             : 
    1928          71 :     return m_pszCoordSys != nullptr;
    1929             : }
    1930             : 
    1931         932 : int MIFFile::SetCharset(const char *pszCharset)
    1932             : {
    1933         932 :     if (0 != IMapInfoFile::SetCharset(pszCharset))
    1934             :     {
    1935           0 :         return -1;
    1936             :     }
    1937             : 
    1938         932 :     if (m_poMIDFile != nullptr)
    1939             :     {
    1940          85 :         m_poMIDFile->SetEncoding(CharsetToEncoding(pszCharset));
    1941             :     }
    1942         932 :     if (m_poMIFFile != nullptr)
    1943             :     {
    1944         932 :         m_poMIFFile->SetEncoding(CharsetToEncoding(pszCharset));
    1945             :     }
    1946         932 :     return 0;
    1947             : }
    1948             : 
    1949             : /************************************************************************/
    1950             : /*                       MIFFile::GetSpatialRef()                       */
    1951             : /************************************************************************/
    1952             : 
    1953        7233 : OGRSpatialReference *MIFFile::GetSpatialRef()
    1954             : 
    1955             : {
    1956        7233 :     if (m_poSpatialRef == nullptr)
    1957        6999 :         m_poSpatialRef = MITABCoordSys2SpatialRef(m_pszCoordSys);
    1958             : 
    1959        7233 :     return m_poSpatialRef;
    1960             : }
    1961             : 
    1962             : /**********************************************************************
    1963             :  *                   MIFFile::UpdateExtents()
    1964             :  *
    1965             :  * Private method used to update the dataset extents.
    1966             :  **********************************************************************/
    1967         250 : void MIFFile::UpdateExtents(double dfX, double dfY)
    1968             : {
    1969         250 :     if (m_bExtentsSet == FALSE)
    1970             :     {
    1971           2 :         m_bExtentsSet = TRUE;
    1972           2 :         m_sExtents.MinX = m_sExtents.MaxX = dfX;
    1973           2 :         m_sExtents.MinY = m_sExtents.MaxY = dfY;
    1974             :     }
    1975             :     else
    1976             :     {
    1977         248 :         if (dfX < m_sExtents.MinX)
    1978          18 :             m_sExtents.MinX = dfX;
    1979         248 :         if (dfX > m_sExtents.MaxX)
    1980          28 :             m_sExtents.MaxX = dfX;
    1981         248 :         if (dfY < m_sExtents.MinY)
    1982          31 :             m_sExtents.MinY = dfY;
    1983         248 :         if (dfY > m_sExtents.MaxY)
    1984           6 :             m_sExtents.MaxY = dfY;
    1985             :     }
    1986         250 : }
    1987             : 
    1988             : /**********************************************************************
    1989             :  *                   MIFFile::SetBounds()
    1990             :  *
    1991             :  * Set projection coordinates bounds of the newly created dataset.
    1992             :  *
    1993             :  * This function must be called after creating a new dataset and before any
    1994             :  * feature can be written to it.
    1995             :  *
    1996             :  * Returns 0 on success, -1 on error.
    1997             :  **********************************************************************/
    1998           1 : int MIFFile::SetBounds(double dXMin, double dYMin, double dXMax, double dYMax)
    1999             : {
    2000           1 :     if (m_eAccessMode != TABWrite)
    2001             :     {
    2002           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2003             :                  "SetBounds() can be used only with Write access.");
    2004           0 :         return -1;
    2005             :     }
    2006             : 
    2007           1 :     m_dXMin = dXMin;
    2008           1 :     m_dXMax = dXMax;
    2009           1 :     m_dYMin = dYMin;
    2010           1 :     m_dYMax = dYMax;
    2011           1 :     m_bBoundsSet = TRUE;
    2012             : 
    2013           1 :     return 0;
    2014             : }
    2015             : 
    2016             : /**********************************************************************
    2017             :  *                   MIFFile::GetFeatureCountByType()
    2018             :  *
    2019             :  * Return number of features of each type.
    2020             :  *
    2021             :  * NOTE: The current implementation always returns -1 for MIF files
    2022             :  *       since this would require scanning the whole file.
    2023             :  *
    2024             :  * When properly implemented, the bForce flag will force scanning the
    2025             :  * whole file by default.
    2026             :  *
    2027             :  * Returns 0 on success, or silently returns -1 (with no error) if this
    2028             :  * information is not available.
    2029             :  **********************************************************************/
    2030         895 : int MIFFile::GetFeatureCountByType(int &numPoints, int &numLines,
    2031             :                                    int &numRegions, int &numTexts, GBool bForce)
    2032             : {
    2033         895 :     if (m_bPreParsed || bForce)
    2034             :     {
    2035           0 :         PreParseFile();
    2036             : 
    2037           0 :         numPoints = m_nPoints;
    2038           0 :         numLines = m_nLines;
    2039           0 :         numRegions = m_nRegions;
    2040           0 :         numTexts = m_nTexts;
    2041           0 :         return 0;
    2042             :     }
    2043             :     else
    2044             :     {
    2045         895 :         numPoints = numLines = numRegions = numTexts = 0;
    2046         895 :         return -1;
    2047             :     }
    2048             : }
    2049             : 
    2050             : /**********************************************************************
    2051             :  *                   MIFFile::GetBounds()
    2052             :  *
    2053             :  * Fetch projection coordinates bounds of a dataset.
    2054             :  *
    2055             :  * Pass bForce=FALSE to avoid a scan of the whole file if the bounds
    2056             :  * are not already available.
    2057             :  *
    2058             :  * Returns 0 on success, -1 on error or if bounds are not available and
    2059             :  * bForce=FALSE.
    2060             :  **********************************************************************/
    2061           0 : int MIFFile::GetBounds(double &dXMin, double &dYMin, double &dXMax,
    2062             :                        double &dYMax, GBool bForce /*= TRUE*/)
    2063             : {
    2064           0 :     if (m_bBoundsSet == FALSE && bForce == FALSE)
    2065             :     {
    2066           0 :         return -1;
    2067             :     }
    2068           0 :     else if (m_bBoundsSet == FALSE)
    2069             :     {
    2070           0 :         PreParseFile();
    2071             :     }
    2072             : 
    2073           0 :     if (m_bBoundsSet == FALSE)
    2074             :     {
    2075           0 :         return -1;
    2076             :     }
    2077             : 
    2078           0 :     dXMin = m_dXMin;
    2079           0 :     dXMax = m_dXMax;
    2080           0 :     dYMin = m_dYMin;
    2081           0 :     dYMax = m_dYMax;
    2082             : 
    2083           0 :     return 0;
    2084             : }
    2085             : 
    2086             : /**********************************************************************
    2087             :  *                   MIFFile::GetExtent()
    2088             :  *
    2089             :  * Fetch extent of the data currently stored in the dataset.  We collect
    2090             :  * this information while preparsing the file ... often already done for
    2091             :  * other reasons, and if not it is still faster than fully reading all
    2092             :  * the features just to count them.
    2093             :  *
    2094             :  * Returns OGRERR_NONE/OGRRERR_FAILURE.
    2095             :  **********************************************************************/
    2096           4 : OGRErr MIFFile::GetExtent(OGREnvelope *psExtent, int bForce)
    2097             : {
    2098           4 :     if (bForce == TRUE)
    2099           4 :         PreParseFile();
    2100             : 
    2101           4 :     if (m_bPreParsed && m_bExtentsSet)
    2102             :     {
    2103           4 :         *psExtent = m_sExtents;
    2104           4 :         return OGRERR_NONE;
    2105             :     }
    2106             :     else
    2107           0 :         return OGRERR_FAILURE;
    2108             : }
    2109             : 
    2110             : /************************************************************************/
    2111             : /*                           TestCapability()                           */
    2112             : /************************************************************************/
    2113             : 
    2114         208 : int MIFFile::TestCapability(const char *pszCap)
    2115             : 
    2116             : {
    2117         208 :     if (EQUAL(pszCap, OLCRandomRead))
    2118           0 :         return TRUE;
    2119             : 
    2120         208 :     else if (EQUAL(pszCap, OLCSequentialWrite))
    2121           1 :         return TRUE;
    2122             : 
    2123         207 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
    2124           0 :         return m_bPreParsed;
    2125             : 
    2126         207 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
    2127           0 :         return FALSE;
    2128             : 
    2129         207 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    2130           2 :         return m_bPreParsed;
    2131             : 
    2132         205 :     else if (EQUAL(pszCap, OLCCreateField))
    2133           2 :         return TRUE;
    2134             : 
    2135         203 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    2136          20 :         return TestUtf8Capability();
    2137             : 
    2138             :     else
    2139         183 :         return FALSE;
    2140             : }

Generated by: LCOV version 1.14