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