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

Generated by: LCOV version 1.14