LCOV - code coverage report
Current view: top level - frmts/hfa - hfaentry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 328 390 84.1 %
Date: 2025-08-19 18:03:11 Functions: 31 32 96.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Erdas Imagine (.img) Translator
       4             :  * Purpose:  Implementation of the HFAEntry class for reading and relating
       5             :  *           one node in the HFA object tree structure.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 1999, Intergraph Corporation
      10             :  * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ******************************************************************************
      14             :  *
      15             :  * hfaentry.cpp
      16             :  *
      17             :  * Implementation of the HFAEntry class.
      18             :  *
      19             :  */
      20             : 
      21             : #include "cpl_port.h"
      22             : #include "hfa_p.h"
      23             : 
      24             : #include <cerrno>
      25             : #include <climits>
      26             : #include <cstddef>
      27             : #include <cstdio>
      28             : #include <cstring>
      29             : #include <vector>
      30             : 
      31             : #include "cpl_conv.h"
      32             : #include "cpl_error.h"
      33             : #include "cpl_string.h"
      34             : #include "cpl_vsi.h"
      35             : 
      36             : /************************************************************************/
      37             : /*                              HFAEntry()                              */
      38             : /************************************************************************/
      39             : 
      40        5117 : HFAEntry::HFAEntry()
      41             :     : bDirty(false), nFilePos(0), psHFA(nullptr), poParent(nullptr),
      42             :       poPrev(nullptr), nNextPos(0), poNext(nullptr), nChildPos(0),
      43             :       poChild(nullptr), poType(nullptr), nDataPos(0), nDataSize(0),
      44        5117 :       pabyData(nullptr), bIsMIFObject(false)
      45             : {
      46        5117 :     szName[0] = '\0';
      47        5117 :     szType[0] = '\0';
      48        5117 : }
      49             : 
      50             : /************************************************************************/
      51             : /*                              HFAEntry()                              */
      52             : /*                                                                      */
      53             : /*      Construct an HFAEntry from the source file.                     */
      54             : /************************************************************************/
      55             : 
      56        5117 : HFAEntry *HFAEntry::New(HFAInfo_t *psHFAIn, GUInt32 nPos, HFAEntry *poParentIn,
      57             :                         HFAEntry *poPrevIn)
      58             : 
      59             : {
      60        5117 :     HFAEntry *poEntry = new HFAEntry;
      61        5117 :     poEntry->psHFA = psHFAIn;
      62             : 
      63        5117 :     poEntry->nFilePos = nPos;
      64        5117 :     poEntry->poParent = poParentIn;
      65        5117 :     poEntry->poPrev = poPrevIn;
      66             : 
      67             :     // Read the entry information from the file.
      68        5117 :     GInt32 anEntryNums[6] = {};
      69             : 
      70       10234 :     if (VSIFSeekL(poEntry->psHFA->fp, poEntry->nFilePos, SEEK_SET) == -1 ||
      71        5117 :         VSIFReadL(anEntryNums, sizeof(GInt32) * 6, 1, poEntry->psHFA->fp) < 1)
      72             :     {
      73           4 :         CPLError(CE_Failure, CPLE_FileIO,
      74             :                  "VSIFReadL(%p,6*4) @ %u failed in HFAEntry().\n%s",
      75           2 :                  poEntry->psHFA->fp, poEntry->nFilePos, VSIStrerror(errno));
      76           2 :         delete poEntry;
      77           2 :         return nullptr;
      78             :     }
      79             : 
      80       35805 :     for (int i = 0; i < 6; i++)
      81             :         HFAStandard(4, anEntryNums + i);
      82             : 
      83        5115 :     poEntry->nNextPos = anEntryNums[0];
      84        5115 :     poEntry->nChildPos = anEntryNums[3];
      85        5115 :     poEntry->nDataPos = anEntryNums[4];
      86        5115 :     poEntry->nDataSize = anEntryNums[5];
      87             : 
      88             :     // Read the name, and type.
      89       10230 :     if (VSIFReadL(poEntry->szName, 64, 1, poEntry->psHFA->fp) < 1 ||
      90        5115 :         VSIFReadL(poEntry->szType, 32, 1, poEntry->psHFA->fp) < 1)
      91             :     {
      92           0 :         poEntry->szName[sizeof(poEntry->szName) - 1] = '\0';
      93           0 :         poEntry->szType[sizeof(poEntry->szType) - 1] = '\0';
      94           0 :         CPLError(CE_Failure, CPLE_FileIO, "VSIFReadL() failed in HFAEntry().");
      95           0 :         delete poEntry;
      96           0 :         return nullptr;
      97             :     }
      98        5115 :     poEntry->szName[sizeof(poEntry->szName) - 1] = '\0';
      99        5115 :     poEntry->szType[sizeof(poEntry->szType) - 1] = '\0';
     100        5115 :     return poEntry;
     101             : }
     102             : 
     103             : /************************************************************************/
     104             : /*                              HFAEntry()                              */
     105             : /*                                                                      */
     106             : /*      Construct an HFAEntry in memory, with the intention that it     */
     107             : /*      would be written to disk later.                                 */
     108             : /************************************************************************/
     109             : 
     110        2006 : HFAEntry::HFAEntry(HFAInfo_t *psHFAIn, const char *pszNodeName,
     111        2006 :                    const char *pszTypeName, HFAEntry *poParentIn)
     112             :     : nFilePos(0), psHFA(psHFAIn), poParent(poParentIn), poPrev(nullptr),
     113             :       nNextPos(0), poNext(nullptr), nChildPos(0), poChild(nullptr),
     114             :       poType(nullptr), nDataPos(0), nDataSize(0), pabyData(nullptr),
     115        2006 :       bIsMIFObject(false)
     116             : {
     117             :     // Initialize Entry.
     118        2006 :     SetName(pszNodeName);
     119        2006 :     memset(szType, 0, sizeof(szType));
     120        2006 :     snprintf(szType, sizeof(szType), "%s", pszTypeName);
     121             : 
     122             :     // Update the previous or parent node to refer to this one.
     123        2006 :     if (poParent == nullptr)
     124             :     {
     125             :         // Do nothing.
     126             :     }
     127        1812 :     else if (poParent->poChild == nullptr)
     128             :     {
     129         655 :         poParent->poChild = this;
     130         655 :         poParent->MarkDirty();
     131             :     }
     132             :     else
     133             :     {
     134        1157 :         poPrev = poParent->poChild;
     135        2565 :         while (poPrev->poNext != nullptr)
     136        1408 :             poPrev = poPrev->poNext;
     137             : 
     138        1157 :         poPrev->poNext = this;
     139        1157 :         poPrev->MarkDirty();
     140             :     }
     141             : 
     142        2006 :     MarkDirty();
     143        2006 : }
     144             : 
     145             : /************************************************************************/
     146             : /*                              New()                                   */
     147             : /*                                                                      */
     148             : /*      Construct an HFAEntry in memory, with the intention that it     */
     149             : /*      would be written to disk later.                                 */
     150             : /************************************************************************/
     151             : 
     152        1812 : HFAEntry *HFAEntry::New(HFAInfo_t *psHFAIn, const char *pszNodeName,
     153             :                         const char *pszTypeName, HFAEntry *poParentIn)
     154             : {
     155        1812 :     CPLAssert(poParentIn != nullptr);
     156        1812 :     return new HFAEntry(psHFAIn, pszNodeName, pszTypeName, poParentIn);
     157             : }
     158             : 
     159             : /************************************************************************/
     160             : /*                      BuildEntryFromMIFObject()                       */
     161             : /*                                                                      */
     162             : /*      Create a pseudo-HFAEntry wrapping a MIFObject.                  */
     163             : /************************************************************************/
     164             : 
     165           2 : HFAEntry *HFAEntry::BuildEntryFromMIFObject(HFAEntry *poContainer,
     166             :                                             const char *pszMIFObjectPath)
     167             : {
     168           4 :     CPLString osFieldName;
     169             : 
     170           2 :     osFieldName.Printf("%s.%s", pszMIFObjectPath, "MIFDictionary");
     171           2 :     const char *pszField = poContainer->GetStringField(osFieldName.c_str());
     172           2 :     if (pszField == nullptr)
     173             :     {
     174           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry",
     175             :                  osFieldName.c_str());
     176           0 :         return nullptr;
     177             :     }
     178           4 :     CPLString osDictionary = pszField;
     179             : 
     180           2 :     osFieldName.Printf("%s.%s", pszMIFObjectPath, "type.string");
     181           2 :     pszField = poContainer->GetStringField(osFieldName.c_str());
     182           2 :     if (pszField == nullptr)
     183             :     {
     184           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry",
     185             :                  osFieldName.c_str());
     186           0 :         return nullptr;
     187             :     }
     188           4 :     CPLString osType = pszField;
     189             : 
     190           2 :     osFieldName.Printf("%s.%s", pszMIFObjectPath, "MIFObject");
     191           2 :     int nRemainingDataSize = 0;
     192           2 :     pszField = poContainer->GetStringField(osFieldName.c_str(), nullptr,
     193             :                                            &nRemainingDataSize);
     194           2 :     if (pszField == nullptr)
     195             :     {
     196           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry",
     197             :                  osFieldName.c_str());
     198           0 :         return nullptr;
     199             :     }
     200             : 
     201           2 :     GInt32 nMIFObjectSize = 0;
     202             :     // We rudely look before the field data to get at the pointer/size info.
     203           2 :     memcpy(&nMIFObjectSize, pszField - 8, 4);
     204             :     HFAStandard(4, &nMIFObjectSize);
     205           2 :     if (nMIFObjectSize <= 0)
     206             :     {
     207           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid MIF object size (%d)",
     208             :                  nMIFObjectSize);
     209           0 :         return nullptr;
     210             :     }
     211             : 
     212             :     // Check that we won't copy more bytes than available in the buffer.
     213           2 :     if (nMIFObjectSize > nRemainingDataSize)
     214             :     {
     215           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     216             :                  "Invalid MIF object size (%d > %d)", nMIFObjectSize,
     217             :                  nRemainingDataSize);
     218           0 :         return nullptr;
     219             :     }
     220             : 
     221           2 :     GByte *l_pabyData = static_cast<GByte *>(VSIMalloc(nMIFObjectSize));
     222           2 :     if (l_pabyData == nullptr)
     223           0 :         return nullptr;
     224             : 
     225           2 :     memcpy(l_pabyData, pszField, nMIFObjectSize);
     226             : 
     227           2 :     return new HFAEntry(osDictionary, osType, nMIFObjectSize, l_pabyData);
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                              HFAEntry()                              */
     232             : /*                                                                      */
     233             : /*      Create a pseudo-HFAEntry wrapping a MIFObject.                  */
     234             : /************************************************************************/
     235             : 
     236           2 : HFAEntry::HFAEntry(const char *pszDictionary, const char *pszTypeName,
     237           2 :                    int nDataSizeIn, GByte *pabyDataIn)
     238             :     : bDirty(false), nFilePos(0), poParent(nullptr), poPrev(nullptr),
     239             :       nNextPos(0), poNext(nullptr), nChildPos(0), poChild(nullptr), nDataPos(0),
     240           2 :       nDataSize(0), bIsMIFObject(true)
     241             : {
     242             :     // Initialize Entry
     243           2 :     memset(szName, 0, sizeof(szName));
     244             : 
     245             :     // Create a dummy HFAInfo_t.
     246           2 :     psHFA = static_cast<HFAInfo_t *>(CPLCalloc(sizeof(HFAInfo_t), 1));
     247             : 
     248           2 :     psHFA->eAccess = HFA_ReadOnly;
     249           2 :     psHFA->bTreeDirty = false;
     250           2 :     psHFA->poRoot = this;
     251             : 
     252           2 :     psHFA->poDictionary = new HFADictionary(pszDictionary);
     253             : 
     254             :     // Work out the type for this MIFObject.
     255           2 :     memset(szType, 0, sizeof(szType));
     256           2 :     snprintf(szType, sizeof(szType), "%s", pszTypeName);
     257             : 
     258           2 :     poType = psHFA->poDictionary->FindType(szType);
     259             : 
     260           2 :     nDataSize = nDataSizeIn;
     261           2 :     pabyData = pabyDataIn;
     262           2 : }
     263             : 
     264             : /************************************************************************/
     265             : /*                             ~HFAEntry()                              */
     266             : /*                                                                      */
     267             : /*      Ensure that children are cleaned up when this node is           */
     268             : /*      cleaned up.                                                     */
     269             : /************************************************************************/
     270             : 
     271       21375 : HFAEntry::~HFAEntry()
     272             : 
     273             : {
     274        7125 :     CPLFree(pabyData);
     275             : 
     276        7125 :     if (poNext != nullptr)
     277        4076 :         delete poNext;
     278             : 
     279        7125 :     if (poChild != nullptr)
     280        2287 :         delete poChild;
     281             : 
     282        7125 :     if (bIsMIFObject)
     283             :     {
     284           2 :         delete psHFA->poDictionary;
     285           2 :         CPLFree(psHFA);
     286             :     }
     287       14250 : }
     288             : 
     289             : /************************************************************************/
     290             : /*                          RemoveAndDestroy()                          */
     291             : /*                                                                      */
     292             : /*      Removes this entry, and its children from the current           */
     293             : /*      tree.  The parent and/or siblings are appropriately updated     */
     294             : /*      so that they will be flushed back to disk without the           */
     295             : /*      reference to this node.                                         */
     296             : /************************************************************************/
     297             : 
     298           9 : CPLErr HFAEntry::RemoveAndDestroy()
     299             : 
     300             : {
     301           9 :     if (poPrev != nullptr)
     302             :     {
     303           9 :         poPrev->poNext = poNext;
     304           9 :         if (poNext != nullptr)
     305           7 :             poPrev->nNextPos = poNext->nFilePos;
     306             :         else
     307           2 :             poPrev->nNextPos = 0;
     308           9 :         poPrev->MarkDirty();
     309             :     }
     310           9 :     if (poParent != nullptr && poParent->poChild == this)
     311             :     {
     312           0 :         poParent->poChild = poNext;
     313           0 :         if (poNext)
     314           0 :             poParent->nChildPos = poNext->nFilePos;
     315             :         else
     316           0 :             poParent->nChildPos = 0;
     317           0 :         poParent->MarkDirty();
     318             :     }
     319             : 
     320           9 :     if (poNext != nullptr)
     321             :     {
     322           7 :         poNext->poPrev = poPrev;
     323             :     }
     324             : 
     325           9 :     poNext = nullptr;
     326           9 :     poPrev = nullptr;
     327           9 :     poParent = nullptr;
     328             : 
     329           9 :     delete this;
     330             : 
     331           9 :     return CE_None;
     332             : }
     333             : 
     334             : /************************************************************************/
     335             : /*                              SetName()                               */
     336             : /*                                                                      */
     337             : /*    Changes the name assigned to this node                            */
     338             : /************************************************************************/
     339             : 
     340        2013 : void HFAEntry::SetName(const char *pszNodeName)
     341             : {
     342        2013 :     memset(szName, 0, sizeof(szName));
     343        2013 :     snprintf(szName, sizeof(szName), "%s", pszNodeName);
     344             : 
     345        2013 :     MarkDirty();
     346        2013 : }
     347             : 
     348             : /************************************************************************/
     349             : /*                              GetChild()                              */
     350             : /************************************************************************/
     351             : 
     352       22359 : HFAEntry *HFAEntry::GetChild()
     353             : 
     354             : {
     355             :     // Do we need to create the child node?
     356       22359 :     if (poChild == nullptr && nChildPos != 0)
     357             :     {
     358        1634 :         poChild = HFAEntry::New(psHFA, nChildPos, this, nullptr);
     359        1634 :         if (poChild == nullptr)
     360           2 :             nChildPos = 0;
     361             :     }
     362             : 
     363       22359 :     return poChild;
     364             : }
     365             : 
     366             : /************************************************************************/
     367             : /*                              GetNext()                               */
     368             : /************************************************************************/
     369             : 
     370       64335 : HFAEntry *HFAEntry::GetNext()
     371             : 
     372             : {
     373             :     // Do we need to create the next node?
     374       64335 :     if (poNext == nullptr && nNextPos != 0)
     375             :     {
     376             :         // Check if we have a loop on the next node in this sibling chain.
     377             :         HFAEntry *poPast;
     378             : 
     379        9988 :         for (poPast = this; poPast != nullptr && poPast->nFilePos != nNextPos;
     380        7059 :              poPast = poPast->poPrev)
     381             :         {
     382             :         }
     383             : 
     384        2929 :         if (poPast != nullptr)
     385             :         {
     386           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     387             :                      "Corrupt (looping) entry in %s, "
     388             :                      "ignoring some entries after %s.",
     389           1 :                      psHFA->pszFilename, szName);
     390           1 :             nNextPos = 0;
     391           1 :             return nullptr;
     392             :         }
     393             : 
     394        2928 :         poNext = HFAEntry::New(psHFA, nNextPos, poParent, this);
     395        2928 :         if (poNext == nullptr)
     396           0 :             nNextPos = 0;
     397             :     }
     398             : 
     399       64334 :     return poNext;
     400             : }
     401             : 
     402             : /************************************************************************/
     403             : /*                              LoadData()                              */
     404             : /*                                                                      */
     405             : /*      Load the data for this entry, and build up the field            */
     406             : /*      information for it.                                             */
     407             : /************************************************************************/
     408             : 
     409       57661 : void HFAEntry::LoadData()
     410             : 
     411             : {
     412       57661 :     if (pabyData != nullptr || nDataSize == 0)
     413       54767 :         return;
     414        2894 :     if (nDataSize > INT_MAX - 1)
     415             :     {
     416           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     417             :                  "Invalid value for nDataSize = %u", nDataSize);
     418           0 :         return;
     419             :     }
     420             : 
     421             :     // Allocate buffer, and read data.
     422        2894 :     pabyData = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nDataSize + 1));
     423        2894 :     if (pabyData == nullptr)
     424             :     {
     425           0 :         return;
     426             :     }
     427             : 
     428        2894 :     if (VSIFSeekL(psHFA->fp, nDataPos, SEEK_SET) < 0)
     429             :     {
     430           0 :         CPLError(CE_Failure, CPLE_FileIO,
     431             :                  "VSIFSeekL() failed in HFAEntry::LoadData().");
     432           0 :         return;
     433             :     }
     434             : 
     435        2894 :     if (VSIFReadL(pabyData, nDataSize, 1, psHFA->fp) < 1)
     436             :     {
     437           0 :         CPLError(CE_Failure, CPLE_FileIO,
     438             :                  "VSIFReadL() failed in HFAEntry::LoadData().");
     439           0 :         return;
     440             :     }
     441             : 
     442             :     // Make sure the buffer is always null terminated to avoid
     443             :     // issues when extracting strings from a corrupted file.
     444        2894 :     pabyData[nDataSize] = '\0';
     445             : 
     446             :     // Get the type corresponding to this entry.
     447        2894 :     poType = psHFA->poDictionary->FindType(szType);
     448        2894 :     if (poType == nullptr)
     449           0 :         return;
     450             : }
     451             : 
     452             : /************************************************************************/
     453             : /*                           GetTypeObject()                            */
     454             : /************************************************************************/
     455             : 
     456         169 : HFAType *HFAEntry::GetTypeObject()
     457             : 
     458             : {
     459         169 :     if (poType == nullptr)
     460         169 :         poType = psHFA->poDictionary->FindType(szType);
     461             : 
     462         169 :     return poType;
     463             : }
     464             : 
     465             : /************************************************************************/
     466             : /*                              MakeData()                              */
     467             : /*                                                                      */
     468             : /*      Create a data block on the this HFAEntry in memory.  By         */
     469             : /*      default it will create the data the correct size for fixed      */
     470             : /*      sized types, or do nothing for variable length types.           */
     471             : /*      However, the caller can supply a desired size for variable      */
     472             : /*      sized fields.                                                   */
     473             : /************************************************************************/
     474             : 
     475       13420 : GByte *HFAEntry::MakeData(int nSize)
     476             : 
     477             : {
     478       13420 :     if (poType == nullptr)
     479             :     {
     480        1644 :         poType = psHFA->poDictionary->FindType(szType);
     481        1644 :         if (poType == nullptr)
     482           0 :             return nullptr;
     483             :     }
     484             : 
     485       13420 :     if (nSize == 0 && poType->nBytes > 0)
     486        2986 :         nSize = poType->nBytes;
     487             : 
     488             :     // nDataSize is a GUInt32.
     489       13420 :     if (static_cast<int>(nDataSize) < nSize && nSize > 0)
     490             :     {
     491        1816 :         pabyData = static_cast<GByte *>(CPLRealloc(pabyData, nSize));
     492        1816 :         memset(pabyData + nDataSize, 0, nSize - nDataSize);
     493        1816 :         nDataSize = nSize;
     494             : 
     495        1816 :         MarkDirty();
     496             : 
     497             :         // If the data already had a file position, we now need to
     498             :         // clear that, forcing it to be rewritten at the end of the
     499             :         // file.  Referencing nodes will need to be marked dirty so
     500             :         // they are rewritten.
     501        1816 :         if (nFilePos != 0)
     502             :         {
     503           5 :             nFilePos = 0;
     504           5 :             nDataPos = 0;
     505           5 :             if (poPrev != nullptr)
     506           2 :                 poPrev->MarkDirty();
     507           5 :             if (poNext != nullptr)
     508           4 :                 poNext->MarkDirty();
     509           5 :             if (poChild != nullptr)
     510           0 :                 poChild->MarkDirty();
     511           5 :             if (poParent != nullptr)
     512           5 :                 poParent->MarkDirty();
     513             :         }
     514             :     }
     515             :     else
     516             :     {
     517       11604 :         LoadData();  // Make sure the data is loaded before we return pointer.
     518             :     }
     519             : 
     520       13420 :     return pabyData;
     521             : }
     522             : 
     523             : /************************************************************************/
     524             : /*                          DumpFieldValues()                           */
     525             : /************************************************************************/
     526             : 
     527           0 : void HFAEntry::DumpFieldValues(FILE *fp, const char *pszPrefix)
     528             : 
     529             : {
     530           0 :     if (pszPrefix == nullptr)
     531           0 :         pszPrefix = "";
     532             : 
     533           0 :     LoadData();
     534             : 
     535           0 :     if (pabyData == nullptr || poType == nullptr)
     536           0 :         return;
     537             : 
     538           0 :     poType->DumpInstValue(fp, pabyData, nDataPos, nDataSize, pszPrefix);
     539             : }
     540             : 
     541             : /************************************************************************/
     542             : /*                            FindChildren()                            */
     543             : /*                                                                      */
     544             : /*      Find all the children of the current node that match the        */
     545             : /*      name and type provided.  Either may be NULL if it is not a      */
     546             : /*      factor.  The pszName should be just the node name, not a        */
     547             : /*      path.                                                           */
     548             : /************************************************************************/
     549             : 
     550        1167 : std::vector<HFAEntry *> HFAEntry::FindChildren(const char *pszName,
     551             :                                                const char *pszType,
     552             :                                                int nRecLevel,
     553             :                                                int *pbErrorDetected)
     554             : 
     555             : {
     556        1167 :     std::vector<HFAEntry *> apoChildren;
     557             : 
     558        1167 :     if (*pbErrorDetected)
     559           0 :         return apoChildren;
     560        1167 :     if (nRecLevel == 50)
     561             :     {
     562           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     563             :                  "Bad entry structure: recursion detected !");
     564           0 :         *pbErrorDetected = TRUE;
     565           0 :         return apoChildren;
     566             :     }
     567             : 
     568        2211 :     for (HFAEntry *poEntry = GetChild(); poEntry != nullptr;
     569        1044 :          poEntry = poEntry->GetNext())
     570             :     {
     571        1044 :         std::vector<HFAEntry *> apoEntryChildren;
     572             : 
     573        1992 :         if ((pszName == nullptr || EQUAL(poEntry->GetName(), pszName)) &&
     574         948 :             (pszType == nullptr || EQUAL(poEntry->GetType(), pszType)))
     575          40 :             apoChildren.push_back(poEntry);
     576             : 
     577        2088 :         apoEntryChildren = poEntry->FindChildren(
     578        1044 :             pszName, pszType, nRecLevel + 1, pbErrorDetected);
     579        1044 :         if (*pbErrorDetected)
     580           0 :             return apoChildren;
     581             : 
     582        1089 :         for (size_t i = 0; i < apoEntryChildren.size(); i++)
     583          45 :             apoChildren.push_back(apoEntryChildren[i]);
     584             :     }
     585             : 
     586        1167 :     return apoChildren;
     587             : }
     588             : 
     589         123 : std::vector<HFAEntry *> HFAEntry::FindChildren(const char *pszName,
     590             :                                                const char *pszType)
     591             : 
     592             : {
     593         123 :     int bErrorDetected = FALSE;
     594         246 :     return FindChildren(pszName, pszType, 0, &bErrorDetected);
     595             : }
     596             : 
     597             : /************************************************************************/
     598             : /*                           GetNamedChild()                            */
     599             : /************************************************************************/
     600             : 
     601       18420 : HFAEntry *HFAEntry::GetNamedChild(const char *pszName)
     602             : 
     603             : {
     604             :     // Establish how much of this name path is for the next child.
     605             :     // Up to the '.' or end of the string.
     606       18420 :     int nNameLen = 0;
     607      269658 :     for (; pszName[nNameLen] != '.' && pszName[nNameLen] != '\0' &&
     608      251238 :            pszName[nNameLen] != ':';
     609             :          nNameLen++)
     610             :     {
     611             :     }
     612             : 
     613             :     // Scan children looking for this name.
     614       74077 :     for (HFAEntry *poEntry = GetChild(); poEntry != nullptr;
     615       55657 :          poEntry = poEntry->GetNext())
     616             :     {
     617       64080 :         if (EQUALN(poEntry->GetName(), pszName, nNameLen) &&
     618        4810 :             static_cast<int>(strlen(poEntry->GetName())) == nNameLen)
     619             :         {
     620        3798 :             if (pszName[nNameLen] == '.')
     621             :             {
     622             :                 HFAEntry *poResult;
     623             : 
     624         590 :                 poResult = poEntry->GetNamedChild(pszName + nNameLen + 1);
     625         590 :                 if (poResult != nullptr)
     626         405 :                     return poResult;
     627             :             }
     628             :             else
     629        3208 :                 return poEntry;
     630             :         }
     631             :     }
     632             : 
     633       14807 :     return nullptr;
     634             : }
     635             : 
     636             : /************************************************************************/
     637             : /*                           GetFieldValue()                            */
     638             : /************************************************************************/
     639             : 
     640       32624 : bool HFAEntry::GetFieldValue(const char *pszFieldPath, char chReqType,
     641             :                              void *pReqReturn, int *pnRemainingDataSize)
     642             : 
     643             : {
     644             :     // Is there a node path in this string?
     645       32624 :     if (strchr(pszFieldPath, ':') != nullptr)
     646             :     {
     647           0 :         HFAEntry *poEntry = GetNamedChild(pszFieldPath);
     648           0 :         if (poEntry == nullptr)
     649           0 :             return false;
     650             : 
     651           0 :         pszFieldPath = strchr(pszFieldPath, ':') + 1;
     652             :     }
     653             : 
     654             :     // Do we have the data and type for this node?
     655       32624 :     LoadData();
     656             : 
     657       32624 :     if (pabyData == nullptr)
     658          25 :         return false;
     659             : 
     660       32599 :     if (poType == nullptr)
     661           0 :         return false;
     662             : 
     663             :     // Extract the instance information.
     664       32599 :     return poType->ExtractInstValue(pszFieldPath, pabyData, nDataPos, nDataSize,
     665       32599 :                                     chReqType, pReqReturn, pnRemainingDataSize);
     666             : }
     667             : 
     668             : /************************************************************************/
     669             : /*                           GetFieldCount()                            */
     670             : /************************************************************************/
     671             : 
     672         934 : int HFAEntry::GetFieldCount(const char *pszFieldPath, CPLErr * /* peErr */)
     673             : {
     674             :     // Is there a node path in this string?
     675         934 :     if (strchr(pszFieldPath, ':') != nullptr)
     676             :     {
     677           0 :         HFAEntry *poEntry = GetNamedChild(pszFieldPath);
     678           0 :         if (poEntry == nullptr)
     679           0 :             return -1;
     680             : 
     681           0 :         pszFieldPath = strchr(pszFieldPath, ':') + 1;
     682             :     }
     683             : 
     684             :     // Do we have the data and type for this node?
     685         934 :     LoadData();
     686             : 
     687         934 :     if (pabyData == nullptr)
     688           0 :         return -1;
     689             : 
     690         934 :     if (poType == nullptr)
     691           0 :         return -1;
     692             : 
     693             :     // Extract the instance information.
     694             : 
     695         934 :     return poType->GetInstCount(pszFieldPath, pabyData, nDataPos, nDataSize);
     696             : }
     697             : 
     698             : /************************************************************************/
     699             : /*                            GetIntField()                             */
     700             : /************************************************************************/
     701             : 
     702       21467 : GInt32 HFAEntry::GetIntField(const char *pszFieldPath, CPLErr *peErr)
     703             : 
     704             : {
     705       21467 :     GInt32 nIntValue = 0;
     706             : 
     707       21467 :     if (!GetFieldValue(pszFieldPath, 'i', &nIntValue, nullptr))
     708             :     {
     709          31 :         if (peErr != nullptr)
     710          13 :             *peErr = CE_Failure;
     711             : 
     712          31 :         return 0;
     713             :     }
     714             : 
     715       21436 :     if (peErr != nullptr)
     716       10136 :         *peErr = CE_None;
     717             : 
     718       21436 :     return nIntValue;
     719             : }
     720             : 
     721             : /************************************************************************/
     722             : /*                           GetBigIntField()                           */
     723             : /*                                                                      */
     724             : /*      This is just a helper method that reads two ULONG array         */
     725             : /*      entries as a GIntBig.  The passed name should be the name of    */
     726             : /*      the array with no array index.  Array indexes 0 and 1 will      */
     727             : /*      be concatenated.                                                */
     728             : /************************************************************************/
     729             : 
     730          32 : GIntBig HFAEntry::GetBigIntField(const char *pszFieldPath, CPLErr *peErr)
     731             : 
     732             : {
     733             :     char szFullFieldPath[1024];
     734             : 
     735          32 :     snprintf(szFullFieldPath, sizeof(szFullFieldPath), "%s[0]", pszFieldPath);
     736          32 :     const GUInt32 nLower = GetIntField(szFullFieldPath, peErr);
     737          32 :     if (peErr != nullptr && *peErr != CE_None)
     738           0 :         return 0;
     739             : 
     740          32 :     snprintf(szFullFieldPath, sizeof(szFullFieldPath), "%s[1]", pszFieldPath);
     741          32 :     const GUInt32 nUpper = GetIntField(szFullFieldPath, peErr);
     742          32 :     if (peErr != nullptr && *peErr != CE_None)
     743           0 :         return 0;
     744             : 
     745          32 :     return nLower + (static_cast<GIntBig>(nUpper) << 32);
     746             : }
     747             : 
     748             : /************************************************************************/
     749             : /*                           GetDoubleField()                           */
     750             : /************************************************************************/
     751             : 
     752        7687 : double HFAEntry::GetDoubleField(const char *pszFieldPath, CPLErr *peErr)
     753             : 
     754             : {
     755        7687 :     double dfDoubleValue = 0;
     756             : 
     757        7687 :     if (!GetFieldValue(pszFieldPath, 'd', &dfDoubleValue, nullptr))
     758             :     {
     759         250 :         if (peErr != nullptr)
     760          26 :             *peErr = CE_Failure;
     761             : 
     762         250 :         return 0.0;
     763             :     }
     764             : 
     765        7437 :     if (peErr != nullptr)
     766        1126 :         *peErr = CE_None;
     767             : 
     768        7437 :     return dfDoubleValue;
     769             : }
     770             : 
     771             : /************************************************************************/
     772             : /*                           GetStringField()                           */
     773             : /************************************************************************/
     774             : 
     775        3470 : const char *HFAEntry::GetStringField(const char *pszFieldPath, CPLErr *peErr,
     776             :                                      int *pnRemainingDataSize)
     777             : 
     778             : {
     779        3470 :     char *pszResult = nullptr;
     780             : 
     781        3470 :     if (!GetFieldValue(pszFieldPath, 's', &pszResult, pnRemainingDataSize))
     782             :     {
     783         350 :         if (peErr != nullptr)
     784          33 :             *peErr = CE_Failure;
     785             : 
     786         350 :         return nullptr;
     787             :     }
     788             : 
     789        3120 :     if (peErr != nullptr)
     790         672 :         *peErr = CE_None;
     791             : 
     792        3120 :     return pszResult;
     793             : }
     794             : 
     795             : /************************************************************************/
     796             : /*                           SetFieldValue()                            */
     797             : /************************************************************************/
     798             : 
     799       11966 : CPLErr HFAEntry::SetFieldValue(const char *pszFieldPath, char chReqType,
     800             :                                void *pValue)
     801             : 
     802             : {
     803             :     // Is there a node path in this string?
     804       11966 :     if (strchr(pszFieldPath, ':') != nullptr)
     805             :     {
     806           0 :         HFAEntry *poEntry = GetNamedChild(pszFieldPath);
     807           0 :         if (poEntry == nullptr)
     808           0 :             return CE_Failure;
     809             : 
     810           0 :         pszFieldPath = strchr(pszFieldPath, ':') + 1;
     811             :     }
     812             : 
     813             :     // Do we have the data and type for this node?  Try loading
     814             :     // from a file, or instantiating a new node.
     815       11966 :     LoadData();
     816       11966 :     if (MakeData() == nullptr || pabyData == nullptr || poType == nullptr)
     817             :     {
     818          16 :         return CE_Failure;
     819             :     }
     820             : 
     821             :     // Extract the instance information.
     822       11950 :     MarkDirty();
     823             : 
     824       11950 :     return poType->SetInstValue(pszFieldPath, pabyData, nDataPos, nDataSize,
     825       11950 :                                 chReqType, pValue);
     826             : }
     827             : 
     828             : /************************************************************************/
     829             : /*                           SetStringField()                           */
     830             : /************************************************************************/
     831             : 
     832        2741 : CPLErr HFAEntry::SetStringField(const char *pszFieldPath, const char *pszValue)
     833             : 
     834             : {
     835        2741 :     return SetFieldValue(pszFieldPath, 's', (void *)pszValue);
     836             : }
     837             : 
     838             : /************************************************************************/
     839             : /*                            SetIntField()                             */
     840             : /************************************************************************/
     841             : 
     842        3542 : CPLErr HFAEntry::SetIntField(const char *pszFieldPath, int nValue)
     843             : 
     844             : {
     845        3542 :     return SetFieldValue(pszFieldPath, 'i', &nValue);
     846             : }
     847             : 
     848             : /************************************************************************/
     849             : /*                           SetDoubleField()                           */
     850             : /************************************************************************/
     851             : 
     852        5683 : CPLErr HFAEntry::SetDoubleField(const char *pszFieldPath, double dfValue)
     853             : 
     854             : {
     855        5683 :     return SetFieldValue(pszFieldPath, 'd', &dfValue);
     856             : }
     857             : 
     858             : /************************************************************************/
     859             : /*                            SetPosition()                             */
     860             : /*                                                                      */
     861             : /*      Set the disk position for this entry, and recursively apply     */
     862             : /*      to any children of this node.  The parent will take care of     */
     863             : /*      our siblings.                                                   */
     864             : /************************************************************************/
     865             : 
     866        4419 : void HFAEntry::SetPosition()
     867             : 
     868             : {
     869             :     // Establish the location of this entry, and its data.
     870        4419 :     if (nFilePos == 0)
     871             :     {
     872        2011 :         nFilePos =
     873        2011 :             HFAAllocateSpace(psHFA, psHFA->nEntryHeaderLength + nDataSize);
     874             : 
     875        2011 :         if (nDataSize > 0)
     876        1816 :             nDataPos = nFilePos + psHFA->nEntryHeaderLength;
     877             :     }
     878             : 
     879             :     // Force all children to set their position.
     880        7260 :     for (HFAEntry *poThisChild = poChild; poThisChild != nullptr;
     881        2841 :          poThisChild = poThisChild->poNext)
     882             :     {
     883        2841 :         poThisChild->SetPosition();
     884             :     }
     885        4419 : }
     886             : 
     887             : /************************************************************************/
     888             : /*                            FlushToDisk()                             */
     889             : /*                                                                      */
     890             : /*      Write this entry, and its data to disk if the entries           */
     891             : /*      information is dirty.  Also force children to do the same.      */
     892             : /************************************************************************/
     893             : 
     894        3210 : CPLErr HFAEntry::FlushToDisk()
     895             : 
     896             : {
     897             :     // If we are the root node, call SetPosition() on the whole
     898             :     // tree to ensure that all entries have an allocated position.
     899        3210 :     if (poParent == nullptr)
     900         393 :         SetPosition();
     901             : 
     902             :     // Only write this node out if it is dirty.
     903        3210 :     if (bDirty)
     904             :     {
     905             :         // Ensure we know where the relative entries are located.
     906        2345 :         if (poNext != nullptr)
     907        1247 :             nNextPos = poNext->nFilePos;
     908             : 
     909        2345 :         if (poChild != nullptr)
     910         709 :             nChildPos = poChild->nFilePos;
     911             : 
     912             :         // Write the Ehfa_Entry fields.
     913             : 
     914             :         // VSIFFlushL(psHFA->fp);
     915        2345 :         if (VSIFSeekL(psHFA->fp, nFilePos, SEEK_SET) != 0)
     916             :         {
     917           0 :             CPLError(CE_Failure, CPLE_FileIO,
     918             :                      "Failed to seek to %d for writing, out of disk space?",
     919             :                      nFilePos);
     920           6 :             return CE_Failure;
     921             :         }
     922             : 
     923        2345 :         GUInt32 nLong = nNextPos;
     924             :         HFAStandard(4, &nLong);
     925        2345 :         bool bOK = VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
     926             : 
     927        2345 :         if (poPrev != nullptr)
     928        1420 :             nLong = poPrev->nFilePos;
     929             :         else
     930         925 :             nLong = 0;
     931             :         HFAStandard(4, &nLong);
     932        2345 :         bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
     933             : 
     934        2345 :         if (poParent != nullptr)
     935        2150 :             nLong = poParent->nFilePos;
     936             :         else
     937         195 :             nLong = 0;
     938             :         HFAStandard(4, &nLong);
     939        2345 :         bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
     940             : 
     941        2345 :         nLong = nChildPos;
     942             :         HFAStandard(4, &nLong);
     943        2345 :         bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
     944             : 
     945        2345 :         nLong = nDataPos;
     946             :         HFAStandard(4, &nLong);
     947        2345 :         bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
     948             : 
     949        2345 :         nLong = nDataSize;
     950             :         HFAStandard(4, &nLong);
     951        2345 :         bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
     952             : 
     953        2345 :         bOK &= VSIFWriteL(szName, 1, 64, psHFA->fp) > 0;
     954        2345 :         bOK &= VSIFWriteL(szType, 1, 32, psHFA->fp) > 0;
     955             : 
     956        2345 :         nLong = 0;  // Should we keep the time, or set it more reasonably?
     957        2345 :         bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
     958        2345 :         if (!bOK)
     959             :         {
     960           5 :             CPLError(CE_Failure, CPLE_FileIO,
     961             :                      "Failed to write HFAEntry %s(%s), out of disk space?",
     962           5 :                      szName, szType);
     963           5 :             return CE_Failure;
     964             :         }
     965             : 
     966             :         // Write out the data.
     967             :         // VSIFFlushL(psHFA->fp);
     968        2340 :         if (nDataSize > 0 && pabyData != nullptr)
     969             :         {
     970        3902 :             if (VSIFSeekL(psHFA->fp, nDataPos, SEEK_SET) != 0 ||
     971        1951 :                 VSIFWriteL(pabyData, nDataSize, 1, psHFA->fp) != 1)
     972             :             {
     973           1 :                 CPLError(CE_Failure, CPLE_FileIO,
     974             :                          "Failed to write %d bytes HFAEntry %s(%s) data, "
     975             :                          "out of disk space?",
     976           1 :                          nDataSize, szName, szType);
     977           1 :                 return CE_Failure;
     978             :             }
     979             :         }
     980             : 
     981             :         // VSIFFlushL(psHFA->fp);
     982             :     }
     983             : 
     984             :     // Process all the children of this node.
     985        6019 :     for (HFAEntry *poThisChild = poChild; poThisChild != nullptr;
     986        2815 :          poThisChild = poThisChild->poNext)
     987             :     {
     988        2817 :         CPLErr eErr = poThisChild->FlushToDisk();
     989        2817 :         if (eErr != CE_None)
     990           2 :             return eErr;
     991             :     }
     992             : 
     993        3202 :     bDirty = false;
     994             : 
     995        3202 :     return CE_None;
     996             : }
     997             : 
     998             : /************************************************************************/
     999             : /*                             MarkDirty()                              */
    1000             : /*                                                                      */
    1001             : /*      Mark this node as dirty (in need of writing to disk), and       */
    1002             : /*      also mark the tree as a whole as being dirty.                   */
    1003             : /************************************************************************/
    1004             : 
    1005       20138 : void HFAEntry::MarkDirty()
    1006             : 
    1007             : {
    1008       20138 :     bDirty = true;
    1009       20138 :     psHFA->bTreeDirty = true;
    1010       20138 : }

Generated by: LCOV version 1.14