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