LCOV - code coverage report
Current view: top level - frmts/pds - vicarkeywordhandler.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 185 220 84.1 %
Date: 2024-11-21 22:18:42 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             :     GUInt64 nPixelOffset;
     113             :     GUInt64 nLineOffset;
     114             :     GUInt64 nBandOffset;
     115             :     GUInt64 nImageOffsetWithoutNBB;
     116             :     GUInt64 nNBB;
     117             :     GUInt64 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<GUInt64>::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          50 :     char *pszChunkEOL = (char *)VSIMalloc(EOLabelSize + 1);
     192          50 :     if (pszChunkEOL == nullptr)
     193           0 :         return false;
     194          50 :     nBytesRead = static_cast<int>(VSIFReadL(pszChunkEOL, 1, EOLabelSize, fp));
     195          50 :     pszChunkEOL[nBytesRead] = '\0';
     196          50 :     osHeaderText += pszChunkEOL + nSkipEOLLBLSize;
     197          50 :     VSIFree(pszChunkEOL);
     198          50 :     CSLDestroy(papszKeywordList);
     199          50 :     papszKeywordList = nullptr;
     200          50 :     pszHeaderNext = osHeaderText.c_str();
     201          50 :     return Parse();
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*                               Parse()                                */
     206             : /************************************************************************/
     207             : 
     208             : #define SYNTHETIC_END_MARKER "__END__"
     209             : 
     210         163 : bool VICARKeywordHandler::Parse()
     211             : {
     212         326 :     CPLString osName, osValue, osGroupName;
     213         326 :     CPLJSONObject oProperties;
     214         326 :     CPLJSONObject oTasks;
     215         326 :     CPLJSONObject oCurObj;
     216         163 :     bool bHasProperties = false;
     217         163 :     bool bHasTasks = false;
     218             : 
     219         163 :     oJSon = CPLJSONObject();
     220             :     for (; true;)
     221             :     {
     222        6769 :         if (!ReadPair(osName, osValue, osGroupName.empty() ? oJSon : oCurObj))
     223           0 :             return false;
     224             : 
     225        6769 :         if (EQUAL(osName, SYNTHETIC_END_MARKER))
     226         163 :             break;
     227             : 
     228        6606 :         if (EQUAL(osName, "PROPERTY"))
     229             :         {
     230         114 :             osGroupName = osValue;
     231         114 :             oCurObj = CPLJSONObject();
     232         114 :             bHasProperties = true;
     233         114 :             oProperties.Add(osValue, oCurObj);
     234             :         }
     235        6492 :         else if (EQUAL(osName, "TASK"))
     236             :         {
     237         149 :             osGroupName = osValue;
     238         149 :             oCurObj = CPLJSONObject();
     239         149 :             bHasTasks = true;
     240         149 :             oTasks.Add(osValue, oCurObj);
     241             :         }
     242             :         else
     243             :         {
     244        6343 :             if (!osGroupName.empty())
     245        1879 :                 osName = osGroupName + "." + osName;
     246        6343 :             papszKeywordList =
     247        6343 :                 CSLSetNameValue(papszKeywordList, osName, osValue);
     248             :         }
     249             :     }
     250         163 :     if (bHasProperties)
     251          62 :         oJSon.Add("PROPERTY", oProperties);
     252         163 :     if (bHasTasks)
     253         109 :         oJSon.Add("TASK", oTasks);
     254         163 :     return true;
     255             : }
     256             : 
     257             : /************************************************************************/
     258             : /*                              ReadPair()                              */
     259             : /*                                                                      */
     260             : /*      Read a name/value pair from the input stream.  Strip off        */
     261             : /*      white space, ignore comments, split on '='.                     */
     262             : /*      Returns TRUE on success.                                        */
     263             : /************************************************************************/
     264             : 
     265        6769 : bool VICARKeywordHandler::ReadPair(CPLString &osName, CPLString &osValue,
     266             :                                    CPLJSONObject &oCur)
     267             : {
     268        6769 :     osName.clear();
     269        6769 :     osValue.clear();
     270             : 
     271        6769 :     if (!ReadName(osName))
     272             :     {
     273             :         // VICAR has no NULL string termination
     274         163 :         if (*pszHeaderNext == '\0')
     275             :         {
     276         163 :             osName = SYNTHETIC_END_MARKER;
     277         163 :             return true;
     278             :         }
     279           0 :         return false;
     280             :     }
     281             : 
     282        6606 :     bool bIsString = false;
     283        6606 :     if (*pszHeaderNext == '(')
     284             :     {
     285         186 :         CPLString osWord;
     286          93 :         pszHeaderNext++;
     287         186 :         CPLJSONArray oArray;
     288          93 :         oCur.Add(osName, oArray);
     289         162 :         while (ReadValue(osWord, true, bIsString))
     290             :         {
     291         162 :             if (!osValue.empty())
     292          69 :                 osValue += ',';
     293         162 :             osValue += osWord;
     294         162 :             if (bIsString)
     295             :             {
     296          24 :                 oArray.Add(osWord);
     297             :             }
     298         138 :             else if (CPLGetValueType(osWord) == CPL_VALUE_INTEGER)
     299             :             {
     300           8 :                 oArray.Add(atoi(osWord));
     301             :             }
     302             :             else
     303             :             {
     304         130 :                 oArray.Add(CPLAtof(osWord));
     305             :             }
     306         162 :             if (*pszHeaderNext == ')')
     307             :             {
     308          93 :                 pszHeaderNext++;
     309          93 :                 break;
     310             :             }
     311          69 :             pszHeaderNext++;
     312             :         }
     313             :     }
     314             :     else
     315             :     {
     316        6513 :         if (!ReadValue(osValue, false, bIsString))
     317           0 :             return false;
     318        6513 :         if (!EQUAL(osName, "PROPERTY") && !EQUAL(osName, "TASK"))
     319             :         {
     320        6250 :             if (bIsString)
     321             :             {
     322        2874 :                 oCur.Add(osName, osValue);
     323             :             }
     324        3376 :             else if (CPLGetValueType(osValue) == CPL_VALUE_INTEGER)
     325             :             {
     326        2772 :                 oCur.Add(osName, atoi(osValue));
     327             :             }
     328             :             else
     329             :             {
     330         604 :                 oCur.Add(osName, CPLAtof(osValue));
     331             :             }
     332             :         }
     333             :     }
     334             : 
     335        6606 :     return true;
     336             : }
     337             : 
     338             : /************************************************************************/
     339             : /*                              ReadName()                              */
     340             : /************************************************************************/
     341             : 
     342        6769 : bool VICARKeywordHandler::ReadName(CPLString &osWord)
     343             : 
     344             : {
     345        6769 :     osWord.clear();
     346             : 
     347        6769 :     SkipWhite();
     348             : 
     349        6769 :     if (*pszHeaderNext == '\0')
     350         163 :         return false;
     351             : 
     352       53804 :     while (*pszHeaderNext != '=' && !isspace((unsigned char)*pszHeaderNext))
     353             :     {
     354       47198 :         if (*pszHeaderNext == '\0')
     355           0 :             return false;
     356       47198 :         osWord += *pszHeaderNext;
     357       47198 :         pszHeaderNext++;
     358             :     }
     359             : 
     360        6606 :     SkipWhite();
     361             : 
     362        6606 :     if (*pszHeaderNext != '=')
     363           0 :         return false;
     364        6606 :     pszHeaderNext++;
     365             : 
     366        6606 :     SkipWhite();
     367             : 
     368        6606 :     return true;
     369             : }
     370             : 
     371             : /************************************************************************/
     372             : /*                              ReadWord()                              */
     373             : /************************************************************************/
     374             : 
     375        6675 : bool VICARKeywordHandler::ReadValue(CPLString &osWord, bool bInList,
     376             :                                     bool &bIsString)
     377             : 
     378             : {
     379        6675 :     osWord.clear();
     380             : 
     381        6675 :     SkipWhite();
     382             : 
     383        6675 :     if (*pszHeaderNext == '\0')
     384           0 :         return false;
     385             : 
     386        6675 :     if (*pszHeaderNext == '\'')
     387             :     {
     388        3151 :         bIsString = true;
     389        3151 :         pszHeaderNext++;
     390             :         while (true)
     391             :         {
     392       24057 :             if (*pszHeaderNext == '\0')
     393           0 :                 return false;
     394       24057 :             if (*(pszHeaderNext) == '\'')
     395             :             {
     396        3163 :                 if (*(pszHeaderNext + 1) == '\'')
     397             :                 {
     398             :                     // Skip Double Quotes
     399          12 :                     pszHeaderNext++;
     400             :                 }
     401             :                 else
     402        3151 :                     break;
     403             :             }
     404       20906 :             osWord += *pszHeaderNext;
     405       20906 :             pszHeaderNext++;
     406             :         }
     407        3151 :         pszHeaderNext++;
     408             :     }
     409             :     else
     410             :     {
     411       11447 :         while (!isspace((unsigned char)*pszHeaderNext))
     412             :         {
     413        8092 :             if (*pszHeaderNext == '\0')
     414          31 :                 return !bInList;
     415        8061 :             if (bInList && (*pszHeaderNext == ',' || *pszHeaderNext == ')'))
     416             :             {
     417         138 :                 return true;
     418             :             }
     419        7923 :             osWord += *pszHeaderNext;
     420        7923 :             pszHeaderNext++;
     421             :         }
     422        3355 :         bIsString = CPLGetValueType(osWord) == CPL_VALUE_STRING;
     423             :     }
     424             : 
     425        6506 :     SkipWhite();
     426        6506 :     if (bInList && *pszHeaderNext != ',' && *pszHeaderNext != ')')
     427           0 :         return false;
     428             : 
     429        6506 :     return true;
     430             : }
     431             : 
     432             : /************************************************************************/
     433             : /*                             SkipWhite()                              */
     434             : /*  Skip white spaces                                                   */
     435             : /************************************************************************/
     436             : 
     437       47603 : void VICARKeywordHandler::SkipWhite()
     438             : 
     439             : {
     440             :     for (; true;)
     441             :     {
     442       47603 :         if (isspace((unsigned char)*pszHeaderNext))
     443             :         {
     444       14441 :             pszHeaderNext++;
     445       14441 :             continue;
     446             :         }
     447             : 
     448             :         // not white space, return.
     449       33162 :         return;
     450             :     }
     451             : }
     452             : 
     453             : /************************************************************************/
     454             : /*                             GetKeyword()                             */
     455             : /************************************************************************/
     456             : 
     457        5028 : const char *VICARKeywordHandler::GetKeyword(const char *pszPath,
     458             :                                             const char *pszDefault) const
     459             : 
     460             : {
     461        5028 :     const char *pszResult = CSLFetchNameValue(papszKeywordList, pszPath);
     462             : 
     463        5028 :     if (pszResult == nullptr)
     464        1809 :         return pszDefault;
     465             : 
     466        3219 :     return pszResult;
     467             : }

Generated by: LCOV version 1.14