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