LCOV - code coverage report
Current view: top level - frmts/iso8211 - ddfrecord.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 406 554 73.3 %
Date: 2026-04-19 18:43:50 Functions: 22 24 91.7 %

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

Generated by: LCOV version 1.14