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: 2024-11-21 22:18:42 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        2397 : IMapInfoFile::IMapInfoFile(GDALDataset *poDS)
      46             :     : m_poDS(poDS), m_nCurFeatureId(0), m_poCurFeature(nullptr),
      47        2397 :       m_bBoundsSet(FALSE), m_pszCharset(nullptr)
      48             : {
      49        2397 : }
      50             : 
      51             : /**********************************************************************
      52             :  *                   IMapInfoFile::~IMapInfoFile()
      53             :  *
      54             :  * Destructor.
      55             :  **********************************************************************/
      56        2397 : IMapInfoFile::~IMapInfoFile()
      57             : {
      58        2397 :     if (m_poCurFeature)
      59             :     {
      60           0 :         delete m_poCurFeature;
      61           0 :         m_poCurFeature = nullptr;
      62             :     }
      63             : 
      64        2397 :     CPLFree(m_pszCharset);
      65        2397 :     m_pszCharset = nullptr;
      66        2397 : }
      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        2177 : IMapInfoFile *IMapInfoFile::SmartOpen(GDALDataset *poDS, const char *pszFname,
     104             :                                       GBool bUpdate,
     105             :                                       GBool bTestOpenNoError /*=FALSE*/)
     106             : {
     107        2177 :     IMapInfoFile *poFile = nullptr;
     108        2177 :     int nLen = 0;
     109             : 
     110        2177 :     if (pszFname)
     111        2177 :         nLen = static_cast<int>(strlen(pszFname));
     112             : 
     113        2177 :     if (nLen > 4 && (EQUAL(pszFname + nLen - 4, ".MIF") ||
     114        1302 :                      EQUAL(pszFname + nLen - 4, ".MID")))
     115             :     {
     116             :         /*-------------------------------------------------------------
     117             :          * MIF/MID file
     118             :          *------------------------------------------------------------*/
     119         875 :         poFile = new MIFFile(poDS);
     120             :     }
     121        1302 :     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        1302 :         char *pszAdjFname = CPLStrdup(pszFname);
     128        1302 :         GBool bFoundFields = FALSE;
     129        1302 :         GBool bFoundView = FALSE;
     130        1302 :         GBool bFoundSeamless = FALSE;
     131             : 
     132        1302 :         TABAdjustFilenameExtension(pszAdjFname);
     133        1302 :         VSILFILE *fp = VSIFOpenL(pszAdjFname, "r");
     134        1302 :         const char *pszLine = nullptr;
     135       12028 :         while (fp && (pszLine = CPLReadLineL(fp)) != nullptr)
     136             :         {
     137       22016 :             while (isspace(static_cast<unsigned char>(*pszLine)))
     138       11290 :                 pszLine++;
     139       10726 :             if (STARTS_WITH_CI(pszLine, "Fields"))
     140        1299 :                 bFoundFields = TRUE;
     141        9427 :             else if (STARTS_WITH_CI(pszLine, "create view"))
     142           2 :                 bFoundView = TRUE;
     143        9425 :             else if (STARTS_WITH_CI(pszLine, "\"\\IsSeamless\" = \"TRUE\""))
     144           1 :                 bFoundSeamless = TRUE;
     145             :         }
     146             : 
     147        1302 :         if (bFoundView)
     148           2 :             poFile = new TABView(poDS);
     149        1300 :         else if (bFoundFields && bFoundSeamless)
     150           1 :             poFile = new TABSeamless(poDS);
     151        1299 :         else if (bFoundFields)
     152        1298 :             poFile = new TABFile(poDS);
     153             : 
     154        1302 :         if (fp)
     155        1302 :             VSIFCloseL(fp);
     156             : 
     157        1302 :         CPLFree(pszAdjFname);
     158             :     }
     159             : 
     160             :     /*-----------------------------------------------------------------
     161             :      * Perform the open() call
     162             :      *----------------------------------------------------------------*/
     163        4353 :     if (poFile && poFile->Open(pszFname, bUpdate ? TABReadWrite : TABRead,
     164        2176 :                                bTestOpenNoError) != 0)
     165             :     {
     166          75 :         delete poFile;
     167          75 :         poFile = nullptr;
     168             :     }
     169             : 
     170        2177 :     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        2177 :     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      223071 : OGRFeature *IMapInfoFile::GetNextFeature()
     186             : {
     187      223071 :     GIntBig nFeatureId = 0;
     188             : 
     189      547509 :     while ((nFeatureId = GetNextFeatureId(m_nCurFeatureId)) != -1)
     190             :     {
     191      536698 :         OGRGeometry *poGeom = nullptr;
     192      536698 :         OGRFeature *poFeatureRef = GetFeatureRef(nFeatureId);
     193      536698 :         if (poFeatureRef == nullptr)
     194         328 :             return nullptr;
     195     1451540 :         else if ((m_poFilterGeom == nullptr ||
     196      378805 :                   ((poGeom = poFeatureRef->GetGeometryRef()) != nullptr &&
     197     1129310 :                    FilterGeometry(poGeom))) &&
     198      214136 :                  (m_poAttrQuery == nullptr ||
     199        4894 :                   m_poAttrQuery->Evaluate(poFeatureRef)))
     200             :         {
     201             :             // Avoid cloning feature... return the copy owned by the class
     202      211932 :             CPLAssert(poFeatureRef == m_poCurFeature);
     203      211932 :             m_poCurFeature = nullptr;
     204      211932 :             if (poFeatureRef->GetGeometryRef() != nullptr)
     205      211763 :                 poFeatureRef->GetGeometryRef()->assignSpatialReference(
     206      211763 :                     GetSpatialRef());
     207      211932 :             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         111 :             poTABFeature = new TABRegion(poFeature->GetDefnRef());
     301         111 :             if (poFeature->GetStyleString())
     302             :             {
     303          19 :                 poTABRegionFeature = cpl::down_cast<TABRegion *>(poTABFeature);
     304          19 :                 poTABRegionFeature->SetPenFromStyleString(
     305          19 :                     poFeature->GetStyleString());
     306             : 
     307          19 :                 poTABRegionFeature->SetBrushFromStyleString(
     308          19 :                     poFeature->GetStyleString());
     309             :             }
     310         111 :             break;
     311             :         /*-------------------------------------------------------------
     312             :          * LINE/PLINE/MULTIPLINE
     313             :          *------------------------------------------------------------*/
     314         238 :         case wkbLineString:
     315             :         case wkbMultiLineString:
     316         238 :             poTABFeature = new TABPolyline(poFeature->GetDefnRef());
     317         238 :             if (poFeature->GetStyleString())
     318             :             {
     319             :                 poTABPolylineFeature =
     320          13 :                     cpl::down_cast<TABPolyline *>(poTABFeature);
     321          13 :                 poTABPolylineFeature->SetPenFromStyleString(
     322          13 :                     poFeature->GetStyleString());
     323             :             }
     324         238 :             break;
     325             :         /*-------------------------------------------------------------
     326             :          * Collection types that are not directly supported... convert
     327             :          * to multiple features in output file through recursive calls.
     328             :          *------------------------------------------------------------*/
     329          18 :         case wkbGeometryCollection:
     330             :         case wkbMultiPoint:
     331             :         {
     332          18 :             OGRErr eStatus = OGRERR_NONE;
     333          18 :             assert(poGeom);  // for clang static analyzer
     334          18 :             OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
     335          18 :             OGRFeature *poTmpFeature = poFeature->Clone();
     336             : 
     337         112 :             for (int i = 0; eStatus == OGRERR_NONE && poColl != nullptr &&
     338          56 :                             i < poColl->getNumGeometries();
     339             :                  i++)
     340             :             {
     341          38 :                 poTmpFeature->SetFID(OGRNullFID);
     342          38 :                 poTmpFeature->SetGeometry(poColl->getGeometryRef(i));
     343          38 :                 eStatus = ICreateFeature(poTmpFeature);
     344             :             }
     345          18 :             delete poTmpFeature;
     346          18 :             return nullptr;
     347             :         }
     348             :         break;
     349             :         /*-------------------------------------------------------------
     350             :          * Unsupported type.... convert to MapInfo geometry NONE
     351             :          *------------------------------------------------------------*/
     352          78 :         case wkbUnknown:
     353             :         default:
     354          78 :             poTABFeature = new TABFeature(poFeature->GetDefnRef());
     355          78 :             break;
     356             :     }
     357             : 
     358       15206 :     if (poGeom != nullptr)
     359       15128 :         poTABFeature->SetGeometryDirectly(poGeom->clone());
     360             : 
     361       31169 :     for (int i = 0; i < poFeature->GetDefnRef()->GetFieldCount(); i++)
     362             :     {
     363       15963 :         poTABFeature->SetField(i, poFeature->GetRawFieldRef(i));
     364             :     }
     365             : 
     366       15206 :     poTABFeature->SetFID(poFeature->GetFID());
     367             : 
     368       15206 :     return poTABFeature;
     369             : }
     370             : 
     371             : /**********************************************************************
     372             :  *                   IMapInfoFile::ICreateFeature()
     373             :  *
     374             :  * Standard OGR CreateFeature implementation.  This method is used
     375             :  * to create a new feature in current dataset
     376             :  **********************************************************************/
     377       15008 : OGRErr IMapInfoFile::ICreateFeature(OGRFeature *poFeature)
     378             : {
     379       15008 :     TABFeature *poTABFeature = CreateTABFeature(poFeature);
     380       15008 :     if (poTABFeature == nullptr) /* MultiGeometry */
     381          18 :         return OGRERR_NONE;
     382             : 
     383       14990 :     OGRErr eErr = CreateFeature(poTABFeature);
     384       14990 :     if (eErr == OGRERR_NONE)
     385       14989 :         poFeature->SetFID(poTABFeature->GetFID());
     386             : 
     387       14990 :     delete poTABFeature;
     388             : 
     389       14990 :     return eErr;
     390             : }
     391             : 
     392             : /**********************************************************************
     393             :  *                   IMapInfoFile::GetFeature()
     394             :  *
     395             :  * Standard OGR GetFeature implementation.  This method is used
     396             :  * to get the wanted (nFeatureId) feature, a NULL value will be
     397             :  * returned on error.
     398             :  **********************************************************************/
     399         292 : OGRFeature *IMapInfoFile::GetFeature(GIntBig nFeatureId)
     400             : {
     401             :     /*fprintf(stderr, "GetFeature(%ld)\n", nFeatureId);*/
     402             : 
     403         292 :     OGRFeature *poFeatureRef = GetFeatureRef(nFeatureId);
     404         292 :     if (poFeatureRef)
     405             :     {
     406             :         // Avoid cloning feature... return the copy owned by the class
     407         274 :         CPLAssert(poFeatureRef == m_poCurFeature);
     408         274 :         m_poCurFeature = nullptr;
     409             : 
     410         274 :         return poFeatureRef;
     411             :     }
     412             :     else
     413          18 :         return nullptr;
     414             : }
     415             : 
     416             : /************************************************************************/
     417             : /*                            GetTABType()                              */
     418             : /*                                                                      */
     419             : /*      Create a native field based on a generic OGR definition.        */
     420             : /************************************************************************/
     421             : 
     422         352 : int IMapInfoFile::GetTABType(const OGRFieldDefn *poField,
     423             :                              TABFieldType *peTABType, int *pnWidth,
     424             :                              int *pnPrecision)
     425             : {
     426             :     TABFieldType eTABType;
     427         352 :     int nWidth = poField->GetWidth();
     428             :     int nPrecision =
     429         352 :         poField->GetType() == OFTReal ? poField->GetPrecision() : 0;
     430             : 
     431         352 :     if (poField->GetType() == OFTInteger)
     432             :     {
     433         103 :         if (poField->GetSubType() == OFSTBoolean)
     434             :         {
     435           2 :             eTABType = TABFLogical;
     436           2 :             nWidth = 1;
     437             :         }
     438             :         else
     439             :         {
     440         101 :             eTABType = TABFInteger;
     441         101 :             if (nWidth == 0)
     442          86 :                 nWidth = 12;
     443             :         }
     444             :     }
     445         249 :     else if (poField->GetType() == OFTInteger64)
     446             :     {
     447           4 :         eTABType = TABFLargeInt;
     448           4 :         if (nWidth == 0)
     449           4 :             nWidth = 20;
     450             :     }
     451         245 :     else if (poField->GetType() == OFTReal)
     452             :     {
     453          41 :         if (nWidth == 0 && poField->GetPrecision() == 0)
     454             :         {
     455          29 :             eTABType = TABFFloat;
     456          29 :             nWidth = 32;
     457             :         }
     458             :         else
     459             :         {
     460          12 :             eTABType = TABFDecimal;
     461             :             // Enforce Mapinfo limits, otherwise MapInfo will crash (#6392)
     462          12 :             if (nWidth > 20 || nWidth - nPrecision < 2 || nPrecision > 16)
     463             :             {
     464           4 :                 if (nWidth > 20)
     465           4 :                     nWidth = 20;
     466           4 :                 if (nWidth - nPrecision < 2)
     467           1 :                     nPrecision = nWidth - 2;
     468           4 :                 if (nPrecision > 16)
     469           1 :                     nPrecision = 16;
     470           4 :                 CPLDebug("MITAB",
     471             :                          "Adjusting initial width,precision of %s from %d,%d "
     472             :                          "to %d,%d",
     473             :                          poField->GetNameRef(), poField->GetWidth(),
     474             :                          poField->GetPrecision(), nWidth, nPrecision);
     475             :             }
     476             :         }
     477             :     }
     478         204 :     else if (poField->GetType() == OFTDate)
     479             :     {
     480          19 :         eTABType = TABFDate;
     481          19 :         if (nWidth == 0)
     482          17 :             nWidth = 10;
     483             :     }
     484         185 :     else if (poField->GetType() == OFTTime)
     485             :     {
     486           3 :         eTABType = TABFTime;
     487           3 :         if (nWidth == 0)
     488           1 :             nWidth = 9;
     489             :     }
     490         182 :     else if (poField->GetType() == OFTDateTime)
     491             :     {
     492          19 :         eTABType = TABFDateTime;
     493          19 :         if (nWidth == 0)
     494          17 :             nWidth = 19;
     495             :     }
     496         163 :     else if (poField->GetType() == OFTString)
     497             :     {
     498         163 :         eTABType = TABFChar;
     499         163 :         if (nWidth == 0)
     500         118 :             nWidth = 254;
     501             :         else
     502          45 :             nWidth = std::min(254, nWidth);
     503             :     }
     504             :     else
     505             :     {
     506           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     507             :                  "IMapInfoFile::CreateField() called with unsupported field"
     508             :                  " type %d.\n"
     509             :                  "Note that Mapinfo files don't support list field types.\n",
     510           0 :                  poField->GetType());
     511             : 
     512           0 :         return -1;
     513             :     }
     514             : 
     515         352 :     if (peTABType)
     516         333 :         *peTABType = eTABType;
     517         352 :     if (pnWidth)
     518         341 :         *pnWidth = nWidth;
     519         352 :     if (pnPrecision)
     520         341 :         *pnPrecision = nPrecision;
     521             : 
     522         352 :     return 0;
     523             : }
     524             : 
     525             : /************************************************************************/
     526             : /*                            CreateField()                             */
     527             : /*                                                                      */
     528             : /*      Create a native field based on a generic OGR definition.        */
     529             : /************************************************************************/
     530             : 
     531         322 : OGRErr IMapInfoFile::CreateField(const OGRFieldDefn *poField, int bApproxOK)
     532             : 
     533             : {
     534             :     TABFieldType eTABType;
     535         322 :     int nWidth = 0;
     536         322 :     int nPrecision = 0;
     537             : 
     538         322 :     if (GetTABType(poField, &eTABType, &nWidth, &nPrecision) < 0)
     539           0 :         return OGRERR_FAILURE;
     540             : 
     541         322 :     if (AddFieldNative(poField->GetNameRef(), eTABType, nWidth, nPrecision,
     542         644 :                        FALSE, FALSE, bApproxOK) > -1)
     543         322 :         return OGRERR_NONE;
     544             : 
     545           0 :     return OGRERR_FAILURE;
     546             : }
     547             : 
     548             : /**********************************************************************
     549             :  *                   IMapInfoFile::SetCharset()
     550             :  *
     551             :  * Set the charset for the tab header.
     552             :  *
     553             :  *
     554             :  * Returns 0 on success, -1 on error.
     555             :  **********************************************************************/
     556        2374 : int IMapInfoFile::SetCharset(const char *pszCharset)
     557             : {
     558        2374 :     if (pszCharset && strlen(pszCharset) > 0)
     559             :     {
     560        2374 :         if (pszCharset == m_pszCharset)
     561             :         {
     562           0 :             return 0;
     563             :         }
     564        2374 :         CPLFree(m_pszCharset);
     565        2374 :         m_pszCharset = CPLStrdup(pszCharset);
     566        2374 :         return 0;
     567             :     }
     568           0 :     return -1;
     569             : }
     570             : 
     571        5327 : const char *IMapInfoFile::GetCharset() const
     572             : {
     573        5327 :     return m_pszCharset;
     574             : }
     575             : 
     576             : // Table is adopted from
     577             : // http://www.i-signum.com/Formation/download/MB_ReferenceGuide.pdf pp. 127-128
     578             : // NOTE: if modifying this table, please keep doc/source/drivers/vector/mapinfo_encodings.csv in sync
     579             : static const char *const apszCharsets[][2] = {
     580             :     {"Neutral", ""},                 // No character conversions performed.
     581             :     {"ISO8859_1", "ISO-8859-1"},     // ISO 8859-1 (UNIX)
     582             :     {"ISO8859_2", "ISO-8859-2"},     // ISO 8859-2 (UNIX)
     583             :     {"ISO8859_3", "ISO-8859-3"},     // ISO 8859-3 (UNIX)
     584             :     {"ISO8859_4", "ISO-8859-4"},     // ISO 8859-4 (UNIX)
     585             :     {"ISO8859_5", "ISO-8859-5"},     // ISO 8859-5 (UNIX)
     586             :     {"ISO8859_6", "ISO-8859-6"},     // ISO 8859-6 (UNIX)
     587             :     {"ISO8859_7", "ISO-8859-7"},     // ISO 8859-7 (UNIX)
     588             :     {"ISO8859_8", "ISO-8859-8"},     // ISO 8859-8 (UNIX)
     589             :     {"ISO8859_9", "ISO-8859-9"},     // ISO 8859-9 (UNIX)
     590             :     {"PackedEUCJapaese", "EUC-JP"},  // UNIX, standard Japanese implementation.
     591             :     {"WindowsLatin1", "CP1252"},
     592             :     {"WindowsLatin2", "CP1250"},
     593             :     {"WindowsArabic", "CP1256"},
     594             :     {"WindowsCyrillic", "CP1251"},
     595             :     {"WindowsBalticRim", "CP1257"},
     596             :     {"WindowsGreek", "CP1253"},
     597             :     {"WindowsHebrew", "CP1255"},
     598             :     {"WindowsTurkish", "CP1254"},     // Windows Eastern Europe
     599             :     {"WindowsTradChinese", "CP950"},  // Windows Traditional Chinese
     600             :     {"WindowsSimpChinese", "CP936"},  // Windows Simplified Chinese
     601             :     {"WindowsJapanese", "CP932"},
     602             :     {"WindowsKorean", "CP949"},
     603             :     {"CodePage437", "CP437"},  // DOS Code Page 437 = IBM Extended ASCII
     604             :     {"CodePage850", "CP850"},  // DOS Code Page 850 = Multilingual
     605             :     {"CodePage852", "CP852"},  // DOS Code Page 852 = Eastern Europe
     606             :     {"CodePage855", "CP855"},  // DOS Code Page 855 = Cyrillic
     607             :     {"CodePage857", "CP857"},
     608             :     {"CodePage860", "CP860"},  // DOS Code Page 860 = Portuguese
     609             :     {"CodePage861", "CP861"},  // DOS Code Page 861 = Icelandic
     610             :     {"CodePage863", "CP863"},  // DOS Code Page 863 = French Canadian
     611             :     {"CodePage864", "CP864"},  // DOS Code Page 864 = Arabic
     612             :     {"CodePage865", "CP865"},  // DOS Code Page 865 = Nordic
     613             :     {"CodePage869", "CP869"},  // DOS Code Page 869 = Modern Greek
     614             :     {"LICS", ""},              // Lotus worksheet release 1,2 character set
     615             :     {"LMBCS", ""},             // Lotus worksheet release 3,4 character set
     616             :     {"UTF-8", "UTF-8"},
     617             :     {nullptr, nullptr}};
     618             : 
     619        7530 : const char *IMapInfoFile::CharsetToEncoding(const char *pszCharset)
     620             : {
     621        7530 :     if (pszCharset == nullptr)
     622             :     {
     623         874 :         return apszCharsets[0][1];
     624             :     }
     625             : 
     626       18049 :     for (size_t i = 0; apszCharsets[i][0] != nullptr; ++i)
     627             :     {
     628       18043 :         if (EQUAL(pszCharset, apszCharsets[i][0]))
     629             :         {
     630        6650 :             return apszCharsets[i][1];
     631             :         }
     632             :     }
     633             : 
     634           6 :     CPLError(CE_Warning, CPLE_NotSupported,
     635             :              "Cannot find iconv encoding corresponding to MapInfo %s charset",
     636             :              pszCharset);
     637           6 :     return apszCharsets[0][1];
     638             : }
     639             : 
     640         361 : const char *IMapInfoFile::EncodingToCharset(const char *pszEncoding)
     641             : {
     642         361 :     if (pszEncoding == nullptr)
     643             :     {
     644         352 :         return apszCharsets[0][0];
     645             :     }
     646             : 
     647         179 :     for (size_t i = 0; apszCharsets[i][1] != nullptr; ++i)
     648             :     {
     649         179 :         if (EQUAL(pszEncoding, apszCharsets[i][1]))
     650             :         {
     651           9 :             return apszCharsets[i][0];
     652             :         }
     653             :     }
     654             : 
     655           0 :     CPLError(CE_Warning, CPLE_NotSupported,
     656             :              "Cannot find MapInfo charset corresponding to iconv %s encoding",
     657             :              pszEncoding);
     658           0 :     return apszCharsets[0][0];
     659             : }
     660             : 
     661        3920 : const char *IMapInfoFile::GetEncoding() const
     662             : {
     663        3920 :     return CharsetToEncoding(GetCharset());
     664             : }
     665             : 
     666           0 : void IMapInfoFile::SetEncoding(const char *pszEncoding)
     667             : {
     668           0 :     SetCharset(EncodingToCharset(pszEncoding));
     669           0 : }
     670             : 
     671         360 : void IMapInfoFile::SetStrictLaundering(bool bStrictLaundering)
     672             : {
     673         360 :     m_bStrictLaundering = bStrictLaundering;
     674         360 : }
     675             : 
     676          69 : int IMapInfoFile::TestUtf8Capability() const
     677             : {
     678          69 :     const char *pszEncoding(GetEncoding());
     679          69 :     if (strlen(pszEncoding) == 0)
     680             :     {
     681          55 :         return FALSE;
     682             :     }
     683             : 
     684          14 :     return CPLCanRecode("test", GetEncoding(), CPL_ENC_UTF8);
     685             : }
     686             : 
     687         493 : CPLString IMapInfoFile::NormalizeFieldName(const char *pszName) const
     688             : {
     689         986 :     CPLString osName(pszName);
     690         493 :     if (strlen(GetEncoding()) > 0)
     691          84 :         osName.Recode(CPL_ENC_UTF8, GetEncoding());
     692             : 
     693             :     char szNewFieldName[31 + 1]; /* 31 is the max characters for a field name*/
     694         493 :     unsigned int nRenameNum = 1;
     695             : 
     696         493 :     strncpy(szNewFieldName, osName.c_str(), sizeof(szNewFieldName) - 1);
     697         493 :     szNewFieldName[sizeof(szNewFieldName) - 1] = '\0';
     698             : 
     699         986 :     while (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
     700        1479 :                m_oSetFields.end() &&
     701             :            nRenameNum < 10)
     702             :     {
     703           0 :         CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.29s_%.1u",
     704             :                     osName.c_str(), nRenameNum);
     705           0 :         nRenameNum++;
     706             :     }
     707             : 
     708         986 :     while (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
     709        1479 :                m_oSetFields.end() &&
     710             :            nRenameNum < 100)
     711             :     {
     712           0 :         CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.29s%.2u",
     713             :                     osName.c_str(), nRenameNum);
     714           0 :         nRenameNum++;
     715             :     }
     716             : 
     717         493 :     if (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
     718         986 :         m_oSetFields.end())
     719             :     {
     720           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     721             :                  "Too many field names like '%s' when truncated to 31 letters "
     722             :                  "for MapInfo format.",
     723             :                  pszName);
     724             :     }
     725             : 
     726         493 :     CPLString osNewFieldName(szNewFieldName);
     727         493 :     if (strlen(GetEncoding()) > 0)
     728          84 :         osNewFieldName.Recode(GetEncoding(), CPL_ENC_UTF8);
     729             : 
     730         493 :     if (!EQUAL(pszName, osNewFieldName.c_str()))
     731             :     {
     732           0 :         CPLError(CE_Warning, CPLE_NotSupported,
     733             :                  "Normalized/laundered field name: '%s' to '%s'", pszName,
     734             :                  osNewFieldName.c_str());
     735             :     }
     736             : 
     737         986 :     return osNewFieldName;
     738             : }

Generated by: LCOV version 1.14