Coverage Report

Created: 2025-08-11 09:23

/src/gdal/frmts/iso8211/ddfrecord.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  ISO 8211 Access
4
 * Purpose:  Implements the DDFRecord class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 * Copyright (c) 2010-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 <cstddef>
18
#include <cstdio>
19
#include <cstring>
20
#if HAVE_FCNTL_H
21
#include <fcntl.h>
22
#endif
23
24
#include "cpl_conv.h"
25
#include "cpl_error.h"
26
#include "cpl_vsi.h"
27
28
constexpr int nLeaderSize = 24;
29
30
/************************************************************************/
31
/*                             DDFRecord()                              */
32
/************************************************************************/
33
34
DDFRecord::DDFRecord(DDFModule *poModuleIn)
35
574k
    : poModule(poModuleIn), nReuseHeader(FALSE), nFieldOffset(0),
36
574k
      _sizeFieldTag(poModuleIn->GetSizeFieldTag()), _sizeFieldPos(5),
37
574k
      _sizeFieldLength(5), nDataSize(0), pachData(nullptr), nFieldCount(0),
38
574k
      paoFields(nullptr), bIsClone(FALSE)
39
574k
{
40
574k
}
41
42
/************************************************************************/
43
/*                             ~DDFRecord()                             */
44
/************************************************************************/
45
46
DDFRecord::~DDFRecord()
47
48
574k
{
49
574k
    Clear();
50
51
574k
    if (bIsClone)
52
58.2k
        poModule->RemoveCloneRecord(this);
53
574k
}
54
55
/************************************************************************/
56
/*                                Dump()                                */
57
/************************************************************************/
58
59
/**
60
 * Write out record contents to debugging file.
61
 *
62
 * A variety of information about this record, and all its fields and
63
 * subfields is written to the given debugging file handle.  Note that
64
 * field definition information (ala DDFFieldDefn) isn't written.
65
 *
66
 * @param fp The standard IO file handle to write to.  i.e. stderr
67
 */
68
69
void DDFRecord::Dump(FILE *fp)
70
71
0
{
72
0
    fprintf(fp, "DDFRecord:\n");
73
0
    fprintf(fp, "    nReuseHeader = %d\n", nReuseHeader);
74
0
    fprintf(fp, "    nDataSize = %d\n", nDataSize);
75
0
    fprintf(fp, "    _sizeFieldLength=%d, _sizeFieldPos=%d, _sizeFieldTag=%d\n",
76
0
            _sizeFieldLength, _sizeFieldPos, _sizeFieldTag);
77
78
0
    for (int i = 0; i < nFieldCount; i++)
79
0
    {
80
0
        paoFields[i].Dump(fp);
81
0
    }
82
0
}
83
84
/************************************************************************/
85
/*                                Read()                                */
86
/*                                                                      */
87
/*      Read a record of data from the file, and parse the header to    */
88
/*      build a field list for the record (or reuse the existing one    */
89
/*      if reusing headers).  It is expected that the file pointer      */
90
/*      will be positioned at the beginning of a data record.  It is    */
91
/*      the DDFModule's responsibility to do so.                        */
92
/*                                                                      */
93
/*      This method should only be called by the DDFModule class.       */
94
/************************************************************************/
95
96
int DDFRecord::Read()
97
98
936k
{
99
    /* -------------------------------------------------------------------- */
100
    /*      Redefine the record on the basis of the header if needed.       */
101
    /*      As a side effect this will read the data for the record as well.*/
102
    /* -------------------------------------------------------------------- */
103
936k
    if (!nReuseHeader)
104
74.0k
    {
105
74.0k
        return ReadHeader();
106
74.0k
    }
107
862k
    if (nFieldOffset < 0)
108
7
        return FALSE;
109
110
    /* -------------------------------------------------------------------- */
111
    /*      Otherwise we read just the data and carefully overlay it on     */
112
    /*      the previous records data without disturbing the rest of the    */
113
    /*      record.                                                         */
114
    /* -------------------------------------------------------------------- */
115
862k
    size_t nReadBytes;
116
117
862k
    CPLAssert(nFieldOffset <= nDataSize);
118
862k
    nReadBytes = VSIFReadL(pachData + nFieldOffset, 1, nDataSize - nFieldOffset,
119
862k
                           poModule->GetFP());
120
862k
    if (nReadBytes != (size_t)(nDataSize - nFieldOffset) && nReadBytes == 0 &&
121
862k
        VSIFEofL(poModule->GetFP()))
122
225
    {
123
225
        return FALSE;
124
225
    }
125
861k
    else if (nReadBytes != (size_t)(nDataSize - nFieldOffset))
126
706
    {
127
706
        CPLError(CE_Failure, CPLE_FileIO,
128
706
                 "Data record is short on DDF file.\n");
129
130
706
        return FALSE;
131
706
    }
132
133
    // notdef: eventually we may have to do something at this point to
134
    // notify the DDFField's that their data values have changed.
135
136
861k
    return TRUE;
137
862k
}
138
139
/************************************************************************/
140
/*                               Write()                                */
141
/************************************************************************/
142
143
/**
144
 * Write record out to module.
145
 *
146
 * This method writes the current record to the module to which it is
147
 * attached.  Normally this would be at the end of the file, and only used
148
 * for modules newly created with DDFModule::Create().  Rewriting existing
149
 * records is not supported at this time.  Calling Write() multiple times
150
 * on a DDFRecord will result it multiple copies being written at the end of
151
 * the module.
152
 *
153
 * @return TRUE on success or FALSE on failure.
154
 */
155
156
int DDFRecord::Write()
157
158
0
{
159
0
    ResetDirectory();
160
161
    /* -------------------------------------------------------------------- */
162
    /*      Prepare leader.                                                 */
163
    /* -------------------------------------------------------------------- */
164
0
    char szLeader[nLeaderSize + 1];
165
166
0
    memset(szLeader, ' ', nLeaderSize);
167
168
0
    snprintf(szLeader + 0, sizeof(szLeader) - 0, "%05d",
169
0
             (int)(nDataSize + nLeaderSize));
170
0
    szLeader[5] = ' ';
171
0
    szLeader[6] = 'D';
172
173
0
    snprintf(szLeader + 12, sizeof(szLeader) - 12, "%05d",
174
0
             (int)(nFieldOffset + nLeaderSize));
175
0
    szLeader[17] = ' ';
176
177
0
    szLeader[20] = (char)('0' + _sizeFieldLength);
178
0
    szLeader[21] = (char)('0' + _sizeFieldPos);
179
0
    szLeader[22] = '0';
180
0
    szLeader[23] = (char)('0' + _sizeFieldTag);
181
182
    /* notdef: lots of stuff missing */
183
184
    /* -------------------------------------------------------------------- */
185
    /*      Write the leader.                                               */
186
    /* -------------------------------------------------------------------- */
187
0
    int bRet = VSIFWriteL(szLeader, nLeaderSize, 1, poModule->GetFP()) > 0;
188
189
    /* -------------------------------------------------------------------- */
190
    /*      Write the remainder of the record.                              */
191
    /* -------------------------------------------------------------------- */
192
0
    bRet &= VSIFWriteL(pachData, nDataSize, 1, poModule->GetFP()) > 0;
193
194
0
    return bRet ? TRUE : FALSE;
195
0
}
196
197
/************************************************************************/
198
/*                               Clear()                                */
199
/*                                                                      */
200
/*      Clear any information associated with the last header in        */
201
/*      preparation for reading a new header.                           */
202
/************************************************************************/
203
204
void DDFRecord::Clear()
205
206
648k
{
207
648k
    if (paoFields != nullptr)
208
605k
        delete[] paoFields;
209
210
648k
    paoFields = nullptr;
211
648k
    nFieldCount = 0;
212
213
648k
    if (pachData != nullptr)
214
606k
        CPLFree(pachData);
215
216
648k
    pachData = nullptr;
217
648k
    nDataSize = 0;
218
648k
    nReuseHeader = FALSE;
219
648k
}
220
221
/************************************************************************/
222
/*                             ReadHeader()                             */
223
/*                                                                      */
224
/*      This perform the header reading and parsing job for the         */
225
/*      Read() method.  It reads the header, and builds a field         */
226
/*      list.                                                           */
227
/************************************************************************/
228
229
int DDFRecord::ReadHeader()
230
231
74.0k
{
232
    /* -------------------------------------------------------------------- */
233
    /*      Clear any existing information.                                 */
234
    /* -------------------------------------------------------------------- */
235
74.0k
    Clear();
236
237
    /* -------------------------------------------------------------------- */
238
    /*      Read the 24 byte leader.                                        */
239
    /* -------------------------------------------------------------------- */
240
74.0k
    char achLeader[nLeaderSize];
241
74.0k
    int nReadBytes;
242
243
74.0k
    nReadBytes = static_cast<int>(
244
74.0k
        VSIFReadL(achLeader, 1, nLeaderSize, poModule->GetFP()));
245
74.0k
    if (nReadBytes == 0 && VSIFEofL(poModule->GetFP()))
246
351
    {
247
351
        nFieldOffset = -1;
248
351
        return FALSE;
249
351
    }
250
    // The ASRP and USRP specifications mentions that 0x5E / ^ character can be
251
    // used as a padding byte so that the file size is a multiple of 8192.
252
73.7k
    else if (achLeader[0] == '^')
253
9
    {
254
9
        nFieldOffset = -1;
255
9
        return FALSE;
256
9
    }
257
73.7k
    else if (nReadBytes != (int)nLeaderSize)
258
2.60k
    {
259
2.60k
        CPLError(CE_Failure, CPLE_FileIO, "Leader is short on DDF file.");
260
2.60k
        nFieldOffset = -1;
261
2.60k
        return FALSE;
262
2.60k
    }
263
264
    /* -------------------------------------------------------------------- */
265
    /*      Extract information from leader.                                */
266
    /* -------------------------------------------------------------------- */
267
71.1k
    int _recLength, _fieldAreaStart;
268
71.1k
    char _leaderIden;
269
270
71.1k
    _recLength = DDFScanInt(achLeader + 0, 5);
271
71.1k
    _leaderIden = achLeader[6];
272
71.1k
    _fieldAreaStart = DDFScanInt(achLeader + 12, 5);
273
274
71.1k
    _sizeFieldLength = achLeader[20] - '0';
275
71.1k
    _sizeFieldPos = achLeader[21] - '0';
276
71.1k
    _sizeFieldTag = achLeader[23] - '0';
277
278
71.1k
    if (_sizeFieldLength <= 0 || _sizeFieldLength > 9 || _sizeFieldPos <= 0 ||
279
71.1k
        _sizeFieldPos > 9 || _sizeFieldTag <= 0 || _sizeFieldTag > 9)
280
7.29k
    {
281
7.29k
        CPLError(CE_Failure, CPLE_AppDefined,
282
7.29k
                 "ISO8211 record leader appears to be corrupt.");
283
7.29k
        nFieldOffset = -1;
284
7.29k
        return FALSE;
285
7.29k
    }
286
287
63.8k
    if (_leaderIden == 'R')
288
974
        nReuseHeader = TRUE;
289
290
63.8k
    nFieldOffset = _fieldAreaStart - nLeaderSize;
291
292
    /* -------------------------------------------------------------------- */
293
    /*      Is there anything seemly screwy about this record?              */
294
    /* -------------------------------------------------------------------- */
295
63.8k
    if (((_recLength <= 24 || _recLength > 100000000) && (_recLength != 0)) ||
296
63.8k
        _fieldAreaStart < 24 || _fieldAreaStart > 100000)
297
136
    {
298
136
        CPLError(
299
136
            CE_Failure, CPLE_FileIO,
300
136
            "Data record appears to be corrupt on DDF file.\n"
301
136
            " -- ensure that the files were uncompressed without modifying\n"
302
136
            "carriage return/linefeeds (by default WINZIP does this).");
303
136
        nFieldOffset = -1;
304
136
        return FALSE;
305
136
    }
306
307
    /* ==================================================================== */
308
    /*      Handle the normal case with the record length available.        */
309
    /* ==================================================================== */
310
63.6k
    if (_recLength != 0)
311
32.0k
    {
312
        /* --------------------------------------------------------------------
313
         */
314
        /*      Read the remainder of the record. */
315
        /* --------------------------------------------------------------------
316
         */
317
32.0k
        nDataSize = _recLength - nLeaderSize;
318
32.0k
        pachData = (char *)CPLMalloc(nDataSize + 1);
319
32.0k
        pachData[nDataSize] = '\0';
320
321
32.0k
        if (VSIFReadL(pachData, 1, nDataSize, poModule->GetFP()) !=
322
32.0k
            (size_t)nDataSize)
323
96
        {
324
96
            CPLError(CE_Failure, CPLE_FileIO,
325
96
                     "Data record is short on DDF file.");
326
96
            nFieldOffset = -1;
327
96
            return FALSE;
328
96
        }
329
330
        /* --------------------------------------------------------------------
331
         */
332
        /*      If we don't find a field terminator at the end of the record */
333
        /*      we will read extra bytes till we get to it. */
334
        /* --------------------------------------------------------------------
335
         */
336
31.9k
        int nDataSizeAlloc = nDataSize;
337
31.9k
        while (
338
10.7M
            pachData[nDataSize - 1] != DDF_FIELD_TERMINATOR &&
339
10.7M
            (nDataSize < 2 || pachData[nDataSize - 2] != DDF_FIELD_TERMINATOR))
340
10.6M
        {
341
10.6M
            nDataSize++;
342
10.6M
            if (nDataSize > nDataSizeAlloc)
343
32.6k
            {
344
32.6k
                nDataSizeAlloc *= 2;
345
32.6k
                pachData = (char *)CPLRealloc(pachData, nDataSizeAlloc + 1);
346
32.6k
            }
347
10.6M
            pachData[nDataSize] = '\0';
348
349
10.6M
            if (VSIFReadL(pachData + nDataSize - 1, 1, 1, poModule->GetFP()) !=
350
10.6M
                1)
351
35
            {
352
35
                CPLError(CE_Failure, CPLE_FileIO,
353
35
                         "Data record is short on DDF file.");
354
35
                nFieldOffset = -1;
355
35
                return FALSE;
356
35
            }
357
10.6M
            static bool bFirstTime = true;
358
10.6M
            if (bFirstTime)
359
5
            {
360
5
                bFirstTime = false;
361
5
                CPLDebug("ISO8211",
362
5
                         "Didn't find field terminator, read one more byte.");
363
5
            }
364
10.6M
        }
365
366
31.8k
        if (nFieldOffset >= nDataSize)
367
38
        {
368
38
            CPLError(CE_Failure, CPLE_AssertionFailed,
369
38
                     "nFieldOffset < nDataSize");
370
38
            nFieldOffset = -1;
371
38
            return FALSE;
372
38
        }
373
374
        /* --------------------------------------------------------------------
375
         */
376
        /*      Loop over the directory entries, making a pass counting them. */
377
        /* --------------------------------------------------------------------
378
         */
379
31.8k
        int i;
380
31.8k
        int nFieldEntryWidth;
381
382
31.8k
        nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
383
31.8k
        if (nFieldEntryWidth <= 0)
384
0
        {
385
0
            CPLError(CE_Failure, CPLE_FileIO, "Invalid entry width = %d",
386
0
                     nFieldEntryWidth);
387
0
            nFieldOffset = -1;
388
0
            return FALSE;
389
0
        }
390
391
31.8k
        nFieldCount = 0;
392
759k
        for (i = 0; i + nFieldEntryWidth <= nDataSize; i += nFieldEntryWidth)
393
758k
        {
394
758k
            if (pachData[i] == DDF_FIELD_TERMINATOR)
395
31.0k
                break;
396
397
727k
            nFieldCount++;
398
727k
        }
399
400
        /* --------------------------------------------------------------------
401
         */
402
        /*      Allocate, and read field definitions. */
403
        /* --------------------------------------------------------------------
404
         */
405
31.8k
        paoFields = new DDFField[nFieldCount];
406
407
144k
        for (i = 0; i < nFieldCount; i++)
408
113k
        {
409
113k
            char szTag[128];
410
113k
            int nEntryOffset = i * nFieldEntryWidth;
411
113k
            int nFieldLength, nFieldPos;
412
413
            /* --------------------------------------------------------------------
414
             */
415
            /*      Read the position information and tag. */
416
            /* --------------------------------------------------------------------
417
             */
418
113k
            strncpy(szTag, pachData + nEntryOffset, _sizeFieldTag);
419
113k
            szTag[_sizeFieldTag] = '\0';
420
421
113k
            nEntryOffset += _sizeFieldTag;
422
113k
            nFieldLength =
423
113k
                DDFScanInt(pachData + nEntryOffset, _sizeFieldLength);
424
425
113k
            nEntryOffset += _sizeFieldLength;
426
113k
            nFieldPos = DDFScanInt(pachData + nEntryOffset, _sizeFieldPos);
427
428
            /* --------------------------------------------------------------------
429
             */
430
            /*      Find the corresponding field in the module directory. */
431
            /* --------------------------------------------------------------------
432
             */
433
113k
            DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn(szTag);
434
435
113k
            if (poFieldDefn == nullptr || nFieldLength < 0 || nFieldPos < 0)
436
425
            {
437
425
                CPLError(CE_Failure, CPLE_AppDefined,
438
425
                         "Undefined field `%s' encountered in data record.",
439
425
                         szTag);
440
425
                return FALSE;
441
425
            }
442
443
112k
            if (_fieldAreaStart + nFieldPos - nLeaderSize < 0 ||
444
112k
                nDataSize - (_fieldAreaStart + nFieldPos - nLeaderSize) <
445
112k
                    nFieldLength)
446
80
            {
447
80
                CPLError(CE_Failure, CPLE_AppDefined,
448
80
                         "Not enough byte to initialize field `%s'.", szTag);
449
80
                nFieldOffset = -1;
450
80
                return FALSE;
451
80
            }
452
453
            /* --------------------------------------------------------------------
454
             */
455
            /*      Assign info the DDFField. */
456
            /* --------------------------------------------------------------------
457
             */
458
112k
            paoFields[i].Initialize(poFieldDefn,
459
112k
                                    pachData + _fieldAreaStart + nFieldPos -
460
112k
                                        nLeaderSize,
461
112k
                                    nFieldLength);
462
112k
        }
463
464
31.3k
        return TRUE;
465
31.8k
    }
466
    /* ==================================================================== */
467
    /*      Handle the exceptional case where the record length is          */
468
    /*      zero.  In this case we have to read all the data based on       */
469
    /*      the size of data items as per ISO8211 spec Annex C, 1.5.1.      */
470
    /*                                                                      */
471
    /*      See Bugzilla bug 181 and test with file US4CN21M.000.           */
472
    /* ==================================================================== */
473
31.6k
    else
474
31.6k
    {
475
31.6k
        CPLDebug("ISO8211",
476
31.6k
                 "Record with zero length, use variant (C.1.5.1) logic.");
477
478
        /* ----------------------------------------------------------------- */
479
        /*   _recLength == 0, handle the large record.                       */
480
        /*                                                                   */
481
        /*   Read the remainder of the record.                               */
482
        /* ----------------------------------------------------------------- */
483
31.6k
        nDataSize = 0;
484
31.6k
        pachData = nullptr;
485
486
        /* ----------------------------------------------------------------- */
487
        /*   Loop over the directory entries, making a pass counting them.   */
488
        /* ----------------------------------------------------------------- */
489
31.6k
        int nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
490
31.6k
        nFieldCount = 0;
491
31.6k
        int i = 0;
492
493
31.6k
        if (nFieldEntryWidth == 0)
494
0
        {
495
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
496
0
                     "Invalid record buffer size : %d.", nFieldEntryWidth);
497
0
            nFieldOffset = -1;
498
0
            return FALSE;
499
0
        }
500
501
31.6k
        char *tmpBuf = (char *)VSI_MALLOC_VERBOSE(nFieldEntryWidth);
502
503
31.6k
        if (tmpBuf == nullptr)
504
0
        {
505
0
            nFieldOffset = -1;
506
0
            return FALSE;
507
0
        }
508
509
        // while we're not at the end, store this entry,
510
        // and keep on reading...
511
31.6k
        do
512
229k
        {
513
            // read an Entry:
514
229k
            if (nFieldEntryWidth !=
515
229k
                (int)VSIFReadL(tmpBuf, 1, nFieldEntryWidth, poModule->GetFP()))
516
52
            {
517
52
                CPLError(CE_Failure, CPLE_FileIO,
518
52
                         "Data record is short on DDF file.");
519
52
                CPLFree(tmpBuf);
520
52
                nFieldOffset = -1;
521
52
                return FALSE;
522
52
            }
523
524
            // move this temp buffer into more permanent storage:
525
229k
            char *newBuf = (char *)CPLMalloc(nDataSize + nFieldEntryWidth + 1);
526
229k
            newBuf[nDataSize + nFieldEntryWidth] = '\0';
527
229k
            if (pachData != nullptr)
528
197k
            {
529
197k
                memcpy(newBuf, pachData, nDataSize);
530
197k
                CPLFree(pachData);
531
197k
            }
532
229k
            memcpy(&newBuf[nDataSize], tmpBuf, nFieldEntryWidth);
533
229k
            pachData = newBuf;
534
229k
            nDataSize += nFieldEntryWidth;
535
536
229k
            if (DDF_FIELD_TERMINATOR != tmpBuf[0])
537
197k
            {
538
197k
                nFieldCount++;
539
197k
                if (nFieldCount == 1000)
540
6
                {
541
6
                    CPLError(CE_Failure, CPLE_FileIO,
542
6
                             "Too many fields in DDF file.");
543
6
                    CPLFree(tmpBuf);
544
6
                    nFieldOffset = -1;
545
6
                    return FALSE;
546
6
                }
547
197k
            }
548
229k
        } while (DDF_FIELD_TERMINATOR != tmpBuf[0]);
549
550
31.6k
        CPLFree(tmpBuf);
551
31.6k
        tmpBuf = nullptr;
552
553
        // --------------------------------------------------------------------
554
        // Now, rewind a little.  Only the TERMINATOR should have been read
555
        // --------------------------------------------------------------------
556
31.6k
        int rewindSize = nFieldEntryWidth - 1;
557
31.6k
        VSILFILE *fp = poModule->GetFP();
558
31.6k
        vsi_l_offset pos = VSIFTellL(fp) - rewindSize;
559
31.6k
        if (VSIFSeekL(fp, pos, SEEK_SET) < 0)
560
0
            return FALSE;
561
31.6k
        nDataSize -= rewindSize;
562
563
        // --------------------------------------------------------------------
564
        // Okay, now let's populate the heck out of pachData...
565
        // --------------------------------------------------------------------
566
215k
        for (i = 0; i < nFieldCount; i++)
567
183k
        {
568
183k
            int nEntryOffset = (i * nFieldEntryWidth) + _sizeFieldTag;
569
183k
            int nFieldLength =
570
183k
                DDFScanInt(pachData + nEntryOffset, _sizeFieldLength);
571
183k
            tmpBuf = nullptr;
572
183k
            if (nFieldLength >= 0)
573
183k
                tmpBuf = (char *)VSI_MALLOC_VERBOSE(nFieldLength);
574
183k
            if (tmpBuf == nullptr)
575
17
            {
576
17
                nFieldOffset = -1;
577
17
                return FALSE;
578
17
            }
579
580
            // read an Entry:
581
183k
            if (nFieldLength !=
582
183k
                (int)VSIFReadL(tmpBuf, 1, nFieldLength, poModule->GetFP()))
583
141
            {
584
141
                CPLError(CE_Failure, CPLE_FileIO,
585
141
                         "Data record is short on DDF file.");
586
141
                CPLFree(tmpBuf);
587
141
                nFieldOffset = -1;
588
141
                return FALSE;
589
141
            }
590
591
            // move this temp buffer into more permanent storage:
592
183k
            char *newBuf =
593
183k
                (char *)VSI_MALLOC_VERBOSE(nDataSize + nFieldLength + 1);
594
183k
            if (newBuf == nullptr)
595
0
            {
596
0
                CPLFree(tmpBuf);
597
0
                nFieldOffset = -1;
598
0
                return FALSE;
599
0
            }
600
183k
            newBuf[nDataSize + nFieldLength] = '\0';
601
183k
            memcpy(newBuf, pachData, nDataSize);
602
183k
            CPLFree(pachData);
603
183k
            memcpy(&newBuf[nDataSize], tmpBuf, nFieldLength);
604
183k
            CPLFree(tmpBuf);
605
183k
            pachData = newBuf;
606
183k
            nDataSize += nFieldLength;
607
183k
        }
608
609
31.4k
        if (nFieldOffset >= nDataSize)
610
30
        {
611
30
            CPLError(CE_Failure, CPLE_AssertionFailed,
612
30
                     "nFieldOffset < nDataSize");
613
30
            nFieldOffset = -1;
614
30
            return FALSE;
615
30
        }
616
617
        /* ----------------------------------------------------------------- */
618
        /*     Allocate, and read field definitions.                         */
619
        /* ----------------------------------------------------------------- */
620
31.4k
        paoFields = new DDFField[nFieldCount];
621
622
201k
        for (i = 0; i < nFieldCount; i++)
623
170k
        {
624
170k
            char szTag[128];
625
170k
            int nEntryOffset = i * nFieldEntryWidth;
626
170k
            int nFieldLength, nFieldPos;
627
628
            /* ------------------------------------------------------------- */
629
            /* Read the position information and tag.                        */
630
            /* ------------------------------------------------------------- */
631
170k
            strncpy(szTag, pachData + nEntryOffset, _sizeFieldTag);
632
170k
            szTag[_sizeFieldTag] = '\0';
633
634
170k
            nEntryOffset += _sizeFieldTag;
635
170k
            nFieldLength =
636
170k
                DDFScanInt(pachData + nEntryOffset, _sizeFieldLength);
637
638
170k
            nEntryOffset += _sizeFieldLength;
639
170k
            nFieldPos = DDFScanInt(pachData + nEntryOffset, _sizeFieldPos);
640
641
            /* ------------------------------------------------------------- */
642
            /* Find the corresponding field in the module directory.         */
643
            /* ------------------------------------------------------------- */
644
170k
            DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn(szTag);
645
646
170k
            if (poFieldDefn == nullptr || nFieldLength < 0 || nFieldPos < 0)
647
144
            {
648
144
                CPLError(CE_Failure, CPLE_AppDefined,
649
144
                         "Undefined field `%s' encountered in data record.",
650
144
                         szTag);
651
144
                nFieldOffset = -1;
652
144
                return FALSE;
653
144
            }
654
655
170k
            if (_fieldAreaStart + nFieldPos - nLeaderSize < 0 ||
656
170k
                nDataSize - (_fieldAreaStart + nFieldPos - nLeaderSize) <
657
170k
                    nFieldLength)
658
44
            {
659
44
                CPLError(CE_Failure, CPLE_AppDefined,
660
44
                         "Not enough byte to initialize field `%s'.", szTag);
661
44
                nFieldOffset = -1;
662
44
                return FALSE;
663
44
            }
664
665
            /* ------------------------------------------------------------- */
666
            /* Assign info the DDFField.                                     */
667
            /* ------------------------------------------------------------- */
668
669
170k
            paoFields[i].Initialize(poFieldDefn,
670
170k
                                    pachData + _fieldAreaStart + nFieldPos -
671
170k
                                        nLeaderSize,
672
170k
                                    nFieldLength);
673
170k
        }
674
675
31.2k
        return TRUE;
676
31.4k
    }
677
63.6k
}
678
679
/************************************************************************/
680
/*                             FindField()                              */
681
/************************************************************************/
682
683
/**
684
 * Find the named field within this record.
685
 *
686
 * @param pszName The name of the field to fetch.  The comparison is
687
 * case insensitive.
688
 * @param iFieldIndex The instance of this field to fetch.  Use zero (the
689
 * default) for the first instance.
690
 *
691
 * @return Pointer to the requested DDFField.  This pointer is to an
692
 * internal object, and should not be freed.  It remains valid until
693
 * the next record read.
694
 */
695
696
const DDFField *DDFRecord::FindField(const char *pszName, int iFieldIndex) const
697
698
3.65M
{
699
10.0M
    for (int i = 0; i < nFieldCount; i++)
700
9.85M
    {
701
9.85M
        const DDFFieldDefn *poFieldDefn = paoFields[i].GetFieldDefn();
702
9.85M
        if (poFieldDefn && EQUAL(poFieldDefn->GetName(), pszName))
703
3.41M
        {
704
3.41M
            if (iFieldIndex == 0)
705
3.41M
                return paoFields + i;
706
3
            else
707
3
                iFieldIndex--;
708
3.41M
        }
709
9.85M
    }
710
711
231k
    return nullptr;
712
3.65M
}
713
714
/************************************************************************/
715
/*                              GetField()                              */
716
/************************************************************************/
717
718
/**
719
 * Fetch field object based on index.
720
 *
721
 * @param i The index of the field to fetch.  Between 0 and GetFieldCount()-1.
722
 *
723
 * @return A DDFField pointer, or NULL if the index is out of range.
724
 */
725
726
const DDFField *DDFRecord::GetField(int i) const
727
728
810k
{
729
810k
    if (i < 0 || i >= nFieldCount)
730
13
        return nullptr;
731
810k
    else
732
810k
        return paoFields + i;
733
810k
}
734
735
/************************************************************************/
736
/*                           GetIntSubfield()                           */
737
/************************************************************************/
738
739
/**
740
 * Fetch value of a subfield as an integer.  This is a convenience
741
 * function for fetching a subfield of a field within this record.
742
 *
743
 * @param pszField The name of the field containing the subfield.
744
 * @param iFieldIndex The instance of this field within the record.  Use
745
 * zero for the first instance of this field.
746
 * @param pszSubfield The name of the subfield within the selected field.
747
 * @param iSubfieldIndex The instance of this subfield within the record.
748
 * Use zero for the first instance.
749
 * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
750
 * succeeds, or FALSE if it fails.  Use NULL if you don't want to check
751
 * success.
752
 * @return The value of the subfield, or zero if it failed for some reason.
753
 */
754
755
int DDFRecord::GetIntSubfield(const char *pszField, int iFieldIndex,
756
                              const char *pszSubfield, int iSubfieldIndex,
757
                              int *pnSuccess) const
758
759
1.68M
{
760
1.68M
    int nDummyErr = FALSE;
761
762
1.68M
    if (pnSuccess == nullptr)
763
800k
        pnSuccess = &nDummyErr;
764
765
1.68M
    *pnSuccess = FALSE;
766
767
    /* -------------------------------------------------------------------- */
768
    /*      Fetch the field. If this fails, return zero.                    */
769
    /* -------------------------------------------------------------------- */
770
1.68M
    const DDFField *poField = FindField(pszField, iFieldIndex);
771
1.68M
    if (poField == nullptr)
772
29.1k
        return 0;
773
774
    /* -------------------------------------------------------------------- */
775
    /*      Get the subfield definition                                     */
776
    /* -------------------------------------------------------------------- */
777
1.65M
    const DDFSubfieldDefn *poSFDefn =
778
1.65M
        poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
779
1.65M
    if (poSFDefn == nullptr)
780
337k
        return 0;
781
782
    /* -------------------------------------------------------------------- */
783
    /*      Get a pointer to the data.                                      */
784
    /* -------------------------------------------------------------------- */
785
1.32M
    int nBytesRemaining;
786
787
1.32M
    const char *l_pachData =
788
1.32M
        poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
789
1.32M
    if (l_pachData == nullptr)
790
20.8k
        return 0;
791
792
    /* -------------------------------------------------------------------- */
793
    /*      Return the extracted value.                                     */
794
    /*                                                                      */
795
    /*      Assume an error has occurred if no bytes are consumed.           */
796
    /* -------------------------------------------------------------------- */
797
1.29M
    int nConsumedBytes = 0;
798
1.29M
    int nResult =
799
1.29M
        poSFDefn->ExtractIntData(l_pachData, nBytesRemaining, &nConsumedBytes);
800
801
1.29M
    if (nConsumedBytes > 0)
802
1.29M
        *pnSuccess = TRUE;
803
804
1.29M
    return nResult;
805
1.32M
}
806
807
/************************************************************************/
808
/*                          GetFloatSubfield()                          */
809
/************************************************************************/
810
811
/**
812
 * Fetch value of a subfield as a float (double).  This is a convenience
813
 * function for fetching a subfield of a field within this record.
814
 *
815
 * @param pszField The name of the field containing the subfield.
816
 * @param iFieldIndex The instance of this field within the record.  Use
817
 * zero for the first instance of this field.
818
 * @param pszSubfield The name of the subfield within the selected field.
819
 * @param iSubfieldIndex The instance of this subfield within the record.
820
 * Use zero for the first instance.
821
 * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
822
 * succeeds, or FALSE if it fails.  Use NULL if you don't want to check
823
 * success.
824
 * @return The value of the subfield, or zero if it failed for some reason.
825
 */
826
827
double DDFRecord::GetFloatSubfield(const char *pszField, int iFieldIndex,
828
                                   const char *pszSubfield, int iSubfieldIndex,
829
                                   int *pnSuccess)
830
831
60.0k
{
832
60.0k
    int nDummyErr = FALSE;
833
834
60.0k
    if (pnSuccess == nullptr)
835
29.8k
        pnSuccess = &nDummyErr;
836
837
60.0k
    *pnSuccess = FALSE;
838
839
    /* -------------------------------------------------------------------- */
840
    /*      Fetch the field. If this fails, return zero.                    */
841
    /* -------------------------------------------------------------------- */
842
60.0k
    const DDFField *poField = FindField(pszField, iFieldIndex);
843
60.0k
    if (poField == nullptr)
844
0
        return 0;
845
846
    /* -------------------------------------------------------------------- */
847
    /*      Get the subfield definition                                     */
848
    /* -------------------------------------------------------------------- */
849
60.0k
    const DDFSubfieldDefn *poSFDefn =
850
60.0k
        poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
851
60.0k
    if (poSFDefn == nullptr)
852
58.1k
        return 0;
853
854
    /* -------------------------------------------------------------------- */
855
    /*      Get a pointer to the data.                                      */
856
    /* -------------------------------------------------------------------- */
857
1.94k
    int nBytesRemaining;
858
859
1.94k
    const char *l_pachData =
860
1.94k
        poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
861
1.94k
    if (l_pachData == nullptr)
862
177
        return 0;
863
864
    /* -------------------------------------------------------------------- */
865
    /*      Return the extracted value.                                     */
866
    /* -------------------------------------------------------------------- */
867
1.76k
    int nConsumedBytes = 0;
868
1.76k
    double dfResult = poSFDefn->ExtractFloatData(l_pachData, nBytesRemaining,
869
1.76k
                                                 &nConsumedBytes);
870
871
1.76k
    if (nConsumedBytes > 0)
872
1.70k
        *pnSuccess = TRUE;
873
874
1.76k
    return dfResult;
875
1.94k
}
876
877
/************************************************************************/
878
/*                         GetStringSubfield()                          */
879
/************************************************************************/
880
881
/**
882
 * Fetch value of a subfield as a string.  This is a convenience
883
 * function for fetching a subfield of a field within this record.
884
 *
885
 * @param pszField The name of the field containing the subfield.
886
 * @param iFieldIndex The instance of this field within the record.  Use
887
 * zero for the first instance of this field.
888
 * @param pszSubfield The name of the subfield within the selected field.
889
 * @param iSubfieldIndex The instance of this subfield within the record.
890
 * Use zero for the first instance.
891
 * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
892
 * succeeds, or FALSE if it fails.  Use NULL if you don't want to check
893
 * success.
894
 * @return The value of the subfield, or NULL if it failed for some reason.
895
 * The returned pointer is to internal data and should not be modified or
896
 * freed by the application.
897
 */
898
899
const char *DDFRecord::GetStringSubfield(const char *pszField, int iFieldIndex,
900
                                         const char *pszSubfield,
901
                                         int iSubfieldIndex, int *pnSuccess)
902
903
1.38M
{
904
1.38M
    int nDummyErr = FALSE;
905
906
1.38M
    if (pnSuccess == nullptr)
907
1.31M
        pnSuccess = &nDummyErr;
908
909
1.38M
    *pnSuccess = FALSE;
910
911
    /* -------------------------------------------------------------------- */
912
    /*      Fetch the field. If this fails, return zero.                    */
913
    /* -------------------------------------------------------------------- */
914
1.38M
    const DDFField *poField = FindField(pszField, iFieldIndex);
915
1.38M
    if (poField == nullptr)
916
161k
        return nullptr;
917
918
    /* -------------------------------------------------------------------- */
919
    /*      Get the subfield definition                                     */
920
    /* -------------------------------------------------------------------- */
921
1.21M
    const DDFSubfieldDefn *poSFDefn =
922
1.21M
        poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
923
1.21M
    if (poSFDefn == nullptr)
924
570k
        return nullptr;
925
926
    /* -------------------------------------------------------------------- */
927
    /*      Get a pointer to the data.                                      */
928
    /* -------------------------------------------------------------------- */
929
648k
    int nBytesRemaining;
930
931
648k
    const char *l_pachData =
932
648k
        poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
933
648k
    if (l_pachData == nullptr)
934
189k
        return nullptr;
935
936
    /* -------------------------------------------------------------------- */
937
    /*      Return the extracted value.                                     */
938
    /* -------------------------------------------------------------------- */
939
459k
    *pnSuccess = TRUE;
940
941
459k
    return poSFDefn->ExtractStringData(l_pachData, nBytesRemaining, nullptr);
942
648k
}
943
944
/************************************************************************/
945
/*                               Clone()                                */
946
/************************************************************************/
947
948
/**
949
 * Make a copy of a record.
950
 *
951
 * This method is used to make a copy of a record that will become (mostly)
952
 * the properly of application.  However, it is automatically destroyed if
953
 * the DDFModule it was created relative to is destroyed, as its field
954
 * and subfield definitions relate to that DDFModule.  However, it does
955
 * persist even when the record returned by DDFModule::ReadRecord() is
956
 * invalidated, such as when reading a new record.  This allows an application
957
 * to cache whole DDFRecords.
958
 *
959
 * @return A new copy of the DDFRecord.  This can be delete'd by the
960
 * application when no longer needed, otherwise it will be cleaned up when
961
 * the DDFModule it relates to is destroyed or closed.
962
 */
963
964
DDFRecord *DDFRecord::Clone()
965
966
542k
{
967
542k
    DDFRecord *poNR = new DDFRecord(poModule);
968
969
542k
    poNR->nReuseHeader = FALSE;
970
542k
    poNR->nFieldOffset = nFieldOffset;
971
972
542k
    poNR->nDataSize = nDataSize;
973
542k
    poNR->pachData = (char *)CPLMalloc(nDataSize + 1);
974
542k
    memcpy(poNR->pachData, pachData, nDataSize);
975
542k
    poNR->pachData[nDataSize] = '\0';
976
977
542k
    poNR->nFieldCount = nFieldCount;
978
542k
    poNR->paoFields = new DDFField[nFieldCount];
979
1.70M
    for (int i = 0; i < nFieldCount; i++)
980
1.15M
    {
981
1.15M
        int nOffset;
982
983
1.15M
        nOffset = static_cast<int>(paoFields[i].GetData() - pachData);
984
1.15M
        poNR->paoFields[i].Initialize(paoFields[i].GetFieldDefn(),
985
1.15M
                                      poNR->pachData + nOffset,
986
1.15M
                                      paoFields[i].GetDataSize());
987
1.15M
    }
988
989
542k
    poNR->bIsClone = TRUE;
990
542k
    poModule->AddCloneRecord(poNR);
991
992
542k
    return poNR;
993
542k
}
994
995
/************************************************************************/
996
/*                              CloneOn()                               */
997
/************************************************************************/
998
999
/**
1000
 * Recreate a record referencing another module.
1001
 *
1002
 * Works similarly to the DDFRecord::Clone() method, but creates the
1003
 * new record with reference to a different DDFModule.  All DDFFieldDefn
1004
 * references are transcribed onto the new module based on field names.
1005
 * If any fields don't have a similarly named field on the target module
1006
 * the operation will fail.  No validation of field types and properties
1007
 * is done, but this operation is intended only to be used between
1008
 * modules with matching definitions of all affected fields.
1009
 *
1010
 * The new record will be managed as a clone by the target module in
1011
 * a manner similar to regular clones.
1012
 *
1013
 * @param poTargetModule the module on which the record copy should be
1014
 * created.
1015
 *
1016
 * @return NULL on failure or a pointer to the cloned record.
1017
 */
1018
1019
DDFRecord *DDFRecord::CloneOn(DDFModule *poTargetModule)
1020
1021
0
{
1022
    /* -------------------------------------------------------------------- */
1023
    /*      Verify that all fields have a corresponding field definition    */
1024
    /*      on the target module.                                           */
1025
    /* -------------------------------------------------------------------- */
1026
0
    for (int i = 0; i < nFieldCount; i++)
1027
0
    {
1028
0
        DDFFieldDefn *poDefn = paoFields[i].GetFieldDefn();
1029
1030
0
        if (poTargetModule->FindFieldDefn(poDefn->GetName()) == nullptr)
1031
0
            return nullptr;
1032
0
    }
1033
1034
    /* -------------------------------------------------------------------- */
1035
    /*      Create a clone.                                                 */
1036
    /* -------------------------------------------------------------------- */
1037
0
    DDFRecord *poClone = Clone();
1038
1039
    /* -------------------------------------------------------------------- */
1040
    /*      Update all internal information to reference other module.      */
1041
    /* -------------------------------------------------------------------- */
1042
0
    for (int i = 0; i < nFieldCount; i++)
1043
0
    {
1044
0
        DDFField *poField = poClone->paoFields + i;
1045
0
        DDFFieldDefn *poDefn =
1046
0
            poTargetModule->FindFieldDefn(poField->GetFieldDefn()->GetName());
1047
1048
0
        poField->Initialize(poDefn, poField->GetData(), poField->GetDataSize());
1049
0
    }
1050
1051
0
    poModule->RemoveCloneRecord(poClone);
1052
0
    poClone->poModule = poTargetModule;
1053
0
    poTargetModule->AddCloneRecord(poClone);
1054
1055
0
    return poClone;
1056
0
}
1057
1058
/************************************************************************/
1059
/*                            DeleteField()                             */
1060
/************************************************************************/
1061
1062
/**
1063
 * Delete a field instance from a record.
1064
 *
1065
 * Remove a field from this record, cleaning up the data
1066
 * portion and repacking the fields list.  We don't try to
1067
 * reallocate the data area of the record to be smaller.
1068
 *
1069
 * NOTE: This method doesn't actually remove the header
1070
 * information for this field from the record tag list yet.
1071
 * This should be added if the resulting record is even to be
1072
 * written back to disk!
1073
 *
1074
 * @param poTarget the field instance on this record to delete.
1075
 *
1076
 * @return TRUE on success, or FALSE on failure.  Failure can occur if
1077
 * poTarget isn't really a field on this record.
1078
 */
1079
1080
int DDFRecord::DeleteField(DDFField *poTarget)
1081
1082
0
{
1083
0
    int iTarget, i;
1084
1085
    /* -------------------------------------------------------------------- */
1086
    /*      Find which field we are to delete.                              */
1087
    /* -------------------------------------------------------------------- */
1088
0
    for (iTarget = 0; iTarget < nFieldCount; iTarget++)
1089
0
    {
1090
0
        if (paoFields + iTarget == poTarget)
1091
0
            break;
1092
0
    }
1093
1094
0
    if (iTarget == nFieldCount)
1095
0
        return FALSE;
1096
1097
    /* -------------------------------------------------------------------- */
1098
    /*      Change the target fields data size to zero.  This takes care    */
1099
    /*      of repacking the data array, and updating all the following     */
1100
    /*      field data pointers.                                            */
1101
    /* -------------------------------------------------------------------- */
1102
0
    ResizeField(poTarget, 0);
1103
1104
    /* -------------------------------------------------------------------- */
1105
    /*      remove the target field, moving down all the other fields       */
1106
    /*      one step in the field list.                                     */
1107
    /* -------------------------------------------------------------------- */
1108
0
    for (i = iTarget; i < nFieldCount - 1; i++)
1109
0
    {
1110
0
        paoFields[i] = paoFields[i + 1];
1111
0
    }
1112
1113
0
    nFieldCount--;
1114
1115
0
    return TRUE;
1116
0
}
1117
1118
/************************************************************************/
1119
/*                            ResizeField()                             */
1120
/************************************************************************/
1121
1122
/**
1123
 * Alter field data size within record.
1124
 *
1125
 * This method will rearrange a DDFRecord altering the amount of space
1126
 * reserved for one of the existing fields.  All following fields will
1127
 * be shifted accordingly.  This includes updating the DDFField infos,
1128
 * and actually moving stuff within the data array after reallocating
1129
 * to the desired size.
1130
 *
1131
 * @param poField the field to alter.
1132
 * @param nNewDataSize the number of data bytes to be reserved for the field.
1133
 *
1134
 * @return TRUE on success or FALSE on failure.
1135
 */
1136
1137
int DDFRecord::ResizeField(DDFField *poField, int nNewDataSize)
1138
1139
0
{
1140
0
    int iTarget, i;
1141
0
    int nBytesToMove;
1142
1143
    /* -------------------------------------------------------------------- */
1144
    /*      Find which field we are to resize.                              */
1145
    /* -------------------------------------------------------------------- */
1146
0
    for (iTarget = 0; iTarget < nFieldCount; iTarget++)
1147
0
    {
1148
0
        if (paoFields + iTarget == poField)
1149
0
            break;
1150
0
    }
1151
1152
0
    if (iTarget == nFieldCount)
1153
0
    {
1154
0
        CPLAssert(false);
1155
0
        return FALSE;
1156
0
    }
1157
1158
    /* -------------------------------------------------------------------- */
1159
    /*      Reallocate the data buffer accordingly.                         */
1160
    /* -------------------------------------------------------------------- */
1161
0
    int nBytesToAdd = nNewDataSize - poField->GetDataSize();
1162
0
    const char *pachOldData = pachData;
1163
1164
    // Don't realloc things smaller ... we will cut off some data.
1165
0
    if (nBytesToAdd > 0)
1166
0
    {
1167
0
        pachData = (char *)CPLRealloc(pachData, nDataSize + nBytesToAdd + 1);
1168
0
        pachData[nDataSize + nBytesToAdd] = '\0';
1169
0
    }
1170
1171
0
    nDataSize += nBytesToAdd;
1172
1173
    /* -------------------------------------------------------------------- */
1174
    /*      How much data needs to be shifted up or down after this field?  */
1175
    /* -------------------------------------------------------------------- */
1176
0
    nBytesToMove = nDataSize - static_cast<int>(poField->GetData() +
1177
0
                                                poField->GetDataSize() -
1178
0
                                                pachOldData + nBytesToAdd);
1179
1180
    /* -------------------------------------------------------------------- */
1181
    /*      Update fields to point into newly allocated buffer.             */
1182
    /* -------------------------------------------------------------------- */
1183
0
    for (i = 0; i < nFieldCount; i++)
1184
0
    {
1185
0
        int nOffset;
1186
1187
0
        nOffset = static_cast<int>(paoFields[i].GetData() - pachOldData);
1188
0
        paoFields[i].Initialize(paoFields[i].GetFieldDefn(), pachData + nOffset,
1189
0
                                paoFields[i].GetDataSize());
1190
0
    }
1191
1192
    /* -------------------------------------------------------------------- */
1193
    /*      Shift the data beyond this field up or down as needed.          */
1194
    /* -------------------------------------------------------------------- */
1195
0
    if (nBytesToMove > 0)
1196
0
        memmove(
1197
0
            (char *)poField->GetData() + poField->GetDataSize() + nBytesToAdd,
1198
0
            (char *)poField->GetData() + poField->GetDataSize(), nBytesToMove);
1199
1200
    /* -------------------------------------------------------------------- */
1201
    /*      Update the target fields info.                                  */
1202
    /* -------------------------------------------------------------------- */
1203
0
    poField->Initialize(poField->GetFieldDefn(), poField->GetData(),
1204
0
                        poField->GetDataSize() + nBytesToAdd);
1205
1206
    /* -------------------------------------------------------------------- */
1207
    /*      Shift all following fields down, and update their data          */
1208
    /*      locations.                                                      */
1209
    /* -------------------------------------------------------------------- */
1210
0
    if (nBytesToAdd < 0)
1211
0
    {
1212
0
        for (i = iTarget + 1; i < nFieldCount; i++)
1213
0
        {
1214
0
            char *pszOldDataLocation = (char *)paoFields[i].GetData();
1215
1216
0
            paoFields[i].Initialize(paoFields[i].GetFieldDefn(),
1217
0
                                    pszOldDataLocation + nBytesToAdd,
1218
0
                                    paoFields[i].GetDataSize());
1219
0
        }
1220
0
    }
1221
0
    else
1222
0
    {
1223
0
        for (i = nFieldCount - 1; i > iTarget; i--)
1224
0
        {
1225
0
            char *pszOldDataLocation = (char *)paoFields[i].GetData();
1226
1227
0
            paoFields[i].Initialize(paoFields[i].GetFieldDefn(),
1228
0
                                    pszOldDataLocation + nBytesToAdd,
1229
0
                                    paoFields[i].GetDataSize());
1230
0
        }
1231
0
    }
1232
1233
0
    return TRUE;
1234
0
}
1235
1236
/************************************************************************/
1237
/*                              AddField()                              */
1238
/************************************************************************/
1239
1240
/**
1241
 * Add a new field to record.
1242
 *
1243
 * Add a new zero sized field to the record.  The new field is always
1244
 * added at the end of the record.
1245
 *
1246
 * NOTE: This method doesn't currently update the header information for
1247
 * the record to include the field information for this field, so the
1248
 * resulting record image isn't suitable for writing to disk.  However,
1249
 * everything else about the record state should be updated properly to
1250
 * reflect the new field.
1251
 *
1252
 * @param poDefn the definition of the field to be added.
1253
 *
1254
 * @return the field object on success, or NULL on failure.
1255
 */
1256
1257
DDFField *DDFRecord::AddField(DDFFieldDefn *poDefn)
1258
1259
0
{
1260
    /* -------------------------------------------------------------------- */
1261
    /*      Reallocate the fields array larger by one, and initialize       */
1262
    /*      the new field.                                                  */
1263
    /* -------------------------------------------------------------------- */
1264
0
    DDFField *paoNewFields = new DDFField[nFieldCount + 1];
1265
0
    if (nFieldCount > 0)
1266
0
    {
1267
0
        memcpy(paoNewFields, paoFields, sizeof(DDFField) * nFieldCount);
1268
0
        delete[] paoFields;
1269
0
    }
1270
0
    paoFields = paoNewFields;
1271
0
    nFieldCount++;
1272
1273
    /* -------------------------------------------------------------------- */
1274
    /*      Initialize the new field properly.                              */
1275
    /* -------------------------------------------------------------------- */
1276
0
    if (nFieldCount == 1)
1277
0
    {
1278
0
        paoFields[0].Initialize(poDefn, GetData(), 0);
1279
0
    }
1280
0
    else
1281
0
    {
1282
0
        paoFields[nFieldCount - 1].Initialize(
1283
0
            poDefn,
1284
0
            paoFields[nFieldCount - 2].GetData() +
1285
0
                paoFields[nFieldCount - 2].GetDataSize(),
1286
0
            0);
1287
0
    }
1288
1289
    /* -------------------------------------------------------------------- */
1290
    /*      Initialize field.                                               */
1291
    /* -------------------------------------------------------------------- */
1292
0
    CreateDefaultFieldInstance(paoFields + nFieldCount - 1, 0);
1293
1294
0
    return paoFields + (nFieldCount - 1);
1295
0
}
1296
1297
/************************************************************************/
1298
/*                            SetFieldRaw()                             */
1299
/************************************************************************/
1300
1301
/**
1302
 * Set the raw contents of a field instance.
1303
 *
1304
 * @param poField the field to set data within.
1305
 * @param iIndexWithinField The instance of this field to replace.  Must
1306
 * be a value between 0 and GetRepeatCount().  If GetRepeatCount() is used, a
1307
 * new instance of the field is appended.
1308
 * @param pachRawData the raw data to replace this field instance with.
1309
 * @param nRawDataSize the number of bytes pointed to by pachRawData.
1310
 *
1311
 * @return TRUE on success or FALSE on failure.
1312
 */
1313
1314
int DDFRecord::SetFieldRaw(DDFField *poField, int iIndexWithinField,
1315
                           const char *pachRawData, int nRawDataSize)
1316
1317
0
{
1318
0
    int iTarget, nRepeatCount;
1319
1320
    /* -------------------------------------------------------------------- */
1321
    /*      Find which field we are to update.                              */
1322
    /* -------------------------------------------------------------------- */
1323
0
    for (iTarget = 0; iTarget < nFieldCount; iTarget++)
1324
0
    {
1325
0
        if (paoFields + iTarget == poField)
1326
0
            break;
1327
0
    }
1328
1329
0
    if (iTarget == nFieldCount)
1330
0
        return FALSE;
1331
1332
0
    nRepeatCount = poField->GetRepeatCount();
1333
1334
0
    if (iIndexWithinField < 0 || iIndexWithinField > nRepeatCount)
1335
0
        return FALSE;
1336
1337
    /* -------------------------------------------------------------------- */
1338
    /*      Are we adding an instance?  This is easier and different        */
1339
    /*      than replacing an existing instance.                            */
1340
    /* -------------------------------------------------------------------- */
1341
0
    if (iIndexWithinField == nRepeatCount ||
1342
0
        !poField->GetFieldDefn()->IsRepeating())
1343
0
    {
1344
0
        if (!poField->GetFieldDefn()->IsRepeating() && iIndexWithinField != 0)
1345
0
            return FALSE;
1346
1347
0
        int nOldSize = poField->GetDataSize();
1348
0
        if (nOldSize == 0)
1349
0
            nOldSize++;  // for added DDF_FIELD_TERMINATOR.
1350
1351
0
        if (!ResizeField(poField, nOldSize + nRawDataSize))
1352
0
            return FALSE;
1353
1354
0
        char *pachFieldData = (char *)poField->GetData();
1355
0
        memcpy(pachFieldData + nOldSize - 1, pachRawData, nRawDataSize);
1356
0
        pachFieldData[nOldSize + nRawDataSize - 1] = DDF_FIELD_TERMINATOR;
1357
1358
0
        return TRUE;
1359
0
    }
1360
1361
    /* -------------------------------------------------------------------- */
1362
    /*      Get a pointer to the start of the existing data for this        */
1363
    /*      iteration of the field.                                         */
1364
    /* -------------------------------------------------------------------- */
1365
0
    const char *pachWrkData = nullptr;
1366
0
    int nInstanceSize = 0;
1367
1368
    // We special case this to avoid a lot of warnings when initializing
1369
    // the field the first time.
1370
0
    if (poField->GetDataSize() == 0)
1371
0
    {
1372
0
        pachWrkData = poField->GetData();
1373
0
    }
1374
0
    else
1375
0
    {
1376
0
        pachWrkData =
1377
0
            poField->GetInstanceData(iIndexWithinField, &nInstanceSize);
1378
0
    }
1379
1380
    /* -------------------------------------------------------------------- */
1381
    /*      Create new image of this whole field.                           */
1382
    /* -------------------------------------------------------------------- */
1383
0
    int nNewFieldSize = poField->GetDataSize() - nInstanceSize + nRawDataSize;
1384
1385
0
    char *pachNewImage = (char *)CPLMalloc(nNewFieldSize);
1386
1387
0
    int nPreBytes = static_cast<int>(pachWrkData - poField->GetData());
1388
0
    int nPostBytes = poField->GetDataSize() - nPreBytes - nInstanceSize;
1389
1390
0
    memcpy(pachNewImage, poField->GetData(), nPreBytes);
1391
0
    memcpy(pachNewImage + nPreBytes + nRawDataSize,
1392
0
           poField->GetData() + nPreBytes + nInstanceSize, nPostBytes);
1393
0
    memcpy(pachNewImage + nPreBytes, pachRawData, nRawDataSize);
1394
1395
    /* -------------------------------------------------------------------- */
1396
    /*      Resize the field to the desired new size.                       */
1397
    /* -------------------------------------------------------------------- */
1398
0
    ResizeField(poField, nNewFieldSize);
1399
1400
0
    memcpy((void *)poField->GetData(), pachNewImage, nNewFieldSize);
1401
0
    CPLFree(pachNewImage);
1402
1403
0
    return TRUE;
1404
0
}
1405
1406
/************************************************************************/
1407
/*                           UpdateFieldRaw()                           */
1408
/************************************************************************/
1409
1410
int DDFRecord::UpdateFieldRaw(DDFField *poField, int iIndexWithinField,
1411
                              int nStartOffset, int nOldSize,
1412
                              const char *pachRawData, int nRawDataSize)
1413
1414
0
{
1415
0
    int iTarget, nRepeatCount;
1416
1417
    /* -------------------------------------------------------------------- */
1418
    /*      Find which field we are to update.                              */
1419
    /* -------------------------------------------------------------------- */
1420
0
    for (iTarget = 0; iTarget < nFieldCount; iTarget++)
1421
0
    {
1422
0
        if (paoFields + iTarget == poField)
1423
0
            break;
1424
0
    }
1425
1426
0
    if (iTarget == nFieldCount)
1427
0
        return FALSE;
1428
1429
0
    nRepeatCount = poField->GetRepeatCount();
1430
1431
0
    if (iIndexWithinField < 0 || iIndexWithinField >= nRepeatCount)
1432
0
        return FALSE;
1433
1434
    /* -------------------------------------------------------------------- */
1435
    /*      Figure out how much pre and post data there is.                 */
1436
    /* -------------------------------------------------------------------- */
1437
0
    int nInstanceSize = 0;
1438
1439
0
    char *pachWrkData =
1440
0
        (char *)poField->GetInstanceData(iIndexWithinField, &nInstanceSize);
1441
0
    int nPreBytes =
1442
0
        static_cast<int>(pachWrkData - poField->GetData() + nStartOffset);
1443
0
    int nPostBytes = poField->GetDataSize() - nPreBytes - nOldSize;
1444
1445
    /* -------------------------------------------------------------------- */
1446
    /*      If we aren't changing the size, just copy over the existing     */
1447
    /*      data.                                                           */
1448
    /* -------------------------------------------------------------------- */
1449
0
    if (nOldSize == nRawDataSize)
1450
0
    {
1451
0
        memcpy(pachWrkData + nStartOffset, pachRawData, nRawDataSize);
1452
0
        return TRUE;
1453
0
    }
1454
1455
    /* -------------------------------------------------------------------- */
1456
    /*      If we are shrinking, move in the new data, and shuffle down     */
1457
    /*      the old before resizing.                                        */
1458
    /* -------------------------------------------------------------------- */
1459
0
    if (nRawDataSize < nOldSize)
1460
0
    {
1461
0
        memcpy(((char *)poField->GetData()) + nPreBytes, pachRawData,
1462
0
               nRawDataSize);
1463
0
        memmove(((char *)poField->GetData()) + nPreBytes + nRawDataSize,
1464
0
                ((char *)poField->GetData()) + nPreBytes + nOldSize,
1465
0
                nPostBytes);
1466
0
    }
1467
1468
    /* -------------------------------------------------------------------- */
1469
    /*      Resize the whole buffer.                                        */
1470
    /* -------------------------------------------------------------------- */
1471
0
    if (!ResizeField(poField, poField->GetDataSize() - nOldSize + nRawDataSize))
1472
0
        return FALSE;
1473
1474
    /* -------------------------------------------------------------------- */
1475
    /*      If we growing the buffer, shuffle up the post data, and         */
1476
    /*      move in our new values.                                         */
1477
    /* -------------------------------------------------------------------- */
1478
0
    if (nRawDataSize >= nOldSize)
1479
0
    {
1480
0
        memmove(((char *)poField->GetData()) + nPreBytes + nRawDataSize,
1481
0
                ((char *)poField->GetData()) + nPreBytes + nOldSize,
1482
0
                nPostBytes);
1483
0
        memcpy(((char *)poField->GetData()) + nPreBytes, pachRawData,
1484
0
               nRawDataSize);
1485
0
    }
1486
1487
0
    return TRUE;
1488
0
}
1489
1490
/************************************************************************/
1491
/*                           ResetDirectory()                           */
1492
/*                                                                      */
1493
/*      Re-prepares the directory information for the record.           */
1494
/************************************************************************/
1495
1496
void DDFRecord::ResetDirectory()
1497
1498
0
{
1499
0
    int iField;
1500
1501
    /* -------------------------------------------------------------------- */
1502
    /*      Eventually we should try to optimize the size of offset and     */
1503
    /*      field length.                                                   */
1504
    /* -------------------------------------------------------------------- */
1505
1506
    /* -------------------------------------------------------------------- */
1507
    /*      Compute how large the directory needs to be.                    */
1508
    /* -------------------------------------------------------------------- */
1509
0
    int nEntrySize, nDirSize;
1510
1511
0
    nEntrySize = _sizeFieldPos + _sizeFieldLength + _sizeFieldTag;
1512
0
    nDirSize = nEntrySize * nFieldCount + 1;
1513
1514
    /* -------------------------------------------------------------------- */
1515
    /*      If the directory size is different than what is currently       */
1516
    /*      reserved for it, we must resize.                                */
1517
    /* -------------------------------------------------------------------- */
1518
0
    if (nDirSize != nFieldOffset)
1519
0
    {
1520
0
        const int nNewDataSize = nDataSize - nFieldOffset + nDirSize;
1521
0
        char *pachNewData = (char *)CPLMalloc(nNewDataSize + 1);
1522
0
        pachNewData[nNewDataSize] = '\0';
1523
0
        memcpy(pachNewData + nDirSize, pachData + nFieldOffset,
1524
0
               nNewDataSize - nDirSize);
1525
1526
0
        for (iField = 0; paoFields != nullptr && iField < nFieldCount; iField++)
1527
0
        {
1528
0
            int nOffset;
1529
0
            DDFField *poField = /*GetField( iField )*/ paoFields + iField;
1530
1531
0
            nOffset = static_cast<int>(poField->GetData() - pachData -
1532
0
                                       nFieldOffset + nDirSize);
1533
0
            poField->Initialize(poField->GetFieldDefn(), pachNewData + nOffset,
1534
0
                                poField->GetDataSize());
1535
0
        }
1536
1537
0
        CPLFree(pachData);
1538
0
        pachData = pachNewData;
1539
0
        nDataSize = nNewDataSize;
1540
0
        nFieldOffset = nDirSize;
1541
0
    }
1542
1543
    /* -------------------------------------------------------------------- */
1544
    /*      Now set each directory entry.                                   */
1545
    /* -------------------------------------------------------------------- */
1546
0
    for (iField = 0; paoFields != nullptr && iField < nFieldCount; iField++)
1547
0
    {
1548
0
        DDFField *poField = /*GetField( iField )*/ paoFields + iField;
1549
0
        DDFFieldDefn *poDefn = poField->GetFieldDefn();
1550
0
        char szFormat[128];
1551
1552
0
        snprintf(szFormat, sizeof(szFormat), "%%%ds%%0%dd%%0%dd", _sizeFieldTag,
1553
0
                 _sizeFieldLength, _sizeFieldPos);
1554
1555
0
        snprintf(pachData + nEntrySize * iField, nEntrySize + 1, szFormat,
1556
0
                 poDefn->GetName(), poField->GetDataSize(),
1557
0
                 poField->GetData() - pachData - nFieldOffset);
1558
0
    }
1559
1560
0
    pachData[nEntrySize * nFieldCount] = DDF_FIELD_TERMINATOR;
1561
0
}
1562
1563
/************************************************************************/
1564
/*                     CreateDefaultFieldInstance()                     */
1565
/************************************************************************/
1566
1567
/**
1568
 * Initialize default instance.
1569
 *
1570
 * This method is normally only used internally by the AddField() method
1571
 * to initialize the new field instance with default subfield values.  It
1572
 * installs default data for one instance of the field in the record
1573
 * using the DDFFieldDefn::GetDefaultValue() method and
1574
 * DDFRecord::SetFieldRaw().
1575
 *
1576
 * @param poField the field within the record to be assign a default
1577
 * instance.
1578
 * @param iIndexWithinField the instance to set (may not have been tested with
1579
 * values other than 0).
1580
 *
1581
 * @return TRUE on success or FALSE on failure.
1582
 */
1583
1584
int DDFRecord::CreateDefaultFieldInstance(DDFField *poField,
1585
                                          int iIndexWithinField)
1586
1587
0
{
1588
0
    int nRawSize = 0;
1589
0
    char *pachRawData = poField->GetFieldDefn()->GetDefaultValue(&nRawSize);
1590
0
    if (pachRawData == nullptr)
1591
0
        return FALSE;
1592
1593
0
    const int nSuccess =
1594
0
        SetFieldRaw(poField, iIndexWithinField, pachRawData, nRawSize);
1595
1596
0
    CPLFree(pachRawData);
1597
1598
0
    return nSuccess;
1599
0
}
1600
1601
/************************************************************************/
1602
/*                         SetStringSubfield()                          */
1603
/************************************************************************/
1604
1605
/**
1606
 * Set a string subfield in record.
1607
 *
1608
 * The value of a given subfield is replaced with a new string value
1609
 * formatted appropriately.
1610
 *
1611
 * @param pszField the field name to operate on.
1612
 * @param iFieldIndex the field index to operate on (zero based).
1613
 * @param pszSubfield the subfield name to operate on.
1614
 * @param iSubfieldIndex the subfield index to operate on (zero based).
1615
 * @param pszValue the new string to place in the subfield.  This may be
1616
 * arbitrary binary bytes if nValueLength is specified.
1617
 * @param nValueLength the number of valid bytes in pszValue, may be -1 to
1618
 * internally fetch with strlen().
1619
 *
1620
 * @return TRUE if successful, and FALSE if not.
1621
 */
1622
1623
int DDFRecord::SetStringSubfield(const char *pszField, int iFieldIndex,
1624
                                 const char *pszSubfield, int iSubfieldIndex,
1625
                                 const char *pszValue, int nValueLength)
1626
1627
0
{
1628
    /* -------------------------------------------------------------------- */
1629
    /*      Fetch the field. If this fails, return zero.                    */
1630
    /* -------------------------------------------------------------------- */
1631
0
    DDFField *poField = FindField(pszField, iFieldIndex);
1632
0
    if (poField == nullptr)
1633
0
        return FALSE;
1634
1635
    /* -------------------------------------------------------------------- */
1636
    /*      Get the subfield definition                                     */
1637
    /* -------------------------------------------------------------------- */
1638
0
    const DDFSubfieldDefn *poSFDefn =
1639
0
        poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
1640
0
    if (poSFDefn == nullptr)
1641
0
        return FALSE;
1642
1643
    /* -------------------------------------------------------------------- */
1644
    /*      How long will the formatted value be?                           */
1645
    /* -------------------------------------------------------------------- */
1646
0
    int nFormattedLen;
1647
1648
0
    if (!poSFDefn->FormatStringValue(nullptr, 0, &nFormattedLen, pszValue,
1649
0
                                     nValueLength))
1650
0
        return FALSE;
1651
1652
    /* -------------------------------------------------------------------- */
1653
    /*      Get a pointer to the data.                                      */
1654
    /* -------------------------------------------------------------------- */
1655
0
    int nMaxBytes;
1656
0
    char *pachSubfieldData =
1657
0
        (char *)poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex);
1658
0
    if (pachSubfieldData == nullptr)
1659
0
        return FALSE;
1660
1661
    /* -------------------------------------------------------------------- */
1662
    /*      Add new instance if we have run out of data.                    */
1663
    /* -------------------------------------------------------------------- */
1664
0
    if (nMaxBytes == 0 ||
1665
0
        (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR))
1666
0
    {
1667
0
        CreateDefaultFieldInstance(poField, iSubfieldIndex);
1668
1669
        // Refetch.
1670
0
        pachSubfieldData = (char *)poField->GetSubfieldData(
1671
0
            poSFDefn, &nMaxBytes, iSubfieldIndex);
1672
0
        if (pachSubfieldData == nullptr)
1673
0
            return FALSE;
1674
0
    }
1675
1676
    /* -------------------------------------------------------------------- */
1677
    /*      If the new length matches the existing length, just overlay     */
1678
    /*      and return.                                                     */
1679
    /* -------------------------------------------------------------------- */
1680
0
    int nExistingLength;
1681
1682
0
    poSFDefn->GetDataLength(pachSubfieldData, nMaxBytes, &nExistingLength);
1683
1684
0
    if (nExistingLength == nFormattedLen)
1685
0
    {
1686
0
        return poSFDefn->FormatStringValue(pachSubfieldData, nFormattedLen,
1687
0
                                           nullptr, pszValue, nValueLength);
1688
0
    }
1689
1690
    /* -------------------------------------------------------------------- */
1691
    /*      We will need to resize the raw data.                            */
1692
    /* -------------------------------------------------------------------- */
1693
0
    int nInstanceSize = 0;
1694
1695
0
    const char *pachFieldInstData =
1696
0
        poField->GetInstanceData(iFieldIndex, &nInstanceSize);
1697
1698
0
    const int nStartOffset =
1699
0
        static_cast<int>(pachSubfieldData - pachFieldInstData);
1700
1701
0
    char *pachNewData = (char *)CPLMalloc(nFormattedLen);
1702
0
    poSFDefn->FormatStringValue(pachNewData, nFormattedLen, nullptr, pszValue,
1703
0
                                nValueLength);
1704
1705
0
    const int nSuccess =
1706
0
        UpdateFieldRaw(poField, iFieldIndex, nStartOffset, nExistingLength,
1707
0
                       pachNewData, nFormattedLen);
1708
1709
0
    CPLFree(pachNewData);
1710
1711
0
    return nSuccess;
1712
0
}
1713
1714
/************************************************************************/
1715
/*                           SetIntSubfield()                           */
1716
/************************************************************************/
1717
1718
/**
1719
 * Set an integer subfield in record.
1720
 *
1721
 * The value of a given subfield is replaced with a new integer value
1722
 * formatted appropriately.
1723
 *
1724
 * @param pszField the field name to operate on.
1725
 * @param iFieldIndex the field index to operate on (zero based).
1726
 * @param pszSubfield the subfield name to operate on.
1727
 * @param iSubfieldIndex the subfield index to operate on (zero based).
1728
 * @param nNewValue the new value to place in the subfield.
1729
 *
1730
 * @return TRUE if successful, and FALSE if not.
1731
 */
1732
1733
int DDFRecord::SetIntSubfield(const char *pszField, int iFieldIndex,
1734
                              const char *pszSubfield, int iSubfieldIndex,
1735
                              int nNewValue)
1736
1737
0
{
1738
    /* -------------------------------------------------------------------- */
1739
    /*      Fetch the field. If this fails, return zero.                    */
1740
    /* -------------------------------------------------------------------- */
1741
0
    DDFField *poField = FindField(pszField, iFieldIndex);
1742
0
    if (poField == nullptr)
1743
0
        return FALSE;
1744
1745
    /* -------------------------------------------------------------------- */
1746
    /*      Get the subfield definition                                     */
1747
    /* -------------------------------------------------------------------- */
1748
0
    const DDFSubfieldDefn *poSFDefn =
1749
0
        poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
1750
0
    if (poSFDefn == nullptr)
1751
0
        return FALSE;
1752
1753
    /* -------------------------------------------------------------------- */
1754
    /*      How long will the formatted value be?                           */
1755
    /* -------------------------------------------------------------------- */
1756
0
    int nFormattedLen;
1757
1758
0
    if (!poSFDefn->FormatIntValue(nullptr, 0, &nFormattedLen, nNewValue))
1759
0
        return FALSE;
1760
1761
    /* -------------------------------------------------------------------- */
1762
    /*      Get a pointer to the data.                                      */
1763
    /* -------------------------------------------------------------------- */
1764
0
    int nMaxBytes;
1765
0
    char *pachSubfieldData =
1766
0
        (char *)poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex);
1767
0
    if (pachSubfieldData == nullptr)
1768
0
        return FALSE;
1769
1770
    /* -------------------------------------------------------------------- */
1771
    /*      Add new instance if we have run out of data.                    */
1772
    /* -------------------------------------------------------------------- */
1773
0
    if (nMaxBytes == 0 ||
1774
0
        (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR))
1775
0
    {
1776
0
        CreateDefaultFieldInstance(poField, iSubfieldIndex);
1777
1778
        // Refetch.
1779
0
        pachSubfieldData = (char *)poField->GetSubfieldData(
1780
0
            poSFDefn, &nMaxBytes, iSubfieldIndex);
1781
0
        if (pachSubfieldData == nullptr)
1782
0
            return FALSE;
1783
0
    }
1784
1785
    /* -------------------------------------------------------------------- */
1786
    /*      If the new length matches the existing length, just overlay     */
1787
    /*      and return.                                                     */
1788
    /* -------------------------------------------------------------------- */
1789
0
    int nExistingLength;
1790
1791
0
    poSFDefn->GetDataLength(pachSubfieldData, nMaxBytes, &nExistingLength);
1792
1793
0
    if (nExistingLength == nFormattedLen)
1794
0
    {
1795
0
        return poSFDefn->FormatIntValue(pachSubfieldData, nFormattedLen,
1796
0
                                        nullptr, nNewValue);
1797
0
    }
1798
1799
    /* -------------------------------------------------------------------- */
1800
    /*      We will need to resize the raw data.                            */
1801
    /* -------------------------------------------------------------------- */
1802
0
    int nInstanceSize = 0;
1803
1804
0
    const char *pachFieldInstData =
1805
0
        poField->GetInstanceData(iFieldIndex, &nInstanceSize);
1806
1807
0
    const int nStartOffset =
1808
0
        static_cast<int>(pachSubfieldData - pachFieldInstData);
1809
1810
0
    char *pachNewData = (char *)CPLMalloc(nFormattedLen);
1811
0
    poSFDefn->FormatIntValue(pachNewData, nFormattedLen, nullptr, nNewValue);
1812
1813
0
    const int nSuccess =
1814
0
        UpdateFieldRaw(poField, iFieldIndex, nStartOffset, nExistingLength,
1815
0
                       pachNewData, nFormattedLen);
1816
1817
0
    CPLFree(pachNewData);
1818
1819
0
    return nSuccess;
1820
0
}
1821
1822
/************************************************************************/
1823
/*                          SetFloatSubfield()                          */
1824
/************************************************************************/
1825
1826
/**
1827
 * Set a float subfield in record.
1828
 *
1829
 * The value of a given subfield is replaced with a new float value
1830
 * formatted appropriately.
1831
 *
1832
 * @param pszField the field name to operate on.
1833
 * @param iFieldIndex the field index to operate on (zero based).
1834
 * @param pszSubfield the subfield name to operate on.
1835
 * @param iSubfieldIndex the subfield index to operate on (zero based).
1836
 * @param dfNewValue the new value to place in the subfield.
1837
 *
1838
 * @return TRUE if successful, and FALSE if not.
1839
 */
1840
1841
int DDFRecord::SetFloatSubfield(const char *pszField, int iFieldIndex,
1842
                                const char *pszSubfield, int iSubfieldIndex,
1843
                                double dfNewValue)
1844
1845
0
{
1846
    /* -------------------------------------------------------------------- */
1847
    /*      Fetch the field. If this fails, return zero.                    */
1848
    /* -------------------------------------------------------------------- */
1849
0
    DDFField *poField = FindField(pszField, iFieldIndex);
1850
0
    if (poField == nullptr)
1851
0
        return FALSE;
1852
1853
    /* -------------------------------------------------------------------- */
1854
    /*      Get the subfield definition                                     */
1855
    /* -------------------------------------------------------------------- */
1856
0
    const DDFSubfieldDefn *poSFDefn =
1857
0
        poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
1858
0
    if (poSFDefn == nullptr)
1859
0
        return FALSE;
1860
1861
    /* -------------------------------------------------------------------- */
1862
    /*      How long will the formatted value be?                           */
1863
    /* -------------------------------------------------------------------- */
1864
0
    int nFormattedLen;
1865
1866
0
    if (!poSFDefn->FormatFloatValue(nullptr, 0, &nFormattedLen, dfNewValue))
1867
0
        return FALSE;
1868
1869
    /* -------------------------------------------------------------------- */
1870
    /*      Get a pointer to the data.                                      */
1871
    /* -------------------------------------------------------------------- */
1872
0
    int nMaxBytes;
1873
0
    char *pachSubfieldData =
1874
0
        (char *)poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex);
1875
0
    if (pachSubfieldData == nullptr)
1876
0
        return FALSE;
1877
1878
    /* -------------------------------------------------------------------- */
1879
    /*      Add new instance if we have run out of data.                    */
1880
    /* -------------------------------------------------------------------- */
1881
0
    if (nMaxBytes == 0 ||
1882
0
        (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR))
1883
0
    {
1884
0
        CreateDefaultFieldInstance(poField, iSubfieldIndex);
1885
1886
        // Refetch.
1887
0
        pachSubfieldData = (char *)poField->GetSubfieldData(
1888
0
            poSFDefn, &nMaxBytes, iSubfieldIndex);
1889
0
        if (pachSubfieldData == nullptr)
1890
0
            return FALSE;
1891
0
    }
1892
1893
    /* -------------------------------------------------------------------- */
1894
    /*      If the new length matches the existing length, just overlay     */
1895
    /*      and return.                                                     */
1896
    /* -------------------------------------------------------------------- */
1897
0
    int nExistingLength;
1898
1899
0
    poSFDefn->GetDataLength(pachSubfieldData, nMaxBytes, &nExistingLength);
1900
1901
0
    if (nExistingLength == nFormattedLen)
1902
0
    {
1903
0
        return poSFDefn->FormatFloatValue(pachSubfieldData, nFormattedLen,
1904
0
                                          nullptr, dfNewValue);
1905
0
    }
1906
1907
    /* -------------------------------------------------------------------- */
1908
    /*      We will need to resize the raw data.                            */
1909
    /* -------------------------------------------------------------------- */
1910
0
    int nInstanceSize = 0;
1911
1912
0
    const char *pachFieldInstData =
1913
0
        poField->GetInstanceData(iFieldIndex, &nInstanceSize);
1914
1915
0
    const int nStartOffset = (int)(pachSubfieldData - pachFieldInstData);
1916
1917
0
    char *pachNewData = (char *)CPLMalloc(nFormattedLen);
1918
0
    poSFDefn->FormatFloatValue(pachNewData, nFormattedLen, nullptr, dfNewValue);
1919
1920
0
    const int nSuccess =
1921
0
        UpdateFieldRaw(poField, iFieldIndex, nStartOffset, nExistingLength,
1922
0
                       pachNewData, nFormattedLen);
1923
1924
0
    CPLFree(pachNewData);
1925
1926
0
    return nSuccess;
1927
0
}