Coverage Report

Created: 2026-09-26 08:22

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
72.4k
DDFModule::DDFModule() = default;
35
36
/************************************************************************/
37
/*                             ~DDFModule()                             */
38
/************************************************************************/
39
40
/**
41
 * The destructor.
42
 */
43
44
DDFModule::~DDFModule()
45
46
72.4k
{
47
72.4k
    Close();
48
72.4k
}
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
72.4k
{
64
    /* -------------------------------------------------------------------- */
65
    /*      Close the file.                                                 */
66
    /* -------------------------------------------------------------------- */
67
72.4k
    if (fpDDF != nullptr)
68
42.2k
    {
69
42.2k
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
70
42.2k
        fpDDF = nullptr;
71
42.2k
    }
72
73
72.4k
    poRecord.reset();
74
75
72.4k
    apoFieldDefns.clear();
76
72.4k
}
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
51.1k
{
105
51.1k
    constexpr int nLeaderSize = 24;
106
107
    /* -------------------------------------------------------------------- */
108
    /*      Close the existing file if there is one.                        */
109
    /* -------------------------------------------------------------------- */
110
51.1k
    if (fpDDF != nullptr)
111
0
        Close();
112
113
    /* -------------------------------------------------------------------- */
114
    /*      Open the file.                                                  */
115
    /* -------------------------------------------------------------------- */
116
51.1k
    VSIStatBufL sStat;
117
51.1k
    if (fpDDFIn)
118
7.73k
    {
119
7.73k
        fpDDF = fpDDFIn;
120
7.73k
        CPL_IGNORE_RET_VAL(VSIFSeekL(fpDDF, 0, SEEK_SET));
121
7.73k
    }
122
43.3k
    else
123
43.3k
    {
124
43.3k
        if (VSIStatL(pszFilename, &sStat) == 0 && !VSI_ISDIR(sStat.st_mode))
125
34.8k
            fpDDF = VSIFOpenL(pszFilename, "rb");
126
127
43.3k
        if (fpDDF == nullptr)
128
8.53k
        {
129
8.53k
            if (!bFailQuietly)
130
6
                CPLError(CE_Failure, CPLE_OpenFailed,
131
6
                         "Unable to open DDF file `%s'.", pszFilename);
132
8.53k
            return FALSE;
133
8.53k
        }
134
43.3k
    }
135
136
    /* -------------------------------------------------------------------- */
137
    /*      Read the 24 byte leader.                                        */
138
    /* -------------------------------------------------------------------- */
139
42.5k
    char achLeader[nLeaderSize];
140
141
42.5k
    if (static_cast<int>(VSIFReadL(achLeader, 1, nLeaderSize, fpDDF)) !=
142
42.5k
        nLeaderSize)
143
13
    {
144
13
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
145
13
        fpDDF = nullptr;
146
147
13
        if (!bFailQuietly)
148
6
            CPLError(CE_Failure, CPLE_FileIO,
149
6
                     "Leader is short on DDF file `%s'.", pszFilename);
150
151
13
        return FALSE;
152
13
    }
153
154
    /* -------------------------------------------------------------------- */
155
    /*      Verify that this appears to be a valid DDF file.                */
156
    /* -------------------------------------------------------------------- */
157
42.5k
    int i, bValid = TRUE;
158
159
1.06M
    for (i = 0; i < nLeaderSize; i++)
160
1.02M
    {
161
1.02M
        if (achLeader[i] < 32 || achLeader[i] > 126)
162
1.79k
            bValid = FALSE;
163
1.02M
    }
164
165
42.5k
    if (achLeader[5] != '1' && achLeader[5] != '2' && achLeader[5] != '3')
166
174
        bValid = FALSE;
167
168
42.5k
    if (achLeader[6] != 'L')
169
190
        bValid = FALSE;
170
42.5k
    if (achLeader[8] != '1' && achLeader[8] != ' ')
171
170
        bValid = FALSE;
172
173
    /* -------------------------------------------------------------------- */
174
    /*      Extract information from leader.                                */
175
    /* -------------------------------------------------------------------- */
176
177
42.5k
    if (bValid)
178
42.3k
    {
179
42.3k
        _recLength = DDFScanInt(achLeader + 0, 5);
180
42.3k
        _interchangeLevel = achLeader[5];
181
42.3k
        _leaderIden = achLeader[6];
182
42.3k
        _inlineCodeExtensionIndicator = achLeader[7];
183
42.3k
        _versionNumber = achLeader[8];
184
42.3k
        _appIndicator = achLeader[9];
185
42.3k
        _fieldControlLength = DDFScanInt(achLeader + 10, 2);
186
42.3k
        _fieldAreaStart = DDFScanInt(achLeader + 12, 5);
187
42.3k
        _extendedCharSet[0] = achLeader[17];
188
42.3k
        _extendedCharSet[1] = achLeader[18];
189
42.3k
        _extendedCharSet[2] = achLeader[19];
190
42.3k
        _sizeFieldLength = DDFScanInt(achLeader + 20, 1);
191
42.3k
        _sizeFieldPos = DDFScanInt(achLeader + 21, 1);
192
42.3k
        _sizeFieldTag = DDFScanInt(achLeader + 23, 1);
193
194
42.3k
        if (_recLength < nLeaderSize || _fieldControlLength <= 0 ||
195
42.2k
            _fieldAreaStart < 24 || _sizeFieldLength <= 0 ||
196
42.2k
            _sizeFieldPos <= 0 || _sizeFieldTag <= 0)
197
99
        {
198
99
            bValid = FALSE;
199
99
        }
200
42.3k
    }
201
202
    /* -------------------------------------------------------------------- */
203
    /*      If the header is invalid, then clean up, report the error       */
204
    /*      and return.                                                     */
205
    /* -------------------------------------------------------------------- */
206
42.5k
    if (!bValid)
207
333
    {
208
333
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
209
333
        fpDDF = nullptr;
210
211
333
        if (!bFailQuietly)
212
81
            CPLError(CE_Failure, CPLE_AppDefined,
213
81
                     "File `%s' does not appear to have\n"
214
81
                     "a valid ISO 8211 header.\n",
215
81
                     pszFilename);
216
333
        return FALSE;
217
333
    }
218
219
    /* -------------------------------------------------------------------- */
220
    /*      Read the whole record info memory.                              */
221
    /* -------------------------------------------------------------------- */
222
42.2k
    std::string osRecord;
223
42.2k
    osRecord.assign(achLeader, nLeaderSize);
224
42.2k
    osRecord.resize(_recLength);
225
226
42.2k
    if (static_cast<int>(VSIFReadL(osRecord.data() + nLeaderSize, 1,
227
42.2k
                                   _recLength - nLeaderSize, fpDDF)) !=
228
42.2k
        _recLength - nLeaderSize)
229
155
    {
230
155
        if (!bFailQuietly)
231
74
            CPLError(CE_Failure, CPLE_FileIO,
232
74
                     "Header record is short on DDF file `%s'.", pszFilename);
233
234
155
        return FALSE;
235
155
    }
236
237
    /* -------------------------------------------------------------------- */
238
    /*      First make a pass counting the directory entries.               */
239
    /* -------------------------------------------------------------------- */
240
42.0k
    int nFieldEntryWidth, nFDCount = 0;
241
242
42.0k
    nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
243
244
631k
    for (i = nLeaderSize; i + nFieldEntryWidth <= _recLength;
245
589k
         i += nFieldEntryWidth)
246
628k
    {
247
628k
        if (osRecord[i] == DDF_FIELD_TERMINATOR)
248
38.6k
            break;
249
250
589k
        nFDCount++;
251
589k
    }
252
253
    /* -------------------------------------------------------------------- */
254
    /*      Allocate, and read field definitions.                           */
255
    /* -------------------------------------------------------------------- */
256
491k
    for (i = 0; i < nFDCount; i++)
257
452k
    {
258
452k
        char szTag[128];
259
452k
        int nEntryOffset = nLeaderSize + i * nFieldEntryWidth;
260
452k
        int nFieldLength, nFieldPos;
261
262
452k
        strncpy(szTag, osRecord.c_str() + nEntryOffset, _sizeFieldTag);
263
452k
        szTag[_sizeFieldTag] = '\0';
264
265
452k
        nEntryOffset += _sizeFieldTag;
266
452k
        nFieldLength =
267
452k
            DDFScanInt(osRecord.c_str() + nEntryOffset, _sizeFieldLength);
268
269
452k
        nEntryOffset += _sizeFieldLength;
270
452k
        nFieldPos = DDFScanInt(osRecord.c_str() + nEntryOffset, _sizeFieldPos);
271
272
452k
        if (nFieldPos < 0 || nFieldPos > INT_MAX - _fieldAreaStart ||
273
452k
            nFieldLength <
274
452k
                2 ||  // DDFFieldDefn::Initialize() assumes at least 2 bytes
275
449k
            _recLength - (_fieldAreaStart + nFieldPos) < nFieldLength)
276
3.26k
        {
277
3.26k
            if (!bFailQuietly)
278
1.47k
                CPLError(CE_Failure, CPLE_FileIO,
279
1.47k
                         "Header record invalid on DDF file `%s'.",
280
1.47k
                         pszFilename);
281
282
3.26k
            return FALSE;
283
3.26k
        }
284
285
449k
        auto poFDefn = std::make_unique<DDFFieldDefn>();
286
449k
        if (poFDefn->Initialize(this, szTag, nFieldLength,
287
449k
                                osRecord.c_str() + _fieldAreaStart + nFieldPos))
288
396k
            AddField(std::move(poFDefn));
289
449k
    }
290
291
    /* -------------------------------------------------------------------- */
292
    /*      Record the current file offset, the beginning of the first      */
293
    /*      data record.                                                    */
294
    /* -------------------------------------------------------------------- */
295
38.8k
    nFirstRecordOffset = static_cast<long>(VSIFTellL(fpDDF));
296
297
38.8k
    return TRUE;
298
42.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
882k
{
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
882k
    for (const auto &poFieldDefn : apoFieldDefns)
515
4.50M
    {
516
4.50M
        const char *pszThisName = poFieldDefn->GetName();
517
518
4.50M
        if (*pszThisName == *pszFieldName && *pszFieldName != '\0' &&
519
1.22M
            strcmp(pszFieldName + 1, pszThisName + 1) == 0)
520
876k
            return poFieldDefn.get();
521
4.50M
    }
522
523
    /* -------------------------------------------------------------------- */
524
    /*      Now do a more general check.  Application code may not          */
525
    /*      always use the correct name case.                               */
526
    /* -------------------------------------------------------------------- */
527
5.80k
    for (const auto &poFieldDefn : apoFieldDefns)
528
21.3k
    {
529
21.3k
        if (EQUAL(pszFieldName, poFieldDefn->GetName()))
530
5.00k
            return poFieldDefn.get();
531
21.3k
    }
532
533
806
    return nullptr;
534
5.80k
}
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
429k
{
557
429k
    if (poRecord == nullptr)
558
38.6k
        poRecord = std::make_unique<DDFRecord>(this);
559
560
429k
    if (poRecord->Read())
561
411k
        return poRecord.get();
562
18.6k
    else
563
18.6k
        return nullptr;
564
429k
}
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
396k
{
583
396k
    apoFieldDefns.push_back(std::move(poNewFDefn));
584
396k
}
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
}