LCOV - code coverage report
Current view: top level - frmts/iso8211 - ddfrecord.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 426 603 70.6 %
Date: 2026-03-26 23:25:44 Functions: 21 24 87.5 %

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

Generated by: LCOV version 1.14