LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_imapinfofile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 259 288 89.9 %
Date: 2025-01-18 12:42:00 Functions: 17 20 85.0 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_imapinfo
       4             :  * Project:  MapInfo mid/mif Tab Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the IMapInfoFile class, super class of
       7             :  *           of MIFFile and TABFile
       8             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999-2008, Daniel Morissette
      12             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      13             :  *
      14             :  * SPDX-License-Identifier: MIT
      15             :  **********************************************************************/
      16             : 
      17             : #include "cpl_port.h"
      18             : #include "mitab.h"
      19             : 
      20             : #include <cassert>
      21             : #include <cctype>
      22             : #include <cstring>
      23             : #include <algorithm>
      24             : 
      25             : #include "cpl_conv.h"
      26             : #include "cpl_error.h"
      27             : #include "cpl_vsi.h"
      28             : #include "mitab_priv.h"
      29             : #include "mitab_utils.h"
      30             : #include "ogr_core.h"
      31             : #include "ogr_feature.h"
      32             : #include "ogr_geometry.h"
      33             : 
      34             : #ifdef __HP_aCC
      35             : #include <wchar.h> /* iswspace() */
      36             : #else
      37             : #include <wctype.h> /* iswspace() */
      38             : #endif
      39             : 
      40             : /**********************************************************************
      41             :  *                   IMapInfoFile::IMapInfoFile()
      42             :  *
      43             :  * Constructor.
      44             :  **********************************************************************/
      45        2398 : IMapInfoFile::IMapInfoFile(GDALDataset *poDS)
      46             :     : m_poDS(poDS), m_nCurFeatureId(0), m_poCurFeature(nullptr),
      47        2398 :       m_bBoundsSet(FALSE), m_pszCharset(nullptr)
      48             : {
      49        2398 : }
      50             : 
      51             : /**********************************************************************
      52             :  *                   IMapInfoFile::~IMapInfoFile()
      53             :  *
      54             :  * Destructor.
      55             :  **********************************************************************/
      56        2398 : IMapInfoFile::~IMapInfoFile()
      57             : {
      58        2398 :     if (m_poCurFeature)
      59             :     {
      60           0 :         delete m_poCurFeature;
      61           0 :         m_poCurFeature = nullptr;
      62             :     }
      63             : 
      64        2398 :     CPLFree(m_pszCharset);
      65        2398 :     m_pszCharset = nullptr;
      66        2398 : }
      67             : 
      68             : /**********************************************************************
      69             :  *                   IMapInfoFile::Open()
      70             :  *
      71             :  * Compatibility layer with new interface.
      72             :  * Return 0 on success, -1 in case of failure.
      73             :  **********************************************************************/
      74             : 
      75           0 : int IMapInfoFile::Open(const char *pszFname, const char *pszAccess,
      76             :                        GBool bTestOpenNoError, const char *pszCharset)
      77             : {
      78             :     // cppcheck-suppress nullPointer
      79           0 :     if (STARTS_WITH_CI(pszAccess, "r"))
      80           0 :         return Open(pszFname, TABRead, bTestOpenNoError, pszCharset);
      81           0 :     else if (STARTS_WITH_CI(pszAccess, "w"))
      82           0 :         return Open(pszFname, TABWrite, bTestOpenNoError, pszCharset);
      83             :     else
      84             :     {
      85           0 :         CPLError(CE_Failure, CPLE_FileIO,
      86             :                  "Open() failed: access mode \"%s\" not supported", pszAccess);
      87           0 :         return -1;
      88             :     }
      89             : }
      90             : 
      91             : /**********************************************************************
      92             :  *                   IMapInfoFile::SmartOpen()
      93             :  *
      94             :  * Use this static method to automatically open any flavor of MapInfo
      95             :  * dataset.  This method will detect the file type, create an object
      96             :  * of the right type, and open the file.
      97             :  *
      98             :  * Call GetFileClass() on the returned object if you need to find out
      99             :  * its exact type.  (To access format-specific methods for instance)
     100             :  *
     101             :  * Returns the new object ptr. , or NULL if the open failed.
     102             :  **********************************************************************/
     103        2178 : IMapInfoFile *IMapInfoFile::SmartOpen(GDALDataset *poDS, const char *pszFname,
     104             :                                       GBool bUpdate,
     105             :                                       GBool bTestOpenNoError /*=FALSE*/)
     106             : {
     107        2178 :     IMapInfoFile *poFile = nullptr;
     108        2178 :     int nLen = 0;
     109             : 
     110        2178 :     if (pszFname)
     111        2178 :         nLen = static_cast<int>(strlen(pszFname));
     112             : 
     113        2178 :     if (nLen > 4 && (EQUAL(pszFname + nLen - 4, ".MIF") ||
     114        1303 :                      EQUAL(pszFname + nLen - 4, ".MID")))
     115             :     {
     116             :         /*-------------------------------------------------------------
     117             :          * MIF/MID file
     118             :          *------------------------------------------------------------*/
     119         875 :         poFile = new MIFFile(poDS);
     120             :     }
     121        1303 :     else if (nLen > 4 && EQUAL(pszFname + nLen - 4, ".TAB"))
     122             :     {
     123             :         /*-------------------------------------------------------------
     124             :          * .TAB file ... is it a TABFileView or a TABFile?
     125             :          * We have to read the .tab header to find out.
     126             :          *------------------------------------------------------------*/
     127        1303 :         char *pszAdjFname = CPLStrdup(pszFname);
     128        1303 :         GBool bFoundFields = FALSE;
     129        1303 :         GBool bFoundView = FALSE;
     130        1303 :         GBool bFoundSeamless = FALSE;
     131             : 
     132        1303 :         TABAdjustFilenameExtension(pszAdjFname);
     133        1303 :         VSILFILE *fp = VSIFOpenL(pszAdjFname, "r");
     134        1303 :         const char *pszLine = nullptr;
     135       12038 :         while (fp && (pszLine = CPLReadLineL(fp)) != nullptr)
     136             :         {
     137       22037 :             while (isspace(static_cast<unsigned char>(*pszLine)))
     138       11302 :                 pszLine++;
     139       10735 :             if (STARTS_WITH_CI(pszLine, "Fields"))
     140        1300 :                 bFoundFields = TRUE;
     141        9435 :             else if (STARTS_WITH_CI(pszLine, "create view"))
     142           2 :                 bFoundView = TRUE;
     143        9433 :             else if (STARTS_WITH_CI(pszLine, "\"\\IsSeamless\" = \"TRUE\""))
     144           1 :                 bFoundSeamless = TRUE;
     145             :         }
     146             : 
     147        1303 :         if (bFoundView)
     148           2 :             poFile = new TABView(poDS);
     149        1301 :         else if (bFoundFields && bFoundSeamless)
     150           1 :             poFile = new TABSeamless(poDS);
     151        1300 :         else if (bFoundFields)
     152        1299 :             poFile = new TABFile(poDS);
     153             : 
     154        1303 :         if (fp)
     155        1303 :             VSIFCloseL(fp);
     156             : 
     157        1303 :         CPLFree(pszAdjFname);
     158             :     }
     159             : 
     160             :     /*-----------------------------------------------------------------
     161             :      * Perform the open() call
     162             :      *----------------------------------------------------------------*/
     163        4355 :     if (poFile && poFile->Open(pszFname, bUpdate ? TABReadWrite : TABRead,
     164        2177 :                                bTestOpenNoError) != 0)
     165             :     {
     166          75 :         delete poFile;
     167          75 :         poFile = nullptr;
     168             :     }
     169             : 
     170        2178 :     if (!bTestOpenNoError && poFile == nullptr)
     171             :     {
     172           0 :         CPLError(CE_Failure, CPLE_FileIO,
     173             :                  "%s could not be opened as a MapInfo dataset.", pszFname);
     174             :     }
     175             : 
     176        2178 :     return poFile;
     177             : }
     178             : 
     179             : /**********************************************************************
     180             :  *                   IMapInfoFile::GetNextFeature()
     181             :  *
     182             :  * Standard OGR GetNextFeature implementation.  This method is used
     183             :  * to retrieve the next OGRFeature.
     184             :  **********************************************************************/
     185      223072 : OGRFeature *IMapInfoFile::GetNextFeature()
     186             : {
     187      223072 :     GIntBig nFeatureId = 0;
     188             : 
     189      544068 :     while ((nFeatureId = GetNextFeatureId(m_nCurFeatureId)) != -1)
     190             :     {
     191      533257 :         OGRGeometry *poGeom = nullptr;
     192      533257 :         OGRFeature *poFeatureRef = GetFeatureRef(nFeatureId);
     193      533257 :         if (poFeatureRef == nullptr)
     194         328 :             return nullptr;
     195     1441220 :         else if ((m_poFilterGeom == nullptr ||
     196      375363 :                   ((poGeom = poFeatureRef->GetGeometryRef()) != nullptr &&
     197     1122430 :                    FilterGeometry(poGeom))) &&
     198      214137 :                  (m_poAttrQuery == nullptr ||
     199        4894 :                   m_poAttrQuery->Evaluate(poFeatureRef)))
     200             :         {
     201             :             // Avoid cloning feature... return the copy owned by the class
     202      211933 :             CPLAssert(poFeatureRef == m_poCurFeature);
     203      211933 :             m_poCurFeature = nullptr;
     204      211933 :             if (poFeatureRef->GetGeometryRef() != nullptr)
     205      211763 :                 poFeatureRef->GetGeometryRef()->assignSpatialReference(
     206      211763 :                     GetSpatialRef());
     207      211933 :             return poFeatureRef;
     208             :         }
     209             :     }
     210       10811 :     return nullptr;
     211             : }
     212             : 
     213             : /**********************************************************************
     214             :  *                   IMapInfoFile::CreateTABFeature()
     215             :  *
     216             :  * Instantiate a TABFeature* from a OGRFeature* (or NULL on error)
     217             :  **********************************************************************/
     218             : 
     219       15224 : TABFeature *IMapInfoFile::CreateTABFeature(OGRFeature *poFeature)
     220             : {
     221       15224 :     TABFeature *poTABFeature = nullptr;
     222             :     OGRwkbGeometryType eGType;
     223       15224 :     TABPoint *poTABPointFeature = nullptr;
     224       15224 :     TABRegion *poTABRegionFeature = nullptr;
     225       15224 :     TABPolyline *poTABPolylineFeature = nullptr;
     226             : 
     227             :     /*-----------------------------------------------------------------
     228             :      * MITAB won't accept new features unless they are in a type derived
     229             :      * from TABFeature... so we have to do our best to map to the right
     230             :      * feature type based on the geometry type.
     231             :      *----------------------------------------------------------------*/
     232       15224 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
     233       15224 :     if (poGeom != nullptr)
     234       15146 :         eGType = poGeom->getGeometryType();
     235             :     else
     236          78 :         eGType = wkbNone;
     237             : 
     238       15224 :     switch (wkbFlatten(eGType))
     239             :     {
     240             :         /*-------------------------------------------------------------
     241             :          * POINT
     242             :          *------------------------------------------------------------*/
     243       14779 :         case wkbPoint:
     244             :         {
     245       14779 :             const char *pszStyleString = poFeature->GetStyleString();
     246       14779 :             if (pszStyleString)
     247             :             {
     248         113 :                 if (strstr(pszStyleString, "LABEL("))
     249             :                 {
     250           4 :                     auto poText = new TABText(poFeature->GetDefnRef());
     251           4 :                     poText->SetLabelFromStyleString(pszStyleString);
     252           4 :                     poTABFeature = poText;
     253             : 
     254           4 :                     if (strstr(pszStyleString, "SYMBOL("))
     255             :                     {
     256           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     257             :                                  "OGR style string contains both "
     258             :                                  "Label and Symbol parts. "
     259             :                                  "Only Label taken into account due to "
     260             :                                  "MapInfo TAB/MIF format limitations.");
     261             :                     }
     262             :                 }
     263             :                 else
     264             :                 {
     265             :                     TABFeatureClass featureClass =
     266         109 :                         ITABFeatureSymbol::GetSymbolFeatureClass(
     267             :                             pszStyleString);
     268         109 :                     if (featureClass == TABFCFontPoint)
     269             :                     {
     270           2 :                         poTABFeature =
     271           2 :                             new TABFontPoint(poFeature->GetDefnRef());
     272             :                     }
     273         107 :                     else if (featureClass == TABFCCustomPoint)
     274             :                     {
     275           2 :                         poTABFeature =
     276           2 :                             new TABCustomPoint(poFeature->GetDefnRef());
     277             :                     }
     278             :                     else
     279             :                     {
     280         105 :                         poTABFeature = new TABPoint(poFeature->GetDefnRef());
     281             :                     }
     282             :                     poTABPointFeature =
     283         109 :                         cpl::down_cast<TABPoint *>(poTABFeature);
     284         109 :                     poTABPointFeature->SetSymbolFromStyleString(
     285         109 :                         poFeature->GetStyleString());
     286             :                 }
     287             :             }
     288             :             else
     289             :             {
     290       14666 :                 poTABFeature = new TABPoint(poFeature->GetDefnRef());
     291             :             }
     292       14779 :             break;
     293             :         }
     294             : 
     295             :         /*-------------------------------------------------------------
     296             :          * REGION
     297             :          *------------------------------------------------------------*/
     298         111 :         case wkbPolygon:
     299             :         case wkbMultiPolygon:
     300             :         {
     301         111 :             poTABFeature = new TABRegion(poFeature->GetDefnRef());
     302         111 :             if (poFeature->GetStyleString())
     303             :             {
     304          19 :                 poTABRegionFeature = cpl::down_cast<TABRegion *>(poTABFeature);
     305          19 :                 poTABRegionFeature->SetPenFromStyleString(
     306          19 :                     poFeature->GetStyleString());
     307             : 
     308          19 :                 poTABRegionFeature->SetBrushFromStyleString(
     309          19 :                     poFeature->GetStyleString());
     310             :             }
     311         111 :             break;
     312             :         }
     313             :         /*-------------------------------------------------------------
     314             :          * LINE/PLINE/MULTIPLINE
     315             :          *------------------------------------------------------------*/
     316         238 :         case wkbLineString:
     317             :         case wkbMultiLineString:
     318             :         {
     319         238 :             poTABFeature = new TABPolyline(poFeature->GetDefnRef());
     320         238 :             if (poFeature->GetStyleString())
     321             :             {
     322             :                 poTABPolylineFeature =
     323          13 :                     cpl::down_cast<TABPolyline *>(poTABFeature);
     324          13 :                 poTABPolylineFeature->SetPenFromStyleString(
     325          13 :                     poFeature->GetStyleString());
     326             :             }
     327         238 :             break;
     328             :         }
     329             :         /*-------------------------------------------------------------
     330             :          * Collection types that are not directly supported... convert
     331             :          * to multiple features in output file through recursive calls.
     332             :          *------------------------------------------------------------*/
     333          18 :         case wkbGeometryCollection:
     334             :         case wkbMultiPoint:
     335             :         {
     336          18 :             OGRErr eStatus = OGRERR_NONE;
     337          18 :             assert(poGeom);  // for clang static analyzer
     338          18 :             OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
     339          36 :             auto poTmpFeature = std::unique_ptr<OGRFeature>(poFeature->Clone());
     340             : 
     341         112 :             for (int i = 0; eStatus == OGRERR_NONE && poColl != nullptr &&
     342          56 :                             i < poColl->getNumGeometries();
     343             :                  i++)
     344             :             {
     345          38 :                 poTmpFeature->SetFID(OGRNullFID);
     346          38 :                 poTmpFeature->SetGeometry(poColl->getGeometryRef(i));
     347          38 :                 eStatus = ICreateFeature(poTmpFeature.get());
     348             :             }
     349          18 :             break;
     350             :         }
     351             : 
     352             :         /*-------------------------------------------------------------
     353             :          * Unsupported type.... convert to MapInfo geometry NONE
     354             :          *------------------------------------------------------------*/
     355          78 :         default:
     356             :         {
     357          78 :             poTABFeature = new TABFeature(poFeature->GetDefnRef());
     358          78 :             break;
     359             :         }
     360             :     }
     361             : 
     362       15224 :     if (poTABFeature)
     363             :     {
     364       15206 :         if (poGeom != nullptr)
     365       15128 :             poTABFeature->SetGeometryDirectly(poGeom->clone());
     366             : 
     367       31169 :         for (int i = 0; i < poFeature->GetDefnRef()->GetFieldCount(); i++)
     368             :         {
     369       15963 :             poTABFeature->SetField(i, poFeature->GetRawFieldRef(i));
     370             :         }
     371             : 
     372       15206 :         poTABFeature->SetFID(poFeature->GetFID());
     373             :     }
     374             : 
     375       15224 :     return poTABFeature;
     376             : }
     377             : 
     378             : /**********************************************************************
     379             :  *                   IMapInfoFile::ICreateFeature()
     380             :  *
     381             :  * Standard OGR CreateFeature implementation.  This method is used
     382             :  * to create a new feature in current dataset
     383             :  **********************************************************************/
     384       15008 : OGRErr IMapInfoFile::ICreateFeature(OGRFeature *poFeature)
     385             : {
     386       15008 :     TABFeature *poTABFeature = CreateTABFeature(poFeature);
     387       15008 :     if (poTABFeature == nullptr) /* MultiGeometry */
     388          18 :         return OGRERR_NONE;
     389             : 
     390       14990 :     OGRErr eErr = CreateFeature(poTABFeature);
     391       14990 :     if (eErr == OGRERR_NONE)
     392       14989 :         poFeature->SetFID(poTABFeature->GetFID());
     393             : 
     394       14990 :     delete poTABFeature;
     395             : 
     396       14990 :     return eErr;
     397             : }
     398             : 
     399             : /**********************************************************************
     400             :  *                   IMapInfoFile::GetFeature()
     401             :  *
     402             :  * Standard OGR GetFeature implementation.  This method is used
     403             :  * to get the wanted (nFeatureId) feature, a NULL value will be
     404             :  * returned on error.
     405             :  **********************************************************************/
     406         292 : OGRFeature *IMapInfoFile::GetFeature(GIntBig nFeatureId)
     407             : {
     408             :     /*fprintf(stderr, "GetFeature(%ld)\n", nFeatureId);*/
     409             : 
     410         292 :     OGRFeature *poFeatureRef = GetFeatureRef(nFeatureId);
     411         292 :     if (poFeatureRef)
     412             :     {
     413             :         // Avoid cloning feature... return the copy owned by the class
     414         274 :         CPLAssert(poFeatureRef == m_poCurFeature);
     415         274 :         m_poCurFeature = nullptr;
     416             : 
     417         274 :         return poFeatureRef;
     418             :     }
     419             :     else
     420          18 :         return nullptr;
     421             : }
     422             : 
     423             : /************************************************************************/
     424             : /*                            GetTABType()                              */
     425             : /*                                                                      */
     426             : /*      Create a native field based on a generic OGR definition.        */
     427             : /************************************************************************/
     428             : 
     429         352 : int IMapInfoFile::GetTABType(const OGRFieldDefn *poField,
     430             :                              TABFieldType *peTABType, int *pnWidth,
     431             :                              int *pnPrecision)
     432             : {
     433             :     TABFieldType eTABType;
     434         352 :     int nWidth = poField->GetWidth();
     435             :     int nPrecision =
     436         352 :         poField->GetType() == OFTReal ? poField->GetPrecision() : 0;
     437             : 
     438         352 :     if (poField->GetType() == OFTInteger)
     439             :     {
     440         103 :         if (poField->GetSubType() == OFSTBoolean)
     441             :         {
     442           2 :             eTABType = TABFLogical;
     443           2 :             nWidth = 1;
     444             :         }
     445             :         else
     446             :         {
     447         101 :             eTABType = TABFInteger;
     448         101 :             if (nWidth == 0)
     449          86 :                 nWidth = 12;
     450             :         }
     451             :     }
     452         249 :     else if (poField->GetType() == OFTInteger64)
     453             :     {
     454           4 :         eTABType = TABFLargeInt;
     455           4 :         if (nWidth == 0)
     456           4 :             nWidth = 20;
     457             :     }
     458         245 :     else if (poField->GetType() == OFTReal)
     459             :     {
     460          41 :         if (nWidth == 0 && poField->GetPrecision() == 0)
     461             :         {
     462          29 :             eTABType = TABFFloat;
     463          29 :             nWidth = 32;
     464             :         }
     465             :         else
     466             :         {
     467          12 :             eTABType = TABFDecimal;
     468             :             // Enforce Mapinfo limits, otherwise MapInfo will crash (#6392)
     469          12 :             if (nWidth > 20 || nWidth - nPrecision < 2 || nPrecision > 16)
     470             :             {
     471           4 :                 if (nWidth > 20)
     472           4 :                     nWidth = 20;
     473           4 :                 if (nWidth - nPrecision < 2)
     474           1 :                     nPrecision = nWidth - 2;
     475           4 :                 if (nPrecision > 16)
     476           1 :                     nPrecision = 16;
     477           4 :                 CPLDebug("MITAB",
     478             :                          "Adjusting initial width,precision of %s from %d,%d "
     479             :                          "to %d,%d",
     480             :                          poField->GetNameRef(), poField->GetWidth(),
     481             :                          poField->GetPrecision(), nWidth, nPrecision);
     482             :             }
     483             :         }
     484             :     }
     485         204 :     else if (poField->GetType() == OFTDate)
     486             :     {
     487          19 :         eTABType = TABFDate;
     488          19 :         if (nWidth == 0)
     489          17 :             nWidth = 10;
     490             :     }
     491         185 :     else if (poField->GetType() == OFTTime)
     492             :     {
     493           3 :         eTABType = TABFTime;
     494           3 :         if (nWidth == 0)
     495           1 :             nWidth = 9;
     496             :     }
     497         182 :     else if (poField->GetType() == OFTDateTime)
     498             :     {
     499          19 :         eTABType = TABFDateTime;
     500          19 :         if (nWidth == 0)
     501          17 :             nWidth = 19;
     502             :     }
     503         163 :     else if (poField->GetType() == OFTString)
     504             :     {
     505         163 :         eTABType = TABFChar;
     506         163 :         if (nWidth == 0)
     507         118 :             nWidth = 254;
     508             :         else
     509          45 :             nWidth = std::min(254, nWidth);
     510             :     }
     511             :     else
     512             :     {
     513           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     514             :                  "IMapInfoFile::CreateField() called with unsupported field"
     515             :                  " type %d.\n"
     516             :                  "Note that Mapinfo files don't support list field types.\n",
     517           0 :                  poField->GetType());
     518             : 
     519           0 :         return -1;
     520             :     }
     521             : 
     522         352 :     if (peTABType)
     523         333 :         *peTABType = eTABType;
     524         352 :     if (pnWidth)
     525         341 :         *pnWidth = nWidth;
     526         352 :     if (pnPrecision)
     527         341 :         *pnPrecision = nPrecision;
     528             : 
     529         352 :     return 0;
     530             : }
     531             : 
     532             : /************************************************************************/
     533             : /*                            CreateField()                             */
     534             : /*                                                                      */
     535             : /*      Create a native field based on a generic OGR definition.        */
     536             : /************************************************************************/
     537             : 
     538         322 : OGRErr IMapInfoFile::CreateField(const OGRFieldDefn *poField, int bApproxOK)
     539             : 
     540             : {
     541             :     TABFieldType eTABType;
     542         322 :     int nWidth = 0;
     543         322 :     int nPrecision = 0;
     544             : 
     545         322 :     if (GetTABType(poField, &eTABType, &nWidth, &nPrecision) < 0)
     546           0 :         return OGRERR_FAILURE;
     547             : 
     548         322 :     if (AddFieldNative(poField->GetNameRef(), eTABType, nWidth, nPrecision,
     549         644 :                        FALSE, FALSE, bApproxOK) > -1)
     550         322 :         return OGRERR_NONE;
     551             : 
     552           0 :     return OGRERR_FAILURE;
     553             : }
     554             : 
     555             : /**********************************************************************
     556             :  *                   IMapInfoFile::SetCharset()
     557             :  *
     558             :  * Set the charset for the tab header.
     559             :  *
     560             :  *
     561             :  * Returns 0 on success, -1 on error.
     562             :  **********************************************************************/
     563        2375 : int IMapInfoFile::SetCharset(const char *pszCharset)
     564             : {
     565        2375 :     if (pszCharset && strlen(pszCharset) > 0)
     566             :     {
     567        2375 :         if (pszCharset == m_pszCharset)
     568             :         {
     569           0 :             return 0;
     570             :         }
     571        2375 :         CPLFree(m_pszCharset);
     572        2375 :         m_pszCharset = CPLStrdup(pszCharset);
     573        2375 :         return 0;
     574             :     }
     575           0 :     return -1;
     576             : }
     577             : 
     578        5330 : const char *IMapInfoFile::GetCharset() const
     579             : {
     580        5330 :     return m_pszCharset;
     581             : }
     582             : 
     583             : // Table is adopted from
     584             : // http://www.i-signum.com/Formation/download/MB_ReferenceGuide.pdf pp. 127-128
     585             : // NOTE: if modifying this table, please keep doc/source/drivers/vector/mapinfo_encodings.csv in sync
     586             : static const char *const apszCharsets[][2] = {
     587             :     {"Neutral", ""},                 // No character conversions performed.
     588             :     {"ISO8859_1", "ISO-8859-1"},     // ISO 8859-1 (UNIX)
     589             :     {"ISO8859_2", "ISO-8859-2"},     // ISO 8859-2 (UNIX)
     590             :     {"ISO8859_3", "ISO-8859-3"},     // ISO 8859-3 (UNIX)
     591             :     {"ISO8859_4", "ISO-8859-4"},     // ISO 8859-4 (UNIX)
     592             :     {"ISO8859_5", "ISO-8859-5"},     // ISO 8859-5 (UNIX)
     593             :     {"ISO8859_6", "ISO-8859-6"},     // ISO 8859-6 (UNIX)
     594             :     {"ISO8859_7", "ISO-8859-7"},     // ISO 8859-7 (UNIX)
     595             :     {"ISO8859_8", "ISO-8859-8"},     // ISO 8859-8 (UNIX)
     596             :     {"ISO8859_9", "ISO-8859-9"},     // ISO 8859-9 (UNIX)
     597             :     {"PackedEUCJapaese", "EUC-JP"},  // UNIX, standard Japanese implementation.
     598             :     {"WindowsLatin1", "CP1252"},
     599             :     {"WindowsLatin2", "CP1250"},
     600             :     {"WindowsArabic", "CP1256"},
     601             :     {"WindowsCyrillic", "CP1251"},
     602             :     {"WindowsBalticRim", "CP1257"},
     603             :     {"WindowsGreek", "CP1253"},
     604             :     {"WindowsHebrew", "CP1255"},
     605             :     {"WindowsTurkish", "CP1254"},     // Windows Eastern Europe
     606             :     {"WindowsTradChinese", "CP950"},  // Windows Traditional Chinese
     607             :     {"WindowsSimpChinese", "CP936"},  // Windows Simplified Chinese
     608             :     {"WindowsJapanese", "CP932"},
     609             :     {"WindowsKorean", "CP949"},
     610             :     {"CodePage437", "CP437"},  // DOS Code Page 437 = IBM Extended ASCII
     611             :     {"CodePage850", "CP850"},  // DOS Code Page 850 = Multilingual
     612             :     {"CodePage852", "CP852"},  // DOS Code Page 852 = Eastern Europe
     613             :     {"CodePage855", "CP855"},  // DOS Code Page 855 = Cyrillic
     614             :     {"CodePage857", "CP857"},
     615             :     {"CodePage860", "CP860"},  // DOS Code Page 860 = Portuguese
     616             :     {"CodePage861", "CP861"},  // DOS Code Page 861 = Icelandic
     617             :     {"CodePage863", "CP863"},  // DOS Code Page 863 = French Canadian
     618             :     {"CodePage864", "CP864"},  // DOS Code Page 864 = Arabic
     619             :     {"CodePage865", "CP865"},  // DOS Code Page 865 = Nordic
     620             :     {"CodePage869", "CP869"},  // DOS Code Page 869 = Modern Greek
     621             :     {"LICS", ""},              // Lotus worksheet release 1,2 character set
     622             :     {"LMBCS", ""},             // Lotus worksheet release 3,4 character set
     623             :     {"UTF-8", "UTF-8"},
     624             :     {nullptr, nullptr}};
     625             : 
     626        7533 : const char *IMapInfoFile::CharsetToEncoding(const char *pszCharset)
     627             : {
     628        7533 :     if (pszCharset == nullptr)
     629             :     {
     630         874 :         return apszCharsets[0][1];
     631             :     }
     632             : 
     633       18052 :     for (size_t i = 0; apszCharsets[i][0] != nullptr; ++i)
     634             :     {
     635       18046 :         if (EQUAL(pszCharset, apszCharsets[i][0]))
     636             :         {
     637        6653 :             return apszCharsets[i][1];
     638             :         }
     639             :     }
     640             : 
     641           6 :     CPLError(CE_Warning, CPLE_NotSupported,
     642             :              "Cannot find iconv encoding corresponding to MapInfo %s charset",
     643             :              pszCharset);
     644           6 :     return apszCharsets[0][1];
     645             : }
     646             : 
     647         361 : const char *IMapInfoFile::EncodingToCharset(const char *pszEncoding)
     648             : {
     649         361 :     if (pszEncoding == nullptr)
     650             :     {
     651         352 :         return apszCharsets[0][0];
     652             :     }
     653             : 
     654         179 :     for (size_t i = 0; apszCharsets[i][1] != nullptr; ++i)
     655             :     {
     656         179 :         if (EQUAL(pszEncoding, apszCharsets[i][1]))
     657             :         {
     658           9 :             return apszCharsets[i][0];
     659             :         }
     660             :     }
     661             : 
     662           0 :     CPLError(CE_Warning, CPLE_NotSupported,
     663             :              "Cannot find MapInfo charset corresponding to iconv %s encoding",
     664             :              pszEncoding);
     665           0 :     return apszCharsets[0][0];
     666             : }
     667             : 
     668        3922 : const char *IMapInfoFile::GetEncoding() const
     669             : {
     670        3922 :     return CharsetToEncoding(GetCharset());
     671             : }
     672             : 
     673           0 : void IMapInfoFile::SetEncoding(const char *pszEncoding)
     674             : {
     675           0 :     SetCharset(EncodingToCharset(pszEncoding));
     676           0 : }
     677             : 
     678         360 : void IMapInfoFile::SetStrictLaundering(bool bStrictLaundering)
     679             : {
     680         360 :     m_bStrictLaundering = bStrictLaundering;
     681         360 : }
     682             : 
     683          69 : int IMapInfoFile::TestUtf8Capability() const
     684             : {
     685          69 :     const char *pszEncoding(GetEncoding());
     686          69 :     if (strlen(pszEncoding) == 0)
     687             :     {
     688          55 :         return FALSE;
     689             :     }
     690             : 
     691          14 :     return CPLCanRecode("test", GetEncoding(), CPL_ENC_UTF8);
     692             : }
     693             : 
     694         493 : CPLString IMapInfoFile::NormalizeFieldName(const char *pszName) const
     695             : {
     696         986 :     CPLString osName(pszName);
     697         493 :     if (strlen(GetEncoding()) > 0)
     698          84 :         osName.Recode(CPL_ENC_UTF8, GetEncoding());
     699             : 
     700             :     char szNewFieldName[31 + 1]; /* 31 is the max characters for a field name*/
     701         493 :     unsigned int nRenameNum = 1;
     702             : 
     703         493 :     strncpy(szNewFieldName, osName.c_str(), sizeof(szNewFieldName) - 1);
     704         493 :     szNewFieldName[sizeof(szNewFieldName) - 1] = '\0';
     705             : 
     706         986 :     while (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
     707        1479 :                m_oSetFields.end() &&
     708             :            nRenameNum < 10)
     709             :     {
     710           0 :         CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.29s_%.1u",
     711             :                     osName.c_str(), nRenameNum);
     712           0 :         nRenameNum++;
     713             :     }
     714             : 
     715         986 :     while (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
     716        1479 :                m_oSetFields.end() &&
     717             :            nRenameNum < 100)
     718             :     {
     719           0 :         CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.29s%.2u",
     720             :                     osName.c_str(), nRenameNum);
     721           0 :         nRenameNum++;
     722             :     }
     723             : 
     724         493 :     if (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
     725         986 :         m_oSetFields.end())
     726             :     {
     727           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     728             :                  "Too many field names like '%s' when truncated to 31 letters "
     729             :                  "for MapInfo format.",
     730             :                  pszName);
     731             :     }
     732             : 
     733         493 :     CPLString osNewFieldName(szNewFieldName);
     734         493 :     if (strlen(GetEncoding()) > 0)
     735          84 :         osNewFieldName.Recode(GetEncoding(), CPL_ENC_UTF8);
     736             : 
     737         493 :     if (!EQUAL(pszName, osNewFieldName.c_str()))
     738             :     {
     739           0 :         CPLError(CE_Warning, CPLE_NotSupported,
     740             :                  "Normalized/laundered field name: '%s' to '%s'", pszName,
     741             :                  osNewFieldName.c_str());
     742             :     }
     743             : 
     744         986 :     return osNewFieldName;
     745             : }

Generated by: LCOV version 1.14