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: 674 904 74.6 %
Date: 2024-11-21 22:18:42 Functions: 27 33 81.8 %

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

Generated by: LCOV version 1.14