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

Generated by: LCOV version 1.14