Coverage Report

Created: 2025-06-09 07:43

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