Line data Source code
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 : * Permission is hereby granted, free of charge, to any person
12 : * obtaining a copy of this software and associated documentation
13 : * files (the "Software"), to deal in the Software without
14 : * restriction, including without limitation the rights to use, copy,
15 : * modify, merge, publish, distribute, sublicense, and/or sell copies
16 : * of the Software, and to permit persons to whom the Software is
17 : * furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be
20 : * included in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 : * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 : * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
26 : * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27 : * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28 : * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 : * SOFTWARE.
30 : ****************************************************************************/
31 :
32 : #include <sys/stat.h>
33 :
34 : #include "vfkreader.h"
35 : #include "vfkreaderp.h"
36 :
37 : #include "cpl_conv.h"
38 : #include "cpl_error.h"
39 : #include "cpl_string.h"
40 :
41 : #include "ogr_geometry.h"
42 :
43 : static char *GetDataBlockName(const char *);
44 :
45 : /*!
46 : \brief IVFKReader destructor
47 : */
48 17 : IVFKReader::~IVFKReader()
49 : {
50 17 : }
51 :
52 : /*!
53 : \brief Create new instance of VFKReader
54 :
55 : \return pointer to VFKReader instance
56 : */
57 17 : IVFKReader *CreateVFKReader(const GDALOpenInfo *poOpenInfo)
58 : {
59 17 : return new VFKReaderSQLite(poOpenInfo);
60 : }
61 :
62 : /*!
63 : \brief VFKReader constructor
64 : */
65 17 : VFKReader::VFKReader(const GDALOpenInfo *poOpenInfo)
66 : : m_pszEncoding("ISO-8859-2"), // Encoding, supported are ISO-8859-2,
67 : // WINDOWS-1250 and UTF-8.
68 34 : m_poFD(nullptr), m_pszFilename(CPLStrdup(poOpenInfo->pszFilename)),
69 34 : m_poFStat((VSIStatBufL *)CPLCalloc(1, sizeof(VSIStatBufL))),
70 : // VFK is provided in two forms - stative and amendment data.
71 : m_bAmendment(false),
72 : m_bFileField(
73 34 : CPLFetchBool(poOpenInfo->papszOpenOptions, "FILE_FIELD", false)),
74 17 : m_nDataBlockCount(0), m_papoDataBlock(nullptr)
75 : {
76 : // Open VFK file for reading.
77 17 : CPLAssert(nullptr != m_pszFilename);
78 :
79 34 : if (VSIStatL(m_pszFilename, m_poFStat) != 0 ||
80 17 : !VSI_ISREG(m_poFStat->st_mode))
81 : {
82 0 : CPLError(CE_Failure, CPLE_OpenFailed, "%s is not a regular file.",
83 : m_pszFilename);
84 : }
85 :
86 17 : m_poFD = VSIFOpenL(m_pszFilename, "rb");
87 17 : if (m_poFD == nullptr)
88 : {
89 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.",
90 : m_pszFilename);
91 : }
92 17 : }
93 :
94 : /*!
95 : \brief VFKReader destructor
96 : */
97 17 : VFKReader::~VFKReader()
98 : {
99 17 : CPLFree(m_pszFilename);
100 :
101 17 : if (m_poFD)
102 17 : VSIFCloseL(m_poFD);
103 17 : CPLFree(m_poFStat);
104 :
105 : /* clear data blocks */
106 993 : for (int i = 0; i < m_nDataBlockCount; i++)
107 976 : delete m_papoDataBlock[i];
108 17 : CPLFree(m_papoDataBlock);
109 17 : }
110 :
111 1787 : char *GetDataBlockName(const char *pszLine)
112 : {
113 1787 : int n = 0; // Used after for.
114 1787 : const char *pszLineChar = pszLine + 2;
115 :
116 8723 : for (; *pszLineChar != '\0' && *pszLineChar != ';'; pszLineChar++, n++)
117 : ;
118 :
119 1787 : if (*pszLineChar == '\0')
120 0 : return nullptr;
121 :
122 1787 : char *pszBlockName = (char *)CPLMalloc(n + 1);
123 1787 : strncpy(pszBlockName, pszLine + 2, n);
124 1787 : pszBlockName[n] = '\0';
125 :
126 1787 : return pszBlockName;
127 : }
128 :
129 : /*!
130 : \brief Read a line from file
131 :
132 : \return a NULL terminated string which should be freed with CPLFree().
133 : */
134 4828 : char *VFKReader::ReadLine()
135 : {
136 : int nBufLength;
137 : const char *pszRawLine =
138 4828 : CPLReadLine3L(m_poFD, 100 * 1024, &nBufLength, nullptr);
139 4828 : if (pszRawLine == nullptr)
140 1 : return nullptr;
141 :
142 4827 : char *pszLine = (char *)CPLMalloc(nBufLength + 1);
143 4827 : memcpy(pszLine, pszRawLine, nBufLength + 1);
144 :
145 4827 : const int nLineLength = static_cast<int>(strlen(pszRawLine));
146 4827 : if (nLineLength != nBufLength)
147 : {
148 : /* replace nul characters in line by spaces */
149 746407 : for (int i = nLineLength; i < nBufLength; i++)
150 : {
151 746078 : if (pszLine[i] == '\0')
152 701956 : pszLine[i] = ' ';
153 : }
154 : }
155 :
156 4827 : return pszLine;
157 : }
158 :
159 : /*!
160 : \brief Load text encoding from header (&HENCODING)
161 :
162 : Called from VFKReader::ReadDataBlocks()
163 : */
164 16 : void VFKReader::ReadEncoding()
165 : {
166 16 : VSIFSeekL(m_poFD, 0, SEEK_SET);
167 16 : char *pszLine = nullptr;
168 318 : while ((pszLine = ReadLine()) != nullptr)
169 : {
170 318 : if (strlen(pszLine) < 2 || pszLine[0] != '&')
171 : {
172 107 : CPLFree(pszLine);
173 107 : continue;
174 : }
175 211 : if (pszLine[1] == 'B' || (pszLine[1] == 'K' && strlen(pszLine) == 2))
176 : {
177 : /* 'B' record closes the header section */
178 : /* 'K' record is end of file */
179 16 : CPLFree(pszLine);
180 16 : break;
181 : }
182 195 : if (pszLine[1] != 'H')
183 : {
184 : /* (not) 'H' header */
185 0 : CPLFree(pszLine);
186 0 : continue;
187 : }
188 :
189 195 : char *pszKey = pszLine + 2; /* &H */
190 195 : char *pszValue = pszKey;
191 1350 : while (*pszValue != '\0' && *pszValue != ';')
192 1155 : pszValue++;
193 195 : if (*pszValue != ';')
194 : {
195 : /* no value, ignoring */
196 0 : CPLFree(pszLine);
197 0 : continue;
198 : }
199 :
200 195 : *pszValue = '\0';
201 195 : pszValue++; /* skip ; */
202 195 : if (*pszValue == '"')
203 : { /* trim "" */
204 105 : pszValue++;
205 105 : size_t nValueLen = strlen(pszValue);
206 105 : if (nValueLen > 0)
207 105 : pszValue[nValueLen - 1] = '\0';
208 : }
209 :
210 : /* read encoding to m_pszEncoding */
211 195 : if (EQUAL(pszKey, "CODEPAGE"))
212 : {
213 15 : if (EQUAL(pszValue, CPL_ENC_UTF8))
214 0 : m_pszEncoding = CPL_ENC_UTF8;
215 15 : else if (!EQUAL(pszValue, "WE8ISO8859P2"))
216 0 : m_pszEncoding = "WINDOWS-1250";
217 : }
218 :
219 195 : CPLFree(pszLine);
220 : }
221 16 : }
222 :
223 : /*!
224 : \brief Load data block definitions (&B)
225 :
226 : Call VFKReader::OpenFile() before this function.
227 :
228 : \param bSuppressGeometry True for skipping geometry resolver (force wkbNone
229 : type)
230 :
231 : \return number of data blocks or -1 on error
232 : */
233 16 : int VFKReader::ReadDataBlocks(bool bSuppressGeometry)
234 : {
235 16 : CPLAssert(nullptr != m_pszFilename);
236 :
237 : /* load text encoding in extra pass through header */
238 16 : ReadEncoding();
239 :
240 16 : VSIFSeekL(m_poFD, 0, SEEK_SET);
241 16 : bool bInHeader = true;
242 16 : char *pszLine = nullptr;
243 2485 : while ((pszLine = ReadLine()) != nullptr)
244 : {
245 2484 : if (strlen(pszLine) < 2 || pszLine[0] != '&')
246 : {
247 487 : CPLFree(pszLine);
248 487 : continue;
249 : }
250 :
251 1997 : if (pszLine[1] == 'B')
252 : {
253 917 : if (bInHeader)
254 16 : bInHeader = false; /* 'B' record closes the header section */
255 :
256 917 : char *pszBlockName = GetDataBlockName(pszLine);
257 917 : if (pszBlockName == nullptr)
258 : {
259 0 : CPLError(CE_Failure, CPLE_NotSupported,
260 : "Corrupted data - line\n%s\n", pszLine);
261 0 : CPLFree(pszLine);
262 0 : return -1;
263 : }
264 :
265 : /* skip duplicated data blocks (when reading multiple files into
266 : * single DB) */
267 917 : if (!GetDataBlock(pszBlockName))
268 : {
269 : IVFKDataBlock *poNewDataBlock =
270 915 : (IVFKDataBlock *)CreateDataBlock(pszBlockName);
271 915 : poNewDataBlock->SetGeometryType(bSuppressGeometry);
272 915 : poNewDataBlock->SetProperties(
273 : pszLine); /* TODO: check consistency on property level */
274 :
275 915 : AddDataBlock(poNewDataBlock, pszLine);
276 : }
277 917 : CPLFree(pszBlockName);
278 : }
279 1080 : else if (pszLine[1] == 'H')
280 : {
281 : /* check for amendment file */
282 195 : if (EQUAL(pszLine, "&HZMENY;1"))
283 : {
284 0 : m_bAmendment = true;
285 : }
286 :
287 : /* header - metadata */
288 195 : AddInfo(pszLine);
289 : }
290 885 : else if (pszLine[1] == 'K' && strlen(pszLine) == 2)
291 : {
292 : /* end of file */
293 15 : CPLFree(pszLine);
294 15 : break;
295 : }
296 870 : else if (bInHeader && pszLine[1] == 'D')
297 : {
298 : /* process 'D' records in the header section */
299 0 : AddInfo(pszLine);
300 : }
301 :
302 1982 : CPLFree(pszLine);
303 : }
304 :
305 16 : return m_nDataBlockCount;
306 : }
307 :
308 : /*!
309 : \brief Load data records (&D)
310 :
311 : Call VFKReader::OpenFile() before this function.
312 :
313 : \param poDataBlock limit to selected data block or NULL for all
314 :
315 : \return number of data records or -1 on error
316 : */
317 15 : int VFKReader::ReadDataRecords(IVFKDataBlock *poDataBlock)
318 : {
319 15 : const char *pszName = nullptr;
320 15 : IVFKDataBlock *poDataBlockCurrent = nullptr;
321 :
322 15 : if (poDataBlock)
323 : { /* read only given data block */
324 0 : poDataBlockCurrent = poDataBlock;
325 0 : if (poDataBlockCurrent->GetFeatureCount(FALSE) < 0)
326 0 : poDataBlockCurrent->SetFeatureCount(0);
327 0 : pszName = poDataBlockCurrent->GetName();
328 : }
329 : else
330 : { /* read all data blocks */
331 930 : for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++)
332 : {
333 915 : poDataBlockCurrent = GetDataBlock(iDataBlock);
334 915 : if (poDataBlockCurrent->GetFeatureCount(FALSE) < 0)
335 915 : poDataBlockCurrent->SetFeatureCount(0);
336 : }
337 15 : poDataBlockCurrent = nullptr;
338 : }
339 :
340 15 : VSIFSeekL(m_poFD, 0, SEEK_SET);
341 :
342 15 : int iLine = 0;
343 15 : int nSkipped = 0;
344 15 : int nDupl = 0;
345 15 : int nRecords = 0;
346 15 : bool bInHeader = true;
347 30 : CPLString osBlockNameLast;
348 15 : char *pszLine = nullptr;
349 :
350 : /* currency sign in current encoding */
351 15 : const char *pszCurSign = "\244";
352 15 : if (EQUAL(m_pszEncoding, CPL_ENC_UTF8))
353 0 : pszCurSign = "\302\244";
354 15 : size_t nCurSignLen = strlen(pszCurSign);
355 :
356 1995 : while ((pszLine = ReadLine()) != nullptr)
357 : {
358 1995 : iLine++;
359 1995 : size_t nLength = strlen(pszLine);
360 1995 : if (nLength < 2)
361 : {
362 0 : CPLFree(pszLine);
363 0 : continue;
364 : }
365 :
366 1995 : if (bInHeader && pszLine[1] == 'B')
367 15 : bInHeader = false; /* 'B' record closes the header section */
368 :
369 1995 : if (pszLine[1] == 'D')
370 : {
371 870 : if (bInHeader)
372 : {
373 : /* skip 'D' records from the header section, already
374 : * processed as metadata */
375 0 : CPLFree(pszLine);
376 0 : continue;
377 : }
378 :
379 870 : char *pszBlockName = GetDataBlockName(pszLine);
380 :
381 870 : if (pszBlockName && (!pszName || EQUAL(pszBlockName, pszName)))
382 : {
383 : /* merge lines if needed
384 :
385 : See http://en.wikipedia.org/wiki/ISO/IEC_8859
386 : - \244 - general currency sign
387 : */
388 870 : if (EQUAL(pszLine + nLength - nCurSignLen, pszCurSign))
389 : {
390 : /* trim the currency sign and trailing spaces from line */
391 30 : nLength -= nCurSignLen;
392 30 : while (nLength > 0 && pszLine[nLength - 1] == ' ')
393 0 : nLength--;
394 30 : pszLine[nLength] = '\0';
395 :
396 30 : CPLString osMultiLine(pszLine);
397 30 : CPLFree(pszLine);
398 :
399 30 : while ((pszLine = ReadLine()) != nullptr &&
400 60 : (nLength = strlen(pszLine)) >= nCurSignLen &&
401 30 : EQUAL(pszLine + nLength - nCurSignLen, pszCurSign))
402 : {
403 : /* trim leading spaces from continued line */
404 0 : char *pszLineTrim = pszLine;
405 0 : while (*pszLineTrim == ' ')
406 0 : pszLineTrim++;
407 : /* trim the currency sign and trailing spaces from line
408 : */
409 0 : nLength = strlen(pszLineTrim) - nCurSignLen;
410 0 : while (nLength > 0 && pszLineTrim[nLength - 1] == ' ')
411 0 : nLength--;
412 0 : pszLineTrim[nLength] = '\0';
413 : /* append a space and the trimmed line */
414 0 : osMultiLine += " ";
415 0 : osMultiLine += pszLineTrim;
416 :
417 0 : CPLFree(pszLine);
418 0 : if (osMultiLine.size() > 100U * 1024U * 1024U)
419 : {
420 0 : CPLFree(pszBlockName);
421 0 : return -1;
422 : }
423 : }
424 30 : if (pszLine)
425 : {
426 : /* trim leading spaces from continued line */
427 30 : char *pszLineTrim = pszLine;
428 30 : while (*pszLineTrim == ' ')
429 0 : pszLineTrim++;
430 : /* append a space and the trimmed line */
431 30 : osMultiLine += " ";
432 30 : osMultiLine += pszLineTrim;
433 : }
434 30 : CPLFree(pszLine);
435 :
436 30 : nLength = osMultiLine.size();
437 30 : if (nLength > 100U * 1024U * 1024U)
438 : {
439 0 : CPLFree(pszBlockName);
440 0 : return -1;
441 : }
442 30 : pszLine = (char *)CPLMalloc(nLength + 1);
443 30 : strncpy(pszLine, osMultiLine.c_str(), nLength);
444 30 : pszLine[nLength] = '\0';
445 : }
446 :
447 870 : if (!poDataBlock)
448 : { /* read all data blocks */
449 1725 : if (osBlockNameLast.empty() ||
450 855 : !EQUAL(pszBlockName, osBlockNameLast.c_str()))
451 : {
452 75 : poDataBlockCurrent = GetDataBlock(pszBlockName);
453 75 : osBlockNameLast = CPLString(pszBlockName);
454 : }
455 : }
456 870 : if (!poDataBlockCurrent)
457 : {
458 0 : CPLFree(pszBlockName);
459 0 : CPLFree(pszLine);
460 0 : continue; // assert ?
461 : }
462 :
463 : VFKFeature *poNewFeature =
464 : new VFKFeature(poDataBlockCurrent,
465 870 : poDataBlockCurrent->GetFeatureCount() + 1);
466 870 : if (poNewFeature->SetProperties(pszLine))
467 : {
468 870 : if (AddFeature(poDataBlockCurrent, poNewFeature) !=
469 : OGRERR_NONE)
470 : {
471 0 : CPLDebug("OGR-VFK",
472 : "%s: duplicated VFK data record skipped "
473 : "(line %d).\n%s\n",
474 : pszBlockName, iLine, pszLine);
475 0 : poDataBlockCurrent->SetIncRecordCount(RecordDuplicated);
476 : }
477 : else
478 : {
479 870 : nRecords++;
480 870 : poDataBlockCurrent->SetIncRecordCount(RecordValid);
481 : }
482 870 : delete poNewFeature;
483 : }
484 : else
485 : {
486 0 : CPLDebug("OGR-VFK",
487 : "Invalid VFK data record skipped (line %d).\n%s\n",
488 : iLine, pszLine);
489 0 : poDataBlockCurrent->SetIncRecordCount(RecordSkipped);
490 0 : delete poNewFeature;
491 : }
492 : }
493 870 : CPLFree(pszBlockName);
494 : }
495 1125 : else if (pszLine[1] == 'K' && strlen(pszLine) == 2)
496 : {
497 : /* end of file */
498 15 : CPLFree(pszLine);
499 15 : break;
500 : }
501 :
502 1980 : CPLFree(pszLine);
503 : }
504 :
505 930 : for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++)
506 : {
507 915 : poDataBlockCurrent = GetDataBlock(iDataBlock);
508 :
509 915 : if (poDataBlock && poDataBlock != poDataBlockCurrent)
510 0 : continue;
511 :
512 915 : nSkipped = poDataBlockCurrent->GetRecordCount(RecordSkipped);
513 915 : nDupl = poDataBlockCurrent->GetRecordCount(RecordDuplicated);
514 915 : if (nSkipped > 0)
515 0 : CPLError(CE_Warning, CPLE_AppDefined,
516 : "%s: %d invalid VFK data records skipped",
517 : poDataBlockCurrent->GetName(), nSkipped);
518 915 : if (nDupl > 0)
519 0 : CPLError(CE_Warning, CPLE_AppDefined,
520 : "%s: %d duplicated VFK data records skipped",
521 : poDataBlockCurrent->GetName(), nDupl);
522 :
523 915 : CPLDebug("OGR-VFK", "VFKReader::ReadDataRecords(): name=%s n=%d",
524 : poDataBlockCurrent->GetName(),
525 : poDataBlockCurrent->GetRecordCount(RecordValid));
526 : }
527 :
528 15 : return nRecords;
529 : }
530 :
531 0 : IVFKDataBlock *VFKReader::CreateDataBlock(const char *pszBlockName)
532 : {
533 0 : return (IVFKDataBlock *)new VFKDataBlock(pszBlockName, (IVFKReader *)this);
534 : }
535 :
536 : /*!
537 : \brief Add new data block
538 :
539 : \param poNewDataBlock pointer to VFKDataBlock instance
540 : \param pszDefn unused (see VFKReaderSQLite::AddDataBlock)
541 : */
542 976 : void VFKReader::AddDataBlock(IVFKDataBlock *poNewDataBlock,
543 : CPL_UNUSED const char *pszDefn)
544 : {
545 976 : m_nDataBlockCount++;
546 :
547 1952 : m_papoDataBlock = (IVFKDataBlock **)CPLRealloc(
548 976 : m_papoDataBlock, sizeof(IVFKDataBlock *) * m_nDataBlockCount);
549 976 : m_papoDataBlock[m_nDataBlockCount - 1] = poNewDataBlock;
550 976 : }
551 :
552 : /*!
553 : \brief Add feature
554 :
555 : \param poDataBlock pointer to VFKDataBlock instance
556 : \param poFeature pointer to VFKFeature instance
557 : */
558 0 : OGRErr VFKReader::AddFeature(IVFKDataBlock *poDataBlock, VFKFeature *poFeature)
559 : {
560 0 : poDataBlock->AddFeature(poFeature);
561 0 : return OGRERR_NONE;
562 : }
563 :
564 : /*!
565 : \brief Get data block
566 :
567 : \param i index (starting with 0)
568 :
569 : \return pointer to VFKDataBlock instance or NULL on failure
570 : */
571 72009 : IVFKDataBlock *VFKReader::GetDataBlock(int i) const
572 : {
573 72009 : if (i < 0 || i >= m_nDataBlockCount)
574 0 : return nullptr;
575 :
576 72009 : return m_papoDataBlock[i];
577 : }
578 :
579 : /*!
580 : \brief Get data block
581 :
582 : \param pszName data block name
583 :
584 : \return pointer to VFKDataBlock instance or NULL on failure
585 : */
586 2073 : IVFKDataBlock *VFKReader::GetDataBlock(const char *pszName) const
587 : {
588 66154 : for (int i = 0; i < m_nDataBlockCount; i++)
589 : {
590 65239 : if (EQUAL(GetDataBlock(i)->GetName(), pszName))
591 1158 : return GetDataBlock(i);
592 : }
593 :
594 915 : return nullptr;
595 : }
596 :
597 : /*!
598 : \brief Load geometry (loop datablocks)
599 :
600 : \return number of invalid features
601 : */
602 0 : int VFKReader::LoadGeometry()
603 : {
604 0 : long int nfeatures = 0;
605 0 : for (int i = 0; i < m_nDataBlockCount; i++)
606 : {
607 0 : nfeatures += m_papoDataBlock[i]->LoadGeometry();
608 : }
609 :
610 0 : CPLDebug("OGR_VFK", "VFKReader::LoadGeometry(): invalid=%ld", nfeatures);
611 :
612 0 : return static_cast<int>(nfeatures);
613 : }
614 :
615 : /*!
616 : \brief Add info
617 :
618 : \param pszLine pointer to line
619 : */
620 195 : void VFKReader::AddInfo(const char *pszLine)
621 : {
622 195 : const int nOffset = pszLine[1] == 'H' ? 2 : 1; // &DKATUZE
623 :
624 195 : const char *poKey = pszLine + nOffset; /* &H */
625 195 : const char *poChar = poKey;
626 195 : int iKeyLength = 0;
627 1350 : while (*poChar != '\0' && *poChar != ';')
628 : {
629 1155 : iKeyLength++;
630 1155 : poChar++;
631 : }
632 195 : if (*poChar == '\0')
633 0 : return;
634 :
635 195 : char *pszKey = (char *)CPLMalloc(iKeyLength + 1);
636 195 : strncpy(pszKey, poKey, iKeyLength);
637 195 : pszKey[iKeyLength] = '\0';
638 :
639 195 : poChar++; /* skip ; */
640 :
641 195 : int iValueLength = 0;
642 195 : int nSkip = 3; /* &H + ; */
643 17460 : while (*poChar != '\0')
644 : {
645 17265 : if (*poChar == '"' && iValueLength == 0)
646 : {
647 105 : nSkip++;
648 : }
649 : else
650 : {
651 17160 : iValueLength++;
652 : }
653 17265 : poChar++;
654 : }
655 195 : if (nSkip > 3 && iValueLength > 0)
656 105 : iValueLength--;
657 :
658 195 : char *pszValue = (char *)CPLMalloc(iValueLength + 1);
659 17250 : for (int i = 0; i < iValueLength; i++)
660 : {
661 17055 : pszValue[i] = pszLine[iKeyLength + nSkip + i];
662 17055 : if (pszValue[i] == '"')
663 : {
664 270 : pszValue[i] = '\''; /* " -> ' */
665 : }
666 : }
667 :
668 195 : pszValue[iValueLength] = '\0';
669 :
670 : /* recode values */
671 195 : char *pszValueEnc = CPLRecode(pszValue, m_pszEncoding, CPL_ENC_UTF8);
672 :
673 195 : if (poInfo.find(pszKey) == poInfo.end())
674 : {
675 195 : poInfo[pszKey] = pszValueEnc;
676 : }
677 : else
678 : {
679 : /* max. number of duplicated keys can be 101 */
680 0 : const size_t nLen = strlen(pszKey) + 5;
681 0 : char *pszKeyUniq = (char *)CPLMalloc(nLen);
682 :
683 0 : int nCount = 1; /* assuming at least one match */
684 0 : for (std::map<CPLString, CPLString>::iterator i = poInfo.begin();
685 0 : i != poInfo.end(); ++i)
686 : {
687 0 : size_t iFound = i->first.find("_");
688 0 : if (iFound != std::string::npos &&
689 0 : EQUALN(pszKey, i->first.c_str(), iFound))
690 0 : nCount += 1;
691 : }
692 :
693 0 : snprintf(pszKeyUniq, nLen, "%s_%d", pszKey, nCount);
694 0 : poInfo[pszKeyUniq] = pszValueEnc;
695 0 : CPLFree(pszKeyUniq);
696 : }
697 :
698 195 : CPLFree(pszKey);
699 195 : CPLFree(pszValue);
700 195 : CPLFree(pszValueEnc);
701 : }
702 :
703 : /*!
704 : \brief Get info
705 :
706 : \param key key string
707 :
708 : \return pointer to value string or NULL if key not found
709 : */
710 0 : const char *VFKReader::GetInfo(const char *key)
711 : {
712 0 : if (poInfo.find(key) == poInfo.end())
713 0 : return nullptr;
714 :
715 0 : return poInfo[key].c_str();
716 : }
|