/src/gdal/frmts/hfa/hfaentry.cpp
Line | Count | Source |
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 | | HFAEntry::HFAEntry() |
41 | 1.11M | : bDirty(false), nFilePos(0), psHFA(nullptr), poParent(nullptr), |
42 | 1.11M | poPrev(nullptr), nNextPos(0), poNext(nullptr), nChildPos(0), |
43 | 1.11M | poChild(nullptr), poType(nullptr), nDataPos(0), nDataSize(0), |
44 | 1.11M | pabyData(nullptr), bIsMIFObject(false) |
45 | 1.11M | { |
46 | 1.11M | szName[0] = '\0'; |
47 | 1.11M | szType[0] = '\0'; |
48 | 1.11M | } |
49 | | |
50 | | /************************************************************************/ |
51 | | /* HFAEntry() */ |
52 | | /* */ |
53 | | /* Construct an HFAEntry from the source file. */ |
54 | | /************************************************************************/ |
55 | | |
56 | | HFAEntry *HFAEntry::New(HFAInfo_t *psHFAIn, GUInt32 nPos, HFAEntry *poParentIn, |
57 | | HFAEntry *poPrevIn) |
58 | | |
59 | 1.11M | { |
60 | 1.11M | HFAEntry *poEntry = new HFAEntry; |
61 | 1.11M | poEntry->psHFA = psHFAIn; |
62 | | |
63 | 1.11M | poEntry->nFilePos = nPos; |
64 | 1.11M | poEntry->poParent = poParentIn; |
65 | 1.11M | poEntry->poPrev = poPrevIn; |
66 | | |
67 | | // Read the entry information from the file. |
68 | 1.11M | GInt32 anEntryNums[6] = {}; |
69 | | |
70 | 1.11M | if (VSIFSeekL(poEntry->psHFA->fp, |
71 | 1.11M | static_cast<vsi_l_offset>(poEntry->nFilePos), |
72 | 1.11M | SEEK_SET) == -1 || |
73 | 1.11M | VSIFReadL(anEntryNums, sizeof(GInt32) * 6, 1, poEntry->psHFA->fp) < 1) |
74 | 363k | { |
75 | 363k | CPLError(CE_Failure, CPLE_FileIO, |
76 | 363k | "VSIFReadL(%p,6*4) @ %u failed in HFAEntry().\n%s", |
77 | 363k | poEntry->psHFA->fp, poEntry->nFilePos, VSIStrerror(errno)); |
78 | 363k | delete poEntry; |
79 | 363k | return nullptr; |
80 | 363k | } |
81 | | |
82 | 5.29M | for (int i = 0; i < 6; i++) |
83 | 4.53M | HFAStandard(4, anEntryNums + i); |
84 | | |
85 | 756k | poEntry->nNextPos = anEntryNums[0]; |
86 | 756k | poEntry->nChildPos = anEntryNums[3]; |
87 | 756k | poEntry->nDataPos = anEntryNums[4]; |
88 | 756k | poEntry->nDataSize = anEntryNums[5]; |
89 | | |
90 | | // Read the name, and type. |
91 | 756k | if (VSIFReadL(poEntry->szName, 64, 1, poEntry->psHFA->fp) < 1 || |
92 | 755k | VSIFReadL(poEntry->szType, 32, 1, poEntry->psHFA->fp) < 1) |
93 | 1.96k | { |
94 | 1.96k | poEntry->szName[sizeof(poEntry->szName) - 1] = '\0'; |
95 | 1.96k | poEntry->szType[sizeof(poEntry->szType) - 1] = '\0'; |
96 | 1.96k | CPLError(CE_Failure, CPLE_FileIO, "VSIFReadL() failed in HFAEntry()."); |
97 | 1.96k | delete poEntry; |
98 | 1.96k | return nullptr; |
99 | 1.96k | } |
100 | 754k | poEntry->szName[sizeof(poEntry->szName) - 1] = '\0'; |
101 | 754k | poEntry->szType[sizeof(poEntry->szType) - 1] = '\0'; |
102 | 754k | return poEntry; |
103 | 756k | } |
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 | | HFAEntry::HFAEntry(HFAInfo_t *psHFAIn, const char *pszNodeName, |
113 | | const char *pszTypeName, HFAEntry *poParentIn) |
114 | 224k | : nFilePos(0), psHFA(psHFAIn), poParent(poParentIn), poPrev(nullptr), |
115 | 224k | nNextPos(0), poNext(nullptr), nChildPos(0), poChild(nullptr), |
116 | 224k | poType(nullptr), nDataPos(0), nDataSize(0), pabyData(nullptr), |
117 | 224k | bIsMIFObject(false) |
118 | 224k | { |
119 | | // Initialize Entry. |
120 | 224k | SetName(pszNodeName); |
121 | 224k | memset(szType, 0, sizeof(szType)); |
122 | 224k | snprintf(szType, sizeof(szType), "%s", pszTypeName); |
123 | | |
124 | | // Update the previous or parent node to refer to this one. |
125 | 224k | if (poParent == nullptr) |
126 | 188 | { |
127 | | // Do nothing. |
128 | 188 | } |
129 | 224k | else if (poParent->poChild == nullptr) |
130 | 53.1k | { |
131 | 53.1k | poParent->poChild = this; |
132 | 53.1k | poParent->MarkDirty(); |
133 | 53.1k | } |
134 | 171k | else |
135 | 171k | { |
136 | 171k | poPrev = poParent->poChild; |
137 | 4.35M | while (poPrev->poNext != nullptr) |
138 | 4.17M | poPrev = poPrev->poNext; |
139 | | |
140 | 171k | poPrev->poNext = this; |
141 | 171k | poPrev->MarkDirty(); |
142 | 171k | } |
143 | | |
144 | 224k | MarkDirty(); |
145 | 224k | } |
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 | | HFAEntry *HFAEntry::New(HFAInfo_t *psHFAIn, const char *pszNodeName, |
155 | | const char *pszTypeName, HFAEntry *poParentIn) |
156 | 224k | { |
157 | 224k | CPLAssert(poParentIn != nullptr); |
158 | 224k | return new HFAEntry(psHFAIn, pszNodeName, pszTypeName, poParentIn); |
159 | 224k | } |
160 | | |
161 | | /************************************************************************/ |
162 | | /* BuildEntryFromMIFObject() */ |
163 | | /* */ |
164 | | /* Create a pseudo-HFAEntry wrapping a MIFObject. */ |
165 | | /************************************************************************/ |
166 | | |
167 | | HFAEntry *HFAEntry::BuildEntryFromMIFObject(HFAEntry *poContainer, |
168 | | const char *pszMIFObjectPath) |
169 | 18 | { |
170 | 18 | CPLString osFieldName; |
171 | | |
172 | 18 | osFieldName.Printf("%s.%s", pszMIFObjectPath, "MIFDictionary"); |
173 | 18 | const char *pszField = poContainer->GetStringField(osFieldName.c_str()); |
174 | 18 | if (pszField == nullptr) |
175 | 15 | { |
176 | 15 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry", |
177 | 15 | osFieldName.c_str()); |
178 | 15 | return nullptr; |
179 | 15 | } |
180 | 3 | 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 | 1 | { |
186 | 1 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry", |
187 | 1 | osFieldName.c_str()); |
188 | 1 | return nullptr; |
189 | 1 | } |
190 | 2 | CPLString osType = pszField; |
191 | | |
192 | 2 | osFieldName.Printf("%s.%s", pszMIFObjectPath, "MIFObject"); |
193 | 2 | int nRemainingDataSize = 0; |
194 | 2 | pszField = poContainer->GetStringField(osFieldName.c_str(), nullptr, |
195 | 2 | &nRemainingDataSize); |
196 | 2 | if (pszField == nullptr) |
197 | 1 | { |
198 | 1 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry", |
199 | 1 | osFieldName.c_str()); |
200 | 1 | return nullptr; |
201 | 1 | } |
202 | | |
203 | 1 | GInt32 nMIFObjectSize = 0; |
204 | | // We look before the field data to get at the pointer/size info. |
205 | 1 | const GByte *pabyEntryData = poContainer->GetData(); |
206 | 1 | CPLAssert(reinterpret_cast<const GByte *>(pszField) - pabyEntryData >= 0); |
207 | 1 | if (reinterpret_cast<const GByte *>(pszField) - pabyEntryData < 8) |
208 | 1 | { |
209 | 1 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s entry", |
210 | 1 | osFieldName.c_str()); |
211 | 1 | return nullptr; |
212 | 1 | } |
213 | 0 | memcpy(&nMIFObjectSize, pszField - 8, 4); |
214 | 0 | HFAStandard(4, &nMIFObjectSize); |
215 | 0 | if (nMIFObjectSize <= 0) |
216 | 0 | { |
217 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid MIF object size (%d)", |
218 | 0 | nMIFObjectSize); |
219 | 0 | return nullptr; |
220 | 0 | } |
221 | | |
222 | | // Check that we won't copy more bytes than available in the buffer. |
223 | 0 | if (nMIFObjectSize > nRemainingDataSize) |
224 | 0 | { |
225 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
226 | 0 | "Invalid MIF object size (%d > %d)", nMIFObjectSize, |
227 | 0 | nRemainingDataSize); |
228 | 0 | return nullptr; |
229 | 0 | } |
230 | | |
231 | 0 | GByte *l_pabyData = static_cast<GByte *>(VSIMalloc(nMIFObjectSize)); |
232 | 0 | if (l_pabyData == nullptr) |
233 | 0 | return nullptr; |
234 | | |
235 | 0 | memcpy(l_pabyData, pszField, nMIFObjectSize); |
236 | |
|
237 | 0 | return new HFAEntry(osDictionary, osType, nMIFObjectSize, l_pabyData); |
238 | 0 | } |
239 | | |
240 | | /************************************************************************/ |
241 | | /* HFAEntry() */ |
242 | | /* */ |
243 | | /* Create a pseudo-HFAEntry wrapping a MIFObject. */ |
244 | | /************************************************************************/ |
245 | | |
246 | | HFAEntry::HFAEntry(const char *pszDictionary, const char *pszTypeName, |
247 | | int nDataSizeIn, GByte *pabyDataIn) |
248 | 0 | : bDirty(false), nFilePos(0), poParent(nullptr), poPrev(nullptr), |
249 | 0 | nNextPos(0), poNext(nullptr), nChildPos(0), poChild(nullptr), nDataPos(0), |
250 | 0 | nDataSize(0), bIsMIFObject(true) |
251 | 0 | { |
252 | | // Initialize Entry |
253 | 0 | memset(szName, 0, sizeof(szName)); |
254 | | |
255 | | // Create a dummy HFAInfo_t. |
256 | 0 | psHFA = static_cast<HFAInfo_t *>(CPLCalloc(sizeof(HFAInfo_t), 1)); |
257 | |
|
258 | 0 | psHFA->eAccess = HFA_ReadOnly; |
259 | 0 | psHFA->bTreeDirty = false; |
260 | 0 | psHFA->poRoot = this; |
261 | |
|
262 | 0 | psHFA->poDictionary = new HFADictionary(pszDictionary); |
263 | | |
264 | | // Work out the type for this MIFObject. |
265 | 0 | memset(szType, 0, sizeof(szType)); |
266 | 0 | snprintf(szType, sizeof(szType), "%s", pszTypeName); |
267 | |
|
268 | 0 | poType = psHFA->poDictionary->FindType(szType); |
269 | |
|
270 | 0 | nDataSize = nDataSizeIn; |
271 | 0 | pabyData = pabyDataIn; |
272 | 0 | } |
273 | | |
274 | | /************************************************************************/ |
275 | | /* ~HFAEntry() */ |
276 | | /* */ |
277 | | /* Ensure that children are cleaned up when this node is */ |
278 | | /* cleaned up. */ |
279 | | /************************************************************************/ |
280 | | |
281 | | HFAEntry::~HFAEntry() |
282 | | |
283 | 1.34M | { |
284 | 1.34M | CPLFree(pabyData); |
285 | | |
286 | 1.34M | if (poNext != nullptr) |
287 | 519k | delete poNext; |
288 | | |
289 | 1.34M | if (poChild != nullptr) |
290 | 443k | delete poChild; |
291 | | |
292 | 1.34M | if (bIsMIFObject) |
293 | 0 | { |
294 | 0 | delete psHFA->poDictionary; |
295 | 0 | CPLFree(psHFA); |
296 | 0 | } |
297 | 1.34M | } |
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 | | CPLErr HFAEntry::RemoveAndDestroy() |
309 | | |
310 | 0 | { |
311 | 0 | if (poPrev != nullptr) |
312 | 0 | { |
313 | 0 | poPrev->poNext = poNext; |
314 | 0 | if (poNext != nullptr) |
315 | 0 | poPrev->nNextPos = poNext->nFilePos; |
316 | 0 | else |
317 | 0 | poPrev->nNextPos = 0; |
318 | 0 | poPrev->MarkDirty(); |
319 | 0 | } |
320 | 0 | if (poParent != nullptr && poParent->poChild == this) |
321 | 0 | { |
322 | 0 | poParent->poChild = poNext; |
323 | 0 | if (poNext) |
324 | 0 | poParent->nChildPos = poNext->nFilePos; |
325 | 0 | else |
326 | 0 | poParent->nChildPos = 0; |
327 | 0 | poParent->MarkDirty(); |
328 | 0 | } |
329 | |
|
330 | 0 | if (poNext != nullptr) |
331 | 0 | { |
332 | 0 | poNext->poPrev = poPrev; |
333 | 0 | } |
334 | |
|
335 | 0 | poNext = nullptr; |
336 | 0 | poPrev = nullptr; |
337 | 0 | poParent = nullptr; |
338 | |
|
339 | 0 | delete this; |
340 | |
|
341 | 0 | return CE_None; |
342 | 0 | } |
343 | | |
344 | | /************************************************************************/ |
345 | | /* SetName() */ |
346 | | /* */ |
347 | | /* Changes the name assigned to this node */ |
348 | | /************************************************************************/ |
349 | | |
350 | | void HFAEntry::SetName(const char *pszNodeName) |
351 | 225k | { |
352 | 225k | memset(szName, 0, sizeof(szName)); |
353 | 225k | snprintf(szName, sizeof(szName), "%s", pszNodeName); |
354 | | |
355 | 225k | MarkDirty(); |
356 | 225k | } |
357 | | |
358 | | /************************************************************************/ |
359 | | /* GetChild() */ |
360 | | /************************************************************************/ |
361 | | |
362 | | HFAEntry *HFAEntry::GetChild() |
363 | | |
364 | 2.49M | { |
365 | | // Do we need to create the child node? |
366 | 2.49M | if (poChild == nullptr && nChildPos != 0) |
367 | 635k | { |
368 | 635k | poChild = HFAEntry::New(psHFA, nChildPos, this, nullptr); |
369 | 635k | if (poChild == nullptr) |
370 | 245k | nChildPos = 0; |
371 | 635k | } |
372 | | |
373 | 2.49M | return poChild; |
374 | 2.49M | } |
375 | | |
376 | | /************************************************************************/ |
377 | | /* GetNext() */ |
378 | | /************************************************************************/ |
379 | | |
380 | | HFAEntry *HFAEntry::GetNext() |
381 | | |
382 | 4.28M | { |
383 | | // Do we need to create the next node? |
384 | 4.28M | if (poNext == nullptr && nNextPos != 0) |
385 | 468k | { |
386 | | // Check if we have a loop on the next node in this sibling chain. |
387 | 468k | HFAEntry *poPast; |
388 | | |
389 | 4.97M | for (poPast = this; poPast != nullptr && poPast->nFilePos != nNextPos; |
390 | 4.51M | poPast = poPast->poPrev) |
391 | 4.51M | { |
392 | 4.51M | } |
393 | | |
394 | 468k | if (poPast != nullptr) |
395 | 952 | { |
396 | 952 | CPLError(CE_Warning, CPLE_AppDefined, |
397 | 952 | "Corrupt (looping) entry in %s, " |
398 | 952 | "ignoring some entries after %s.", |
399 | 952 | psHFA->pszFilename, szName); |
400 | 952 | nNextPos = 0; |
401 | 952 | return nullptr; |
402 | 952 | } |
403 | | |
404 | 467k | poNext = HFAEntry::New(psHFA, nNextPos, poParent, this); |
405 | 467k | if (poNext == nullptr) |
406 | 118k | nNextPos = 0; |
407 | 467k | } |
408 | | |
409 | 4.28M | return poNext; |
410 | 4.28M | } |
411 | | |
412 | | /************************************************************************/ |
413 | | /* LoadData() */ |
414 | | /* */ |
415 | | /* Load the data for this entry, and build up the field */ |
416 | | /* information for it. */ |
417 | | /************************************************************************/ |
418 | | |
419 | | void HFAEntry::LoadData() |
420 | | |
421 | 4.27M | { |
422 | 4.27M | if (pabyData != nullptr || nDataSize == 0) |
423 | 4.19M | return; |
424 | 72.8k | if (nDataSize > INT_MAX - 1) |
425 | 2.58k | { |
426 | 2.58k | CPLError(CE_Failure, CPLE_AppDefined, |
427 | 2.58k | "Invalid value for nDataSize = %u", nDataSize); |
428 | 2.58k | return; |
429 | 2.58k | } |
430 | | |
431 | | // Allocate buffer, and read data. |
432 | 70.2k | pabyData = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nDataSize + 1)); |
433 | 70.2k | if (pabyData == nullptr) |
434 | 0 | { |
435 | 0 | return; |
436 | 0 | } |
437 | | |
438 | 70.2k | if (VSIFSeekL(psHFA->fp, static_cast<vsi_l_offset>(nDataPos), SEEK_SET) < 0) |
439 | 0 | { |
440 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
441 | 0 | "VSIFSeekL() failed in HFAEntry::LoadData()."); |
442 | 0 | return; |
443 | 0 | } |
444 | | |
445 | 70.2k | if (VSIFReadL(pabyData, nDataSize, 1, psHFA->fp) < 1) |
446 | 2.43k | { |
447 | 2.43k | CPLError(CE_Failure, CPLE_FileIO, |
448 | 2.43k | "VSIFReadL() failed in HFAEntry::LoadData()."); |
449 | 2.43k | return; |
450 | 2.43k | } |
451 | | |
452 | | // Make sure the buffer is always null terminated to avoid |
453 | | // issues when extracting strings from a corrupted file. |
454 | 67.8k | pabyData[nDataSize] = '\0'; |
455 | | |
456 | | // Get the type corresponding to this entry. |
457 | 67.8k | poType = psHFA->poDictionary->FindType(szType); |
458 | 67.8k | if (poType == nullptr) |
459 | 1.59k | return; |
460 | 67.8k | } |
461 | | |
462 | | /************************************************************************/ |
463 | | /* GetTypeObject() */ |
464 | | /************************************************************************/ |
465 | | |
466 | | HFAType *HFAEntry::GetTypeObject() |
467 | | |
468 | 9.68k | { |
469 | 9.68k | if (poType == nullptr) |
470 | 9.68k | poType = psHFA->poDictionary->FindType(szType); |
471 | | |
472 | 9.68k | return poType; |
473 | 9.68k | } |
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 | | GByte *HFAEntry::MakeData(int nSize) |
486 | | |
487 | 1.34M | { |
488 | 1.34M | if (poType == nullptr) |
489 | 214k | { |
490 | 214k | poType = psHFA->poDictionary->FindType(szType); |
491 | 214k | if (poType == nullptr) |
492 | 0 | return nullptr; |
493 | 214k | } |
494 | | |
495 | 1.34M | if (nSize == 0 && poType->nBytes > 0) |
496 | 412k | nSize = poType->nBytes; |
497 | | |
498 | | // nDataSize is a GUInt32. |
499 | 1.34M | if (static_cast<int>(nDataSize) < nSize && nSize > 0) |
500 | 224k | { |
501 | 224k | pabyData = static_cast<GByte *>(CPLRealloc(pabyData, nSize)); |
502 | 224k | memset(pabyData + nDataSize, 0, nSize - nDataSize); |
503 | 224k | nDataSize = nSize; |
504 | | |
505 | 224k | 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 | 224k | if (nFilePos != 0) |
512 | 0 | { |
513 | 0 | nFilePos = 0; |
514 | 0 | nDataPos = 0; |
515 | 0 | if (poPrev != nullptr) |
516 | 0 | poPrev->MarkDirty(); |
517 | 0 | if (poNext != nullptr) |
518 | 0 | poNext->MarkDirty(); |
519 | 0 | if (poChild != nullptr) |
520 | 0 | poChild->MarkDirty(); |
521 | 0 | if (poParent != nullptr) |
522 | 0 | poParent->MarkDirty(); |
523 | 0 | } |
524 | 224k | } |
525 | 1.12M | else |
526 | 1.12M | { |
527 | 1.12M | LoadData(); // Make sure the data is loaded before we return pointer. |
528 | 1.12M | } |
529 | | |
530 | 1.34M | return pabyData; |
531 | 1.34M | } |
532 | | |
533 | | /************************************************************************/ |
534 | | /* DumpFieldValues() */ |
535 | | /************************************************************************/ |
536 | | |
537 | | void HFAEntry::DumpFieldValues(FILE *fp, const char *pszPrefix) |
538 | | |
539 | 0 | { |
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 | 0 | } |
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 | | std::vector<HFAEntry *> HFAEntry::FindChildren(const char *pszName, |
561 | | const char *pszType, |
562 | | int nRecLevel, |
563 | | int *pbErrorDetected) |
564 | | |
565 | 1.34M | { |
566 | 1.34M | std::vector<HFAEntry *> apoChildren; |
567 | | |
568 | 1.34M | if (*pbErrorDetected) |
569 | 0 | return apoChildren; |
570 | 1.34M | if (nRecLevel == 50) |
571 | 10.0k | { |
572 | 10.0k | CPLError(CE_Failure, CPLE_AppDefined, |
573 | 10.0k | "Bad entry structure: recursion detected !"); |
574 | 10.0k | *pbErrorDetected = TRUE; |
575 | 10.0k | return apoChildren; |
576 | 10.0k | } |
577 | | |
578 | 2.15M | for (HFAEntry *poEntry = GetChild(); poEntry != nullptr; |
579 | 1.33M | poEntry = poEntry->GetNext()) |
580 | 1.32M | { |
581 | 1.32M | std::vector<HFAEntry *> apoEntryChildren; |
582 | | |
583 | 1.32M | if ((pszName == nullptr || EQUAL(poEntry->GetName(), pszName)) && |
584 | 1.32M | (pszType == nullptr || EQUAL(poEntry->GetType(), pszType))) |
585 | 3.50k | apoChildren.push_back(poEntry); |
586 | | |
587 | 1.32M | apoEntryChildren = poEntry->FindChildren( |
588 | 1.32M | pszName, pszType, nRecLevel + 1, pbErrorDetected); |
589 | 1.32M | if (*pbErrorDetected) |
590 | 503k | return apoChildren; |
591 | | |
592 | 826k | for (size_t i = 0; i < apoEntryChildren.size(); i++) |
593 | 6.95k | apoChildren.push_back(apoEntryChildren[i]); |
594 | 819k | } |
595 | | |
596 | 831k | return apoChildren; |
597 | 1.33M | } |
598 | | |
599 | | std::vector<HFAEntry *> HFAEntry::FindChildren(const char *pszName, |
600 | | const char *pszType) |
601 | | |
602 | 21.7k | { |
603 | 21.7k | int bErrorDetected = FALSE; |
604 | 21.7k | return FindChildren(pszName, pszType, 0, &bErrorDetected); |
605 | 21.7k | } |
606 | | |
607 | | /************************************************************************/ |
608 | | /* GetNamedChild() */ |
609 | | /************************************************************************/ |
610 | | |
611 | | HFAEntry *HFAEntry::GetNamedChild(const char *pszName) |
612 | | |
613 | 1.07M | { |
614 | | // Establish how much of this name path is for the next child. |
615 | | // Up to the '.' or end of the string. |
616 | 1.07M | int nNameLen = 0; |
617 | 16.4M | for (; pszName[nNameLen] != '.' && pszName[nNameLen] != '\0' && |
618 | 15.3M | pszName[nNameLen] != ':'; |
619 | 15.3M | nNameLen++) |
620 | 15.3M | { |
621 | 15.3M | } |
622 | | |
623 | | // Scan children looking for this name. |
624 | 4.22M | for (HFAEntry *poEntry = GetChild(); poEntry != nullptr; |
625 | 3.14M | poEntry = poEntry->GetNext()) |
626 | 3.40M | { |
627 | 3.40M | if (EQUALN(poEntry->GetName(), pszName, nNameLen) && |
628 | 302k | static_cast<int>(strlen(poEntry->GetName())) == nNameLen) |
629 | 266k | { |
630 | 266k | if (pszName[nNameLen] == '.') |
631 | 8.62k | { |
632 | 8.62k | HFAEntry *poResult; |
633 | | |
634 | 8.62k | poResult = poEntry->GetNamedChild(pszName + nNameLen + 1); |
635 | 8.62k | if (poResult != nullptr) |
636 | 2.97k | return poResult; |
637 | 8.62k | } |
638 | 257k | else |
639 | 257k | return poEntry; |
640 | 266k | } |
641 | 3.40M | } |
642 | | |
643 | 817k | return nullptr; |
644 | 1.07M | } |
645 | | |
646 | | /************************************************************************/ |
647 | | /* GetFieldValue() */ |
648 | | /************************************************************************/ |
649 | | |
650 | | bool HFAEntry::GetFieldValue(const char *pszFieldPath, char chReqType, |
651 | | void *pReqReturn, int *pnRemainingDataSize) |
652 | | |
653 | 1.89M | { |
654 | | // Is there a node path in this string? |
655 | 1.89M | if (strchr(pszFieldPath, ':') != nullptr) |
656 | 0 | { |
657 | 0 | HFAEntry *poEntry = GetNamedChild(pszFieldPath); |
658 | 0 | if (poEntry == nullptr) |
659 | 0 | return false; |
660 | | |
661 | 0 | pszFieldPath = strchr(pszFieldPath, ':') + 1; |
662 | 0 | } |
663 | | |
664 | | // Do we have the data and type for this node? |
665 | 1.89M | LoadData(); |
666 | | |
667 | 1.89M | if (pabyData == nullptr) |
668 | 5.03k | return false; |
669 | | |
670 | 1.88M | if (poType == nullptr) |
671 | 26.6k | return false; |
672 | | |
673 | | // Extract the instance information. |
674 | 1.86M | return poType->ExtractInstValue(pszFieldPath, pabyData, nDataPos, nDataSize, |
675 | 1.86M | chReqType, pReqReturn, pnRemainingDataSize); |
676 | 1.88M | } |
677 | | |
678 | | /************************************************************************/ |
679 | | /* GetFieldCount() */ |
680 | | /************************************************************************/ |
681 | | |
682 | | int HFAEntry::GetFieldCount(const char *pszFieldPath, CPLErr * /* peErr */) |
683 | 10.4k | { |
684 | | // Is there a node path in this string? |
685 | 10.4k | if (strchr(pszFieldPath, ':') != nullptr) |
686 | 0 | { |
687 | 0 | HFAEntry *poEntry = GetNamedChild(pszFieldPath); |
688 | 0 | if (poEntry == nullptr) |
689 | 0 | return -1; |
690 | | |
691 | 0 | pszFieldPath = strchr(pszFieldPath, ':') + 1; |
692 | 0 | } |
693 | | |
694 | | // Do we have the data and type for this node? |
695 | 10.4k | LoadData(); |
696 | | |
697 | 10.4k | if (pabyData == nullptr) |
698 | 42 | return -1; |
699 | | |
700 | 10.4k | if (poType == nullptr) |
701 | 954 | return -1; |
702 | | |
703 | | // Extract the instance information. |
704 | | |
705 | 9.48k | return poType->GetInstCount(pszFieldPath, pabyData, nDataPos, nDataSize); |
706 | 10.4k | } |
707 | | |
708 | | /************************************************************************/ |
709 | | /* GetIntField() */ |
710 | | /************************************************************************/ |
711 | | |
712 | | GInt32 HFAEntry::GetIntField(const char *pszFieldPath, CPLErr *peErr) |
713 | | |
714 | 1.02M | { |
715 | 1.02M | GInt32 nIntValue = 0; |
716 | | |
717 | 1.02M | if (!GetFieldValue(pszFieldPath, 'i', &nIntValue, nullptr)) |
718 | 45.4k | { |
719 | 45.4k | if (peErr != nullptr) |
720 | 395 | *peErr = CE_Failure; |
721 | | |
722 | 45.4k | return 0; |
723 | 45.4k | } |
724 | | |
725 | 983k | if (peErr != nullptr) |
726 | 455k | *peErr = CE_None; |
727 | | |
728 | 983k | return nIntValue; |
729 | 1.02M | } |
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 | | GIntBig HFAEntry::GetBigIntField(const char *pszFieldPath, CPLErr *peErr) |
741 | | |
742 | 0 | { |
743 | 0 | char szFullFieldPath[1024]; |
744 | |
|
745 | 0 | snprintf(szFullFieldPath, sizeof(szFullFieldPath), "%s[0]", pszFieldPath); |
746 | 0 | const GUInt32 nLower = GetIntField(szFullFieldPath, peErr); |
747 | 0 | if (peErr != nullptr && *peErr != CE_None) |
748 | 0 | return 0; |
749 | | |
750 | 0 | snprintf(szFullFieldPath, sizeof(szFullFieldPath), "%s[1]", pszFieldPath); |
751 | 0 | const GUInt32 nUpper = GetIntField(szFullFieldPath, peErr); |
752 | 0 | if (peErr != nullptr && *peErr != CE_None) |
753 | 0 | return 0; |
754 | | |
755 | 0 | return nLower + (static_cast<GIntBig>(nUpper) << 32); |
756 | 0 | } |
757 | | |
758 | | /************************************************************************/ |
759 | | /* GetDoubleField() */ |
760 | | /************************************************************************/ |
761 | | |
762 | | double HFAEntry::GetDoubleField(const char *pszFieldPath, CPLErr *peErr) |
763 | | |
764 | 670k | { |
765 | 670k | double dfDoubleValue = 0; |
766 | | |
767 | 670k | if (!GetFieldValue(pszFieldPath, 'd', &dfDoubleValue, nullptr)) |
768 | 65.1k | { |
769 | 65.1k | if (peErr != nullptr) |
770 | 6.08k | *peErr = CE_Failure; |
771 | | |
772 | 65.1k | return 0.0; |
773 | 65.1k | } |
774 | | |
775 | 605k | if (peErr != nullptr) |
776 | 547k | *peErr = CE_None; |
777 | | |
778 | 605k | return dfDoubleValue; |
779 | 670k | } |
780 | | |
781 | | /************************************************************************/ |
782 | | /* GetStringField() */ |
783 | | /************************************************************************/ |
784 | | |
785 | | const char *HFAEntry::GetStringField(const char *pszFieldPath, CPLErr *peErr, |
786 | | int *pnRemainingDataSize) |
787 | | |
788 | 192k | { |
789 | 192k | char *pszResult = nullptr; |
790 | | |
791 | 192k | if (!GetFieldValue(pszFieldPath, 's', &pszResult, pnRemainingDataSize)) |
792 | 22.6k | { |
793 | 22.6k | if (peErr != nullptr) |
794 | 8.99k | *peErr = CE_Failure; |
795 | | |
796 | 22.6k | return nullptr; |
797 | 22.6k | } |
798 | | |
799 | 169k | if (peErr != nullptr) |
800 | 141k | *peErr = CE_None; |
801 | | |
802 | 169k | return pszResult; |
803 | 192k | } |
804 | | |
805 | | /************************************************************************/ |
806 | | /* SetFieldValue() */ |
807 | | /************************************************************************/ |
808 | | |
809 | | CPLErr HFAEntry::SetFieldValue(const char *pszFieldPath, char chReqType, |
810 | | void *pValue) |
811 | | |
812 | 1.21M | { |
813 | | // Is there a node path in this string? |
814 | 1.21M | if (strchr(pszFieldPath, ':') != nullptr) |
815 | 0 | { |
816 | 0 | HFAEntry *poEntry = GetNamedChild(pszFieldPath); |
817 | 0 | if (poEntry == nullptr) |
818 | 0 | return CE_Failure; |
819 | | |
820 | 0 | pszFieldPath = strchr(pszFieldPath, ':') + 1; |
821 | 0 | } |
822 | | |
823 | | // Do we have the data and type for this node? Try loading |
824 | | // from a file, or instantiating a new node. |
825 | 1.21M | LoadData(); |
826 | 1.21M | if (MakeData() == nullptr || pabyData == nullptr || poType == nullptr) |
827 | 0 | { |
828 | 0 | return CE_Failure; |
829 | 0 | } |
830 | | |
831 | | // Extract the instance information. |
832 | 1.21M | MarkDirty(); |
833 | | |
834 | 1.21M | return poType->SetInstValue(pszFieldPath, pabyData, nDataPos, nDataSize, |
835 | 1.21M | chReqType, pValue); |
836 | 1.21M | } |
837 | | |
838 | | /************************************************************************/ |
839 | | /* SetStringField() */ |
840 | | /************************************************************************/ |
841 | | |
842 | | CPLErr HFAEntry::SetStringField(const char *pszFieldPath, const char *pszValue) |
843 | | |
844 | 259k | { |
845 | 259k | return SetFieldValue(pszFieldPath, 's', (void *)pszValue); |
846 | 259k | } |
847 | | |
848 | | /************************************************************************/ |
849 | | /* SetIntField() */ |
850 | | /************************************************************************/ |
851 | | |
852 | | CPLErr HFAEntry::SetIntField(const char *pszFieldPath, int nValue) |
853 | | |
854 | 436k | { |
855 | 436k | return SetFieldValue(pszFieldPath, 'i', &nValue); |
856 | 436k | } |
857 | | |
858 | | /************************************************************************/ |
859 | | /* SetDoubleField() */ |
860 | | /************************************************************************/ |
861 | | |
862 | | CPLErr HFAEntry::SetDoubleField(const char *pszFieldPath, double dfValue) |
863 | | |
864 | 516k | { |
865 | 516k | return SetFieldValue(pszFieldPath, 'd', &dfValue); |
866 | 516k | } |
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 | | void HFAEntry::SetPosition() |
877 | | |
878 | 358k | { |
879 | | // Establish the location of this entry, and its data. |
880 | 358k | if (nFilePos == 0) |
881 | 224k | { |
882 | 224k | const auto nFilePos64 = |
883 | 224k | HFAAllocateSpace(psHFA, psHFA->nEntryHeaderLength + nDataSize); |
884 | 224k | if (nFilePos64 >= static_cast<unsigned>(INT_MAX)) |
885 | 0 | return; |
886 | 224k | nFilePos = static_cast<int>(nFilePos64); |
887 | | |
888 | 224k | if (nDataSize > 0) |
889 | 224k | nDataPos = nFilePos + psHFA->nEntryHeaderLength; |
890 | 224k | } |
891 | | |
892 | | // Force all children to set their position. |
893 | 633k | for (HFAEntry *poThisChild = poChild; poThisChild != nullptr; |
894 | 358k | poThisChild = poThisChild->poNext) |
895 | 274k | { |
896 | 274k | poThisChild->SetPosition(); |
897 | 274k | } |
898 | 358k | } |
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 | | CPLErr HFAEntry::FlushToDisk() |
908 | | |
909 | 275k | { |
910 | | // If we are the root node, call SetPosition() on the whole |
911 | | // tree to ensure that all entries have an allocated position. |
912 | 275k | if (poParent == nullptr) |
913 | 333 | SetPosition(); |
914 | | |
915 | | // Only write this node out if it is dirty. |
916 | 275k | if (bDirty) |
917 | 259k | { |
918 | | // Ensure we know where the relative entries are located. |
919 | 259k | if (poNext != nullptr) |
920 | 189k | nNextPos = poNext->nFilePos; |
921 | | |
922 | 259k | if (poChild != nullptr) |
923 | 54.9k | nChildPos = poChild->nFilePos; |
924 | | |
925 | | // Write the Ehfa_Entry fields. |
926 | | |
927 | | // VSIFFlushL(psHFA->fp); |
928 | 259k | if (VSIFSeekL(psHFA->fp, static_cast<vsi_l_offset>(nFilePos), |
929 | 259k | SEEK_SET) != 0) |
930 | 0 | { |
931 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
932 | 0 | "Failed to seek to %d for writing, out of disk space?", |
933 | 0 | nFilePos); |
934 | 0 | return CE_Failure; |
935 | 0 | } |
936 | | |
937 | 259k | GUInt32 nLong = nNextPos; |
938 | 259k | HFAStandard(4, &nLong); |
939 | 259k | bool bOK = VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0; |
940 | | |
941 | 259k | if (poPrev != nullptr) |
942 | 189k | nLong = poPrev->nFilePos; |
943 | 70.0k | else |
944 | 70.0k | nLong = 0; |
945 | 259k | HFAStandard(4, &nLong); |
946 | 259k | bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0; |
947 | | |
948 | 259k | if (poParent != nullptr) |
949 | 259k | nLong = poParent->nFilePos; |
950 | 188 | else |
951 | 188 | nLong = 0; |
952 | 259k | HFAStandard(4, &nLong); |
953 | 259k | bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0; |
954 | | |
955 | 259k | nLong = nChildPos; |
956 | 259k | HFAStandard(4, &nLong); |
957 | 259k | bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0; |
958 | | |
959 | 259k | nLong = nDataPos; |
960 | 259k | HFAStandard(4, &nLong); |
961 | 259k | bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0; |
962 | | |
963 | 259k | nLong = nDataSize; |
964 | 259k | HFAStandard(4, &nLong); |
965 | 259k | bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0; |
966 | | |
967 | 259k | bOK &= VSIFWriteL(szName, 1, 64, psHFA->fp) > 0; |
968 | 259k | bOK &= VSIFWriteL(szType, 1, 32, psHFA->fp) > 0; |
969 | | |
970 | 259k | nLong = 0; // Should we keep the time, or set it more reasonably? |
971 | 259k | bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0; |
972 | 259k | if (!bOK) |
973 | 0 | { |
974 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
975 | 0 | "Failed to write HFAEntry %s(%s), out of disk space?", |
976 | 0 | szName, szType); |
977 | 0 | return CE_Failure; |
978 | 0 | } |
979 | | |
980 | | // Write out the data. |
981 | | // VSIFFlushL(psHFA->fp); |
982 | 259k | if (nDataSize > 0 && pabyData != nullptr) |
983 | 242k | { |
984 | 242k | if (VSIFSeekL(psHFA->fp, static_cast<vsi_l_offset>(nDataPos), |
985 | 242k | SEEK_SET) != 0 || |
986 | 242k | VSIFWriteL(pabyData, nDataSize, 1, psHFA->fp) != 1) |
987 | 0 | { |
988 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
989 | 0 | "Failed to write %d bytes HFAEntry %s(%s) data, " |
990 | 0 | "out of disk space?", |
991 | 0 | nDataSize, szName, szType); |
992 | 0 | return CE_Failure; |
993 | 0 | } |
994 | 242k | } |
995 | | |
996 | | // VSIFFlushL(psHFA->fp); |
997 | 259k | } |
998 | | |
999 | | // Process all the children of this node. |
1000 | 549k | for (HFAEntry *poThisChild = poChild; poThisChild != nullptr; |
1001 | 275k | poThisChild = poThisChild->poNext) |
1002 | 274k | { |
1003 | 274k | CPLErr eErr = poThisChild->FlushToDisk(); |
1004 | 274k | if (eErr != CE_None) |
1005 | 0 | return eErr; |
1006 | 274k | } |
1007 | | |
1008 | 275k | bDirty = false; |
1009 | | |
1010 | 275k | return CE_None; |
1011 | 275k | } |
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 | | void HFAEntry::MarkDirty() |
1021 | | |
1022 | 2.14M | { |
1023 | 2.14M | bDirty = true; |
1024 | 2.14M | psHFA->bTreeDirty = true; |
1025 | 2.14M | } |