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-08-19 18:03:11 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      105337 : HFAField::HFAField()
      62             :     : nBytes(0), nItemCount(0), chPointer('\0'), chItemType('\0'),
      63             :       pszItemObjectType(nullptr), poItemObjectType(nullptr),
      64      105337 :       papszEnumNames(nullptr), pszFieldName(nullptr)
      65             : {
      66      105337 :     memset(szNumberString, 0, sizeof(szNumberString));
      67      105337 : }
      68             : 
      69             : /************************************************************************/
      70             : /*                             ~HFAField()                              */
      71             : /************************************************************************/
      72             : 
      73      210674 : HFAField::~HFAField()
      74             : 
      75             : {
      76      105337 :     CPLFree(pszItemObjectType);
      77      105337 :     CSLDestroy(papszEnumNames);
      78      105337 :     CPLFree(pszFieldName);
      79      105337 : }
      80             : 
      81             : /************************************************************************/
      82             : /*                             Initialize()                             */
      83             : /************************************************************************/
      84             : 
      85      105337 : const char *HFAField::Initialize(const char *pszInput)
      86             : 
      87             : {
      88             :     // Read the number.
      89      105337 :     nItemCount = atoi(pszInput);
      90      105337 :     if (nItemCount < 0)
      91           0 :         return nullptr;
      92             : 
      93      212915 :     while (*pszInput != '\0' && *pszInput != ':')
      94      107578 :         pszInput++;
      95             : 
      96      105337 :     if (*pszInput == '\0')
      97           0 :         return nullptr;
      98             : 
      99      105337 :     pszInput++;
     100             : 
     101             :     // Is this a pointer?
     102      105337 :     if (*pszInput == 'p' || *pszInput == '*')
     103       24169 :         chPointer = *(pszInput++);
     104             : 
     105             :     // Get the general type.
     106      105337 :     if (*pszInput == '\0')
     107           0 :         return nullptr;
     108             : 
     109      105337 :     chItemType = *(pszInput++);
     110             : 
     111      105337 :     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      105336 :     int i = 0;  // TODO: Describe why i needs to span chItemType blocks.
     120             : 
     121      105336 :     if (chItemType == 'o')
     122             :     {
     123      171030 :         for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
     124             :         {
     125             :         }
     126       12585 :         if (pszInput[i] == '\0')
     127           0 :             return nullptr;
     128             : 
     129       12585 :         pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
     130       12585 :         strncpy(pszItemObjectType, pszInput, i);
     131       12585 :         pszItemObjectType[i] = '\0';
     132             : 
     133       12585 :         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      105336 :     if (chItemType == 'x' && *pszInput == '{')
     143             :     {
     144        2862 :         int nBraceDepth = 1;
     145        2862 :         pszInput++;
     146             : 
     147             :         // Skip past the definition.
     148       93318 :         while (nBraceDepth > 0 && *pszInput != '\0')
     149             :         {
     150       90456 :             if (*pszInput == '{')
     151        1433 :                 nBraceDepth++;
     152       89023 :             else if (*pszInput == '}')
     153        4295 :                 nBraceDepth--;
     154             : 
     155       90456 :             pszInput++;
     156             :         }
     157        2862 :         if (*pszInput == '\0')
     158           0 :             return nullptr;
     159             : 
     160        2862 :         chItemType = 'o';
     161             : 
     162             :         // Find the comma terminating the type name.
     163       36516 :         for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
     164             :         {
     165             :         }
     166        2862 :         if (pszInput[i] == '\0')
     167           0 :             return nullptr;
     168             : 
     169        2862 :         pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
     170        2862 :         strncpy(pszItemObjectType, pszInput, i);
     171        2862 :         pszItemObjectType[i] = '\0';
     172             : 
     173        2862 :         pszInput += i + 1;
     174             :     }
     175             : 
     176             :     // If this is an enumeration we have to extract all the
     177             :     // enumeration values.
     178      105336 :     if (chItemType == 'e')
     179             :     {
     180       14377 :         const int nEnumCount = atoi(pszInput);
     181             : 
     182       14377 :         if (nEnumCount < 0 || nEnumCount > 100000)
     183           0 :             return nullptr;
     184             : 
     185       14377 :         pszInput = strchr(pszInput, ':');
     186       14377 :         if (pszInput == nullptr)
     187           0 :             return nullptr;
     188             : 
     189       14377 :         pszInput++;
     190             : 
     191       14377 :         papszEnumNames =
     192       14377 :             static_cast<char **>(VSICalloc(sizeof(char *), nEnumCount + 1));
     193       14377 :         if (papszEnumNames == nullptr)
     194           0 :             return nullptr;
     195             : 
     196       81369 :         for (int iEnum = 0; iEnum < nEnumCount; iEnum++)
     197             :         {
     198      648178 :             for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
     199             :             {
     200             :             }
     201             : 
     202       66992 :             if (pszInput[i] != ',')
     203           0 :                 return nullptr;
     204             : 
     205       66992 :             char *pszToken = static_cast<char *>(CPLMalloc(i + 1));
     206       66992 :             strncpy(pszToken, pszInput, i);
     207       66992 :             pszToken[i] = '\0';
     208             : 
     209       66992 :             papszEnumNames[iEnum] = pszToken;
     210             : 
     211       66992 :             pszInput += i + 1;
     212             :         }
     213             :     }
     214             : 
     215             :     // Extract the field name.
     216     1017650 :     for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
     217             :     {
     218             :     }
     219      105336 :     if (pszInput[i] == '\0')
     220           0 :         return nullptr;
     221             : 
     222      105336 :     pszFieldName = static_cast<char *>(CPLMalloc(i + 1));
     223      105336 :     strncpy(pszFieldName, pszInput, i);
     224      105336 :     pszFieldName[i] = '\0';
     225             : 
     226      105336 :     pszInput += i + 1;
     227             : 
     228      105336 :     return pszInput;
     229             : }
     230             : 
     231             : /************************************************************************/
     232             : /*                            CompleteDefn()                            */
     233             : /*                                                                      */
     234             : /*      Establish size, and pointers to component types.                */
     235             : /************************************************************************/
     236             : 
     237      105330 : 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      105330 :     if (pszItemObjectType != nullptr)
     243       15441 :         poItemObjectType = poDict->FindType(pszItemObjectType);
     244             : 
     245             :     // Figure out the size.
     246      105330 :     if (chPointer == 'p')
     247             :     {
     248       16609 :         nBytes = -1;  // We can't know the instance size.
     249             :     }
     250       88721 :     else if (poItemObjectType != nullptr)
     251             :     {
     252       11755 :         if (!poItemObjectType->CompleteDefn(poDict))
     253           2 :             return false;
     254       11753 :         if (poItemObjectType->nBytes == -1)
     255        9548 :             nBytes = -1;
     256        2205 :         else if (poItemObjectType->nBytes != 0 &&
     257        2205 :                  nItemCount > INT_MAX / poItemObjectType->nBytes)
     258           0 :             nBytes = -1;
     259             :         else
     260        2205 :             nBytes = poItemObjectType->nBytes * nItemCount;
     261             : 
     262             :         // TODO(schwehr): What does the 8 represent?
     263       11753 :         if (chPointer == '*' && nBytes != -1)
     264             :         {
     265        2205 :             if (nBytes > INT_MAX - 8)
     266           0 :                 nBytes = -1;
     267             :             else
     268        2205 :                 nBytes += 8;  // Count, and offset.
     269             :         }
     270             :     }
     271             :     else
     272             :     {
     273       76966 :         const int nItemSize = poDict->GetItemSize(chItemType);
     274       76966 :         if (nItemSize != 0 && nItemCount > INT_MAX / nItemSize)
     275        3933 :             nBytes = -1;
     276             :         else
     277       73033 :             nBytes = nItemSize * nItemCount;
     278             :     }
     279      105328 :     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       14924 : 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       14924 :     if (chPointer != '\0')
     396             :     {
     397        7681 :         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        7681 :         if (chItemType == 'b')
     403             :         {
     404          53 :             nCount = 1;
     405             :         }
     406             :         // Set the size from string length.
     407        7628 :         else if (chReqType == 's' && (chItemType == 'c' || chItemType == 'C'))
     408             :         {
     409        1766 :             if (pValue != nullptr)
     410        1459 :                 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        5862 :             nCount = nIndexValue + 1;
     416             :         }
     417             : 
     418             :         // TODO(schwehr): What does the 8 represent?
     419        7681 :         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        7681 :         GUInt32 nOffset = 0;
     430        7681 :         memcpy(&nOffset, pabyData, 4);
     431             :         HFAStandard(4, &nOffset);
     432        7681 :         if (nOffset < nCount)
     433             :         {
     434        5933 :             nOffset = nCount;
     435             :             HFAStandard(4, &nOffset);
     436        5933 :             memcpy(pabyData, &nOffset, 4);
     437             :         }
     438             : 
     439        7681 :         if (pValue == nullptr)
     440         307 :             nOffset = 0;
     441             :         else
     442        7374 :             nOffset = nDataOffset + 8;
     443             :         HFAStandard(4, &nOffset);
     444        7681 :         memcpy(pabyData + 4, &nOffset, 4);
     445             : 
     446        7681 :         pabyData += 8;
     447             : 
     448        7681 :         nDataOffset += 8;
     449        7681 :         nDataSize -= 8;
     450             :     }
     451             : 
     452             :     // Pointers to char or uchar arrays requested as strings are
     453             :     // handled as a special case.
     454       14924 :     if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
     455             :     {
     456        1766 :         int nBytesToCopy = 0;
     457             : 
     458        1766 :         if (nBytes == -1)
     459             :         {
     460        1766 :             if (pValue != nullptr)
     461        1459 :                 nBytesToCopy = static_cast<int>(strlen((char *)pValue) + 1);
     462             :         }
     463             :         else
     464             :         {
     465           0 :             nBytesToCopy = nBytes;
     466             :         }
     467             : 
     468        1766 :         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        1766 :         memset(pabyData, 0, nBytesToCopy);
     478             : 
     479        1766 :         if (pValue != nullptr)
     480        1459 :             strncpy((char *)pabyData, (char *)pValue, nBytesToCopy);
     481             : 
     482        1766 :         return CE_None;
     483             :     }
     484             : 
     485             :     // Translate the passed type into different representations.
     486       13158 :     int nIntValue = 0;
     487       13158 :     double dfDoubleValue = 0.0;
     488             : 
     489       13158 :     if (chReqType == 's')
     490             :     {
     491        2114 :         CPLAssert(pValue != nullptr);
     492        2114 :         nIntValue = atoi((char *)pValue);
     493        2114 :         dfDoubleValue = CPLAtof((char *)pValue);
     494             :     }
     495       11044 :     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        3581 :     else if (chReqType == 'i')
     507             :     {
     508        3581 :         CPLAssert(pValue != nullptr);
     509        3581 :         nIntValue = *((int *)pValue);
     510        3581 :         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       13158 :     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        1507 :         case 'e':
     552             :         case 's':
     553             :         {
     554        1507 :             if (chItemType == 'e' && chReqType == 's')
     555             :             {
     556         926 :                 CPLAssert(pValue != nullptr);
     557         926 :                 nIntValue = CSLFindString(papszEnumNames, (char *)pValue);
     558         926 :                 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        1507 :             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        1507 :             unsigned short nNumber = static_cast<unsigned short>(nIntValue);
     579             :             // TODO(schwehr): What is this 2?
     580             :             HFAStandard(2, &nNumber);
     581        1507 :             memcpy(pabyData + nIndexValue * 2, &nNumber, 2);
     582             :         }
     583        1507 :         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        2314 :         case 't':
     605             :         case 'l':
     606             :         {
     607        2314 :             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        2314 :             GUInt32 nNumber = nIntValue;
     617             :             // TODO(schwehr): What is this 4?
     618             :             HFAStandard(4, &nNumber);
     619        2314 :             memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
     620             :         }
     621        2314 :         break;
     622             : 
     623         607 :         case 'L':
     624             :         {
     625         607 :             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         607 :             GInt32 nNumber = nIntValue;
     635             :             HFAStandard(4, &nNumber);
     636         607 :             memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
     637             :         }
     638         607 :         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        3019 :         case 'o':
     761        3019 :             if (poItemObjectType != nullptr)
     762             :             {
     763        3019 :                 int nExtraOffset = 0;
     764             : 
     765        3019 :                 if (poItemObjectType->nBytes > 0)
     766             :                 {
     767        1251 :                     if (nIndexValue != 0 &&
     768          49 :                         poItemObjectType->nBytes > INT_MAX / nIndexValue)
     769             :                     {
     770           0 :                         return CE_Failure;
     771             :                     }
     772        1251 :                     nExtraOffset = poItemObjectType->nBytes * nIndexValue;
     773             :                 }
     774             :                 else
     775             :                 {
     776        1777 :                     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        3019 :                 if (nExtraOffset >= nDataSize)
     796           1 :                     return CE_Failure;
     797             : 
     798        3018 :                 if (pszField != nullptr && strlen(pszField) > 0)
     799             :                 {
     800        6036 :                     return poItemObjectType->SetInstValue(
     801        3018 :                         pszField, pabyData + nExtraOffset,
     802        3018 :                         nDataOffset + nExtraOffset, nDataSize - nExtraOffset,
     803        3018 :                         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             :             break;
     817             :     }
     818             : 
     819       10138 :     return CE_None;
     820             : }
     821             : 
     822             : /************************************************************************/
     823             : /*                          ExtractInstValue()                          */
     824             : /*                                                                      */
     825             : /*      Extract the value of an instance of a field.                    */
     826             : /*                                                                      */
     827             : /*      pszField should be NULL if this field is not a                  */
     828             : /*      substructure.                                                   */
     829             : /************************************************************************/
     830             : 
     831       46505 : bool HFAField::ExtractInstValue(const char *pszField, int nIndexValue,
     832             :                                 GByte *pabyData, GUInt32 nDataOffset,
     833             :                                 int nDataSize, char chReqType, void *pReqReturn,
     834             :                                 int *pnRemainingDataSize)
     835             : 
     836             : {
     837       46505 :     const int nInstItemCount = GetInstCount(pabyData, nDataSize);
     838             : 
     839       46505 :     if (pnRemainingDataSize)
     840         424 :         *pnRemainingDataSize = -1;
     841             : 
     842             :     // Check the index value is valid.
     843             :     // Eventually this will have to account for variable fields.
     844       46505 :     if (nIndexValue < 0 || nIndexValue >= nInstItemCount)
     845             :     {
     846         566 :         if (chItemType == 'b' && nIndexValue >= -3 && nIndexValue < 0)
     847             :             /* ok - special index values */;
     848             :         else
     849         566 :             return false;
     850             :     }
     851             : 
     852             :     // If this field contains a pointer, then we will adjust the
     853             :     // data offset relative to it.
     854       45939 :     if (chPointer != '\0')
     855             :     {
     856       19187 :         if (nDataSize < 8)
     857             :         {
     858           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     859           0 :             return false;
     860             :         }
     861             : 
     862       19187 :         GUInt32 nOffset = 0;
     863       19187 :         memcpy(&nOffset, pabyData + 4, 4);
     864             :         HFAStandard(4, &nOffset);
     865             : 
     866             : #if DEBUG_VERBOSE
     867             :         if (nOffset != static_cast<GUInt32>(nDataOffset + 8))
     868             :         {
     869             :             // TODO(schwehr): Debug why this is happening.
     870             :             CPLError(CE_Warning, CPLE_AppDefined,
     871             :                      "ExtractInstValue: "
     872             :                      "%s.%s points at %d, not %d as expected",
     873             :                      pszFieldName, pszField ? pszField : "", nOffset,
     874             :                      nDataOffset + 8);
     875             :         }
     876             : #endif
     877             : 
     878       19187 :         pabyData += 8;
     879       19187 :         nDataOffset += 8;
     880       19187 :         nDataSize -= 8;
     881             :     }
     882             : 
     883             :     // Pointers to char or uchar arrays requested as strings are
     884             :     // handled as a special case.
     885       45939 :     if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
     886             :     {
     887        2018 :         *((GByte **)pReqReturn) = pabyData;
     888        2018 :         if (pnRemainingDataSize)
     889         212 :             *pnRemainingDataSize = nDataSize;
     890        2018 :         return pabyData != nullptr;
     891             :     }
     892             : 
     893             :     // Handle by type.
     894       43921 :     char *pszStringRet = nullptr;
     895       43921 :     int nIntRet = 0;
     896       43921 :     double dfDoubleRet = 0.0;
     897       43921 :     GByte *pabyRawData = nullptr;
     898             : 
     899       43921 :     switch (chItemType)
     900             :     {
     901           0 :         case 'c':
     902             :         case 'C':
     903           0 :             if (nIndexValue >= nDataSize)
     904             :             {
     905           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     906           0 :                 return false;
     907             :             }
     908           0 :             nIntRet = pabyData[nIndexValue];
     909           0 :             dfDoubleRet = nIntRet;
     910           0 :             break;
     911             : 
     912        8816 :         case 'e':
     913             :         case 's':
     914             :         {
     915        8816 :             if (nIndexValue * 2 + 2 > nDataSize)
     916             :             {
     917           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     918           0 :                 return false;
     919             :             }
     920        8816 :             unsigned short nNumber = 0;
     921        8816 :             memcpy(&nNumber, pabyData + nIndexValue * 2, 2);
     922             :             HFAStandard(2, &nNumber);
     923        8816 :             nIntRet = nNumber;
     924        8816 :             dfDoubleRet = nIntRet;
     925             : 
     926       17632 :             if (chItemType == 'e' &&
     927        8816 :                 nNumber < static_cast<unsigned>(CSLCount(papszEnumNames)))
     928             :             {
     929        8815 :                 pszStringRet = papszEnumNames[nNumber];
     930             :             }
     931             :         }
     932        8816 :         break;
     933             : 
     934           0 :         case 'S':
     935             :         {
     936           0 :             if (nIndexValue * 2 + 2 > nDataSize)
     937             :             {
     938           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     939           0 :                 return false;
     940             :             }
     941           0 :             short nNumber = 0;
     942           0 :             memcpy(&nNumber, pabyData + nIndexValue * 2, 2);
     943             :             HFAStandard(2, &nNumber);
     944           0 :             nIntRet = nNumber;
     945           0 :             dfDoubleRet = nIntRet;
     946             :         }
     947           0 :         break;
     948             : 
     949       10613 :         case 't':
     950             :         case 'l':
     951             :         {
     952       10613 :             if (nIndexValue * 4 + 4 > nDataSize)
     953             :             {
     954           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     955           0 :                 return false;
     956             :             }
     957       10613 :             GUInt32 nNumber = 0;
     958       10613 :             memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
     959             :             HFAStandard(4, &nNumber);
     960       10613 :             nIntRet = nNumber;
     961       10613 :             dfDoubleRet = nIntRet;
     962             :         }
     963       10613 :         break;
     964             : 
     965        3070 :         case 'L':
     966             :         {
     967        3070 :             if (nIndexValue * 4 + 4 > nDataSize)
     968             :             {
     969           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     970           0 :                 return false;
     971             :             }
     972        3070 :             GInt32 nNumber = 0;
     973             :             // TODO(schwehr): What is 4?
     974        3070 :             memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
     975             :             HFAStandard(4, &nNumber);
     976        3070 :             nIntRet = nNumber;
     977        3070 :             dfDoubleRet = nIntRet;
     978             :         }
     979        3070 :         break;
     980             : 
     981           0 :         case 'f':
     982             :         {
     983           0 :             if (nIndexValue * 4 + 4 > nDataSize)
     984             :             {
     985           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
     986           0 :                 return false;
     987             :             }
     988           0 :             float fNumber = 0.0f;
     989             :             // TODO(schwehr): What is 4?
     990           0 :             memcpy(&fNumber, pabyData + nIndexValue * 4, 4);
     991             :             HFAStandard(4, &fNumber);
     992           0 :             if (static_cast<double>(fNumber) >
     993           0 :                     std::numeric_limits<int>::max() ||
     994           0 :                 static_cast<double>(fNumber) <
     995           0 :                     std::numeric_limits<int>::min() ||
     996           0 :                 std::isnan(fNumber))
     997             :             {
     998           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too large for int: %f",
     999             :                          fNumber);
    1000           0 :                 return false;
    1001             :             }
    1002           0 :             dfDoubleRet = fNumber;
    1003           0 :             nIntRet = static_cast<int>(fNumber);
    1004             :         }
    1005           0 :         break;
    1006             : 
    1007        7341 :         case 'd':
    1008             :         {
    1009        7341 :             if (nIndexValue * 8 + 8 > nDataSize)
    1010             :             {
    1011           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1012           0 :                 return false;
    1013             :             }
    1014        7341 :             double dfNumber = 0;
    1015        7341 :             memcpy(&dfNumber, pabyData + nIndexValue * 8, 8);
    1016             :             HFAStandard(8, &dfNumber);
    1017        7341 :             dfDoubleRet = dfNumber;
    1018        7341 :             if (chReqType == 'i')
    1019             :             {
    1020           0 :                 if (dfNumber > std::numeric_limits<int>::max() ||
    1021           0 :                     dfNumber < std::numeric_limits<int>::min() ||
    1022           0 :                     std::isnan(dfNumber))
    1023             :                 {
    1024           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1025             :                              "Too large for int: %f", dfNumber);
    1026           0 :                     return false;
    1027             :                 }
    1028           0 :                 nIntRet = static_cast<int>(dfNumber);
    1029             :             }
    1030             :         }
    1031        7341 :         break;
    1032             : 
    1033         135 :         case 'b':
    1034             :         {
    1035         135 :             if (nDataSize < 12)
    1036           0 :                 return false;
    1037             : 
    1038         135 :             GInt32 nRows = 0;
    1039         135 :             memcpy(&nRows, pabyData, 4);
    1040             :             HFAStandard(4, &nRows);
    1041             : 
    1042         135 :             GInt32 nColumns = 0;
    1043         135 :             memcpy(&nColumns, pabyData + 4, 4);
    1044             :             HFAStandard(4, &nColumns);
    1045             : 
    1046         135 :             GInt16 nBaseItemType = 0;
    1047         135 :             memcpy(&nBaseItemType, pabyData + 8, 2);
    1048             :             HFAStandard(2, &nBaseItemType);
    1049             :             // We ignore the 2 byte objecttype value.
    1050             : 
    1051         135 :             if (nIndexValue < -3 || nRows <= 0 || nColumns <= 0 ||
    1052         135 :                 nRows > INT_MAX / nColumns || nIndexValue >= nRows * nColumns)
    1053           0 :                 return false;
    1054             : 
    1055         135 :             pabyData += 12;
    1056         135 :             nDataSize -= 12;
    1057             : 
    1058         135 :             if (nIndexValue == -3)
    1059             :             {
    1060           0 :                 dfDoubleRet = nBaseItemType;
    1061           0 :                 nIntRet = nBaseItemType;
    1062             :             }
    1063         135 :             else if (nIndexValue == -2)
    1064             :             {
    1065           0 :                 dfDoubleRet = nColumns;
    1066           0 :                 nIntRet = nColumns;
    1067             :             }
    1068         135 :             else if (nIndexValue == -1)
    1069             :             {
    1070           0 :                 dfDoubleRet = nRows;
    1071           0 :                 nIntRet = nRows;
    1072             :             }
    1073         135 :             else if (nBaseItemType == EPT_u1)
    1074             :             {
    1075             :                 // TODO(schwehr): What are these constants like 8 and 0x7?
    1076           0 :                 if (nIndexValue * 8 >= nDataSize)
    1077             :                 {
    1078           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1079           0 :                     return false;
    1080             :                 }
    1081             : 
    1082           0 :                 if (pabyData[nIndexValue >> 3] & (1 << (nIndexValue & 0x7)))
    1083             :                 {
    1084           0 :                     dfDoubleRet = 1;
    1085           0 :                     nIntRet = 1;
    1086             :                 }
    1087             :                 else
    1088             :                 {
    1089           0 :                     dfDoubleRet = 0.0;
    1090           0 :                     nIntRet = 0;
    1091             :                 }
    1092             :             }
    1093         135 :             else if (nBaseItemType == EPT_u2)
    1094             :             {
    1095           0 :                 const int nBitOffset = nIndexValue & 0x3;
    1096           0 :                 const int nByteOffset = nIndexValue >> 2;
    1097             : 
    1098           0 :                 if (nByteOffset >= nDataSize)
    1099             :                 {
    1100           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1101           0 :                     return false;
    1102             :                 }
    1103             : 
    1104           0 :                 const int nMask = 0x3;
    1105           0 :                 nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
    1106           0 :                 dfDoubleRet = nIntRet;
    1107             :             }
    1108         135 :             else if (nBaseItemType == EPT_u4)
    1109             :             {
    1110           0 :                 const int nBitOffset = nIndexValue & 0x7;
    1111           0 :                 const int nByteOffset = nIndexValue >> 3;
    1112             : 
    1113           0 :                 if (nByteOffset >= nDataSize)
    1114             :                 {
    1115           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1116           0 :                     return false;
    1117             :                 }
    1118             : 
    1119           0 :                 const int nMask = 0x7;
    1120           0 :                 nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
    1121           0 :                 dfDoubleRet = nIntRet;
    1122             :             }
    1123         135 :             else if (nBaseItemType == EPT_u8)
    1124             :             {
    1125          18 :                 if (nIndexValue >= nDataSize)
    1126             :                 {
    1127           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1128           0 :                     return false;
    1129             :                 }
    1130          18 :                 dfDoubleRet = pabyData[nIndexValue];
    1131          18 :                 nIntRet = pabyData[nIndexValue];
    1132             :             }
    1133         117 :             else if (nBaseItemType == EPT_s8)
    1134             :             {
    1135           0 :                 if (nIndexValue >= nDataSize)
    1136             :                 {
    1137           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1138           0 :                     return false;
    1139             :                 }
    1140           0 :                 dfDoubleRet = ((signed char *)pabyData)[nIndexValue];
    1141           0 :                 nIntRet = ((signed char *)pabyData)[nIndexValue];
    1142             :             }
    1143         117 :             else if (nBaseItemType == EPT_s16)
    1144             :             {
    1145           0 :                 if (nIndexValue * 2 + 2 > nDataSize)
    1146             :                 {
    1147           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1148           0 :                     return false;
    1149             :                 }
    1150           0 :                 GInt16 nValue = 0;
    1151           0 :                 memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
    1152             :                 HFAStandard(2, &nValue);
    1153             : 
    1154           0 :                 dfDoubleRet = nValue;
    1155           0 :                 nIntRet = nValue;
    1156             :             }
    1157         117 :             else if (nBaseItemType == EPT_u16)
    1158             :             {
    1159           0 :                 if (nIndexValue * 2 + 2 > nDataSize)
    1160             :                 {
    1161           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1162           0 :                     return false;
    1163             :                 }
    1164           0 :                 GUInt16 nValue = 0;
    1165           0 :                 memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
    1166             :                 HFAStandard(2, &nValue);
    1167             : 
    1168           0 :                 dfDoubleRet = nValue;
    1169           0 :                 nIntRet = nValue;
    1170             :             }
    1171         117 :             else if (nBaseItemType == EPT_s32)
    1172             :             {
    1173           0 :                 if (nIndexValue * 4 + 4 > nDataSize)
    1174             :                 {
    1175           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1176           0 :                     return false;
    1177             :                 }
    1178           0 :                 GInt32 nValue = 0;
    1179           0 :                 memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
    1180             :                 HFAStandard(4, &nValue);
    1181             : 
    1182           0 :                 dfDoubleRet = nValue;
    1183           0 :                 nIntRet = nValue;
    1184             :             }
    1185         117 :             else if (nBaseItemType == EPT_u32)
    1186             :             {
    1187           0 :                 if (nIndexValue * 4 + 4 > nDataSize)
    1188             :                 {
    1189           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1190           0 :                     return false;
    1191             :                 }
    1192           0 :                 GUInt32 nValue = 0;
    1193           0 :                 memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
    1194             :                 HFAStandard(4, &nValue);
    1195             : 
    1196           0 :                 dfDoubleRet = nValue;
    1197           0 :                 nIntRet = nValue;
    1198             :             }
    1199         117 :             else if (nBaseItemType == EPT_f32)
    1200             :             {
    1201           0 :                 if (nIndexValue * 4 + 4 > nDataSize)
    1202             :                 {
    1203           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1204           0 :                     return false;
    1205             :                 }
    1206           0 :                 float fValue = 0.0f;
    1207           0 :                 memcpy(&fValue, pabyData + 4 * nIndexValue, 4);
    1208             :                 HFAStandard(4, &fValue);
    1209             : 
    1210           0 :                 dfDoubleRet = fValue;
    1211           0 :                 nIntRet = FloatToIntClamp(fValue);
    1212             :             }
    1213         117 :             else if (nBaseItemType == EPT_f64)
    1214             :             {
    1215         117 :                 if (nIndexValue * 8 + 8 > nDataSize)
    1216             :                 {
    1217           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1218           0 :                     return false;
    1219             :                 }
    1220         117 :                 double dfValue = 0.0;
    1221         117 :                 memcpy(&dfValue, pabyData + 8 * nIndexValue, 8);
    1222             :                 HFAStandard(8, &dfValue);
    1223             : 
    1224         117 :                 dfDoubleRet = dfValue;
    1225         117 :                 if (chReqType == 'i')
    1226             :                 {
    1227           0 :                     const int nMax = std::numeric_limits<int>::max();
    1228           0 :                     const int nMin = std::numeric_limits<int>::min();
    1229           0 :                     if (dfDoubleRet >= nMax)
    1230             :                     {
    1231           0 :                         nIntRet = nMax;
    1232             :                     }
    1233           0 :                     else if (dfDoubleRet <= nMin)
    1234             :                     {
    1235           0 :                         nIntRet = nMin;
    1236             :                     }
    1237           0 :                     else if (std::isnan(dfDoubleRet))
    1238             :                     {
    1239           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1240             :                                  "NaN converted to INT_MAX.");
    1241           0 :                         nIntRet = nMax;
    1242             :                     }
    1243             :                     else
    1244             :                     {
    1245           0 :                         nIntRet = static_cast<int>(dfDoubleRet);
    1246             :                     }
    1247             :                 }
    1248             :             }
    1249             :             else
    1250             :             {
    1251           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1252             :                          "Unknown base item type: %d", nBaseItemType);
    1253           0 :                 return false;
    1254             :             }
    1255             :         }
    1256         135 :         break;
    1257             : 
    1258       13946 :         case 'o':
    1259       13946 :             if (poItemObjectType != nullptr)
    1260             :             {
    1261       13946 :                 int nExtraOffset = 0;
    1262             : 
    1263       13946 :                 if (poItemObjectType->nBytes > 0)
    1264             :                 {
    1265       11396 :                     if (nIndexValue != 0 &&
    1266        9264 :                         poItemObjectType->nBytes > INT_MAX / nIndexValue)
    1267             :                         // TODO(schwehr): Why was this CE_Failure when the
    1268             :                         // others are false?
    1269           0 :                         return false;
    1270       11396 :                     nExtraOffset = poItemObjectType->nBytes * nIndexValue;
    1271             :                 }
    1272             :                 else
    1273             :                 {
    1274        2567 :                     for (int iIndexCounter = 0; iIndexCounter < nIndexValue &&
    1275             :                                                 nExtraOffset < nDataSize;
    1276             :                          iIndexCounter++)
    1277             :                     {
    1278          17 :                         std::set<HFAField *> oVisitedFields;
    1279          34 :                         const int nInc = poItemObjectType->GetInstBytes(
    1280          17 :                             pabyData + nExtraOffset, nDataSize - nExtraOffset,
    1281             :                             oVisitedFields);
    1282          17 :                         if (nInc <= 0 || nExtraOffset > INT_MAX - nInc)
    1283             :                         {
    1284           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    1285             :                                      "Invalid return value");
    1286             :                             // TODO(schwehr): Verify this false is okay.
    1287           0 :                             return false;
    1288             :                         }
    1289             : 
    1290          17 :                         nExtraOffset += nInc;
    1291             :                     }
    1292             :                 }
    1293             : 
    1294       13946 :                 if (nExtraOffset >= nDataSize)
    1295           0 :                     return false;
    1296             : 
    1297       13946 :                 pabyRawData = pabyData + nExtraOffset;
    1298             : 
    1299       13946 :                 if (pszField != nullptr && strlen(pszField) > 0)
    1300             :                 {
    1301       27892 :                     return poItemObjectType->ExtractInstValue(
    1302       13946 :                         pszField, pabyRawData, nDataOffset + nExtraOffset,
    1303             :                         nDataSize - nExtraOffset, chReqType, pReqReturn,
    1304       13946 :                         pnRemainingDataSize);
    1305             :                 }
    1306             :             }
    1307             :             else
    1308             :             {
    1309             :                 // E. Rouault: not completely sure about this, but helps avoid
    1310             :                 // DoS timeouts in cases like
    1311             :                 // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=1806
    1312           0 :                 return false;
    1313             :             }
    1314           0 :             break;
    1315             : 
    1316           0 :         default:
    1317           0 :             return false;
    1318             :             break;
    1319             :     }
    1320             : 
    1321             :     // Return the appropriate representation.
    1322       29975 :     if (chReqType == 's')
    1323             :     {
    1324        1102 :         if (pszStringRet == nullptr)
    1325             :         {
    1326             :             // HFAEntry:: BuildEntryFromMIFObject() expects to have always 8
    1327             :             // bytes before the data. In normal situations, it should not go
    1328             :             // here, but that can happen if the file is corrupted so reserve the
    1329             :             // first 8 bytes before the string to contain null bytes.
    1330          39 :             memset(szNumberString, 0, 8);
    1331          39 :             CPLsnprintf(szNumberString + 8, sizeof(szNumberString) - 8, "%.14g",
    1332             :                         dfDoubleRet);
    1333          39 :             pszStringRet = szNumberString + 8;
    1334             :         }
    1335             : 
    1336        1102 :         *((char **)pReqReturn) = pszStringRet;
    1337        1102 :         return true;
    1338             :     }
    1339       28873 :     else if (chReqType == 'd')
    1340             :     {
    1341        7437 :         *((double *)pReqReturn) = dfDoubleRet;
    1342        7437 :         return true;
    1343             :     }
    1344       21436 :     else if (chReqType == 'i')
    1345             :     {
    1346       21436 :         *((int *)pReqReturn) = nIntRet;
    1347       21436 :         return true;
    1348             :     }
    1349           0 :     else if (chReqType == 'p')
    1350             :     {
    1351           0 :         *((GByte **)pReqReturn) = pabyRawData;
    1352           0 :         return true;
    1353             :     }
    1354             :     else
    1355             :     {
    1356           0 :         CPLAssert(false);
    1357             :         return false;
    1358             :     }
    1359             : }
    1360             : 
    1361             : /************************************************************************/
    1362             : /*                            GetInstBytes()                            */
    1363             : /*                                                                      */
    1364             : /*      Get the number of bytes in a particular instance of a           */
    1365             : /*      field.  This will normally be the fixed internal nBytes         */
    1366             : /*      value, but for pointer objects will include the variable        */
    1367             : /*      portion.                                                        */
    1368             : /************************************************************************/
    1369             : 
    1370      168738 : int HFAField::GetInstBytes(GByte *pabyData, int nDataSize,
    1371             :                            std::set<HFAField *> &oVisitedFields)
    1372             : 
    1373             : {
    1374      168738 :     if (oVisitedFields.find(this) != oVisitedFields.end())
    1375             :     {
    1376           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1377           0 :         return -1;
    1378             :     }
    1379             : 
    1380      168738 :     if (nBytes > -1)
    1381      135344 :         return nBytes;
    1382             : 
    1383       33394 :     int nCount = 1;
    1384       33394 :     int nInstBytes = 0;
    1385             : 
    1386       33394 :     if (chPointer != '\0')
    1387             :     {
    1388       30962 :         if (nDataSize < 4)
    1389             :         {
    1390           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1391           0 :             return -1;
    1392             :         }
    1393             : 
    1394       30962 :         memcpy(&nCount, pabyData, 4);
    1395             :         HFAStandard(4, &nCount);
    1396             : 
    1397       30962 :         pabyData += 8;
    1398       30962 :         nInstBytes += 8;
    1399             :     }
    1400             : 
    1401       33394 :     if (chItemType == 'b' && nCount != 0)  // BASEDATA
    1402             :     {
    1403         515 :         if (nDataSize - nInstBytes < 4 + 4 + 2)
    1404             :         {
    1405           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
    1406           0 :             return -1;
    1407             :         }
    1408             : 
    1409         515 :         GInt32 nRows = 0;
    1410         515 :         memcpy(&nRows, pabyData, 4);
    1411             :         HFAStandard(4, &nRows);
    1412         515 :         GInt32 nColumns = 0;
    1413         515 :         memcpy(&nColumns, pabyData + 4, 4);
    1414             :         HFAStandard(4, &nColumns);
    1415         515 :         GInt16 nBaseItemType = 0;
    1416         515 :         memcpy(&nBaseItemType, pabyData + 8, 2);
    1417             :         HFAStandard(2, &nBaseItemType);
    1418         515 :         if (nBaseItemType < EPT_MIN || nBaseItemType > EPT_MAX)
    1419           0 :             return -1;
    1420             : 
    1421         515 :         EPTType eBaseItemType = static_cast<EPTType>(nBaseItemType);
    1422             : 
    1423         515 :         nInstBytes += 12;
    1424             : 
    1425         515 :         if (nRows < 0 || nColumns < 0)
    1426           0 :             return -1;
    1427         515 :         if (nColumns != 0 && nRows > INT_MAX / nColumns)
    1428           0 :             return -1;
    1429        1022 :         if (nRows != 0 &&
    1430         507 :             ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) > INT_MAX / nRows)
    1431           0 :             return -1;
    1432        1022 :         if (nColumns != 0 &&
    1433         507 :             ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows >
    1434         507 :                 INT_MAX / nColumns)
    1435           0 :             return -1;
    1436         515 :         if (((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns >
    1437         515 :             INT_MAX - nInstBytes)
    1438           0 :             return -1;
    1439             : 
    1440         515 :         nInstBytes +=
    1441         515 :             ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns;
    1442             :     }
    1443       32879 :     else if (poItemObjectType == nullptr)
    1444             :     {
    1445       49434 :         if (nCount != 0 &&
    1446       20000 :             HFADictionary::GetItemSize(chItemType) > INT_MAX / nCount)
    1447           0 :             return -1;
    1448       29434 :         if (nCount * HFADictionary::GetItemSize(chItemType) >
    1449       29434 :             INT_MAX - nInstBytes)
    1450           0 :             return -1;
    1451       29434 :         nInstBytes += nCount * HFADictionary::GetItemSize(chItemType);
    1452             :     }
    1453             :     else
    1454             :     {
    1455        3445 :         oVisitedFields.insert(this);
    1456        5931 :         for (int i = 0; i < nCount && nInstBytes < nDataSize && nInstBytes >= 0;
    1457             :              i++)
    1458             :         {
    1459        2486 :             const int nThisBytes = poItemObjectType->GetInstBytes(
    1460             :                 pabyData, nDataSize - nInstBytes, oVisitedFields);
    1461        2486 :             if (nThisBytes <= 0 || nInstBytes > INT_MAX - nThisBytes)
    1462             :             {
    1463           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value");
    1464           0 :                 return -1;
    1465             :             }
    1466             : 
    1467        2486 :             nInstBytes += nThisBytes;
    1468        2486 :             pabyData += nThisBytes;
    1469             :         }
    1470        3445 :         oVisitedFields.erase(this);
    1471             :     }
    1472             : 
    1473       33394 :     return nInstBytes;
    1474             : }
    1475             : 
    1476             : /************************************************************************/
    1477             : /*                            GetInstCount()                            */
    1478             : /*                                                                      */
    1479             : /*      Get the count for a particular instance of a field.  This       */
    1480             : /*      will normally be the built in value, but for variable fields    */
    1481             : /*      this is extracted from the data itself.                         */
    1482             : /************************************************************************/
    1483             : 
    1484       47439 : int HFAField::GetInstCount(GByte *pabyData, int nDataSize) const
    1485             : 
    1486             : {
    1487       47439 :     if (chPointer == '\0')
    1488       27415 :         return nItemCount;
    1489             : 
    1490       20024 :     if (chItemType == 'b')
    1491             :     {
    1492         213 :         if (nDataSize < 20)
    1493           0 :             return 0;
    1494             : 
    1495         213 :         GInt32 nRows = 0;
    1496         213 :         memcpy(&nRows, pabyData + 8, 4);
    1497             :         HFAStandard(4, &nRows);
    1498         213 :         GInt32 nColumns = 0;
    1499         213 :         memcpy(&nColumns, pabyData + 12, 4);
    1500             :         HFAStandard(4, &nColumns);
    1501             : 
    1502         213 :         if (nRows < 0 || nColumns < 0)
    1503           0 :             return 0;
    1504         213 :         if (nColumns != 0 && nRows > INT_MAX / nColumns)
    1505           0 :             return 0;
    1506             : 
    1507         213 :         return nRows * nColumns;
    1508             :     }
    1509             : 
    1510       19811 :     if (nDataSize < 4)
    1511           0 :         return 0;
    1512             : 
    1513       19811 :     GInt32 nCount = 0;
    1514       19811 :     memcpy(&nCount, pabyData, 4);
    1515             :     HFAStandard(4, &nCount);
    1516       19811 :     return nCount;
    1517             : }
    1518             : 
    1519             : /************************************************************************/
    1520             : /*                           DumpInstValue()                            */
    1521             : /************************************************************************/
    1522             : 
    1523           0 : void HFAField::DumpInstValue(FILE *fpOut, GByte *pabyData, GUInt32 nDataOffset,
    1524             :                              int nDataSize, const char *pszPrefix)
    1525             : 
    1526             : {
    1527           0 :     const int nEntries = GetInstCount(pabyData, nDataSize);
    1528             : 
    1529             :     // Special case for arrays of chars or uchars which are printed
    1530             :     // as a string.
    1531           0 :     if ((chItemType == 'c' || chItemType == 'C') && nEntries > 0)
    1532             :     {
    1533           0 :         void *pReturn = nullptr;
    1534           0 :         if (ExtractInstValue(nullptr, 0, pabyData, nDataOffset, nDataSize, 's',
    1535             :                              &pReturn))
    1536           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = `%s'\n", pszPrefix,
    1537             :                                           pszFieldName,
    1538             :                                           static_cast<char *>(pReturn)));
    1539             :         else
    1540           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = (access failed)\n",
    1541             :                                           pszPrefix, pszFieldName));
    1542             : 
    1543           0 :         return;
    1544             :     }
    1545             : 
    1546             :     // For BASEDATA objects, we want to first dump their dimension and type.
    1547           0 :     if (chItemType == 'b')
    1548             :     {
    1549           0 :         int nDataType = 0;
    1550           0 :         const bool bSuccess = ExtractInstValue(
    1551             :             nullptr, -3, pabyData, nDataOffset, nDataSize, 'i', &nDataType);
    1552           0 :         if (bSuccess)
    1553             :         {
    1554           0 :             int nColumns = 0;
    1555           0 :             ExtractInstValue(nullptr, -2, pabyData, nDataOffset, nDataSize, 'i',
    1556             :                              &nColumns);
    1557           0 :             int nRows = 0;
    1558           0 :             ExtractInstValue(nullptr, -1, pabyData, nDataOffset, nDataSize, 'i',
    1559             :                              &nRows);
    1560           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(
    1561             :                 fpOut, "%sBASEDATA(%s): %dx%d of %s\n", pszPrefix, pszFieldName,
    1562             :                 nColumns, nRows,
    1563           0 :                 (nDataType >= EPT_MIN && nDataType <= EPT_MAX)
    1564           0 :                     ? HFAGetDataTypeName(static_cast<EPTType>(nDataType))
    1565             :                     : "invalid type"));
    1566             :         }
    1567             :         else
    1568             :         {
    1569           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%sBASEDATA(%s): empty\n",
    1570             :                                           pszPrefix, pszFieldName));
    1571             :         }
    1572             :     }
    1573             : 
    1574             :     // Dump each entry in the field array.
    1575           0 :     void *pReturn = nullptr;
    1576             : 
    1577           0 :     const int nMaxEntry = std::min(MAX_ENTRY_REPORT, nEntries);
    1578           0 :     for (int iEntry = 0; iEntry < nMaxEntry; iEntry++)
    1579             :     {
    1580           0 :         if (nEntries == 1)
    1581           0 :             CPL_IGNORE_RET_VAL(
    1582           0 :                 VSIFPrintf(fpOut, "%s%s = ", pszPrefix, pszFieldName));
    1583             :         else
    1584           0 :             CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s[%d] = ", pszPrefix,
    1585             :                                           pszFieldName, iEntry));
    1586             : 
    1587           0 :         switch (chItemType)
    1588             :         {
    1589           0 :             case 'f':
    1590             :             case 'd':
    1591             :             {
    1592           0 :                 double dfValue = 0.0;
    1593           0 :                 if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1594             :                                      nDataSize, 'd', &dfValue))
    1595           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%f\n", dfValue));
    1596             :                 else
    1597           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
    1598             :             }
    1599           0 :             break;
    1600             : 
    1601           0 :             case 'b':
    1602             :             {
    1603           0 :                 double dfValue = 0.0;
    1604             : 
    1605           0 :                 if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1606             :                                      nDataSize, 'd', &dfValue))
    1607           0 :                     CPL_IGNORE_RET_VAL(
    1608           0 :                         VSIFPrintf(fpOut, "%s%.15g\n", pszPrefix, dfValue));
    1609             :                 else
    1610           0 :                     CPL_IGNORE_RET_VAL(
    1611           0 :                         VSIFPrintf(fpOut, "%s(access failed)\n", pszPrefix));
    1612             :             }
    1613           0 :             break;
    1614             : 
    1615           0 :             case 'e':
    1616           0 :                 if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1617             :                                      nDataSize, 's', &pReturn))
    1618           0 :                     CPL_IGNORE_RET_VAL(
    1619           0 :                         VSIFPrintf(fpOut, "%s\n", (char *)pReturn));
    1620             :                 else
    1621           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
    1622           0 :                 break;
    1623             : 
    1624           0 :             case 'o':
    1625           0 :                 if (!ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1626             :                                       nDataSize, 'p', &pReturn))
    1627             :                 {
    1628           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
    1629             :                 }
    1630             :                 else
    1631             :                 {
    1632           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "\n"));
    1633             : 
    1634           0 :                     const int nByteOffset =
    1635           0 :                         static_cast<int>(((GByte *)pReturn) - pabyData);
    1636             : 
    1637           0 :                     char szLongFieldName[256] = {};
    1638           0 :                     snprintf(szLongFieldName, sizeof(szLongFieldName), "%s    ",
    1639             :                              pszPrefix);
    1640             : 
    1641           0 :                     if (poItemObjectType)
    1642           0 :                         poItemObjectType->DumpInstValue(
    1643           0 :                             fpOut, pabyData + nByteOffset,
    1644           0 :                             nDataOffset + nByteOffset, nDataSize - nByteOffset,
    1645             :                             szLongFieldName);
    1646             :                 }
    1647           0 :                 break;
    1648             : 
    1649           0 :             default:
    1650             :             {
    1651           0 :                 GInt32 nIntValue = 0;
    1652             : 
    1653           0 :                 if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
    1654             :                                      nDataSize, 'i', &nIntValue))
    1655           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%d\n", nIntValue));
    1656             :                 else
    1657           0 :                     CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
    1658             :             }
    1659           0 :             break;
    1660             :         }
    1661             :     }
    1662             : 
    1663           0 :     if (nEntries > MAX_ENTRY_REPORT)
    1664           0 :         CPL_IGNORE_RET_VAL(VSIFPrintf(
    1665             :             fpOut, "%s ... remaining instances omitted ...\n", pszPrefix));
    1666             : 
    1667           0 :     if (nEntries == 0)
    1668           0 :         CPL_IGNORE_RET_VAL(
    1669           0 :             VSIFPrintf(fpOut, "%s%s = (no values)\n", pszPrefix, pszFieldName));
    1670             : }

Generated by: LCOV version 1.14