LCOV - code coverage report
Current view: top level - frmts/iso8211 - ddfmodule.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 192 251 76.5 %
Date: 2025-09-10 17:48:50 Functions: 11 14 78.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ISO 8211 Access
       4             :  * Purpose:  Implements the DDFModule class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2011-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 <cstdio>
      18             : #include <cstring>
      19             : 
      20             : #include "cpl_conv.h"
      21             : #include "cpl_error.h"
      22             : #include "cpl_vsi.h"
      23             : 
      24             : /************************************************************************/
      25             : /*                             DDFModule()                              */
      26             : /************************************************************************/
      27             : 
      28             : /**
      29             :  * The constructor.
      30             :  */
      31             : 
      32         168 : DDFModule::DDFModule()
      33             :     : fpDDF(nullptr), bReadOnly(TRUE), nFirstRecordOffset(0),
      34             :       _interchangeLevel('\0'), _inlineCodeExtensionIndicator('\0'),
      35             :       _versionNumber('\0'), _appIndicator('\0'), _fieldControlLength(9),
      36             :       _recLength(0), _leaderIden('L'), _fieldAreaStart(0), _sizeFieldLength(0),
      37             :       _sizeFieldPos(0), _sizeFieldTag(0), nFieldDefnCount(0),
      38             :       papoFieldDefns(nullptr), poRecord(nullptr), nCloneCount(0),
      39         168 :       nMaxCloneCount(0), papoClones(nullptr)
      40             : {
      41         168 :     strcpy(_extendedCharSet, " ! ");
      42         168 : }
      43             : 
      44             : /************************************************************************/
      45             : /*                             ~DDFModule()                             */
      46             : /************************************************************************/
      47             : 
      48             : /**
      49             :  * The destructor.
      50             :  */
      51             : 
      52         336 : DDFModule::~DDFModule()
      53             : 
      54             : {
      55         168 :     Close();
      56         168 : }
      57             : 
      58             : /************************************************************************/
      59             : /*                               Close()                                */
      60             : /*                                                                      */
      61             : /*      Note that closing a file also destroys essentially all other    */
      62             : /*      module datastructures.                                          */
      63             : /************************************************************************/
      64             : 
      65             : /**
      66             :  * Close an ISO 8211 file.
      67             :  */
      68             : 
      69         186 : void DDFModule::Close()
      70             : 
      71             : {
      72             :     /* -------------------------------------------------------------------- */
      73             :     /*      Close the file.                                                 */
      74             :     /* -------------------------------------------------------------------- */
      75         186 :     if (fpDDF != nullptr)
      76             :     {
      77         112 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
      78         112 :         fpDDF = nullptr;
      79             :     }
      80             : 
      81             :     /* -------------------------------------------------------------------- */
      82             :     /*      Cleanup the working record.                                     */
      83             :     /* -------------------------------------------------------------------- */
      84         186 :     if (poRecord != nullptr)
      85             :     {
      86          92 :         delete poRecord;
      87          92 :         poRecord = nullptr;
      88             :     }
      89             : 
      90             :     /* -------------------------------------------------------------------- */
      91             :     /*      Cleanup the clones.                                             */
      92             :     /* -------------------------------------------------------------------- */
      93        1544 :     for (int i = 0; i < nCloneCount; i++)
      94             :     {
      95        1358 :         papoClones[i]->RemoveIsCloneFlag();
      96        1358 :         delete papoClones[i];
      97             :     }
      98         186 :     nCloneCount = 0;
      99         186 :     nMaxCloneCount = 0;
     100         186 :     CPLFree(papoClones);
     101         186 :     papoClones = nullptr;
     102             : 
     103             :     /* -------------------------------------------------------------------- */
     104             :     /*      Cleanup the field definitions.                                  */
     105             :     /* -------------------------------------------------------------------- */
     106        1724 :     for (int i = 0; i < nFieldDefnCount; i++)
     107        1538 :         delete papoFieldDefns[i];
     108         186 :     CPLFree(papoFieldDefns);
     109         186 :     papoFieldDefns = nullptr;
     110         186 :     nFieldDefnCount = 0;
     111         186 : }
     112             : 
     113             : /************************************************************************/
     114             : /*                                Open()                                */
     115             : /*                                                                      */
     116             : /*      Open an ISO 8211 file, and read the DDR record to build the     */
     117             : /*      field definitions.                                              */
     118             : /************************************************************************/
     119             : 
     120             : /**
     121             :  * Open a ISO 8211 (DDF) file for reading.
     122             :  *
     123             :  * If the open succeeds the data descriptive record (DDR) will have been
     124             :  * read, and all the field and subfield definitions will be available.
     125             :  *
     126             :  * @param pszFilename   The name of the file to open.
     127             :  * @param bFailQuietly If FALSE a CPL Error is issued for non-8211 files,
     128             :  * otherwise quietly return NULL.
     129             :  *
     130             :  * @return FALSE if the open fails or TRUE if it succeeds.  Errors messages
     131             :  * are issued internally with CPLError().
     132             :  */
     133             : 
     134         131 : int DDFModule::Open(const char *pszFilename, int bFailQuietly)
     135             : 
     136             : {
     137         131 :     constexpr int nLeaderSize = 24;
     138             : 
     139             :     /* -------------------------------------------------------------------- */
     140             :     /*      Close the existing file if there is one.                        */
     141             :     /* -------------------------------------------------------------------- */
     142         131 :     if (fpDDF != nullptr)
     143           0 :         Close();
     144             : 
     145             :     /* -------------------------------------------------------------------- */
     146             :     /*      Open the file.                                                  */
     147             :     /* -------------------------------------------------------------------- */
     148             :     VSIStatBufL sStat;
     149         131 :     if (VSIStatL(pszFilename, &sStat) == 0 && !VSI_ISDIR(sStat.st_mode))
     150          94 :         fpDDF = VSIFOpenL(pszFilename, "rb");
     151             : 
     152         131 :     if (fpDDF == nullptr)
     153             :     {
     154          37 :         if (!bFailQuietly)
     155           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     156             :                      "Unable to open DDF file `%s'.", pszFilename);
     157          37 :         return FALSE;
     158             :     }
     159             : 
     160             :     /* -------------------------------------------------------------------- */
     161             :     /*      Read the 24 byte leader.                                        */
     162             :     /* -------------------------------------------------------------------- */
     163             :     char achLeader[nLeaderSize];
     164             : 
     165          94 :     if ((int)VSIFReadL(achLeader, 1, nLeaderSize, fpDDF) != nLeaderSize)
     166             :     {
     167           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
     168           0 :         fpDDF = nullptr;
     169             : 
     170           0 :         if (!bFailQuietly)
     171           0 :             CPLError(CE_Failure, CPLE_FileIO,
     172             :                      "Leader is short on DDF file `%s'.", pszFilename);
     173             : 
     174           0 :         return FALSE;
     175             :     }
     176             : 
     177             :     /* -------------------------------------------------------------------- */
     178             :     /*      Verify that this appears to be a valid DDF file.                */
     179             :     /* -------------------------------------------------------------------- */
     180          94 :     int i, bValid = TRUE;
     181             : 
     182        2350 :     for (i = 0; i < nLeaderSize; i++)
     183             :     {
     184        2256 :         if (achLeader[i] < 32 || achLeader[i] > 126)
     185           0 :             bValid = FALSE;
     186             :     }
     187             : 
     188          94 :     if (achLeader[5] != '1' && achLeader[5] != '2' && achLeader[5] != '3')
     189           0 :         bValid = FALSE;
     190             : 
     191          94 :     if (achLeader[6] != 'L')
     192           0 :         bValid = FALSE;
     193          94 :     if (achLeader[8] != '1' && achLeader[8] != ' ')
     194           0 :         bValid = FALSE;
     195             : 
     196             :     /* -------------------------------------------------------------------- */
     197             :     /*      Extract information from leader.                                */
     198             :     /* -------------------------------------------------------------------- */
     199             : 
     200          94 :     if (bValid)
     201             :     {
     202          94 :         _recLength = DDFScanInt(achLeader + 0, 5);
     203          94 :         _interchangeLevel = achLeader[5];
     204          94 :         _leaderIden = achLeader[6];
     205          94 :         _inlineCodeExtensionIndicator = achLeader[7];
     206          94 :         _versionNumber = achLeader[8];
     207          94 :         _appIndicator = achLeader[9];
     208          94 :         _fieldControlLength = DDFScanInt(achLeader + 10, 2);
     209          94 :         _fieldAreaStart = DDFScanInt(achLeader + 12, 5);
     210          94 :         _extendedCharSet[0] = achLeader[17];
     211          94 :         _extendedCharSet[1] = achLeader[18];
     212          94 :         _extendedCharSet[2] = achLeader[19];
     213          94 :         _extendedCharSet[3] = '\0';
     214          94 :         _sizeFieldLength = DDFScanInt(achLeader + 20, 1);
     215          94 :         _sizeFieldPos = DDFScanInt(achLeader + 21, 1);
     216          94 :         _sizeFieldTag = DDFScanInt(achLeader + 23, 1);
     217             : 
     218          94 :         if (_recLength < nLeaderSize || _fieldControlLength <= 0 ||
     219          94 :             _fieldAreaStart < 24 || _sizeFieldLength <= 0 ||
     220          94 :             _sizeFieldPos <= 0 || _sizeFieldTag <= 0)
     221             :         {
     222           0 :             bValid = FALSE;
     223             :         }
     224             :     }
     225             : 
     226             :     /* -------------------------------------------------------------------- */
     227             :     /*      If the header is invalid, then clean up, report the error       */
     228             :     /*      and return.                                                     */
     229             :     /* -------------------------------------------------------------------- */
     230          94 :     if (!bValid)
     231             :     {
     232           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
     233           0 :         fpDDF = nullptr;
     234             : 
     235           0 :         if (!bFailQuietly)
     236           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     237             :                      "File `%s' does not appear to have\n"
     238             :                      "a valid ISO 8211 header.\n",
     239             :                      pszFilename);
     240           0 :         return FALSE;
     241             :     }
     242             : 
     243             :     /* -------------------------------------------------------------------- */
     244             :     /*      Read the whole record info memory.                              */
     245             :     /* -------------------------------------------------------------------- */
     246          94 :     char *pachRecord = (char *)CPLMalloc(_recLength);
     247          94 :     memcpy(pachRecord, achLeader, nLeaderSize);
     248             : 
     249          94 :     if ((int)VSIFReadL(pachRecord + nLeaderSize, 1, _recLength - nLeaderSize,
     250          94 :                        fpDDF) != _recLength - nLeaderSize)
     251             :     {
     252           0 :         if (!bFailQuietly)
     253           0 :             CPLError(CE_Failure, CPLE_FileIO,
     254             :                      "Header record is short on DDF file `%s'.", pszFilename);
     255             : 
     256           0 :         CPLFree(pachRecord);
     257           0 :         return FALSE;
     258             :     }
     259             : 
     260             :     /* -------------------------------------------------------------------- */
     261             :     /*      First make a pass counting the directory entries.               */
     262             :     /* -------------------------------------------------------------------- */
     263          94 :     int nFieldEntryWidth, nFDCount = 0;
     264             : 
     265          94 :     nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
     266             : 
     267        1252 :     for (i = nLeaderSize; i + nFieldEntryWidth <= _recLength;
     268        1158 :          i += nFieldEntryWidth)
     269             :     {
     270        1252 :         if (pachRecord[i] == DDF_FIELD_TERMINATOR)
     271          94 :             break;
     272             : 
     273        1158 :         nFDCount++;
     274             :     }
     275             : 
     276             :     /* -------------------------------------------------------------------- */
     277             :     /*      Allocate, and read field definitions.                           */
     278             :     /* -------------------------------------------------------------------- */
     279        1252 :     for (i = 0; i < nFDCount; i++)
     280             :     {
     281             :         char szTag[128];
     282        1158 :         int nEntryOffset = nLeaderSize + i * nFieldEntryWidth;
     283             :         int nFieldLength, nFieldPos;
     284             : 
     285        1158 :         strncpy(szTag, pachRecord + nEntryOffset, _sizeFieldTag);
     286        1158 :         szTag[_sizeFieldTag] = '\0';
     287             : 
     288        1158 :         nEntryOffset += _sizeFieldTag;
     289        1158 :         nFieldLength = DDFScanInt(pachRecord + nEntryOffset, _sizeFieldLength);
     290             : 
     291        1158 :         nEntryOffset += _sizeFieldLength;
     292        1158 :         nFieldPos = DDFScanInt(pachRecord + nEntryOffset, _sizeFieldPos);
     293             : 
     294        1158 :         if (nFieldPos < 0 || nFieldPos > INT_MAX - _fieldAreaStart ||
     295             :             nFieldLength <
     296        1158 :                 2 ||  // DDFFieldDefn::Initialize() assumes at least 2 bytes
     297        1158 :             _recLength - (_fieldAreaStart + nFieldPos) < nFieldLength)
     298             :         {
     299           0 :             if (!bFailQuietly)
     300           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     301             :                          "Header record invalid on DDF file `%s'.",
     302             :                          pszFilename);
     303             : 
     304           0 :             CPLFree(pachRecord);
     305           0 :             return FALSE;
     306             :         }
     307             : 
     308        1158 :         DDFFieldDefn *poFDefn = new DDFFieldDefn();
     309        2316 :         if (poFDefn->Initialize(this, szTag, nFieldLength,
     310        1158 :                                 pachRecord + _fieldAreaStart + nFieldPos))
     311        1158 :             AddField(poFDefn);
     312             :         else
     313           0 :             delete poFDefn;
     314             :     }
     315             : 
     316          94 :     CPLFree(pachRecord);
     317             : 
     318             :     /* -------------------------------------------------------------------- */
     319             :     /*      Record the current file offset, the beginning of the first      */
     320             :     /*      data record.                                                    */
     321             :     /* -------------------------------------------------------------------- */
     322          94 :     nFirstRecordOffset = (long)VSIFTellL(fpDDF);
     323             : 
     324          94 :     return TRUE;
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                             Initialize()                             */
     329             : /************************************************************************/
     330             : 
     331          19 : int DDFModule::Initialize(char chInterchangeLevel, char chLeaderIden,
     332             :                           char chCodeExtensionIndicator, char chVersionNumber,
     333             :                           char chAppIndicator, const char *pszExtendedCharSet,
     334             :                           int nSizeFieldLength, int nSizeFieldPos,
     335             :                           int nSizeFieldTag)
     336             : 
     337             : {
     338          19 :     _interchangeLevel = chInterchangeLevel;
     339          19 :     _leaderIden = chLeaderIden;
     340          19 :     _inlineCodeExtensionIndicator = chCodeExtensionIndicator;
     341          19 :     _versionNumber = chVersionNumber;
     342          19 :     _appIndicator = chAppIndicator;
     343          19 :     snprintf(_extendedCharSet, sizeof(_extendedCharSet), "%s",
     344             :              pszExtendedCharSet);
     345          19 :     _sizeFieldLength = nSizeFieldLength;
     346          19 :     _sizeFieldPos = nSizeFieldPos;
     347          19 :     _sizeFieldTag = nSizeFieldTag;
     348             : 
     349          19 :     return TRUE;
     350             : }
     351             : 
     352             : /************************************************************************/
     353             : /*                               Create()                               */
     354             : /************************************************************************/
     355             : 
     356          19 : int DDFModule::Create(const char *pszFilename)
     357             : 
     358             : {
     359          19 :     CPLAssert(fpDDF == nullptr);
     360             : 
     361             :     /* -------------------------------------------------------------------- */
     362             :     /*      Create the file on disk.                                        */
     363             :     /* -------------------------------------------------------------------- */
     364          19 :     fpDDF = VSIFOpenL(pszFilename, "wb+");
     365          19 :     if (fpDDF == nullptr)
     366             :     {
     367           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     368             :                  "Failed to create file %s, check path and permissions.",
     369             :                  pszFilename);
     370           1 :         return FALSE;
     371             :     }
     372             : 
     373          18 :     bReadOnly = FALSE;
     374             : 
     375             :     /* -------------------------------------------------------------------- */
     376             :     /*      Prepare all the field definition information.                   */
     377             :     /* -------------------------------------------------------------------- */
     378             :     int iField;
     379             : 
     380          18 :     _recLength =
     381             :         24 +
     382          18 :         nFieldDefnCount * (_sizeFieldLength + _sizeFieldPos + _sizeFieldTag) +
     383             :         1;
     384             : 
     385          18 :     _fieldAreaStart = _recLength;
     386             : 
     387         378 :     for (iField = 0; iField < nFieldDefnCount; iField++)
     388             :     {
     389             :         int nLength;
     390             : 
     391         360 :         papoFieldDefns[iField]->GenerateDDREntry(this, nullptr, &nLength);
     392         360 :         _recLength += nLength;
     393             :     }
     394             : 
     395             :     /* -------------------------------------------------------------------- */
     396             :     /*      Setup 24 byte leader.                                           */
     397             :     /* -------------------------------------------------------------------- */
     398             :     char achLeader[25];
     399             : 
     400          18 :     snprintf(achLeader + 0, sizeof(achLeader) - 0, "%05d", (int)_recLength);
     401          18 :     achLeader[5] = _interchangeLevel;
     402          18 :     achLeader[6] = _leaderIden;
     403          18 :     achLeader[7] = _inlineCodeExtensionIndicator;
     404          18 :     achLeader[8] = _versionNumber;
     405          18 :     achLeader[9] = _appIndicator;
     406          18 :     snprintf(achLeader + 10, sizeof(achLeader) - 10, "%02d",
     407          18 :              (int)_fieldControlLength);
     408          18 :     snprintf(achLeader + 12, sizeof(achLeader) - 12, "%05d",
     409          18 :              (int)_fieldAreaStart);
     410          18 :     memcpy(achLeader + 17, _extendedCharSet, 3);
     411          18 :     snprintf(achLeader + 20, sizeof(achLeader) - 20, "%1d",
     412          18 :              (int)_sizeFieldLength);
     413          18 :     snprintf(achLeader + 21, sizeof(achLeader) - 21, "%1d", (int)_sizeFieldPos);
     414          18 :     achLeader[22] = '0';
     415          18 :     snprintf(achLeader + 23, sizeof(achLeader) - 23, "%1d", (int)_sizeFieldTag);
     416          18 :     int bRet = VSIFWriteL(achLeader, 24, 1, fpDDF) > 0;
     417             : 
     418             :     /* -------------------------------------------------------------------- */
     419             :     /*      Write out directory entries.                                    */
     420             :     /* -------------------------------------------------------------------- */
     421          18 :     int nOffset = 0;
     422         378 :     for (iField = 0; iField < nFieldDefnCount; iField++)
     423             :     {
     424             :         char achDirEntry[255];
     425             :         char szFormat[32];
     426             :         int nLength;
     427             : 
     428         360 :         CPLAssert(_sizeFieldLength + _sizeFieldPos + _sizeFieldTag <
     429             :                   (int)sizeof(achDirEntry));
     430             : 
     431         360 :         papoFieldDefns[iField]->GenerateDDREntry(this, nullptr, &nLength);
     432             : 
     433         360 :         CPLAssert((int)strlen(papoFieldDefns[iField]->GetName()) ==
     434             :                   _sizeFieldTag);
     435         720 :         snprintf(achDirEntry, sizeof(achDirEntry), "%s",
     436         360 :                  papoFieldDefns[iField]->GetName());
     437         360 :         snprintf(szFormat, sizeof(szFormat), "%%0%dd", (int)_sizeFieldLength);
     438         360 :         snprintf(achDirEntry + _sizeFieldTag,
     439         360 :                  sizeof(achDirEntry) - _sizeFieldTag, szFormat, nLength);
     440         360 :         snprintf(szFormat, sizeof(szFormat), "%%0%dd", (int)_sizeFieldPos);
     441         360 :         snprintf(achDirEntry + _sizeFieldTag + _sizeFieldLength,
     442         360 :                  sizeof(achDirEntry) - _sizeFieldTag - _sizeFieldLength,
     443             :                  szFormat, nOffset);
     444         360 :         nOffset += nLength;
     445             : 
     446         720 :         bRet &= VSIFWriteL(achDirEntry,
     447         360 :                            _sizeFieldLength + _sizeFieldPos + _sizeFieldTag, 1,
     448         360 :                            fpDDF) > 0;
     449             :     }
     450             : 
     451          18 :     char chUT = DDF_FIELD_TERMINATOR;
     452          18 :     bRet &= VSIFWriteL(&chUT, 1, 1, fpDDF) > 0;
     453             : 
     454             :     /* -------------------------------------------------------------------- */
     455             :     /*      Write out the field descriptions themselves.                    */
     456             :     /* -------------------------------------------------------------------- */
     457         378 :     for (iField = 0; iField < nFieldDefnCount; iField++)
     458             :     {
     459         360 :         char *pachData = nullptr;
     460         360 :         int nLength = 0;
     461             : 
     462         360 :         papoFieldDefns[iField]->GenerateDDREntry(this, &pachData, &nLength);
     463         360 :         bRet &= VSIFWriteL(pachData, nLength, 1, fpDDF) > 0;
     464         360 :         CPLFree(pachData);
     465             :     }
     466             : 
     467          18 :     return bRet ? TRUE : FALSE;
     468             : }
     469             : 
     470             : /************************************************************************/
     471             : /*                                Dump()                                */
     472             : /************************************************************************/
     473             : 
     474             : /**
     475             :  * Write out module info to debugging file.
     476             :  *
     477             :  * A variety of information about the module is written to the debugging
     478             :  * file.  This includes all the field and subfield definitions read from
     479             :  * the header.
     480             :  *
     481             :  * @param fp The standard IO file handle to write to.  i.e. stderr.
     482             :  */
     483             : 
     484           0 : void DDFModule::Dump(FILE *fp)
     485             : 
     486             : {
     487           0 :     fprintf(fp, "DDFModule:\n");
     488           0 :     fprintf(fp, "    _recLength = %d\n", _recLength);
     489           0 :     fprintf(fp, "    _interchangeLevel = %c\n", _interchangeLevel);
     490           0 :     fprintf(fp, "    _leaderIden = %c\n", _leaderIden);
     491           0 :     fprintf(fp, "    _inlineCodeExtensionIndicator = %c\n",
     492           0 :             _inlineCodeExtensionIndicator);
     493           0 :     fprintf(fp, "    _versionNumber = %c\n", _versionNumber);
     494           0 :     fprintf(fp, "    _appIndicator = %c\n", _appIndicator);
     495           0 :     fprintf(fp, "    _extendedCharSet = `%s'\n", _extendedCharSet);
     496           0 :     fprintf(fp, "    _fieldControlLength = %d\n", _fieldControlLength);
     497           0 :     fprintf(fp, "    _fieldAreaStart = %d\n", _fieldAreaStart);
     498           0 :     fprintf(fp, "    _sizeFieldLength = %d\n", _sizeFieldLength);
     499           0 :     fprintf(fp, "    _sizeFieldPos = %d\n", _sizeFieldPos);
     500           0 :     fprintf(fp, "    _sizeFieldTag = %d\n", _sizeFieldTag);
     501             : 
     502           0 :     for (int i = 0; i < nFieldDefnCount; i++)
     503             :     {
     504           0 :         papoFieldDefns[i]->Dump(fp);
     505             :     }
     506           0 : }
     507             : 
     508             : /************************************************************************/
     509             : /*                           FindFieldDefn()                            */
     510             : /************************************************************************/
     511             : 
     512             : /**
     513             :  * Fetch the definition of the named field.
     514             :  *
     515             :  * This function will scan the DDFFieldDefn's on this module, to find
     516             :  * one with the indicated field name.
     517             :  *
     518             :  * @param pszFieldName The name of the field to search for.  The comparison is
     519             :  *                     case insensitive.
     520             :  *
     521             :  * @return A pointer to the request DDFFieldDefn object is returned, or NULL
     522             :  * if none matching the name are found.  The return object remains owned by
     523             :  * the DDFModule, and should not be deleted by application code.
     524             :  */
     525             : 
     526        6529 : const DDFFieldDefn *DDFModule::FindFieldDefn(const char *pszFieldName) const
     527             : 
     528             : {
     529             :     int i;
     530             : 
     531             :     /* -------------------------------------------------------------------- */
     532             :     /*      This pass tries to reduce the cost of comparing strings by      */
     533             :     /*      first checking the first character, and by using strcmp()       */
     534             :     /* -------------------------------------------------------------------- */
     535       54259 :     for (i = 0; i < nFieldDefnCount; i++)
     536             :     {
     537       54256 :         const char *pszThisName = papoFieldDefns[i]->GetName();
     538             : 
     539       54256 :         if (*pszThisName == *pszFieldName && *pszFieldName != '\0' &&
     540       13241 :             strcmp(pszFieldName + 1, pszThisName + 1) == 0)
     541        6526 :             return papoFieldDefns[i];
     542             :     }
     543             : 
     544             :     /* -------------------------------------------------------------------- */
     545             :     /*      Now do a more general check.  Application code may not          */
     546             :     /*      always use the correct name case.                               */
     547             :     /* -------------------------------------------------------------------- */
     548           9 :     for (i = 0; i < nFieldDefnCount; i++)
     549             :     {
     550           6 :         if (EQUAL(pszFieldName, papoFieldDefns[i]->GetName()))
     551           0 :             return papoFieldDefns[i];
     552             :     }
     553             : 
     554           3 :     return nullptr;
     555             : }
     556             : 
     557             : /************************************************************************/
     558             : /*                             ReadRecord()                             */
     559             : /*                                                                      */
     560             : /*      Read one record from the file, and return to the                */
     561             : /*      application.  The returned record is owned by the module,       */
     562             : /*      and is reused from call to call in order to preserve headers    */
     563             : /*      when they aren't being re-read from record to record.           */
     564             : /************************************************************************/
     565             : 
     566             : /**
     567             :  * Read one record from the file.
     568             :  *
     569             :  * @return A pointer to a DDFRecord object is returned, or NULL if a read
     570             :  * error, or end of file occurs.  The returned record is owned by the
     571             :  * module, and should not be deleted by the application.  The record is
     572             :  * only valid until the next ReadRecord() at which point it is overwritten.
     573             :  */
     574             : 
     575        1634 : DDFRecord *DDFModule::ReadRecord()
     576             : 
     577             : {
     578        1634 :     if (poRecord == nullptr)
     579          92 :         poRecord = new DDFRecord(this);
     580             : 
     581        1634 :     if (poRecord->Read())
     582        1562 :         return poRecord;
     583             :     else
     584          72 :         return nullptr;
     585             : }
     586             : 
     587             : /************************************************************************/
     588             : /*                              AddField()                              */
     589             : /************************************************************************/
     590             : 
     591             : /**
     592             :  * Add new field definition.
     593             :  *
     594             :  * Field definitions may only be added to DDFModules being used for
     595             :  * writing, not those being used for reading.  Ownership of the
     596             :  * DDFFieldDefn object is taken by the DDFModule.
     597             :  *
     598             :  * @param poNewFDefn definition to be added to the module.
     599             :  */
     600             : 
     601        1538 : void DDFModule::AddField(DDFFieldDefn *poNewFDefn)
     602             : 
     603             : {
     604        1538 :     nFieldDefnCount++;
     605        3076 :     papoFieldDefns = (DDFFieldDefn **)CPLRealloc(
     606        1538 :         papoFieldDefns, sizeof(void *) * nFieldDefnCount);
     607        1538 :     papoFieldDefns[nFieldDefnCount - 1] = poNewFDefn;
     608        1538 : }
     609             : 
     610             : /************************************************************************/
     611             : /*                              GetField()                              */
     612             : /************************************************************************/
     613             : 
     614             : /**
     615             :  * Fetch a field definition by index.
     616             :  *
     617             :  * @param i (from 0 to GetFieldCount() - 1.
     618             :  * @return the returned field pointer or NULL if the index is out of range.
     619             :  */
     620             : 
     621           0 : DDFFieldDefn *DDFModule::GetField(int i)
     622             : 
     623             : {
     624           0 :     if (i < 0 || i >= nFieldDefnCount)
     625           0 :         return nullptr;
     626             :     else
     627           0 :         return papoFieldDefns[i];
     628             : }
     629             : 
     630             : /************************************************************************/
     631             : /*                           AddCloneRecord()                           */
     632             : /*                                                                      */
     633             : /*      We want to keep track of cloned records, so we can clean        */
     634             : /*      them up when the module is destroyed.                           */
     635             : /************************************************************************/
     636             : 
     637        1428 : void DDFModule::AddCloneRecord(DDFRecord *poRecordIn)
     638             : 
     639             : {
     640             :     /* -------------------------------------------------------------------- */
     641             :     /*      Do we need to grow the container array?                         */
     642             :     /* -------------------------------------------------------------------- */
     643        1428 :     if (nCloneCount == nMaxCloneCount)
     644             :     {
     645          71 :         nMaxCloneCount = nCloneCount * 2 + 20;
     646          71 :         papoClones = (DDFRecord **)CPLRealloc(papoClones,
     647          71 :                                               nMaxCloneCount * sizeof(void *));
     648             :     }
     649             : 
     650             :     /* -------------------------------------------------------------------- */
     651             :     /*      Add to the list.                                                */
     652             :     /* -------------------------------------------------------------------- */
     653        1428 :     papoClones[nCloneCount++] = poRecordIn;
     654        1428 : }
     655             : 
     656             : /************************************************************************/
     657             : /*                         RemoveCloneRecord()                          */
     658             : /************************************************************************/
     659             : 
     660          70 : void DDFModule::RemoveCloneRecord(DDFRecord *poRecordIn)
     661             : 
     662             : {
     663             :     int i;
     664             : 
     665          87 :     for (i = 0; i < nCloneCount; i++)
     666             :     {
     667          87 :         if (papoClones[i] == poRecordIn)
     668             :         {
     669          70 :             papoClones[i] = papoClones[nCloneCount - 1];
     670          70 :             nCloneCount--;
     671          70 :             return;
     672             :         }
     673             :     }
     674             : 
     675           0 :     CPLAssert(false);
     676             : }
     677             : 
     678             : /************************************************************************/
     679             : /*                               Rewind()                               */
     680             : /************************************************************************/
     681             : 
     682             : /**
     683             :  * Return to first record.
     684             :  *
     685             :  * The next call to ReadRecord() will read the first data record in the file.
     686             :  *
     687             :  * @param nOffset the offset in the file to return to.  By default this is
     688             :  * -1, a special value indicating that reading should return to the first
     689             :  * data record.  Otherwise it is an absolute byte offset in the file.
     690             :  */
     691             : 
     692           0 : void DDFModule::Rewind(long nOffset)
     693             : 
     694             : {
     695           0 :     if (nOffset == -1)
     696           0 :         nOffset = nFirstRecordOffset;
     697             : 
     698           0 :     if (fpDDF == nullptr)
     699           0 :         return;
     700             : 
     701           0 :     if (VSIFSeekL(fpDDF, nOffset, SEEK_SET) < 0)
     702           0 :         return;
     703             : 
     704           0 :     if (nOffset == nFirstRecordOffset && poRecord != nullptr)
     705           0 :         poRecord->Clear();
     706             : }

Generated by: LCOV version 1.14