Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/mitab/mitab_imapinfofile.cpp
Line
Count
Source
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
IMapInfoFile::IMapInfoFile(GDALDataset *poDS)
46
51.4k
    : m_poDS(poDS), m_nCurFeatureId(0), m_poCurFeature(nullptr),
47
51.4k
      m_bBoundsSet(FALSE), m_pszCharset(nullptr)
48
51.4k
{
49
51.4k
}
50
51
/**********************************************************************
52
 *                   IMapInfoFile::~IMapInfoFile()
53
 *
54
 * Destructor.
55
 **********************************************************************/
56
IMapInfoFile::~IMapInfoFile()
57
51.4k
{
58
51.4k
    if (m_poCurFeature)
59
0
    {
60
0
        delete m_poCurFeature;
61
0
        m_poCurFeature = nullptr;
62
0
    }
63
64
51.4k
    CPLFree(m_pszCharset);
65
51.4k
    m_pszCharset = nullptr;
66
51.4k
}
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
int IMapInfoFile::Open(const char *pszFname, const char *pszAccess,
76
                       GBool bTestOpenNoError, const char *pszCharset)
77
0
{
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
0
    else
84
0
    {
85
0
        CPLError(CE_Failure, CPLE_FileIO,
86
0
                 "Open() failed: access mode \"%s\" not supported", pszAccess);
87
0
        return -1;
88
0
    }
89
0
}
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
IMapInfoFile *IMapInfoFile::SmartOpen(GDALDataset *poDS, const char *pszFname,
104
                                      GBool bUpdate,
105
                                      GBool bTestOpenNoError /*=FALSE*/)
106
51.3k
{
107
51.3k
    IMapInfoFile *poFile = nullptr;
108
51.3k
    int nLen = 0;
109
110
51.3k
    if (pszFname)
111
51.3k
        nLen = static_cast<int>(strlen(pszFname));
112
113
51.3k
    if (nLen > 4 && (EQUAL(pszFname + nLen - 4, ".MIF") ||
114
5.38k
                     EQUAL(pszFname + nLen - 4, ".MID")))
115
46.0k
    {
116
        /*-------------------------------------------------------------
117
         * MIF/MID file
118
         *------------------------------------------------------------*/
119
46.0k
        poFile = new MIFFile(poDS);
120
46.0k
    }
121
5.38k
    else if (nLen > 4 && EQUAL(pszFname + nLen - 4, ".TAB"))
122
5.38k
    {
123
        /*-------------------------------------------------------------
124
         * .TAB file ... is it a TABFileView or a TABFile?
125
         * We have to read the .tab header to find out.
126
         *------------------------------------------------------------*/
127
5.38k
        char *pszAdjFname = CPLStrdup(pszFname);
128
5.38k
        GBool bFoundFields = FALSE;
129
5.38k
        GBool bFoundView = FALSE;
130
5.38k
        GBool bFoundSeamless = FALSE;
131
132
5.38k
        TABAdjustFilenameExtension(pszAdjFname);
133
5.38k
        VSILFILE *fp = VSIFOpenL(pszAdjFname, "r");
134
5.38k
        const char *pszLine = nullptr;
135
3.53M
        while (fp && (pszLine = CPLReadLineL(fp)) != nullptr)
136
3.53M
        {
137
3.87M
            while (isspace(static_cast<unsigned char>(*pszLine)))
138
340k
                pszLine++;
139
3.53M
            if (STARTS_WITH_CI(pszLine, "Fields"))
140
8.54k
                bFoundFields = TRUE;
141
3.52M
            else if (STARTS_WITH_CI(pszLine, "create view"))
142
9.46k
                bFoundView = TRUE;
143
3.51M
            else if (STARTS_WITH_CI(pszLine, "\"\\IsSeamless\" = \"TRUE\""))
144
713
                bFoundSeamless = TRUE;
145
3.53M
        }
146
147
5.38k
        if (bFoundView)
148
1.16k
            poFile = new TABView(poDS);
149
4.21k
        else if (bFoundFields && bFoundSeamless)
150
105
            poFile = new TABSeamless(poDS);
151
4.11k
        else if (bFoundFields)
152
2.60k
            poFile = new TABFile(poDS);
153
154
5.38k
        if (fp)
155
4.98k
            VSIFCloseL(fp);
156
157
5.38k
        CPLFree(pszAdjFname);
158
5.38k
    }
159
160
    /*-----------------------------------------------------------------
161
     * Perform the open() call
162
     *----------------------------------------------------------------*/
163
51.3k
    if (poFile && poFile->Open(pszFname, bUpdate ? TABReadWrite : TABRead,
164
49.8k
                               bTestOpenNoError) != 0)
165
22.2k
    {
166
22.2k
        delete poFile;
167
22.2k
        poFile = nullptr;
168
22.2k
    }
169
170
51.3k
    if (!bTestOpenNoError && poFile == nullptr)
171
0
    {
172
0
        CPLError(CE_Failure, CPLE_FileIO,
173
0
                 "%s could not be opened as a MapInfo dataset.", pszFname);
174
0
    }
175
176
51.3k
    return poFile;
177
51.3k
}
178
179
/**********************************************************************
180
 *                   IMapInfoFile::GetNextFeature()
181
 *
182
 * Standard OGR GetNextFeature implementation.  This method is used
183
 * to retrieve the next OGRFeature.
184
 **********************************************************************/
185
OGRFeature *IMapInfoFile::GetNextFeature()
186
588k
{
187
588k
    GIntBig nFeatureId = 0;
188
189
588k
    while ((nFeatureId = GetNextFeatureId(m_nCurFeatureId)) != -1)
190
577k
    {
191
577k
        OGRGeometry *poGeom = nullptr;
192
577k
        OGRFeature *poFeatureRef = GetFeatureRef(nFeatureId);
193
577k
        if (poFeatureRef == nullptr)
194
10.2k
            return nullptr;
195
567k
        else if ((m_poFilterGeom == nullptr ||
196
0
                  ((poGeom = poFeatureRef->GetGeometryRef()) != nullptr &&
197
0
                   FilterGeometry(poGeom))) &&
198
567k
                 (m_poAttrQuery == nullptr ||
199
0
                  m_poAttrQuery->Evaluate(poFeatureRef)))
200
567k
        {
201
            // Avoid cloning feature... return the copy owned by the class
202
567k
            CPLAssert(poFeatureRef == m_poCurFeature);
203
567k
            m_poCurFeature = nullptr;
204
567k
            if (poFeatureRef->GetGeometryRef() != nullptr)
205
350k
                poFeatureRef->GetGeometryRef()->assignSpatialReference(
206
350k
                    GetSpatialRef());
207
567k
            return poFeatureRef;
208
567k
        }
209
577k
    }
210
11.4k
    return nullptr;
211
588k
}
212
213
/**********************************************************************
214
 *                   IMapInfoFile::CreateTABFeature()
215
 *
216
 * Instantiate a TABFeature* from a OGRFeature* (or NULL on error)
217
 **********************************************************************/
218
219
TABFeature *IMapInfoFile::CreateTABFeature(OGRFeature *poFeature)
220
372k
{
221
372k
    TABFeature *poTABFeature = nullptr;
222
372k
    OGRwkbGeometryType eGType;
223
372k
    TABPoint *poTABPointFeature = nullptr;
224
372k
    TABRegion *poTABRegionFeature = nullptr;
225
372k
    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
372k
    OGRGeometry *poGeom = poFeature->GetGeometryRef();
233
372k
    if (poGeom != nullptr)
234
24.4k
        eGType = poGeom->getGeometryType();
235
347k
    else
236
347k
        eGType = wkbNone;
237
238
372k
    switch (wkbFlatten(eGType))
239
372k
    {
240
        /*-------------------------------------------------------------
241
         * POINT
242
         *------------------------------------------------------------*/
243
4.58k
        case wkbPoint:
244
4.58k
        {
245
4.58k
            const char *pszStyleString = poFeature->GetStyleString();
246
4.58k
            if (pszStyleString)
247
0
            {
248
0
                if (strstr(pszStyleString, "LABEL("))
249
0
                {
250
0
                    auto poText = new TABText(poFeature->GetDefnRef());
251
0
                    poText->SetLabelFromStyleString(pszStyleString);
252
0
                    poTABFeature = poText;
253
254
0
                    if (strstr(pszStyleString, "SYMBOL("))
255
0
                    {
256
0
                        CPLError(CE_Warning, CPLE_AppDefined,
257
0
                                 "OGR style string contains both "
258
0
                                 "Label and Symbol parts. "
259
0
                                 "Only Label taken into account due to "
260
0
                                 "MapInfo TAB/MIF format limitations.");
261
0
                    }
262
0
                }
263
0
                else
264
0
                {
265
0
                    TABFeatureClass featureClass =
266
0
                        ITABFeatureSymbol::GetSymbolFeatureClass(
267
0
                            pszStyleString);
268
0
                    if (featureClass == TABFCFontPoint)
269
0
                    {
270
0
                        poTABFeature =
271
0
                            new TABFontPoint(poFeature->GetDefnRef());
272
0
                    }
273
0
                    else if (featureClass == TABFCCustomPoint)
274
0
                    {
275
0
                        poTABFeature =
276
0
                            new TABCustomPoint(poFeature->GetDefnRef());
277
0
                    }
278
0
                    else
279
0
                    {
280
0
                        poTABFeature = new TABPoint(poFeature->GetDefnRef());
281
0
                    }
282
0
                    poTABPointFeature =
283
0
                        cpl::down_cast<TABPoint *>(poTABFeature);
284
0
                    poTABPointFeature->SetSymbolFromStyleString(
285
0
                        poFeature->GetStyleString());
286
0
                }
287
0
            }
288
4.58k
            else
289
4.58k
            {
290
4.58k
                poTABFeature = new TABPoint(poFeature->GetDefnRef());
291
4.58k
            }
292
4.58k
            break;
293
0
        }
294
295
        /*-------------------------------------------------------------
296
         * REGION
297
         *------------------------------------------------------------*/
298
3.05k
        case wkbPolygon:
299
3.82k
        case wkbMultiPolygon:
300
3.82k
        {
301
3.82k
            poTABFeature = new TABRegion(poFeature->GetDefnRef());
302
3.82k
            if (poFeature->GetStyleString())
303
0
            {
304
0
                poTABRegionFeature = cpl::down_cast<TABRegion *>(poTABFeature);
305
0
                poTABRegionFeature->SetPenFromStyleString(
306
0
                    poFeature->GetStyleString());
307
308
0
                poTABRegionFeature->SetBrushFromStyleString(
309
0
                    poFeature->GetStyleString());
310
0
            }
311
3.82k
            break;
312
3.05k
        }
313
        /*-------------------------------------------------------------
314
         * LINE/PLINE/MULTIPLINE
315
         *------------------------------------------------------------*/
316
6.78k
        case wkbLineString:
317
12.0k
        case wkbMultiLineString:
318
12.0k
        {
319
12.0k
            poTABFeature = new TABPolyline(poFeature->GetDefnRef());
320
12.0k
            if (poFeature->GetStyleString())
321
1
            {
322
1
                poTABPolylineFeature =
323
1
                    cpl::down_cast<TABPolyline *>(poTABFeature);
324
1
                poTABPolylineFeature->SetPenFromStyleString(
325
1
                    poFeature->GetStyleString());
326
1
            }
327
12.0k
            break;
328
6.78k
        }
329
        /*-------------------------------------------------------------
330
         * Collection types that are not directly supported... convert
331
         * to multiple features in output file through recursive calls.
332
         *------------------------------------------------------------*/
333
2.15k
        case wkbGeometryCollection:
334
2.42k
        case wkbMultiPoint:
335
2.42k
        {
336
2.42k
            OGRErr eStatus = OGRERR_NONE;
337
2.42k
            assert(poGeom);  // for clang static analyzer
338
2.42k
            OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
339
2.42k
            auto poTmpFeature = std::unique_ptr<OGRFeature>(poFeature->Clone());
340
341
5.54k
            for (int i = 0; eStatus == OGRERR_NONE && poColl != nullptr &&
342
4.70k
                            i < poColl->getNumGeometries();
343
3.12k
                 i++)
344
3.12k
            {
345
3.12k
                poTmpFeature->SetFID(OGRNullFID);
346
3.12k
                poTmpFeature->SetGeometry(poColl->getGeometryRef(i));
347
3.12k
                eStatus = ICreateFeature(poTmpFeature.get());
348
3.12k
            }
349
2.42k
            break;
350
2.42k
        }
351
352
        /*-------------------------------------------------------------
353
         * Unsupported type.... convert to MapInfo geometry NONE
354
         *------------------------------------------------------------*/
355
349k
        default:
356
349k
        {
357
349k
            poTABFeature = new TABFeature(poFeature->GetDefnRef());
358
349k
            break;
359
2.42k
        }
360
372k
    }
361
362
372k
    if (poTABFeature)
363
369k
    {
364
369k
        if (poGeom != nullptr)
365
22.0k
            poTABFeature->SetGeometryDirectly(poGeom->clone());
366
367
1.49M
        for (int i = 0; i < poFeature->GetDefnRef()->GetFieldCount(); i++)
368
1.12M
        {
369
1.12M
            poTABFeature->SetField(i, poFeature->GetRawFieldRef(i));
370
1.12M
        }
371
372
369k
        poTABFeature->SetFID(poFeature->GetFID());
373
369k
    }
374
375
372k
    return poTABFeature;
376
372k
}
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
OGRErr IMapInfoFile::ICreateFeature(OGRFeature *poFeature)
385
372k
{
386
372k
    TABFeature *poTABFeature = CreateTABFeature(poFeature);
387
372k
    if (poTABFeature == nullptr) /* MultiGeometry */
388
2.42k
        return OGRERR_NONE;
389
390
369k
    OGRErr eErr = CreateFeature(poTABFeature);
391
369k
    if (eErr == OGRERR_NONE)
392
363k
        poFeature->SetFID(poTABFeature->GetFID());
393
394
369k
    delete poTABFeature;
395
396
369k
    return eErr;
397
372k
}
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
OGRFeature *IMapInfoFile::GetFeature(GIntBig nFeatureId)
407
0
{
408
    /*fprintf(stderr, "GetFeature(%ld)\n", nFeatureId);*/
409
410
0
    OGRFeature *poFeatureRef = GetFeatureRef(nFeatureId);
411
0
    if (poFeatureRef)
412
0
    {
413
        // Avoid cloning feature... return the copy owned by the class
414
0
        CPLAssert(poFeatureRef == m_poCurFeature);
415
0
        m_poCurFeature = nullptr;
416
417
0
        return poFeatureRef;
418
0
    }
419
0
    else
420
0
        return nullptr;
421
0
}
422
423
/************************************************************************/
424
/*                            GetTABType()                              */
425
/*                                                                      */
426
/*      Create a native field based on a generic OGR definition.        */
427
/************************************************************************/
428
429
int IMapInfoFile::GetTABType(const OGRFieldDefn *poField,
430
                             TABFieldType *peTABType, int *pnWidth,
431
                             int *pnPrecision)
432
32.2k
{
433
32.2k
    TABFieldType eTABType;
434
32.2k
    int nWidth = poField->GetWidth();
435
32.2k
    int nPrecision =
436
32.2k
        poField->GetType() == OFTReal ? poField->GetPrecision() : 0;
437
438
32.2k
    if (poField->GetType() == OFTInteger)
439
13
    {
440
13
        if (poField->GetSubType() == OFSTBoolean)
441
0
        {
442
0
            eTABType = TABFLogical;
443
0
            nWidth = 1;
444
0
        }
445
13
        else
446
13
        {
447
13
            eTABType = TABFInteger;
448
13
            if (nWidth == 0)
449
12
                nWidth = 12;
450
13
        }
451
13
    }
452
32.2k
    else if (poField->GetType() == OFTInteger64)
453
0
    {
454
0
        eTABType = TABFLargeInt;
455
0
        if (nWidth == 0)
456
0
            nWidth = 20;
457
0
    }
458
32.2k
    else if (poField->GetType() == OFTReal)
459
9
    {
460
9
        if (nWidth == 0 && poField->GetPrecision() == 0)
461
9
        {
462
9
            eTABType = TABFFloat;
463
9
            nWidth = 32;
464
9
        }
465
0
        else
466
0
        {
467
0
            eTABType = TABFDecimal;
468
            // Enforce Mapinfo limits, otherwise MapInfo will crash (#6392)
469
0
            if (nWidth > 20 || nWidth - nPrecision < 2 || nPrecision > 16)
470
0
            {
471
0
                if (nWidth > 20)
472
0
                    nWidth = 20;
473
0
                if (nWidth - nPrecision < 2)
474
0
                    nPrecision = nWidth - 2;
475
0
                if (nPrecision > 16)
476
0
                    nPrecision = 16;
477
0
                CPLDebug("MITAB",
478
0
                         "Adjusting initial width,precision of %s from %d,%d "
479
0
                         "to %d,%d",
480
0
                         poField->GetNameRef(), poField->GetWidth(),
481
0
                         poField->GetPrecision(), nWidth, nPrecision);
482
0
            }
483
0
        }
484
9
    }
485
32.2k
    else if (poField->GetType() == OFTDate)
486
0
    {
487
0
        eTABType = TABFDate;
488
0
        if (nWidth == 0)
489
0
            nWidth = 10;
490
0
    }
491
32.2k
    else if (poField->GetType() == OFTTime)
492
0
    {
493
0
        eTABType = TABFTime;
494
0
        if (nWidth == 0)
495
0
            nWidth = 9;
496
0
    }
497
32.2k
    else if (poField->GetType() == OFTDateTime)
498
0
    {
499
0
        eTABType = TABFDateTime;
500
0
        if (nWidth == 0)
501
0
            nWidth = 19;
502
0
    }
503
32.2k
    else if (poField->GetType() == OFTString)
504
32.2k
    {
505
32.2k
        eTABType = TABFChar;
506
32.2k
        if (nWidth == 0)
507
32.2k
            nWidth = 254;
508
6
        else
509
6
            nWidth = std::min(254, nWidth);
510
32.2k
    }
511
0
    else
512
0
    {
513
0
        CPLError(CE_Failure, CPLE_AppDefined,
514
0
                 "IMapInfoFile::CreateField() called with unsupported field"
515
0
                 " type %d.\n"
516
0
                 "Note that Mapinfo files don't support list field types.\n",
517
0
                 poField->GetType());
518
519
0
        return -1;
520
0
    }
521
522
32.2k
    if (peTABType)
523
32.2k
        *peTABType = eTABType;
524
32.2k
    if (pnWidth)
525
32.2k
        *pnWidth = nWidth;
526
32.2k
    if (pnPrecision)
527
32.2k
        *pnPrecision = nPrecision;
528
529
32.2k
    return 0;
530
32.2k
}
531
532
/************************************************************************/
533
/*                            CreateField()                             */
534
/*                                                                      */
535
/*      Create a native field based on a generic OGR definition.        */
536
/************************************************************************/
537
538
OGRErr IMapInfoFile::CreateField(const OGRFieldDefn *poField, int bApproxOK)
539
540
32.2k
{
541
32.2k
    TABFieldType eTABType;
542
32.2k
    int nWidth = 0;
543
32.2k
    int nPrecision = 0;
544
545
32.2k
    if (GetTABType(poField, &eTABType, &nWidth, &nPrecision) < 0)
546
0
        return OGRERR_FAILURE;
547
548
32.2k
    if (AddFieldNative(poField->GetNameRef(), eTABType, nWidth, nPrecision,
549
32.2k
                       FALSE, FALSE, bApproxOK) > -1)
550
32.2k
        return OGRERR_NONE;
551
552
3
    return OGRERR_FAILURE;
553
32.2k
}
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
int IMapInfoFile::SetCharset(const char *pszCharset)
564
49.6k
{
565
49.6k
    if (pszCharset && strlen(pszCharset) > 0)
566
49.6k
    {
567
49.6k
        if (pszCharset == m_pszCharset)
568
0
        {
569
0
            return 0;
570
0
        }
571
49.6k
        CPLFree(m_pszCharset);
572
49.6k
        m_pszCharset = CPLStrdup(pszCharset);
573
49.6k
        return 0;
574
49.6k
    }
575
0
    return -1;
576
49.6k
}
577
578
const char *IMapInfoFile::GetCharset() const
579
1.11M
{
580
1.11M
    return m_pszCharset;
581
1.11M
}
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
const char *IMapInfoFile::CharsetToEncoding(const char *pszCharset)
627
1.19M
{
628
1.19M
    if (pszCharset == nullptr)
629
446k
    {
630
446k
        return apszCharsets[0][1];
631
446k
    }
632
633
9.82M
    for (size_t i = 0; apszCharsets[i][0] != nullptr; ++i)
634
9.65M
    {
635
9.65M
        if (EQUAL(pszCharset, apszCharsets[i][0]))
636
587k
        {
637
587k
            return apszCharsets[i][1];
638
587k
        }
639
9.65M
    }
640
641
164k
    CPLError(CE_Warning, CPLE_NotSupported,
642
164k
             "Cannot find iconv encoding corresponding to MapInfo %s charset",
643
164k
             pszCharset);
644
164k
    return apszCharsets[0][1];
645
751k
}
646
647
const char *IMapInfoFile::EncodingToCharset(const char *pszEncoding)
648
676
{
649
676
    if (pszEncoding == nullptr)
650
676
    {
651
676
        return apszCharsets[0][0];
652
676
    }
653
654
0
    for (size_t i = 0; apszCharsets[i][1] != nullptr; ++i)
655
0
    {
656
0
        if (EQUAL(pszEncoding, apszCharsets[i][1]))
657
0
        {
658
0
            return apszCharsets[i][0];
659
0
        }
660
0
    }
661
662
0
    CPLError(CE_Warning, CPLE_NotSupported,
663
0
             "Cannot find MapInfo charset corresponding to iconv %s encoding",
664
0
             pszEncoding);
665
0
    return apszCharsets[0][0];
666
0
}
667
668
const char *IMapInfoFile::GetEncoding() const
669
1.09M
{
670
1.09M
    return CharsetToEncoding(GetCharset());
671
1.09M
}
672
673
void IMapInfoFile::SetEncoding(const char *pszEncoding)
674
0
{
675
0
    SetCharset(EncodingToCharset(pszEncoding));
676
0
}
677
678
void IMapInfoFile::SetStrictLaundering(bool bStrictLaundering)
679
676
{
680
676
    m_bStrictLaundering = bStrictLaundering;
681
676
}
682
683
int IMapInfoFile::TestUtf8Capability() const
684
0
{
685
0
    const char *pszEncoding(GetEncoding());
686
0
    if (strlen(pszEncoding) == 0)
687
0
    {
688
0
        return FALSE;
689
0
    }
690
691
0
    return CPLCanRecode("test", GetEncoding(), CPL_ENC_UTF8);
692
0
}
693
694
CPLString IMapInfoFile::NormalizeFieldName(const char *pszName) const
695
260k
{
696
260k
    CPLString osName(pszName);
697
260k
    if (strlen(GetEncoding()) > 0)
698
37.0k
        osName.Recode(CPL_ENC_UTF8, GetEncoding());
699
700
260k
    char szNewFieldName[31 + 1]; /* 31 is the max characters for a field name*/
701
260k
    unsigned int nRenameNum = 1;
702
703
260k
    strncpy(szNewFieldName, osName.c_str(), sizeof(szNewFieldName) - 1);
704
260k
    szNewFieldName[sizeof(szNewFieldName) - 1] = '\0';
705
706
1.11M
    while (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
707
1.11M
               m_oSetFields.end() &&
708
919k
           nRenameNum < 10)
709
852k
    {
710
852k
        CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.29s_%.1u",
711
852k
                    osName.c_str(), nRenameNum);
712
852k
        nRenameNum++;
713
852k
    }
714
715
2.64M
    while (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
716
2.64M
               m_oSetFields.end() &&
717
2.39M
           nRenameNum < 100)
718
2.38M
    {
719
2.38M
        CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.29s%.2u",
720
2.38M
                    osName.c_str(), nRenameNum);
721
2.38M
        nRenameNum++;
722
2.38M
    }
723
724
260k
    if (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
725
260k
        m_oSetFields.end())
726
11.0k
    {
727
11.0k
        CPLError(CE_Failure, CPLE_NotSupported,
728
11.0k
                 "Too many field names like '%s' when truncated to 31 letters "
729
11.0k
                 "for MapInfo format.",
730
11.0k
                 pszName);
731
11.0k
    }
732
733
260k
    CPLString osNewFieldName(szNewFieldName);
734
260k
    if (strlen(GetEncoding()) > 0)
735
37.0k
        osNewFieldName.Recode(GetEncoding(), CPL_ENC_UTF8);
736
737
260k
    if (!EQUAL(pszName, osNewFieldName.c_str()))
738
145k
    {
739
145k
        CPLError(CE_Warning, CPLE_NotSupported,
740
145k
                 "Normalized/laundered field name: '%s' to '%s'", pszName,
741
145k
                 osNewFieldName.c_str());
742
145k
    }
743
744
260k
    return osNewFieldName;
745
260k
}