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 : }
|