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

Generated by: LCOV version 1.14