Coverage Report

Created: 2026-08-14 09:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/iso8211/ddfmodule.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  ISO 8211 Access
4
 * Purpose:  Implements the DDFModule class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "iso8211.h"
16
17
#include <algorithm>
18
#include <array>
19
#include <cstdio>
20
#include <cstring>
21
22
#include "cpl_conv.h"
23
#include "cpl_error.h"
24
#include "cpl_vsi.h"
25
26
/************************************************************************/
27
/*                             DDFModule()                              */
28
/************************************************************************/
29
30
/**
31
 * The constructor.
32
 */
33
34
55.3k
DDFModule::DDFModule() = default;
35
36
/************************************************************************/
37
/*                             ~DDFModule()                             */
38
/************************************************************************/
39
40
/**
41
 * The destructor.
42
 */
43
44
DDFModule::~DDFModule()
45
46
55.3k
{
47
55.3k
    Close();
48
55.3k
}
49
50
/************************************************************************/
51
/*                               Close()                                */
52
/*                                                                      */
53
/*      Note that closing a file also destroys essentially all other    */
54
/*      module datastructures.                                          */
55
/************************************************************************/
56
57
/**
58
 * Close an ISO 8211 file.
59
 */
60
61
void DDFModule::Close()
62
63
55.3k
{
64
    /* -------------------------------------------------------------------- */
65
    /*      Close the file.                                                 */
66
    /* -------------------------------------------------------------------- */
67
55.3k
    if (fpDDF != nullptr)
68
34.1k
    {
69
34.1k
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
70
34.1k
        fpDDF = nullptr;
71
34.1k
    }
72
73
55.3k
    poRecord.reset();
74
75
55.3k
    apoFieldDefns.clear();
76
55.3k
}
77
78
/************************************************************************/
79
/*                                Open()                                */
80
/*                                                                      */
81
/*      Open an ISO 8211 file, and read the DDR record to build the     */
82
/*      field definitions.                                              */
83
/************************************************************************/
84
85
/**
86
 * Open a ISO 8211 (DDF) file for reading.
87
 *
88
 * If the open succeeds the data descriptive record (DDR) will have been
89
 * read, and all the field and subfield definitions will be available.
90
 *
91
 * @param pszFilename   The name of the file to open.
92
 * @param bFailQuietly If FALSE a CPL Error is issued for non-8211 files,
93
 * otherwise quietly return NULL.
94
 * @param fpDDFIn The open file, or nullptr. Ownership is transferred to the
95
 * DDFModule.
96
 *
97
 * @return FALSE if the open fails or TRUE if it succeeds.  Errors messages
98
 * are issued internally with CPLError().
99
 */
100
101
int DDFModule::Open(const char *pszFilename, int bFailQuietly,
102
                    VSILFILE *fpDDFIn)
103
104
42.8k
{
105
42.8k
    constexpr int nLeaderSize = 24;
106
107
    /* -------------------------------------------------------------------- */
108
    /*      Close the existing file if there is one.                        */
109
    /* -------------------------------------------------------------------- */
110
42.8k
    if (fpDDF != nullptr)
111
0
        Close();
112
113
    /* -------------------------------------------------------------------- */
114
    /*      Open the file.                                                  */
115
    /* -------------------------------------------------------------------- */
116
42.8k
    VSIStatBufL sStat;
117
42.8k
    if (fpDDFIn)
118
7.65k
    {
119
7.65k
        fpDDF = fpDDFIn;
120
7.65k
        CPL_IGNORE_RET_VAL(VSIFSeekL(fpDDF, 0, SEEK_SET));
121
7.65k
    }
122
35.2k
    else
123
35.2k
    {
124
35.2k
        if (VSIStatL(pszFilename, &sStat) == 0 && !VSI_ISDIR(sStat.st_mode))
125
26.8k
            fpDDF = VSIFOpenL(pszFilename, "rb");
126
127
35.2k
        if (fpDDF == nullptr)
128
8.33k
        {
129
8.33k
            if (!bFailQuietly)
130
6
                CPLError(CE_Failure, CPLE_OpenFailed,
131
6
                         "Unable to open DDF file `%s'.", pszFilename);
132
8.33k
            return FALSE;
133
8.33k
        }
134
35.2k
    }
135
136
    /* -------------------------------------------------------------------- */
137
    /*      Read the 24 byte leader.                                        */
138
    /* -------------------------------------------------------------------- */
139
34.5k
    char achLeader[nLeaderSize];
140
141
34.5k
    if (static_cast<int>(VSIFReadL(achLeader, 1, nLeaderSize, fpDDF)) !=
142
34.5k
        nLeaderSize)
143
11
    {
144
11
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
145
11
        fpDDF = nullptr;
146
147
11
        if (!bFailQuietly)
148
2
            CPLError(CE_Failure, CPLE_FileIO,
149
2
                     "Leader is short on DDF file `%s'.", pszFilename);
150
151
11
        return FALSE;
152
11
    }
153
154
    /* -------------------------------------------------------------------- */
155
    /*      Verify that this appears to be a valid DDF file.                */
156
    /* -------------------------------------------------------------------- */
157
34.5k
    int i, bValid = TRUE;
158
159
863k
    for (i = 0; i < nLeaderSize; i++)
160
828k
    {
161
828k
        if (achLeader[i] < 32 || achLeader[i] > 126)
162
2.24k
            bValid = FALSE;
163
828k
    }
164
165
34.5k
    if (achLeader[5] != '1' && achLeader[5] != '2' && achLeader[5] != '3')
166
201
        bValid = FALSE;
167
168
34.5k
    if (achLeader[6] != 'L')
169
217
        bValid = FALSE;
170
34.5k
    if (achLeader[8] != '1' && achLeader[8] != ' ')
171
196
        bValid = FALSE;
172
173
    /* -------------------------------------------------------------------- */
174
    /*      Extract information from leader.                                */
175
    /* -------------------------------------------------------------------- */
176
177
34.5k
    if (bValid)
178
34.2k
    {
179
34.2k
        _recLength = DDFScanInt(achLeader + 0, 5);
180
34.2k
        _interchangeLevel = achLeader[5];
181
34.2k
        _leaderIden = achLeader[6];
182
34.2k
        _inlineCodeExtensionIndicator = achLeader[7];
183
34.2k
        _versionNumber = achLeader[8];
184
34.2k
        _appIndicator = achLeader[9];
185
34.2k
        _fieldControlLength = DDFScanInt(achLeader + 10, 2);
186
34.2k
        _fieldAreaStart = DDFScanInt(achLeader + 12, 5);
187
34.2k
        _extendedCharSet[0] = achLeader[17];
188
34.2k
        _extendedCharSet[1] = achLeader[18];
189
34.2k
        _extendedCharSet[2] = achLeader[19];
190
34.2k
        _sizeFieldLength = DDFScanInt(achLeader + 20, 1);
191
34.2k
        _sizeFieldPos = DDFScanInt(achLeader + 21, 1);
192
34.2k
        _sizeFieldTag = DDFScanInt(achLeader + 23, 1);
193
194
34.2k
        if (_recLength < nLeaderSize || _fieldControlLength <= 0 ||
195
34.2k
            _fieldAreaStart < 24 || _sizeFieldLength <= 0 ||
196
34.2k
            _sizeFieldPos <= 0 || _sizeFieldTag <= 0)
197
90
        {
198
90
            bValid = FALSE;
199
90
        }
200
34.2k
    }
201
202
    /* -------------------------------------------------------------------- */
203
    /*      If the header is invalid, then clean up, report the error       */
204
    /*      and return.                                                     */
205
    /* -------------------------------------------------------------------- */
206
34.5k
    if (!bValid)
207
348
    {
208
348
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
209
348
        fpDDF = nullptr;
210
211
348
        if (!bFailQuietly)
212
87
            CPLError(CE_Failure, CPLE_AppDefined,
213
87
                     "File `%s' does not appear to have\n"
214
87
                     "a valid ISO 8211 header.\n",
215
87
                     pszFilename);
216
348
        return FALSE;
217
348
    }
218
219
    /* -------------------------------------------------------------------- */
220
    /*      Read the whole record info memory.                              */
221
    /* -------------------------------------------------------------------- */
222
34.1k
    std::string osRecord;
223
34.1k
    osRecord.assign(achLeader, nLeaderSize);
224
34.1k
    osRecord.resize(_recLength);
225
226
34.1k
    if (static_cast<int>(VSIFReadL(osRecord.data() + nLeaderSize, 1,
227
34.1k
                                   _recLength - nLeaderSize, fpDDF)) !=
228
34.1k
        _recLength - nLeaderSize)
229
161
    {
230
161
        if (!bFailQuietly)
231
77
            CPLError(CE_Failure, CPLE_FileIO,
232
77
                     "Header record is short on DDF file `%s'.", pszFilename);
233
234
161
        return FALSE;
235
161
    }
236
237
    /* -------------------------------------------------------------------- */
238
    /*      First make a pass counting the directory entries.               */
239
    /* -------------------------------------------------------------------- */
240
34.0k
    int nFieldEntryWidth, nFDCount = 0;
241
242
34.0k
    nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
243
244
529k
    for (i = nLeaderSize; i + nFieldEntryWidth <= _recLength;
245
495k
         i += nFieldEntryWidth)
246
526k
    {
247
526k
        if (osRecord[i] == DDF_FIELD_TERMINATOR)
248
30.6k
            break;
249
250
495k
        nFDCount++;
251
495k
    }
252
253
    /* -------------------------------------------------------------------- */
254
    /*      Allocate, and read field definitions.                           */
255
    /* -------------------------------------------------------------------- */
256
414k
    for (i = 0; i < nFDCount; i++)
257
383k
    {
258
383k
        char szTag[128];
259
383k
        int nEntryOffset = nLeaderSize + i * nFieldEntryWidth;
260
383k
        int nFieldLength, nFieldPos;
261
262
383k
        strncpy(szTag, osRecord.c_str() + nEntryOffset, _sizeFieldTag);
263
383k
        szTag[_sizeFieldTag] = '\0';
264
265
383k
        nEntryOffset += _sizeFieldTag;
266
383k
        nFieldLength =
267
383k
            DDFScanInt(osRecord.c_str() + nEntryOffset, _sizeFieldLength);
268
269
383k
        nEntryOffset += _sizeFieldLength;
270
383k
        nFieldPos = DDFScanInt(osRecord.c_str() + nEntryOffset, _sizeFieldPos);
271
272
383k
        if (nFieldPos < 0 || nFieldPos > INT_MAX - _fieldAreaStart ||
273
383k
            nFieldLength <
274
383k
                2 ||  // DDFFieldDefn::Initialize() assumes at least 2 bytes
275
380k
            _recLength - (_fieldAreaStart + nFieldPos) < nFieldLength)
276
3.07k
        {
277
3.07k
            if (!bFailQuietly)
278
1.56k
                CPLError(CE_Failure, CPLE_FileIO,
279
1.56k
                         "Header record invalid on DDF file `%s'.",
280
1.56k
                         pszFilename);
281
282
3.07k
            return FALSE;
283
3.07k
        }
284
285
380k
        auto poFDefn = std::make_unique<DDFFieldDefn>();
286
380k
        if (poFDefn->Initialize(this, szTag, nFieldLength,
287
380k
                                osRecord.c_str() + _fieldAreaStart + nFieldPos))
288
331k
            AddField(std::move(poFDefn));
289
380k
    }
290
291
    /* -------------------------------------------------------------------- */
292
    /*      Record the current file offset, the beginning of the first      */
293
    /*      data record.                                                    */
294
    /* -------------------------------------------------------------------- */
295
30.9k
    nFirstRecordOffset = static_cast<long>(VSIFTellL(fpDDF));
296
297
30.9k
    return TRUE;
298
34.0k
}
299
300
/************************************************************************/
301
/*                             Initialize()                             */
302
/************************************************************************/
303
304
int DDFModule::Initialize(char chInterchangeLevel, char chLeaderIden,
305
                          char chCodeExtensionIndicator, char chVersionNumber,
306
                          char chAppIndicator,
307
                          const std::array<char, 3> &achExtendedCharSet,
308
                          int nSizeFieldLength, int nSizeFieldPos,
309
                          int nSizeFieldTag)
310
311
0
{
312
0
    _interchangeLevel = chInterchangeLevel;
313
0
    _leaderIden = chLeaderIden;
314
0
    _inlineCodeExtensionIndicator = chCodeExtensionIndicator;
315
0
    _versionNumber = chVersionNumber;
316
0
    _appIndicator = chAppIndicator;
317
0
    _extendedCharSet = achExtendedCharSet;
318
0
    _sizeFieldLength = nSizeFieldLength;
319
0
    _sizeFieldPos = nSizeFieldPos;
320
0
    _sizeFieldTag = nSizeFieldTag;
321
322
0
    return TRUE;
323
0
}
324
325
/************************************************************************/
326
/*                               Create()                               */
327
/************************************************************************/
328
329
int DDFModule::Create(const char *pszFilename)
330
331
0
{
332
0
    CPLAssert(fpDDF == nullptr);
333
334
    /* -------------------------------------------------------------------- */
335
    /*      Create the file on disk.                                        */
336
    /* -------------------------------------------------------------------- */
337
0
    fpDDF = VSIFOpenL(pszFilename, "wb+");
338
0
    if (fpDDF == nullptr)
339
0
    {
340
0
        CPLError(CE_Failure, CPLE_OpenFailed,
341
0
                 "Failed to create file %s, check path and permissions.",
342
0
                 pszFilename);
343
0
        return FALSE;
344
0
    }
345
346
0
    bReadOnly = FALSE;
347
348
    /* -------------------------------------------------------------------- */
349
    /*      Prepare all the field definition information.                   */
350
    /* -------------------------------------------------------------------- */
351
0
    _recLength =
352
0
        24 +
353
0
        GetFieldCount() * (_sizeFieldLength + _sizeFieldPos + _sizeFieldTag) +
354
0
        1;
355
356
0
    _fieldAreaStart = _recLength;
357
358
0
    for (auto &poFieldDefn : apoFieldDefns)
359
0
    {
360
0
        int nLength;
361
362
0
        poFieldDefn->GenerateDDREntry(this, nullptr, &nLength);
363
0
        _recLength += nLength;
364
0
    }
365
366
    /* -------------------------------------------------------------------- */
367
    /*      Setup 24 byte leader.                                           */
368
    /* -------------------------------------------------------------------- */
369
0
    char achLeader[25];
370
371
0
    snprintf(achLeader + 0, sizeof(achLeader) - 0, "%05d", _recLength);
372
0
    achLeader[5] = _interchangeLevel;
373
0
    achLeader[6] = _leaderIden;
374
0
    achLeader[7] = _inlineCodeExtensionIndicator;
375
0
    achLeader[8] = _versionNumber;
376
0
    achLeader[9] = _appIndicator;
377
0
    snprintf(achLeader + 10, sizeof(achLeader) - 10, "%02d",
378
0
             _fieldControlLength);
379
0
    snprintf(achLeader + 12, sizeof(achLeader) - 12, "%05d", _fieldAreaStart);
380
0
    memcpy(achLeader + 17, _extendedCharSet.data(), 3);
381
0
    snprintf(achLeader + 20, sizeof(achLeader) - 20, "%1d", _sizeFieldLength);
382
0
    snprintf(achLeader + 21, sizeof(achLeader) - 21, "%1d", _sizeFieldPos);
383
0
    achLeader[22] = '0';
384
0
    snprintf(achLeader + 23, sizeof(achLeader) - 23, "%1d", _sizeFieldTag);
385
0
    int bRet = VSIFWriteL(achLeader, 24, 1, fpDDF) > 0;
386
387
    /* -------------------------------------------------------------------- */
388
    /*      Write out directory entries.                                    */
389
    /* -------------------------------------------------------------------- */
390
0
    int nOffset = 0;
391
0
    for (auto &poFieldDefn : apoFieldDefns)
392
0
    {
393
0
        char achDirEntry[255];
394
0
        char szFormat[32];
395
0
        int nLength;
396
397
0
        CPLAssert(_sizeFieldLength + _sizeFieldPos + _sizeFieldTag <
398
0
                  static_cast<int>(sizeof(achDirEntry)));
399
400
0
        poFieldDefn->GenerateDDREntry(this, nullptr, &nLength);
401
402
0
        CPLAssert(static_cast<int>(strlen(poFieldDefn->GetName())) ==
403
0
                  _sizeFieldTag);
404
0
        snprintf(achDirEntry, sizeof(achDirEntry), "%s",
405
0
                 poFieldDefn->GetName());
406
0
        snprintf(szFormat, sizeof(szFormat), "%%0%dd", _sizeFieldLength);
407
0
        snprintf(achDirEntry + _sizeFieldTag,
408
0
                 sizeof(achDirEntry) - _sizeFieldTag, szFormat, nLength);
409
0
        snprintf(szFormat, sizeof(szFormat), "%%0%dd", _sizeFieldPos);
410
0
        snprintf(achDirEntry + _sizeFieldTag + _sizeFieldLength,
411
0
                 sizeof(achDirEntry) - _sizeFieldTag - _sizeFieldLength,
412
0
                 szFormat, nOffset);
413
0
        nOffset += nLength;
414
415
0
        bRet &= VSIFWriteL(achDirEntry,
416
0
                           _sizeFieldLength + _sizeFieldPos + _sizeFieldTag, 1,
417
0
                           fpDDF) > 0;
418
0
    }
419
420
0
    char chUT = DDF_FIELD_TERMINATOR;
421
0
    bRet &= VSIFWriteL(&chUT, 1, 1, fpDDF) > 0;
422
423
    /* -------------------------------------------------------------------- */
424
    /*      Write out the field descriptions themselves.                    */
425
    /* -------------------------------------------------------------------- */
426
0
    for (auto &poFieldDefn : apoFieldDefns)
427
0
    {
428
0
        char *pachData = nullptr;
429
0
        int nLength = 0;
430
431
0
        poFieldDefn->GenerateDDREntry(this, &pachData, &nLength);
432
0
        bRet &= VSIFWriteL(pachData, nLength, 1, fpDDF) > 0;
433
0
        CPLFree(pachData);
434
0
    }
435
436
0
    return bRet ? TRUE : FALSE;
437
0
}
438
439
/************************************************************************/
440
/*                                Dump()                                */
441
/************************************************************************/
442
443
/**
444
 * Write out module info to debugging file.
445
 *
446
 * A variety of information about the module is written to the debugging
447
 * file.  This includes all the field and subfield definitions read from
448
 * the header.
449
 *
450
 * @param fp The standard IO file handle to write to.  i.e. stderr.
451
 */
452
453
void DDFModule::Dump(FILE *fp, int nNestingLevel) const
454
455
0
{
456
0
    std::string osIndent;
457
0
    for (int i = 0; i < nNestingLevel; ++i)
458
0
        osIndent += "  ";
459
460
0
#define Print(...)                                                             \
461
0
    do                                                                         \
462
0
    {                                                                          \
463
0
        fprintf(fp, "%s", osIndent.c_str());                                   \
464
0
        fprintf(fp, __VA_ARGS__);                                              \
465
0
    } while (0)
466
467
0
    Print("DDFModule:\n");
468
0
    Print("    _recLength = %d\n", _recLength);
469
0
    Print("    _interchangeLevel = %c\n", _interchangeLevel);
470
0
    Print("    _leaderIden = %c\n", _leaderIden);
471
0
    Print("    _inlineCodeExtensionIndicator = %c\n",
472
0
          _inlineCodeExtensionIndicator);
473
0
    Print("    _versionNumber = %c\n", _versionNumber);
474
0
    Print("    _appIndicator = %c\n", _appIndicator);
475
0
    Print("    _extendedCharSet = `%c%c%c'\n", _extendedCharSet[0],
476
0
          _extendedCharSet[1], _extendedCharSet[2]);
477
0
    Print("    _fieldControlLength = %d\n", _fieldControlLength);
478
0
    Print("    _fieldAreaStart = %d\n", _fieldAreaStart);
479
0
    Print("    _sizeFieldLength = %d\n", _sizeFieldLength);
480
0
    Print("    _sizeFieldPos = %d\n", _sizeFieldPos);
481
0
    Print("    _sizeFieldTag = %d\n", _sizeFieldTag);
482
483
0
    for (const auto &poFieldDefn : apoFieldDefns)
484
0
    {
485
0
        poFieldDefn->Dump(fp, nNestingLevel + 1);
486
0
    }
487
0
}
488
489
/************************************************************************/
490
/*                           FindFieldDefn()                            */
491
/************************************************************************/
492
493
/**
494
 * Fetch the definition of the named field.
495
 *
496
 * This function will scan the DDFFieldDefn's on this module, to find
497
 * one with the indicated field name.
498
 *
499
 * @param pszFieldName The name of the field to search for.  The comparison is
500
 *                     case insensitive.
501
 *
502
 * @return A pointer to the request DDFFieldDefn object is returned, or NULL
503
 * if none matching the name are found.  The return object remains owned by
504
 * the DDFModule, and should not be deleted by application code.
505
 */
506
507
const DDFFieldDefn *DDFModule::FindFieldDefn(const char *pszFieldName) const
508
509
258k
{
510
    /* -------------------------------------------------------------------- */
511
    /*      This pass tries to reduce the cost of comparing strings by      */
512
    /*      first checking the first character, and by using strcmp()       */
513
    /* -------------------------------------------------------------------- */
514
258k
    for (const auto &poFieldDefn : apoFieldDefns)
515
1.92M
    {
516
1.92M
        const char *pszThisName = poFieldDefn->GetName();
517
518
1.92M
        if (*pszThisName == *pszFieldName && *pszFieldName != '\0' &&
519
436k
            strcmp(pszFieldName + 1, pszThisName + 1) == 0)
520
253k
            return poFieldDefn.get();
521
1.92M
    }
522
523
    /* -------------------------------------------------------------------- */
524
    /*      Now do a more general check.  Application code may not          */
525
    /*      always use the correct name case.                               */
526
    /* -------------------------------------------------------------------- */
527
5.19k
    for (const auto &poFieldDefn : apoFieldDefns)
528
18.3k
    {
529
18.3k
        if (EQUAL(pszFieldName, poFieldDefn->GetName()))
530
4.52k
            return poFieldDefn.get();
531
18.3k
    }
532
533
669
    return nullptr;
534
5.19k
}
535
536
/************************************************************************/
537
/*                             ReadRecord()                             */
538
/*                                                                      */
539
/*      Read one record from the file, and return to the                */
540
/*      application.  The returned record is owned by the module,       */
541
/*      and is reused from call to call in order to preserve headers    */
542
/*      when they aren't being re-read from record to record.           */
543
/************************************************************************/
544
545
/**
546
 * Read one record from the file.
547
 *
548
 * @return A pointer to a DDFRecord object is returned, or NULL if a read
549
 * error, or end of file occurs.  The returned record is owned by the
550
 * module, and should not be deleted by the application.  The record is
551
 * only valid until the next ReadRecord() at which point it is overwritten.
552
 */
553
554
DDFRecord *DDFModule::ReadRecord()
555
556
328k
{
557
328k
    if (poRecord == nullptr)
558
30.8k
        poRecord = std::make_unique<DDFRecord>(this);
559
560
328k
    if (poRecord->Read())
561
308k
        return poRecord.get();
562
19.7k
    else
563
19.7k
        return nullptr;
564
328k
}
565
566
/************************************************************************/
567
/*                              AddField()                              */
568
/************************************************************************/
569
570
/**
571
 * Add new field definition.
572
 *
573
 * Field definitions may only be added to DDFModules being used for
574
 * writing, not those being used for reading.  Ownership of the
575
 * DDFFieldDefn object is taken by the DDFModule.
576
 *
577
 * @param poNewFDefn definition to be added to the module.
578
 */
579
580
void DDFModule::AddField(std::unique_ptr<DDFFieldDefn> poNewFDefn)
581
582
331k
{
583
331k
    apoFieldDefns.push_back(std::move(poNewFDefn));
584
331k
}
585
586
/************************************************************************/
587
/*                              GetField()                              */
588
/************************************************************************/
589
590
/**
591
 * Fetch a field definition by index.
592
 *
593
 * @param i (from 0 to GetFieldCount() - 1.
594
 * @return the returned field pointer or NULL if the index is out of range.
595
 */
596
597
DDFFieldDefn *DDFModule::GetField(int i)
598
599
0
{
600
0
    if (i < 0 || static_cast<size_t>(i) >= apoFieldDefns.size())
601
0
        return nullptr;
602
0
    else
603
0
        return apoFieldDefns[i].get();
604
0
}
605
606
/************************************************************************/
607
/*                               Rewind()                               */
608
/************************************************************************/
609
610
/**
611
 * Return to first record.
612
 *
613
 * The next call to ReadRecord() will read the first data record in the file.
614
 *
615
 * @param nOffset the offset in the file to return to.  By default this is
616
 * -1, a special value indicating that reading should return to the first
617
 * data record.  Otherwise it is an absolute byte offset in the file.
618
 */
619
620
void DDFModule::Rewind(vsi_l_offset nOffset)
621
622
0
{
623
0
    if (nOffset == static_cast<vsi_l_offset>(-1))
624
0
        nOffset = nFirstRecordOffset;
625
626
0
    if (fpDDF == nullptr)
627
0
        return;
628
629
0
    if (VSIFSeekL(fpDDF, nOffset, SEEK_SET) < 0)
630
0
        return;
631
632
0
    if (nOffset == nFirstRecordOffset && poRecord != nullptr)
633
0
        poRecord->Clear();
634
0
}