LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pds - ogrpdsdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 109 176 61.9 %
Date: 2025-09-10 17:48:50 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PDS Translator
       4             :  * Purpose:  Implements OGRPDSDataSource class
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2011, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_conv.h"
      14             : #include "cpl_string.h"
      15             : #include "ogr_pds.h"
      16             : 
      17             : using namespace OGRPDS;
      18             : 
      19             : /************************************************************************/
      20             : /*                           OGRPDSDataSource()                         */
      21             : /************************************************************************/
      22             : 
      23           2 : OGRPDSDataSource::OGRPDSDataSource() : papoLayers(nullptr), nLayers(0)
      24             : {
      25           2 : }
      26             : 
      27             : /************************************************************************/
      28             : /*                        ~OGRPDSDataSource()                           */
      29             : /************************************************************************/
      30             : 
      31           4 : OGRPDSDataSource::~OGRPDSDataSource()
      32             : 
      33             : {
      34           4 :     for (int i = 0; i < nLayers; i++)
      35           2 :         delete papoLayers[i];
      36           2 :     CPLFree(papoLayers);
      37           4 : }
      38             : 
      39             : /************************************************************************/
      40             : /*                              GetLayer()                              */
      41             : /************************************************************************/
      42             : 
      43           2 : const OGRLayer *OGRPDSDataSource::GetLayer(int iLayer) const
      44             : 
      45             : {
      46           2 :     if (iLayer < 0 || iLayer >= nLayers)
      47           0 :         return nullptr;
      48             : 
      49           2 :     return papoLayers[iLayer];
      50             : }
      51             : 
      52             : /************************************************************************/
      53             : /*                          GetKeywordSub()                             */
      54             : /************************************************************************/
      55             : 
      56           2 : const char *OGRPDSDataSource::GetKeywordSub(const char *pszPath, int iSubscript,
      57             :                                             const char *pszDefault)
      58             : 
      59             : {
      60           2 :     const char *pszResult = oKeywords.GetKeyword(pszPath, nullptr);
      61             : 
      62           2 :     if (pszResult == nullptr)
      63           0 :         return pszDefault;
      64             : 
      65           2 :     if (pszResult[0] != '(')
      66           0 :         return pszDefault;
      67             : 
      68             :     char **papszTokens =
      69           2 :         CSLTokenizeString2(pszResult, "(,)", CSLT_HONOURSTRINGS);
      70             : 
      71           2 :     if (iSubscript <= CSLCount(papszTokens))
      72             :     {
      73           2 :         osTempResult = papszTokens[iSubscript - 1];
      74           2 :         CSLDestroy(papszTokens);
      75           2 :         return osTempResult.c_str();
      76             :     }
      77             : 
      78           0 :     CSLDestroy(papszTokens);
      79           0 :     return pszDefault;
      80             : }
      81             : 
      82             : /************************************************************************/
      83             : /*                            CleanString()                             */
      84             : /*                                                                      */
      85             : /* Removes single or double quotes, and converts spaces to underscores. */
      86             : /* The change is made in-place to CPLString.                            */
      87             : /************************************************************************/
      88             : 
      89         126 : void OGRPDSDataSource::CleanString(CPLString &osInput)
      90             : 
      91             : {
      92         252 :     if ((osInput.size() < 2) ||
      93         126 :         ((osInput.at(0) != '"' || osInput.at(osInput.size() - 1) != '"') &&
      94          65 :          (osInput.at(0) != '\'' || osInput.at(osInput.size() - 1) != '\'')))
      95          65 :         return;
      96             : 
      97          61 :     char *pszWrk = CPLStrdup(osInput.c_str() + 1);
      98             : 
      99          61 :     pszWrk[strlen(pszWrk) - 1] = '\0';
     100             : 
     101         956 :     for (int i = 0; pszWrk[i] != '\0'; i++)
     102             :     {
     103         895 :         if (pszWrk[i] == ' ')
     104           0 :             pszWrk[i] = '_';
     105             :     }
     106             : 
     107          61 :     osInput = pszWrk;
     108          61 :     CPLFree(pszWrk);
     109             : }
     110             : 
     111             : /************************************************************************/
     112             : /*                           LoadTable()                                */
     113             : /************************************************************************/
     114             : 
     115           8 : static CPLString MakeAttr(CPLString os1, CPLString os2)
     116             : {
     117          16 :     return os1 + "." + os2;
     118             : }
     119             : 
     120           2 : bool OGRPDSDataSource::LoadTable(const char *pszFilename, int nRecordSize,
     121             :                                  CPLString osTableID)
     122             : {
     123           4 :     CPLString osTableFilename;
     124           2 :     int nStartBytes = 0;
     125             : 
     126           4 :     CPLString osTableLink = "^";
     127           2 :     osTableLink += osTableID;
     128             : 
     129           4 :     CPLString osTable = oKeywords.GetKeyword(osTableLink, "");
     130           2 :     if (osTable[0] == '(')
     131             :     {
     132           1 :         osTableFilename = GetKeywordSub(osTableLink, 1, "");
     133           1 :         CPLString osStartRecord = GetKeywordSub(osTableLink, 2, "");
     134           1 :         nStartBytes = atoi(osStartRecord.c_str());
     135           1 :         if (nStartBytes <= 0 ||
     136           1 :             ((nRecordSize > 0 && nStartBytes > INT_MAX / nRecordSize)))
     137             :         {
     138           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Invalid StartBytes value");
     139           0 :             return false;
     140             :         }
     141           1 :         nStartBytes--;
     142           1 :         nStartBytes *= nRecordSize;
     143           1 :         if (osTableFilename.empty() || osStartRecord.empty() || nStartBytes < 0)
     144             :         {
     145           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Cannot parse %s line",
     146             :                      osTableLink.c_str());
     147           0 :             return false;
     148             :         }
     149           1 :         CPLString osTPath = CPLGetPathSafe(pszFilename);
     150           1 :         CleanString(osTableFilename);
     151             : 
     152           1 :         if (CPLHasPathTraversal(osTableFilename.c_str()))
     153             :         {
     154           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     155             :                      "Path traversal detected in %s", osTableFilename.c_str());
     156           0 :             return false;
     157             :         }
     158             : 
     159             :         osTableFilename =
     160           1 :             CPLFormCIFilenameSafe(osTPath, osTableFilename, nullptr);
     161             :     }
     162             :     else
     163             :     {
     164           1 :         osTableFilename = oKeywords.GetKeyword(osTableLink, "");
     165           1 :         if (!osTableFilename.empty() && osTableFilename[0] >= '0' &&
     166           0 :             osTableFilename[0] <= '9')
     167             :         {
     168           0 :             nStartBytes = atoi(osTableFilename.c_str());
     169           0 :             if (nStartBytes <= 1)
     170             :             {
     171           0 :                 CPLError(CE_Failure, CPLE_NotSupported, "Cannot parse %s line",
     172             :                          osTableFilename.c_str());
     173           0 :                 return false;
     174             :             }
     175           0 :             nStartBytes = nStartBytes - 1;
     176           0 :             if (strstr(osTableFilename.c_str(), "<BYTES>") == nullptr)
     177             :             {
     178           0 :                 if (nRecordSize > 0 && nStartBytes > INT_MAX / nRecordSize)
     179             :                 {
     180           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     181             :                              "Too big StartBytes value");
     182           0 :                     return false;
     183             :                 }
     184           0 :                 nStartBytes *= nRecordSize;
     185             :             }
     186           0 :             osTableFilename = pszFilename;
     187             :         }
     188             :         else
     189             :         {
     190           1 :             CPLString osTPath = CPLGetPathSafe(pszFilename);
     191           1 :             CleanString(osTableFilename);
     192             : 
     193           1 :             if (CPLHasPathTraversal(osTableFilename.c_str()))
     194             :             {
     195           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     196             :                          "Path traversal detected in %s",
     197             :                          osTableFilename.c_str());
     198           0 :                 return false;
     199             :             }
     200             : 
     201             :             osTableFilename =
     202           1 :                 CPLFormCIFilenameSafe(osTPath, osTableFilename, nullptr);
     203           1 :             nStartBytes = 0;
     204             :         }
     205             :     }
     206             : 
     207             :     CPLString osTableName =
     208           6 :         oKeywords.GetKeyword(MakeAttr(osTableID, "NAME"), "");
     209           2 :     if (osTableName.empty())
     210             :     {
     211           1 :         if (GetLayerByName(osTableID.c_str()) == nullptr)
     212           1 :             osTableName = osTableID;
     213             :         else
     214           0 :             osTableName = CPLSPrintf("Layer_%d", nLayers + 1);
     215             :     }
     216           2 :     CleanString(osTableName);
     217             :     CPLString osTableInterchangeFormat =
     218           6 :         oKeywords.GetKeyword(MakeAttr(osTableID, "INTERCHANGE_FORMAT"), "");
     219             :     CPLString osTableRows =
     220           6 :         oKeywords.GetKeyword(MakeAttr(osTableID, "ROWS"), "");
     221           2 :     const int nRecords = atoi(osTableRows);
     222           2 :     if (osTableInterchangeFormat.empty() || osTableRows.empty() || nRecords < 0)
     223             :     {
     224           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     225             :                  "One of TABLE.INTERCHANGE_FORMAT or TABLE.ROWS is missing");
     226           0 :         return false;
     227             :     }
     228             : 
     229           2 :     CleanString(osTableInterchangeFormat);
     230           3 :     if (osTableInterchangeFormat.compare("ASCII") != 0 &&
     231           1 :         osTableInterchangeFormat.compare("BINARY") != 0)
     232             :     {
     233           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     234             :                  "Only INTERCHANGE_FORMAT=ASCII or BINARY is supported");
     235           0 :         return false;
     236             :     }
     237             : 
     238           2 :     VSILFILE *fp = VSIFOpenL(osTableFilename, "rb");
     239           2 :     if (fp == nullptr)
     240             :     {
     241           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
     242             :                  osTableFilename.c_str());
     243           0 :         return false;
     244             :     }
     245             : 
     246             :     CPLString osTableStructure =
     247           6 :         oKeywords.GetKeyword(MakeAttr(osTableID, "^STRUCTURE"), "");
     248           2 :     if (!osTableStructure.empty())
     249             :     {
     250           2 :         CPLString osTPath = CPLGetPathSafe(pszFilename);
     251           2 :         CleanString(osTableStructure);
     252             : 
     253           2 :         if (CPLHasPathTraversal(osTableStructure.c_str()))
     254             :         {
     255           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     256             :                      "Path traversal detected in %s", osTableStructure.c_str());
     257           0 :             VSIFCloseL(fp);
     258           0 :             return false;
     259             :         }
     260             : 
     261             :         osTableStructure =
     262           2 :             CPLFormCIFilenameSafe(osTPath, osTableStructure, nullptr);
     263             :     }
     264             : 
     265           2 :     GByte *pabyRecord = (GByte *)VSI_MALLOC_VERBOSE(nRecordSize + 1);
     266           2 :     if (pabyRecord == nullptr)
     267             :     {
     268           0 :         VSIFCloseL(fp);
     269           0 :         return false;
     270             :     }
     271           2 :     pabyRecord[nRecordSize] = 0;
     272             : 
     273           2 :     papoLayers = static_cast<OGRLayer **>(
     274           2 :         CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer *)));
     275           4 :     papoLayers[nLayers] = new OGRPDSLayer(
     276             :         osTableID, osTableName, fp, pszFilename, osTableStructure, nRecords,
     277             :         nStartBytes, nRecordSize, pabyRecord,
     278           4 :         osTableInterchangeFormat.compare("ASCII") == 0);
     279           2 :     nLayers++;
     280             : 
     281           2 :     return true;
     282             : }
     283             : 
     284             : /************************************************************************/
     285             : /*                                Open()                                */
     286             : /************************************************************************/
     287             : 
     288           2 : int OGRPDSDataSource::Open(const char *pszFilename)
     289             : 
     290             : {
     291             :     // --------------------------------------------------------------------
     292             :     //      Does this appear to be a .PDS table file?
     293             :     // --------------------------------------------------------------------
     294             : 
     295           2 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
     296           2 :     if (fp == nullptr)
     297           0 :         return FALSE;
     298             : 
     299             :     char szBuffer[512];
     300             :     int nbRead =
     301           2 :         static_cast<int>(VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp));
     302           2 :     szBuffer[nbRead] = '\0';
     303             : 
     304           2 :     const char *pszPos = strstr(szBuffer, "PDS_VERSION_ID");
     305           2 :     const bool bIsPDS = pszPos != nullptr;
     306             : 
     307           2 :     if (!bIsPDS)
     308             :     {
     309           0 :         VSIFCloseL(fp);
     310           0 :         return FALSE;
     311             :     }
     312             : 
     313           2 :     if (!oKeywords.Ingest(fp, static_cast<int>(pszPos - szBuffer)))
     314             :     {
     315           0 :         VSIFCloseL(fp);
     316           0 :         return FALSE;
     317             :     }
     318             : 
     319           2 :     VSIFCloseL(fp);
     320           4 :     CPLString osRecordType = oKeywords.GetKeyword("RECORD_TYPE", "");
     321           4 :     CPLString osFileRecords = oKeywords.GetKeyword("FILE_RECORDS", "");
     322           4 :     CPLString osRecordBytes = oKeywords.GetKeyword("RECORD_BYTES", "");
     323           2 :     int nRecordSize = atoi(osRecordBytes);
     324           6 :     if (osRecordType.empty() || osFileRecords.empty() ||
     325           6 :         osRecordBytes.empty() || nRecordSize <= 0 ||
     326             :         nRecordSize > 10 * 1024 * 1024)
     327             :     {
     328           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     329             :                  "One of RECORD_TYPE, FILE_RECORDS or RECORD_BYTES is missing");
     330           0 :         return FALSE;
     331             :     }
     332           2 :     CleanString(osRecordType);
     333           2 :     if (osRecordType.compare("FIXED_LENGTH") != 0)
     334             :     {
     335           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     336             :                  "Only RECORD_TYPE=FIXED_LENGTH is supported");
     337           0 :         return FALSE;
     338             :     }
     339             : 
     340           4 :     CPLString osTable = oKeywords.GetKeyword("^TABLE", "");
     341           2 :     if (!osTable.empty())
     342             :     {
     343           2 :         LoadTable(pszFilename, nRecordSize, "TABLE");
     344             :     }
     345             :     else
     346             :     {
     347           0 :         fp = VSIFOpenL(pszFilename, "rb");
     348           0 :         if (fp == nullptr)
     349           0 :             return FALSE;
     350             : 
     351             :         // To avoid performance issues with datasets generated by oss-fuzz
     352           0 :         int nErrors = 0;
     353           0 :         while (nErrors < 10)
     354             :         {
     355           0 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     356           0 :             const char *pszLine = CPLReadLine2L(fp, 256, nullptr);
     357           0 :             CPLPopErrorHandler();
     358           0 :             CPLErrorReset();
     359           0 :             if (pszLine == nullptr)
     360           0 :                 break;
     361             :             char **papszTokens =
     362           0 :                 CSLTokenizeString2(pszLine, " =", CSLT_HONOURSTRINGS);
     363           0 :             int nTokens = CSLCount(papszTokens);
     364           0 :             if (nTokens == 2 && papszTokens[0][0] == '^' &&
     365           0 :                 strstr(papszTokens[0], "TABLE") != nullptr)
     366             :             {
     367           0 :                 if (!LoadTable(pszFilename, nRecordSize, papszTokens[0] + 1))
     368             :                 {
     369           0 :                     nErrors++;
     370             :                 }
     371             :             }
     372           0 :             CSLDestroy(papszTokens);
     373           0 :             papszTokens = nullptr;
     374             :         }
     375           0 :         VSIFCloseL(fp);
     376             :     }
     377             : 
     378           2 :     return nLayers != 0;
     379             : }

Generated by: LCOV version 1.14