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