LCOV - code coverage report
Current view: top level - frmts/ers - ershdrnode.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 172 189 91.0 %
Date: 2024-11-21 22:18:42 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ERMapper .ers Driver
       4             :  * Purpose:  Implementation of ERSHdrNode class for parsing/accessing .ers hdr.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_conv.h"
      14             : #include "cpl_string.h"
      15             : #include "ershdrnode.h"
      16             : 
      17             : /************************************************************************/
      18             : /*                             ERSHdrNode()                             */
      19             : /************************************************************************/
      20             : 
      21         284 : ERSHdrNode::ERSHdrNode()
      22             :     : nItemMax(0), nItemCount(0), papszItemName(nullptr),
      23         284 :       papszItemValue(nullptr), papoItemChild(nullptr)
      24             : {
      25         284 : }
      26             : 
      27             : /************************************************************************/
      28             : /*                            ~ERSHdrNode()                             */
      29             : /************************************************************************/
      30             : 
      31         284 : ERSHdrNode::~ERSHdrNode()
      32             : 
      33             : {
      34        1562 :     for (int i = 0; i < nItemCount; i++)
      35             :     {
      36        1278 :         if (papoItemChild[i] != nullptr)
      37         222 :             delete papoItemChild[i];
      38        1278 :         if (papszItemValue[i] != nullptr)
      39        1056 :             CPLFree(papszItemValue[i]);
      40        1278 :         CPLFree(papszItemName[i]);
      41             :     }
      42             : 
      43         284 :     CPLFree(papszItemName);
      44         284 :     CPLFree(papszItemValue);
      45         284 :     CPLFree(papoItemChild);
      46         284 : }
      47             : 
      48             : /************************************************************************/
      49             : /*                             MakeSpace()                              */
      50             : /*                                                                      */
      51             : /*      Ensure we have room for at least one more entry in our item     */
      52             : /*      lists.                                                          */
      53             : /************************************************************************/
      54             : 
      55        1278 : void ERSHdrNode::MakeSpace()
      56             : 
      57             : {
      58        1278 :     if (nItemCount == nItemMax)
      59             :     {
      60         287 :         nItemMax = (int)(nItemMax * 1.3) + 10;
      61         287 :         papszItemName =
      62         287 :             (char **)CPLRealloc(papszItemName, sizeof(char *) * nItemMax);
      63         287 :         papszItemValue =
      64         287 :             (char **)CPLRealloc(papszItemValue, sizeof(char *) * nItemMax);
      65         287 :         papoItemChild =
      66         287 :             (ERSHdrNode **)CPLRealloc(papoItemChild, sizeof(void *) * nItemMax);
      67             :     }
      68        1278 : }
      69             : 
      70             : /************************************************************************/
      71             : /*                              ReadLine()                              */
      72             : /*                                                                      */
      73             : /*      Read one virtual line from the input source.  Multiple lines    */
      74             : /*      will be appended for objects enclosed in {}.                    */
      75             : /************************************************************************/
      76             : 
      77        1128 : int ERSHdrNode::ReadLine(VSILFILE *fp, CPLString &osLine)
      78             : 
      79             : {
      80        1128 :     int nBracketLevel = 0;
      81        1128 :     bool bInQuote = false;
      82        1128 :     size_t i = 0;
      83        1128 :     bool bLastCharWasSlashInQuote = false;
      84             : 
      85        1128 :     osLine = "";
      86          19 :     do
      87             :     {
      88        1147 :         const char *pszNewLine = CPLReadLineL(fp);
      89             : 
      90        1147 :         if (pszNewLine == nullptr)
      91           0 :             return FALSE;
      92             : 
      93        1147 :         osLine += pszNewLine;
      94             : 
      95       24171 :         for (; i < osLine.length(); i++)
      96             :         {
      97       23024 :             const char ch = osLine[i];
      98       23024 :             if (bLastCharWasSlashInQuote)
      99             :             {
     100          12 :                 bLastCharWasSlashInQuote = false;
     101             :             }
     102       23012 :             else if (ch == '"')
     103         398 :                 bInQuote = !bInQuote;
     104       22614 :             else if (ch == '{' && !bInQuote)
     105          20 :                 nBracketLevel++;
     106       22594 :             else if (ch == '}' && !bInQuote)
     107          20 :                 nBracketLevel--;
     108             :             // We have to ignore escaped quotes and backslashes in strings.
     109       22574 :             else if (ch == '\\' && bInQuote)
     110             :             {
     111          12 :                 bLastCharWasSlashInQuote = true;
     112             :             }
     113             :             // A comment is a '#' up to the end of the line.
     114       22562 :             else if (ch == '#' && !bInQuote)
     115             :             {
     116          12 :                 osLine = osLine.substr(0, i) + "\n";
     117             :             }
     118             :         }
     119        1147 :     } while (nBracketLevel > 0);
     120             : 
     121        1128 :     return TRUE;
     122             : }
     123             : 
     124             : /************************************************************************/
     125             : /*                            ParseHeader()                             */
     126             : /*                                                                      */
     127             : /*      We receive the FILE * positioned at the start of the file       */
     128             : /*      and read all children.  This allows reading comment lines       */
     129             : /*      at the start of the file.                                       */
     130             : /************************************************************************/
     131             : 
     132          65 : int ERSHdrNode::ParseHeader(VSILFILE *fp)
     133             : 
     134             : {
     135             :     while (true)
     136             :     {
     137             :         /* --------------------------------------------------------------------
     138             :          */
     139             :         /*      Read the next line */
     140             :         /* --------------------------------------------------------------------
     141             :          */
     142          65 :         CPLString osLine;
     143             :         size_t iOff;
     144             : 
     145          65 :         if (!ReadLine(fp, osLine))
     146           0 :             return FALSE;
     147             : 
     148             :         /* --------------------------------------------------------------------
     149             :          */
     150             :         /*      Got a DatasetHeader Begin */
     151             :         /* --------------------------------------------------------------------
     152             :          */
     153          65 :         else if ((iOff = osLine.ifind(" Begin")) != std::string::npos)
     154             :         {
     155          62 :             CPLString osName = osLine.substr(0, iOff);
     156          62 :             osName.Trim();
     157             : 
     158          62 :             if (osName.tolower() == CPLString("DatasetHeader").tolower())
     159             :             {
     160          62 :                 return ParseChildren(fp);
     161             :             }
     162             :         }
     163           3 :     }
     164             : }
     165             : 
     166             : /************************************************************************/
     167             : /*                           ParseChildren()                            */
     168             : /*                                                                      */
     169             : /*      We receive the FILE * positioned after the "Object Begin"       */
     170             : /*      line for this object, and are responsible for reading all       */
     171             : /*      children.  We should return after consuming the                 */
     172             : /*      corresponding End line for this object.  Really the first       */
     173             : /*      unmatched End since we don't know what object we are.           */
     174             : /*                                                                      */
     175             : /*      This function is used recursively to read sub-objects.          */
     176             : /************************************************************************/
     177             : 
     178         184 : int ERSHdrNode::ParseChildren(VSILFILE *fp, int nRecLevel)
     179             : 
     180             : {
     181         184 :     if (nRecLevel == 100)  // arbitrary limit
     182             :     {
     183           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     184             :                  "Too many recursion level while parsing .ers header");
     185           0 :         return FALSE;
     186             :     }
     187             : 
     188             :     while (true)
     189             :     {
     190             :         /* --------------------------------------------------------------------
     191             :          */
     192             :         /*      Read the next line (or multi-line for bracketed value). */
     193             :         /* --------------------------------------------------------------------
     194             :          */
     195        1063 :         CPLString osLine;
     196             : 
     197        1063 :         if (!ReadLine(fp, osLine))
     198           0 :             return FALSE;
     199             : 
     200             :         /* --------------------------------------------------------------------
     201             :          */
     202             :         /*      Got a Name=Value. */
     203             :         /* --------------------------------------------------------------------
     204             :          */
     205             :         size_t iOff;
     206             : 
     207        1063 :         if ((iOff = osLine.find_first_of('=')) != std::string::npos)
     208             :         {
     209             :             CPLString osName =
     210        1496 :                 iOff == 0 ? std::string() : osLine.substr(0, iOff);
     211         748 :             osName.Trim();
     212             : 
     213         748 :             CPLString osValue = osLine.c_str() + iOff + 1;
     214         748 :             osValue.Trim();
     215             : 
     216         748 :             MakeSpace();
     217         748 :             papszItemName[nItemCount] = CPLStrdup(osName);
     218         748 :             papszItemValue[nItemCount] = CPLStrdup(osValue);
     219         748 :             papoItemChild[nItemCount] = nullptr;
     220             : 
     221         748 :             nItemCount++;
     222             :         }
     223             : 
     224             :         /* --------------------------------------------------------------------
     225             :          */
     226             :         /*      Got a Begin for an object. */
     227             :         /* --------------------------------------------------------------------
     228             :          */
     229         315 :         else if ((iOff = osLine.ifind(" Begin")) != std::string::npos)
     230             :         {
     231         122 :             CPLString osName = osLine.substr(0, iOff);
     232         122 :             osName.Trim();
     233             : 
     234         122 :             MakeSpace();
     235         122 :             papszItemName[nItemCount] = CPLStrdup(osName);
     236         122 :             papszItemValue[nItemCount] = nullptr;
     237         122 :             papoItemChild[nItemCount] = new ERSHdrNode();
     238             : 
     239         122 :             nItemCount++;
     240             : 
     241         122 :             if (!papoItemChild[nItemCount - 1]->ParseChildren(fp,
     242             :                                                               nRecLevel + 1))
     243           0 :                 return FALSE;
     244             :         }
     245             : 
     246             :         /* --------------------------------------------------------------------
     247             :          */
     248             :         /*      Got an End for our object.  Well, at least we *assume* it */
     249             :         /*      must be for our object. */
     250             :         /* --------------------------------------------------------------------
     251             :          */
     252         193 :         else if (osLine.ifind(" End") != std::string::npos)
     253             :         {
     254         184 :             return TRUE;
     255             :         }
     256             : 
     257             :         /* --------------------------------------------------------------------
     258             :          */
     259             :         /*      Error? */
     260             :         /* --------------------------------------------------------------------
     261             :          */
     262           9 :         else if (osLine.Trim().length() > 0)
     263             :         {
     264           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     265             :                      "Unexpected line parsing .ecw:\n%s", osLine.c_str());
     266           0 :             return FALSE;
     267             :         }
     268         879 :     }
     269             : }
     270             : 
     271             : /************************************************************************/
     272             : /*                             WriteSelf()                              */
     273             : /*                                                                      */
     274             : /*      Recursively write self and children to file.                    */
     275             : /************************************************************************/
     276             : 
     277         175 : int ERSHdrNode::WriteSelf(VSILFILE *fp, int nIndent)
     278             : 
     279             : {
     280         350 :     CPLString oIndent;
     281             : 
     282         175 :     oIndent.assign(nIndent, '\t');
     283             : 
     284         959 :     for (int i = 0; i < nItemCount; i++)
     285             :     {
     286         784 :         if (papszItemValue[i] != nullptr)
     287             :         {
     288         646 :             if (VSIFPrintfL(fp, "%s%s\t= %s\n", oIndent.c_str(),
     289        1292 :                             papszItemName[i], papszItemValue[i]) < 1)
     290           0 :                 return FALSE;
     291             :         }
     292             :         else
     293             :         {
     294         138 :             VSIFPrintfL(fp, "%s%s Begin\n", oIndent.c_str(), papszItemName[i]);
     295         138 :             if (!papoItemChild[i]->WriteSelf(fp, nIndent + 1))
     296           0 :                 return FALSE;
     297         138 :             if (VSIFPrintfL(fp, "%s%s End\n", oIndent.c_str(),
     298         276 :                             papszItemName[i]) < 1)
     299           0 :                 return FALSE;
     300             :         }
     301             :     }
     302             : 
     303         175 :     return TRUE;
     304             : }
     305             : 
     306             : /************************************************************************/
     307             : /*                                Find()                                */
     308             : /*                                                                      */
     309             : /*      Find the desired entry value.  The input is a path with         */
     310             : /*      components separated by dots, relative to the current node.     */
     311             : /************************************************************************/
     312             : 
     313        2393 : const char *ERSHdrNode::Find(const char *pszPath, const char *pszDefault)
     314             : 
     315             : {
     316             :     /* -------------------------------------------------------------------- */
     317             :     /*      If this is the final component of the path, search for a        */
     318             :     /*      matching child and return the value.                            */
     319             :     /* -------------------------------------------------------------------- */
     320        2393 :     if (strchr(pszPath, '.') == nullptr)
     321             :     {
     322        4573 :         for (int i = 0; i < nItemCount; i++)
     323             :         {
     324        4201 :             if (EQUAL(pszPath, papszItemName[i]))
     325             :             {
     326         785 :                 if (papszItemValue[i] != nullptr)
     327             :                 {
     328         785 :                     if (papszItemValue[i][0] == '"')
     329             :                     {
     330             :                         // strip off quotes.
     331          67 :                         osTempReturn = papszItemValue[i];
     332          67 :                         if (osTempReturn.length() < 2)
     333           0 :                             osTempReturn.clear();
     334             :                         else
     335         134 :                             osTempReturn = osTempReturn.substr(
     336         134 :                                 1, osTempReturn.length() - 2);
     337          67 :                         return osTempReturn;
     338             :                     }
     339             :                     else
     340         718 :                         return papszItemValue[i];
     341             :                 }
     342             :                 else
     343           0 :                     return pszDefault;
     344             :             }
     345             :         }
     346         372 :         return pszDefault;
     347             :     }
     348             : 
     349             :     /* -------------------------------------------------------------------- */
     350             :     /*      This is a dot path - extract the first element, find a match    */
     351             :     /*      and recurse.                                                    */
     352             :     /* -------------------------------------------------------------------- */
     353        2472 :     CPLString osPathFirst, osPathRest, osPath = pszPath;
     354             : 
     355        1236 :     size_t iDot = osPath.find_first_of('.');
     356        1236 :     osPathFirst = osPath.substr(0, iDot);
     357        1236 :     osPathRest = osPath.substr(iDot + 1);
     358             : 
     359        7785 :     for (int i = 0; i < nItemCount; i++)
     360             :     {
     361        7550 :         if (EQUAL(osPathFirst, papszItemName[i]))
     362             :         {
     363        1001 :             if (papoItemChild[i] != nullptr)
     364        1001 :                 return papoItemChild[i]->Find(osPathRest, pszDefault);
     365             : 
     366           0 :             return pszDefault;
     367             :         }
     368             :     }
     369             : 
     370         235 :     return pszDefault;
     371             : }
     372             : 
     373             : /************************************************************************/
     374             : /*                              FindElem()                              */
     375             : /*                                                                      */
     376             : /*      Find a particular element from an array valued item.            */
     377             : /************************************************************************/
     378             : 
     379          12 : const char *ERSHdrNode::FindElem(const char *pszPath, int iElem,
     380             :                                  const char *pszDefault)
     381             : 
     382             : {
     383          12 :     const char *pszArray = Find(pszPath, nullptr);
     384             : 
     385          12 :     if (pszArray == nullptr)
     386           0 :         return pszDefault;
     387             : 
     388          12 :     bool bDefault = true;
     389             :     char **papszTokens =
     390          12 :         CSLTokenizeStringComplex(pszArray, "{ \t}", TRUE, FALSE);
     391          12 :     if (iElem >= 0 && iElem < CSLCount(papszTokens))
     392             :     {
     393          12 :         osTempReturn = papszTokens[iElem];
     394          12 :         bDefault = false;
     395             :     }
     396             : 
     397          12 :     CSLDestroy(papszTokens);
     398             : 
     399          12 :     if (bDefault)
     400           0 :         return pszDefault;
     401             : 
     402          12 :     return osTempReturn;
     403             : }
     404             : 
     405             : /************************************************************************/
     406             : /*                              FindNode()                              */
     407             : /*                                                                      */
     408             : /*      Find the desired node.                                          */
     409             : /************************************************************************/
     410             : 
     411         646 : ERSHdrNode *ERSHdrNode::FindNode(const char *pszPath)
     412             : 
     413             : {
     414        1292 :     std::string osPathFirst, osPathRest;
     415        1292 :     const std::string osPath = pszPath;
     416         646 :     size_t iDot = osPath.find_first_of('.');
     417         646 :     if (iDot == std::string::npos)
     418             :     {
     419         585 :         osPathFirst = osPath;
     420             :     }
     421             :     else
     422             :     {
     423          61 :         osPathFirst = osPath.substr(0, iDot);
     424          61 :         osPathRest = osPath.substr(iDot + 1);
     425             :     }
     426             : 
     427        3995 :     for (int i = 0; i < nItemCount; i++)
     428             :     {
     429        3836 :         if (EQUAL(osPathFirst.c_str(), papszItemName[i]))
     430             :         {
     431         487 :             if (papoItemChild[i] != nullptr)
     432             :             {
     433         487 :                 if (osPathRest.length() > 0)
     434          61 :                     return papoItemChild[i]->FindNode(osPathRest.c_str());
     435             :                 else
     436         426 :                     return papoItemChild[i];
     437             :             }
     438             :             else
     439           0 :                 return nullptr;
     440             :         }
     441             :     }
     442             : 
     443         159 :     return nullptr;
     444             : }
     445             : 
     446             : /************************************************************************/
     447             : /*                                Set()                                 */
     448             : /*                                                                      */
     449             : /*      Set a value item.                                               */
     450             : /************************************************************************/
     451             : 
     452         778 : void ERSHdrNode::Set(const char *pszPath, const char *pszValue)
     453             : 
     454             : {
     455         778 :     CPLString osPath = pszPath;
     456         778 :     size_t iDot = osPath.find_first_of('.');
     457             : 
     458             :     /* -------------------------------------------------------------------- */
     459             :     /*      We have an intermediate node, find or create it and             */
     460             :     /*      recurse.                                                        */
     461             :     /* -------------------------------------------------------------------- */
     462         778 :     if (iDot != std::string::npos)
     463             :     {
     464         920 :         CPLString osPathFirst = osPath.substr(0, iDot);
     465         460 :         CPLString osPathRest = osPath.substr(iDot + 1);
     466         460 :         ERSHdrNode *poFirst = FindNode(osPathFirst);
     467             : 
     468         460 :         if (poFirst == nullptr)
     469             :         {
     470         100 :             poFirst = new ERSHdrNode();
     471             : 
     472         100 :             MakeSpace();
     473         100 :             papszItemName[nItemCount] = CPLStrdup(osPathFirst);
     474         100 :             papszItemValue[nItemCount] = nullptr;
     475         100 :             papoItemChild[nItemCount] = poFirst;
     476         100 :             nItemCount++;
     477             :         }
     478             : 
     479         460 :         poFirst->Set(osPathRest, pszValue);
     480         460 :         return;
     481             :     }
     482             : 
     483             :     /* -------------------------------------------------------------------- */
     484             :     /*      This is the final item name.  Find or create it.                */
     485             :     /* -------------------------------------------------------------------- */
     486         763 :     for (int i = 0; i < nItemCount; i++)
     487             :     {
     488         455 :         if (EQUAL(osPath, papszItemName[i]) && papszItemValue[i] != nullptr)
     489             :         {
     490          10 :             CPLFree(papszItemValue[i]);
     491          10 :             papszItemValue[i] = CPLStrdup(pszValue);
     492          10 :             return;
     493             :         }
     494             :     }
     495             : 
     496         308 :     MakeSpace();
     497         308 :     papszItemName[nItemCount] = CPLStrdup(osPath);
     498         308 :     papszItemValue[nItemCount] = CPLStrdup(pszValue);
     499         308 :     papoItemChild[nItemCount] = nullptr;
     500         308 :     nItemCount++;
     501             : }

Generated by: LCOV version 1.14