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