LCOV - code coverage report
Current view: top level - port - cplkeywordparser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 130 161 80.7 %
Date: 2024-11-21 22:18:42 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Common Portability Library
       4             :  * Purpose:  Implementation of CPLKeywordParser - a class for parsing
       5             :  *           the keyword format used for files like QuickBird .RPB files.
       6             :  *           This is a slight variation on the NASAKeywordParser used for
       7             :  *           the PDS/ISIS2/ISIS3 formats.
       8             :  * Author:   Frank Warmerdam <warmerdam@pobox.com
       9             :  *
      10             :  ******************************************************************************
      11             :  * Copyright (c) 2008, Frank Warmerdam <warmerdam@pobox.com>
      12             :  * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com>
      13             :  *
      14             :  * SPDX-License-Identifier: MIT
      15             :  ****************************************************************************/
      16             : 
      17             : //! @cond Doxygen_Suppress
      18             : 
      19             : #include "cpl_port.h"
      20             : #include "cplkeywordparser.h"
      21             : 
      22             : #include <cctype>
      23             : #include <cstring>
      24             : #include <string>
      25             : 
      26             : #include "cpl_string.h"
      27             : #include "cpl_vsi.h"
      28             : 
      29             : /************************************************************************/
      30             : /* ==================================================================== */
      31             : /*                          CPLKeywordParser                           */
      32             : /* ==================================================================== */
      33             : /************************************************************************/
      34             : 
      35             : /************************************************************************/
      36             : /*                         CPLKeywordParser()                          */
      37             : /************************************************************************/
      38             : 
      39             : CPLKeywordParser::CPLKeywordParser() = default;
      40             : 
      41             : /************************************************************************/
      42             : /*                        ~CPLKeywordParser()                          */
      43             : /************************************************************************/
      44             : 
      45         159 : CPLKeywordParser::~CPLKeywordParser()
      46             : 
      47             : {
      48          53 :     CSLDestroy(papszKeywordList);
      49          53 :     papszKeywordList = nullptr;
      50          53 : }
      51             : 
      52             : /************************************************************************/
      53             : /*                               Ingest()                               */
      54             : /************************************************************************/
      55             : 
      56         212 : int CPLKeywordParser::Ingest(VSILFILE *fp)
      57             : 
      58             : {
      59             :     /* -------------------------------------------------------------------- */
      60             :     /*      Read in buffer till we find END all on its own line.            */
      61             :     /* -------------------------------------------------------------------- */
      62             :     for (; true;)
      63             :     {
      64         212 :         char szChunk[513] = {};
      65         212 :         const size_t nBytesRead = VSIFReadL(szChunk, 1, 512, fp);
      66             : 
      67         212 :         szChunk[nBytesRead] = '\0';
      68         212 :         osHeaderText += szChunk;
      69             : 
      70         212 :         if (nBytesRead < 512)
      71          53 :             break;
      72             : 
      73         159 :         const char *pszCheck = nullptr;
      74         159 :         if (osHeaderText.size() > 520)
      75         117 :             pszCheck = osHeaderText.c_str() + (osHeaderText.size() - 520);
      76             :         else
      77          42 :             pszCheck = szChunk;
      78             : 
      79         159 :         if (strstr(pszCheck, "\r\nEND;\r\n") != nullptr ||
      80         159 :             strstr(pszCheck, "\nEND;\n") != nullptr)
      81             :             break;
      82         159 :     }
      83             : 
      84          53 :     pszHeaderNext = osHeaderText.c_str();
      85             : 
      86             :     /* -------------------------------------------------------------------- */
      87             :     /*      Process name/value pairs, keeping track of a "path stack".      */
      88             :     /* -------------------------------------------------------------------- */
      89          53 :     return ReadGroup("", 0);
      90             : }
      91             : 
      92             : /************************************************************************/
      93             : /*                             ReadGroup()                              */
      94             : /************************************************************************/
      95             : 
      96         161 : bool CPLKeywordParser::ReadGroup(const char *pszPathPrefix, int nRecLevel)
      97             : 
      98             : {
      99         322 :     CPLString osName;
     100         322 :     CPLString osValue;
     101             : 
     102             :     // Arbitrary threshold to avoid stack overflow
     103         161 :     if (nRecLevel == 100)
     104           0 :         return false;
     105             : 
     106             :     for (; true;)
     107             :     {
     108        2749 :         if (!ReadPair(osName, osValue))
     109           4 :             return false;
     110             : 
     111        2745 :         if (EQUAL(osName, "BEGIN_GROUP") || EQUAL(osName, "GROUP"))
     112             :         {
     113         108 :             if (!ReadGroup((CPLString(pszPathPrefix) + osValue + ".").c_str(),
     114             :                            nRecLevel + 1))
     115           4 :                 return false;
     116             :         }
     117        2637 :         else if (STARTS_WITH_CI(osName, "END"))
     118             :         {
     119         153 :             return true;
     120             :         }
     121             :         else
     122             :         {
     123        2484 :             osName = pszPathPrefix + osName;
     124        2484 :             papszKeywordList =
     125        2484 :                 CSLSetNameValue(papszKeywordList, osName, osValue);
     126             :         }
     127             :     }
     128             : }
     129             : 
     130             : /************************************************************************/
     131             : /*                              ReadPair()                              */
     132             : /*                                                                      */
     133             : /*      Read a name/value pair from the input stream.  Strip off        */
     134             : /*      white space, ignore comments, split on '='.                     */
     135             : /************************************************************************/
     136             : 
     137        2749 : bool CPLKeywordParser::ReadPair(CPLString &osName, CPLString &osValue)
     138             : 
     139             : {
     140        2749 :     osName = "";
     141        2749 :     osValue = "";
     142             : 
     143        2749 :     if (!ReadWord(osName))
     144           0 :         return false;
     145             : 
     146        2749 :     SkipWhite();
     147             : 
     148        2749 :     if (EQUAL(osName, "END"))
     149          49 :         return TRUE;
     150             : 
     151        2700 :     if (*pszHeaderNext != '=')
     152             :     {
     153             :         // ISIS3 does not have anything after the end group/object keyword.
     154           5 :         return EQUAL(osName, "End_Group") || EQUAL(osName, "End_Object");
     155             :     }
     156             : 
     157        2695 :     pszHeaderNext++;
     158             : 
     159        2695 :     SkipWhite();
     160             : 
     161        2695 :     osValue = "";
     162             : 
     163             :     // Handle value lists like:     Name   = (Red, Red)
     164             :     // or list of lists like: TLCList = ( (0, 0.000000), (8299, 4.811014) );
     165        2695 :     if (*pszHeaderNext == '(')
     166             :     {
     167         184 :         CPLString osWord;
     168          92 :         int nDepth = 0;
     169          92 :         const char *pszLastPos = pszHeaderNext;
     170             : 
     171        1456 :         while (ReadWord(osWord) && pszLastPos != pszHeaderNext)
     172             :         {
     173        1456 :             SkipWhite();
     174        1456 :             pszLastPos = pszHeaderNext;
     175             : 
     176        1456 :             osValue += osWord;
     177        1456 :             const char *pszIter = osWord.c_str();
     178        1456 :             bool bInQuote = false;
     179       18799 :             while (*pszIter != '\0')
     180             :             {
     181       17435 :                 if (*pszIter == '"')
     182           0 :                     bInQuote = !bInQuote;
     183       17435 :                 else if (!bInQuote)
     184             :                 {
     185       17435 :                     if (*pszIter == '(')
     186          92 :                         nDepth++;
     187       17343 :                     else if (*pszIter == ')')
     188             :                     {
     189          92 :                         nDepth--;
     190          92 :                         if (nDepth == 0)
     191          92 :                             break;
     192             :                     }
     193             :                 }
     194       17343 :                 pszIter++;
     195             :             }
     196        1456 :             if (*pszIter == ')' && nDepth == 0)
     197          92 :                 break;
     198             :         }
     199             :     }
     200             : 
     201             :     else  // Handle more normal "single word" values.
     202             :     {
     203             :         // Special case to handle non-conformant IMD files generated by
     204             :         // previous GDAL version where we omit to surround values that have
     205             :         // spaces with double quotes.
     206             :         // So we use a heuristics to handle things like:
     207             :         //       key = value with spaces without single or double quotes at
     208             :         //       beginning of value;[\r]\n
     209        2603 :         const char *pszNextLF = strchr(pszHeaderNext, '\n');
     210        2603 :         if (pszNextLF)
     211             :         {
     212        2603 :             std::string osTxt(pszHeaderNext, pszNextLF - pszHeaderNext);
     213        2603 :             const auto nCRPos = osTxt.find('\r');
     214        2603 :             const auto nSemiColonPos = osTxt.find(';');
     215        2603 :             const auto nQuotePos = osTxt.find('\'');
     216        2603 :             const auto nDoubleQuotePos = osTxt.find('"');
     217        2603 :             const auto nLTPos = osTxt.find('<');
     218        2354 :             if (nSemiColonPos != std::string::npos &&
     219        2354 :                 (nCRPos == std::string::npos || (nCRPos + 1 == osTxt.size())) &&
     220           1 :                 ((nCRPos != std::string::npos &&
     221        2354 :                   (nSemiColonPos + 1 == nCRPos)) ||
     222        2353 :                  (nCRPos == std::string::npos &&
     223        4704 :                   (nSemiColonPos + 1 == osTxt.size()))) &&
     224        2351 :                 (nQuotePos == std::string::npos || nQuotePos != 0) &&
     225         595 :                 (nDoubleQuotePos == std::string::npos ||
     226        4957 :                  nDoubleQuotePos != 0) &&
     227           0 :                 (nLTPos == std::string::npos ||
     228           0 :                  osTxt.find('>') == std::string::npos))
     229             :             {
     230        1741 :                 pszHeaderNext = pszNextLF;
     231        1741 :                 osTxt.resize(nSemiColonPos);
     232        1741 :                 osValue = osTxt;
     233        1742 :                 while (!osValue.empty() && osValue.back() == ' ')
     234           1 :                     osValue.pop_back();
     235        1741 :                 return true;
     236             :             }
     237             :         }
     238             : 
     239         862 :         if (!ReadWord(osValue))
     240           0 :             return false;
     241             :     }
     242             : 
     243         954 :     SkipWhite();
     244             : 
     245             :     // No units keyword?
     246         954 :     if (*pszHeaderNext != '<')
     247         954 :         return true;
     248             : 
     249             :     // Append units keyword.  For lines that like like this:
     250             :     //  MAP_RESOLUTION               = 4.0 <PIXEL/DEGREE>
     251             : 
     252           0 :     CPLString osWord;
     253             : 
     254           0 :     osValue += " ";
     255             : 
     256           0 :     while (ReadWord(osWord))
     257             :     {
     258           0 :         SkipWhite();
     259             : 
     260           0 :         osValue += osWord;
     261           0 :         if (osWord.back() == '>')
     262           0 :             break;
     263             :     }
     264             : 
     265           0 :     return true;
     266             : }
     267             : 
     268             : /************************************************************************/
     269             : /*                              ReadWord()                              */
     270             : /************************************************************************/
     271             : 
     272        5067 : bool CPLKeywordParser::ReadWord(CPLString &osWord)
     273             : 
     274             : {
     275        5067 :     osWord = "";
     276             : 
     277        5067 :     SkipWhite();
     278             : 
     279        5067 :     if (*pszHeaderNext == '\0' || *pszHeaderNext == '=')
     280           0 :         return false;
     281             : 
     282       98965 :     while (*pszHeaderNext != '\0' && *pszHeaderNext != '=' &&
     283      104032 :            *pszHeaderNext != ';' &&
     284       51264 :            !isspace(static_cast<unsigned char>(*pszHeaderNext)))
     285             :     {
     286       46949 :         if (*pszHeaderNext == '"')
     287             :         {
     288         608 :             osWord += *(pszHeaderNext++);
     289        4152 :             while (*pszHeaderNext != '"')
     290             :             {
     291        3544 :                 if (*pszHeaderNext == '\0')
     292           0 :                     return false;
     293             : 
     294        3544 :                 osWord += *(pszHeaderNext++);
     295             :             }
     296         608 :             osWord += *(pszHeaderNext++);
     297             :         }
     298       46341 :         else if (*pszHeaderNext == '\'')
     299             :         {
     300          15 :             osWord += *(pszHeaderNext++);
     301         264 :             while (*pszHeaderNext != '\'')
     302             :             {
     303         249 :                 if (*pszHeaderNext == '\0')
     304           0 :                     return false;
     305             : 
     306         249 :                 osWord += *(pszHeaderNext++);
     307             :             }
     308          15 :             osWord += *(pszHeaderNext++);
     309             :         }
     310             :         else
     311             :         {
     312       46326 :             osWord += *pszHeaderNext;
     313       46326 :             pszHeaderNext++;
     314             :         }
     315             :     }
     316             : 
     317        5067 :     if (*pszHeaderNext == ';')
     318         752 :         pszHeaderNext++;
     319             : 
     320        5067 :     return true;
     321             : }
     322             : 
     323             : /************************************************************************/
     324             : /*                             SkipWhite()                              */
     325             : /************************************************************************/
     326             : 
     327       29395 : void CPLKeywordParser::SkipWhite()
     328             : 
     329             : {
     330             :     for (; true;)
     331             :     {
     332             :         // Skip white space (newline, space, tab, etc )
     333       29395 :         if (isspace(static_cast<unsigned char>(*pszHeaderNext)))
     334             :         {
     335       16474 :             pszHeaderNext++;
     336       16474 :             continue;
     337             :         }
     338             : 
     339             :         // Skip C style comments
     340       12921 :         if (*pszHeaderNext == '/' && pszHeaderNext[1] == '*')
     341             :         {
     342           0 :             pszHeaderNext += 2;
     343             : 
     344           0 :             while (*pszHeaderNext != '\0' &&
     345           0 :                    (*pszHeaderNext != '*' || pszHeaderNext[1] != '/'))
     346             :             {
     347           0 :                 pszHeaderNext++;
     348             :             }
     349           0 :             if (*pszHeaderNext == '\0')
     350           0 :                 break;
     351             : 
     352           0 :             pszHeaderNext += 2;
     353           0 :             continue;
     354             :         }
     355             : 
     356             :         // Skip # style comments
     357       12921 :         if (*pszHeaderNext == '#')
     358             :         {
     359           0 :             pszHeaderNext += 1;
     360             : 
     361             :             // consume till end of line.
     362           0 :             while (*pszHeaderNext != '\0' && *pszHeaderNext != 10 &&
     363           0 :                    *pszHeaderNext != 13)
     364             :             {
     365           0 :                 pszHeaderNext++;
     366             :             }
     367           0 :             continue;
     368             :         }
     369             : 
     370             :         // not white space, return.
     371       12921 :         return;
     372             :     }
     373             : }
     374             : 
     375             : /************************************************************************/
     376             : /*                             GetKeyword()                             */
     377             : /************************************************************************/
     378             : 
     379         160 : const char *CPLKeywordParser::GetKeyword(const char *pszPath,
     380             :                                          const char *pszDefault)
     381             : 
     382             : {
     383         160 :     const char *pszResult = CSLFetchNameValue(papszKeywordList, pszPath);
     384         160 :     if (pszResult == nullptr)
     385           0 :         return pszDefault;
     386             : 
     387         160 :     return pszResult;
     388             : }
     389             : 
     390             : //! @endcond

Generated by: LCOV version 1.14