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