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

Generated by: LCOV version 1.14