LCOV - code coverage report
Current view: top level - frmts/pds - vicarkeywordhandler.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 186 221 84.2 %
Date: 2025-06-28 21:28:23 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VICAR Driver; JPL/MIPL VICAR Format
       4             :  * Purpose:  Implementation of VICARKeywordHandler - a class to read
       5             :  *           keyword data from VICAR data products.
       6             :  * Author:   Sebastian Walter <sebastian dot walter at fu-berlin dot de>
       7             :  *
       8             :  * NOTE: This driver code is loosely based on the ISIS and PDS drivers.
       9             :  * It is not intended to diminish the contribution of the authors.
      10             :  ******************************************************************************
      11             :  * Copyright (c) 2014, Sebastian Walter <sebastian dot walter at fu-berlin dot
      12             :  *de>
      13             :  *
      14             :  * SPDX-License-Identifier: MIT
      15             :  ****************************************************************************/
      16             : 
      17             : #include "cpl_string.h"
      18             : #include "vicarkeywordhandler.h"
      19             : #include "vicardataset.h"
      20             : 
      21             : #include <algorithm>
      22             : #include <limits>
      23             : 
      24             : /************************************************************************/
      25             : /* ==================================================================== */
      26             : /*                          VICARKeywordHandler                         */
      27             : /* ==================================================================== */
      28             : /************************************************************************/
      29             : 
      30             : /************************************************************************/
      31             : /*                         VICARKeywordHandler()                        */
      32             : /************************************************************************/
      33             : 
      34         166 : VICARKeywordHandler::VICARKeywordHandler()
      35         166 :     : papszKeywordList(nullptr), pszHeaderNext(nullptr)
      36             : {
      37         166 :     oJSon.Deinit();
      38         166 : }
      39             : 
      40             : /************************************************************************/
      41             : /*                        ~VICARKeywordHandler()                        */
      42             : /************************************************************************/
      43             : 
      44         166 : VICARKeywordHandler::~VICARKeywordHandler()
      45             : 
      46             : {
      47         166 :     CSLDestroy(papszKeywordList);
      48         166 : }
      49             : 
      50             : /************************************************************************/
      51             : /*                               Ingest()                               */
      52             : /************************************************************************/
      53             : 
      54         113 : bool VICARKeywordHandler::Ingest(VSILFILE *fp, const GByte *pabyHeader)
      55             : 
      56             : {
      57             :     /* -------------------------------------------------------------------- */
      58             :     /*      Read in label at beginning of file.                             */
      59             :     /* -------------------------------------------------------------------- */
      60         113 :     if (VSIFSeekL(fp, 0, SEEK_SET) != 0)
      61           0 :         return false;
      62             : 
      63             :     // Find LBLSIZE Entry
      64             :     const char *pszLBLSIZE =
      65         113 :         strstr(reinterpret_cast<const char *>(pabyHeader), "LBLSIZE");
      66         113 :     if (!pszLBLSIZE)
      67           0 :         return false;
      68             : 
      69         113 :     const char *pch1 = strchr(pszLBLSIZE, '=');
      70         113 :     if (pch1 == nullptr)
      71           0 :         return false;
      72         113 :     ++pch1;
      73         121 :     while (isspace(static_cast<unsigned char>(*pch1)))
      74           8 :         ++pch1;
      75         113 :     const char *pch2 = strchr(pch1, ' ');
      76         113 :     if (pch2 == nullptr)
      77           0 :         return false;
      78             : 
      79         226 :     std::string keyval;
      80         113 :     keyval.assign(pch1, static_cast<size_t>(pch2 - pch1));
      81         113 :     int LabelSize = atoi(keyval.c_str());
      82         113 :     if (LabelSize <= 0 || LabelSize > 10 * 1024 * 124)
      83           0 :         return false;
      84             : 
      85         113 :     char *pszChunk = reinterpret_cast<char *>(VSIMalloc(LabelSize + 1));
      86         113 :     if (pszChunk == nullptr)
      87           0 :         return false;
      88         113 :     int nBytesRead = static_cast<int>(VSIFReadL(pszChunk, 1, LabelSize, fp));
      89         113 :     pszChunk[nBytesRead] = '\0';
      90             : 
      91         113 :     osHeaderText += pszChunk;
      92         113 :     VSIFree(pszChunk);
      93         113 :     pszHeaderNext = osHeaderText.c_str();
      94             : 
      95             :     /* -------------------------------------------------------------------- */
      96             :     /*      Process name/value pairs                                        */
      97             :     /* -------------------------------------------------------------------- */
      98         113 :     if (!Parse())
      99           0 :         return false;
     100             : 
     101             :     /* -------------------------------------------------------------------- */
     102             :     /*      Now check for the Vicar End-of-Dataset Label...                 */
     103             :     /* -------------------------------------------------------------------- */
     104         113 :     const char *pszResult = CSLFetchNameValueDef(papszKeywordList, "EOL", "0");
     105         113 :     if (!EQUAL(pszResult, "1"))
     106          63 :         return true;
     107             : 
     108             :     /* -------------------------------------------------------------------- */
     109             :     /*      There is a EOL!   e.G.  h4231_0000.nd4.06                       */
     110             :     /* -------------------------------------------------------------------- */
     111             : 
     112             :     uint64_t nPixelOffset;
     113             :     uint64_t nLineOffset;
     114             :     uint64_t nBandOffset;
     115             :     uint64_t nImageOffsetWithoutNBB;
     116             :     uint64_t nNBB;
     117             :     uint64_t nImageSize;
     118          50 :     if (!VICARDataset::GetSpacings(*this, nPixelOffset, nLineOffset,
     119             :                                    nBandOffset, nImageOffsetWithoutNBB, nNBB,
     120             :                                    nImageSize))
     121           0 :         return false;
     122             : 
     123             :     // Position of EOL in case of compressed data
     124             :     const vsi_l_offset nEOCI1 = static_cast<vsi_l_offset>(
     125          50 :         CPLAtoGIntBig(CSLFetchNameValueDef(papszKeywordList, "EOCI1", "0")));
     126             :     const vsi_l_offset nEOCI2 = static_cast<vsi_l_offset>(
     127          50 :         CPLAtoGIntBig(CSLFetchNameValueDef(papszKeywordList, "EOCI2", "0")));
     128          50 :     const vsi_l_offset nEOCI = (nEOCI2 << 32) | nEOCI1;
     129             : 
     130          50 :     if (nImageOffsetWithoutNBB >
     131          50 :         std::numeric_limits<uint64_t>::max() - nImageSize)
     132             :     {
     133           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid label values");
     134           0 :         return false;
     135             :     }
     136             : 
     137          50 :     const vsi_l_offset nStartEOL =
     138          50 :         nEOCI ? nEOCI : nImageOffsetWithoutNBB + nImageSize;
     139             : 
     140          50 :     if (VSIFSeekL(fp, nStartEOL, SEEK_SET) != 0)
     141             :     {
     142           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error seeking to EOL");
     143           0 :         return false;
     144             :     }
     145          50 :     char *pszEOLHeader = static_cast<char *>(VSIMalloc(32));
     146          50 :     if (pszEOLHeader == nullptr)
     147           0 :         return false;
     148          50 :     nBytesRead = static_cast<int>(VSIFReadL(pszEOLHeader, 1, 31, fp));
     149          50 :     pszEOLHeader[nBytesRead] = '\0';
     150          50 :     pszLBLSIZE = strstr(pszEOLHeader, "LBLSIZE");
     151          50 :     if (!pszLBLSIZE)
     152             :     {
     153           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     154             :                  "END-OF-DATASET LABEL NOT FOUND!");
     155           0 :         VSIFree(pszEOLHeader);
     156           0 :         return false;
     157             :     }
     158          50 :     pch1 = strchr(pszLBLSIZE, '=');
     159          50 :     if (pch1 == nullptr)
     160             :     {
     161           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     162             :                  "END-OF-DATASET LABEL NOT FOUND!");
     163           0 :         VSIFree(pszEOLHeader);
     164           0 :         return false;
     165             :     }
     166          50 :     ++pch1;
     167          50 :     while (isspace(static_cast<unsigned char>(*pch1)))
     168           0 :         ++pch1;
     169          50 :     pch2 = strchr(pch1, ' ');
     170          50 :     if (pch2 == nullptr)
     171             :     {
     172           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     173             :                  "END-OF-DATASET LABEL NOT FOUND!");
     174           0 :         VSIFree(pszEOLHeader);
     175           0 :         return false;
     176             :     }
     177          50 :     keyval.assign(pch1, static_cast<size_t>(pch2 - pch1));
     178          50 :     const auto nSkipEOLLBLSize = static_cast<size_t>(pch2 - pszEOLHeader);
     179          50 :     VSIFree(pszEOLHeader);
     180             : 
     181          50 :     int EOLabelSize = atoi(keyval.c_str());
     182          50 :     if (EOLabelSize <= 0 ||
     183          50 :         static_cast<size_t>(EOLabelSize) <= nSkipEOLLBLSize ||
     184             :         EOLabelSize > 100 * 1024 * 1024)
     185           0 :         return false;
     186          50 :     if (VSIFSeekL(fp, nStartEOL, SEEK_SET) != 0)
     187             :     {
     188           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error seeking to EOL");
     189           0 :         return false;
     190             :     }
     191             :     char *pszChunkEOL =
     192          50 :         static_cast<char *>(VSI_MALLOC_VERBOSE(EOLabelSize + 1));
     193          50 :     if (pszChunkEOL == nullptr)
     194           0 :         return false;
     195          50 :     nBytesRead = static_cast<int>(VSIFReadL(pszChunkEOL, 1, EOLabelSize, fp));
     196          50 :     pszChunkEOL[nBytesRead] = '\0';
     197          50 :     osHeaderText += pszChunkEOL + nSkipEOLLBLSize;
     198          50 :     VSIFree(pszChunkEOL);
     199          50 :     CSLDestroy(papszKeywordList);
     200          50 :     papszKeywordList = nullptr;
     201          50 :     pszHeaderNext = osHeaderText.c_str();
     202          50 :     return Parse();
     203             : }
     204             : 
     205             : /************************************************************************/
     206             : /*                               Parse()                                */
     207             : /************************************************************************/
     208             : 
     209             : #define SYNTHETIC_END_MARKER "__END__"
     210             : 
     211         163 : bool VICARKeywordHandler::Parse()
     212             : {
     213         326 :     CPLString osName, osValue, osGroupName;
     214         326 :     CPLJSONObject oProperties;
     215         326 :     CPLJSONObject oTasks;
     216         326 :     CPLJSONObject oCurObj;
     217         163 :     bool bHasProperties = false;
     218         163 :     bool bHasTasks = false;
     219             : 
     220         163 :     oJSon = CPLJSONObject();
     221             :     for (; true;)
     222             :     {
     223        6769 :         if (!ReadPair(osName, osValue, osGroupName.empty() ? oJSon : oCurObj))
     224           0 :             return false;
     225             : 
     226        6769 :         if (EQUAL(osName, SYNTHETIC_END_MARKER))
     227         163 :             break;
     228             : 
     229        6606 :         if (EQUAL(osName, "PROPERTY"))
     230             :         {
     231         114 :             osGroupName = osValue;
     232         114 :             oCurObj = CPLJSONObject();
     233         114 :             bHasProperties = true;
     234         114 :             oProperties.Add(osValue, oCurObj);
     235             :         }
     236        6492 :         else if (EQUAL(osName, "TASK"))
     237             :         {
     238         149 :             osGroupName = osValue;
     239         149 :             oCurObj = CPLJSONObject();
     240         149 :             bHasTasks = true;
     241         149 :             oTasks.Add(osValue, oCurObj);
     242             :         }
     243             :         else
     244             :         {
     245        6343 :             if (!osGroupName.empty())
     246        1879 :                 osName = osGroupName + "." + osName;
     247        6343 :             papszKeywordList =
     248        6343 :                 CSLSetNameValue(papszKeywordList, osName, osValue);
     249             :         }
     250             :     }
     251         163 :     if (bHasProperties)
     252          62 :         oJSon.Add("PROPERTY", oProperties);
     253         163 :     if (bHasTasks)
     254         109 :         oJSon.Add("TASK", oTasks);
     255         163 :     return true;
     256             : }
     257             : 
     258             : /************************************************************************/
     259             : /*                              ReadPair()                              */
     260             : /*                                                                      */
     261             : /*      Read a name/value pair from the input stream.  Strip off        */
     262             : /*      white space, ignore comments, split on '='.                     */
     263             : /*      Returns TRUE on success.                                        */
     264             : /************************************************************************/
     265             : 
     266        6769 : bool VICARKeywordHandler::ReadPair(CPLString &osName, CPLString &osValue,
     267             :                                    CPLJSONObject &oCur)
     268             : {
     269        6769 :     osName.clear();
     270        6769 :     osValue.clear();
     271             : 
     272        6769 :     if (!ReadName(osName))
     273             :     {
     274             :         // VICAR has no NULL string termination
     275         163 :         if (*pszHeaderNext == '\0')
     276             :         {
     277         163 :             osName = SYNTHETIC_END_MARKER;
     278         163 :             return true;
     279             :         }
     280           0 :         return false;
     281             :     }
     282             : 
     283        6606 :     bool bIsString = false;
     284        6606 :     if (*pszHeaderNext == '(')
     285             :     {
     286         186 :         CPLString osWord;
     287          93 :         pszHeaderNext++;
     288         186 :         CPLJSONArray oArray;
     289          93 :         oCur.Add(osName, oArray);
     290         162 :         while (ReadValue(osWord, true, bIsString))
     291             :         {
     292         162 :             if (!osValue.empty())
     293          69 :                 osValue += ',';
     294         162 :             osValue += osWord;
     295         162 :             if (bIsString)
     296             :             {
     297          24 :                 oArray.Add(osWord);
     298             :             }
     299         138 :             else if (CPLGetValueType(osWord) == CPL_VALUE_INTEGER)
     300             :             {
     301           8 :                 oArray.Add(atoi(osWord));
     302             :             }
     303             :             else
     304             :             {
     305         130 :                 oArray.Add(CPLAtof(osWord));
     306             :             }
     307         162 :             if (*pszHeaderNext == ')')
     308             :             {
     309          93 :                 pszHeaderNext++;
     310          93 :                 break;
     311             :             }
     312          69 :             pszHeaderNext++;
     313             :         }
     314             :     }
     315             :     else
     316             :     {
     317        6513 :         if (!ReadValue(osValue, false, bIsString))
     318           0 :             return false;
     319        6513 :         if (!EQUAL(osName, "PROPERTY") && !EQUAL(osName, "TASK"))
     320             :         {
     321        6250 :             if (bIsString)
     322             :             {
     323        2874 :                 oCur.Add(osName, osValue);
     324             :             }
     325        3376 :             else if (CPLGetValueType(osValue) == CPL_VALUE_INTEGER)
     326             :             {
     327        2772 :                 oCur.Add(osName, atoi(osValue));
     328             :             }
     329             :             else
     330             :             {
     331         604 :                 oCur.Add(osName, CPLAtof(osValue));
     332             :             }
     333             :         }
     334             :     }
     335             : 
     336        6606 :     return true;
     337             : }
     338             : 
     339             : /************************************************************************/
     340             : /*                              ReadName()                              */
     341             : /************************************************************************/
     342             : 
     343        6769 : bool VICARKeywordHandler::ReadName(CPLString &osWord)
     344             : 
     345             : {
     346        6769 :     osWord.clear();
     347             : 
     348        6769 :     SkipWhite();
     349             : 
     350        6769 :     if (*pszHeaderNext == '\0')
     351         163 :         return false;
     352             : 
     353       53804 :     while (*pszHeaderNext != '=' &&
     354       47206 :            !isspace(static_cast<unsigned char>(*pszHeaderNext)))
     355             :     {
     356       47198 :         if (*pszHeaderNext == '\0')
     357           0 :             return false;
     358       47198 :         osWord += *pszHeaderNext;
     359       47198 :         pszHeaderNext++;
     360             :     }
     361             : 
     362        6606 :     SkipWhite();
     363             : 
     364        6606 :     if (*pszHeaderNext != '=')
     365           0 :         return false;
     366        6606 :     pszHeaderNext++;
     367             : 
     368        6606 :     SkipWhite();
     369             : 
     370        6606 :     return true;
     371             : }
     372             : 
     373             : /************************************************************************/
     374             : /*                              ReadWord()                              */
     375             : /************************************************************************/
     376             : 
     377        6675 : bool VICARKeywordHandler::ReadValue(CPLString &osWord, bool bInList,
     378             :                                     bool &bIsString)
     379             : 
     380             : {
     381        6675 :     osWord.clear();
     382             : 
     383        6675 :     SkipWhite();
     384             : 
     385        6675 :     if (*pszHeaderNext == '\0')
     386           0 :         return false;
     387             : 
     388        6675 :     if (*pszHeaderNext == '\'')
     389             :     {
     390        3151 :         bIsString = true;
     391        3151 :         pszHeaderNext++;
     392             :         while (true)
     393             :         {
     394       24057 :             if (*pszHeaderNext == '\0')
     395           0 :                 return false;
     396       24057 :             if (*(pszHeaderNext) == '\'')
     397             :             {
     398        3163 :                 if (*(pszHeaderNext + 1) == '\'')
     399             :                 {
     400             :                     // Skip Double Quotes
     401          12 :                     pszHeaderNext++;
     402             :                 }
     403             :                 else
     404        3151 :                     break;
     405             :             }
     406       20906 :             osWord += *pszHeaderNext;
     407       20906 :             pszHeaderNext++;
     408             :         }
     409        3151 :         pszHeaderNext++;
     410             :     }
     411             :     else
     412             :     {
     413       11447 :         while (!isspace(static_cast<unsigned char>(*pszHeaderNext)))
     414             :         {
     415        8092 :             if (*pszHeaderNext == '\0')
     416          31 :                 return !bInList;
     417        8061 :             if (bInList && (*pszHeaderNext == ',' || *pszHeaderNext == ')'))
     418             :             {
     419         138 :                 return true;
     420             :             }
     421        7923 :             osWord += *pszHeaderNext;
     422        7923 :             pszHeaderNext++;
     423             :         }
     424        3355 :         bIsString = CPLGetValueType(osWord) == CPL_VALUE_STRING;
     425             :     }
     426             : 
     427        6506 :     SkipWhite();
     428        6506 :     if (bInList && *pszHeaderNext != ',' && *pszHeaderNext != ')')
     429           0 :         return false;
     430             : 
     431        6506 :     return true;
     432             : }
     433             : 
     434             : /************************************************************************/
     435             : /*                             SkipWhite()                              */
     436             : /*  Skip white spaces                                                   */
     437             : /************************************************************************/
     438             : 
     439       47603 : void VICARKeywordHandler::SkipWhite()
     440             : 
     441             : {
     442             :     for (; true;)
     443             :     {
     444       47603 :         if (isspace(static_cast<unsigned char>(*pszHeaderNext)))
     445             :         {
     446       14441 :             pszHeaderNext++;
     447       14441 :             continue;
     448             :         }
     449             : 
     450             :         // not white space, return.
     451       33162 :         return;
     452             :     }
     453             : }
     454             : 
     455             : /************************************************************************/
     456             : /*                             GetKeyword()                             */
     457             : /************************************************************************/
     458             : 
     459        5028 : const char *VICARKeywordHandler::GetKeyword(const char *pszPath,
     460             :                                             const char *pszDefault) const
     461             : 
     462             : {
     463        5028 :     const char *pszResult = CSLFetchNameValue(papszKeywordList, pszPath);
     464             : 
     465        5028 :     if (pszResult == nullptr)
     466        1809 :         return pszDefault;
     467             : 
     468        3219 :     return pszResult;
     469             : }

Generated by: LCOV version 1.14