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