Coverage Report

Created: 2026-08-11 08:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/iso8211/ddffielddefn.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  ISO 8211 Access
4
 * Purpose:  Implements the DDFFieldDefn 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 <algorithm>
18
#include <cctype>
19
#include <cstddef>
20
#include <cstdio>
21
#include <cstdlib>
22
#include <cstring>
23
24
#include "cpl_conv.h"
25
#include "cpl_error.h"
26
#include "cpl_string.h"
27
28
41.0k
#define CPLE_DiscardedFormat 1301
29
30
/************************************************************************/
31
/*                            DDFFieldDefn()                            */
32
/************************************************************************/
33
34
419k
DDFFieldDefn::DDFFieldDefn() = default;
35
36
/************************************************************************/
37
/*                           ~DDFFieldDefn()                            */
38
/************************************************************************/
39
40
419k
DDFFieldDefn::~DDFFieldDefn() = default;
41
42
/************************************************************************/
43
/*                            AddSubfield()                             */
44
/************************************************************************/
45
46
void DDFFieldDefn::AddSubfield(const char *pszName, const char *pszFormat)
47
48
0
{
49
0
    auto poSFDefn = std::make_unique<DDFSubfieldDefn>();
50
51
0
    poSFDefn->SetName(pszName);
52
0
    poSFDefn->SetFormat(pszFormat);
53
0
    AddSubfield(std::move(poSFDefn));
54
0
}
55
56
/************************************************************************/
57
/*                            AddSubfield()                             */
58
/************************************************************************/
59
60
void DDFFieldDefn::AddSubfield(std::unique_ptr<DDFSubfieldDefn> poNewSFDefn,
61
                               bool bDontAddToFormat)
62
63
1.54M
{
64
1.54M
    if (bDontAddToFormat)
65
1.54M
    {
66
1.54M
        apoSubfields.push_back(std::move(poNewSFDefn));
67
1.54M
        return;
68
1.54M
    }
69
70
    /* -------------------------------------------------------------------- */
71
    /*      Add this format to the format list.  We don't bother            */
72
    /*      aggregating formats here.                                       */
73
    /* -------------------------------------------------------------------- */
74
0
    if (_formatControls.empty())
75
0
    {
76
0
        _formatControls = "()";
77
0
    }
78
79
0
    std::string osNewFormatControls = _formatControls;
80
0
    osNewFormatControls.pop_back();
81
0
    if (!osNewFormatControls.empty() && osNewFormatControls.back() != '(')
82
0
        osNewFormatControls += ',';
83
0
    osNewFormatControls += poNewSFDefn->GetFormat();
84
0
    osNewFormatControls += ')';
85
86
0
    _formatControls = std::move(osNewFormatControls);
87
88
    /* -------------------------------------------------------------------- */
89
    /*      Add the subfield name to the list.                              */
90
    /* -------------------------------------------------------------------- */
91
0
    if (!_arrayDescr.empty() &&
92
0
        (_arrayDescr[0] != '*' || _arrayDescr.size() > 1))
93
0
        _arrayDescr += '!';
94
0
    _arrayDescr += poNewSFDefn->GetName();
95
96
0
    apoSubfields.push_back(std::move(poNewSFDefn));
97
0
}
98
99
/************************************************************************/
100
/*                               Create()                               */
101
/*                                                                      */
102
/*      Initialize a new field defn from application input, instead     */
103
/*      of from an existing file.                                       */
104
/************************************************************************/
105
106
int DDFFieldDefn::Create(const char *pszTagIn, const char *pszFieldName,
107
                         const char *pszDescription,
108
                         DDF_data_struct_code eDataStructCode,
109
                         DDF_data_type_code eDataTypeCode,
110
                         const char *pszFormat)
111
112
0
{
113
0
    CPLAssert(osTag.empty());
114
0
    poModule = nullptr;
115
0
    osTag = pszTagIn;
116
0
    _fieldName = pszFieldName;
117
0
    _arrayDescr = pszDescription ? pszDescription : "";
118
119
0
    _data_struct_code = eDataStructCode;
120
0
    _data_type_code = eDataTypeCode;
121
122
0
    _formatControls = pszFormat ? pszFormat : "";
123
124
0
    bRepeatingSubfields = (pszDescription != nullptr && *pszDescription == '*');
125
126
0
    if (!_formatControls.empty() && _data_struct_code != dsc_elementary)
127
0
    {
128
0
        if (!BuildSubfields())
129
0
            return false;
130
131
0
        if (apoFieldParts.empty() && !ApplyFormats())
132
0
            return false;
133
0
    }
134
135
0
    return TRUE;
136
0
}
137
138
/************************************************************************/
139
/*                          GenerateDDREntry()                          */
140
/************************************************************************/
141
142
int DDFFieldDefn::GenerateDDREntry(DDFModule *poModuleIn, char **ppachData,
143
                                   int *pnLength)
144
145
0
{
146
0
    const int iFDOffset = poModuleIn->GetFieldControlLength();
147
0
    CPLAssert(iFDOffset >= 6 && iFDOffset <= 9);
148
0
    *pnLength = static_cast<int>(iFDOffset + _fieldName.size() + 1 +
149
0
                                 _arrayDescr.size() + 1);
150
0
    if (!_formatControls.empty())
151
0
    {
152
0
        *pnLength += static_cast<int>(_formatControls.size() + 1);
153
0
    }
154
155
0
    if (ppachData == nullptr)
156
0
        return TRUE;
157
158
0
    *ppachData = static_cast<char *>(CPLMalloc(*pnLength + 1));
159
0
    (*ppachData)[*pnLength] = 0;
160
161
0
    if (_data_struct_code == dsc_elementary)
162
0
        (*ppachData)[0] = '0';
163
0
    else if (_data_struct_code == dsc_vector)
164
0
        (*ppachData)[0] = '1';
165
0
    else if (_data_struct_code == dsc_array)
166
0
        (*ppachData)[0] = '2';
167
0
    else if (_data_struct_code == dsc_concatenated)
168
0
        (*ppachData)[0] = '3';
169
170
0
    if (_data_type_code == dtc_char_string)
171
0
        (*ppachData)[1] = '0';
172
0
    else if (_data_type_code == dtc_implicit_point)
173
0
        (*ppachData)[1] = '1';
174
0
    else if (_data_type_code == dtc_explicit_point)
175
0
        (*ppachData)[1] = '2';
176
0
    else if (_data_type_code == dtc_explicit_point_scaled)
177
0
        (*ppachData)[1] = '3';
178
0
    else if (_data_type_code == dtc_char_bit_string)
179
0
        (*ppachData)[1] = '4';
180
0
    else if (_data_type_code == dtc_bit_string)
181
0
        (*ppachData)[1] = '5';
182
0
    else if (_data_type_code == dtc_mixed_data_type)
183
0
        (*ppachData)[1] = '6';
184
185
0
    (*ppachData)[2] = '0';
186
0
    (*ppachData)[3] = '0';
187
0
    (*ppachData)[4] = ';';
188
0
    (*ppachData)[5] = '&';
189
0
    if (iFDOffset > 6 && _escapeSequence.size() >= 1)
190
0
        (*ppachData)[6] = _escapeSequence[0];
191
0
    if (iFDOffset > 7 && _escapeSequence.size() >= 2)
192
0
        (*ppachData)[7] = _escapeSequence[1];
193
0
    if (iFDOffset > 8 && _escapeSequence.size() >= 3)
194
0
        (*ppachData)[8] = _escapeSequence[2];
195
0
    snprintf(*ppachData + iFDOffset, *pnLength + 1 - iFDOffset, "%s",
196
0
             _fieldName.c_str());
197
0
    snprintf(*ppachData + strlen(*ppachData),
198
0
             *pnLength + 1 - strlen(*ppachData), "%c%s", DDF_UNIT_TERMINATOR,
199
0
             _arrayDescr.c_str());
200
0
    if (!_formatControls.empty())
201
0
    {
202
        // empty for '0000' of S-57 & S-111
203
0
        snprintf(*ppachData + strlen(*ppachData),
204
0
                 *pnLength + 1 - strlen(*ppachData), "%c%s",
205
0
                 DDF_UNIT_TERMINATOR, _formatControls.c_str());
206
0
    }
207
0
    snprintf(*ppachData + strlen(*ppachData),
208
0
             *pnLength + 1 - strlen(*ppachData), "%c", DDF_FIELD_TERMINATOR);
209
210
0
    return TRUE;
211
0
}
212
213
/************************************************************************/
214
/*                             Initialize()                             */
215
/*                                                                      */
216
/*      Initialize the field definition from the information in the     */
217
/*      DDR record.  This is called by DDFModule::Open().               */
218
/************************************************************************/
219
220
int DDFFieldDefn::Initialize(DDFModule *poModuleIn, const char *pszTagIn,
221
                             int nFieldEntrySize, const char *pachFieldArea)
222
223
377k
{
224
377k
    int iFDOffset = poModuleIn->GetFieldControlLength();
225
226
377k
    poModule = poModuleIn;
227
228
377k
    osTag = pszTagIn;
229
230
    /* -------------------------------------------------------------------- */
231
    /*      Set the data struct and type codes.                             */
232
    /* -------------------------------------------------------------------- */
233
377k
    switch (pachFieldArea[0])
234
377k
    {
235
22.7k
        case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */
236
39.2k
        case '0':
237
39.2k
            _data_struct_code = dsc_elementary;
238
39.2k
            break;
239
240
151k
        case '1':
241
151k
            _data_struct_code = dsc_vector;
242
151k
            break;
243
244
94.9k
        case '2':
245
94.9k
            _data_struct_code = dsc_array;
246
94.9k
            break;
247
248
39.8k
        case '3':
249
39.8k
            _data_struct_code = dsc_concatenated;
250
39.8k
            break;
251
252
52.1k
        default:
253
52.1k
            CPLError(CE_Failure, CPLE_AppDefined,
254
52.1k
                     "Unrecognized data_struct_code value %c.\n"
255
52.1k
                     "Field %s initialization incorrect.",
256
52.1k
                     pachFieldArea[0], osTag.c_str());
257
52.1k
            _data_struct_code = dsc_elementary;
258
377k
    }
259
260
377k
    switch (pachFieldArea[1])
261
377k
    {
262
23.1k
        case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */
263
71.8k
        case '0':
264
71.8k
            _data_type_code = dtc_char_string;
265
71.8k
            break;
266
267
90.0k
        case '1':
268
90.0k
            _data_type_code = dtc_implicit_point;
269
90.0k
            break;
270
271
16.7k
        case '2':
272
16.7k
            _data_type_code = dtc_explicit_point;
273
16.7k
            break;
274
275
14.6k
        case '3':
276
14.6k
            _data_type_code = dtc_explicit_point_scaled;
277
14.6k
            break;
278
279
2.68k
        case '4':
280
2.68k
            _data_type_code = dtc_char_bit_string;
281
2.68k
            break;
282
283
3.36k
        case '5':
284
3.36k
            _data_type_code = dtc_bit_string;
285
3.36k
            break;
286
287
121k
        case '6':
288
121k
            _data_type_code = dtc_mixed_data_type;
289
121k
            break;
290
291
56.8k
        default:
292
56.8k
            CPLError(CE_Failure, CPLE_AppDefined,
293
56.8k
                     "Unrecognized data_type_code value %c.\n"
294
56.8k
                     "Field %s initialization incorrect.",
295
56.8k
                     pachFieldArea[1], osTag.c_str());
296
56.8k
            _data_type_code = dtc_char_string;
297
377k
    }
298
299
377k
    if (nFieldEntrySize >= iFDOffset && iFDOffset > 6)
300
173k
        _escapeSequence.assign(pachFieldArea + 6, iFDOffset - 6);
301
302
    /* -------------------------------------------------------------------- */
303
    /*      Capture the field name, description (sub field names), and      */
304
    /*      format statements.                                              */
305
    /* -------------------------------------------------------------------- */
306
307
377k
    int nCharsConsumed = 0;
308
377k
    _fieldName = DDFFetchVariable(
309
377k
        pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset,
310
377k
        DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed);
311
377k
    iFDOffset += nCharsConsumed;
312
313
377k
    _arrayDescr = DDFFetchVariable(
314
377k
        pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset,
315
377k
        DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed);
316
377k
    iFDOffset += nCharsConsumed;
317
318
377k
    _formatControls = DDFFetchVariable(
319
377k
        pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset,
320
377k
        DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed);
321
322
    /* -------------------------------------------------------------------- */
323
    /*      Parse the subfield info.                                        */
324
    /* -------------------------------------------------------------------- */
325
377k
    if (_data_struct_code != dsc_elementary)
326
285k
    {
327
285k
        if (!BuildSubfields())
328
13.4k
            return false;
329
330
272k
        if (apoFieldParts.empty() && !ApplyFormats())
331
34.6k
            return false;
332
272k
    }
333
334
329k
    return TRUE;
335
377k
}
336
337
/************************************************************************/
338
/*                         SetEscapeSequence()                          */
339
/************************************************************************/
340
341
void DDFFieldDefn::SetEscapeSequence(const std::string &val)
342
0
{
343
0
    _escapeSequence = val;
344
0
}
345
346
/************************************************************************/
347
/*                                Dump()                                */
348
/************************************************************************/
349
350
/**
351
 * Write out field definition info to debugging file.
352
 *
353
 * A variety of information about this field definition, and all its
354
 * subfields is written to the give debugging file handle.
355
 *
356
 * @param fp The standard IO file handle to write to.  i.e. stderr
357
 */
358
359
void DDFFieldDefn::Dump(FILE *fp, int nNestingLevel) const
360
361
0
{
362
0
    std::string osIndent;
363
0
    for (int i = 0; i < nNestingLevel; ++i)
364
0
        osIndent += "  ";
365
366
0
#define Print(...)                                                             \
367
0
    do                                                                         \
368
0
    {                                                                          \
369
0
        fprintf(fp, "%s", osIndent.c_str());                                   \
370
0
        fprintf(fp, __VA_ARGS__);                                              \
371
0
    } while (0)
372
373
0
    const char *pszValue = "";
374
0
    CPL_IGNORE_RET_VAL(pszValue);  // Make CSA happy
375
376
0
    Print("DDFFieldDefn:\n");
377
0
    Print("    Tag = `%s'\n", osTag.c_str());
378
0
    Print("    _fieldName = `%s'\n", _fieldName.c_str());
379
0
    Print("    _arrayDescr = `%s'\n", _arrayDescr.c_str());
380
0
    Print("    _formatControls = `%s'\n", _formatControls.c_str());
381
382
0
    switch (_data_struct_code)
383
0
    {
384
0
        case dsc_elementary:
385
0
            pszValue = "elementary";
386
0
            break;
387
388
0
        case dsc_vector:
389
0
            pszValue = "vector";
390
0
            break;
391
392
0
        case dsc_array:
393
0
            pszValue = "array";
394
0
            break;
395
396
0
        case dsc_concatenated:
397
0
            pszValue = "concatenated";
398
0
            break;
399
0
    }
400
401
0
    Print("    _data_struct_code = %s\n", pszValue);
402
403
0
    switch (_data_type_code)
404
0
    {
405
0
        case dtc_char_string:
406
0
            pszValue = "char_string";
407
0
            break;
408
409
0
        case dtc_implicit_point:
410
0
            pszValue = "implicit_point";
411
0
            break;
412
413
0
        case dtc_explicit_point:
414
0
            pszValue = "explicit_point";
415
0
            break;
416
417
0
        case dtc_explicit_point_scaled:
418
0
            pszValue = "explicit_point_scaled";
419
0
            break;
420
421
0
        case dtc_char_bit_string:
422
0
            pszValue = "char_bit_string";
423
0
            break;
424
425
0
        case dtc_bit_string:
426
0
            pszValue = "bit_string";
427
0
            break;
428
429
0
        case dtc_mixed_data_type:
430
0
            pszValue = "mixed_data_type";
431
0
            break;
432
0
    }
433
434
0
    Print("    _data_type_code = %s\n", pszValue);
435
436
0
    for (const auto &poField : apoFieldParts)
437
0
        poField->Dump(fp, nNestingLevel + 1);
438
439
0
    for (const auto &poSubfield : apoSubfields)
440
0
        poSubfield->Dump(fp, nNestingLevel + 1);
441
0
}
442
443
/************************************************************************/
444
/*                           BuildSubfields()                           */
445
/*                                                                      */
446
/*      Based on the _arrayDescr build a set of subfields.              */
447
/************************************************************************/
448
449
bool DDFFieldDefn::BuildSubfields()
450
451
327k
{
452
327k
    if (_data_struct_code == dsc_concatenated)
453
39.8k
    {
454
        // Split on two consecutive backslashes.
455
39.8k
        std::vector<std::string> aosPartDescr;
456
39.8k
        {
457
39.8k
            std::string osCur;
458
2.23M
            for (size_t i = 0; i < _arrayDescr.size(); ++i)
459
2.19M
            {
460
2.19M
                const char c = _arrayDescr[i];
461
2.19M
                if (c == '\\' && _arrayDescr[i + 1] == '\\')
462
41.4k
                {
463
41.4k
                    aosPartDescr.push_back(osCur);
464
41.4k
                    osCur.clear();
465
41.4k
                    ++i;
466
41.4k
                }
467
2.15M
                else
468
2.15M
                {
469
2.15M
                    osCur += c;
470
2.15M
                }
471
2.19M
            }
472
39.8k
            aosPartDescr.push_back(std::move(osCur));
473
39.8k
        }
474
39.8k
        if (aosPartDescr.size() > 1 && !_formatControls.empty() &&
475
30.5k
            _formatControls.front() == '(' && _formatControls.back() == ')')
476
29.3k
        {
477
29.3k
            const char *pszFormatCur = _formatControls.c_str() + 1;
478
67.8k
            for (size_t i = 0; i < aosPartDescr.size(); ++i)
479
51.9k
            {
480
51.9k
                const std::string &osPartDescr = aosPartDescr[i];
481
                // Check there are no repeated subfields but in the last part
482
51.9k
                if (i < aosPartDescr.size() - 1 &&
483
34.3k
                    osPartDescr.find('*') != std::string::npos)
484
549
                {
485
549
                    CPLError(CE_Failure, CPLE_NotSupported,
486
549
                             "Tag %s: repeated fields found in a part that is "
487
549
                             "not the last one: %s",
488
549
                             osTag.c_str(), _arrayDescr.c_str());
489
549
                    return false;
490
549
                }
491
492
51.3k
                const int nSubfieldsInPart = static_cast<int>(
493
51.3k
                    std::count(osPartDescr.begin(), osPartDescr.end(), '!') +
494
51.3k
                    1);
495
51.3k
                int nSubFieldCounter = 0;
496
51.3k
                const char *pszFormatStart = pszFormatCur;
497
51.3k
                bool justAfterFieldFormat = true;
498
51.3k
                int nParenthesisLevel = 0;
499
442k
                while (i < aosPartDescr.size() - 1 && *pszFormatCur != '\0')
500
424k
                {
501
424k
                    if (justAfterFieldFormat && *pszFormatCur >= '1' &&
502
99.5k
                        *pszFormatCur <= '9')
503
38.1k
                    {
504
38.1k
                        char *pszNext = nullptr;
505
38.1k
                        const int nRepeat = static_cast<int>(
506
38.1k
                            strtol(pszFormatCur, &pszNext, 10));
507
38.1k
                        if (!(*pszNext) || nRepeat <= 0 || nRepeat > 1000)
508
993
                        {
509
993
                            CPLError(CE_Failure, CPLE_AppDefined,
510
993
                                     "Tag %s: invalid formatControls: %s",
511
993
                                     osTag.c_str(), _formatControls.c_str());
512
993
                            return false;
513
993
                        }
514
37.1k
                        if (*pszNext == '(')
515
5.01k
                        {
516
5.01k
                            pszFormatCur = pszNext + 1;
517
5.01k
                            int nGroupSubFieldCount = 0;
518
77.0k
                            while (*pszFormatCur)
519
76.4k
                            {
520
76.4k
                                if (*pszFormatCur == '(')
521
590
                                {
522
                                    // Implementation limitation. Perhaps OK per the standard
523
590
                                    CPLError(CE_Failure, CPLE_AppDefined,
524
590
                                             "Tag %s: unsupported "
525
590
                                             "formatControls: %s",
526
590
                                             osTag.c_str(),
527
590
                                             _formatControls.c_str());
528
590
                                    return false;
529
590
                                }
530
75.8k
                                else if (*pszFormatCur >= '1' &&
531
41.8k
                                         *pszFormatCur <= '9')
532
10.7k
                                {
533
10.7k
                                    const long nIncrement =
534
10.7k
                                        strtol(pszFormatCur, &pszNext, 10);
535
10.7k
                                    if (!(*pszNext) ||
536
10.5k
                                        nIncrement >
537
10.5k
                                            INT_MAX - nGroupSubFieldCount)
538
747
                                    {
539
747
                                        CPLError(CE_Failure, CPLE_AppDefined,
540
747
                                                 "Tag %s: invalid "
541
747
                                                 "formatControls: %s",
542
747
                                                 osTag.c_str(),
543
747
                                                 _formatControls.c_str());
544
747
                                        return false;
545
747
                                    }
546
9.99k
                                    nGroupSubFieldCount +=
547
9.99k
                                        static_cast<int>(nIncrement);
548
9.99k
                                    pszFormatCur = pszNext;
549
9.99k
                                }
550
65.0k
                                else if (*pszFormatCur == ',')
551
2.64k
                                {
552
2.64k
                                    if (nGroupSubFieldCount == INT_MAX)
553
403
                                    {
554
403
                                        CPLError(CE_Failure, CPLE_AppDefined,
555
403
                                                 "Tag %s: invalid "
556
403
                                                 "formatControls: %s",
557
403
                                                 osTag.c_str(),
558
403
                                                 _formatControls.c_str());
559
403
                                        return false;
560
403
                                    }
561
2.24k
                                    nGroupSubFieldCount++;
562
2.24k
                                    ++pszFormatCur;
563
2.24k
                                }
564
62.4k
                                else if (*pszFormatCur == ')')
565
2.63k
                                {
566
2.63k
                                    if (nGroupSubFieldCount == INT_MAX)
567
407
                                    {
568
407
                                        CPLError(CE_Failure, CPLE_AppDefined,
569
407
                                                 "Tag %s: invalid "
570
407
                                                 "formatControls: %s",
571
407
                                                 osTag.c_str(),
572
407
                                                 _formatControls.c_str());
573
407
                                        return false;
574
407
                                    }
575
2.23k
                                    nGroupSubFieldCount++;
576
2.23k
                                    break;
577
2.63k
                                }
578
59.7k
                                else
579
59.7k
                                {
580
59.7k
                                    ++pszFormatCur;
581
59.7k
                                }
582
76.4k
                            }
583
2.86k
                            if (!*pszFormatCur)
584
636
                            {
585
636
                                CPLError(CE_Failure, CPLE_AppDefined,
586
636
                                         "Tag %s: invalid formatControls: %s",
587
636
                                         osTag.c_str(),
588
636
                                         _formatControls.c_str());
589
636
                                return false;
590
636
                            }
591
2.23k
                            ++pszFormatCur;
592
2.23k
                            if (nGroupSubFieldCount < 0 ||
593
2.23k
                                nGroupSubFieldCount > INT_MAX / nRepeat ||
594
1.68k
                                nSubFieldCounter >
595
1.68k
                                    INT_MAX - nGroupSubFieldCount * nRepeat)
596
693
                            {
597
693
                                CPLError(CE_Failure, CPLE_AppDefined,
598
693
                                         "Tag %s: invalid "
599
693
                                         "formatControls: %s",
600
693
                                         osTag.c_str(),
601
693
                                         _formatControls.c_str());
602
693
                                return false;
603
693
                            }
604
1.53k
                            nSubFieldCounter += nGroupSubFieldCount * nRepeat;
605
1.53k
                            if (*pszFormatCur == ')')
606
227
                                break;
607
1.31k
                            if (*pszFormatCur != ',')
608
677
                            {
609
677
                                CPLError(CE_Failure, CPLE_AppDefined,
610
677
                                         "Tag %s: invalid formatControls: %s",
611
677
                                         osTag.c_str(),
612
677
                                         _formatControls.c_str());
613
677
                                return false;
614
677
                            }
615
634
                            ++pszFormatCur;
616
634
                        }
617
32.1k
                        else
618
32.1k
                        {
619
32.1k
                            nSubFieldCounter += nRepeat;
620
32.1k
                            pszFormatCur = pszNext;
621
105k
                            while (*pszFormatCur && *pszFormatCur != ',' &&
622
74.0k
                                   *pszFormatCur != ')')
623
73.6k
                            {
624
73.6k
                                ++pszFormatCur;
625
73.6k
                            }
626
32.1k
                            if (*pszFormatCur == ')')
627
380
                                break;
628
31.7k
                            if (*pszFormatCur != ',')
629
593
                            {
630
593
                                CPLError(CE_Failure, CPLE_AppDefined,
631
593
                                         "Tag %s: invalid formatControls: %s",
632
593
                                         osTag.c_str(),
633
593
                                         _formatControls.c_str());
634
593
                                return false;
635
593
                            }
636
31.1k
                            ++pszFormatCur;
637
31.1k
                        }
638
31.8k
                        justAfterFieldFormat = true;
639
31.8k
                    }
640
385k
                    else if (*pszFormatCur == ',')
641
65.6k
                    {
642
65.6k
                        ++nSubFieldCounter;
643
65.6k
                        ++pszFormatCur;
644
65.6k
                        justAfterFieldFormat = true;
645
65.6k
                    }
646
320k
                    else if (*pszFormatCur == '(')
647
15.1k
                    {
648
15.1k
                        ++nParenthesisLevel;
649
15.1k
                        ++pszFormatCur;
650
15.1k
                        justAfterFieldFormat = false;
651
15.1k
                    }
652
305k
                    else if (*pszFormatCur == ')')
653
15.3k
                    {
654
15.3k
                        if (--nParenthesisLevel < 0)
655
1.54k
                        {
656
1.54k
                            ++nSubFieldCounter;
657
1.54k
                            break;
658
1.54k
                        }
659
13.7k
                        ++pszFormatCur;
660
13.7k
                        justAfterFieldFormat = false;
661
13.7k
                    }
662
289k
                    else
663
289k
                    {
664
289k
                        ++pszFormatCur;
665
289k
                        justAfterFieldFormat = false;
666
289k
                    }
667
668
416k
                    if (nSubFieldCounter > nSubfieldsInPart)
669
850
                    {
670
850
                        CPLError(CE_Failure, CPLE_AppDefined,
671
850
                                 "Tag %s: mismatch between arrayDescr:%s and "
672
850
                                 "formatControls: %s",
673
850
                                 osTag.c_str(), _arrayDescr.c_str(),
674
850
                                 _formatControls.c_str());
675
850
                        return false;
676
850
                    }
677
415k
                    else if (nSubFieldCounter == nSubfieldsInPart)
678
24.6k
                    {
679
24.6k
                        break;
680
24.6k
                    }
681
416k
                }
682
44.7k
                if (i < aosPartDescr.size() - 1)
683
27.1k
                {
684
27.1k
                    if (*pszFormatCur == 0 || pszFormatCur[-1] != ',' ||
685
25.8k
                        nParenthesisLevel > 0)
686
1.66k
                    {
687
1.66k
                        CPLError(CE_Failure, CPLE_AppDefined,
688
1.66k
                                 "Tag %s: invalid formatControls: %s",
689
1.66k
                                 osTag.c_str(), _formatControls.c_str());
690
1.66k
                        return false;
691
1.66k
                    }
692
25.5k
                    if (nSubFieldCounter != nSubfieldsInPart)
693
577
                    {
694
577
                        CPLError(CE_Failure, CPLE_AppDefined,
695
577
                                 "Tag %s: mismatch between arrayDescr:%s and "
696
577
                                 "formatControls: %s",
697
577
                                 osTag.c_str(), _arrayDescr.c_str(),
698
577
                                 _formatControls.c_str());
699
577
                        return false;
700
577
                    }
701
25.5k
                }
702
703
42.5k
                std::string osPartFormatControls;
704
42.5k
                if (i < aosPartDescr.size() - 1)
705
24.9k
                {
706
24.9k
                    if (pszFormatCur == pszFormatStart)
707
588
                    {
708
588
                        CPLError(CE_Failure, CPLE_AppDefined,
709
588
                                 "Tag %s: mismatch between arrayDescr:%s and "
710
588
                                 "formatControls or invalid formatControls: %s",
711
588
                                 osTag.c_str(), _arrayDescr.c_str(),
712
588
                                 _formatControls.c_str());
713
588
                        return false;
714
588
                    }
715
24.3k
                    osPartFormatControls = '(';
716
24.3k
                    osPartFormatControls.append(
717
24.3k
                        pszFormatStart, pszFormatCur - pszFormatStart - 1);
718
24.3k
                    osPartFormatControls += ')';
719
24.3k
                }
720
17.6k
                else
721
17.6k
                {
722
17.6k
                    if (*pszFormatStart == '(' && _formatControls.size() > 2 &&
723
16.1k
                        _formatControls[_formatControls.size() - 2] == ')')
724
15.7k
                    {
725
                        // S101 2.0
726
15.7k
                        osPartFormatControls.append(pszFormatStart,
727
15.7k
                                                    strlen(pszFormatStart) - 1);
728
15.7k
                    }
729
1.82k
                    else
730
1.82k
                    {
731
                        // Earlier versions
732
1.82k
                        osPartFormatControls = '(';
733
1.82k
                        osPartFormatControls += pszFormatStart;
734
1.82k
                    }
735
17.6k
                }
736
737
41.9k
                auto poPartFieldDefn = std::make_unique<DDFFieldDefn>();
738
41.9k
                poPartFieldDefn->poModule = poModule;
739
                // poPartFieldDefn->osTag: not set on purpose
740
                // poPartFieldDefn->_fieldName: not set on purpose
741
41.9k
                poPartFieldDefn->_arrayDescr = osPartDescr;
742
41.9k
                poPartFieldDefn->_formatControls =
743
41.9k
                    std::move(osPartFormatControls);
744
41.9k
                poPartFieldDefn->_data_struct_code =
745
41.9k
                    dsc_vector;  // not necessarily exact, but good enough
746
41.9k
                poPartFieldDefn->_data_type_code = _data_type_code;
747
41.9k
                poPartFieldDefn->bRepeatingSubfields =
748
41.9k
                    !osPartDescr.empty() && osPartDescr.front() == '*';
749
750
41.9k
                if (!poPartFieldDefn->BuildSubfields() ||
751
41.9k
                    !poPartFieldDefn->ApplyFormats())
752
3.49k
                    return false;
753
754
38.4k
                apoFieldParts.push_back(std::move(poPartFieldDefn));
755
38.4k
            }
756
757
15.9k
            return true;
758
29.3k
        }
759
39.8k
    }
760
761
    /* -------------------------------------------------------------------- */
762
    /*      It is valid to define a field with _arrayDesc                   */
763
    /*      '*STPT!CTPT!ENPT*YCOO!XCOO' and formatControls '(2b24)'.        */
764
    /*      This basically indicates that there are 3 (YCOO,XCOO)           */
765
    /*      structures named STPT, CTPT and ENPT.  But we can't handle      */
766
    /*      such a case gracefully here, so we just ignore the              */
767
    /*      "structure names" and treat such a thing as a repeating         */
768
    /*      YCOO/XCOO array.  This occurs with the AR2D field of some       */
769
    /*      AML S-57 files for instance.                                    */
770
    /*                                                                      */
771
    /*      We accomplish this by ignoring everything before the last       */
772
    /*      '*' in the subfield list.                                       */
773
    /* -------------------------------------------------------------------- */
774
298k
    const char *pszSublist = _arrayDescr.c_str();
775
298k
    if (strrchr(pszSublist, '*') != nullptr)
776
100k
        pszSublist = strrchr(pszSublist, '*');
777
778
    /* -------------------------------------------------------------------- */
779
    /*      Strip off the repeating marker, when it occurs, but mark our    */
780
    /*      field as repeating.                                             */
781
    /* -------------------------------------------------------------------- */
782
298k
    if (pszSublist[0] == '*')
783
100k
    {
784
100k
        bRepeatingSubfields = TRUE;
785
100k
        pszSublist++;
786
100k
    }
787
788
    /* -------------------------------------------------------------------- */
789
    /*      split list of fields .                                          */
790
    /* -------------------------------------------------------------------- */
791
298k
    const CPLStringList aosSubfieldNames(
792
298k
        CSLTokenizeStringComplex(pszSublist, "!", FALSE, FALSE));
793
794
    /* -------------------------------------------------------------------- */
795
    /*      minimally initialize the subfields.  More will be done later.   */
796
    /* -------------------------------------------------------------------- */
797
298k
    for (const char *pszSubfieldName : cpl::Iterate(aosSubfieldNames))
798
1.54M
    {
799
1.54M
        auto poSFDefn = std::make_unique<DDFSubfieldDefn>();
800
801
1.54M
        poSFDefn->SetName(pszSubfieldName);
802
1.54M
        AddSubfield(std::move(poSFDefn), true);
803
1.54M
    }
804
805
298k
    return true;
806
327k
}
807
808
/************************************************************************/
809
/*                          ExtractSubstring()                          */
810
/*                                                                      */
811
/*      Extract a substring terminated by a comma (or end of            */
812
/*      string).  Commas in brackets are ignored as terminated with     */
813
/*      bracket nesting understood gracefully.  If the returned         */
814
/*      string would begin and end with a bracket then strip off the    */
815
/*      brackets.                                                       */
816
/*                                                                      */
817
/*      Given a string like "(A,3(B,C),D),X,Y)" return "A,3(B,C),D".    */
818
/*      Giveh a string like "3A,2C" return "3A".                        */
819
/*      Giveh a string like "(3A,2C" return an empty string             */
820
/*      Giveh a string like "3A),2C" return an empty string             */
821
/************************************************************************/
822
823
std::string DDFFieldDefn::ExtractSubstring(const char *pszSrc)
824
825
535k
{
826
535k
    int nBracket = 0;
827
535k
    int i = 0;  // Used after for.
828
7.28M
    for (; pszSrc[i] != '\0' && (nBracket > 0 || pszSrc[i] != ','); i++)
829
6.75M
    {
830
6.75M
        if (pszSrc[i] == '(')
831
984k
            nBracket++;
832
5.76M
        else if (pszSrc[i] == ')')
833
983k
        {
834
983k
            nBracket--;
835
983k
            if (nBracket < 0)
836
1.08k
                return std::string();
837
983k
        }
838
6.75M
    }
839
534k
    if (nBracket > 0)
840
1.78k
        return std::string();
841
842
532k
    if (pszSrc[0] == '(')
843
283k
    {
844
283k
        CPLAssert(i >= 2);
845
283k
        return std::string(pszSrc + 1, i - 2);
846
283k
    }
847
248k
    else
848
248k
    {
849
248k
        return std::string(pszSrc, i);
850
248k
    }
851
532k
}
852
853
/************************************************************************/
854
/*                            ExpandFormat()                            */
855
/************************************************************************/
856
857
std::string DDFFieldDefn::ExpandFormat(const char *pszSrc)
858
859
807k
{
860
807k
    std::string osDest;
861
807k
    size_t iSrc = 0;
862
863
5.81M
    while (pszSrc[iSrc] != '\0')
864
5.02M
    {
865
        // This is presumably an extra level of brackets around some
866
        // binary stuff related to rescanning which we don't care to do
867
        // (see 6.4.3.3 of the standard.  We just strip off the extra
868
        // layer of brackets.
869
5.02M
        if ((iSrc == 0 || pszSrc[iSrc - 1] == ',') && pszSrc[iSrc] == '(')
870
279k
        {
871
279k
            const std::string osContents = ExtractSubstring(pszSrc + iSrc);
872
279k
            if (osContents.empty())
873
2.71k
            {
874
2.71k
                return std::string();
875
2.71k
            }
876
276k
            const std::string osExpandedContents =
877
276k
                ExpandFormat(osContents.c_str());
878
276k
            if (osExpandedContents.empty())
879
2.79k
            {
880
2.79k
                return std::string();
881
2.79k
            }
882
883
273k
            if (osDest.size() + osExpandedContents.size() > 1024 * 1024)
884
4
            {
885
4
                return std::string();
886
4
            }
887
888
273k
            osDest += osExpandedContents;
889
890
273k
            iSrc += osContents.size() + 2;
891
273k
        }
892
893
        // This is a repeated subclause.
894
4.74M
        else if ((iSrc == 0 || pszSrc[iSrc - 1] == ',') &&
895
1.22M
                 isdigit(static_cast<unsigned char>(pszSrc[iSrc])))
896
257k
        {
897
257k
            const int nRepeat = atoi(pszSrc + iSrc);
898
            // 100: arbitrary number. Higher values might cause performance
899
            // problems in the below loop
900
257k
            if (nRepeat < 0 || nRepeat > 100)
901
1.22k
            {
902
1.22k
                CPLError(CE_Failure, CPLE_AppDefined,
903
1.22k
                         "Too large repeat count: %d", nRepeat);
904
1.22k
                return std::string();
905
1.22k
            }
906
907
            // Skip over repeat count.
908
256k
            const char *pszNext = pszSrc + iSrc;  // Used after for.
909
527k
            for (; isdigit(static_cast<unsigned char>(*pszNext)); pszNext++)
910
270k
                iSrc++;
911
912
256k
            const std::string osContents = ExtractSubstring(pszNext);
913
256k
            if (osContents.empty())
914
984
            {
915
984
                return std::string();
916
984
            }
917
255k
            const std::string osExpandedContents =
918
255k
                ExpandFormat(osContents.c_str());
919
255k
            if (osExpandedContents.empty())
920
1.98k
            {
921
1.98k
                return std::string();
922
1.98k
            }
923
924
253k
            const size_t nExpandedContentsLen = osExpandedContents.size();
925
253k
            if (osDest.size() + nExpandedContentsLen * nRepeat > 1024 * 1024)
926
324
            {
927
324
                return std::string();
928
324
            }
929
930
1.28M
            for (int i = 0; i < nRepeat; i++)
931
1.02M
            {
932
1.02M
                if (i > 0)
933
777k
                    osDest += ',';
934
1.02M
                osDest += osExpandedContents;
935
1.02M
            }
936
937
253k
            if (pszNext[0] == '(')
938
4.91k
                iSrc += osContents.size() + 2;
939
248k
            else
940
248k
                iSrc += osContents.size();
941
253k
        }
942
4.48M
        else
943
4.48M
        {
944
4.48M
            osDest += pszSrc[iSrc++];
945
4.48M
        }
946
5.02M
    }
947
948
797k
    return osDest;
949
807k
}
950
951
/************************************************************************/
952
/*                            ApplyFormats()                            */
953
/*                                                                      */
954
/*      This method parses the format string partially, and then        */
955
/*      applies a subfield format string to each subfield object.       */
956
/*      It in turn does final parsing of the subfield formats.          */
957
/************************************************************************/
958
959
int DDFFieldDefn::ApplyFormats()
960
961
298k
{
962
    /* -------------------------------------------------------------------- */
963
    /*      Verify that the format string is contained within brackets.     */
964
    /* -------------------------------------------------------------------- */
965
298k
    if (_formatControls.size() < 2 || _formatControls[0] != '(' ||
966
279k
        _formatControls.back() != ')')
967
22.7k
    {
968
22.7k
        CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
969
22.7k
                 "Format controls for `%s' field missing brackets:%s",
970
22.7k
                 osTag.c_str(), _formatControls.c_str());
971
972
22.7k
        return FALSE;
973
22.7k
    }
974
975
    /* -------------------------------------------------------------------- */
976
    /*      Duplicate the string, and strip off the brackets.               */
977
    /* -------------------------------------------------------------------- */
978
979
275k
    const std::string osFormatList = ExpandFormat(_formatControls.c_str());
980
275k
    if (osFormatList.empty())
981
5.30k
    {
982
5.30k
        CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
983
5.30k
                 "Invalid format controls for `%s': %s", osTag.c_str(),
984
5.30k
                 _formatControls.c_str());
985
5.30k
        return FALSE;
986
5.30k
    }
987
988
    /* -------------------------------------------------------------------- */
989
    /*      Tokenize based on commas.                                       */
990
    /* -------------------------------------------------------------------- */
991
270k
    const CPLStringList aosFormatItems(
992
270k
        CSLTokenizeStringComplex(osFormatList.c_str(), ",", FALSE, FALSE));
993
994
    /* -------------------------------------------------------------------- */
995
    /*      Apply the format items to subfields.                            */
996
    /* -------------------------------------------------------------------- */
997
270k
    int iFormatItem = 0;  // Used after for.
998
999
1.74M
    for (; iFormatItem < aosFormatItems.size(); iFormatItem++)
1000
1.48M
    {
1001
1.48M
        const char *pszPastPrefix = aosFormatItems[iFormatItem];
1002
1.48M
        while (*pszPastPrefix >= '0' && *pszPastPrefix <= '9')
1003
0
            pszPastPrefix++;
1004
1005
        ///////////////////////////////////////////////////////////////
1006
        // Did we get too many formats for the subfields created
1007
        // by names?  This may be legal by the 8211 specification, but
1008
        // isn't encountered in any formats we care about so we just
1009
        // blow.
1010
1011
1.48M
        if (iFormatItem >= GetSubfieldCount())
1012
11.1k
        {
1013
11.1k
            CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
1014
11.1k
                     "Got more formats than subfields for field `%s'.",
1015
11.1k
                     osTag.c_str());
1016
11.1k
            break;
1017
11.1k
        }
1018
1019
1.47M
        if (!apoSubfields[iFormatItem]->SetFormat(pszPastPrefix))
1020
8.20k
        {
1021
8.20k
            return FALSE;
1022
8.20k
        }
1023
1.47M
    }
1024
1025
    /* -------------------------------------------------------------------- */
1026
    /*      Verify that we got enough formats, cleanup and return.          */
1027
    /* -------------------------------------------------------------------- */
1028
1029
262k
    if (iFormatItem < GetSubfieldCount())
1030
1.34k
    {
1031
1.34k
        CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
1032
1.34k
                 "Got less formats than subfields for field `%s'.",
1033
1.34k
                 osTag.c_str());
1034
1.34k
        return FALSE;
1035
1.34k
    }
1036
1037
    /* -------------------------------------------------------------------- */
1038
    /*      If all the fields are fixed width, then we are fixed width      */
1039
    /*      too.  This is important for repeating fields.                   */
1040
    /* -------------------------------------------------------------------- */
1041
260k
    nFixedWidth = 0;
1042
260k
    for (auto &poSubfield : apoSubfields)
1043
1.17M
    {
1044
1.17M
        if (poSubfield->GetWidth() == 0)
1045
97.0k
        {
1046
97.0k
            nFixedWidth = 0;
1047
97.0k
            break;
1048
97.0k
        }
1049
1.07M
        else
1050
1.07M
        {
1051
1.07M
            if (nFixedWidth > INT_MAX - poSubfield->GetWidth())
1052
500
            {
1053
500
                CPLError(CE_Warning,
1054
500
                         static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
1055
500
                         "Invalid format controls for `%s': %s", osTag.c_str(),
1056
500
                         _formatControls.c_str());
1057
500
                return FALSE;
1058
500
            }
1059
1.07M
            nFixedWidth += poSubfield->GetWidth();
1060
1.07M
        }
1061
1.17M
    }
1062
1063
260k
    return TRUE;
1064
260k
}
1065
1066
/************************************************************************/
1067
/*                          FindSubfieldDefn()                          */
1068
/************************************************************************/
1069
1070
/**
1071
 * Find a subfield definition by its mnemonic tag.
1072
 *
1073
 * @param pszMnemonic The name of the field.
1074
 *
1075
 * @return The subfield pointer, or NULL if there isn't any such subfield.
1076
 */
1077
1078
const DDFSubfieldDefn *
1079
DDFFieldDefn::FindSubfieldDefn(const char *pszMnemonic) const
1080
1081
2.30M
{
1082
2.30M
    for (const auto &poSubfield : apoSubfields)
1083
5.36M
    {
1084
5.36M
        if (EQUAL(poSubfield->GetName(), pszMnemonic))
1085
1.17M
            return poSubfield.get();
1086
5.36M
    }
1087
1088
1.12M
    return nullptr;
1089
2.30M
}
1090
1091
/************************************************************************/
1092
/*                          GetDefaultValue()                           */
1093
/************************************************************************/
1094
1095
/**
1096
 * Return default data for field instance.
1097
 */
1098
1099
char *DDFFieldDefn::GetDefaultValue(int *pnSize) const
1100
1101
588
{
1102
588
    if (!apoFieldParts.empty())
1103
174
    {
1104
174
        std::string osData;
1105
174
        for (auto &poPartFieldDefn : apoFieldParts)
1106
348
        {
1107
348
            if (poPartFieldDefn->IsRepeating())  // only last part
1108
174
                break;
1109
174
            int nPartSize = 0;
1110
174
            char *pszVal = poPartFieldDefn->GetDefaultValue(&nPartSize);
1111
174
            if (!pszVal)
1112
0
                return nullptr;
1113
174
            osData.append(pszVal, nPartSize);
1114
174
            CPLFree(pszVal);
1115
174
        }
1116
174
        if (pnSize)
1117
174
            *pnSize = static_cast<int>(osData.size());
1118
174
        char *pabyRet = static_cast<char *>(CPLMalloc(osData.size()));
1119
174
        memcpy(pabyRet, osData.data(), osData.size());
1120
174
        return pabyRet;
1121
174
    }
1122
1123
    /* -------------------------------------------------------------------- */
1124
    /*      Loop once collecting the sum of the subfield lengths.           */
1125
    /* -------------------------------------------------------------------- */
1126
414
    int nTotalSize = 0;
1127
414
    for (auto &poSubfield : apoSubfields)
1128
2.08k
    {
1129
2.08k
        int nSubfieldSize = 0;
1130
1131
2.08k
        if (!poSubfield->GetDefaultValue(nullptr, 0, &nSubfieldSize))
1132
0
            return nullptr;
1133
2.08k
        nTotalSize += nSubfieldSize;
1134
2.08k
    }
1135
1136
    /* -------------------------------------------------------------------- */
1137
    /*      Allocate buffer.                                                */
1138
    /* -------------------------------------------------------------------- */
1139
414
    char *pachData = static_cast<char *>(CPLMalloc(nTotalSize));
1140
1141
414
    if (pnSize != nullptr)
1142
414
        *pnSize = nTotalSize;
1143
1144
    /* -------------------------------------------------------------------- */
1145
    /*      Loop again, collecting actual default values.                   */
1146
    /* -------------------------------------------------------------------- */
1147
414
    int nOffset = 0;
1148
414
    for (auto &poSubfield : apoSubfields)
1149
2.08k
    {
1150
2.08k
        int nSubfieldSize;
1151
1152
2.08k
        if (!poSubfield->GetDefaultValue(pachData + nOffset,
1153
2.08k
                                         nTotalSize - nOffset, &nSubfieldSize))
1154
0
        {
1155
0
            CPLAssert(false);
1156
0
            return nullptr;
1157
0
        }
1158
1159
2.08k
        nOffset += nSubfieldSize;
1160
2.08k
    }
1161
1162
414
    CPLAssert(nOffset == nTotalSize);
1163
1164
414
    return pachData;
1165
414
}