Coverage Report

Created: 2026-08-11 08:26

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.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
}