LCOV - code coverage report
Current view: top level - gcore - gdal_mdreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 367 489 75.1 %
Date: 2024-11-21 22:18:42 Functions: 21 25 84.0 %

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

Generated by: LCOV version 1.14