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-05-04 12:52:34 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             :  * 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             : }

Generated by: LCOV version 1.14