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-04-29 01:40:10 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      15             :  * copy of this software and associated documentation files (the "Software"),
      16             :  * to deal in the Software without restriction, including without limitation
      17             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      18             :  * and/or sell copies of the Software, and to permit persons to whom the
      19             :  * Software is furnished to do so, subject to the following conditions:
      20             :  *
      21             :  * The above copyright notice and this permission notice shall be included
      22             :  * in all copies or substantial portions of the Software.
      23             :  *
      24             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      25             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      26             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      27             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      28             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      29             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      30             :  * DEALINGS IN THE SOFTWARE.
      31             :  ****************************************************************************/
      32             : 
      33             : //! @cond Doxygen_Suppress
      34             : 
      35             : #include "cpl_port.h"
      36             : #include "cplkeywordparser.h"
      37             : 
      38             : #include <cctype>
      39             : #include <cstring>
      40             : #include <string>
      41             : 
      42             : #include "cpl_string.h"
      43             : #include "cpl_vsi.h"
      44             : 
      45             : /************************************************************************/
      46             : /* ==================================================================== */
      47             : /*                          CPLKeywordParser                           */
      48             : /* ==================================================================== */
      49             : /************************************************************************/
      50             : 
      51             : /************************************************************************/
      52             : /*                         CPLKeywordParser()                          */
      53             : /************************************************************************/
      54             : 
      55             : CPLKeywordParser::CPLKeywordParser() = default;
      56             : 
      57             : /************************************************************************/
      58             : /*                        ~CPLKeywordParser()                          */
      59             : /************************************************************************/
      60             : 
      61         159 : CPLKeywordParser::~CPLKeywordParser()
      62             : 
      63             : {
      64          53 :     CSLDestroy(papszKeywordList);
      65          53 :     papszKeywordList = nullptr;
      66          53 : }
      67             : 
      68             : /************************************************************************/
      69             : /*                               Ingest()                               */
      70             : /************************************************************************/
      71             : 
      72         212 : int CPLKeywordParser::Ingest(VSILFILE *fp)
      73             : 
      74             : {
      75             :     /* -------------------------------------------------------------------- */
      76             :     /*      Read in buffer till we find END all on its own line.            */
      77             :     /* -------------------------------------------------------------------- */
      78             :     for (; true;)
      79             :     {
      80         212 :         char szChunk[513] = {};
      81         212 :         const size_t nBytesRead = VSIFReadL(szChunk, 1, 512, fp);
      82             : 
      83         212 :         szChunk[nBytesRead] = '\0';
      84         212 :         osHeaderText += szChunk;
      85             : 
      86         212 :         if (nBytesRead < 512)
      87          53 :             break;
      88             : 
      89         159 :         const char *pszCheck = nullptr;
      90         159 :         if (osHeaderText.size() > 520)
      91         117 :             pszCheck = osHeaderText.c_str() + (osHeaderText.size() - 520);
      92             :         else
      93          42 :             pszCheck = szChunk;
      94             : 
      95         159 :         if (strstr(pszCheck, "\r\nEND;\r\n") != nullptr ||
      96         159 :             strstr(pszCheck, "\nEND;\n") != nullptr)
      97             :             break;
      98         159 :     }
      99             : 
     100          53 :     pszHeaderNext = osHeaderText.c_str();
     101             : 
     102             :     /* -------------------------------------------------------------------- */
     103             :     /*      Process name/value pairs, keeping track of a "path stack".      */
     104             :     /* -------------------------------------------------------------------- */
     105          53 :     return ReadGroup("", 0);
     106             : }
     107             : 
     108             : /************************************************************************/
     109             : /*                             ReadGroup()                              */
     110             : /************************************************************************/
     111             : 
     112         161 : bool CPLKeywordParser::ReadGroup(const char *pszPathPrefix, int nRecLevel)
     113             : 
     114             : {
     115         322 :     CPLString osName;
     116         322 :     CPLString osValue;
     117             : 
     118             :     // Arbitrary threshold to avoid stack overflow
     119         161 :     if (nRecLevel == 100)
     120           0 :         return false;
     121             : 
     122             :     for (; true;)
     123             :     {
     124        2749 :         if (!ReadPair(osName, osValue))
     125           4 :             return false;
     126             : 
     127        2745 :         if (EQUAL(osName, "BEGIN_GROUP") || EQUAL(osName, "GROUP"))
     128             :         {
     129         108 :             if (!ReadGroup((CPLString(pszPathPrefix) + osValue + ".").c_str(),
     130             :                            nRecLevel + 1))
     131           4 :                 return false;
     132             :         }
     133        2637 :         else if (STARTS_WITH_CI(osName, "END"))
     134             :         {
     135         153 :             return true;
     136             :         }
     137             :         else
     138             :         {
     139        2484 :             osName = pszPathPrefix + osName;
     140        2484 :             papszKeywordList =
     141        2484 :                 CSLSetNameValue(papszKeywordList, osName, osValue);
     142             :         }
     143             :     }
     144             : }
     145             : 
     146             : /************************************************************************/
     147             : /*                              ReadPair()                              */
     148             : /*                                                                      */
     149             : /*      Read a name/value pair from the input stream.  Strip off        */
     150             : /*      white space, ignore comments, split on '='.                     */
     151             : /************************************************************************/
     152             : 
     153        2749 : bool CPLKeywordParser::ReadPair(CPLString &osName, CPLString &osValue)
     154             : 
     155             : {
     156        2749 :     osName = "";
     157        2749 :     osValue = "";
     158             : 
     159        2749 :     if (!ReadWord(osName))
     160           0 :         return false;
     161             : 
     162        2749 :     SkipWhite();
     163             : 
     164        2749 :     if (EQUAL(osName, "END"))
     165          49 :         return TRUE;
     166             : 
     167        2700 :     if (*pszHeaderNext != '=')
     168             :     {
     169             :         // ISIS3 does not have anything after the end group/object keyword.
     170           5 :         return EQUAL(osName, "End_Group") || EQUAL(osName, "End_Object");
     171             :     }
     172             : 
     173        2695 :     pszHeaderNext++;
     174             : 
     175        2695 :     SkipWhite();
     176             : 
     177        2695 :     osValue = "";
     178             : 
     179             :     // Handle value lists like:     Name   = (Red, Red)
     180             :     // or list of lists like: TLCList = ( (0, 0.000000), (8299, 4.811014) );
     181        2695 :     if (*pszHeaderNext == '(')
     182             :     {
     183         184 :         CPLString osWord;
     184          92 :         int nDepth = 0;
     185          92 :         const char *pszLastPos = pszHeaderNext;
     186             : 
     187        1456 :         while (ReadWord(osWord) && pszLastPos != pszHeaderNext)
     188             :         {
     189        1456 :             SkipWhite();
     190        1456 :             pszLastPos = pszHeaderNext;
     191             : 
     192        1456 :             osValue += osWord;
     193        1456 :             const char *pszIter = osWord.c_str();
     194        1456 :             bool bInQuote = false;
     195       18799 :             while (*pszIter != '\0')
     196             :             {
     197       17435 :                 if (*pszIter == '"')
     198           0 :                     bInQuote = !bInQuote;
     199       17435 :                 else if (!bInQuote)
     200             :                 {
     201       17435 :                     if (*pszIter == '(')
     202          92 :                         nDepth++;
     203       17343 :                     else if (*pszIter == ')')
     204             :                     {
     205          92 :                         nDepth--;
     206          92 :                         if (nDepth == 0)
     207          92 :                             break;
     208             :                     }
     209             :                 }
     210       17343 :                 pszIter++;
     211             :             }
     212        1456 :             if (*pszIter == ')' && nDepth == 0)
     213          92 :                 break;
     214             :         }
     215             :     }
     216             : 
     217             :     else  // Handle more normal "single word" values.
     218             :     {
     219             :         // Special case to handle non-conformant IMD files generated by
     220             :         // previous GDAL version where we omit to surround values that have
     221             :         // spaces with double quotes.
     222             :         // So we use a heuristics to handle things like:
     223             :         //       key = value with spaces without single or double quotes at
     224             :         //       beginning of value;[\r]\n
     225        2603 :         const char *pszNextLF = strchr(pszHeaderNext, '\n');
     226        2603 :         if (pszNextLF)
     227             :         {
     228        2603 :             std::string osTxt(pszHeaderNext, pszNextLF - pszHeaderNext);
     229        2603 :             const auto nCRPos = osTxt.find('\r');
     230        2603 :             const auto nSemiColonPos = osTxt.find(';');
     231        2603 :             const auto nQuotePos = osTxt.find('\'');
     232        2603 :             const auto nDoubleQuotePos = osTxt.find('"');
     233        2603 :             const auto nLTPos = osTxt.find('<');
     234        2354 :             if (nSemiColonPos != std::string::npos &&
     235        2354 :                 (nCRPos == std::string::npos || (nCRPos + 1 == osTxt.size())) &&
     236           1 :                 ((nCRPos != std::string::npos &&
     237        2354 :                   (nSemiColonPos + 1 == nCRPos)) ||
     238        2353 :                  (nCRPos == std::string::npos &&
     239        4704 :                   (nSemiColonPos + 1 == osTxt.size()))) &&
     240        2351 :                 (nQuotePos == std::string::npos || nQuotePos != 0) &&
     241         595 :                 (nDoubleQuotePos == std::string::npos ||
     242        4957 :                  nDoubleQuotePos != 0) &&
     243           0 :                 (nLTPos == std::string::npos ||
     244           0 :                  osTxt.find('>') == std::string::npos))
     245             :             {
     246        1741 :                 pszHeaderNext = pszNextLF;
     247        1741 :                 osTxt.resize(nSemiColonPos);
     248        1741 :                 osValue = osTxt;
     249        1742 :                 while (!osValue.empty() && osValue.back() == ' ')
     250           1 :                     osValue.resize(osValue.size() - 1);
     251        1741 :                 return true;
     252             :             }
     253             :         }
     254             : 
     255         862 :         if (!ReadWord(osValue))
     256           0 :             return false;
     257             :     }
     258             : 
     259         954 :     SkipWhite();
     260             : 
     261             :     // No units keyword?
     262         954 :     if (*pszHeaderNext != '<')
     263         954 :         return true;
     264             : 
     265             :     // Append units keyword.  For lines that like like this:
     266             :     //  MAP_RESOLUTION               = 4.0 <PIXEL/DEGREE>
     267             : 
     268           0 :     CPLString osWord;
     269             : 
     270           0 :     osValue += " ";
     271             : 
     272           0 :     while (ReadWord(osWord))
     273             :     {
     274           0 :         SkipWhite();
     275             : 
     276           0 :         osValue += osWord;
     277           0 :         if (osWord.back() == '>')
     278           0 :             break;
     279             :     }
     280             : 
     281           0 :     return true;
     282             : }
     283             : 
     284             : /************************************************************************/
     285             : /*                              ReadWord()                              */
     286             : /************************************************************************/
     287             : 
     288        5067 : bool CPLKeywordParser::ReadWord(CPLString &osWord)
     289             : 
     290             : {
     291        5067 :     osWord = "";
     292             : 
     293        5067 :     SkipWhite();
     294             : 
     295        5067 :     if (*pszHeaderNext == '\0' || *pszHeaderNext == '=')
     296           0 :         return false;
     297             : 
     298       98965 :     while (*pszHeaderNext != '\0' && *pszHeaderNext != '=' &&
     299      104032 :            *pszHeaderNext != ';' &&
     300       51264 :            !isspace(static_cast<unsigned char>(*pszHeaderNext)))
     301             :     {
     302       46949 :         if (*pszHeaderNext == '"')
     303             :         {
     304         608 :             osWord += *(pszHeaderNext++);
     305        4152 :             while (*pszHeaderNext != '"')
     306             :             {
     307        3544 :                 if (*pszHeaderNext == '\0')
     308           0 :                     return false;
     309             : 
     310        3544 :                 osWord += *(pszHeaderNext++);
     311             :             }
     312         608 :             osWord += *(pszHeaderNext++);
     313             :         }
     314       46341 :         else if (*pszHeaderNext == '\'')
     315             :         {
     316          15 :             osWord += *(pszHeaderNext++);
     317         264 :             while (*pszHeaderNext != '\'')
     318             :             {
     319         249 :                 if (*pszHeaderNext == '\0')
     320           0 :                     return false;
     321             : 
     322         249 :                 osWord += *(pszHeaderNext++);
     323             :             }
     324          15 :             osWord += *(pszHeaderNext++);
     325             :         }
     326             :         else
     327             :         {
     328       46326 :             osWord += *pszHeaderNext;
     329       46326 :             pszHeaderNext++;
     330             :         }
     331             :     }
     332             : 
     333        5067 :     if (*pszHeaderNext == ';')
     334         752 :         pszHeaderNext++;
     335             : 
     336        5067 :     return true;
     337             : }
     338             : 
     339             : /************************************************************************/
     340             : /*                             SkipWhite()                              */
     341             : /************************************************************************/
     342             : 
     343       29395 : void CPLKeywordParser::SkipWhite()
     344             : 
     345             : {
     346             :     for (; true;)
     347             :     {
     348             :         // Skip white space (newline, space, tab, etc )
     349       29395 :         if (isspace(static_cast<unsigned char>(*pszHeaderNext)))
     350             :         {
     351       16474 :             pszHeaderNext++;
     352       16474 :             continue;
     353             :         }
     354             : 
     355             :         // Skip C style comments
     356       12921 :         if (*pszHeaderNext == '/' && pszHeaderNext[1] == '*')
     357             :         {
     358           0 :             pszHeaderNext += 2;
     359             : 
     360           0 :             while (*pszHeaderNext != '\0' &&
     361           0 :                    (*pszHeaderNext != '*' || pszHeaderNext[1] != '/'))
     362             :             {
     363           0 :                 pszHeaderNext++;
     364             :             }
     365           0 :             if (*pszHeaderNext == '\0')
     366           0 :                 break;
     367             : 
     368           0 :             pszHeaderNext += 2;
     369           0 :             continue;
     370             :         }
     371             : 
     372             :         // Skip # style comments
     373       12921 :         if (*pszHeaderNext == '#')
     374             :         {
     375           0 :             pszHeaderNext += 1;
     376             : 
     377             :             // consume till end of line.
     378           0 :             while (*pszHeaderNext != '\0' && *pszHeaderNext != 10 &&
     379           0 :                    *pszHeaderNext != 13)
     380             :             {
     381           0 :                 pszHeaderNext++;
     382             :             }
     383           0 :             continue;
     384             :         }
     385             : 
     386             :         // not white space, return.
     387       12921 :         return;
     388             :     }
     389             : }
     390             : 
     391             : /************************************************************************/
     392             : /*                             GetKeyword()                             */
     393             : /************************************************************************/
     394             : 
     395         160 : const char *CPLKeywordParser::GetKeyword(const char *pszPath,
     396             :                                          const char *pszDefault)
     397             : 
     398             : {
     399         160 :     const char *pszResult = CSLFetchNameValue(papszKeywordList, pszPath);
     400         160 :     if (pszResult == nullptr)
     401           0 :         return pszDefault;
     402             : 
     403         160 :     return pszResult;
     404             : }
     405             : 
     406             : //! @endcond

Generated by: LCOV version 1.14