Coverage Report

Created: 2026-11-12 08:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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.12M
    : bDirty(false), nFilePos(0), psHFA(nullptr), poParent(nullptr),
42
1.12M
      poPrev(nullptr), nNextPos(0), poNext(nullptr), nChildPos(0),
43
1.12M
      poChild(nullptr), poType(nullptr), nDataPos(0), nDataSize(0),
44
1.12M
      pabyData(nullptr), bIsMIFObject(false)
45
1.12M
{
46
1.12M
    szName[0] = '\0';
47
1.12M
    szType[0] = '\0';
48
1.12M
}
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.12M
{
60
1.12M
    HFAEntry *poEntry = new HFAEntry;
61
1.12M
    poEntry->psHFA = psHFAIn;
62
63
1.12M
    poEntry->nFilePos = nPos;
64
1.12M
    poEntry->poParent = poParentIn;
65
1.12M
    poEntry->poPrev = poPrevIn;
66
67
    // Read the entry information from the file.
68
1.12M
    GInt32 anEntryNums[6] = {};
69
70
1.12M
    if (VSIFSeekL(poEntry->psHFA->fp,
71
1.12M
                  static_cast<vsi_l_offset>(poEntry->nFilePos),
72
1.12M
                  SEEK_SET) == -1 ||
73
1.12M
        VSIFReadL(anEntryNums, sizeof(GInt32) * 6, 1, poEntry->psHFA->fp) < 1)
74
366k
    {
75
366k
        CPLError(CE_Failure, CPLE_FileIO,
76
366k
                 "VSIFReadL(%p,6*4) @ %u failed in HFAEntry().\n%s",
77
366k
                 poEntry->psHFA->fp, poEntry->nFilePos, VSIStrerror(errno));
78
366k
        delete poEntry;
79
366k
        return nullptr;
80
366k
    }
81
82
5.31M
    for (int i = 0; i < 6; i++)
83
4.55M
        HFAStandard(4, anEntryNums + i);
84
85
759k
    poEntry->nNextPos = anEntryNums[0];
86
759k
    poEntry->nChildPos = anEntryNums[3];
87
759k
    poEntry->nDataPos = anEntryNums[4];
88
759k
    poEntry->nDataSize = anEntryNums[5];
89
90
    // Read the name, and type.
91
759k
    if (VSIFReadL(poEntry->szName, 64, 1, poEntry->psHFA->fp) < 1 ||
92
758k
        VSIFReadL(poEntry->szType, 32, 1, poEntry->psHFA->fp) < 1)
93
1.84k
    {
94
1.84k
        poEntry->szName[sizeof(poEntry->szName) - 1] = '\0';
95
1.84k
        poEntry->szType[sizeof(poEntry->szType) - 1] = '\0';
96
1.84k
        CPLError(CE_Failure, CPLE_FileIO, "VSIFReadL() failed in HFAEntry().");
97
1.84k
        delete poEntry;
98
1.84k
        return nullptr;
99
1.84k
    }
100
757k
    poEntry->szName[sizeof(poEntry->szName) - 1] = '\0';
101
757k
    poEntry->szType[sizeof(poEntry->szType) - 1] = '\0';
102
757k
    return poEntry;
103
759k
}
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
160k
    : nFilePos(0), psHFA(psHFAIn), poParent(poParentIn), poPrev(nullptr),
115
160k
      nNextPos(0), poNext(nullptr), nChildPos(0), poChild(nullptr),
116
160k
      poType(nullptr), nDataPos(0), nDataSize(0), pabyData(nullptr),
117
160k
      bIsMIFObject(false)
118
160k
{
119
    // Initialize Entry.
120
160k
    SetName(pszNodeName);
121
160k
    memset(szType, 0, sizeof(szType));
122
160k
    snprintf(szType, sizeof(szType), "%s", pszTypeName);
123
124
    // Update the previous or parent node to refer to this one.
125
160k
    if (poParent == nullptr)
126
187
    {
127
        // Do nothing.
128
187
    }
129
160k
    else if (poParent->poChild == nullptr)
130
37.6k
    {
131
37.6k
        poParent->poChild = this;
132
37.6k
        poParent->MarkDirty();
133
37.6k
    }
134
122k
    else
135
122k
    {
136
122k
        poPrev = poParent->poChild;
137
2.63M
        while (poPrev->poNext != nullptr)
138
2.50M
            poPrev = poPrev->poNext;
139
140
122k
        poPrev->poNext = this;
141
122k
        poPrev->MarkDirty();
142
122k
    }
143
144
160k
    MarkDirty();
145
160k
}
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
160k
{
157
160k
    CPLAssert(poParentIn != nullptr);
158
160k
    return new HFAEntry(psHFAIn, pszNodeName, pszTypeName, poParentIn);
159
160k
}
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
16
{
170
16
    CPLString osFieldName;
171
172
16
    osFieldName.Printf("%s.%s", pszMIFObjectPath, "MIFDictionary");
173
16
    const char *pszField = poContainer->GetStringField(osFieldName.c_str());
174
16
    if (pszField == nullptr)
175
13
    {
176
13
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry",
177
13
                 osFieldName.c_str());
178
13
        return nullptr;
179
13
    }
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.28M
{
284
1.28M
    CPLFree(pabyData);
285
286
1.28M
    if (poNext != nullptr)
287
470k
        delete poNext;
288
289
1.28M
    if (poChild != nullptr)
290
431k
        delete poChild;
291
292
1.28M
    if (bIsMIFObject)
293
0
    {
294
0
        delete psHFA->poDictionary;
295
0
        CPLFree(psHFA);
296
0
    }
297
1.28M
}
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
161k
{
352
161k
    memset(szName, 0, sizeof(szName));
353
161k
    snprintf(szName, sizeof(szName), "%s", pszNodeName);
354
355
161k
    MarkDirty();
356
161k
}
357
358
/************************************************************************/
359
/*                              GetChild()                              */
360
/************************************************************************/
361
362
HFAEntry *HFAEntry::GetChild()
363
364
2.36M
{
365
    // Do we need to create the child node?
366
2.36M
    if (poChild == nullptr && nChildPos != 0)
367
644k
    {
368
644k
        poChild = HFAEntry::New(psHFA, nChildPos, this, nullptr);
369
644k
        if (poChild == nullptr)
370
250k
            nChildPos = 0;
371
644k
    }
372
373
2.36M
    return poChild;
374
2.36M
}
375
376
/************************************************************************/
377
/*                              GetNext()                               */
378
/************************************************************************/
379
380
HFAEntry *HFAEntry::GetNext()
381
382
3.83M
{
383
    // Do we need to create the next node?
384
3.83M
    if (poNext == nullptr && nNextPos != 0)
385
466k
    {
386
        // Check if we have a loop on the next node in this sibling chain.
387
466k
        HFAEntry *poPast;
388
389
3.46M
        for (poPast = this; poPast != nullptr && poPast->nFilePos != nNextPos;
390
3.00M
             poPast = poPast->poPrev)
391
3.00M
        {
392
3.00M
        }
393
394
466k
        if (poPast != nullptr)
395
1.06k
        {
396
1.06k
            CPLError(CE_Warning, CPLE_AppDefined,
397
1.06k
                     "Corrupt (looping) entry in %s, "
398
1.06k
                     "ignoring some entries after %s.",
399
1.06k
                     psHFA->pszFilename, szName);
400
1.06k
            nNextPos = 0;
401
1.06k
            return nullptr;
402
1.06k
        }
403
404
465k
        poNext = HFAEntry::New(psHFA, nNextPos, poParent, this);
405
465k
        if (poNext == nullptr)
406
117k
            nNextPos = 0;
407
465k
    }
408
409
3.83M
    return poNext;
410
3.83M
}
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
3.69M
{
422
3.69M
    if (pabyData != nullptr || nDataSize == 0)
423
3.62M
        return;
424
66.0k
    if (nDataSize > INT_MAX - 1)
425
2.70k
    {
426
2.70k
        CPLError(CE_Failure, CPLE_AppDefined,
427
2.70k
                 "Invalid value for nDataSize = %u", nDataSize);
428
2.70k
        return;
429
2.70k
    }
430
431
    // Allocate buffer, and read data.
432
63.3k
    pabyData = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nDataSize + 1));
433
63.3k
    if (pabyData == nullptr)
434
0
    {
435
0
        return;
436
0
    }
437
438
63.3k
    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
63.3k
    if (VSIFReadL(pabyData, nDataSize, 1, psHFA->fp) < 1)
446
2.61k
    {
447
2.61k
        CPLError(CE_Failure, CPLE_FileIO,
448
2.61k
                 "VSIFReadL() failed in HFAEntry::LoadData().");
449
2.61k
        return;
450
2.61k
    }
451
452
    // Make sure the buffer is always null terminated to avoid
453
    // issues when extracting strings from a corrupted file.
454
60.7k
    pabyData[nDataSize] = '\0';
455
456
    // Get the type corresponding to this entry.
457
60.7k
    poType = psHFA->poDictionary->FindType(szType);
458
60.7k
    if (poType == nullptr)
459
1.68k
        return;
460
60.7k
}
461
462
/************************************************************************/
463
/*                           GetTypeObject()                            */
464
/************************************************************************/
465
466
HFAType *HFAEntry::GetTypeObject()
467
468
5.87k
{
469
5.87k
    if (poType == nullptr)
470
5.87k
        poType = psHFA->poDictionary->FindType(szType);
471
472
5.87k
    return poType;
473
5.87k
}
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
944k
{
488
944k
    if (poType == nullptr)
489
154k
    {
490
154k
        poType = psHFA->poDictionary->FindType(szType);
491
154k
        if (poType == nullptr)
492
0
            return nullptr;
493
154k
    }
494
495
944k
    if (nSize == 0 && poType->nBytes > 0)
496
309k
        nSize = poType->nBytes;
497
498
    // nDataSize is a GUInt32.
499
944k
    if (static_cast<int>(nDataSize) < nSize && nSize > 0)
500
160k
    {
501
160k
        pabyData = static_cast<GByte *>(CPLRealloc(pabyData, nSize));
502
160k
        memset(pabyData + nDataSize, 0, nSize - nDataSize);
503
160k
        nDataSize = nSize;
504
505
160k
        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
160k
        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
160k
    }
525
783k
    else
526
783k
    {
527
783k
        LoadData();  // Make sure the data is loaded before we return pointer.
528
783k
    }
529
530
944k
    return pabyData;
531
944k
}
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.37M
{
566
1.37M
    std::vector<HFAEntry *> apoChildren;
567
568
1.37M
    if (*pbErrorDetected)
569
0
        return apoChildren;
570
1.37M
    if (nRecLevel == 50)
571
10.4k
    {
572
10.4k
        CPLError(CE_Failure, CPLE_AppDefined,
573
10.4k
                 "Bad entry structure: recursion detected !");
574
10.4k
        *pbErrorDetected = TRUE;
575
10.4k
        return apoChildren;
576
10.4k
    }
577
578
2.19M
    for (HFAEntry *poEntry = GetChild(); poEntry != nullptr;
579
1.36M
         poEntry = poEntry->GetNext())
580
1.35M
    {
581
1.35M
        std::vector<HFAEntry *> apoEntryChildren;
582
583
1.35M
        if ((pszName == nullptr || EQUAL(poEntry->GetName(), pszName)) &&
584
1.35M
            (pszType == nullptr || EQUAL(poEntry->GetType(), pszType)))
585
2.77k
            apoChildren.push_back(poEntry);
586
587
1.35M
        apoEntryChildren = poEntry->FindChildren(
588
1.35M
            pszName, pszType, nRecLevel + 1, pbErrorDetected);
589
1.35M
        if (*pbErrorDetected)
590
522k
            return apoChildren;
591
592
836k
        for (size_t i = 0; i < apoEntryChildren.size(); i++)
593
4.60k
            apoChildren.push_back(apoEntryChildren[i]);
594
831k
    }
595
596
843k
    return apoChildren;
597
1.36M
}
598
599
std::vector<HFAEntry *> HFAEntry::FindChildren(const char *pszName,
600
                                               const char *pszType)
601
602
22.3k
{
603
22.3k
    int bErrorDetected = FALSE;
604
22.3k
    return FindChildren(pszName, pszType, 0, &bErrorDetected);
605
22.3k
}
606
607
/************************************************************************/
608
/*                           GetNamedChild()                            */
609
/************************************************************************/
610
611
HFAEntry *HFAEntry::GetNamedChild(const char *pszName)
612
613
912k
{
614
    // Establish how much of this name path is for the next child.
615
    // Up to the '.' or end of the string.
616
912k
    int nNameLen = 0;
617
13.8M
    for (; pszName[nNameLen] != '.' && pszName[nNameLen] != '\0' &&
618
12.9M
           pszName[nNameLen] != ':';
619
12.9M
         nNameLen++)
620
12.9M
    {
621
12.9M
    }
622
623
    // Scan children looking for this name.
624
3.61M
    for (HFAEntry *poEntry = GetChild(); poEntry != nullptr;
625
2.70M
         poEntry = poEntry->GetNext())
626
2.91M
    {
627
2.91M
        if (EQUALN(poEntry->GetName(), pszName, nNameLen) &&
628
243k
            static_cast<int>(strlen(poEntry->GetName())) == nNameLen)
629
213k
        {
630
213k
            if (pszName[nNameLen] == '.')
631
8.45k
            {
632
8.45k
                HFAEntry *poResult;
633
634
8.45k
                poResult = poEntry->GetNamedChild(pszName + nNameLen + 1);
635
8.45k
                if (poResult != nullptr)
636
2.95k
                    return poResult;
637
8.45k
            }
638
204k
            else
639
204k
                return poEntry;
640
213k
        }
641
2.91M
    }
642
643
704k
    return nullptr;
644
912k
}
645
646
/************************************************************************/
647
/*                           GetFieldValue()                            */
648
/************************************************************************/
649
650
bool HFAEntry::GetFieldValue(const char *pszFieldPath, char chReqType,
651
                             void *pReqReturn, int *pnRemainingDataSize)
652
653
2.02M
{
654
    // Is there a node path in this string?
655
2.02M
    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
2.02M
    LoadData();
666
667
2.02M
    if (pabyData == nullptr)
668
5.10k
        return false;
669
670
2.02M
    if (poType == nullptr)
671
29.9k
        return false;
672
673
    // Extract the instance information.
674
1.99M
    return poType->ExtractInstValue(pszFieldPath, pabyData, nDataPos, nDataSize,
675
1.99M
                                    chReqType, pReqReturn, pnRemainingDataSize);
676
2.02M
}
677
678
/************************************************************************/
679
/*                           GetFieldCount()                            */
680
/************************************************************************/
681
682
int HFAEntry::GetFieldCount(const char *pszFieldPath, CPLErr * /* peErr */)
683
10.3k
{
684
    // Is there a node path in this string?
685
10.3k
    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.3k
    LoadData();
696
697
10.3k
    if (pabyData == nullptr)
698
54
        return -1;
699
700
10.3k
    if (poType == nullptr)
701
1.04k
        return -1;
702
703
    // Extract the instance information.
704
705
9.27k
    return poType->GetInstCount(pszFieldPath, pabyData, nDataPos, nDataSize);
706
10.3k
}
707
708
/************************************************************************/
709
/*                            GetIntField()                             */
710
/************************************************************************/
711
712
GInt32 HFAEntry::GetIntField(const char *pszFieldPath, CPLErr *peErr)
713
714
966k
{
715
966k
    GInt32 nIntValue = 0;
716
717
966k
    if (!GetFieldValue(pszFieldPath, 'i', &nIntValue, nullptr))
718
47.5k
    {
719
47.5k
        if (peErr != nullptr)
720
399
            *peErr = CE_Failure;
721
722
47.5k
        return 0;
723
47.5k
    }
724
725
919k
    if (peErr != nullptr)
726
470k
        *peErr = CE_None;
727
728
919k
    return nIntValue;
729
966k
}
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
854k
{
765
854k
    double dfDoubleValue = 0;
766
767
854k
    if (!GetFieldValue(pszFieldPath, 'd', &dfDoubleValue, nullptr))
768
66.6k
    {
769
66.6k
        if (peErr != nullptr)
770
6.06k
            *peErr = CE_Failure;
771
772
66.6k
        return 0.0;
773
66.6k
    }
774
775
788k
    if (peErr != nullptr)
776
738k
        *peErr = CE_None;
777
778
788k
    return dfDoubleValue;
779
854k
}
780
781
/************************************************************************/
782
/*                           GetStringField()                           */
783
/************************************************************************/
784
785
const char *HFAEntry::GetStringField(const char *pszFieldPath, CPLErr *peErr,
786
                                     int *pnRemainingDataSize)
787
788
203k
{
789
203k
    char *pszResult = nullptr;
790
791
203k
    if (!GetFieldValue(pszFieldPath, 's', &pszResult, pnRemainingDataSize))
792
23.1k
    {
793
23.1k
        if (peErr != nullptr)
794
9.08k
            *peErr = CE_Failure;
795
796
23.1k
        return nullptr;
797
23.1k
    }
798
799
180k
    if (peErr != nullptr)
800
156k
        *peErr = CE_None;
801
802
180k
    return pszResult;
803
203k
}
804
805
/************************************************************************/
806
/*                           SetFieldValue()                            */
807
/************************************************************************/
808
809
CPLErr HFAEntry::SetFieldValue(const char *pszFieldPath, char chReqType,
810
                               void *pValue)
811
812
848k
{
813
    // Is there a node path in this string?
814
848k
    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
848k
    LoadData();
826
848k
    if (MakeData() == nullptr || pabyData == nullptr || poType == nullptr)
827
0
    {
828
0
        return CE_Failure;
829
0
    }
830
831
    // Extract the instance information.
832
848k
    MarkDirty();
833
834
848k
    return poType->SetInstValue(pszFieldPath, pabyData, nDataPos, nDataSize,
835
848k
                                chReqType, pValue);
836
848k
}
837
838
/************************************************************************/
839
/*                           SetStringField()                           */
840
/************************************************************************/
841
842
CPLErr HFAEntry::SetStringField(const char *pszFieldPath, const char *pszValue)
843
844
188k
{
845
188k
    return SetFieldValue(pszFieldPath, 's', (void *)pszValue);
846
188k
}
847
848
/************************************************************************/
849
/*                            SetIntField()                             */
850
/************************************************************************/
851
852
CPLErr HFAEntry::SetIntField(const char *pszFieldPath, int nValue)
853
854
323k
{
855
323k
    return SetFieldValue(pszFieldPath, 'i', &nValue);
856
323k
}
857
858
/************************************************************************/
859
/*                           SetDoubleField()                           */
860
/************************************************************************/
861
862
CPLErr HFAEntry::SetDoubleField(const char *pszFieldPath, double dfValue)
863
864
336k
{
865
336k
    return SetFieldValue(pszFieldPath, 'd', &dfValue);
866
336k
}
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
258k
{
879
    // Establish the location of this entry, and its data.
880
258k
    if (nFilePos == 0)
881
160k
    {
882
160k
        const auto nFilePos64 =
883
160k
            HFAAllocateSpace(psHFA, psHFA->nEntryHeaderLength + nDataSize);
884
160k
        if (nFilePos64 >= static_cast<unsigned>(INT_MAX))
885
0
            return;
886
160k
        nFilePos = static_cast<int>(nFilePos64);
887
888
160k
        if (nDataSize > 0)
889
160k
            nDataPos = nFilePos + psHFA->nEntryHeaderLength;
890
160k
    }
891
892
    // Force all children to set their position.
893
457k
    for (HFAEntry *poThisChild = poChild; poThisChild != nullptr;
894
258k
         poThisChild = poThisChild->poNext)
895
199k
    {
896
199k
        poThisChild->SetPosition();
897
199k
    }
898
258k
}
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
199k
{
910
    // If we are the root node, call SetPosition() on the whole
911
    // tree to ensure that all entries have an allocated position.
912
199k
    if (poParent == nullptr)
913
332
        SetPosition();
914
915
    // Only write this node out if it is dirty.
916
199k
    if (bDirty)
917
186k
    {
918
        // Ensure we know where the relative entries are located.
919
186k
        if (poNext != nullptr)
920
136k
            nNextPos = poNext->nFilePos;
921
922
186k
        if (poChild != nullptr)
923
39.4k
            nChildPos = poChild->nFilePos;
924
925
        // Write the Ehfa_Entry fields.
926
927
        // VSIFFlushL(psHFA->fp);
928
186k
        if (VSIFSeekL(psHFA->fp, static_cast<vsi_l_offset>(nFilePos),
929
186k
                      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
186k
        GUInt32 nLong = nNextPos;
938
186k
        HFAStandard(4, &nLong);
939
186k
        bool bOK = VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
940
941
186k
        if (poPrev != nullptr)
942
136k
            nLong = poPrev->nFilePos;
943
50.3k
        else
944
50.3k
            nLong = 0;
945
186k
        HFAStandard(4, &nLong);
946
186k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
947
948
186k
        if (poParent != nullptr)
949
186k
            nLong = poParent->nFilePos;
950
187
        else
951
187
            nLong = 0;
952
186k
        HFAStandard(4, &nLong);
953
186k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
954
955
186k
        nLong = nChildPos;
956
186k
        HFAStandard(4, &nLong);
957
186k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
958
959
186k
        nLong = nDataPos;
960
186k
        HFAStandard(4, &nLong);
961
186k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
962
963
186k
        nLong = nDataSize;
964
186k
        HFAStandard(4, &nLong);
965
186k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
966
967
186k
        bOK &= VSIFWriteL(szName, 1, 64, psHFA->fp) > 0;
968
186k
        bOK &= VSIFWriteL(szType, 1, 32, psHFA->fp) > 0;
969
970
186k
        nLong = 0;  // Should we keep the time, or set it more reasonably?
971
186k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
972
186k
        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
186k
        if (nDataSize > 0 && pabyData != nullptr)
983
174k
        {
984
174k
            if (VSIFSeekL(psHFA->fp, static_cast<vsi_l_offset>(nDataPos),
985
174k
                          SEEK_SET) != 0 ||
986
174k
                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
174k
        }
995
996
        // VSIFFlushL(psHFA->fp);
997
186k
    }
998
999
    // Process all the children of this node.
1000
399k
    for (HFAEntry *poThisChild = poChild; poThisChild != nullptr;
1001
199k
         poThisChild = poThisChild->poNext)
1002
199k
    {
1003
199k
        CPLErr eErr = poThisChild->FlushToDisk();
1004
199k
        if (eErr != CE_None)
1005
0
            return eErr;
1006
199k
    }
1007
1008
199k
    bDirty = false;
1009
1010
199k
    return CE_None;
1011
199k
}
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
1.51M
{
1023
1.51M
    bDirty = true;
1024
1.51M
    psHFA->bTreeDirty = true;
1025
1.51M
}