LCOV - code coverage report
Current view: top level - frmts/hfa - hfaentry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 332 395 84.1 %
Date: 2026-01-18 23:37:47 Functions: 30 31 96.8 %

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

Generated by: LCOV version 1.14