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

Generated by: LCOV version 1.14