LCOV - code coverage report
Current view: top level - frmts/iso8211 - ddffielddefn.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 231 332 69.6 %
Date: 2026-04-19 18:43:50 Functions: 11 13 84.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ISO 8211 Access
       4             :  * Purpose:  Implements the DDFFieldDefn class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "iso8211.h"
      15             : 
      16             : #include <cctype>
      17             : #include <cstddef>
      18             : #include <cstdio>
      19             : #include <cstdlib>
      20             : #include <cstring>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_error.h"
      24             : #include "cpl_string.h"
      25             : 
      26             : #define CPLE_DiscardedFormat 1301
      27             : 
      28             : /************************************************************************/
      29             : /*                            DDFFieldDefn()                            */
      30             : /************************************************************************/
      31             : 
      32             : DDFFieldDefn::DDFFieldDefn() = default;
      33             : 
      34             : /************************************************************************/
      35             : /*                           ~DDFFieldDefn()                            */
      36             : /************************************************************************/
      37             : 
      38             : DDFFieldDefn::~DDFFieldDefn() = default;
      39             : 
      40             : /************************************************************************/
      41             : /*                            AddSubfield()                             */
      42             : /************************************************************************/
      43             : 
      44        1691 : void DDFFieldDefn::AddSubfield(const char *pszName, const char *pszFormat)
      45             : 
      46             : {
      47        1691 :     auto poSFDefn = std::make_unique<DDFSubfieldDefn>();
      48             : 
      49        1691 :     poSFDefn->SetName(pszName);
      50        1691 :     poSFDefn->SetFormat(pszFormat);
      51        1691 :     AddSubfield(std::move(poSFDefn));
      52        1691 : }
      53             : 
      54             : /************************************************************************/
      55             : /*                            AddSubfield()                             */
      56             : /************************************************************************/
      57             : 
      58        8056 : void DDFFieldDefn::AddSubfield(std::unique_ptr<DDFSubfieldDefn> poNewSFDefn,
      59             :                                bool bDontAddToFormat)
      60             : 
      61             : {
      62        8056 :     if (bDontAddToFormat)
      63             :     {
      64        6365 :         apoSubfields.push_back(std::move(poNewSFDefn));
      65        6365 :         return;
      66             :     }
      67             : 
      68             :     /* -------------------------------------------------------------------- */
      69             :     /*      Add this format to the format list.  We don't bother            */
      70             :     /*      aggregating formats here.                                       */
      71             :     /* -------------------------------------------------------------------- */
      72        1691 :     if (_formatControls.empty())
      73             :     {
      74         342 :         _formatControls = "()";
      75             :     }
      76             : 
      77        3382 :     std::string osNewFormatControls = _formatControls;
      78        1691 :     osNewFormatControls.pop_back();
      79        1691 :     if (!osNewFormatControls.empty() && osNewFormatControls.back() != '(')
      80        1349 :         osNewFormatControls += ',';
      81        1691 :     osNewFormatControls += poNewSFDefn->GetFormat();
      82        1691 :     osNewFormatControls += ')';
      83             : 
      84        1691 :     _formatControls = std::move(osNewFormatControls);
      85             : 
      86             :     /* -------------------------------------------------------------------- */
      87             :     /*      Add the subfield name to the list.                              */
      88             :     /* -------------------------------------------------------------------- */
      89        3192 :     if (!_arrayDescr.empty() &&
      90        1501 :         (_arrayDescr[0] != '*' || _arrayDescr.size() > 1))
      91        1349 :         _arrayDescr += '!';
      92        1691 :     _arrayDescr += poNewSFDefn->GetName();
      93             : 
      94        1691 :     apoSubfields.push_back(std::move(poNewSFDefn));
      95             : }
      96             : 
      97             : /************************************************************************/
      98             : /*                               Create()                               */
      99             : /*                                                                      */
     100             : /*      Initialize a new field defn from application input, instead     */
     101             : /*      of from an existing file.                                       */
     102             : /************************************************************************/
     103             : 
     104         400 : int DDFFieldDefn::Create(const char *pszTagIn, const char *pszFieldName,
     105             :                          const char *pszDescription,
     106             :                          DDF_data_struct_code eDataStructCode,
     107             :                          DDF_data_type_code eDataTypeCode,
     108             :                          const char *pszFormat)
     109             : 
     110             : {
     111         400 :     CPLAssert(osTag.empty());
     112         400 :     poModule = nullptr;
     113         400 :     osTag = pszTagIn;
     114         400 :     _fieldName = pszFieldName;
     115         400 :     _arrayDescr = pszDescription ? pszDescription : "";
     116             : 
     117         400 :     _data_struct_code = eDataStructCode;
     118         400 :     _data_type_code = eDataTypeCode;
     119             : 
     120         400 :     _formatControls = pszFormat ? pszFormat : "";
     121             : 
     122         400 :     bRepeatingSubfields = (pszDescription != nullptr && *pszDescription == '*');
     123             : 
     124         400 :     if (!_formatControls.empty() && _data_struct_code != dsc_elementary)
     125             :     {
     126          18 :         BuildSubfields();
     127             : 
     128          18 :         if (!ApplyFormats())
     129           0 :             return FALSE;
     130             :     }
     131             : 
     132         400 :     return TRUE;
     133             : }
     134             : 
     135             : /************************************************************************/
     136             : /*                         SetFormatControls()                          */
     137             : /************************************************************************/
     138             : 
     139           0 : void DDFFieldDefn::SetFormatControls(const char *pszVal)
     140             : {
     141           0 :     _formatControls = pszVal ? pszVal : "";
     142           0 : }
     143             : 
     144             : /************************************************************************/
     145             : /*                          GenerateDDREntry()                          */
     146             : /************************************************************************/
     147             : 
     148        1140 : int DDFFieldDefn::GenerateDDREntry(DDFModule *poModuleIn, char **ppachData,
     149             :                                    int *pnLength)
     150             : 
     151             : {
     152        1140 :     const int iFDOffset = poModuleIn->GetFieldControlLength();
     153        1140 :     CPLAssert(iFDOffset >= 6 && iFDOffset <= 9);
     154        1140 :     *pnLength = static_cast<int>(iFDOffset + _fieldName.size() + 1 +
     155        1140 :                                  _arrayDescr.size() + 1);
     156        1140 :     if (!_formatControls.empty())
     157             :     {
     158        1083 :         *pnLength += static_cast<int>(_formatControls.size() + 1);
     159             :     }
     160             : 
     161        1140 :     if (ppachData == nullptr)
     162         760 :         return TRUE;
     163             : 
     164         380 :     *ppachData = static_cast<char *>(CPLMalloc(*pnLength + 1));
     165         380 :     (*ppachData)[*pnLength] = 0;
     166             : 
     167         380 :     if (_data_struct_code == dsc_elementary)
     168          38 :         (*ppachData)[0] = '0';
     169         342 :     else if (_data_struct_code == dsc_vector)
     170         190 :         (*ppachData)[0] = '1';
     171         152 :     else if (_data_struct_code == dsc_array)
     172         152 :         (*ppachData)[0] = '2';
     173           0 :     else if (_data_struct_code == dsc_concatenated)
     174           0 :         (*ppachData)[0] = '3';
     175             : 
     176         380 :     if (_data_type_code == dtc_char_string)
     177          19 :         (*ppachData)[1] = '0';
     178         361 :     else if (_data_type_code == dtc_implicit_point)
     179           0 :         (*ppachData)[1] = '1';
     180         361 :     else if (_data_type_code == dtc_explicit_point)
     181           0 :         (*ppachData)[1] = '2';
     182         361 :     else if (_data_type_code == dtc_explicit_point_scaled)
     183           0 :         (*ppachData)[1] = '3';
     184         361 :     else if (_data_type_code == dtc_char_bit_string)
     185           0 :         (*ppachData)[1] = '4';
     186         361 :     else if (_data_type_code == dtc_bit_string)
     187          57 :         (*ppachData)[1] = '5';
     188         304 :     else if (_data_type_code == dtc_mixed_data_type)
     189         304 :         (*ppachData)[1] = '6';
     190             : 
     191         380 :     (*ppachData)[2] = '0';
     192         380 :     (*ppachData)[3] = '0';
     193         380 :     (*ppachData)[4] = ';';
     194         380 :     (*ppachData)[5] = '&';
     195         380 :     if (iFDOffset > 6)
     196         380 :         (*ppachData)[6] = ' ';
     197         380 :     if (iFDOffset > 7)
     198         380 :         (*ppachData)[7] = ' ';
     199         380 :     if (iFDOffset > 8)
     200         380 :         (*ppachData)[8] = ' ';
     201         380 :     snprintf(*ppachData + iFDOffset, *pnLength + 1 - iFDOffset, "%s",
     202             :              _fieldName.c_str());
     203         380 :     snprintf(*ppachData + strlen(*ppachData),
     204         380 :              *pnLength + 1 - strlen(*ppachData), "%c%s", DDF_UNIT_TERMINATOR,
     205             :              _arrayDescr.c_str());
     206         380 :     if (!_formatControls.empty())
     207             :     {
     208             :         // empty for '0000' of S-57 & S-111
     209         361 :         snprintf(*ppachData + strlen(*ppachData),
     210         361 :                  *pnLength + 1 - strlen(*ppachData), "%c%s",
     211             :                  DDF_UNIT_TERMINATOR, _formatControls.c_str());
     212             :     }
     213         380 :     snprintf(*ppachData + strlen(*ppachData),
     214         380 :              *pnLength + 1 - strlen(*ppachData), "%c", DDF_FIELD_TERMINATOR);
     215             : 
     216         380 :     return TRUE;
     217             : }
     218             : 
     219             : /************************************************************************/
     220             : /*                             Initialize()                             */
     221             : /*                                                                      */
     222             : /*      Initialize the field definition from the information in the     */
     223             : /*      DDR record.  This is called by DDFModule::Open().               */
     224             : /************************************************************************/
     225             : 
     226        1354 : int DDFFieldDefn::Initialize(DDFModule *poModuleIn, const char *pszTagIn,
     227             :                              int nFieldEntrySize, const char *pachFieldArea)
     228             : 
     229             : {
     230        1354 :     int iFDOffset = poModuleIn->GetFieldControlLength();
     231             : 
     232        1354 :     poModule = poModuleIn;
     233             : 
     234        1354 :     osTag = pszTagIn;
     235             : 
     236             :     /* -------------------------------------------------------------------- */
     237             :     /*      Set the data struct and type codes.                             */
     238             :     /* -------------------------------------------------------------------- */
     239        1354 :     switch (pachFieldArea[0])
     240             :     {
     241         148 :         case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */
     242             :         case '0':
     243         148 :             _data_struct_code = dsc_elementary;
     244         148 :             break;
     245             : 
     246         762 :         case '1':
     247         762 :             _data_struct_code = dsc_vector;
     248         762 :             break;
     249             : 
     250         444 :         case '2':
     251         444 :             _data_struct_code = dsc_array;
     252         444 :             break;
     253             : 
     254           0 :         case '3':
     255           0 :             _data_struct_code = dsc_concatenated;
     256           0 :             break;
     257             : 
     258           0 :         default:
     259           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     260             :                      "Unrecognized data_struct_code value %c.\n"
     261             :                      "Field %s initialization incorrect.",
     262           0 :                      pachFieldArea[0], osTag.c_str());
     263           0 :             _data_struct_code = dsc_elementary;
     264             :     }
     265             : 
     266        1354 :     switch (pachFieldArea[1])
     267             :     {
     268         209 :         case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */
     269             :         case '0':
     270         209 :             _data_type_code = dtc_char_string;
     271         209 :             break;
     272             : 
     273          64 :         case '1':
     274          64 :             _data_type_code = dtc_implicit_point;
     275          64 :             break;
     276             : 
     277          16 :         case '2':
     278          16 :             _data_type_code = dtc_explicit_point;
     279          16 :             break;
     280             : 
     281           0 :         case '3':
     282           0 :             _data_type_code = dtc_explicit_point_scaled;
     283           0 :             break;
     284             : 
     285           0 :         case '4':
     286           0 :             _data_type_code = dtc_char_bit_string;
     287           0 :             break;
     288             : 
     289         128 :         case '5':
     290         128 :             _data_type_code = dtc_bit_string;
     291         128 :             break;
     292             : 
     293         937 :         case '6':
     294         937 :             _data_type_code = dtc_mixed_data_type;
     295         937 :             break;
     296             : 
     297           0 :         default:
     298           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     299             :                      "Unrecognized data_type_code value %c.\n"
     300             :                      "Field %s initialization incorrect.",
     301           0 :                      pachFieldArea[1], osTag.c_str());
     302           0 :             _data_type_code = dtc_char_string;
     303             :     }
     304             : 
     305             :     /* -------------------------------------------------------------------- */
     306             :     /*      Capture the field name, description (sub field names), and      */
     307             :     /*      format statements.                                              */
     308             :     /* -------------------------------------------------------------------- */
     309             : 
     310        1354 :     int nCharsConsumed = 0;
     311        1354 :     _fieldName = DDFFetchVariable(
     312        1354 :         pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset,
     313        1354 :         DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed);
     314        1354 :     iFDOffset += nCharsConsumed;
     315             : 
     316        1354 :     _arrayDescr = DDFFetchVariable(
     317        1354 :         pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset,
     318        1354 :         DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed);
     319        1354 :     iFDOffset += nCharsConsumed;
     320             : 
     321        1354 :     _formatControls = DDFFetchVariable(
     322        1354 :         pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset,
     323        1354 :         DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed);
     324             : 
     325             :     /* -------------------------------------------------------------------- */
     326             :     /*      Parse the subfield info.                                        */
     327             :     /* -------------------------------------------------------------------- */
     328        1354 :     if (_data_struct_code != dsc_elementary)
     329             :     {
     330        1206 :         BuildSubfields();
     331             : 
     332        1206 :         if (!ApplyFormats())
     333           0 :             return FALSE;
     334             :     }
     335             : 
     336        1354 :     return TRUE;
     337             : }
     338             : 
     339             : /************************************************************************/
     340             : /*                                Dump()                                */
     341             : /************************************************************************/
     342             : 
     343             : /**
     344             :  * Write out field definition info to debugging file.
     345             :  *
     346             :  * A variety of information about this field definition, and all its
     347             :  * subfields is written to the give debugging file handle.
     348             :  *
     349             :  * @param fp The standard IO file handle to write to.  i.e. stderr
     350             :  */
     351             : 
     352           0 : void DDFFieldDefn::Dump(FILE *fp) const
     353             : 
     354             : {
     355           0 :     const char *pszValue = "";
     356           0 :     CPL_IGNORE_RET_VAL(pszValue);  // Make CSA happy
     357             : 
     358           0 :     fprintf(fp, "  DDFFieldDefn:\n");
     359           0 :     fprintf(fp, "      Tag = `%s'\n", osTag.c_str());
     360           0 :     fprintf(fp, "      _fieldName = `%s'\n", _fieldName.c_str());
     361           0 :     fprintf(fp, "      _arrayDescr = `%s'\n", _arrayDescr.c_str());
     362           0 :     fprintf(fp, "      _formatControls = `%s'\n", _formatControls.c_str());
     363             : 
     364           0 :     switch (_data_struct_code)
     365             :     {
     366           0 :         case dsc_elementary:
     367           0 :             pszValue = "elementary";
     368           0 :             break;
     369             : 
     370           0 :         case dsc_vector:
     371           0 :             pszValue = "vector";
     372           0 :             break;
     373             : 
     374           0 :         case dsc_array:
     375           0 :             pszValue = "array";
     376           0 :             break;
     377             : 
     378           0 :         case dsc_concatenated:
     379           0 :             pszValue = "concatenated";
     380           0 :             break;
     381             :     }
     382             : 
     383           0 :     fprintf(fp, "      _data_struct_code = %s\n", pszValue);
     384             : 
     385           0 :     switch (_data_type_code)
     386             :     {
     387           0 :         case dtc_char_string:
     388           0 :             pszValue = "char_string";
     389           0 :             break;
     390             : 
     391           0 :         case dtc_implicit_point:
     392           0 :             pszValue = "implicit_point";
     393           0 :             break;
     394             : 
     395           0 :         case dtc_explicit_point:
     396           0 :             pszValue = "explicit_point";
     397           0 :             break;
     398             : 
     399           0 :         case dtc_explicit_point_scaled:
     400           0 :             pszValue = "explicit_point_scaled";
     401           0 :             break;
     402             : 
     403           0 :         case dtc_char_bit_string:
     404           0 :             pszValue = "char_bit_string";
     405           0 :             break;
     406             : 
     407           0 :         case dtc_bit_string:
     408           0 :             pszValue = "bit_string";
     409           0 :             break;
     410             : 
     411           0 :         case dtc_mixed_data_type:
     412           0 :             pszValue = "mixed_data_type";
     413           0 :             break;
     414             :     }
     415             : 
     416           0 :     fprintf(fp, "      _data_type_code = %s\n", pszValue);
     417             : 
     418           0 :     for (const auto &poSubfield : apoSubfields)
     419           0 :         poSubfield->Dump(fp);
     420           0 : }
     421             : 
     422             : /************************************************************************/
     423             : /*                           BuildSubfields()                           */
     424             : /*                                                                      */
     425             : /*      Based on the _arrayDescr build a set of subfields.              */
     426             : /************************************************************************/
     427             : 
     428        1224 : void DDFFieldDefn::BuildSubfields()
     429             : 
     430             : {
     431        1224 :     const char *pszSublist = _arrayDescr.c_str();
     432             : 
     433             :     /* -------------------------------------------------------------------- */
     434             :     /*      It is valid to define a field with _arrayDesc                   */
     435             :     /*      '*STPT!CTPT!ENPT*YCOO!XCOO' and formatControls '(2b24)'.        */
     436             :     /*      This basically indicates that there are 3 (YCOO,XCOO)           */
     437             :     /*      structures named STPT, CTPT and ENPT.  But we can't handle      */
     438             :     /*      such a case gracefully here, so we just ignore the              */
     439             :     /*      "structure names" and treat such a thing as a repeating         */
     440             :     /*      YCOO/XCOO array.  This occurs with the AR2D field of some       */
     441             :     /*      AML S-57 files for instance.                                    */
     442             :     /*                                                                      */
     443             :     /*      We accomplish this by ignoring everything before the last       */
     444             :     /*      '*' in the subfield list.                                       */
     445             :     /* -------------------------------------------------------------------- */
     446        1224 :     if (strrchr(pszSublist, '*') != nullptr)
     447         470 :         pszSublist = strrchr(pszSublist, '*');
     448             : 
     449             :     /* -------------------------------------------------------------------- */
     450             :     /*      Strip off the repeating marker, when it occurs, but mark our    */
     451             :     /*      field as repeating.                                             */
     452             :     /* -------------------------------------------------------------------- */
     453        1224 :     if (pszSublist[0] == '*')
     454             :     {
     455         470 :         bRepeatingSubfields = TRUE;
     456         470 :         pszSublist++;
     457             :     }
     458             : 
     459             :     /* -------------------------------------------------------------------- */
     460             :     /*      split list of fields .                                          */
     461             :     /* -------------------------------------------------------------------- */
     462             :     const CPLStringList aosSubfieldNames(
     463        2448 :         CSLTokenizeStringComplex(pszSublist, "!", FALSE, FALSE));
     464             : 
     465             :     /* -------------------------------------------------------------------- */
     466             :     /*      minimally initialize the subfields.  More will be done later.   */
     467             :     /* -------------------------------------------------------------------- */
     468        7589 :     for (const char *pszSubfieldName : cpl::Iterate(aosSubfieldNames))
     469             :     {
     470        6365 :         auto poSFDefn = std::make_unique<DDFSubfieldDefn>();
     471             : 
     472        6365 :         poSFDefn->SetName(pszSubfieldName);
     473        6365 :         AddSubfield(std::move(poSFDefn), true);
     474             :     }
     475        1224 : }
     476             : 
     477             : /************************************************************************/
     478             : /*                          ExtractSubstring()                          */
     479             : /*                                                                      */
     480             : /*      Extract a substring terminated by a comma (or end of            */
     481             : /*      string).  Commas in brackets are ignored as terminated with     */
     482             : /*      bracket nesting understood gracefully.  If the returned         */
     483             : /*      string would begin and end with a bracket then strip off the    */
     484             : /*      brackets.                                                       */
     485             : /*                                                                      */
     486             : /*      Given a string like "(A,3(B,C),D),X,Y)" return "A,3(B,C),D".    */
     487             : /*      Giveh a string like "3A,2C" return "3A".                        */
     488             : /*      Giveh a string like "(3A,2C" return an empty string             */
     489             : /*      Giveh a string like "3A),2C" return an empty string             */
     490             : /************************************************************************/
     491             : 
     492        1956 : std::string DDFFieldDefn::ExtractSubstring(const char *pszSrc)
     493             : 
     494             : {
     495        1956 :     int nBracket = 0;
     496        1956 :     int i = 0;  // Used after for.
     497       27535 :     for (; pszSrc[i] != '\0' && (nBracket > 0 || pszSrc[i] != ','); i++)
     498             :     {
     499       25579 :         if (pszSrc[i] == '(')
     500        3342 :             nBracket++;
     501       22237 :         else if (pszSrc[i] == ')')
     502             :         {
     503        3342 :             nBracket--;
     504        3342 :             if (nBracket < 0)
     505           0 :                 return std::string();
     506             :         }
     507             :     }
     508        1956 :     if (nBracket > 0)
     509           0 :         return std::string();
     510             : 
     511        1956 :     if (pszSrc[0] == '(')
     512             :     {
     513        1224 :         CPLAssert(i >= 2);
     514        1224 :         return std::string(pszSrc + 1, i - 2);
     515             :     }
     516             :     else
     517             :     {
     518         732 :         return std::string(pszSrc, i);
     519             :     }
     520             : }
     521             : 
     522             : /************************************************************************/
     523             : /*                            ExpandFormat()                            */
     524             : /************************************************************************/
     525             : 
     526        3180 : std::string DDFFieldDefn::ExpandFormat(const char *pszSrc)
     527             : 
     528             : {
     529        6360 :     std::string osDest;
     530        3180 :     size_t iSrc = 0;
     531             : 
     532       25030 :     while (pszSrc[iSrc] != '\0')
     533             :     {
     534             :         // This is presumably an extra level of brackets around some
     535             :         // binary stuff related to rescanning which we don't care to do
     536             :         // (see 6.4.3.3 of the standard.  We just strip off the extra
     537             :         // layer of brackets.
     538       21850 :         if ((iSrc == 0 || pszSrc[iSrc - 1] == ',') && pszSrc[iSrc] == '(')
     539             :         {
     540        1224 :             const std::string osContents = ExtractSubstring(pszSrc + iSrc);
     541        1224 :             if (osContents.empty())
     542             :             {
     543           0 :                 return std::string();
     544             :             }
     545             :             const std::string osExpandedContents =
     546        1224 :                 ExpandFormat(osContents.c_str());
     547        1224 :             if (osExpandedContents.empty())
     548             :             {
     549           0 :                 return std::string();
     550             :             }
     551             : 
     552        1224 :             if (osDest.size() + osExpandedContents.size() > 1024 * 1024)
     553             :             {
     554           0 :                 return std::string();
     555             :             }
     556             : 
     557        1224 :             osDest += osExpandedContents;
     558             : 
     559        2448 :             iSrc += osContents.size() + 2;
     560             :         }
     561             : 
     562             :         // This is a repeated subclause.
     563       20626 :         else if ((iSrc == 0 || pszSrc[iSrc - 1] == ',') &&
     564        5734 :                  isdigit(static_cast<unsigned char>(pszSrc[iSrc])))
     565             :         {
     566         732 :             const int nRepeat = atoi(pszSrc + iSrc);
     567             :             // 100: arbitrary number. Higher values might cause performance
     568             :             // problems in the below loop
     569         732 :             if (nRepeat < 0 || nRepeat > 100)
     570             :             {
     571           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     572             :                          "Too large repeat count: %d", nRepeat);
     573           0 :                 return std::string();
     574             :             }
     575             : 
     576             :             // Skip over repeat count.
     577         732 :             const char *pszNext = pszSrc + iSrc;  // Used after for.
     578        1464 :             for (; isdigit(static_cast<unsigned char>(*pszNext)); pszNext++)
     579         732 :                 iSrc++;
     580             : 
     581         732 :             const std::string osContents = ExtractSubstring(pszNext);
     582         732 :             if (osContents.empty())
     583             :             {
     584           0 :                 return std::string();
     585             :             }
     586             :             const std::string osExpandedContents =
     587         732 :                 ExpandFormat(osContents.c_str());
     588         732 :             if (osExpandedContents.empty())
     589             :             {
     590           0 :                 return std::string();
     591             :             }
     592             : 
     593         732 :             const size_t nExpandedContentsLen = osExpandedContents.size();
     594         732 :             if (osDest.size() + nExpandedContentsLen * nRepeat > 1024 * 1024)
     595             :             {
     596           0 :                 return std::string();
     597             :             }
     598             : 
     599        2827 :             for (int i = 0; i < nRepeat; i++)
     600             :             {
     601        2095 :                 if (i > 0)
     602        1363 :                     osDest += ',';
     603        2095 :                 osDest += osExpandedContents;
     604             :             }
     605             : 
     606         732 :             if (pszNext[0] == '(')
     607           0 :                 iSrc += osContents.size() + 2;
     608             :             else
     609        1464 :                 iSrc += osContents.size();
     610             :         }
     611             :         else
     612             :         {
     613       19894 :             osDest += pszSrc[iSrc++];
     614             :         }
     615             :     }
     616             : 
     617        3180 :     return osDest;
     618             : }
     619             : 
     620             : /************************************************************************/
     621             : /*                            ApplyFormats()                            */
     622             : /*                                                                      */
     623             : /*      This method parses the format string partially, and then        */
     624             : /*      applies a subfield format string to each subfield object.       */
     625             : /*      It in turn does final parsing of the subfield formats.          */
     626             : /************************************************************************/
     627             : 
     628        1224 : int DDFFieldDefn::ApplyFormats()
     629             : 
     630             : {
     631             :     /* -------------------------------------------------------------------- */
     632             :     /*      Verify that the format string is contained within brackets.     */
     633             :     /* -------------------------------------------------------------------- */
     634        2448 :     if (_formatControls.size() < 2 || _formatControls[0] != '(' ||
     635        1224 :         _formatControls.back() != ')')
     636             :     {
     637           0 :         CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
     638             :                  "Format controls for `%s' field missing brackets:%s",
     639             :                  osTag.c_str(), _formatControls.c_str());
     640             : 
     641           0 :         return FALSE;
     642             :     }
     643             : 
     644             :     /* -------------------------------------------------------------------- */
     645             :     /*      Duplicate the string, and strip off the brackets.               */
     646             :     /* -------------------------------------------------------------------- */
     647             : 
     648        2448 :     const std::string osFormatList = ExpandFormat(_formatControls.c_str());
     649        1224 :     if (osFormatList.empty())
     650             :     {
     651           0 :         CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
     652             :                  "Invalid format controls for `%s': %s", osTag.c_str(),
     653             :                  _formatControls.c_str());
     654           0 :         return FALSE;
     655             :     }
     656             : 
     657             :     /* -------------------------------------------------------------------- */
     658             :     /*      Tokenize based on commas.                                       */
     659             :     /* -------------------------------------------------------------------- */
     660             :     const CPLStringList aosFormatItems(
     661        2448 :         CSLTokenizeStringComplex(osFormatList.c_str(), ",", FALSE, FALSE));
     662             : 
     663             :     /* -------------------------------------------------------------------- */
     664             :     /*      Apply the format items to subfields.                            */
     665             :     /* -------------------------------------------------------------------- */
     666        1224 :     int iFormatItem = 0;  // Used after for.
     667             : 
     668        7589 :     for (; iFormatItem < aosFormatItems.size(); iFormatItem++)
     669             :     {
     670        6365 :         const char *pszPastPrefix = aosFormatItems[iFormatItem];
     671        6365 :         while (*pszPastPrefix >= '0' && *pszPastPrefix <= '9')
     672           0 :             pszPastPrefix++;
     673             : 
     674             :         ///////////////////////////////////////////////////////////////
     675             :         // Did we get too many formats for the subfields created
     676             :         // by names?  This may be legal by the 8211 specification, but
     677             :         // isn't encountered in any formats we care about so we just
     678             :         // blow.
     679             : 
     680        6365 :         if (iFormatItem >= GetSubfieldCount())
     681             :         {
     682           0 :             CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
     683             :                      "Got more formats than subfields for field `%s'.",
     684             :                      osTag.c_str());
     685           0 :             break;
     686             :         }
     687             : 
     688        6365 :         if (!apoSubfields[iFormatItem]->SetFormat(pszPastPrefix))
     689             :         {
     690           0 :             return FALSE;
     691             :         }
     692             :     }
     693             : 
     694             :     /* -------------------------------------------------------------------- */
     695             :     /*      Verify that we got enough formats, cleanup and return.          */
     696             :     /* -------------------------------------------------------------------- */
     697             : 
     698        1224 :     if (iFormatItem < GetSubfieldCount())
     699             :     {
     700           0 :         CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
     701             :                  "Got less formats than subfields for field `%s'.",
     702             :                  osTag.c_str());
     703           0 :         return FALSE;
     704             :     }
     705             : 
     706             :     /* -------------------------------------------------------------------- */
     707             :     /*      If all the fields are fixed width, then we are fixed width      */
     708             :     /*      too.  This is important for repeating fields.                   */
     709             :     /* -------------------------------------------------------------------- */
     710        1224 :     nFixedWidth = 0;
     711        6391 :     for (auto &poSubfield : apoSubfields)
     712             :     {
     713        5591 :         if (poSubfield->GetWidth() == 0)
     714             :         {
     715         424 :             nFixedWidth = 0;
     716         424 :             break;
     717             :         }
     718             :         else
     719             :         {
     720        5167 :             if (nFixedWidth > INT_MAX - poSubfield->GetWidth())
     721             :             {
     722           0 :                 CPLError(CE_Warning,
     723             :                          static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
     724             :                          "Invalid format controls for `%s': %s", osTag.c_str(),
     725             :                          _formatControls.c_str());
     726           0 :                 return FALSE;
     727             :             }
     728        5167 :             nFixedWidth += poSubfield->GetWidth();
     729             :         }
     730             :     }
     731             : 
     732        1224 :     return TRUE;
     733             : }
     734             : 
     735             : /************************************************************************/
     736             : /*                          FindSubfieldDefn()                          */
     737             : /************************************************************************/
     738             : 
     739             : /**
     740             :  * Find a subfield definition by its mnemonic tag.
     741             :  *
     742             :  * @param pszMnemonic The name of the field.
     743             :  *
     744             :  * @return The subfield pointer, or NULL if there isn't any such subfield.
     745             :  */
     746             : 
     747             : const DDFSubfieldDefn *
     748      539756 : DDFFieldDefn::FindSubfieldDefn(const char *pszMnemonic) const
     749             : 
     750             : {
     751      983340 :     for (const auto &poSubfield : apoSubfields)
     752             :     {
     753      983254 :         if (EQUAL(poSubfield->GetName(), pszMnemonic))
     754      539670 :             return poSubfield.get();
     755             :     }
     756             : 
     757          86 :     return nullptr;
     758             : }
     759             : 
     760             : /************************************************************************/
     761             : /*                          GetDefaultValue()                           */
     762             : /************************************************************************/
     763             : 
     764             : /**
     765             :  * Return default data for field instance.
     766             :  */
     767             : 
     768         999 : char *DDFFieldDefn::GetDefaultValue(int *pnSize) const
     769             : 
     770             : {
     771             :     /* -------------------------------------------------------------------- */
     772             :     /*      Loop once collecting the sum of the subfield lengths.           */
     773             :     /* -------------------------------------------------------------------- */
     774         999 :     int nTotalSize = 0;
     775             : 
     776        4430 :     for (auto &poSubfield : apoSubfields)
     777             :     {
     778        3431 :         int nSubfieldSize = 0;
     779             : 
     780        3431 :         if (!poSubfield->GetDefaultValue(nullptr, 0, &nSubfieldSize))
     781           0 :             return nullptr;
     782        3431 :         nTotalSize += nSubfieldSize;
     783             :     }
     784             : 
     785             :     /* -------------------------------------------------------------------- */
     786             :     /*      Allocate buffer.                                                */
     787             :     /* -------------------------------------------------------------------- */
     788         999 :     char *pachData = static_cast<char *>(CPLMalloc(nTotalSize));
     789             : 
     790         999 :     if (pnSize != nullptr)
     791         999 :         *pnSize = nTotalSize;
     792             : 
     793             :     /* -------------------------------------------------------------------- */
     794             :     /*      Loop again, collecting actual default values.                   */
     795             :     /* -------------------------------------------------------------------- */
     796         999 :     int nOffset = 0;
     797        4430 :     for (auto &poSubfield : apoSubfields)
     798             :     {
     799             :         int nSubfieldSize;
     800             : 
     801        3431 :         if (!poSubfield->GetDefaultValue(pachData + nOffset,
     802             :                                          nTotalSize - nOffset, &nSubfieldSize))
     803             :         {
     804           0 :             CPLAssert(false);
     805             :             return nullptr;
     806             :         }
     807             : 
     808        3431 :         nOffset += nSubfieldSize;
     809             :     }
     810             : 
     811         999 :     CPLAssert(nOffset == nTotalSize);
     812             : 
     813         999 :     return pachData;
     814             : }

Generated by: LCOV version 1.14