Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Core
4 : * Purpose: Read metadata (mainly the remote sensing imagery) from files of
5 : * different providers like DigitalGlobe, GeoEye etc.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : * Author: Dmitry Baryshnikov, polimax@mail.ru
8 : *
9 : ******************************************************************************
10 : * Copyright (c) HER MAJESTY THE QUEEN IN RIGHT OF CANADA (2008)
11 : * as represented by the Canadian Nuclear Safety Commission
12 : * Copyright (c) 2014-2015, NextGIS info@nextgis.ru
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : ****************************************************************************/
32 :
33 : #include "cpl_port.h"
34 : #include "gdal_mdreader.h"
35 :
36 : #include <cctype>
37 : #include <cstddef>
38 : #include <cstdio>
39 : #include <cstring>
40 : #include <ctime>
41 : #include <string>
42 :
43 : #include "cpl_conv.h"
44 : #include "cpl_error.h"
45 : #include "cpl_minixml.h"
46 : #include "cpl_string.h"
47 : #include "cpl_time.h"
48 : #include "cpl_vsi.h"
49 : #include "cplkeywordparser.h"
50 : #include "gdal_priv.h"
51 :
52 : // readers
53 : #include "mdreader/reader_alos.h"
54 : #include "mdreader/reader_digital_globe.h"
55 : #include "mdreader/reader_eros.h"
56 : #include "mdreader/reader_geo_eye.h"
57 : #include "mdreader/reader_kompsat.h"
58 : #include "mdreader/reader_landsat.h"
59 : #include "mdreader/reader_orb_view.h"
60 : #include "mdreader/reader_pleiades.h"
61 : #include "mdreader/reader_rapid_eye.h"
62 : #include "mdreader/reader_rdk1.h"
63 : #include "mdreader/reader_spot.h"
64 :
65 : /**
66 : * The RPC parameters names
67 : */
68 :
69 : static const char *const apszRPCTXTSingleValItems[] = {
70 : RPC_ERR_BIAS, RPC_ERR_RAND, RPC_LINE_OFF, RPC_SAMP_OFF,
71 : RPC_LAT_OFF, RPC_LONG_OFF, RPC_HEIGHT_OFF, RPC_LINE_SCALE,
72 : RPC_SAMP_SCALE, RPC_LAT_SCALE, RPC_LONG_SCALE, RPC_HEIGHT_SCALE,
73 : nullptr};
74 :
75 : static const char *const apszRPCTXT20ValItems[] = {
76 : RPC_LINE_NUM_COEFF, RPC_LINE_DEN_COEFF, RPC_SAMP_NUM_COEFF,
77 : RPC_SAMP_DEN_COEFF, nullptr};
78 :
79 : /**
80 : * GDALMDReaderManager()
81 : */
82 : GDALMDReaderManager::GDALMDReaderManager() = default;
83 :
84 : /**
85 : * ~GDALMDReaderManager()
86 : */
87 10332 : GDALMDReaderManager::~GDALMDReaderManager()
88 : {
89 5166 : if (nullptr != m_pReader)
90 : {
91 73 : delete m_pReader;
92 : }
93 5166 : }
94 :
95 : /**
96 : * GetReader()
97 : */
98 :
99 : #define INIT_READER(reader) \
100 : GDALMDReaderBase *pReaderBase = new reader(pszPath, papszSiblingFiles); \
101 : if (pReaderBase->HasRequiredFiles()) \
102 : { \
103 : m_pReader = pReaderBase; \
104 : return m_pReader; \
105 : } \
106 : delete pReaderBase
107 :
108 5166 : GDALMDReaderBase *GDALMDReaderManager::GetReader(const char *pszPath,
109 : char **papszSiblingFiles,
110 : GUInt32 nType)
111 : {
112 5166 : if (!GDALCanFileAcceptSidecarFile(pszPath))
113 71 : return nullptr;
114 :
115 5095 : if (nType & MDR_DG)
116 : {
117 5095 : INIT_READER(GDALMDReaderDigitalGlobe);
118 : }
119 :
120 : // required filename.tif filename.pvl filename_rpc.txt
121 5057 : if (nType & MDR_OV)
122 : {
123 5057 : INIT_READER(GDALMDReaderOrbView);
124 : }
125 :
126 5056 : if (nType & MDR_GE)
127 : {
128 5056 : INIT_READER(GDALMDReaderGeoEye);
129 : }
130 :
131 5044 : if (nType & MDR_LS)
132 : {
133 5044 : INIT_READER(GDALMDReaderLandsat);
134 : }
135 :
136 5043 : if (nType & MDR_PLEIADES)
137 : {
138 5043 : INIT_READER(GDALMDReaderPleiades);
139 : }
140 :
141 5029 : if (nType & MDR_SPOT)
142 : {
143 5029 : INIT_READER(GDALMDReaderSpot);
144 : }
145 :
146 5028 : if (nType & MDR_RDK1)
147 : {
148 5028 : INIT_READER(GDALMDReaderResursDK1);
149 : }
150 :
151 5027 : if (nType & MDR_RE)
152 : {
153 5027 : INIT_READER(GDALMDReaderRapidEye);
154 : }
155 :
156 : // required filename.tif filename.rpc filename.txt
157 5026 : if (nType & MDR_KOMPSAT)
158 : {
159 5026 : INIT_READER(GDALMDReaderKompsat);
160 : }
161 :
162 5024 : if (nType & MDR_EROS)
163 : {
164 5024 : INIT_READER(GDALMDReaderEROS);
165 : }
166 :
167 5023 : if (nType & MDR_ALOS)
168 : {
169 5023 : INIT_READER(GDALMDReaderALOS);
170 : }
171 :
172 5022 : return nullptr;
173 : }
174 :
175 : /**
176 : * GDALMDReaderBase()
177 : */
178 55458 : GDALMDReaderBase::GDALMDReaderBase(const char * /* pszPath */,
179 55458 : char ** /* papszSiblingFiles */)
180 : {
181 55458 : }
182 :
183 : /**
184 : * ~GDALMDReaderBase()
185 : */
186 110916 : GDALMDReaderBase::~GDALMDReaderBase()
187 : {
188 55458 : CSLDestroy(m_papszIMDMD);
189 55458 : CSLDestroy(m_papszRPCMD);
190 55458 : CSLDestroy(m_papszIMAGERYMD);
191 55458 : CSLDestroy(m_papszDEFAULTMD);
192 55458 : }
193 :
194 : /**
195 : * GetMetadataItem()
196 : */
197 70 : char **GDALMDReaderBase::GetMetadataDomain(const char *pszDomain)
198 : {
199 70 : LoadMetadata();
200 70 : if (EQUAL(pszDomain, MD_DOMAIN_DEFAULT))
201 0 : return m_papszDEFAULTMD;
202 70 : else if (EQUAL(pszDomain, MD_DOMAIN_IMD))
203 5 : return m_papszIMDMD;
204 65 : else if (EQUAL(pszDomain, MD_DOMAIN_RPC))
205 65 : return m_papszRPCMD;
206 0 : else if (EQUAL(pszDomain, MD_DOMAIN_IMAGERY))
207 0 : return m_papszIMAGERYMD;
208 0 : return nullptr;
209 : }
210 :
211 : /**
212 : * LoadMetadata()
213 : */
214 0 : void GDALMDReaderBase::LoadMetadata()
215 : {
216 0 : if (m_bIsMetadataLoad)
217 0 : return;
218 0 : m_bIsMetadataLoad = true;
219 : }
220 :
221 : /**
222 : * GetAcqisitionTimeFromString()
223 : */
224 50 : GIntBig GDALMDReaderBase::GetAcquisitionTimeFromString(const char *pszDateTime)
225 : {
226 50 : if (nullptr == pszDateTime)
227 0 : return 0;
228 :
229 50 : int iYear = 0;
230 50 : int iMonth = 0;
231 50 : int iDay = 0;
232 50 : int iHours = 0;
233 50 : int iMin = 0;
234 50 : int iSec = 0;
235 :
236 50 : const int r = sscanf(pszDateTime, "%d-%d-%dT%d:%d:%d.%*dZ", &iYear, &iMonth,
237 : &iDay, &iHours, &iMin, &iSec);
238 :
239 50 : if (r != 6)
240 0 : return 0;
241 :
242 : struct tm tmDateTime;
243 50 : tmDateTime.tm_sec = iSec;
244 50 : tmDateTime.tm_min = iMin;
245 50 : tmDateTime.tm_hour = iHours;
246 50 : tmDateTime.tm_mday = iDay;
247 50 : tmDateTime.tm_mon = iMonth - 1;
248 50 : tmDateTime.tm_year = iYear - 1900;
249 50 : tmDateTime.tm_isdst = -1;
250 :
251 50 : return CPLYMDHMSToUnixTime(&tmDateTime);
252 : }
253 :
254 : /**
255 : * FillMetadata()
256 : */
257 :
258 : #define SETMETADATA(mdmd, md, domain) \
259 : if (nullptr != md) \
260 : { \
261 : char **papszCurrentMd = CSLDuplicate(mdmd->GetMetadata(domain)); \
262 : papszCurrentMd = CSLMerge(papszCurrentMd, md); \
263 : mdmd->SetMetadata(papszCurrentMd, domain); \
264 : CSLDestroy(papszCurrentMd); \
265 : }
266 :
267 73 : bool GDALMDReaderBase::FillMetadata(GDALMultiDomainMetadata *poMDMD)
268 : {
269 73 : if (nullptr == poMDMD)
270 0 : return false;
271 :
272 73 : LoadMetadata();
273 :
274 73 : SETMETADATA(poMDMD, m_papszIMDMD, MD_DOMAIN_IMD);
275 73 : SETMETADATA(poMDMD, m_papszRPCMD, MD_DOMAIN_RPC);
276 73 : SETMETADATA(poMDMD, m_papszIMAGERYMD, MD_DOMAIN_IMAGERY);
277 73 : SETMETADATA(poMDMD, m_papszDEFAULTMD, MD_DOMAIN_DEFAULT);
278 :
279 73 : return true;
280 : }
281 :
282 : /**
283 : * AddXMLNameValueToList()
284 : */
285 3797 : char **GDALMDReaderBase::AddXMLNameValueToList(char **papszList,
286 : const char *pszName,
287 : const char *pszValue)
288 : {
289 3797 : return CSLAddNameValue(papszList, pszName, pszValue);
290 : }
291 :
292 : /**
293 : * ReadXMLToListFirstPass()
294 : */
295 4216 : bool GDALMDReaderBase::ReadXMLToListFirstPass(
296 : const CPLXMLNode *psNode, std::map<std::string, int> &oMapCountKeysFull,
297 : const std::string &osPrefixFull, int nDepth)
298 : {
299 4216 : if (nDepth == 10)
300 : {
301 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too much nested XML");
302 0 : return false;
303 : }
304 4216 : if (nullptr == psNode)
305 0 : return true;
306 : while (true)
307 : {
308 4379 : if (psNode->eType == CXT_Element)
309 : {
310 4366 : std::string osNewPrefixFull;
311 4366 : for (const CPLXMLNode *psChildNode = psNode->psChild;
312 12332 : nullptr != psChildNode; psChildNode = psChildNode->psNext)
313 : {
314 7966 : if (psChildNode->eType == CXT_Element)
315 : {
316 4187 : osNewPrefixFull = !osPrefixFull.empty()
317 8374 : ? osPrefixFull
318 4187 : : std::string(psNode->pszValue);
319 4187 : osNewPrefixFull += '.';
320 4187 : osNewPrefixFull += psChildNode->pszValue;
321 4187 : osNewPrefixFull +=
322 4187 : CPLSPrintf("_%d", ++oMapCountKeysFull[osNewPrefixFull]);
323 :
324 4187 : if (!ReadXMLToListFirstPass(psChildNode, oMapCountKeysFull,
325 : osNewPrefixFull, nDepth + 1))
326 0 : return false;
327 : }
328 : }
329 : }
330 :
331 : // proceed next only on top level
332 :
333 4379 : if (nullptr != psNode->psNext && osPrefixFull.empty())
334 : {
335 163 : psNode = psNode->psNext;
336 : }
337 : else
338 : {
339 4216 : break;
340 : }
341 163 : }
342 4216 : return true;
343 : }
344 :
345 : /**
346 : * ReadXMLToList()
347 : */
348 7651 : char **GDALMDReaderBase::ReadXMLToList(
349 : const CPLXMLNode *psNode, char **papszList,
350 : const std::map<std::string, int> &oMapCountKeysFullRef,
351 : std::map<std::string, int> &oMapCountKeysFull,
352 : std::map<std::string, int> &oMapCountKeys, const std::string &osPrefix,
353 : const std::string &osPrefixFull)
354 : {
355 7651 : if (nullptr == psNode)
356 0 : return papszList;
357 :
358 : while (true)
359 : {
360 7814 : if (psNode->eType == CXT_Text)
361 : {
362 3435 : papszList = AddXMLNameValueToList(papszList, osPrefix.c_str(),
363 3435 : psNode->pszValue);
364 : }
365 :
366 7814 : if (psNode->eType == CXT_Element)
367 : {
368 8732 : std::string osNewPrefix;
369 8732 : std::string osNewPrefixFull;
370 4366 : for (const CPLXMLNode *psChildNode = psNode->psChild;
371 12332 : nullptr != psChildNode; psChildNode = psChildNode->psNext)
372 : {
373 7966 : if (psChildNode->eType == CXT_Element)
374 : {
375 4187 : osNewPrefixFull = !osPrefixFull.empty()
376 8374 : ? osPrefixFull
377 4187 : : std::string(psNode->pszValue);
378 4187 : osNewPrefixFull += '.';
379 4187 : osNewPrefixFull += psChildNode->pszValue;
380 :
381 : const auto oIter =
382 4187 : oMapCountKeysFullRef.find(osNewPrefixFull);
383 4187 : CPLAssert(oIter != oMapCountKeysFullRef.end());
384 4187 : osNewPrefixFull +=
385 4187 : CPLSPrintf("_%d", ++oMapCountKeysFull[osNewPrefixFull]);
386 :
387 4187 : osNewPrefix = !osPrefix.empty()
388 8374 : ? osPrefix
389 4187 : : std::string(psNode->pszValue);
390 4187 : osNewPrefix += '.';
391 4187 : osNewPrefix += psChildNode->pszValue;
392 4187 : const int nIndex = ++oMapCountKeys[osNewPrefix];
393 4187 : const bool bMultipleInstances = oIter->second >= 2;
394 4187 : if (bMultipleInstances)
395 : {
396 221 : osNewPrefix += CPLSPrintf("_%d", nIndex);
397 : }
398 4187 : papszList = ReadXMLToList(psChildNode, papszList,
399 : oMapCountKeysFullRef,
400 : oMapCountKeysFull, oMapCountKeys,
401 : osNewPrefix, osNewPrefixFull);
402 : }
403 3779 : else if (psChildNode->eType == CXT_Attribute)
404 : {
405 344 : papszList = AddXMLNameValueToList(
406 : papszList,
407 : CPLSPrintf("%s.%s", osPrefix.c_str(),
408 344 : psChildNode->pszValue),
409 344 : psChildNode->psChild->pszValue);
410 : }
411 : else
412 : {
413 : // Text nodes should always have name
414 3435 : if (osPrefix.empty())
415 : {
416 69 : papszList = ReadXMLToList(
417 : psChildNode, papszList, oMapCountKeysFullRef,
418 23 : oMapCountKeysFull, oMapCountKeys, psNode->pszValue,
419 23 : psNode->pszValue);
420 : }
421 : else
422 : {
423 3412 : papszList = ReadXMLToList(
424 : psChildNode, papszList, oMapCountKeysFullRef,
425 : oMapCountKeysFull, oMapCountKeys, osPrefix.c_str(),
426 : osNewPrefixFull.c_str());
427 : }
428 : }
429 : }
430 : }
431 :
432 : // proceed next only on top level
433 :
434 7814 : if (nullptr != psNode->psNext && osPrefix.empty())
435 : {
436 163 : psNode = psNode->psNext;
437 : }
438 : else
439 : {
440 7651 : break;
441 : }
442 163 : }
443 :
444 7651 : return papszList;
445 : }
446 :
447 : /**
448 : * ReadXMLToList()
449 : */
450 29 : char **GDALMDReaderBase::ReadXMLToList(CPLXMLNode *psNode, char **papszList,
451 : const char *pszName)
452 : {
453 58 : std::map<std::string, int> oMapCountKeysFullRef;
454 29 : if (!ReadXMLToListFirstPass(psNode, oMapCountKeysFullRef, pszName, 0))
455 0 : return papszList;
456 58 : std::map<std::string, int> oMapCountKeysFull;
457 29 : std::map<std::string, int> oMapCountKeys;
458 58 : return ReadXMLToList(psNode, papszList, oMapCountKeysFullRef,
459 29 : oMapCountKeysFull, oMapCountKeys, pszName, pszName);
460 : }
461 :
462 : //------------------------------------------------------------------------------
463 : // Miscellaneous functions
464 : //------------------------------------------------------------------------------
465 :
466 : /**
467 : * GDALCheckFileHeader()
468 : */
469 17 : bool GDALCheckFileHeader(const CPLString &soFilePath, const char *pszTestString,
470 : int nBufferSize)
471 : {
472 17 : VSILFILE *fpL = VSIFOpenL(soFilePath, "r");
473 17 : if (fpL == nullptr)
474 0 : return false;
475 17 : char *pBuffer = new char[nBufferSize + 1];
476 : const int nReadBytes =
477 17 : static_cast<int>(VSIFReadL(pBuffer, 1, nBufferSize, fpL));
478 17 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
479 17 : if (nReadBytes == 0)
480 : {
481 0 : delete[] pBuffer;
482 0 : return false;
483 : }
484 17 : pBuffer[nReadBytes] = '\0';
485 :
486 17 : const bool bResult = strstr(pBuffer, pszTestString) != nullptr;
487 17 : delete[] pBuffer;
488 :
489 17 : return bResult;
490 : }
491 :
492 : /**
493 : * CPLStrip()
494 : */
495 164 : CPLString CPLStrip(const CPLString &sString, const char cChar)
496 : {
497 164 : if (sString.empty())
498 0 : return sString;
499 :
500 164 : size_t dCopyFrom = 0;
501 164 : size_t dCopyCount = sString.size();
502 :
503 164 : if (sString[0] == cChar)
504 : {
505 36 : dCopyFrom++;
506 36 : dCopyCount--;
507 : }
508 :
509 164 : if (sString.back() == cChar)
510 36 : dCopyCount--;
511 :
512 164 : if (dCopyCount == 0)
513 0 : return CPLString();
514 :
515 328 : return sString.substr(dCopyFrom, dCopyCount);
516 : }
517 :
518 : /**
519 : * CPLStripQuotes()
520 : */
521 82 : CPLString CPLStripQuotes(const CPLString &sString)
522 : {
523 164 : return CPLStrip(CPLStrip(sString, '"'), '\'');
524 : }
525 :
526 : /************************************************************************/
527 : /* GDALLoadRPBFile() */
528 : /************************************************************************/
529 :
530 : static const char *const apszRPBMap[] = {apszRPCTXTSingleValItems[0],
531 : "IMAGE.errBias",
532 : apszRPCTXTSingleValItems[1],
533 : "IMAGE.errRand",
534 : apszRPCTXTSingleValItems[2],
535 : "IMAGE.lineOffset",
536 : apszRPCTXTSingleValItems[3],
537 : "IMAGE.sampOffset",
538 : apszRPCTXTSingleValItems[4],
539 : "IMAGE.latOffset",
540 : apszRPCTXTSingleValItems[5],
541 : "IMAGE.longOffset",
542 : apszRPCTXTSingleValItems[6],
543 : "IMAGE.heightOffset",
544 : apszRPCTXTSingleValItems[7],
545 : "IMAGE.lineScale",
546 : apszRPCTXTSingleValItems[8],
547 : "IMAGE.sampScale",
548 : apszRPCTXTSingleValItems[9],
549 : "IMAGE.latScale",
550 : apszRPCTXTSingleValItems[10],
551 : "IMAGE.longScale",
552 : apszRPCTXTSingleValItems[11],
553 : "IMAGE.heightScale",
554 : apszRPCTXT20ValItems[0],
555 : "IMAGE.lineNumCoef",
556 : apszRPCTXT20ValItems[1],
557 : "IMAGE.lineDenCoef",
558 : apszRPCTXT20ValItems[2],
559 : "IMAGE.sampNumCoef",
560 : apszRPCTXT20ValItems[3],
561 : "IMAGE.sampDenCoef",
562 : nullptr,
563 : nullptr};
564 :
565 10 : char **GDALLoadRPBFile(const CPLString &soFilePath)
566 : {
567 10 : if (soFilePath.empty())
568 0 : return nullptr;
569 :
570 : /* -------------------------------------------------------------------- */
571 : /* Read file and parse. */
572 : /* -------------------------------------------------------------------- */
573 10 : VSILFILE *fp = VSIFOpenL(soFilePath, "r");
574 :
575 10 : if (fp == nullptr)
576 0 : return nullptr;
577 :
578 20 : CPLKeywordParser oParser;
579 10 : if (!oParser.Ingest(fp))
580 : {
581 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
582 0 : return nullptr;
583 : }
584 :
585 10 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
586 :
587 : /* -------------------------------------------------------------------- */
588 : /* Extract RPC information, in a GDAL "standard" metadata format. */
589 : /* -------------------------------------------------------------------- */
590 10 : char **papszMD = nullptr;
591 170 : for (int i = 0; apszRPBMap[i] != nullptr; i += 2)
592 : {
593 160 : const char *pszRPBVal = oParser.GetKeyword(apszRPBMap[i + 1]);
594 160 : CPLString osAdjVal;
595 :
596 160 : if (pszRPBVal == nullptr)
597 : {
598 0 : if (strcmp(apszRPBMap[i], RPC_ERR_RAND) == 0 ||
599 0 : strcmp(apszRPBMap[i], RPC_ERR_BIAS) == 0)
600 : {
601 0 : continue;
602 : }
603 0 : CPLError(
604 : CE_Failure, CPLE_AppDefined,
605 : "%s file found, but missing %s field (and possibly others).",
606 0 : soFilePath.c_str(), apszRPBMap[i + 1]);
607 0 : CSLDestroy(papszMD);
608 0 : return nullptr;
609 : }
610 :
611 160 : if (strchr(pszRPBVal, ',') == nullptr)
612 120 : osAdjVal = pszRPBVal;
613 : else
614 : {
615 : // strip out commas and turn newlines into spaces.
616 11280 : for (int j = 0; pszRPBVal[j] != '\0'; j++)
617 : {
618 11240 : switch (pszRPBVal[j])
619 : {
620 760 : case ',':
621 : case '\n':
622 : case '\r':
623 760 : osAdjVal += ' ';
624 760 : break;
625 :
626 80 : case '(':
627 : case ')':
628 80 : break;
629 :
630 10400 : default:
631 10400 : osAdjVal += pszRPBVal[j];
632 : }
633 : }
634 : }
635 :
636 160 : papszMD = CSLSetNameValue(papszMD, apszRPBMap[i], osAdjVal);
637 : }
638 :
639 10 : return papszMD;
640 : }
641 :
642 : /************************************************************************/
643 : /* GDALLoadRPCFile() */
644 : /************************************************************************/
645 :
646 : /* Load a GeoEye _rpc.txt file. See ticket
647 : * http://trac.osgeo.org/gdal/ticket/3639 */
648 :
649 20 : char **GDALLoadRPCFile(const CPLString &soFilePath)
650 : {
651 20 : if (soFilePath.empty())
652 0 : return nullptr;
653 :
654 : /* -------------------------------------------------------------------- */
655 : /* Read file and parse. */
656 : /* -------------------------------------------------------------------- */
657 : // 100 lines would be enough, but some .rpc files have CR CR LF end of
658 : // lines, which result in a blank line to be recognized, so accept up
659 : // to 200 lines (#6341)
660 20 : char **papszLines = CSLLoad2(soFilePath, 200, 100, nullptr);
661 20 : if (!papszLines)
662 0 : return nullptr;
663 :
664 20 : char **papszMD = nullptr;
665 :
666 : /* From LINE_OFF to HEIGHT_SCALE */
667 260 : for (size_t i = 0; i < 23; i += 2)
668 : {
669 240 : const char *pszRPBVal = CSLFetchNameValue(papszLines, apszRPBMap[i]);
670 :
671 240 : if (pszRPBVal == nullptr)
672 : {
673 8 : if (strcmp(apszRPBMap[i], RPC_ERR_RAND) == 0 ||
674 4 : strcmp(apszRPBMap[i], RPC_ERR_BIAS) == 0)
675 : {
676 8 : continue;
677 : }
678 0 : CPLError(
679 : CE_Failure, CPLE_AppDefined,
680 : "%s file found, but missing %s field (and possibly others).",
681 0 : soFilePath.c_str(), apszRPBMap[i]);
682 0 : CSLDestroy(papszMD);
683 0 : CSLDestroy(papszLines);
684 0 : return nullptr;
685 : }
686 : else
687 : {
688 500 : while (*pszRPBVal == ' ' || *pszRPBVal == '\t')
689 268 : pszRPBVal++;
690 232 : papszMD = CSLSetNameValue(papszMD, apszRPBMap[i], pszRPBVal);
691 : }
692 : }
693 :
694 : /* For LINE_NUM_COEFF, LINE_DEN_COEFF, SAMP_NUM_COEFF, SAMP_DEN_COEFF */
695 : /* parameters that have 20 values each */
696 100 : for (size_t i = 24; apszRPBMap[i] != nullptr; i += 2)
697 : {
698 80 : CPLString soVal;
699 1680 : for (int j = 1; j <= 20; j++)
700 : {
701 1600 : CPLString soRPBMapItem;
702 1600 : soRPBMapItem.Printf("%s_%d", apszRPBMap[i], j);
703 : const char *pszRPBVal =
704 1600 : CSLFetchNameValue(papszLines, soRPBMapItem.c_str());
705 1600 : if (pszRPBVal == nullptr)
706 : {
707 0 : CPLError(CE_Failure, CPLE_AppDefined,
708 : "%s file found, but missing %s field (and possibly "
709 : "others).",
710 : soFilePath.c_str(), soRPBMapItem.c_str());
711 0 : CSLDestroy(papszMD);
712 0 : CSLDestroy(papszLines);
713 0 : return nullptr;
714 : }
715 : else
716 : {
717 3200 : while (*pszRPBVal == ' ' || *pszRPBVal == '\t')
718 1600 : pszRPBVal++;
719 1600 : soVal += pszRPBVal;
720 1600 : soVal += " ";
721 : }
722 : }
723 80 : papszMD = CSLSetNameValue(papszMD, apszRPBMap[i], soVal.c_str());
724 : }
725 :
726 20 : CSLDestroy(papszLines);
727 20 : return papszMD;
728 : }
729 :
730 : /************************************************************************/
731 : /* GDALWriteRPCTXTFile() */
732 : /************************************************************************/
733 :
734 7 : CPLErr GDALWriteRPCTXTFile(const char *pszFilename, char **papszMD)
735 :
736 : {
737 14 : CPLString osRPCFilename = pszFilename;
738 14 : CPLString soPt(".");
739 7 : size_t found = osRPCFilename.rfind(soPt);
740 7 : if (found == CPLString::npos)
741 0 : return CE_Failure;
742 7 : osRPCFilename.replace(found, osRPCFilename.size() - found, "_RPC.TXT");
743 7 : if (papszMD == nullptr)
744 : {
745 5 : VSIUnlink(osRPCFilename);
746 5 : return CE_None;
747 : }
748 :
749 : /* -------------------------------------------------------------------- */
750 : /* Read file and parse. */
751 : /* -------------------------------------------------------------------- */
752 2 : VSILFILE *fp = VSIFOpenL(osRPCFilename, "w");
753 :
754 2 : if (fp == nullptr)
755 : {
756 0 : CPLError(CE_Failure, CPLE_OpenFailed,
757 : "Unable to create %s for writing.\n%s", osRPCFilename.c_str(),
758 : CPLGetLastErrorMsg());
759 0 : return CE_Failure;
760 : }
761 :
762 : /* -------------------------------------------------------------------- */
763 : /* Write RPC values from our RPC metadata. */
764 : /* -------------------------------------------------------------------- */
765 2 : bool bOK = true;
766 26 : for (int i = 0; apszRPCTXTSingleValItems[i] != nullptr; i++)
767 : {
768 : const char *pszRPCVal =
769 24 : CSLFetchNameValue(papszMD, apszRPCTXTSingleValItems[i]);
770 24 : if (pszRPCVal == nullptr)
771 : {
772 2 : if (strcmp(apszRPCTXTSingleValItems[i], RPC_ERR_BIAS) == 0 ||
773 1 : strcmp(apszRPCTXTSingleValItems[i], RPC_ERR_RAND) == 0)
774 : {
775 2 : continue;
776 : }
777 0 : CPLError(CE_Failure, CPLE_AppDefined,
778 : "%s field missing in metadata, %s file not written.",
779 0 : apszRPCTXTSingleValItems[i], osRPCFilename.c_str());
780 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
781 0 : VSIUnlink(osRPCFilename);
782 0 : return CE_Failure;
783 : }
784 :
785 22 : bOK &= VSIFPrintfL(fp, "%s: %s\n", apszRPCTXTSingleValItems[i],
786 22 : pszRPCVal) > 0;
787 : }
788 :
789 10 : for (int i = 0; apszRPCTXT20ValItems[i] != nullptr; i++)
790 : {
791 : const char *pszRPCVal =
792 8 : CSLFetchNameValue(papszMD, apszRPCTXT20ValItems[i]);
793 8 : if (pszRPCVal == nullptr)
794 : {
795 0 : CPLError(CE_Failure, CPLE_AppDefined,
796 : "%s field missing in metadata, %s file not written.",
797 0 : apszRPCTXTSingleValItems[i], osRPCFilename.c_str());
798 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
799 0 : VSIUnlink(osRPCFilename);
800 0 : return CE_Failure;
801 : }
802 :
803 : char **papszItems =
804 8 : CSLTokenizeStringComplex(pszRPCVal, " ,", FALSE, FALSE);
805 :
806 8 : if (CSLCount(papszItems) != 20)
807 : {
808 0 : CPLError(CE_Failure, CPLE_AppDefined,
809 : "%s field is corrupt (not 20 values), %s file not "
810 : "written.\n%s = %s",
811 0 : apszRPCTXT20ValItems[i], osRPCFilename.c_str(),
812 0 : apszRPCTXT20ValItems[i], pszRPCVal);
813 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
814 0 : VSIUnlink(osRPCFilename);
815 0 : CSLDestroy(papszItems);
816 0 : return CE_Failure;
817 : }
818 :
819 168 : for (int j = 0; j < 20; j++)
820 : {
821 320 : bOK &= VSIFPrintfL(fp, "%s_%d: %s\n", apszRPCTXT20ValItems[i],
822 160 : j + 1, papszItems[j]) > 0;
823 : }
824 8 : CSLDestroy(papszItems);
825 : }
826 :
827 2 : if (VSIFCloseL(fp) != 0)
828 0 : bOK = false;
829 :
830 2 : return bOK ? CE_None : CE_Failure;
831 : }
832 :
833 : /************************************************************************/
834 : /* GDALWriteRPBFile() */
835 : /************************************************************************/
836 :
837 9 : CPLErr GDALWriteRPBFile(const char *pszFilename, char **papszMD)
838 :
839 : {
840 18 : CPLString osRPBFilename = CPLResetExtension(pszFilename, "RPB");
841 9 : if (papszMD == nullptr)
842 : {
843 5 : VSIUnlink(osRPBFilename);
844 5 : return CE_None;
845 : }
846 :
847 : /* -------------------------------------------------------------------- */
848 : /* Read file and parse. */
849 : /* -------------------------------------------------------------------- */
850 4 : VSILFILE *fp = VSIFOpenL(osRPBFilename, "w");
851 :
852 4 : if (fp == nullptr)
853 : {
854 0 : CPLError(CE_Failure, CPLE_OpenFailed,
855 : "Unable to create %s for writing.\n%s", osRPBFilename.c_str(),
856 : CPLGetLastErrorMsg());
857 0 : return CE_Failure;
858 : }
859 :
860 : /* -------------------------------------------------------------------- */
861 : /* Write the prefix information. */
862 : /* -------------------------------------------------------------------- */
863 4 : bool bOK = VSIFPrintfL(fp, "%s", "satId = \"QB02\";\n") > 0;
864 4 : bOK &= VSIFPrintfL(fp, "%s", "bandId = \"P\";\n") > 0;
865 4 : bOK &= VSIFPrintfL(fp, "%s", "SpecId = \"RPC00B\";\n") > 0;
866 4 : bOK &= VSIFPrintfL(fp, "%s", "BEGIN_GROUP = IMAGE\n") > 0;
867 :
868 : /* -------------------------------------------------------------------- */
869 : /* Write RPC values from our RPC metadata. */
870 : /* -------------------------------------------------------------------- */
871 68 : for (int i = 0; apszRPBMap[i] != nullptr; i += 2)
872 : {
873 64 : const char *pszRPBVal = CSLFetchNameValue(papszMD, apszRPBMap[i]);
874 : const char *pszRPBTag;
875 :
876 64 : if (pszRPBVal == nullptr)
877 : {
878 8 : if (strcmp(apszRPBMap[i], RPC_ERR_BIAS) == 0)
879 : {
880 4 : bOK &= VSIFPrintfL(fp, "%s", "\terrBias = 0.0;\n") > 0;
881 4 : continue;
882 : }
883 4 : else if (strcmp(apszRPBMap[i], RPC_ERR_RAND) == 0)
884 : {
885 4 : bOK &= VSIFPrintfL(fp, "%s", "\terrRand = 0.0;\n") > 0;
886 4 : continue;
887 : }
888 0 : CPLError(CE_Failure, CPLE_AppDefined,
889 : "%s field missing in metadata, %s file not written.",
890 0 : apszRPBMap[i], osRPBFilename.c_str());
891 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
892 0 : VSIUnlink(osRPBFilename);
893 0 : return CE_Failure;
894 : }
895 :
896 56 : pszRPBTag = apszRPBMap[i + 1];
897 56 : if (STARTS_WITH_CI(pszRPBTag, "IMAGE."))
898 56 : pszRPBTag += 6;
899 :
900 56 : if (strstr(apszRPBMap[i], "COEF") == nullptr)
901 : {
902 40 : bOK &= VSIFPrintfL(fp, "\t%s = %s;\n", pszRPBTag, pszRPBVal) > 0;
903 : }
904 : else
905 : {
906 : // Reformat in brackets with commas over multiple lines.
907 :
908 16 : bOK &= VSIFPrintfL(fp, "\t%s = (\n", pszRPBTag) > 0;
909 :
910 : char **papszItems =
911 16 : CSLTokenizeStringComplex(pszRPBVal, " ,", FALSE, FALSE);
912 :
913 16 : if (CSLCount(papszItems) != 20)
914 : {
915 0 : CPLError(CE_Failure, CPLE_AppDefined,
916 : "%s field is corrupt (not 20 values), %s file not "
917 : "written.\n%s = %s",
918 0 : apszRPBMap[i], osRPBFilename.c_str(), apszRPBMap[i],
919 : pszRPBVal);
920 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
921 0 : VSIUnlink(osRPBFilename);
922 0 : CSLDestroy(papszItems);
923 0 : return CE_Failure;
924 : }
925 :
926 336 : for (int j = 0; j < 20; j++)
927 : {
928 320 : if (j < 19)
929 304 : bOK &= VSIFPrintfL(fp, "\t\t\t%s,\n", papszItems[j]) > 0;
930 : else
931 16 : bOK &= VSIFPrintfL(fp, "\t\t\t%s);\n", papszItems[j]) > 0;
932 : }
933 16 : CSLDestroy(papszItems);
934 : }
935 : }
936 :
937 : /* -------------------------------------------------------------------- */
938 : /* Write end part */
939 : /* -------------------------------------------------------------------- */
940 4 : bOK &= VSIFPrintfL(fp, "%s", "END_GROUP = IMAGE\n") > 0;
941 4 : bOK &= VSIFPrintfL(fp, "END;\n") > 0;
942 4 : if (VSIFCloseL(fp) != 0)
943 0 : bOK = false;
944 :
945 4 : return bOK ? CE_None : CE_Failure;
946 : }
947 :
948 : /************************************************************************/
949 : /* GDAL_IMD_AA2R() */
950 : /* */
951 : /* Translate AA version IMD file to R version. */
952 : /************************************************************************/
953 :
954 0 : static bool GDAL_IMD_AA2R(char ***ppapszIMD)
955 :
956 : {
957 0 : char **papszIMD = *ppapszIMD;
958 :
959 : /* -------------------------------------------------------------------- */
960 : /* Verify that we have a new format file. */
961 : /* -------------------------------------------------------------------- */
962 0 : const char *pszValue = CSLFetchNameValue(papszIMD, "version");
963 :
964 0 : if (pszValue == nullptr)
965 0 : return false;
966 :
967 0 : if (EQUAL(pszValue, "\"R\""))
968 0 : return true;
969 :
970 0 : if (!EQUAL(pszValue, "\"AA\""))
971 : {
972 0 : CPLDebug("IMD",
973 : "The file is not the expected 'version = \"AA\"' format.\n"
974 : "Proceeding, but file may be corrupted.");
975 : }
976 :
977 : /* -------------------------------------------------------------------- */
978 : /* Fix the version line. */
979 : /* -------------------------------------------------------------------- */
980 0 : papszIMD = CSLSetNameValue(papszIMD, "version", "\"R\"");
981 :
982 : /* -------------------------------------------------------------------- */
983 : /* remove a bunch of fields. */
984 : /* -------------------------------------------------------------------- */
985 : static const char *const apszToRemove[] = {
986 : "productCatalogId", "childCatalogId",
987 : "productType", "numberOfLooks",
988 : "effectiveBandwidth", "mode",
989 : "scanDirection", "cloudCover",
990 : "productGSD", nullptr};
991 :
992 0 : for (int iKey = 0; apszToRemove[iKey] != nullptr; iKey++)
993 : {
994 0 : int iTarget = CSLFindName(papszIMD, apszToRemove[iKey]);
995 0 : if (iTarget != -1)
996 0 : papszIMD = CSLRemoveStrings(papszIMD, iTarget, 1, nullptr);
997 : }
998 :
999 : /* -------------------------------------------------------------------- */
1000 : /* Replace various min/mean/max with just the mean. */
1001 : /* -------------------------------------------------------------------- */
1002 : static const char *const keylist[] = {"CollectedRowGSD",
1003 : "CollectedColGSD",
1004 : "SunAz",
1005 : "SunEl",
1006 : "SatAz",
1007 : "SatEl",
1008 : "InTrackViewAngle",
1009 : "CrossTrackViewAngle",
1010 : "OffNadirViewAngle",
1011 : nullptr};
1012 :
1013 0 : for (int iKey = 0; keylist[iKey] != nullptr; iKey++)
1014 : {
1015 0 : CPLString osTarget;
1016 0 : osTarget.Printf("IMAGE_1.min%s", keylist[iKey]);
1017 0 : int iTarget = CSLFindName(papszIMD, osTarget);
1018 0 : if (iTarget != -1)
1019 0 : papszIMD = CSLRemoveStrings(papszIMD, iTarget, 1, nullptr);
1020 :
1021 0 : osTarget.Printf("IMAGE_1.max%s", keylist[iKey]);
1022 0 : iTarget = CSLFindName(papszIMD, osTarget);
1023 0 : if (iTarget != -1)
1024 0 : papszIMD = CSLRemoveStrings(papszIMD, iTarget, 1, nullptr);
1025 :
1026 0 : osTarget.Printf("IMAGE_1.mean%s", keylist[iKey]);
1027 0 : iTarget = CSLFindName(papszIMD, osTarget);
1028 0 : if (iTarget != -1)
1029 : {
1030 0 : CPLString osValue = CSLFetchNameValue(papszIMD, osTarget);
1031 0 : CPLString osLine;
1032 : osTarget.Printf(
1033 : "IMAGE_1.%c%s",
1034 0 : CPLTolower(static_cast<unsigned char>(keylist[iKey][0])),
1035 0 : keylist[iKey] + 1);
1036 :
1037 0 : osLine = osTarget + "=" + osValue;
1038 :
1039 0 : CPLFree(papszIMD[iTarget]);
1040 0 : papszIMD[iTarget] = CPLStrdup(osLine);
1041 : }
1042 : }
1043 :
1044 0 : *ppapszIMD = papszIMD;
1045 0 : return true;
1046 : }
1047 :
1048 : /************************************************************************/
1049 : /* GDALLoadIMDFile() */
1050 : /************************************************************************/
1051 :
1052 38 : char **GDALLoadIMDFile(const CPLString &osFilePath)
1053 : {
1054 38 : if (osFilePath.empty())
1055 0 : return nullptr;
1056 :
1057 : /* -------------------------------------------------------------------- */
1058 : /* Read file and parse. */
1059 : /* -------------------------------------------------------------------- */
1060 76 : CPLKeywordParser oParser;
1061 :
1062 38 : VSILFILE *fp = VSIFOpenL(osFilePath, "r");
1063 :
1064 38 : if (fp == nullptr)
1065 0 : return nullptr;
1066 :
1067 38 : if (!oParser.Ingest(fp))
1068 : {
1069 4 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
1070 4 : return nullptr;
1071 : }
1072 :
1073 34 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
1074 :
1075 : /* -------------------------------------------------------------------- */
1076 : /* Consider version changing. */
1077 : /* -------------------------------------------------------------------- */
1078 34 : char **papszIMD = CSLDuplicate(oParser.GetAllKeywords());
1079 34 : const char *pszVersion = CSLFetchNameValue(papszIMD, "version");
1080 :
1081 34 : if (pszVersion == nullptr)
1082 : {
1083 : /* ? */;
1084 : }
1085 26 : else if (EQUAL(pszVersion, "\"AA\""))
1086 : {
1087 0 : GDAL_IMD_AA2R(&papszIMD);
1088 : }
1089 :
1090 34 : return papszIMD;
1091 : }
1092 :
1093 : /************************************************************************/
1094 : /* GDALWriteIMDMultiLine() */
1095 : /* */
1096 : /* Write a value that is split over multiple lines. */
1097 : /************************************************************************/
1098 :
1099 26 : static void GDALWriteIMDMultiLine(VSILFILE *fp, const char *pszValue)
1100 :
1101 : {
1102 : char **papszItems =
1103 26 : CSLTokenizeStringComplex(pszValue, "(,) ", FALSE, FALSE);
1104 26 : const int nItemCount = CSLCount(papszItems);
1105 :
1106 26 : CPL_IGNORE_RET_VAL(VSIFPrintfL(fp, "(\n"));
1107 :
1108 260 : for (int i = 0; i < nItemCount; i++)
1109 : {
1110 234 : if (i == nItemCount - 1)
1111 26 : CPL_IGNORE_RET_VAL(VSIFPrintfL(fp, "\t%s );\n", papszItems[i]));
1112 : else
1113 208 : CPL_IGNORE_RET_VAL(VSIFPrintfL(fp, "\t%s,\n", papszItems[i]));
1114 : }
1115 26 : CSLDestroy(papszItems);
1116 26 : }
1117 :
1118 : /************************************************************************/
1119 : /* GDALWriteIMDFile() */
1120 : /************************************************************************/
1121 :
1122 20 : CPLErr GDALWriteIMDFile(const char *pszFilename, char **papszMD)
1123 :
1124 : {
1125 40 : CPLString osRPBFilename = CPLResetExtension(pszFilename, "IMD");
1126 :
1127 : /* -------------------------------------------------------------------- */
1128 : /* Read file and parse. */
1129 : /* -------------------------------------------------------------------- */
1130 20 : VSILFILE *fp = VSIFOpenL(osRPBFilename, "w");
1131 :
1132 20 : if (fp == nullptr)
1133 : {
1134 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1135 : "Unable to create %s for writing.\n%s", osRPBFilename.c_str(),
1136 : CPLGetLastErrorMsg());
1137 0 : return CE_Failure;
1138 : }
1139 :
1140 : /* ==================================================================== */
1141 : /* -------------------------------------------------------------------- */
1142 : /* Loop through all values writing. */
1143 : /* -------------------------------------------------------------------- */
1144 : /* ==================================================================== */
1145 20 : CPLString osCurSection;
1146 20 : bool bOK = true;
1147 :
1148 1817 : for (int iKey = 0; papszMD[iKey] != nullptr; iKey++)
1149 : {
1150 1797 : char *pszRawKey = nullptr;
1151 1797 : const char *pszValue = CPLParseNameValue(papszMD[iKey], &pszRawKey);
1152 1797 : if (pszRawKey == nullptr)
1153 0 : continue;
1154 3594 : CPLString osKeySection;
1155 3594 : CPLString osKeyItem;
1156 1797 : char *pszDot = strchr(pszRawKey, '.');
1157 :
1158 : /* --------------------------------------------------------------------
1159 : */
1160 : /* Split stuff like BAND_P.ULLon into section and item. */
1161 : /* --------------------------------------------------------------------
1162 : */
1163 1797 : if (pszDot == nullptr)
1164 : {
1165 186 : osKeyItem = pszRawKey;
1166 : }
1167 : else
1168 : {
1169 1611 : osKeyItem = pszDot + 1;
1170 1611 : *pszDot = '\0';
1171 1611 : osKeySection = pszRawKey;
1172 : }
1173 1797 : CPLFree(pszRawKey);
1174 :
1175 : /* --------------------------------------------------------------------
1176 : */
1177 : /* Close and/or start sections as needed. */
1178 : /* --------------------------------------------------------------------
1179 : */
1180 1797 : if (!osCurSection.empty() && !EQUAL(osCurSection, osKeySection))
1181 73 : bOK &=
1182 73 : VSIFPrintfL(fp, "END_GROUP = %s\n", osCurSection.c_str()) > 0;
1183 :
1184 1797 : if (!osKeySection.empty() && !EQUAL(osCurSection, osKeySection))
1185 76 : bOK &=
1186 76 : VSIFPrintfL(fp, "BEGIN_GROUP = %s\n", osKeySection.c_str()) > 0;
1187 :
1188 1797 : osCurSection = std::move(osKeySection);
1189 :
1190 : /* --------------------------------------------------------------------
1191 : */
1192 : /* Print out simple item. */
1193 : /* --------------------------------------------------------------------
1194 : */
1195 1797 : if (!osCurSection.empty())
1196 1611 : bOK &= VSIFPrintfL(fp, "\t%s = ", osKeyItem.c_str()) > 0;
1197 : else
1198 186 : bOK &= VSIFPrintfL(fp, "%s = ", osKeyItem.c_str()) > 0;
1199 :
1200 1797 : if (pszValue[0] != '(')
1201 : {
1202 1771 : const bool bHasSingleQuote = strchr(pszValue, '\'') != nullptr;
1203 1771 : const bool bHasDoubleQuote = strchr(pszValue, '"') != nullptr;
1204 1771 : if (strchr(pszValue, ' ') != nullptr ||
1205 1584 : strchr(pszValue, ';') != nullptr ||
1206 1583 : strchr(pszValue, '\t') != nullptr || bHasSingleQuote ||
1207 273 : (bHasDoubleQuote && !(pszValue[0] == '"' &&
1208 273 : pszValue[strlen(pszValue) - 1] == '"')))
1209 : {
1210 188 : if (!bHasDoubleQuote)
1211 173 : bOK &= VSIFPrintfL(fp, "\"%s\";\n", pszValue) > 0;
1212 15 : else if (!bHasSingleQuote)
1213 8 : bOK &= VSIFPrintfL(fp, "'%s';\n", pszValue) > 0;
1214 : else
1215 : {
1216 : // There does not seem to have a way to escape double-quotes
1217 : // in a double-quoted string (or single-quote in a
1218 : // single-quoted string), so we are going to convert
1219 : // double-quotes as two single-quotes...
1220 7 : bOK &=
1221 7 : VSIFPrintfL(
1222 : fp, "\"%s\";\n",
1223 14 : CPLString(pszValue).replaceAll('"', "''").c_str()) >
1224 7 : 0;
1225 : }
1226 : }
1227 : else
1228 : {
1229 1583 : bOK &= VSIFPrintfL(fp, "%s;\n", pszValue) > 0;
1230 : }
1231 : }
1232 : else
1233 26 : GDALWriteIMDMultiLine(fp, pszValue);
1234 : }
1235 :
1236 : /* -------------------------------------------------------------------- */
1237 : /* Close off. */
1238 : /* -------------------------------------------------------------------- */
1239 20 : if (!osCurSection.empty())
1240 3 : bOK &= VSIFPrintfL(fp, "END_GROUP = %s\n", osCurSection.c_str()) > 0;
1241 :
1242 20 : bOK &= VSIFPrintfL(fp, "END;\n") > 0;
1243 :
1244 20 : if (VSIFCloseL(fp) != 0)
1245 0 : bOK = false;
1246 :
1247 20 : return bOK ? CE_None : CE_Failure;
1248 : }
|