LCOV - code coverage report
Current view: top level - frmts/hfa - hfafield.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 403 788 51.1 %
Date: 2025-09-10 17:48:50 Functions: 8 11 72.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Erdas Imagine (.img) Translator
       4             :  * Purpose:  Implementation of the HFAField class for managing information
       5             :  *           about one field in a HFA dictionary type.  Managed by HFAType.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 1999, Intergraph Corporation
      10             :  * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "hfa_p.h"
      17             : 
      18             : #include <cerrno>
      19             : #include <climits>
      20             : #include <cstddef>
      21             : #include <cstdio>
      22             : #include <cstring>
      23             : #include <algorithm>
      24             : #include <cmath>
      25             : #include <limits>
      26             : #include <vector>
      27             : 
      28             : #include "cpl_conv.h"
      29             : #include "cpl_error.h"
      30             : #include "cpl_string.h"
      31             : #include "cpl_vsi.h"
      32             : 
      33             : constexpr int MAX_ENTRY_REPORT = 16;
      34             : 
      35             : namespace
      36             : {
      37             : 
      38           0 : int FloatToIntClamp(float fValue)
      39             : {
      40           0 :     if (std::isnan(fValue))
      41           0 :         return 0;
      42           0 :     if (fValue >= static_cast<float>(std::numeric_limits<int>::max()))
      43           0 :         return std::numeric_limits<int>::max();
      44           0 :     if (fValue <= static_cast<float>(std::numeric_limits<int>::min()))
      45           0 :         return std::numeric_limits<int>::min();
      46           0 :     return static_cast<int>(fValue);
      47             : }
      48             : 
      49             : }  // namespace
      50             : 
      51             : /************************************************************************/
      52             : /* ==================================================================== */
      53             : /*                              HFAField                                */
      54             : /* ==================================================================== */
      55             : /************************************************************************/
      56             : 
      57             : /************************************************************************/
      58             : /*                              HFAField()                              */
      59             : /************************************************************************/
      60             : 
      61      105760 : HFAField::HFAField()
      62             :     : nBytes(0), nItemCount(0), chPointer('\0'), chItemType('\0'),
      63             :       pszItemObjectType(nullptr), poItemObjectType(nullptr),
      64      105760 :       papszEnumNames(nullptr), pszFieldName(nullptr)
      65             : {
      66      105760 :     memset(szNumberString, 0, sizeof(szNumberString));
      67      105760 : }
      68             : 
      69             : /************************************************************************/
      70             : /*                             ~HFAField()                              */
      71             : /************************************************************************/
      72             : 
      73      211520 : HFAField::~HFAField()
      74             : 
      75             : {
      76      105760 :     CPLFree(pszItemObjectType);
      77      105760 :     CSLDestroy(papszEnumNames);
      78      105760 :     CPLFree(pszFieldName);
      79      105760 : }
      80             : 
      81             : /************************************************************************/
      82             : /*                             Initialize()                             */
      83             : /************************************************************************/
      84             : 
      85      105760 : const char *HFAField::Initialize(const char *pszInput)
      86             : 
      87             : {
      88             :     // Read the number.
      89      105760 :     nItemCount = atoi(pszInput);
      90      105760 :     if (nItemCount < 0)
      91           0 :         return nullptr;
      92             : 
      93      213770 :     while (*pszInput != '\0' && *pszInput != ':')
      94      108010 :         pszInput++;
      95             : 
      96      105760 :     if (*pszInput == '\0')
      97           0 :         return nullptr;
      98             : 
      99      105760 :     pszInput++;
     100             : 
     101             :     // Is this a pointer?
     102      105760 :     if (*pszInput == 'p' || *pszInput == '*')
     103       24268 :         chPointer = *(pszInput++);
     104             : 
     105             :     // Get the general type.
     106      105760 :     if (*pszInput == '\0')
     107           0 :         return nullptr;
     108             : 
     109      105760 :     chItemType = *(pszInput++);
     110             : 
     111      105760 :     if (strchr("124cCesStlLfdmMbox", chItemType) == nullptr)
     112             :     {
     113           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Unrecognized item type: %c",
     114           1 :                  chItemType);
     115           1 :         return nullptr;
     116             :     }
     117             : 
     118             :     // If this is an object, we extract the type of the object.
     119      105759 :     int i = 0;  // TODO: Describe why i needs to span chItemType blocks.
     120             : 
     121      105759 :     if (chItemType == 'o')
     122             :     {
     123      171723 :         for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
     124             :         {
     125             :         }
     126       12636 :         if (pszInput[i] == '\0')
     127           0 :             return nullptr;
     128             : 
     129       12636 :         pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
     130       12636 :         strncpy(pszItemObjectType, pszInput, i);
     131       12636 :         pszItemObjectType[i] = '\0';
     132             : 
     133       12636 :         pszInput += i + 1;
     134             :     }
     135             : 
     136             :     // If this is an inline object, we need to skip past the
     137             :     // definition, and then extract the object class name.
     138             :     //
     139             :     // We ignore the actual definition, so if the object type isn't
     140             :     // already defined, things will not work properly.  See the
     141             :     // file lceugr250_00_pct.aux for an example of inline defs.
     142      105759 :     if (chItemType == 'x' && *pszInput == '{')
     143             :     {
     144        2874 :         int nBraceDepth = 1;
     145        2874 :         pszInput++;
     146             : 
     147             :         // Skip past the definition.
     148       93708 :         while (nBraceDepth > 0 && *pszInput != '\0')
     149             :         {
     150       90834 :             if (*pszInput == '{')
     151        1439 :                 nBraceDepth++;
     152       89395 :             else if (*pszInput == '}')
     153        4313 :                 nBraceDepth--;
     154             : 
     155       90834 :             pszInput++;
     156             :         }
     157        2874 :         if (*pszInput == '\0')
     158           0 :             return nullptr;
     159             : 
     160        2874 :         chItemType = 'o';
     161             : 
     162             :         // Find the comma terminating the type name.
     163       36669 :         for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
     164             :         {
     165             :         }
     166        2874 :         if (pszInput[i] == '\0')
     167           0 :             return nullptr;
     168             : 
     169        2874 :         pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
     170        2874 :         strncpy(pszItemObjectType, pszInput, i);
     171        2874 :         pszItemObjectType[i] = '\0';
     172             : 
     173        2874 :         pszInput += i + 1;
     174             :     }
     175             : 
     176             :     // If this is an enumeration we have to extract all the
     177             :     // enumeration values.
     178      105759 :     if (chItemType == 'e')
     179             :     {
     180       14434 :         const int nEnumCount = atoi(pszInput);
     181             : 
     182       14434 :         if (nEnumCount < 0 || nEnumCount > 100000)
     183           0 :             return nullptr;
     184             : 
     185       14434 :         pszInput = strchr(pszInput, ':');
     186       14434 :         if (pszInput == nullptr)
     187           0 :             return nullptr;
     188             : 
     189       14434 :         pszInput++;
     190             : 
     191       14434 :         papszEnumNames =
     192       14434 :             static_cast<char **>(VSICalloc(sizeof(char *), nEnumCount + 1));
     193       14434 :         if (papszEnumNames == nullptr)
     194           0 :             return nullptr;
     195             : 
     196       81681 :         for (int iEnum = 0; iEnum < nEnumCount; iEnum++)
     197             :         {
     198      650584 :             for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
     199             :             {
     200             :             }
     201             : 
     202       67247 :             if (pszInput[i] != ',')
     203           0 :                 return nullptr;
     204             : 
     205       67247 :             char *pszToken = static_cast<char *>(CPLMalloc(i + 1));
     206       67247 :             strncpy(pszToken, pszInput, i);
     207       67247 :             pszToken[i] = '\0';
     208             : 
     209       67247 :             papszEnumNames[iEnum] = pszToken;
     210             : 
     211       67247 :             pszInput += i + 1;
     212             :         }
     213             :     }
     214             : 
     215             :     // Extract the field name.
     216     1021740 :     for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
     217             :     {
     218             :     }
     219      105759 :     if (pszInput[i] == '\0')
     220           0 :         return nullptr;
     221             : 
     222      105759 :     pszFieldName = static_cast<char *>(CPLMalloc(i + 1));
     223      105759 :     strncpy(pszFieldName, pszInput, i);
     224      105759 :     pszFieldName[i] = '\0';
     225             : 
     226      105759 :     pszInput += i + 1;
     227             : 
     228      105759 :     return pszInput;
     229             : }
     230             : 
     231             : /************************************************************************/
     232             : /*                            CompleteDefn()                            */
     233             : /*                                                                      */
     234             : /*      Establish size, and pointers to component types.                */
     235             : /************************************************************************/
     236             : 
     237      105753 : bool HFAField::CompleteDefn(HFADictionary *poDict)
     238             : 
     239             : {
     240             :     // Get a reference to the type object if we have a type name
     241             :     // for this field (not a built in).
     242      105753 :     if (pszItemObjectType != nullptr)
     243       15504 :         poItemObjectType = poDict->FindType(pszItemObjectType);
     244             : 
     245             :     // Figure out the size.
     246      105753 :     if (chPointer == 'p')
     247             :     {
     248       16678 :         nBytes = -1;  // We can't know the instance size.
     249             :     }
     250       89075 :     else if (poItemObjectType != nullptr)
     251             :     {
     252       11803 :         if (!poItemObjectType->CompleteDefn(poDict))
     253           2 :             return false;
     254       11801 :         if (poItemObjectType->nBytes == -1)
     255        9587 :             nBytes = -1;
     256        2214 :         else if (poItemObjectType->nBytes != 0 &&
     257        2214 :                  nItemCount > INT_MAX / poItemObjectType->nBytes)
     258           0 :             nBytes = -1;
     259             :         else
     260        2214 :             nBytes = poItemObjectType->nBytes * nItemCount;
     261             : 
     262             :         // TODO(schwehr): What does the 8 represent?
     263       11801 :         if (chPointer == '*' && nBytes != -1)
     264             :         {
     265        2214 :             if (nBytes > INT_MAX - 8)
     266           0 :                 nBytes = -1;
     267             :             else
     268        2214 :                 nBytes += 8;  // Count, and offset.
     269             :         }
     270             :     }
     271             :     else
     272             :     {
     273       77272 :         const int nItemSize = poDict->GetItemSize(chItemType);
     274       77272 :         if (nItemSize != 0 && nItemCount > INT_MAX / nItemSize)
     275        3948 :             nBytes = -1;
     276             :         else
     277       73324 :             nBytes = nItemSize * nItemCount;
     278             :     }
     279      105751 :     return true;
     280             : }
     281             : 
     282             : /************************************************************************/
     283             : /*                                Dump()                                */
     284             : /************************************************************************/
     285             : 
     286           0 : void HFAField::Dump(FILE *fp)
     287             : 
     288             : {
     289             :     const char *pszTypeName;
     290             : 
     291           0 :     switch (chItemType)
     292             :     {
     293           0 :         case '1':
     294           0 :             pszTypeName = "U1";
     295           0 :             break;
     296             : 
     297           0 :         case '2':
     298           0 :             pszTypeName = "U2";
     299           0 :             break;
     300             : 
     301           0 :         case '4':
     302           0 :             pszTypeName = "U4";
     303           0 :             break;
     304             : 
     305           0 :         case 'c':
     306           0 :             pszTypeName = "UCHAR";
     307           0 :             break;
     308             : 
     309           0 :         case 'C':
     310           0 :             pszTypeName = "CHAR";
     311           0 :             break;
     312             : 
     313           0 :         case 'e':
     314           0 :             pszTypeName = "ENUM";
     315           0 :             break;
     316             : 
     317           0 :         case 's':
     318           0 :             pszTypeName = "USHORT";
     319           0 :             break;
     320             : 
     321           0 :         case 'S':
     322           0 :             pszTypeName = "SHORT";
     323           0 :             break;
     324             : 
     325           0 :         case 't':
     326           0 :             pszTypeName = "TIME";
     327           0 :             break;
     328             : 
     329           0 :         case 'l':
     330           0 :             pszTypeName = "ULONG";
     331           0 :             break;
     332             : 
     333           0 :         case 'L':
     334           0 :             pszTypeName = "LONG";
     335           0 :             break;
     336             : 
     337           0 :         case 'f':
     338           0 :             pszTypeName = "FLOAT";
     339           0 :             break;
     340             : 
     341           0 :         case 'd':
     342           0 :             pszTypeName = "DOUBLE";
     343           0 :             break;
     344             : 
     345           0 :         case 'm':
     346           0 :             pszTypeName = "COMPLEX";
     347           0 :             break;
     348             : 
     349           0 :         case 'M':
     350           0 :             pszTypeName = "DCOMPLEX";
     351           0 :             break;
     352             : 
     353           0 :         case 'b':
     354           0 :             pszTypeName = "BASEDATA";
     355           0 :             break;
     356             : 
     357           0 :         case 'o':
     358           0 :             pszTypeName = pszItemObjectType;
     359           0 :             break;
     360             : 
     361           0 :         case 'x':
     362           0 :             pszTypeName = "InlineType";
     363           0 :             break;
     364             : 
     365           0 :         default:
     366           0 :             CPLAssert(false);
     367             :             pszTypeName = "Unknown";
     368             :     }
     369             : 
     370           0 :     CPL_IGNORE_RET_VAL(VSIFPrintf(fp, "    %-19s %c %s[%d];\n", pszTypeName,
     371           0 :                                   chPointer ? chPointer : ' ', pszFieldName,
     372             :                                   nItemCount));
     373             : 
     374           0 :     if (papszEnumNames != nullptr)
     375             :     {
     376           0 :         for (int i = 0; papszEnumNames[i] != nullptr; i++)
     377             :         {
     378           0 :             CPL_IGNORE_RET_VAL(
     379           0 :                 VSIFPrintf(fp, "        %s=%d\n", papszEnumNames[i], i));
     380             :         }
     381             :     }
     382           0 : }
     383             : 
     384             : /************************************************************************/
     385             : /*                            SetInstValue()                            */
     386             : /************************************************************************/
     387             : 
     388       14954 : CPLErr HFAField::SetInstValue(const char *pszField, int nIndexValue,
     389             :                               GByte *pabyData, GUInt32 nDataOffset,
     390             :                               int nDataSize, char chReqType, void *pValue)
     391             : 
     392             : {
     393             :     // If this field contains a pointer, then we will adjust the
     394             :     // data offset relative to it.
     395       14954 :     if (chPointer != '\0')
     396             :     {
     397        7687 :         GUInt32 nCount = 0;
     398             : 
     399             :         // The count returned for BASEDATA's are the contents,
     400             :         // but here we really want to mark it as one BASEDATA instance
     401             :         // (see #2144).
     402        7687 :         if (chItemType == 'b')
     403             :         {
     404          53 :             nCount = 1;
     405             :         }
     406             :         // Set the size from string length.
     407        7634 :         else if (chReqType == 's' && (chItemType == 'c' || chItemType == 'C'))
     408             :         {
     409        1770 :             if (pValue != nullptr)
     410        1463 :                 nCount = static_cast<GUInt32>(strlen((char *)pValue) + 1);
     411             :         }
     412             :         // Set size based on index. Assumes in-order setting of array.
     413             :         else
     414             :         {
     415        5864 :             nCount = nIndexValue + 1;
     416             :         }
     417             : 
     418             :         // TODO(schwehr): What does the 8 represent?
     419        7687 :         if (static_cast<int>(nCount) + 8 > nDataSize)
     420             :         {
     421           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     422             :                      "Attempt to extend field %s in node past end of data, "
     423             :                      "not currently supported.",
     424             :                      pszField);
     425           0 :             return CE_Failure;
     426             :         }
     427             : 
     428             :         // We will update the object count iff we are writing beyond the end.
     429        7687 :         GUInt32 nOffset = 0;
     430        7687 :         memcpy(&nOffset, pabyData, 4);
     431             :         HFAStandard(4, &nOffset);
     432        7687 :         if (nOffset < nCount)
     433             :         {
     434        5938 :             nOffset = nCount;
     435             :             HFAStandard(4, &nOffset);
     436        5938 :             memcpy(pabyData, &nOffset, 4);
     437             :         }
     438             : 
     439        7687 :         if (pValue == nullptr)
     440         307 :             nOffset = 0;
     441             :         else
     442        7380 :             nOffset = nDataOffset + 8;
     443             :         HFAStandard(4, &nOffset);
     444        7687 :         memcpy(pabyData + 4, &nOffset, 4);
     445             : 
     446        7687 :         pabyData += 8;
     447             : 
     448        7687 :         nDataOffset += 8;
     449        7687 :         nDataSize -= 8;
     450             :     }
     451             : 
     452             :     // Pointers to char or uchar arrays requested as strings are
     453             :     // handled as a special case.
     454       14954 :     if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
     455             :     {
     456        1770 :         int nBytesToCopy = 0;
     457             : 
     458        1770 :         if (nBytes == -1)
     459             :         {
     460        1770 :             if (pValue != nullptr)
     461        1463 :                 nBytesToCopy = static_cast<int>(strlen((char *)pValue) + 1);
     462             :         }
     463             :         else
     464             :         {
     465           0 :             nBytesToCopy = nBytes;
     466             :         }
     467             : 
     468        1770 :         if (nBytesToCopy > nDataSize)
     469             :         {
     470           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     471             :                      "Attempt to extend field %s in node past end of data "
     472             :                      "not currently supported.",
     473             :                      pszField);
     474           0 :             return CE_Failure;
     475             :         }
     476             : 
     477        1770 :         memset(pabyData, 0, nBytesToCopy);
     478             : 
     479        1770 :         if (pValue != nullptr)
     480        1463 :             strncpy((char *)pabyData, (char *)pValue, nBytesToCopy);
     481             : 
     482        1770 :         return CE_None;
     483             :     }
     484             : 
     485             :     // Translate the passed type into different representations.
     486       13184 :     int nIntValue = 0;
     487       13184 :     double dfDoubleValue = 0.0;
     488             : 
     489       13184 :     if (chReqType == 's')
     490             :     {
     491        2125 :         CPLAssert(pValue != nullptr);
     492        2125 :         nIntValue = atoi((char *)pValue);
     493        2125 :         dfDoubleValue = CPLAtof((char *)pValue);
     494             :     }
     495       11059 :     else if (chReqType == 'd')
     496             :     {
     497        7463 :         CPLAssert(pValue != nullptr);
     498        7463 :         dfDoubleValue = *((double *)pValue);
     499        7463 :         if (dfDoubleValue > INT_MAX)
     500           0 :             nIntValue = INT_MAX;
     501        7463 :         else if (dfDoubleValue < INT_MIN)
     502           1 :             nIntValue = INT_MIN;
     503        7462 :         else if (std::isfinite(dfDoubleValue))
     504        7461 :             nIntValue = static_cast<int>(dfDoubleValue);
     505             :     }
     506        3596 :     else if (chReqType == 'i')
     507             :     {
     508        3596 :         CPLAssert(pValue != nullptr);
     509        3596 :         nIntValue = *((int *)pValue);
     510        3596 :         dfDoubleValue = nIntValue;
     511             :     }
     512           0 :     else if (chReqType == 'p')
     513             :     {
     514           0 :         CPLError(
     515             :             CE_Failure, CPLE_NotSupported,
     516             :             "HFAField::SetInstValue() not supported yet for pointer values.");
     517             : 
     518           0 :         return CE_Failure;
     519             :     }
     520             :     else
     521             :     {
     522           0 :         CPLAssert(false);
     523             :         return CE_Failure;
     524             :     }
     525             : 
     526             :     // Handle by type.
     527       13184 :     switch (chItemType)
     528             :     {
     529           0 :         case 'c':
     530             :         case 'C':
     531           0 :             if (nIndexValue + 1 > nDataSize)
     532             :             {
     533           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     534             :                          "Attempt to extend field %s in node past end of data, "
     535             :                          "not currently supported.",
     536             :                          pszField);
     537           0 :                 return CE_Failure;
     538             :             }
     539             : 
     540           0 :             if (chReqType == 's')
     541             :             {
     542           0 :                 CPLAssert(pValue != nullptr);
     543           0 :                 pabyData[nIndexValue] = ((char *)pValue)[0];
     544             :             }
     545             :             else
     546             :             {
     547           0 :                 pabyData[nIndexValue] = static_cast<char>(nIntValue);
     548             :             }
     549           0 :             break;
     550             : 
     551        1515 :         case 'e':
     552             :         case 's':
     553             :         {
     554        1515 :             if (chItemType == 'e' && chReqType == 's')
     555             :             {
     556         932 :                 CPLAssert(pValue != nullptr);
     557         932 :                 nIntValue = CSLFindString(papszEnumNames, (char *)pValue);
     558         932 :                 if (nIntValue == -1)
     559             :                 {
     560           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     561             :                              "Attempt to set enumerated field with unknown"
     562             :                              " value `%s'.",
     563             :                              (char *)pValue);
     564           0 :                     return CE_Failure;
     565             :                 }
     566             :             }
     567             : 
     568        1515 :             if (nIndexValue * 2 + 2 > nDataSize)
     569             :             {
     570           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     571             :                          "Attempt to extend field %s in node past end of data, "
     572             :                          "not currently supported.",
     573             :                          pszField);
     574           0 :                 return CE_Failure;
     575             :             }
     576             : 
     577             :             // TODO(schwehr): Warn on clamping.
     578        1515 :             unsigned short nNumber = static_cast<unsigned short>(nIntValue);
     579             :             // TODO(schwehr): What is this 2?
     580             :             HFAStandard(2, &nNumber);
     581        1515 :             memcpy(pabyData + nIndexValue * 2, &nNumber, 2);
     582             :         }
     583        1515 :         break;
     584             : 
     585           0 :         case 'S':
     586             :         {
     587           0 :             if (nIndexValue * 2 + 2 > nDataSize)
     588             :             {
     589           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     590             :                          "Attempt to extend field %s in node past end of data, "
     591             :                          "not currently supported.",
     592             :                          pszField);
     593           0 :                 return CE_Failure;
     594             :             }
     595             : 
     596             :             // TODO(schwehr): Warn on clamping.
     597           0 :             short nNumber = static_cast<short>(nIntValue);
     598             :             // TODO(schwehr): What is this 2?
     599             :             HFAStandard(2, &nNumber);
     600           0 :             memcpy(pabyData + nIndexValue * 2, &nNumber, 2);
     601             :         }
     602           0 :         break;
     603             : 
     604        2325 :         case 't':
     605             :         case 'l':
     606             :         {
     607        2325 :             if (nIndexValue * 4 + 4 > nDataSize)
     608             :             {
     609           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     610             :                          "Attempt to extend field %s in node past end of data, "
     611             :                          "not currently supported.",
     612             :                          pszField);
     613           0 :                 return CE_Failure;
     614             :             }
     615             : 
     616        2325 :             GUInt32 nNumber = nIntValue;
     617             :             // TODO(schwehr): What is this 4?
     618             :             HFAStandard(4, &nNumber);
     619        2325 :             memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
     620             :         }
     621        2325 :         break;
     622             : 
     623         609 :         case 'L':
     624             :         {
     625         609 :             if (nIndexValue * 4 + 4 > nDataSize)
     626             :             {
     627           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     628             :                          "Attempt to extend field %s in node past end of data, "
     629             :                          "not currently supported.",
     630             :                          pszField);
     631           0 :                 return CE_Failure;
     632             :             }
     633             : 
     634         609 :             GInt32 nNumber = nIntValue;
     635             :             HFAStandard(4, &nNumber);
     636         609 :             memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
     637             :         }
     638         609 :         break;
     639             : 
     640           0 :         case 'f':
     641             :         {
     642           0 :             if (nIndexValue * 4 + 4 > nDataSize)
     643             :             {
     644           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     645             :                          "Attempt to extend field %s in node past end of data, "
     646             :                          "not currently supported.",
     647             :                          pszField);
     648           0 :                 return CE_Failure;
     649             :             }
     650             : 
     651             :             // TODO(schwehr): Warn on clamping.
     652           0 :             float fNumber = static_cast<float>(dfDoubleValue);
     653             :             // TODO(schwehr): 4 == sizeof(float)?
     654             :             HFAStandard(4, &fNumber);
     655           0 :             memcpy(pabyData + nIndexValue * 4, &fNumber, 4);
     656             :         }
     657           0 :         break;
     658             : 
     659        5658 :         case 'd':
     660             :         {
     661        5658 :             if (nIndexValue * 8 + 8 > nDataSize)
     662             :             {
     663           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     664             :                          "Attempt to extend field %s in node past end of data, "
     665             :                          "not currently supported.",
     666             :                          pszField);
     667           0 :                 return CE_Failure;
     668             :             }
     669             : 
     670        5658 :             double dfNumber = dfDoubleValue;
     671             :             HFAStandard(8, &dfNumber);
     672        5658 :             memcpy(pabyData + nIndexValue * 8, &dfNumber, 8);
     673             :         }
     674        5658 :         break;
     675             : 
     676          53 :         case 'b':
     677             :         {
     678             :             // Extract existing rows, columns, and datatype.
     679          53 :             GInt32 nRows = 1;  // TODO(schwehr): Why init to 1 instead of 0?
     680          53 :             memcpy(&nRows, pabyData, 4);
     681             :             HFAStandard(4, &nRows);
     682             : 
     683          53 :             GInt32 nColumns = 1;  // TODO(schwehr): Why init to 1 instead of 0?
     684          53 :             memcpy(&nColumns, pabyData + 4, 4);
     685             :             HFAStandard(4, &nColumns);
     686             : 
     687          53 :             GInt16 nBaseItemType = 0;
     688          53 :             memcpy(&nBaseItemType, pabyData + 8, 2);
     689             :             HFAStandard(2, &nBaseItemType);
     690             : 
     691             :             // Are we using special index values to update the rows, columns
     692             :             // or type?
     693             : 
     694          53 :             if (nIndexValue == -3)
     695          12 :                 nBaseItemType = static_cast<GInt16>(nIntValue);
     696          41 :             else if (nIndexValue == -2)
     697          12 :                 nColumns = nIntValue;
     698          29 :             else if (nIndexValue == -1)
     699          12 :                 nRows = nIntValue;
     700             : 
     701          53 :             if (nIndexValue < -3 || nIndexValue >= nRows * nColumns)
     702           1 :                 return CE_Failure;
     703             : 
     704             :             // Write back the rows, columns and basedatatype.
     705             :             HFAStandard(4, &nRows);
     706          52 :             memcpy(pabyData, &nRows, 4);
     707             :             HFAStandard(4, &nColumns);
     708          52 :             memcpy(pabyData + 4, &nColumns, 4);
     709             :             HFAStandard(2, &nBaseItemType);
     710          52 :             memcpy(pabyData + 8, &nBaseItemType, 2);
     711             :             HFAStandard(2, &nBaseItemType);  // Swap back for our use.
     712             : 
     713          52 :             if (nBaseItemType < EPT_MIN || nBaseItemType > EPT_MAX)
     714           0 :                 return CE_Failure;
     715          52 :             const EPTType eBaseItemType = static_cast<EPTType>(nBaseItemType);
     716             : 
     717             :             // We ignore the 2 byte objecttype value.
     718             : 
     719          52 :             nDataSize -= 12;
     720             : 
     721          52 :             if (nIndexValue >= 0)
     722             :             {
     723          32 :                 if ((nIndexValue + 1) *
     724          16 :                         (HFAGetDataTypeBits(eBaseItemType) / 8) >
     725             :                     nDataSize)
     726             :                 {
     727           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     728             :                              "Attempt to extend field %s in node past end of "
     729             :                              "data, not currently supported.",
     730             :                              pszField);
     731           0 :                     return CE_Failure;
     732             :                 }
     733             : 
     734          16 :                 if (eBaseItemType == EPT_f64)
     735             :                 {
     736          16 :                     double dfNumber = dfDoubleValue;
     737             : 
     738             :                     HFAStandard(8, &dfNumber);
     739          16 :                     memcpy(pabyData + 12 + nIndexValue * 8, &dfNumber, 8);
     740             :                 }
     741           0 :                 else if (eBaseItemType == EPT_u8)
     742             :                 {
     743             :                     // TODO(schwehr): Warn on clamping.
     744           0 :                     unsigned char nNumber =
     745           0 :                         static_cast<unsigned char>(dfDoubleValue);
     746           0 :                     memcpy(pabyData + 12 + nIndexValue, &nNumber, 1);
     747             :                 }
     748             :                 else
     749             :                 {
     750           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     751             :                              "Setting basedata field %s with type %s "
     752             :                              "not currently supported.",
     753             :                              pszField, HFAGetDataTypeName(eBaseItemType));
     754           0 :                     return CE_Failure;
     755             :                 }
     756             :             }
     757             :         }
     758          52 :         break;
     759             : 
     760        3024 :         case 'o':
     761        3024 :             if (poItemObjectType != nullptr)
     762             :             {
     763        3024 :                 int nExtraOffset = 0;
     764             : 
     765        3024 :                 if (poItemObjectType->nBytes > 0)
     766             :                 {
     767        1252 :                     if (nIndexValue != 0 &&
     768          49 :                         poItemObjectType->nBytes > INT_MAX / nIndexValue)
     769             :                     {
     770           0 :                         return CE_Failure;
     771             :                     }
     772        1252 :                     nExtraOffset = poItemObjectType->nBytes * nIndexValue;
     773             :                 }
     774             :                 else
     775             :                 {
     776        1781 :                     for (int iIndexCounter = 0; iIndexCounter < nIndexValue &&
     777             :                                                 nExtraOffset < nDataSize;
     778             :                          iIndexCounter++)
     779             :                     {
     780           9 :                         std::set<HFAField *> oVisitedFields;
     781          18 :                         const int nInc = poItemObjectType->GetInstBytes(
     782           9 :                             pabyData + nExtraOffset, nDataSize - nExtraOffset,
     783             :                             oVisitedFields);
     784           9 :                         if (nInc <= 0 || nExtraOffset > INT_MAX - nInc)
     785             :                         {
     786           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     787             :                                      "Invalid return value");
     788           0 :                             return CE_Failure;
     789             :                         }
     790             : 
     791           9 :                         nExtraOffset += nInc;
     792             :                     }
     793             :                 }
     794             : 
     795        3024 :                 if (nExtraOffset >= nDataSize)
     796           1 :                     return CE_Failure;
     797             : 
     798        3023 :                 if (pszField != nullptr && strlen(pszField) > 0)
     799             :                 {
     800        6046 :                     return poItemObjectType->SetInstValue(
     801        3023 :                         pszField, pabyData + nExtraOffset,
     802        3023 :                         nDataOffset + nExtraOffset, nDataSize - nExtraOffset,
     803        3023 :                         chReqType, pValue);
     804             :                 }
     805             :                 else
     806             :                 {
     807           0 :                     CPLAssert(false);
     808             :                     return CE_Failure;
     809             :                 }
     810             :             }
     811           0 :             break;
     812             : 
     813           0 :         default:
     814           0 :             CPLAssert(false);
     815             :             return CE_Failure;
     816             :     }
     817             : 
     818       10159 :     return CE_None;
     819             : }
     820             : 
     821             : /************************************************************************/
     822             : /*                          ExtractInstValue()                          */
     823             : /*                                                                      */
     824             : /*      Extract the value of an instance of a field.                    */
     825             : /*                                                                      */
     826             : /*      pszField should be NULL if this field is not a                  */
     827             : /*      substructure.                                                   */
     828             : /************************************************************************/
     829             : 
     830       46564 : bool HFAField::ExtractInstValue(const char *pszField, int nIndexValue,
     831             :                                 GByte *pabyData, GUInt32 nDataOffset,
     832             :                                 int nDataSize, char chReqType, void *pReqReturn,
     833             :                                 int *pnRemainingDataSize)
     834             : 
     835             : {
     836       46564 :     const int nInstItemCount = GetInstCount(pabyData, nDataSize);
     837             : 
     838       46564 :     if (pnRemainingDataSize)
     839         424 :         *pnRemainingDataSize = -1;
     840             : 
     841             :     // Check the index value is valid.
     842             :     // Eventually this will have to account for variable fields.
     843       46564 :     if (nIndexValue < 0 || nIndexValue >= nInstItemCount)
     844             :     {
     845         567 :         if (chItemType == 'b' && nIndexValue >= -3 && nIndexValue < 0)
     846             :             /* ok - special index values */;
     847             :         else
     848         567 :             return false;
     849             :     }
     850             : 
     851             :     // If this field contains a pointer, then we will adjust the
     852             :     // data offset relative to it.
     853       45997 :     if (chPointer != '\0')
     854             :     {
     855       19195 :         if (nDataSize < 8)
     856             :         {
     857           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     858           0 :             return false;
     859             :         }
     860             : 
     861       19195 :         GUInt32 nOffset = 0;
     862       19195 :         memcpy(&nOffset, pabyData + 4, 4);
     863             :         HFAStandard(4, &nOffset);
     864             : 
     865             : #if DEBUG_VERBOSE
     866             :         if (nOffset != static_cast<GUInt32>(nDataOffset + 8))
     867             :         {
     868             :             // TODO(schwehr): Debug why this is happening.
     869             :             CPLError(CE_Warning, CPLE_AppDefined,
     870             :                      "ExtractInstValue: "
     871             :                      "%s.%s points at %d, not %d as expected",
     872             :                      pszFieldName, pszField ? pszField : "", nOffset,
     873             :                      nDataOffset + 8);
     874             :         }
     875             : #endif
     876             : 
     877       19195 :         pabyData += 8;
     878       19195 :         nDataOffset += 8;
     879       19195 :         nDataSize -= 8;
     880             :     }
     881             : 
     882             :     // Pointers to char or uchar arrays requested as strings are
     883             :     // handled as a special case.
     884       45997 :     if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
     885             :     {
     886        2021 :         *((GByte **)pReqReturn) = pabyData;
     887        2021 :         if (pnRemainingDataSize)
     888         212 :             *pnRemainingDataSize = nDataSize;
     889        2021 :         return pabyData != nullptr;
     890             :     }
     891             : 
     892             :     // Handle by type.
     893       43976 :     char *pszStringRet = nullptr;
     894       43976 :     int nIntRet = 0;
     895       43976 :     double dfDoubleRet = 0.0;
     896       43976 :     GByte *pabyRawData = nullptr;
     897             : 
     898       43976 :     switch (chItemType)
     899             :     {
     900           0 :         case 'c':
     901             :         case 'C':
     902           0 :             if (nIndexValue >= nDataSize)
     903             :             {
     904           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     905           0 :                 return false;
     906             :             }
     907           0 :             nIntRet = pabyData[nIndexValue];
     908           0 :             dfDoubleRet = nIntRet;
     909           0 :             break;
     910             : 
     911        8830 :         case 'e':
     912             :         case 's':
     913             :         {
     914        8830 :             if (nIndexValue * 2 + 2 > nDataSize)
     915             :             {
     916           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     917           0 :                 return false;
     918             :             }
     919        8830 :             unsigned short nNumber = 0;
     920        8830 :             memcpy(&nNumber, pabyData + nIndexValue * 2, 2);
     921             :             HFAStandard(2, &nNumber);
     922        8830 :             nIntRet = nNumber;
     923        8830 :             dfDoubleRet = nIntRet;
     924             : 
     925       17660 :             if (chItemType == 'e' &&
     926        8830 :                 nNumber < static_cast<unsigned>(CSLCount(papszEnumNames)))
     927             :             {
     928        8829 :                 pszStringRet = papszEnumNames[nNumber];
     929             :             }
     930             :         }
     931        8830 :         break;
     932             : 
     933           0 :         case 'S':
     934             :         {
     935           0 :             if (nIndexValue * 2 + 2 > nDataSize)
     936             :             {
     937           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     938           0 :                 return false;
     939             :             }
     940           0 :             short nNumber = 0;
     941           0 :             memcpy(&nNumber, pabyData + nIndexValue * 2, 2);
     942             :             HFAStandard(2, &nNumber);
     943           0 :             nIntRet = nNumber;
     944           0 :             dfDoubleRet = nIntRet;
     945             :         }
     946           0 :         break;
     947             : 
     948       10646 :         case 't':
     949             :         case 'l':
     950             :         {
     951       10646 :             if (nIndexValue * 4 + 4 > nDataSize)
     952             :             {
     953           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     954           0 :                 return false;
     955             :             }
     956       10646 :             GUInt32 nNumber = 0;
     957       10646 :             memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
     958             :             HFAStandard(4, &nNumber);
     959       10646 :             nIntRet = nNumber;
     960       10646 :             dfDoubleRet = nIntRet;
     961             :         }
     962       10646 :         break;
     963             : 
     964        3071 :         case 'L':
     965             :         {
     966        3071 :             if (nIndexValue * 4 + 4 > nDataSize)
     967             :             {
     968           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     969           0 :                 return false;
     970             :             }
     971        3071 :             GInt32 nNumber = 0;
     972             :             // TODO(schwehr): What is 4?
     973        3071 :             memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
     974             :             HFAStandard(4, &nNumber);
     975        3071 :             nIntRet = nNumber;
     976        3071 :             dfDoubleRet = nIntRet;
     977             :         }
     978        3071 :         break;
     979             : 
     980           0 :         case 'f':
     981             :         {
     982           0 :             if (nIndexValue * 4 + 4 > nDataSize)
     983             :             {
     984           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     985           0 :                 return false;
     986             :             }
     987           0 :             float fNumber = 0.0f;
     988             :             // TODO(schwehr): What is 4?
     989           0 :             memcpy(&fNumber, pabyData + nIndexValue * 4, 4);
     990             :             HFAStandard(4, &fNumber);
     991           0 :             if (static_cast<double>(fNumber) >
     992           0 :                     std::numeric_limits<int>::max() ||
     993           0 :                 static_cast<double>(fNumber) <
     994           0 :                     std::numeric_limits<int>::min() ||
     995           0 :                 std::isnan(fNumber))
     996             :             {
     997           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too large for int: %f",
     998             :                          fNumber);
     999           0 :                 return false;
    1000             :             }
    1001           0 :             dfDoubleRet = fNumber;
    1002           0 :             nIntRet = static_cast<int>(fNumber);
    1003             :         }
    1004           0 :         break;
    1005             : 
    1006        7341 :         case 'd':
    1007             :         {
    1008        7341 :             if (nIndexValue * 8 + 8 > nDataSize)
    1009             :             {
    1010           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1011           0 :                 return false;
    1012             :             }
    1013        7341 :             double dfNumber = 0;
    1014        7341 :             memcpy(&dfNumber, pabyData + nIndexValue * 8, 8);
    1015             :             HFAStandard(8, &dfNumber);
    1016        7341 :             dfDoubleRet = dfNumber;
    1017        7341 :             if (chReqType == 'i')
    1018             :             {
    1019           0 :                 if (dfNumber > std::numeric_limits<int>::max() ||
    1020           0 :                     dfNumber < std::numeric_limits<int>::min() ||
    1021           0 :                     std::isnan(dfNumber))
    1022             :                 {
    1023           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1024             :                              "Too large for int: %f", dfNumber);
    1025           0 :                     return false;
    1026             :                 }
    1027           0 :                 nIntRet = static_cast<int>(dfNumber);
    1028             :             }
    1029             :         }
    1030        7341 :         break;
    1031             : 
    1032         135 :         case 'b':
    1033             :         {
    1034         135 :             if (nDataSize < 12)
    1035           0 :                 return false;
    1036             : 
    1037         135 :             GInt32 nRows = 0;
    1038         135 :             memcpy(&nRows, pabyData, 4);
    1039             :             HFAStandard(4, &nRows);
    1040             : 
    1041         135 :             GInt32 nColumns = 0;
    1042         135 :             memcpy(&nColumns, pabyData + 4, 4);
    1043             :             HFAStandard(4, &nColumns);
    1044             : 
    1045         135 :             GInt16 nBaseItemType = 0;
    1046         135 :             memcpy(&nBaseItemType, pabyData + 8, 2);
    1047             :             HFAStandard(2, &nBaseItemType);
    1048             :             // We ignore the 2 byte objecttype value.
    1049             : 
    1050         135 :             if (nIndexValue < -3 || nRows <= 0 || nColumns <= 0 ||
    1051         135 :                 nRows > INT_MAX / nColumns || nIndexValue >= nRows * nColumns)
    1052           0 :                 return false;
    1053             : 
    1054         135 :             pabyData += 12;
    1055         135 :             nDataSize -= 12;
    1056             : 
    1057         135 :             if (nIndexValue == -3)
    1058             :             {
    1059           0 :                 dfDoubleRet = nBaseItemType;
    1060           0 :                 nIntRet = nBaseItemType;
    1061             :             }
    1062         135 :             else if (nIndexValue == -2)
    1063             :             {
    1064           0 :                 dfDoubleRet = nColumns;
    1065           0 :                 nIntRet = nColumns;
    1066             :             }
    1067         135 :             else if (nIndexValue == -1)
    1068             :             {
    1069           0 :                 dfDoubleRet = nRows;
    1070           0 :                 nIntRet = nRows;
    1071             :             }
    1072         135 :             else if (nBaseItemType == EPT_u1)
    1073             :             {
    1074             :                 // TODO(schwehr): What are these constants like 8 and 0x7?
    1075           0 :                 if (nIndexValue * 8 >= nDataSize)
    1076             :                 {
    1077           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1078           0 :                     return false;
    1079             :                 }
    1080             : 
    1081           0 :                 if (pabyData[nIndexValue >> 3] & (1 << (nIndexValue & 0x7)))
    1082             :                 {
    1083           0 :                     dfDoubleRet = 1;
    1084           0 :                     nIntRet = 1;
    1085             :                 }
    1086             :                 else
    1087             :                 {
    1088           0 :                     dfDoubleRet = 0.0;
    1089           0 :                     nIntRet = 0;
    1090             :                 }
    1091             :             }
    1092         135 :             else if (nBaseItemType == EPT_u2)
    1093             :             {
    1094           0 :                 const int nBitOffset = nIndexValue & 0x3;
    1095           0 :                 const int nByteOffset = nIndexValue >> 2;
    1096             : 
    1097           0 :                 if (nByteOffset >= nDataSize)
    1098             :                 {
    1099           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1100           0 :                     return false;
    1101             :                 }
    1102             : 
    1103           0 :                 const int nMask = 0x3;
    1104           0 :                 nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
    1105           0 :                 dfDoubleRet = nIntRet;
    1106             :             }
    1107         135 :             else if (nBaseItemType == EPT_u4)
    1108             :             {
    1109           0 :                 const int nBitOffset = nIndexValue & 0x7;
    1110           0 :                 const int nByteOffset = nIndexValue >> 3;
    1111             : 
    1112           0 :                 if (nByteOffset >= nDataSize)
    1113             :                 {
    1114           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1115           0 :                     return false;
    1116             :                 }
    1117             : 
    1118           0 :                 const int nMask = 0x7;
    1119           0 :                 nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
    1120           0 :                 dfDoubleRet = nIntRet;
    1121             :             }
    1122         135 :             else if (nBaseItemType == EPT_u8)
    1123             :             {
    1124          18 :                 if (nIndexValue >= nDataSize)
    1125             :                 {
    1126           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1127           0 :                     return false;
    1128             :                 }
    1129          18 :                 dfDoubleRet = pabyData[nIndexValue];
    1130          18 :                 nIntRet = pabyData[nIndexValue];
    1131             :             }
    1132         117 :             else if (nBaseItemType == EPT_s8)
    1133             :             {
    1134           0 :                 if (nIndexValue >= nDataSize)
    1135             :                 {
    1136           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1137           0 :                     return false;
    1138             :                 }
    1139           0 :                 dfDoubleRet = ((signed char *)pabyData)[nIndexValue];
    1140           0 :                 nIntRet = ((signed char *)pabyData)[nIndexValue];
    1141             :             }
    1142         117 :             else if (nBaseItemType == EPT_s16)
    1143             :             {
    1144           0 :                 if (nIndexValue * 2 + 2 > nDataSize)
    1145             :                 {
    1146           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1147           0 :                     return false;
    1148             :                 }
    1149           0 :                 GInt16 nValue = 0;
    1150           0 :                 memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
    1151             :                 HFAStandard(2, &nValue);
    1152             : 
    1153           0 :                 dfDoubleRet = nValue;
    1154           0 :                 nIntRet = nValue;
    1155             :             }
    1156         117 :             else if (nBaseItemType == EPT_u16)
    1157             :             {
    1158           0 :                 if (nIndexValue * 2 + 2 > nDataSize)
    1159             :                 {
    1160           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1161           0 :                     return false;
    1162             :                 }
    1163           0 :                 GUInt16 nValue = 0;
    1164           0 :                 memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
    1165             :                 HFAStandard(2, &nValue);
    1166             : 
    1167           0 :                 dfDoubleRet = nValue;
    1168           0 :                 nIntRet = nValue;
    1169             :             }
    1170         117 :             else if (nBaseItemType == EPT_s32)
    1171             :             {
    1172           0 :                 if (nIndexValue * 4 + 4 > nDataSize)
    1173             :                 {
    1174           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1175           0 :                     return false;
    1176             :                 }
    1177           0 :                 GInt32 nValue = 0;
    1178           0 :                 memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
    1179             :                 HFAStandard(4, &nValue);
    1180             : 
    1181           0 :                 dfDoubleRet = nValue;
    1182           0 :                 nIntRet = nValue;
    1183             :             }
    1184         117 :             else if (nBaseItemType == EPT_u32)
    1185             :             {
    1186           0 :                 if (nIndexValue * 4 + 4 > nDataSize)
    1187             :                 {
    1188           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1189           0 :                     return false;
    1190             :                 }
    1191           0 :                 GUInt32 nValue = 0;
    1192           0 :                 memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
    1193             :                 HFAStandard(4, &nValue);
    1194             : 
    1195           0 :                 dfDoubleRet = nValue;
    1196           0 :                 nIntRet = nValue;
    1197             :             }
    1198         117 :             else if (nBaseItemType == EPT_f32)
    1199             :             {
    1200           0 :                 if (nIndexValue * 4 + 4 > nDataSize)
    1201             :                 {
    1202           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1203           0 :                     return false;
    1204             :                 }
    1205           0 :                 float fValue = 0.0f;
    1206           0 :                 memcpy(&fValue, pabyData + 4 * nIndexValue, 4);
    1207             :                 HFAStandard(4, &fValue);
    1208             : 
    1209           0 :                 dfDoubleRet = fValue;
    1210           0 :                 nIntRet = FloatToIntClamp(fValue);
    1211             :             }
    1212         117 :             else if (nBaseItemType == EPT_f64)
    1213             :             {
    1214         117 :                 if (nIndexValue * 8 + 8 > nDataSize)
    1215             :                 {
    1216           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1217           0 :                     return false;
    1218             :                 }
    1219         117 :                 double dfValue = 0.0;
    1220         117 :                 memcpy(&dfValue, pabyData + 8 * nIndexValue, 8);
    1221             :                 HFAStandard(8, &dfValue);
    1222             : 
    1223         117 :                 dfDoubleRet = dfValue;
    1224         117 :                 if (chReqType == 'i')
    1225             :                 {
    1226           0 :                     const int nMax = std::numeric_limits<int>::max();
    1227           0 :                     const int nMin = std::numeric_limits<int>::min();
    1228           0 :                     if (dfDoubleRet >= nMax)
    1229             :                     {
    1230           0 :                         nIntRet = nMax;
    1231             :                     }
    1232           0 :                     else if (dfDoubleRet <= nMin)
    1233             :                     {
    1234           0 :                         nIntRet = nMin;
    1235             :                     }
    1236           0 :                     else if (std::isnan(dfDoubleRet))
    1237             :                     {
    1238           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1239             :                                  "NaN converted to INT_MAX.");
    1240           0 :                         nIntRet = nMax;
    1241             :                     }
    1242             :                     else
    1243             :                     {
    1244           0 :                         nIntRet = static_cast<int>(dfDoubleRet);
    1245             :                     }
    1246             :                 }
    1247             :             }
    1248             :             else
    1249             :             {
    1250           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1251             :                          "Unknown base item type: %d", nBaseItemType);
    1252           0 :                 return false;
    1253             :             }
    1254             :         }
    1255         135 :         break;
    1256             : 
    1257       13953 :         case 'o':
    1258       13953 :             if (poItemObjectType != nullptr)
    1259             :             {
    1260       13953 :                 int nExtraOffset = 0;
    1261             : 
    1262       13953 :                 if (poItemObjectType->nBytes > 0)
    1263             :                 {
    1264       11400 :                     if (nIndexValue != 0 &&
    1265        9264 :                         poItemObjectType->nBytes > INT_MAX / nIndexValue)
    1266             :                         // TODO(schwehr): Why was this CE_Failure when the
    1267             :                         // others are false?
    1268           0 :                         return false;
    1269       11400 :                     nExtraOffset = poItemObjectType->nBytes * nIndexValue;
    1270             :                 }
    1271             :                 else
    1272             :                 {
    1273        2570 :                     for (int iIndexCounter = 0; iIndexCounter < nIndexValue &&
    1274             :                                                 nExtraOffset < nDataSize;
    1275             :                          iIndexCounter++)
    1276             :                     {
    1277          17 :                         std::set<HFAField *> oVisitedFields;
    1278          34 :                         const int nInc = poItemObjectType->GetInstBytes(
    1279          17 :                             pabyData + nExtraOffset, nDataSize - nExtraOffset,
    1280             :                             oVisitedFields);
    1281          17 :                         if (nInc <= 0 || nExtraOffset > INT_MAX - nInc)
    1282             :                         {
    1283           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    1284             :                                      "Invalid return value");
    1285             :                             // TODO(schwehr): Verify this false is okay.
    1286           0 :                             return false;
    1287             :                         }
    1288             : 
    1289          17 :                         nExtraOffset += nInc;
    1290             :                     }
    1291             :                 }
    1292             : 
    1293       13953 :                 if (nExtraOffset >= nDataSize)
    1294           0 :                     return false;
    1295             : 
    1296       13953 :                 pabyRawData = pabyData + nExtraOffset;
    1297             : 
    1298       13953 :                 if (pszField != nullptr && strlen(pszField) > 0)
    1299             :                 {
    1300       27906 :                     return poItemObjectType->ExtractInstValue(
    1301       13953 :                         pszField, pabyRawData, nDataOffset + nExtraOffset,
    1302             :                         nDataSize - nExtraOffset, chReqType, pReqReturn,
    1303       13953 :                         pnRemainingDataSize);
    1304             :                 }
    1305             :             }
    1306             :             else
    1307             :             {
    1308             :                 // E. Rouault: not completely sure about this, but helps avoid
    1309             :                 // DoS timeouts in cases like
    1310             :                 // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=1806
    1311           0 :                 return false;
    1312             :             }
    1313           0 :             break;
    1314             : 
    1315           0 :         default:
    1316           0 :             return false;
    1317             :     }
    1318             : 
    1319             :     // Return the appropriate representation.
    1320       30023 :     if (chReqType == 's')
    1321             :     {
    1322        1104 :         if (pszStringRet == nullptr)
    1323             :         {
    1324             :             // HFAEntry:: BuildEntryFromMIFObject() expects to have always 8
    1325             :             // bytes before the data. In normal situations, it should not go
    1326             :             // here, but that can happen if the file is corrupted so reserve the
    1327             :             // first 8 bytes before the string to contain null bytes.
    1328          39 :             memset(szNumberString, 0, 8);
    1329          39 :             CPLsnprintf(szNumberString + 8, sizeof(szNumberString) - 8, "%.14g",
    1330             :                         dfDoubleRet);
    1331          39 :             pszStringRet = szNumberString + 8;
    1332             :         }
    1333             : 
    1334        1104 :         *((char **)pReqReturn) = pszStringRet;
    1335        1104 :         return true;
    1336             :     }
    1337       28919 :     else if (chReqType == 'd')
    1338             :     {
    1339        7437 :         *((double *)pReqReturn) = dfDoubleRet;
    1340        7437 :         return true;
    1341             :     }
    1342       21482 :     else if (chReqType == 'i')
    1343             :     {
    1344       21482 :         *((int *)pReqReturn) = nIntRet;
    1345       21482 :         return true;
    1346             :     }
    1347           0 :     else if (chReqType == 'p')
    1348             :     {
    1349           0 :         *((GByte **)pReqReturn) = pabyRawData;
    1350           0 :         return true;
    1351             :     }
    1352             :     else
    1353             :     {
    1354           0 :         CPLAssert(false);
    1355             :         return false;
    1356             :     }
    1357             : }
    1358             : 
    1359             : /************************************************************************/
    1360             : /*                            GetInstBytes()                            */
    1361             : /*                                                                      */
    1362             : /*      Get the number of bytes in a particular instance of a           */
    1363             : /*      field.  This will normally be the fixed internal nBytes         */
    1364             : /*      value, but for pointer objects will include the variable        */
    1365             : /*      portion.                                                        */
    1366             : /************************************************************************/
    1367             : 
    1368      168907 : int HFAField::GetInstBytes(GByte *pabyData, int nDataSize,
    1369             :                            std::set<HFAField *> &oVisitedFields)
    1370             : 
    1371             : {
    1372      168907 :     if (oVisitedFields.find(this) != oVisitedFields.end())
    1373             :     {
    1374           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1375           0 :         return -1;
    1376             :     }
    1377             : 
    1378      168907 :     if (nBytes > -1)
    1379      135505 :         return nBytes;
    1380             : 
    1381       33402 :     int nCount = 1;
    1382       33402 :     int nInstBytes = 0;
    1383             : 
    1384       33402 :     if (chPointer != '\0')
    1385             :     {
    1386       30966 :         if (nDataSize < 4)
    1387             :         {
    1388           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1389           0 :             return -1;
    1390             :         }
    1391             : 
    1392       30966 :         memcpy(&nCount, pabyData, 4);
    1393             :         HFAStandard(4, &nCount);
    1394             : 
    1395       30966 :         pabyData += 8;
    1396       30966 :         nInstBytes += 8;
    1397             :     }
    1398             : 
    1399       33402 :     if (chItemType == 'b' && nCount != 0)  // BASEDATA
    1400             :     {
    1401         515 :         if (nDataSize - nInstBytes < 4 + 4 + 2)
    1402             :         {
    1403           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1404           0 :             return -1;
    1405             :         }
    1406             : 
    1407         515 :         GInt32 nRows = 0;
    1408         515 :         memcpy(&nRows, pabyData, 4);
    1409             :         HFAStandard(4, &nRows);
    1410         515 :         GInt32 nColumns = 0;
    1411         515 :         memcpy(&nColumns, pabyData + 4, 4);
    1412             :         HFAStandard(4, &nColumns);
    1413         515 :         GInt16 nBaseItemType = 0;
    1414         515 :         memcpy(&nBaseItemType, pabyData + 8, 2);
    1415             :         HFAStandard(2, &nBaseItemType);
    1416         515 :         if (nBaseItemType < EPT_MIN || nBaseItemType > EPT_MAX)
    1417           0 :             return -1;
    1418             : 
    1419         515 :         EPTType eBaseItemType = static_cast<EPTType>(nBaseItemType);
    1420             : 
    1421         515 :         nInstBytes += 12;
    1422             : 
    1423         515 :         if (nRows < 0 || nColumns < 0)
    1424           0 :             return -1;
    1425         515 :         if (nColumns != 0 && nRows > INT_MAX / nColumns)
    1426           0 :             return -1;
    1427        1022 :         if (nRows != 0 &&
    1428         507 :             ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) > INT_MAX / nRows)
    1429           0 :             return -1;
    1430        1022 :         if (nColumns != 0 &&
    1431         507 :             ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows >
    1432         507 :                 INT_MAX / nColumns)
    1433           0 :             return -1;
    1434         515 :         if (((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns >
    1435         515 :             INT_MAX - nInstBytes)
    1436           0 :             return -1;
    1437             : 
    1438         515 :         nInstBytes +=
    1439         515 :             ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns;
    1440             :     }
    1441       32887 :     else if (poItemObjectType == nullptr)
    1442             :     {
    1443       49442 :         if (nCount != 0 &&
    1444       20004 :             HFADictionary::GetItemSize(chItemType) > INT_MAX / nCount)
    1445           0 :             return -1;
    1446       29438 :         if (nCount * HFADictionary::GetItemSize(chItemType) >
    1447       29438 :             INT_MAX - nInstBytes)
    1448           0 :             return -1;
    1449       29438 :         nInstBytes += nCount * HFADictionary::GetItemSize(chItemType);
    1450             :     }
    1451             :     else
    1452             :     {
    1453        3449 :         oVisitedFields.insert(this);
    1454        5939 :         for (int i = 0; i < nCount && nInstBytes < nDataSize && nInstBytes >= 0;
    1455             :              i++)
    1456             :         {
    1457        2490 :             const int nThisBytes = poItemObjectType->GetInstBytes(
    1458             :                 pabyData, nDataSize - nInstBytes, oVisitedFields);
    1459        2490 :             if (nThisBytes <= 0 || nInstBytes > INT_MAX - nThisBytes)
    1460             :             {
    1461           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value");
    1462           0 :                 return -1;
    1463             :             }
    1464             : 
    1465        2490 :             nInstBytes += nThisBytes;
    1466        2490 :             pabyData += nThisBytes;
    1467             :         }
    1468        3449 :         oVisitedFields.erase(this);
    1469             :     }
    1470             : 
    1471       33402 :     return nInstBytes;
    1472             : }
    1473             : 
    1474             : /************************************************************************/
    1475             : /*                            GetInstCount()                            */
    1476             : /*                                                                      */
    1477             : /*      Get the count for a particular instance of a field.  This       */
    1478             : /*      will normally be the built in value, but for variable fields    */
    1479             : /*      this is extracted from the data itself.                         */
    1480             : /************************************************************************/
    1481             : 
    1482       47499 : int HFAField::GetInstCount(GByte *pabyData, int nDataSize) const
    1483             : 
    1484             : {
    1485       47499 :     if (chPointer == '\0')
    1486       27465 :         return nItemCount;
    1487             : 
    1488       20034 :     if (chItemType == 'b')
    1489             :     {
    1490         213 :         if (nDataSize < 20)
    1491           0 :             return 0;
    1492             : 
    1493         213 :         GInt32 nRows = 0;
    1494         213 :         memcpy(&nRows, pabyData + 8, 4);
    1495             :         HFAStandard(4, &nRows);
    1496         213 :         GInt32 nColumns = 0;
    1497         213 :         memcpy(&nColumns, pabyData + 12, 4);
    1498             :         HFAStandard(4, &nColumns);
    1499             : 
    1500         213 :         if (nRows < 0 || nColumns < 0)
    1501           0 :             return 0;
    1502         213 :         if (nColumns != 0 && nRows > INT_MAX / nColumns)
    1503           0 :             return 0;
    1504             : 
    1505         213 :         return nRows * nColumns;
    1506             :     }
    1507             : 
    1508       19821 :     if (nDataSize < 4)
    1509           0 :         return 0;
    1510             : 
    1511       19821 :     GInt32 nCount = 0;
    1512       19821 :     memcpy(&nCount, pabyData, 4);
    1513             :     HFAStandard(4, &nCount);
    1514       19821 :     return nCount;
    1515             : }
    1516             : 
    1517             : /************************************************************************/
    1518             : /*                           DumpInstValue()                            */
    1519             : /************************************************************************/
    1520             : 
    1521           0 : void HFAField::DumpInstValue(FILE *fpOut, GByte *pabyData, GUInt32 nDataOffset,
    1522             :                              int nDataSize, const char *pszPrefix)
    1523             : 
    1524             : {
    1525           0 :     const int nEntries = GetInstCount(pabyData, nDataSize);
    1526             : 
    1527             :     // Special case for arrays of chars or uchars which are printed
    1528             :     // as a string.
    1529           0 :     if ((chItemType == 'c' || chItemType == 'C') && nEntries > 0)
    1530             :     {
    1531           0 :         void *pReturn = nullptr;
    1532           0 :         if (ExtractInstValue(nullptr, 0, pabyData, nDataOffset, nDataSize, 's',
    1533             :                              &pReturn))
    1534           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = `%s'\n", pszPrefix,
    1535             :                                           pszFieldName,
    1536             :                                           static_cast<char *>(pReturn)));
    1537             :         else
    1538           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = (access failed)\n",
    1539             :                                           pszPrefix, pszFieldName));
    1540             : 
    1541           0 :         return;
    1542             :     }
    1543             : 
    1544             :     // For BASEDATA objects, we want to first dump their dimension and type.
    1545           0 :     if (chItemType == 'b')
    1546             :     {
    1547           0 :         int nDataType = 0;
    1548           0 :         const bool bSuccess = ExtractInstValue(
    1549             :             nullptr, -3, pabyData, nDataOffset, nDataSize, 'i', &nDataType);
    1550           0 :         if (bSuccess)
    1551             :         {
    1552           0 :             int nColumns = 0;
    1553           0 :             ExtractInstValue(nullptr, -2, pabyData, nDataOffset, nDataSize, 'i',
    1554             :                              &nColumns);
    1555           0 :             int nRows = 0;
    1556           0 :             ExtractInstValue(nullptr, -1, pabyData, nDataOffset, nDataSize, 'i',
    1557             :                              &nRows);
    1558           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(
    1559             :                 fpOut, "%sBASEDATA(%s): %dx%d of %s\n", pszPrefix, pszFieldName,
    1560             :                 nColumns, nRows,
    1561           0 :                 (nDataType >= EPT_MIN && nDataType <= EPT_MAX)
    1562           0 :                     ? HFAGetDataTypeName(static_cast<EPTType>(nDataType))
    1563             :                     : "invalid type"));
    1564             :         }
    1565             :         else
    1566             :         {
    1567           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%sBASEDATA(%s): empty\n",
    1568             :                                           pszPrefix, pszFieldName));
    1569             :         }
    1570             :     }
    1571             : 
    1572             :     // Dump each entry in the field array.
    1573           0 :     void *pReturn = nullptr;
    1574             : 
    1575           0 :     const int nMaxEntry = std::min(MAX_ENTRY_REPORT, nEntries);
    1576           0 :     for (int iEntry = 0; iEntry < nMaxEntry; iEntry++)
    1577             :     {
    1578           0 :         if (nEntries == 1)
    1579           0 :             CPL_IGNORE_RET_VAL(
    1580           0 :                 VSIFPrintf(fpOut, "%s%s = ", pszPrefix, pszFieldName));
    1581             :         else
    1582           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s[%d] = ", pszPrefix,
    1583             :                                           pszFieldName, iEntry));
    1584             : 
    1585           0 :         switch (chItemType)
    1586             :         {
    1587           0 :             case 'f':
    1588             :             case 'd':
    1589             :             {
    1590           0 :                 double dfValue = 0.0;
    1591           0 :                 if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1592             :                                      nDataSize, 'd', &dfValue))
    1593           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%f\n", dfValue));
    1594             :                 else
    1595           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
    1596             :             }
    1597           0 :             break;
    1598             : 
    1599           0 :             case 'b':
    1600             :             {
    1601           0 :                 double dfValue = 0.0;
    1602             : 
    1603           0 :                 if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1604             :                                      nDataSize, 'd', &dfValue))
    1605           0 :                     CPL_IGNORE_RET_VAL(
    1606           0 :                         VSIFPrintf(fpOut, "%s%.15g\n", pszPrefix, dfValue));
    1607             :                 else
    1608           0 :                     CPL_IGNORE_RET_VAL(
    1609           0 :                         VSIFPrintf(fpOut, "%s(access failed)\n", pszPrefix));
    1610             :             }
    1611           0 :             break;
    1612             : 
    1613           0 :             case 'e':
    1614           0 :                 if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1615             :                                      nDataSize, 's', &pReturn))
    1616           0 :                     CPL_IGNORE_RET_VAL(
    1617           0 :                         VSIFPrintf(fpOut, "%s\n", (char *)pReturn));
    1618             :                 else
    1619           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
    1620           0 :                 break;
    1621             : 
    1622           0 :             case 'o':
    1623           0 :                 if (!ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1624             :                                       nDataSize, 'p', &pReturn))
    1625             :                 {
    1626           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
    1627             :                 }
    1628             :                 else
    1629             :                 {
    1630           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "\n"));
    1631             : 
    1632           0 :                     const int nByteOffset =
    1633           0 :                         static_cast<int>(((GByte *)pReturn) - pabyData);
    1634             : 
    1635           0 :                     char szLongFieldName[256] = {};
    1636           0 :                     snprintf(szLongFieldName, sizeof(szLongFieldName), "%s    ",
    1637             :                              pszPrefix);
    1638             : 
    1639           0 :                     if (poItemObjectType)
    1640           0 :                         poItemObjectType->DumpInstValue(
    1641           0 :                             fpOut, pabyData + nByteOffset,
    1642           0 :                             nDataOffset + nByteOffset, nDataSize - nByteOffset,
    1643             :                             szLongFieldName);
    1644             :                 }
    1645           0 :                 break;
    1646             : 
    1647           0 :             default:
    1648             :             {
    1649           0 :                 GInt32 nIntValue = 0;
    1650             : 
    1651           0 :                 if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1652             :                                      nDataSize, 'i', &nIntValue))
    1653           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%d\n", nIntValue));
    1654             :                 else
    1655           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
    1656             :             }
    1657           0 :             break;
    1658             :         }
    1659             :     }
    1660             : 
    1661           0 :     if (nEntries > MAX_ENTRY_REPORT)
    1662           0 :         CPL_IGNORE_RET_VAL(VSIFPrintf(
    1663             :             fpOut, "%s ... remaining instances omitted ...\n", pszPrefix));
    1664             : 
    1665           0 :     if (nEntries == 0)
    1666           0 :         CPL_IGNORE_RET_VAL(
    1667           0 :             VSIFPrintf(fpOut, "%s%s = (no values)\n", pszPrefix, pszFieldName));
    1668             : }

Generated by: LCOV version 1.14