LCOV - code coverage report
Current view: top level - frmts/iso8211 - ddfrecord.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 405 603 67.2 %
Date: 2025-09-10 17:48:50 Functions: 20 24 83.3 %

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

Generated by: LCOV version 1.14