Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/iso8211/ddffield.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  ISO 8211 Access
4
 * Purpose:  Implements the DDFField class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 * Copyright (c) 2026, Even Rouault
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "iso8211.h"
16
17
#include <cstdio>
18
#include <cstdlib>
19
20
#include <algorithm>
21
22
#include "cpl_conv.h"
23
#include "cpl_enumerate.h"
24
25
// Note, we implement no constructor for this class to make instantiation
26
// cheaper.  It is required that the Initialize() be called before anything
27
// else.
28
29
/************************************************************************/
30
/*                             Initialize()                             */
31
/************************************************************************/
32
33
bool DDFField::Initialize(const DDFFieldDefn *poDefnIn, const char *pachDataIn,
34
                          int nDataSizeIn, bool bInitializeParts)
35
36
394k
{
37
394k
    pachData = pachDataIn;
38
394k
    nDataSize = nDataSizeIn;
39
394k
    poDefn = poDefnIn;
40
41
394k
    return bInitializeParts ? InitializeParts() : true;
42
394k
}
43
44
/************************************************************************/
45
/*                          InitializeParts()                           */
46
/************************************************************************/
47
48
bool DDFField::InitializeParts()
49
357k
{
50
357k
    const bool bCreateParts = apoFieldParts.empty();
51
357k
    const size_t nDefnPartsCount = poDefn->GetParts().size();
52
357k
    CPLAssert(bCreateParts || apoFieldParts.size() == nDefnPartsCount);
53
54
357k
    int iOffset = 0;
55
357k
    for (const auto &[iPart, poFieldDefnPart] :
56
357k
         cpl::enumerate(poDefn->GetParts()))
57
35.9k
    {
58
35.9k
        if (iOffset > nDataSize)
59
19
        {
60
19
            CPLError(CE_Failure, CPLE_AppDefined,
61
19
                     "Not enough bytes for part %d of field %s",
62
19
                     static_cast<int>(iPart), poDefn->GetName());
63
19
            return false;
64
19
        }
65
35.9k
        const int iOffsetBefore = iOffset;
66
35.9k
        if (nDataSize > 0)
67
35.3k
        {
68
35.3k
            if (iPart + 1 < nDefnPartsCount)
69
17.6k
            {
70
17.6k
                for (const auto &poThisSFDefn : poFieldDefnPart->GetSubfields())
71
142k
                {
72
142k
                    int nBytesConsumed = 0;
73
142k
                    poThisSFDefn->GetDataLength(pachData + iOffset,
74
142k
                                                nDataSize - iOffset,
75
142k
                                                &nBytesConsumed);
76
77
142k
                    iOffset += nBytesConsumed;
78
142k
                }
79
17.6k
            }
80
17.6k
            else
81
17.6k
            {
82
17.6k
                iOffset = nDataSize;
83
17.6k
                if (pachData[nDataSize - 1] == DDF_FIELD_TERMINATOR)
84
14.9k
                    --iOffset;
85
17.6k
            }
86
35.3k
        }
87
88
35.9k
        if (bCreateParts)
89
32.4k
        {
90
32.4k
            auto poFieldPart = std::make_unique<DDFField>();
91
32.4k
            poFieldPart->Initialize(poFieldDefnPart.get(),
92
32.4k
                                    pachData + iOffsetBefore,
93
32.4k
                                    iOffset - iOffsetBefore, false);
94
32.4k
            apoFieldParts.push_back(std::move(poFieldPart));
95
32.4k
        }
96
3.46k
        else
97
3.46k
        {
98
3.46k
            apoFieldParts[iPart]->Initialize(poFieldDefnPart.get(),
99
3.46k
                                             pachData + iOffsetBefore,
100
3.46k
                                             iOffset - iOffsetBefore, false);
101
3.46k
        }
102
35.9k
    }
103
104
357k
    return true;
105
357k
}
106
107
/************************************************************************/
108
/*                                Dump()                                */
109
/************************************************************************/
110
111
/**
112
 * Write out field contents to debugging file.
113
 *
114
 * A variety of information about this field, and all its
115
 * subfields is written to the given debugging file handle.  Note that
116
 * field definition information (ala DDFFieldDefn) isn't written.
117
 *
118
 * @param fp The standard IO file handle to write to.  i.e. stderr
119
 */
120
121
void DDFField::Dump(FILE *fp, int nNestingLevel) const
122
123
0
{
124
0
    std::string osIndent;
125
0
    for (int i = 0; i < nNestingLevel; ++i)
126
0
        osIndent += "  ";
127
128
0
#define Print(...)                                                             \
129
0
    do                                                                         \
130
0
    {                                                                          \
131
0
        fprintf(fp, "%s", osIndent.c_str());                                   \
132
0
        fprintf(fp, __VA_ARGS__);                                              \
133
0
    } while (0)
134
135
0
    int nMaxRepeat = 8;
136
137
0
    const char *pszDDF_MAXDUMP = getenv("DDF_MAXDUMP");
138
0
    if (pszDDF_MAXDUMP != nullptr)
139
0
        nMaxRepeat = atoi(pszDDF_MAXDUMP);
140
141
0
    Print("DDFField:\n");
142
0
    Print("    Tag = `%s'\n", poDefn->GetName());
143
0
    Print("    DataSize = %d\n", nDataSize);
144
145
0
    if (!apoFieldParts.empty())
146
0
    {
147
0
        for (const auto &poPart : apoFieldParts)
148
0
        {
149
0
            poPart->Dump(fp, nNestingLevel + 1);
150
0
        }
151
0
        return;
152
0
    }
153
154
0
    Print("    Data = `");
155
0
    for (int i = 0; i < std::min(nDataSize, 40); i++)
156
0
    {
157
0
        if (pachData[i] < 32 || pachData[i] > 126)
158
0
            fprintf(fp, "\\%02X",
159
0
                    reinterpret_cast<const unsigned char *>(pachData)[i]);
160
0
        else
161
0
            fprintf(fp, "%c", pachData[i]);
162
0
    }
163
164
0
    if (nDataSize > 40)
165
0
        fprintf(fp, "...");
166
0
    fprintf(fp, "'\n");
167
168
    /* -------------------------------------------------------------------- */
169
    /*      dump the data of the subfields.                                 */
170
    /* -------------------------------------------------------------------- */
171
0
    int iOffset = 0;
172
0
    const int nRepeatCount = GetRepeatCount();
173
0
    for (int nLoopCount = 0; nLoopCount < nRepeatCount; nLoopCount++)
174
0
    {
175
0
        if (nLoopCount > nMaxRepeat)
176
0
        {
177
0
            Print("     ...\n");
178
0
            break;
179
0
        }
180
181
0
        for (const auto &poThisSFDefn : poDefn->GetSubfields())
182
0
        {
183
0
            poThisSFDefn->DumpData(pachData + iOffset, nDataSize - iOffset, fp);
184
185
0
            int nBytesConsumed = 0;
186
0
            poThisSFDefn->GetDataLength(pachData + iOffset, nDataSize - iOffset,
187
0
                                        &nBytesConsumed);
188
189
0
            iOffset += nBytesConsumed;
190
0
        }
191
0
    }
192
0
}
193
194
/************************************************************************/
195
/*                          GetSubfieldData()                           */
196
/************************************************************************/
197
198
/**
199
 * Fetch raw data pointer for a particular subfield of this field.
200
 *
201
 * The passed DDFSubfieldDefn (poSFDefn) should be acquired from the
202
 * DDFFieldDefn corresponding with this field.  This is normally done
203
 * once before reading any records.  This method involves a series of
204
 * calls to DDFSubfield::GetDataLength() in order to track through the
205
 * DDFField data to that belonging to the requested subfield.  This can
206
 * be relatively expensive.<p>
207
 *
208
 * @param poSFDefn The definition of the subfield for which the raw
209
 * data pointer is desired.
210
 * @param pnMaxBytes The maximum number of bytes that can be accessed from
211
 * the returned data pointer is placed in this int, unless it is NULL.
212
 * @param iSubfieldIndex The instance of this subfield to fetch.  Use zero
213
 * (the default) for the first instance.
214
 *
215
 * @return A pointer into the DDFField's data that belongs to the subfield.
216
 * This returned pointer is invalidated by the next record read
217
 * (DDFRecord::ReadRecord()) and the returned pointer should not be freed
218
 * by the application.
219
 */
220
221
const char *DDFField::GetSubfieldData(const DDFSubfieldDefn *poSFDefn,
222
                                      int *pnMaxBytes, int iSubfieldIndex) const
223
224
1.32M
{
225
1.32M
    if (poSFDefn == nullptr)
226
0
        return nullptr;
227
228
1.32M
    int iOffset = 0;
229
1.32M
    if (iSubfieldIndex > 0 && poDefn->GetFixedWidth() > 0)
230
206k
    {
231
206k
        iOffset = poDefn->GetFixedWidth() * iSubfieldIndex;
232
206k
        iSubfieldIndex = 0;
233
206k
    }
234
235
2.10M
    while (iSubfieldIndex >= 0)
236
2.10M
    {
237
2.10M
        for (const auto &poThisSFDefn : poDefn->GetSubfields())
238
7.28M
        {
239
7.28M
            if (nDataSize <= iOffset)
240
235k
            {
241
235k
                CPLError(CE_Failure, CPLE_AppDefined,
242
235k
                         "Invalid data size for subfield %s of %s",
243
235k
                         poThisSFDefn->GetName(), poDefn->GetName());
244
235k
                return nullptr;
245
235k
            }
246
247
7.05M
            if (poThisSFDefn.get() == poSFDefn && iSubfieldIndex == 0)
248
1.09M
            {
249
1.09M
                if (pnMaxBytes != nullptr)
250
1.09M
                    *pnMaxBytes = nDataSize - iOffset;
251
252
1.09M
                return pachData + iOffset;
253
1.09M
            }
254
255
5.95M
            int nBytesConsumed = 0;
256
5.95M
            poThisSFDefn->GetDataLength(pachData + iOffset, nDataSize - iOffset,
257
5.95M
                                        &nBytesConsumed);
258
5.95M
            iOffset += nBytesConsumed;
259
5.95M
        }
260
261
772k
        iSubfieldIndex--;
262
772k
    }
263
264
    // We didn't find our target subfield or instance!
265
97
    return nullptr;
266
1.32M
}
267
268
/************************************************************************/
269
/*                           GetRepeatCount()                           */
270
/************************************************************************/
271
272
/**
273
 * How many times do the subfields of this record repeat?  This
274
 * will always be one for non-repeating fields.
275
 *
276
 * @return The number of times that the subfields of this record occur
277
 * in this record.  This will be one for non-repeating fields.
278
 *
279
 * @see <a href="example.html">8211view example program</a>
280
 * for a demonstration of handling repeated fields properly.
281
 */
282
283
int DDFField::GetRepeatCount() const
284
285
328k
{
286
328k
    if (!apoFieldParts.empty())
287
0
        return 0;
288
289
328k
    if (!poDefn->IsRepeating())
290
196k
        return 1;
291
292
    /* -------------------------------------------------------------------- */
293
    /*      The occurrence count depends on how many copies of this         */
294
    /*      field's list of subfields can fit into the data space.          */
295
    /* -------------------------------------------------------------------- */
296
132k
    if (poDefn->GetFixedWidth())
297
87.3k
    {
298
87.3k
        return nDataSize / poDefn->GetFixedWidth();
299
87.3k
    }
300
301
    /* -------------------------------------------------------------------- */
302
    /*      Note that it may be legal to have repeating variable width      */
303
    /*      subfields, but I don't have any samples, so I ignore it for     */
304
    /*      now.                                                            */
305
    /*                                                                      */
306
    /*      The file data/cape_royal_AZ_DEM/1183XREF.DDF has a repeating    */
307
    /*      variable length field, but the count is one, so it isn't        */
308
    /*      much value for testing.                                         */
309
    /* -------------------------------------------------------------------- */
310
44.7k
    int iOffset = 0;
311
44.7k
    int iRepeatCount = 1;
312
313
128k
    while (true)
314
128k
    {
315
128k
        const int iOffsetBefore = iOffset;
316
128k
        for (const auto &poThisSFDefn : poDefn->GetSubfields())
317
335k
        {
318
335k
            int nBytesConsumed = 0;
319
335k
            if (poThisSFDefn->GetWidth() > nDataSize - iOffset)
320
5.80k
                nBytesConsumed = poThisSFDefn->GetWidth();
321
330k
            else
322
330k
                poThisSFDefn->GetDataLength(
323
330k
                    pachData + iOffset, nDataSize - iOffset, &nBytesConsumed);
324
325
335k
            iOffset += nBytesConsumed;
326
335k
            if (iOffset > nDataSize)
327
25.3k
                return iRepeatCount - 1;
328
335k
        }
329
103k
        if (iOffset == iOffsetBefore)
330
3.37k
        {
331
            // Should probably emit error
332
3.37k
            return iRepeatCount - 1;
333
3.37k
        }
334
335
100k
        if (iOffset > nDataSize - 2)
336
16.0k
            return iRepeatCount;
337
338
84.0k
        iRepeatCount++;
339
84.0k
    }
340
44.7k
}
341
342
/************************************************************************/
343
/*                          GetInstanceData()                           */
344
/************************************************************************/
345
346
/**
347
 * Get field instance data and size.
348
 *
349
 * The returned data pointer and size values are suitable for use with
350
 * DDFRecord::SetFieldRaw().
351
 *
352
 * @param nInstance a value from 0 to GetRepeatCount()-1.
353
 * @param pnInstanceSize a location to put the size (in bytes) of the
354
 * field instance data returned.  This size will include the unit terminator
355
 * (if any), but not the field terminator.  This size pointer may be NULL
356
 * if not needed.
357
 *
358
 * @return the data pointer, or NULL on error.
359
 */
360
361
const char *DDFField::GetInstanceData(int nInstance, int *pnInstanceSize)
362
363
4
{
364
4
    const int nRepeatCount = GetRepeatCount();
365
4
    if (!apoFieldParts.empty() && nInstance == 0)
366
0
    {
367
0
        const char *pachWrkData = GetData();
368
0
        if (pnInstanceSize != nullptr)
369
0
            *pnInstanceSize = GetDataSize();
370
0
        return pachWrkData;
371
0
    }
372
373
4
    if (nInstance < 0 || nInstance >= nRepeatCount)
374
0
        return nullptr;
375
376
    /* -------------------------------------------------------------------- */
377
    /*      Special case for fields without subfields (like "0001").  We    */
378
    /*      don't currently handle repeating simple fields.                 */
379
    /* -------------------------------------------------------------------- */
380
4
    if (poDefn->GetSubfieldCount() == 0)
381
0
    {
382
0
        const char *pachWrkData = GetData();
383
0
        if (pnInstanceSize != nullptr)
384
0
            *pnInstanceSize = GetDataSize();
385
0
        return pachWrkData;
386
0
    }
387
388
    /* -------------------------------------------------------------------- */
389
    /*      Get a pointer to the start of the existing data for this        */
390
    /*      iteration of the field.                                         */
391
    /* -------------------------------------------------------------------- */
392
4
    int nBytesRemaining1 = 0;
393
4
    int nBytesRemaining2 = 0;
394
4
    const DDFSubfieldDefn *poFirstSubfield =
395
4
        poDefn->GetSubfields().front().get();
396
397
4
    const char *pachWrkData =
398
4
        GetSubfieldData(poFirstSubfield, &nBytesRemaining1, nInstance);
399
4
    if (pachWrkData == nullptr)
400
0
        return nullptr;
401
402
    /* -------------------------------------------------------------------- */
403
    /*      Figure out the size of the entire field instance, including     */
404
    /*      unit terminators, but not any trailing field terminator.        */
405
    /* -------------------------------------------------------------------- */
406
4
    if (pnInstanceSize != nullptr)
407
0
    {
408
0
        const DDFSubfieldDefn *poLastSubfield =
409
0
            poDefn->GetSubfields().back().get();
410
411
0
        const char *pachLastData =
412
0
            GetSubfieldData(poLastSubfield, &nBytesRemaining2, nInstance);
413
0
        if (pachLastData == nullptr)
414
0
            return nullptr;
415
416
0
        int nLastSubfieldWidth = 0;
417
0
        poLastSubfield->GetDataLength(pachLastData, nBytesRemaining2,
418
0
                                      &nLastSubfieldWidth);
419
420
0
        *pnInstanceSize =
421
0
            nBytesRemaining1 - (nBytesRemaining2 - nLastSubfieldWidth);
422
0
    }
423
424
4
    return pachWrkData;
425
4
}