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

Generated by: LCOV version 1.14