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