LCOV - code coverage report
Current view: top level - frmts/nitf - rpftocfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 247 390 63.3 %
Date: 2025-09-10 17:48:50 Functions: 3 4 75.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  RPF A.TOC read Library
       4             :  * Purpose:  Module responsible for opening a RPF TOC file, populating RPFToc
       5             :  *           structure
       6             :  * Author:   Even Rouault, even.rouault at spatialys.com
       7             :  *
       8             :  **********************************************************************
       9             :  * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : /* Portions of code are placed under the following copyright : */
      15             : /*
      16             :  ******************************************************************************
      17             :  * Copyright (C) 1995 Logiciels et Applications Scientifiques (L.A.S.) Inc
      18             :  * Permission to use, copy, modify and distribute this software and
      19             :  * its documentation for any purpose and without fee is hereby granted,
      20             :  * provided that the above copyright notice appear in all copies, that
      21             :  * both the copyright notice and this permission notice appear in
      22             :  * supporting documentation, and that the name of L.A.S. Inc not be used
      23             :  * in advertising or publicity pertaining to distribution of the software
      24             :  * without specific, written prior permission. L.A.S. Inc. makes no
      25             :  * representations about the suitability of this software for any purpose.
      26             :  * It is provided "as is" without express or implied warranty.
      27             :  ******************************************************************************
      28             :  */
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "rpftoclib.h"
      32             : 
      33             : #include <climits>
      34             : #include <cmath>
      35             : #include <cstring>
      36             : 
      37             : #include "cpl_conv.h"
      38             : #include "cpl_error.h"
      39             : #include "cpl_string.h"
      40             : #include "cpl_vsi.h"
      41             : #include "nitflib.h"
      42             : 
      43             : /************************************************************************/
      44             : /*                        RPFTOCTrim()                                    */
      45             : /************************************************************************/
      46             : 
      47          50 : static void RPFTOCTrim(char *str)
      48             : {
      49          50 :     char *c = str;
      50          50 :     if (str == nullptr || *str == 0)
      51           0 :         return;
      52             : 
      53          50 :     while (*c == ' ')
      54             :     {
      55           0 :         c++;
      56             :     }
      57          50 :     if (c != str)
      58             :     {
      59           0 :         memmove(str, c, strlen(c) + 1);
      60             :     }
      61             : 
      62          50 :     int i = static_cast<int>(strlen(str)) - 1;
      63          70 :     while (i >= 0 && str[i] == ' ')
      64             :     {
      65          20 :         str[i] = 0;
      66          20 :         i--;
      67             :     }
      68             : }
      69             : 
      70             : /************************************************************************/
      71             : /*                        RPFTOCRead()                                 */
      72             : /************************************************************************/
      73             : 
      74           0 : RPFToc *RPFTOCRead(const char *pszFilename, NITFFile *psFile)
      75             : {
      76             :     int nTRESize;
      77             :     const char *pachTRE =
      78           0 :         NITFFindTRE(psFile->pachTRE, psFile->nTREBytes, "RPFHDR", &nTRESize);
      79           0 :     if (pachTRE == nullptr)
      80             :     {
      81           0 :         CPLError(CE_Failure, CPLE_NotSupported,
      82             :                  "Invalid TOC file. Can't find RPFHDR.");
      83           0 :         return nullptr;
      84             :     }
      85             : 
      86           0 :     if (nTRESize != 48)
      87             :     {
      88           0 :         CPLError(CE_Failure, CPLE_NotSupported, "RPFHDR TRE wrong size.");
      89           0 :         return nullptr;
      90             :     }
      91             : 
      92           0 :     return RPFTOCReadFromBuffer(pszFilename, psFile->fp, pachTRE);
      93             : }
      94             : 
      95             : /* This function is directly inspired by function parse_toc coming from
      96             :  * ogdi/driver/rpf/utils.c */
      97             : 
      98          10 : RPFToc *RPFTOCReadFromBuffer(const char *pszFilename, VSILFILE *fp,
      99             :                              const char *tocHeader)
     100             : {
     101          10 :     tocHeader += 1;  /* skip endian */
     102          10 :     tocHeader += 2;  /* skip header length */
     103          10 :     tocHeader += 12; /* skip file name : this should be A.TOC (padded) */
     104          10 :     tocHeader += 1;  /* skip new  */
     105          10 :     tocHeader += 15; /* skip standard_num  */
     106          10 :     tocHeader += 8;  /* skip standard_date  */
     107          10 :     tocHeader += 1;  /* skip classification  */
     108          10 :     tocHeader += 2;  /* skip country  */
     109          10 :     tocHeader += 2;  /* skip release  */
     110             : 
     111             :     unsigned int locationSectionPhysicalLocation;
     112          10 :     memcpy(&locationSectionPhysicalLocation, tocHeader, sizeof(unsigned int));
     113          10 :     CPL_MSBPTR32(&locationSectionPhysicalLocation);
     114             : 
     115          10 :     if (VSIFSeekL(fp, locationSectionPhysicalLocation, SEEK_SET) != 0)
     116             :     {
     117           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     118             :                  "Invalid TOC file. Unable to seek to "
     119             :                  "locationSectionPhysicalLocation at offset %d.",
     120             :                  locationSectionPhysicalLocation);
     121           0 :         return nullptr;
     122             :     }
     123             : 
     124             :     int nSections;
     125          10 :     NITFLocation *pasLocations = NITFReadRPFLocationTable(fp, &nSections);
     126             : 
     127          10 :     unsigned int boundaryRectangleSectionSubHeaderPhysIndex = 0;
     128          10 :     unsigned int boundaryRectangleTablePhysIndex = 0;
     129          10 :     unsigned int frameFileIndexSectionSubHeaderPhysIndex = 0;
     130          10 :     unsigned int frameFileIndexSubsectionPhysIndex = 0;
     131             : 
     132          50 :     for (int i = 0; i < nSections; i++)
     133             :     {
     134          40 :         if (pasLocations[i].nLocId == LID_BoundaryRectangleSectionSubheader)
     135             :         {
     136          10 :             boundaryRectangleSectionSubHeaderPhysIndex =
     137          10 :                 pasLocations[i].nLocOffset;
     138             :         }
     139          30 :         else if (pasLocations[i].nLocId == LID_BoundaryRectangleTable)
     140             :         {
     141          10 :             boundaryRectangleTablePhysIndex = pasLocations[i].nLocOffset;
     142             :         }
     143          20 :         else if (pasLocations[i].nLocId == LID_FrameFileIndexSectionSubHeader)
     144             :         {
     145          10 :             frameFileIndexSectionSubHeaderPhysIndex =
     146          10 :                 pasLocations[i].nLocOffset;
     147             :         }
     148          10 :         else if (pasLocations[i].nLocId == LID_FrameFileIndexSubsection)
     149             :         {
     150          10 :             frameFileIndexSubsectionPhysIndex = pasLocations[i].nLocOffset;
     151             :         }
     152             :     }
     153             : 
     154          10 :     CPLFree(pasLocations);
     155             : 
     156          10 :     if (boundaryRectangleSectionSubHeaderPhysIndex == 0)
     157             :     {
     158           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     159             :                  "Invalid TOC file. Can't find "
     160             :                  "LID_BoundaryRectangleSectionSubheader.");
     161           0 :         return nullptr;
     162             :     }
     163          10 :     if (boundaryRectangleTablePhysIndex == 0)
     164             :     {
     165           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     166             :                  "Invalid TOC file. Can't find LID_BoundaryRectangleTable.");
     167           0 :         return nullptr;
     168             :     }
     169          10 :     if (frameFileIndexSectionSubHeaderPhysIndex == 0)
     170             :     {
     171           0 :         CPLError(
     172             :             CE_Failure, CPLE_NotSupported,
     173             :             "Invalid TOC file. Can't find LID_FrameFileIndexSectionSubHeader.");
     174           0 :         return nullptr;
     175             :     }
     176          10 :     if (frameFileIndexSubsectionPhysIndex == 0)
     177             :     {
     178           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     179             :                  "Invalid TOC file. Can't find LID_FrameFileIndexSubsection.");
     180           0 :         return nullptr;
     181             :     }
     182             : 
     183          10 :     if (VSIFSeekL(fp, boundaryRectangleSectionSubHeaderPhysIndex, SEEK_SET) !=
     184             :         0)
     185             :     {
     186           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     187             :                  "Invalid TOC file. Unable to seek to "
     188             :                  "boundaryRectangleSectionSubHeaderPhysIndex at offset %d.",
     189             :                  boundaryRectangleSectionSubHeaderPhysIndex);
     190           0 :         return nullptr;
     191             :     }
     192             : 
     193             :     unsigned int boundaryRectangleTableOffset;
     194          10 :     bool bOK = VSIFReadL(&boundaryRectangleTableOffset,
     195          10 :                          sizeof(boundaryRectangleTableOffset), 1, fp) == 1;
     196          10 :     CPL_MSBPTR32(&boundaryRectangleTableOffset);
     197             : 
     198             :     unsigned short boundaryRectangleCount;
     199          10 :     bOK &= VSIFReadL(&boundaryRectangleCount, sizeof(boundaryRectangleCount), 1,
     200          10 :                      fp) == 1;
     201          10 :     CPL_MSBPTR16(&boundaryRectangleCount);
     202             : 
     203          10 :     if (!bOK || VSIFSeekL(fp, boundaryRectangleTablePhysIndex, SEEK_SET) != 0)
     204             :     {
     205           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     206             :                  "Invalid TOC file. Unable to seek to "
     207             :                  "boundaryRectangleTablePhysIndex at offset %d.",
     208             :                  boundaryRectangleTablePhysIndex);
     209           0 :         return nullptr;
     210             :     }
     211             : 
     212          10 :     RPFToc *toc = static_cast<RPFToc *>(CPLMalloc(sizeof(RPFToc)));
     213          10 :     toc->nEntries = boundaryRectangleCount;
     214          10 :     toc->entries = reinterpret_cast<RPFTocEntry *>(
     215          10 :         CPLMalloc(boundaryRectangleCount * sizeof(RPFTocEntry)));
     216          10 :     memset(toc->entries, 0, boundaryRectangleCount * sizeof(RPFTocEntry));
     217             : 
     218          20 :     for (int i = 0; i < toc->nEntries; i++)
     219             :     {
     220          10 :         toc->entries[i].isOverviewOrLegend = 0;
     221             : 
     222          10 :         bOK &= VSIFReadL(toc->entries[i].type, 1, 5, fp) == 5;
     223          10 :         toc->entries[i].type[5] = 0;
     224          10 :         RPFTOCTrim(toc->entries[i].type);
     225             : 
     226          10 :         bOK &= VSIFReadL(toc->entries[i].compression, 1, 5, fp) == 5;
     227          10 :         toc->entries[i].compression[5] = 0;
     228          10 :         RPFTOCTrim(toc->entries[i].compression);
     229             : 
     230          10 :         bOK &= VSIFReadL(toc->entries[i].scale, 1, 12, fp) == 12;
     231          10 :         toc->entries[i].scale[12] = 0;
     232          10 :         RPFTOCTrim(toc->entries[i].scale);
     233          10 :         if (toc->entries[i].scale[0] == '1' && toc->entries[i].scale[1] == ':')
     234             :         {
     235          10 :             memmove(toc->entries[i].scale, toc->entries[i].scale + 2,
     236          10 :                     strlen(toc->entries[i].scale + 2) + 1);
     237             :         }
     238             : 
     239          10 :         bOK &= VSIFReadL(toc->entries[i].zone, 1, 1, fp) == 1;
     240          10 :         toc->entries[i].zone[1] = 0;
     241          10 :         RPFTOCTrim(toc->entries[i].zone);
     242             : 
     243          10 :         bOK &= VSIFReadL(toc->entries[i].producer, 1, 5, fp) == 5;
     244          10 :         toc->entries[i].producer[5] = 0;
     245          10 :         RPFTOCTrim(toc->entries[i].producer);
     246             : 
     247          10 :         bOK &= VSIFReadL(&toc->entries[i].nwLat, sizeof(double), 1, fp) == 1;
     248          10 :         CPL_MSBPTR64(&toc->entries[i].nwLat);
     249             : 
     250          10 :         bOK &= VSIFReadL(&toc->entries[i].nwLong, sizeof(double), 1, fp) == 1;
     251          10 :         CPL_MSBPTR64(&toc->entries[i].nwLong);
     252             : 
     253          10 :         bOK &= VSIFReadL(&toc->entries[i].swLat, sizeof(double), 1, fp) == 1;
     254          10 :         CPL_MSBPTR64(&toc->entries[i].swLat);
     255             : 
     256          10 :         bOK &= VSIFReadL(&toc->entries[i].swLong, sizeof(double), 1, fp) == 1;
     257          10 :         CPL_MSBPTR64(&toc->entries[i].swLong);
     258             : 
     259          10 :         bOK &= VSIFReadL(&toc->entries[i].neLat, sizeof(double), 1, fp) == 1;
     260          10 :         CPL_MSBPTR64(&toc->entries[i].neLat);
     261             : 
     262          10 :         bOK &= VSIFReadL(&toc->entries[i].neLong, sizeof(double), 1, fp) == 1;
     263          10 :         CPL_MSBPTR64(&toc->entries[i].neLong);
     264             : 
     265          10 :         bOK &= VSIFReadL(&toc->entries[i].seLat, sizeof(double), 1, fp) == 1;
     266          10 :         CPL_MSBPTR64(&toc->entries[i].seLat);
     267             : 
     268          10 :         bOK &= VSIFReadL(&toc->entries[i].seLong, sizeof(double), 1, fp) == 1;
     269          10 :         CPL_MSBPTR64(&toc->entries[i].seLong);
     270             : 
     271          10 :         bOK &= VSIFReadL(&toc->entries[i].vertResolution, sizeof(double), 1,
     272          10 :                          fp) == 1;
     273          10 :         CPL_MSBPTR64(&toc->entries[i].vertResolution);
     274             : 
     275          10 :         bOK &= VSIFReadL(&toc->entries[i].horizResolution, sizeof(double), 1,
     276          10 :                          fp) == 1;
     277          10 :         CPL_MSBPTR64(&toc->entries[i].horizResolution);
     278             : 
     279          10 :         bOK &= VSIFReadL(&toc->entries[i].vertInterval, sizeof(double), 1,
     280          10 :                          fp) == 1;
     281          10 :         CPL_MSBPTR64(&toc->entries[i].vertInterval);
     282             : 
     283          10 :         bOK &= VSIFReadL(&toc->entries[i].horizInterval, sizeof(double), 1,
     284          10 :                          fp) == 1;
     285          10 :         CPL_MSBPTR64(&toc->entries[i].horizInterval);
     286             : 
     287          10 :         bOK &= VSIFReadL(&toc->entries[i].nVertFrames, sizeof(int), 1, fp) == 1;
     288          10 :         CPL_MSBPTR32(&toc->entries[i].nVertFrames);
     289             : 
     290          10 :         bOK &=
     291          10 :             VSIFReadL(&toc->entries[i].nHorizFrames, sizeof(int), 1, fp) == 1;
     292          10 :         CPL_MSBPTR32(&toc->entries[i].nHorizFrames);
     293             : 
     294          10 :         if (!bOK)
     295             :         {
     296           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     297           0 :             toc->entries[i].nVertFrames = 0;
     298           0 :             toc->entries[i].nHorizFrames = 0;
     299           0 :             RPFTOCFree(toc);
     300           0 :             return nullptr;
     301             :         }
     302             : 
     303             :         // do some basic plausibility checks for all entries
     304          30 :         if (toc->entries[i].vertInterval <= 1e-10 ||
     305          10 :             !std::isfinite(toc->entries[i].vertInterval) ||
     306          10 :             toc->entries[i].horizInterval <= 1e-10 ||
     307          10 :             !std::isfinite(toc->entries[i].horizInterval) ||
     308          10 :             toc->entries[i].nHorizFrames == 0 ||
     309          30 :             toc->entries[i].nVertFrames == 0 ||
     310          10 :             toc->entries[i].nHorizFrames >
     311          10 :                 INT_MAX / toc->entries[i].nVertFrames)
     312             :         {
     313           0 :             CPLError(CE_Failure, CPLE_FileIO, "Invalid TOC entry");
     314           0 :             toc->entries[i].nVertFrames = 0;
     315           0 :             toc->entries[i].nHorizFrames = 0;
     316           0 :             RPFTOCFree(toc);
     317           0 :             return nullptr;
     318             :         }
     319             : 
     320             :         // Overview has ZONE 'R' and Legend ZONE 'D' but because the Zone 'D' is
     321             :         // also a valid Zone we need an additional check. -> In all cases of
     322             :         // Overview/Legend the values of the BoundingBox are equal so we simply
     323             :         // check here that NW == SE is.
     324          10 :         toc->entries[i].isOverviewOrLegend =
     325          20 :             (toc->entries[i].zone[0] == 'R' ||   // Overview
     326          10 :              (toc->entries[i].zone[0] == 'D' &&  // Legend
     327           0 :               memcmp(&(toc->entries[i].seLong), &(toc->entries[i].nwLong),
     328           0 :                      sizeof(toc->entries[i].nwLong)) == 0 &&
     329           0 :               memcmp(&(toc->entries[i].seLat), &(toc->entries[i].nwLat),
     330             :                      sizeof(toc->entries[i].nwLat)) == 0));
     331             : 
     332          20 :         bool isPolarZone = (toc->entries[i].zone[0] == '9') ||
     333          10 :                            (toc->entries[i].zone[0] == 'J');
     334             : 
     335             :         // make additional checks of the bounding for charts (without Legends
     336             :         // and Overviews)
     337          10 :         if (!toc->entries[i].isOverviewOrLegend)
     338             :         {
     339          10 :             if (!(fabs(toc->entries[i].seLong) <= 360.0) ||
     340          10 :                 !(fabs(toc->entries[i].nwLong) <= 360.0) ||
     341          10 :                 !(fabs(toc->entries[i].nwLat) <= 90.0) ||
     342          10 :                 !(fabs(toc->entries[i].seLat) <= 90.0) ||
     343             :                 // check only for non-polar zones, because the values are not
     344             :                 // always correct here
     345          10 :                 (!isPolarZone &&
     346          10 :                  (toc->entries[i].seLong < toc->entries[i].nwLong ||
     347          10 :                   toc->entries[i].nwLat < toc->entries[i].seLat)))
     348             :             {
     349           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Invalid TOC entry");
     350           0 :                 toc->entries[i].nVertFrames = 0;
     351           0 :                 toc->entries[i].nHorizFrames = 0;
     352           0 :                 RPFTOCFree(toc);
     353           0 :                 return nullptr;
     354             :             }
     355             :         }
     356             : 
     357             :         // TODO: We could probably use another data structure, like a list,
     358             :         // instead of an array referenced by the frame coordinate...
     359          20 :         if (static_cast<int>(toc->entries[i].nHorizFrames *
     360          20 :                              toc->entries[i].nVertFrames) >
     361          10 :             atoi(CPLGetConfigOption("RPFTOC_MAX_FRAME_COUNT", "1000000")))
     362             :         {
     363           0 :             CPLError(
     364             :                 CE_Failure, CPLE_AppDefined,
     365             :                 "nHorizFrames=%d x nVertFrames=%d > %d. Please raise "
     366             :                 "the value of the RPFTOC_MAX_FRAME_COUNT configuration "
     367             :                 "option to more than %d if this dataset is legitimate.",
     368           0 :                 toc->entries[i].nHorizFrames, toc->entries[i].nVertFrames,
     369             :                 atoi(CPLGetConfigOption("RPFTOC_MAX_FRAME_COUNT", "1000000")),
     370           0 :                 toc->entries[i].nHorizFrames * toc->entries[i].nVertFrames);
     371           0 :             toc->entries[i].frameEntries = nullptr;
     372             :         }
     373             :         else
     374             :         {
     375          20 :             toc->entries[i].frameEntries =
     376          10 :                 static_cast<RPFTocFrameEntry *>(VSI_CALLOC_VERBOSE(
     377             :                     static_cast<size_t>(toc->entries[i].nVertFrames) *
     378             :                         toc->entries[i].nHorizFrames,
     379             :                     sizeof(RPFTocFrameEntry)));
     380             :         }
     381          10 :         if (toc->entries[i].frameEntries == nullptr)
     382             :         {
     383           0 :             toc->entries[i].nVertFrames = 0;
     384           0 :             toc->entries[i].nHorizFrames = 0;
     385           0 :             RPFTOCFree(toc);
     386           0 :             return nullptr;
     387             :         }
     388             : 
     389          10 :         CPLDebug("RPFTOC",
     390             :                  "[%d] type=%s, compression=%s, scale=%s, zone=%s, "
     391             :                  "producer=%s, nVertFrames=%d, nHorizFrames=%d",
     392          10 :                  i, toc->entries[i].type, toc->entries[i].compression,
     393          10 :                  toc->entries[i].scale, toc->entries[i].zone,
     394          10 :                  toc->entries[i].producer, toc->entries[i].nVertFrames,
     395          10 :                  toc->entries[i].nHorizFrames);
     396             :     }
     397             : 
     398          10 :     if (VSIFSeekL(fp, frameFileIndexSectionSubHeaderPhysIndex, SEEK_SET) != 0)
     399             :     {
     400           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     401             :                  "Invalid TOC file. Unable to seek to "
     402             :                  "frameFileIndexSectionSubHeaderPhysIndex at offset %d.",
     403             :                  frameFileIndexSectionSubHeaderPhysIndex);
     404           0 :         RPFTOCFree(toc);
     405           0 :         return nullptr;
     406             :     }
     407             : 
     408             :     /* Skip 1 byte security classification */
     409          10 :     bOK &= VSIFSeekL(fp, 1, SEEK_CUR) == 0;
     410             : 
     411             :     unsigned int frameIndexTableOffset;
     412          10 :     bOK &= VSIFReadL(&frameIndexTableOffset, sizeof(frameIndexTableOffset), 1,
     413          10 :                      fp) == 1;
     414          10 :     CPL_MSBPTR32(&frameIndexTableOffset);
     415             : 
     416             :     unsigned int nFrameFileIndexRecords;
     417          10 :     bOK &= VSIFReadL(&nFrameFileIndexRecords, sizeof(nFrameFileIndexRecords), 1,
     418          10 :                      fp) == 1;
     419          10 :     CPL_MSBPTR32(&nFrameFileIndexRecords);
     420             : 
     421             :     unsigned short nFrameFilePathnameRecords;
     422          10 :     bOK &= VSIFReadL(&nFrameFilePathnameRecords,
     423          10 :                      sizeof(nFrameFilePathnameRecords), 1, fp) == 1;
     424          10 :     CPL_MSBPTR16(&nFrameFilePathnameRecords);
     425             : 
     426             :     unsigned short frameFileIndexRecordLength;
     427          10 :     bOK &= VSIFReadL(&frameFileIndexRecordLength,
     428          10 :                      sizeof(frameFileIndexRecordLength), 1, fp) == 1;
     429          10 :     CPL_MSBPTR16(&frameFileIndexRecordLength);
     430          10 :     if (frameFileIndexRecordLength < 3 * sizeof(short))
     431             :     {
     432           0 :         CPLError(CE_Failure, CPLE_FileIO, "Invalid file");
     433           0 :         RPFTOCFree(toc);
     434           0 :         return nullptr;
     435             :     }
     436             : 
     437          10 :     if (!bOK)
     438             :     {
     439           0 :         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     440           0 :         RPFTOCFree(toc);
     441           0 :         return nullptr;
     442             :     }
     443             : 
     444          10 :     int newBoundaryId = 0;
     445             : 
     446          20 :     for (int i = 0; i < static_cast<int>(nFrameFileIndexRecords); i++)
     447             :     {
     448          10 :         vsi_l_offset nFrameOffset =
     449          10 :             static_cast<vsi_l_offset>(frameFileIndexSubsectionPhysIndex) +
     450          10 :             static_cast<vsi_l_offset>(frameFileIndexRecordLength) * i;
     451          10 :         if (VSIFSeekL(fp, nFrameOffset, SEEK_SET) != 0)
     452             :         {
     453           0 :             CPLError(
     454             :                 CE_Failure, CPLE_NotSupported,
     455             :                 "Invalid TOC file. Unable to seek to "
     456             :                 "frameFileIndexSubsectionPhysIndex(%d) at offset " CPL_FRMT_GUIB
     457             :                 ".",
     458             :                 i, static_cast<GUIntBig>(nFrameOffset));
     459           0 :             RPFTOCFree(toc);
     460           0 :             return nullptr;
     461             :         }
     462             : 
     463             :         unsigned short boundaryId;
     464          10 :         if (VSIFReadL(&boundaryId, sizeof(boundaryId), 1, fp) != 1)
     465             :         {
     466           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     467           0 :             RPFTOCFree(toc);
     468           0 :             return nullptr;
     469             :         }
     470          10 :         CPL_MSBPTR16(&boundaryId);
     471             : 
     472          10 :         if (i == 0 && boundaryId == 0)
     473          10 :             newBoundaryId = 1;
     474          10 :         if (newBoundaryId == 0)
     475           0 :             boundaryId--;
     476             : 
     477          10 :         if (boundaryId >= toc->nEntries)
     478             :         {
     479           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     480             :                      "Invalid TOC file. Bad boundary id (%d) for frame file "
     481             :                      "index %d.",
     482             :                      boundaryId, i);
     483           0 :             RPFTOCFree(toc);
     484           0 :             return nullptr;
     485             :         }
     486             : 
     487          10 :         RPFTocEntry *entry = &toc->entries[boundaryId];
     488          10 :         entry->boundaryId = boundaryId;
     489             : 
     490             :         unsigned short frameRow;
     491          10 :         bOK &= VSIFReadL(&frameRow, sizeof(frameRow), 1, fp) == 1;
     492          10 :         CPL_MSBPTR16(&frameRow);
     493             : 
     494             :         unsigned short frameCol;
     495          10 :         bOK &= VSIFReadL(&frameCol, sizeof(frameCol), 1, fp) == 1;
     496          10 :         CPL_MSBPTR16(&frameCol);
     497          10 :         if (!bOK)
     498             :         {
     499           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     500           0 :             RPFTOCFree(toc);
     501           0 :             return nullptr;
     502             :         }
     503             : 
     504          10 :         if (newBoundaryId == 0)
     505             :         {
     506           0 :             frameRow--;
     507           0 :             frameCol--;
     508             :         }
     509             :         else
     510             :         {
     511             :             /* Trick so that frames are numbered north to south */
     512          10 :             if (entry->nVertFrames - 1 < frameRow)
     513             :             {
     514           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     515             :                          "Invalid nVertFrames vs frameRow");
     516           0 :                 RPFTOCFree(toc);
     517           0 :                 return nullptr;
     518             :             }
     519          10 :             frameRow = static_cast<unsigned short>((entry->nVertFrames - 1) -
     520             :                                                    frameRow);
     521             :         }
     522             : 
     523          10 :         if (frameRow >= entry->nVertFrames)
     524             :         {
     525           0 :             CPLError(
     526             :                 CE_Failure, CPLE_NotSupported,
     527             :                 "Invalid TOC file. Bad row num (%d) for frame file index %d.",
     528             :                 frameRow, i);
     529           0 :             RPFTOCFree(toc);
     530           0 :             return nullptr;
     531             :         }
     532             : 
     533          10 :         if (frameCol >= entry->nHorizFrames)
     534             :         {
     535           0 :             CPLError(
     536             :                 CE_Failure, CPLE_NotSupported,
     537             :                 "Invalid TOC file. Bad col num (%d) for frame file index %d.",
     538             :                 frameCol, i);
     539           0 :             RPFTOCFree(toc);
     540           0 :             return nullptr;
     541             :         }
     542             : 
     543          10 :         RPFTocFrameEntry *frameEntry =
     544          10 :             &entry->frameEntries[frameRow * entry->nHorizFrames + frameCol];
     545          10 :         frameEntry->frameRow = frameRow;
     546          10 :         frameEntry->frameCol = frameCol;
     547             : 
     548          10 :         if (frameEntry->exists)
     549             :         {
     550           0 :             CPLError(
     551             :                 CE_Warning, CPLE_AppDefined,
     552             :                 "Frame entry(%d,%d) for frame file index %d was already found.",
     553             :                 frameRow, frameCol, i);
     554           0 :             CPLFree(frameEntry->directory);
     555           0 :             frameEntry->directory = nullptr;
     556           0 :             CPLFree(frameEntry->fullFilePath);
     557           0 :             frameEntry->fullFilePath = nullptr;
     558           0 :             frameEntry->exists = 0;
     559             :         }
     560             : 
     561             :         unsigned int offsetFrameFilePathName;
     562          10 :         bOK &= VSIFReadL(&offsetFrameFilePathName,
     563          10 :                          sizeof(offsetFrameFilePathName), 1, fp) == 1;
     564          10 :         CPL_MSBPTR32(&offsetFrameFilePathName);
     565             : 
     566          10 :         bOK &= VSIFReadL(frameEntry->filename, 1, 12, fp) == 12;
     567          10 :         if (!bOK)
     568             :         {
     569           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     570           0 :             RPFTOCFree(toc);
     571           0 :             return nullptr;
     572             :         }
     573          10 :         frameEntry->filename[12] = '\0';
     574          10 :         bOK &= strlen(frameEntry->filename) > 0;
     575             : 
     576          10 :         if (CPLHasPathTraversal(frameEntry->filename))
     577             :         {
     578           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     579           0 :                      "Path traversal detected in %s", frameEntry->filename);
     580           0 :             RPFTOCFree(toc);
     581           0 :             return nullptr;
     582             :         }
     583             : 
     584             :         // Check (case insensitive) if the filename is an overview or legend
     585             :         // some CADRG maps have legend name smaller than 8.3 then the extension
     586             :         // has blanks (0x20) at the end -> check only the first 3 letters of the
     587             :         // extension.
     588          10 :         const std::string fileExt = CPLGetExtensionSafe(frameEntry->filename);
     589          20 :         if (EQUALN(fileExt.c_str(), "ovr", 3) ||
     590          10 :             EQUALN(fileExt.c_str(), "lgd", 3))
     591             :         {
     592           0 :             entry->isOverviewOrLegend = TRUE;
     593             :         }
     594             : 
     595             :         /* Extract series code */
     596          10 :         if (entry->seriesAbbreviation == nullptr)
     597             :         {
     598          10 :             const NITFSeries *series = NITFGetSeriesInfo(frameEntry->filename);
     599          10 :             if (series)
     600             :             {
     601          10 :                 entry->seriesAbbreviation = series->abbreviation;
     602          10 :                 entry->seriesName = series->name;
     603             :             }
     604             :         }
     605             : 
     606             :         /* Get file geo reference */
     607          10 :         bOK &= VSIFReadL(frameEntry->georef, 1, 6, fp) == 6;
     608          10 :         frameEntry->georef[6] = '\0';
     609             : 
     610             :         /* Go to start of pathname record */
     611             :         /* New path_off offset from start of frame file index section of TOC??
     612             :          */
     613             :         /* Add pathoffset wrt frame file index table subsection (loc[3]) */
     614          20 :         if (!bOK || VSIFSeekL(fp,
     615          10 :                               static_cast<vsi_l_offset>(
     616             :                                   frameFileIndexSubsectionPhysIndex) +
     617          10 :                                   offsetFrameFilePathName,
     618             :                               SEEK_SET) != 0)
     619             :         {
     620           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     621             :                      "Invalid TOC file. Unable to seek to "
     622             :                      "frameFileIndexSubsectionPhysIndex + "
     623             :                      "offsetFrameFilePathName(%d) at offset " CPL_FRMT_GUIB ".",
     624             :                      i,
     625           0 :                      static_cast<GUIntBig>(frameFileIndexSubsectionPhysIndex) +
     626           0 :                          offsetFrameFilePathName);
     627           0 :             RPFTOCFree(toc);
     628           0 :             return nullptr;
     629             :         }
     630             : 
     631             :         unsigned short pathLength;
     632          10 :         bOK &= VSIFReadL(&pathLength, sizeof(pathLength), 1, fp) == 1;
     633          10 :         CPL_MSBPTR16(&pathLength);
     634             : 
     635             :         /* if nFrameFileIndexRecords == 65535 and pathLength == 65535 for each
     636             :            record, this leads to 4 GB allocation... Protect against this case */
     637          10 :         if (!bOK || pathLength == 0 || pathLength > 256)
     638             :         {
     639           0 :             CPLError(
     640             :                 CE_Failure, CPLE_NotSupported,
     641             :                 "Path length is invalid : %d. Probably corrupted TOC file.",
     642             :                 static_cast<int>(pathLength));
     643           0 :             RPFTOCFree(toc);
     644           0 :             return nullptr;
     645             :         }
     646             : 
     647          10 :         frameEntry->directory = static_cast<char *>(CPLMalloc(pathLength + 1));
     648          10 :         bOK &=
     649          10 :             VSIFReadL(frameEntry->directory, 1, pathLength, fp) == pathLength;
     650          10 :         if (!bOK)
     651             :         {
     652           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     653           0 :             RPFTOCFree(toc);
     654           0 :             return nullptr;
     655             :         }
     656          10 :         frameEntry->directory[pathLength] = 0;
     657          10 :         if (frameEntry->directory[pathLength - 1] == '/')
     658          10 :             frameEntry->directory[pathLength - 1] = 0;
     659             : 
     660          10 :         if (CPLHasPathTraversal(frameEntry->directory))
     661             :         {
     662           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     663             :                      "Path traversal detected in %s", frameEntry->directory);
     664           0 :             RPFTOCFree(toc);
     665           0 :             return nullptr;
     666             :         }
     667             : 
     668          10 :         if (frameEntry->directory[0] == '.' && frameEntry->directory[1] == '/')
     669             :         {
     670           0 :             memmove(frameEntry->directory, frameEntry->directory + 2,
     671           0 :                     strlen(frameEntry->directory + 2) + 1);
     672             : 
     673             :             // Some A.TOC have subdirectory names like ".//X/" ... (#5979)
     674             :             // Check if it was not intended to be "./X/" instead.
     675             :             VSIStatBufL sStatBuf;
     676           0 :             if (frameEntry->directory[0] == '/' &&
     677           0 :                 VSIStatL(
     678           0 :                     CPLFormFilenameSafe(CPLGetDirnameSafe(pszFilename).c_str(),
     679           0 :                                         frameEntry->directory + 1, nullptr)
     680             :                         .c_str(),
     681           0 :                     &sStatBuf) == 0 &&
     682           0 :                 VSI_ISDIR(sStatBuf.st_mode))
     683             :             {
     684           0 :                 memmove(frameEntry->directory, frameEntry->directory + 1,
     685           0 :                         strlen(frameEntry->directory + 1) + 1);
     686             :             }
     687             :         }
     688             : 
     689             :         {
     690          10 :             char *baseDir = CPLStrdup(CPLGetDirnameSafe(pszFilename).c_str());
     691             :             VSIStatBufL sStatBuf;
     692          10 :             char *subdir = nullptr;
     693          10 :             if (CPLIsFilenameRelative(frameEntry->directory) == FALSE)
     694           0 :                 subdir = CPLStrdup(frameEntry->directory);
     695          10 :             else if (frameEntry->directory[0] == '.' &&
     696          10 :                      frameEntry->directory[1] == 0)
     697          10 :                 subdir = CPLStrdup(baseDir);
     698             :             else
     699           0 :                 subdir = CPLStrdup(
     700           0 :                     CPLFormFilenameSafe(baseDir, frameEntry->directory, nullptr)
     701             :                         .c_str());
     702             : #if !defined(_WIN32) && !defined(_WIN32_CE)
     703          10 :             if (VSIStatL(subdir, &sStatBuf) != 0 &&
     704           0 :                 strlen(subdir) > strlen(baseDir))
     705             :             {
     706           0 :                 char *c = subdir + strlen(baseDir) + 1;
     707           0 :                 while (*c)
     708             :                 {
     709           0 :                     if (*c >= 'A' && *c <= 'Z')
     710           0 :                         *c += 'a' - 'A';
     711           0 :                     c++;
     712             :                 }
     713             :             }
     714             : #endif
     715          10 :             frameEntry->fullFilePath = CPLStrdup(
     716          20 :                 CPLFormFilenameSafe(subdir, frameEntry->filename, nullptr)
     717             :                     .c_str());
     718          10 :             if (VSIStatL(frameEntry->fullFilePath, &sStatBuf) != 0)
     719             :             {
     720             : #if !defined(_WIN32) && !defined(_WIN32_CE)
     721           0 :                 if (strlen(frameEntry->fullFilePath) > strlen(subdir))
     722             :                 {
     723           0 :                     char *c = frameEntry->fullFilePath + strlen(subdir) + 1;
     724           0 :                     while (*c)
     725             :                     {
     726           0 :                         if (*c >= 'A' && *c <= 'Z')
     727           0 :                             *c += 'a' - 'A';
     728           0 :                         c++;
     729             :                     }
     730             :                 }
     731           0 :                 if (VSIStatL(frameEntry->fullFilePath, &sStatBuf) != 0)
     732             : #endif
     733             :                 {
     734           0 :                     frameEntry->fileExists = 0;
     735           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     736             :                              "File %s does not exist.",
     737             :                              frameEntry->fullFilePath);
     738             :                 }
     739             : #if !defined(_WIN32) && !defined(_WIN32_CE)
     740             :                 else
     741             :                 {
     742           0 :                     frameEntry->fileExists = 1;
     743             :                 }
     744             : #endif
     745             :             }
     746             :             else
     747             :             {
     748          10 :                 frameEntry->fileExists = 1;
     749             :             }
     750          10 :             CPLFree(subdir);
     751          10 :             CPLFree(baseDir);
     752             :         }
     753             : 
     754          10 :         CPLDebug("RPFTOC", "Entry %d : %s,%s (%d, %d)", boundaryId,
     755          10 :                  frameEntry->directory, frameEntry->filename, frameRow,
     756             :                  frameCol);
     757             : 
     758          10 :         frameEntry->exists = 1;
     759             :     }
     760             : 
     761          10 :     return toc;
     762             : }
     763             : 
     764             : /************************************************************************/
     765             : /*                        RPFTOCFree()                                 */
     766             : /************************************************************************/
     767             : 
     768          10 : void RPFTOCFree(RPFToc *toc)
     769             : {
     770          10 :     if (!toc)
     771           0 :         return;
     772             : 
     773          20 :     for (int i = 0; i < toc->nEntries; i++)
     774             :     {
     775          20 :         for (int j = 0; j < static_cast<int>(toc->entries[i].nVertFrames *
     776          20 :                                              toc->entries[i].nHorizFrames);
     777             :              j++)
     778             :         {
     779          10 :             CPLFree(toc->entries[i].frameEntries[j].fullFilePath);
     780          10 :             CPLFree(toc->entries[i].frameEntries[j].directory);
     781             :         }
     782          10 :         CPLFree(toc->entries[i].frameEntries);
     783             :     }
     784             : 
     785          10 :     CPLFree(toc->entries);
     786          10 :     CPLFree(toc);
     787             : }

Generated by: LCOV version 1.14