Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/vfk/vfkreader.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  VFK Reader
4
 * Purpose:  Implements VFKReader class.
5
 * Author:   Martin Landa, landa.martin gmail.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2009-2018, Martin Landa <landa.martin gmail.com>
9
 * Copyright (c) 2012-2018, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include <sys/stat.h>
15
16
#include "vfkreader.h"
17
#include "vfkreaderp.h"
18
19
#include "cpl_conv.h"
20
#include "cpl_error.h"
21
#include "cpl_string.h"
22
23
#include "ogr_geometry.h"
24
25
static char *GetDataBlockName(const char *);
26
27
/*!
28
  \brief IVFKReader destructor
29
*/
30
IVFKReader::~IVFKReader()
31
3.32k
{
32
3.32k
}
33
34
/*!
35
  \brief Create new instance of VFKReader
36
37
  \return pointer to VFKReader instance
38
*/
39
IVFKReader *CreateVFKReader(const GDALOpenInfo *poOpenInfo)
40
3.32k
{
41
3.32k
    return new VFKReaderSQLite(poOpenInfo);
42
3.32k
}
43
44
/*!
45
  \brief VFKReader constructor
46
*/
47
VFKReader::VFKReader(const GDALOpenInfo *poOpenInfo)
48
3.32k
    : m_pszEncoding("ISO-8859-2"),  // Encoding, supported are ISO-8859-2,
49
                                    // WINDOWS-1250 and UTF-8.
50
3.32k
      m_poFD(nullptr), m_pszFilename(CPLStrdup(poOpenInfo->pszFilename)),
51
3.32k
      m_poFStat((VSIStatBufL *)CPLCalloc(1, sizeof(VSIStatBufL))),
52
      // VFK is provided in two forms - stative and amendment data.
53
3.32k
      m_bAmendment(false),
54
      m_bFileField(
55
3.32k
          CPLFetchBool(poOpenInfo->papszOpenOptions, "FILE_FIELD", false)),
56
3.32k
      m_nDataBlockCount(0), m_papoDataBlock(nullptr)
57
3.32k
{
58
    // Open VFK file for reading.
59
3.32k
    CPLAssert(nullptr != m_pszFilename);
60
61
3.32k
    if (VSIStatL(m_pszFilename, m_poFStat) != 0 ||
62
3.32k
        !VSI_ISREG(m_poFStat->st_mode))
63
0
    {
64
0
        CPLError(CE_Failure, CPLE_OpenFailed, "%s is not a regular file.",
65
0
                 m_pszFilename);
66
0
    }
67
68
3.32k
    m_poFD = VSIFOpenL(m_pszFilename, "rb");
69
3.32k
    if (m_poFD == nullptr)
70
0
    {
71
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.",
72
0
                 m_pszFilename);
73
0
    }
74
3.32k
}
75
76
/*!
77
  \brief VFKReader destructor
78
*/
79
VFKReader::~VFKReader()
80
3.32k
{
81
3.32k
    CPLFree(m_pszFilename);
82
83
3.32k
    if (m_poFD)
84
3.32k
        VSIFCloseL(m_poFD);
85
3.32k
    CPLFree(m_poFStat);
86
87
    /* clear data blocks */
88
13.2k
    for (int i = 0; i < m_nDataBlockCount; i++)
89
9.95k
        delete m_papoDataBlock[i];
90
3.32k
    CPLFree(m_papoDataBlock);
91
3.32k
}
92
93
char *GetDataBlockName(const char *pszLine)
94
383k
{
95
383k
    int n = 0;  // Used after for.
96
383k
    const char *pszLineChar = pszLine + 2;
97
98
4.79M
    for (; *pszLineChar != '\0' && *pszLineChar != ';'; pszLineChar++, n++)
99
4.41M
        ;
100
101
383k
    if (*pszLineChar == '\0')
102
36.5k
        return nullptr;
103
104
347k
    char *pszBlockName = (char *)CPLMalloc(n + 1);
105
347k
    strncpy(pszBlockName, pszLine + 2, n);
106
347k
    pszBlockName[n] = '\0';
107
108
347k
    return pszBlockName;
109
383k
}
110
111
/*!
112
  \brief Read a line from file
113
114
  \return a NULL terminated string which should be freed with CPLFree().
115
*/
116
char *VFKReader::ReadLine()
117
4.62M
{
118
4.62M
    int nBufLength;
119
4.62M
    const char *pszRawLine =
120
4.62M
        CPLReadLine3L(m_poFD, 100 * 1024, &nBufLength, nullptr);
121
4.62M
    if (pszRawLine == nullptr)
122
7.68k
        return nullptr;
123
124
4.62M
    char *pszLine = (char *)CPLMalloc(nBufLength + 1);
125
4.62M
    memcpy(pszLine, pszRawLine, nBufLength + 1);
126
127
4.62M
    const int nLineLength = static_cast<int>(strlen(pszRawLine));
128
4.62M
    if (nLineLength != nBufLength)
129
942k
    {
130
        /* replace nul characters in line by spaces */
131
67.9M
        for (int i = nLineLength; i < nBufLength; i++)
132
66.9M
        {
133
66.9M
            if (pszLine[i] == '\0')
134
12.0M
                pszLine[i] = ' ';
135
66.9M
        }
136
942k
    }
137
138
4.62M
    return pszLine;
139
4.62M
}
140
141
/*!
142
  \brief Load text encoding from header (&HENCODING)
143
144
  Called from VFKReader::ReadDataBlocks()
145
*/
146
void VFKReader::ReadEncoding()
147
3.31k
{
148
3.31k
    VSIFSeekL(m_poFD, 0, SEEK_SET);
149
3.31k
    char *pszLine = nullptr;
150
1.07M
    while ((pszLine = ReadLine()) != nullptr)
151
1.06M
    {
152
1.06M
        if (strlen(pszLine) < 2 || pszLine[0] != '&')
153
680k
        {
154
680k
            CPLFree(pszLine);
155
680k
            continue;
156
680k
        }
157
389k
        if (pszLine[1] == 'B' || (pszLine[1] == 'K' && strlen(pszLine) == 2))
158
2.02k
        {
159
            /* 'B' record closes the header section */
160
            /* 'K' record is end of file */
161
2.02k
            CPLFree(pszLine);
162
2.02k
            break;
163
2.02k
        }
164
387k
        if (pszLine[1] != 'H')
165
175k
        {
166
            /* (not) 'H' header */
167
175k
            CPLFree(pszLine);
168
175k
            continue;
169
175k
        }
170
171
211k
        char *pszKey = pszLine + 2; /* &H */
172
211k
        char *pszValue = pszKey;
173
3.13M
        while (*pszValue != '\0' && *pszValue != ';')
174
2.91M
            pszValue++;
175
211k
        if (*pszValue != ';')
176
14.6k
        {
177
            /* no value, ignoring */
178
14.6k
            CPLFree(pszLine);
179
14.6k
            continue;
180
14.6k
        }
181
182
196k
        *pszValue = '\0';
183
196k
        pszValue++; /* skip ; */
184
196k
        if (*pszValue == '"')
185
17.6k
        { /* trim "" */
186
17.6k
            pszValue++;
187
17.6k
            size_t nValueLen = strlen(pszValue);
188
17.6k
            if (nValueLen > 0)
189
15.5k
                pszValue[nValueLen - 1] = '\0';
190
17.6k
        }
191
192
        /* read encoding to m_pszEncoding */
193
196k
        if (EQUAL(pszKey, "CODEPAGE"))
194
1.97k
        {
195
1.97k
            if (EQUAL(pszValue, CPL_ENC_UTF8))
196
0
                m_pszEncoding = CPL_ENC_UTF8;
197
1.97k
            else if (!EQUAL(pszValue, "WE8ISO8859P2"))
198
1.35k
                m_pszEncoding = "WINDOWS-1250";
199
1.97k
        }
200
201
196k
        CPLFree(pszLine);
202
196k
    }
203
3.31k
}
204
205
/*!
206
  \brief Load data block definitions (&B)
207
208
  Call VFKReader::OpenFile() before this function.
209
210
  \param bSuppressGeometry True for skipping geometry resolver (force wkbNone
211
  type)
212
213
  \return number of data blocks or -1 on error
214
*/
215
int VFKReader::ReadDataBlocks(bool bSuppressGeometry)
216
3.31k
{
217
3.31k
    CPLAssert(nullptr != m_pszFilename);
218
219
    /* load text encoding in extra pass through header */
220
3.31k
    ReadEncoding();
221
222
3.31k
    VSIFSeekL(m_poFD, 0, SEEK_SET);
223
3.31k
    bool bInHeader = true;
224
3.31k
    char *pszLine = nullptr;
225
1.75M
    while ((pszLine = ReadLine()) != nullptr)
226
1.74M
    {
227
1.74M
        if (strlen(pszLine) < 2 || pszLine[0] != '&')
228
1.11M
        {
229
1.11M
            CPLFree(pszLine);
230
1.11M
            continue;
231
1.11M
        }
232
233
636k
        if (pszLine[1] == 'B')
234
53.8k
        {
235
53.8k
            if (bInHeader)
236
2.02k
                bInHeader = false; /* 'B' record closes the header section */
237
238
53.8k
            char *pszBlockName = GetDataBlockName(pszLine);
239
53.8k
            if (pszBlockName == nullptr)
240
216
            {
241
216
                CPLError(CE_Failure, CPLE_NotSupported,
242
216
                         "Corrupted data - line\n%s\n", pszLine);
243
216
                CPLFree(pszLine);
244
216
                return -1;
245
216
            }
246
247
            /* skip duplicated data blocks (when reading multiple files into
248
             * single DB)  */
249
53.6k
            if (!GetDataBlock(pszBlockName))
250
7.92k
            {
251
7.92k
                IVFKDataBlock *poNewDataBlock =
252
7.92k
                    (IVFKDataBlock *)CreateDataBlock(pszBlockName);
253
7.92k
                poNewDataBlock->SetGeometryType(bSuppressGeometry);
254
7.92k
                poNewDataBlock->SetProperties(
255
7.92k
                    pszLine); /* TODO: check consistency on property level */
256
257
7.92k
                AddDataBlock(poNewDataBlock, pszLine);
258
7.92k
            }
259
53.6k
            CPLFree(pszBlockName);
260
53.6k
        }
261
582k
        else if (pszLine[1] == 'H')
262
247k
        {
263
            /* check for amendment file */
264
247k
            if (EQUAL(pszLine, "&HZMENY;1"))
265
706
            {
266
706
                m_bAmendment = true;
267
706
            }
268
269
            /* header - metadata */
270
247k
            AddInfo(pszLine);
271
247k
        }
272
334k
        else if (pszLine[1] == 'K' && strlen(pszLine) == 2)
273
16
        {
274
            /* end of file */
275
16
            CPLFree(pszLine);
276
16
            break;
277
16
        }
278
334k
        else if (bInHeader && pszLine[1] == 'D')
279
153k
        {
280
            /* process 'D' records in the header section */
281
153k
            AddInfo(pszLine);
282
153k
        }
283
284
636k
        CPLFree(pszLine);
285
636k
    }
286
287
3.09k
    return m_nDataBlockCount;
288
3.31k
}
289
290
/*!
291
  \brief Load data records (&D)
292
293
  Call VFKReader::OpenFile() before this function.
294
295
  \param poDataBlock limit to selected data block or NULL for all
296
297
  \return number of data records or -1 on error
298
*/
299
int64_t VFKReader::ReadDataRecords(IVFKDataBlock *poDataBlock)
300
3.30k
{
301
3.30k
    const char *pszName = nullptr;
302
3.30k
    IVFKDataBlock *poDataBlockCurrent = nullptr;
303
304
3.30k
    if (poDataBlock)
305
0
    { /* read only given data block */
306
0
        poDataBlockCurrent = poDataBlock;
307
0
        if (poDataBlockCurrent->GetFeatureCount(FALSE) < 0)
308
0
            poDataBlockCurrent->SetFeatureCount(0);
309
0
        pszName = poDataBlockCurrent->GetName();
310
0
    }
311
3.30k
    else
312
3.30k
    { /* read all data blocks */
313
13.0k
        for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++)
314
9.78k
        {
315
9.78k
            poDataBlockCurrent = GetDataBlock(iDataBlock);
316
9.78k
            if (poDataBlockCurrent->GetFeatureCount(FALSE) < 0)
317
9.04k
                poDataBlockCurrent->SetFeatureCount(0);
318
9.78k
        }
319
3.30k
        poDataBlockCurrent = nullptr;
320
3.30k
    }
321
322
3.30k
    VSIFSeekL(m_poFD, 0, SEEK_SET);
323
324
3.30k
    int iLine = 0;
325
3.30k
    int nSkipped = 0;
326
3.30k
    int nDupl = 0;
327
3.30k
    int64_t nRecords = 0;
328
3.30k
    bool bInHeader = true;
329
3.30k
    CPLString osBlockNameLast;
330
3.30k
    char *pszLine = nullptr;
331
332
    /* currency sign in current encoding */
333
3.30k
    const char *pszCurSign = "\244";
334
3.30k
    if (EQUAL(m_pszEncoding, CPL_ENC_UTF8))
335
0
        pszCurSign = "\302\244";
336
3.30k
    size_t nCurSignLen = strlen(pszCurSign);
337
338
1.73M
    while ((pszLine = ReadLine()) != nullptr)
339
1.73M
    {
340
1.73M
        iLine++;
341
1.73M
        size_t nLength = strlen(pszLine);
342
1.73M
        if (nLength < 2)
343
530k
        {
344
530k
            CPLFree(pszLine);
345
530k
            continue;
346
530k
        }
347
348
1.20M
        if (bInHeader && pszLine[1] == 'B')
349
2.36k
            bInHeader = false; /* 'B' record closes the header section */
350
351
1.20M
        if (pszLine[1] == 'D')
352
394k
        {
353
394k
            if (bInHeader)
354
64.8k
            {
355
                /* skip 'D' records from the header section, already
356
                 * processed as metadata */
357
64.8k
                CPLFree(pszLine);
358
64.8k
                continue;
359
64.8k
            }
360
361
329k
            char *pszBlockName = GetDataBlockName(pszLine);
362
363
329k
            if (pszBlockName && (!pszName || EQUAL(pszBlockName, pszName)))
364
293k
            {
365
                /* merge lines if needed
366
367
                   See http://en.wikipedia.org/wiki/ISO/IEC_8859
368
                   - \244 - general currency sign
369
                */
370
293k
                if (EQUAL(pszLine + nLength - nCurSignLen, pszCurSign))
371
25.5k
                {
372
                    /* trim the currency sign and trailing spaces from line */
373
25.5k
                    nLength -= nCurSignLen;
374
35.2k
                    while (nLength > 0 && pszLine[nLength - 1] == ' ')
375
9.68k
                        nLength--;
376
25.5k
                    pszLine[nLength] = '\0';
377
378
25.5k
                    CPLString osMultiLine(pszLine);
379
25.5k
                    CPLFree(pszLine);
380
381
69.1k
                    while ((pszLine = ReadLine()) != nullptr &&
382
69.1k
                           (nLength = strlen(pszLine)) >= nCurSignLen &&
383
61.0k
                           EQUAL(pszLine + nLength - nCurSignLen, pszCurSign))
384
43.6k
                    {
385
                        /* trim leading spaces from continued line */
386
43.6k
                        char *pszLineTrim = pszLine;
387
68.3k
                        while (*pszLineTrim == ' ')
388
24.6k
                            pszLineTrim++;
389
                        /* trim the currency sign and trailing spaces from line
390
                         */
391
43.6k
                        nLength = strlen(pszLineTrim) - nCurSignLen;
392
64.3k
                        while (nLength > 0 && pszLineTrim[nLength - 1] == ' ')
393
20.7k
                            nLength--;
394
43.6k
                        pszLineTrim[nLength] = '\0';
395
                        /* append a space and the trimmed line */
396
43.6k
                        osMultiLine += " ";
397
43.6k
                        osMultiLine += pszLineTrim;
398
399
43.6k
                        CPLFree(pszLine);
400
43.6k
                        if (osMultiLine.size() > 100U * 1024U * 1024U)
401
0
                        {
402
0
                            CPLFree(pszBlockName);
403
0
                            return -1;
404
0
                        }
405
43.6k
                    }
406
25.5k
                    if (pszLine)
407
25.5k
                    {
408
                        /* trim leading spaces from continued line */
409
25.5k
                        char *pszLineTrim = pszLine;
410
39.4k
                        while (*pszLineTrim == ' ')
411
13.9k
                            pszLineTrim++;
412
                        /* append a space and the trimmed line */
413
25.5k
                        osMultiLine += " ";
414
25.5k
                        osMultiLine += pszLineTrim;
415
25.5k
                    }
416
25.5k
                    CPLFree(pszLine);
417
418
25.5k
                    nLength = osMultiLine.size();
419
25.5k
                    if (nLength > 100U * 1024U * 1024U)
420
0
                    {
421
0
                        CPLFree(pszBlockName);
422
0
                        return -1;
423
0
                    }
424
25.5k
                    pszLine = (char *)CPLMalloc(nLength + 1);
425
25.5k
                    strncpy(pszLine, osMultiLine.c_str(), nLength);
426
25.5k
                    pszLine[nLength] = '\0';
427
25.5k
                }
428
429
293k
                if (!poDataBlock)
430
293k
                { /* read all data blocks */
431
293k
                    if (osBlockNameLast.empty() ||
432
136k
                        !EQUAL(pszBlockName, osBlockNameLast.c_str()))
433
232k
                    {
434
232k
                        poDataBlockCurrent = GetDataBlock(pszBlockName);
435
232k
                        osBlockNameLast = CPLString(pszBlockName);
436
232k
                    }
437
293k
                }
438
293k
                if (!poDataBlockCurrent)
439
103k
                {
440
103k
                    CPLFree(pszBlockName);
441
103k
                    CPLFree(pszLine);
442
103k
                    continue;  // assert ?
443
103k
                }
444
445
190k
                VFKFeature *poNewFeature =
446
190k
                    new VFKFeature(poDataBlockCurrent,
447
190k
                                   poDataBlockCurrent->GetFeatureCount() + 1);
448
190k
                if (poNewFeature->SetProperties(pszLine))
449
109k
                {
450
109k
                    if (AddFeature(poDataBlockCurrent, poNewFeature) !=
451
109k
                        OGRERR_NONE)
452
88.4k
                    {
453
88.4k
                        CPLDebug("OGR-VFK",
454
88.4k
                                 "%s: duplicated VFK data record skipped "
455
88.4k
                                 "(line %d).\n%s\n",
456
88.4k
                                 pszBlockName, iLine, pszLine);
457
88.4k
                        poDataBlockCurrent->SetIncRecordCount(RecordDuplicated);
458
88.4k
                    }
459
20.9k
                    else
460
20.9k
                    {
461
20.9k
                        nRecords++;
462
20.9k
                        poDataBlockCurrent->SetIncRecordCount(RecordValid);
463
20.9k
                    }
464
109k
                    delete poNewFeature;
465
109k
                }
466
80.7k
                else
467
80.7k
                {
468
80.7k
                    CPLDebug("OGR-VFK",
469
80.7k
                             "Invalid VFK data record skipped (line %d).\n%s\n",
470
80.7k
                             iLine, pszLine);
471
80.7k
                    poDataBlockCurrent->SetIncRecordCount(RecordSkipped);
472
80.7k
                    delete poNewFeature;
473
80.7k
                }
474
190k
            }
475
226k
            CPLFree(pszBlockName);
476
226k
        }
477
810k
        else if (pszLine[1] == 'K' && strlen(pszLine) == 2)
478
18
        {
479
            /* end of file */
480
18
            CPLFree(pszLine);
481
18
            break;
482
18
        }
483
484
1.03M
        CPLFree(pszLine);
485
1.03M
    }
486
487
13.0k
    for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++)
488
9.78k
    {
489
9.78k
        poDataBlockCurrent = GetDataBlock(iDataBlock);
490
491
9.78k
        if (poDataBlock && poDataBlock != poDataBlockCurrent)
492
0
            continue;
493
494
9.78k
        nSkipped = poDataBlockCurrent->GetRecordCount(RecordSkipped);
495
9.78k
        nDupl = poDataBlockCurrent->GetRecordCount(RecordDuplicated);
496
9.78k
        if (nSkipped > 0)
497
1.55k
            CPLError(CE_Warning, CPLE_AppDefined,
498
1.55k
                     "%s: %d invalid VFK data records skipped",
499
1.55k
                     poDataBlockCurrent->GetName(), nSkipped);
500
9.78k
        if (nDupl > 0)
501
1.03k
            CPLError(CE_Warning, CPLE_AppDefined,
502
1.03k
                     "%s: %d duplicated VFK data records skipped",
503
1.03k
                     poDataBlockCurrent->GetName(), nDupl);
504
505
9.78k
        CPLDebug("OGR-VFK", "VFKReader::ReadDataRecords(): name=%s n=%d",
506
9.78k
                 poDataBlockCurrent->GetName(),
507
9.78k
                 poDataBlockCurrent->GetRecordCount(RecordValid));
508
9.78k
    }
509
510
3.30k
    return nRecords;
511
3.30k
}
512
513
IVFKDataBlock *VFKReader::CreateDataBlock(const char *pszBlockName)
514
0
{
515
0
    return (IVFKDataBlock *)new VFKDataBlock(pszBlockName, (IVFKReader *)this);
516
0
}
517
518
/*!
519
  \brief Add new data block
520
521
  \param poNewDataBlock pointer to VFKDataBlock instance
522
  \param pszDefn unused (see VFKReaderSQLite::AddDataBlock)
523
*/
524
void VFKReader::AddDataBlock(IVFKDataBlock *poNewDataBlock,
525
                             CPL_UNUSED const char *pszDefn)
526
9.95k
{
527
9.95k
    m_nDataBlockCount++;
528
529
9.95k
    m_papoDataBlock = (IVFKDataBlock **)CPLRealloc(
530
9.95k
        m_papoDataBlock, sizeof(IVFKDataBlock *) * m_nDataBlockCount);
531
9.95k
    m_papoDataBlock[m_nDataBlockCount - 1] = poNewDataBlock;
532
9.95k
}
533
534
/*!
535
  \brief Add feature
536
537
  \param poDataBlock pointer to VFKDataBlock instance
538
  \param poFeature pointer to VFKFeature instance
539
*/
540
OGRErr VFKReader::AddFeature(IVFKDataBlock *poDataBlock, VFKFeature *poFeature)
541
0
{
542
0
    poDataBlock->AddFeature(poFeature);
543
0
    return OGRERR_NONE;
544
0
}
545
546
/*!
547
  \brief Get data block
548
549
  \param i index (starting with 0)
550
551
  \return pointer to VFKDataBlock instance or NULL on failure
552
*/
553
IVFKDataBlock *VFKReader::GetDataBlock(int i) const
554
1.03M
{
555
1.03M
    if (i < 0 || i >= m_nDataBlockCount)
556
0
        return nullptr;
557
558
1.03M
    return m_papoDataBlock[i];
559
1.03M
}
560
561
/*!
562
  \brief Get data block
563
564
  \param pszName data block name
565
566
  \return pointer to VFKDataBlock instance or NULL on failure
567
*/
568
IVFKDataBlock *VFKReader::GetDataBlock(const char *pszName) const
569
297k
{
570
888k
    for (int i = 0; i < m_nDataBlockCount; i++)
571
784k
    {
572
784k
        if (EQUAL(GetDataBlock(i)->GetName(), pszName))
573
192k
            return GetDataBlock(i);
574
784k
    }
575
576
104k
    return nullptr;
577
297k
}
578
579
/*!
580
  \brief Load geometry (loop datablocks)
581
582
  \return number of invalid features
583
*/
584
int VFKReader::LoadGeometry()
585
0
{
586
0
    long int nfeatures = 0;
587
0
    for (int i = 0; i < m_nDataBlockCount; i++)
588
0
    {
589
0
        nfeatures += m_papoDataBlock[i]->LoadGeometry();
590
0
    }
591
592
0
    CPLDebug("OGR_VFK", "VFKReader::LoadGeometry(): invalid=%ld", nfeatures);
593
594
0
    return static_cast<int>(nfeatures);
595
0
}
596
597
/*!
598
  \brief Add info
599
600
  \param pszLine pointer to line
601
*/
602
void VFKReader::AddInfo(const char *pszLine)
603
401k
{
604
401k
    const int nOffset = pszLine[1] == 'H' ? 2 : 1;  // &DKATUZE
605
606
401k
    const char *poKey = pszLine + nOffset; /* &H */
607
401k
    const char *poChar = poKey;
608
401k
    int iKeyLength = 0;
609
7.04M
    while (*poChar != '\0' && *poChar != ';')
610
6.64M
    {
611
6.64M
        iKeyLength++;
612
6.64M
        poChar++;
613
6.64M
    }
614
401k
    if (*poChar == '\0')
615
43.9k
        return;
616
617
357k
    char *pszKey = (char *)CPLMalloc(iKeyLength + 1);
618
357k
    strncpy(pszKey, poKey, iKeyLength);
619
357k
    pszKey[iKeyLength] = '\0';
620
621
357k
    poChar++; /* skip ; */
622
623
357k
    int iValueLength = 0;
624
357k
    int nSkip = 3; /* &H + ; */
625
7.52M
    while (*poChar != '\0')
626
7.17M
    {
627
7.17M
        if (*poChar == '"' && iValueLength == 0)
628
68.1k
        {
629
68.1k
            nSkip++;
630
68.1k
        }
631
7.10M
        else
632
7.10M
        {
633
7.10M
            iValueLength++;
634
7.10M
        }
635
7.17M
        poChar++;
636
7.17M
    }
637
357k
    if (nSkip > 3 && iValueLength > 0)
638
28.7k
        iValueLength--;
639
640
357k
    char *pszValue = (char *)CPLMalloc(iValueLength + 1);
641
7.43M
    for (int i = 0; i < iValueLength; i++)
642
7.07M
    {
643
7.07M
        pszValue[i] = pszLine[iKeyLength + nSkip + i];
644
7.07M
        if (pszValue[i] == '"')
645
174k
        {
646
174k
            pszValue[i] = '\''; /* " -> ' */
647
174k
        }
648
7.07M
    }
649
650
357k
    pszValue[iValueLength] = '\0';
651
652
    /* recode values */
653
357k
    char *pszValueEnc = CPLRecode(pszValue, m_pszEncoding, CPL_ENC_UTF8);
654
655
357k
    if (poInfo.find(pszKey) == poInfo.end())
656
17.3k
    {
657
17.3k
        poInfo[pszKey] = pszValueEnc;
658
17.3k
    }
659
340k
    else
660
340k
    {
661
        /* max. number of duplicated keys can be 101 */
662
340k
        const size_t nLen = strlen(pszKey) + 5;
663
340k
        char *pszKeyUniq = (char *)CPLMalloc(nLen);
664
665
340k
        int nCount = 1; /* assuming at least one match */
666
340k
        for (std::map<CPLString, CPLString>::iterator i = poInfo.begin();
667
407M
             i != poInfo.end(); ++i)
668
407M
        {
669
407M
            size_t iFound = i->first.find("_");
670
407M
            if (iFound != std::string::npos &&
671
401M
                EQUALN(pszKey, i->first.c_str(), iFound))
672
183M
                nCount += 1;
673
407M
        }
674
675
340k
        snprintf(pszKeyUniq, nLen, "%s_%d", pszKey, nCount);
676
340k
        poInfo[pszKeyUniq] = pszValueEnc;
677
340k
        CPLFree(pszKeyUniq);
678
340k
    }
679
680
357k
    CPLFree(pszKey);
681
357k
    CPLFree(pszValue);
682
357k
    CPLFree(pszValueEnc);
683
357k
}
684
685
/*!
686
  \brief Get info
687
688
  \param key key string
689
690
  \return pointer to value string or NULL if key not found
691
*/
692
const char *VFKReader::GetInfo(const char *key)
693
0
{
694
0
    if (poInfo.find(key) == poInfo.end())
695
0
        return nullptr;
696
697
0
    return poInfo[key].c_str();
698
0
}