LCOV - code coverage report
Current view: top level - frmts/hdf4 - hdf4dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 231 596 38.8 %
Date: 2024-11-21 22:18:42 Functions: 13 19 68.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 4 (HDF4)
       4             :  * Purpose:  HDF4 Datasets. Open HDF4 file, fetch metadata and list of
       5             :  *           subdatasets.
       6             :  *           This driver initially based on code supplied by Markus Neteler
       7             :  * Author:   Andrey Kiselev, dron@ak4719.spb.edu
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2002, Andrey Kiselev <dron@ak4719.spb.edu>
      11             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "cpl_multiproc.h"
      17             : #include "cpl_string.h"
      18             : #include "gdal_frmts.h"
      19             : #include "gdal_priv.h"
      20             : 
      21             : #include "hdf.h"
      22             : #include "mfhdf.h"
      23             : 
      24             : #include "HdfEosDef.h"
      25             : 
      26             : #include "hdf4compat.h"
      27             : #include "hdf4dataset.h"
      28             : #include <cctype>
      29             : 
      30             : #include "hdf4drivercore.h"
      31             : 
      32             : extern const char *const pszGDALSignature;
      33             : 
      34             : CPLMutex *hHDF4Mutex = nullptr;
      35             : 
      36             : /************************************************************************/
      37             : /* ==================================================================== */
      38             : /*                              HDF4Dataset                             */
      39             : /* ==================================================================== */
      40             : /************************************************************************/
      41             : 
      42             : /************************************************************************/
      43             : /*                           HDF4Dataset()                              */
      44             : /************************************************************************/
      45             : 
      46         785 : HDF4Dataset::HDF4Dataset()
      47             :     : bIsHDFEOS(false), hGR(0), hSD(0), nImages(0),
      48             :       iSubdatasetType(H4ST_UNKNOWN), pszSubdatasetType(nullptr),
      49         785 :       papszGlobalMetadata(nullptr), papszSubDatasets(nullptr)
      50             : {
      51         785 : }
      52             : 
      53             : /************************************************************************/
      54             : /*                            ~HDF4Dataset()                            */
      55             : /************************************************************************/
      56             : 
      57        1097 : HDF4Dataset::~HDF4Dataset()
      58             : 
      59             : {
      60        1570 :     CPLMutexHolderD(&hHDF4Mutex);
      61             : 
      62         785 :     if (hSD)
      63           6 :         SDend(hSD);
      64         785 :     if (hGR)
      65           0 :         GRend(hGR);
      66         785 :     if (papszSubDatasets)
      67         302 :         CSLDestroy(papszSubDatasets);
      68         785 :     if (papszGlobalMetadata)
      69         599 :         CSLDestroy(papszGlobalMetadata);
      70        1097 : }
      71             : 
      72             : /************************************************************************/
      73             : /*                      GetMetadataDomainList()                         */
      74             : /************************************************************************/
      75             : 
      76           0 : char **HDF4Dataset::GetMetadataDomainList()
      77             : {
      78           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
      79           0 :                                    TRUE, "SUBDATASETS", nullptr);
      80             : }
      81             : 
      82             : /************************************************************************/
      83             : /*                            GetMetadata()                             */
      84             : /************************************************************************/
      85             : 
      86         248 : char **HDF4Dataset::GetMetadata(const char *pszDomain)
      87             : 
      88             : {
      89         248 :     if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "SUBDATASETS"))
      90           0 :         return papszSubDatasets;
      91             : 
      92         248 :     return GDALDataset::GetMetadata(pszDomain);
      93             : }
      94             : 
      95             : /************************************************************************/
      96             : /*                           SPrintArray()                              */
      97             : /*      Prints numerical arrays in string buffer.                       */
      98             : /*      This function takes pfaDataArray as a pointer to printed array, */
      99             : /*      nValues as a number of values to print and pszDelimiter as a    */
     100             : /*      field delimiting strings.                                       */
     101             : /*      Pointer to filled buffer will be returned.                      */
     102             : /************************************************************************/
     103             : 
     104         304 : char *SPrintArray(GDALDataType eDataType, const void *paDataArray, int nValues,
     105             :                   const char *pszDelimiter)
     106             : {
     107         304 :     const int iFieldSize = 32 + static_cast<int>(strlen(pszDelimiter));
     108         304 :     char *pszField = static_cast<char *>(CPLMalloc(iFieldSize + 1));
     109         304 :     const int iStringSize = nValues * iFieldSize + 1;
     110         304 :     char *pszString = static_cast<char *>(CPLMalloc(iStringSize));
     111         304 :     memset(pszString, 0, iStringSize);
     112        1076 :     for (int i = 0; i < nValues; i++)
     113             :     {
     114         772 :         switch (eDataType)
     115             :         {
     116           0 :             case GDT_Byte:
     117           0 :                 snprintf(pszField, iFieldSize + 1, "%d%s",
     118             :                          reinterpret_cast<GByte *>(
     119           0 :                              const_cast<void *>(paDataArray))[i],
     120           0 :                          (i < nValues - 1) ? pszDelimiter : "");
     121           0 :                 break;
     122           0 :             case GDT_Int8:
     123           0 :                 snprintf(pszField, iFieldSize + 1, "%d%s",
     124             :                          reinterpret_cast<GInt8 *>(
     125           0 :                              const_cast<void *>(paDataArray))[i],
     126           0 :                          (i < nValues - 1) ? pszDelimiter : "");
     127           0 :                 break;
     128           0 :             case GDT_UInt16:
     129           0 :                 snprintf(pszField, iFieldSize + 1, "%u%s",
     130             :                          reinterpret_cast<GUInt16 *>(
     131           0 :                              const_cast<void *>(paDataArray))[i],
     132           0 :                          (i < nValues - 1) ? pszDelimiter : "");
     133           0 :                 break;
     134           0 :             case GDT_Int16:
     135             :             default:
     136           0 :                 snprintf(pszField, iFieldSize + 1, "%d%s",
     137             :                          reinterpret_cast<GInt16 *>(
     138           0 :                              const_cast<void *>(paDataArray))[i],
     139           0 :                          (i < nValues - 1) ? pszDelimiter : "");
     140           0 :                 break;
     141         772 :             case GDT_UInt32:
     142         772 :                 snprintf(pszField, iFieldSize + 1, "%u%s",
     143             :                          reinterpret_cast<GUInt32 *>(
     144         772 :                              const_cast<void *>(paDataArray))[i],
     145         772 :                          (i < nValues - 1) ? pszDelimiter : "");
     146         772 :                 break;
     147           0 :             case GDT_Int32:
     148           0 :                 snprintf(pszField, iFieldSize + 1, "%d%s",
     149             :                          reinterpret_cast<GInt32 *>(
     150           0 :                              const_cast<void *>(paDataArray))[i],
     151           0 :                          (i < nValues - 1) ? pszDelimiter : "");
     152           0 :                 break;
     153           0 :             case GDT_Float32:
     154           0 :                 CPLsnprintf(pszField, iFieldSize + 1, "%.10g%s",
     155             :                             reinterpret_cast<float *>(
     156           0 :                                 const_cast<void *>(paDataArray))[i],
     157           0 :                             (i < nValues - 1) ? pszDelimiter : "");
     158           0 :                 break;
     159           0 :             case GDT_Float64:
     160           0 :                 CPLsnprintf(pszField, iFieldSize + 1, "%.15g%s",
     161             :                             reinterpret_cast<double *>(
     162           0 :                                 const_cast<void *>(paDataArray))[i],
     163           0 :                             (i < nValues - 1) ? pszDelimiter : "");
     164           0 :                 break;
     165             :         }
     166         772 :         strcat(pszString, pszField);
     167             :     }
     168             : 
     169         304 :     CPLFree(pszField);
     170         304 :     return pszString;
     171             : }
     172             : 
     173             : /************************************************************************/
     174             : /*              Translate HDF4 data type into GDAL data type            */
     175             : /************************************************************************/
     176         362 : GDALDataType HDF4Dataset::GetDataType(int32 iNumType)
     177             : {
     178         362 :     switch (iNumType)
     179             :     {
     180         101 :         case DFNT_CHAR8:   // The same as DFNT_CHAR
     181             :         case DFNT_UCHAR8:  // The same as DFNT_UCHAR
     182             :         case DFNT_UINT8:
     183         101 :             return GDT_Byte;
     184          32 :         case DFNT_INT8:
     185          32 :             return GDT_Int8;
     186          52 :         case DFNT_INT16:
     187          52 :             return GDT_Int16;
     188          34 :         case DFNT_UINT16:
     189          34 :             return GDT_UInt16;
     190          36 :         case DFNT_INT32:
     191          36 :             return GDT_Int32;
     192          34 :         case DFNT_UINT32:
     193          34 :             return GDT_UInt32;
     194           0 :         case DFNT_INT64:
     195           0 :             return GDT_Int64;
     196           0 :         case DFNT_UINT64:
     197           0 :             return GDT_UInt64;
     198          37 :         case DFNT_FLOAT32:
     199          37 :             return GDT_Float32;
     200          36 :         case DFNT_FLOAT64:
     201          36 :             return GDT_Float64;
     202           0 :         default:
     203           0 :             return GDT_Unknown;
     204             :     }
     205             : }
     206             : 
     207             : /************************************************************************/
     208             : /*              Return the human readable name of data type             */
     209             : /************************************************************************/
     210             : 
     211         304 : const char *HDF4Dataset::GetDataTypeName(int32 iNumType)
     212             : {
     213         304 :     switch (iNumType)
     214             :     {
     215           0 :         case DFNT_CHAR8:  // The same as DFNT_CHAR
     216           0 :             return "8-bit character";
     217           0 :         case DFNT_UCHAR8:  // The same as DFNT_UCHAR
     218           0 :             return "8-bit unsigned character";
     219          30 :         case DFNT_INT8:
     220          30 :             return "8-bit integer";
     221          78 :         case DFNT_UINT8:
     222          78 :             return "8-bit unsigned integer";
     223          33 :         case DFNT_INT16:
     224          33 :             return "16-bit integer";
     225          32 :         case DFNT_UINT16:
     226          32 :             return "16-bit unsigned integer";
     227          35 :         case DFNT_INT32:
     228          35 :             return "32-bit integer";
     229          32 :         case DFNT_UINT32:
     230          32 :             return "32-bit unsigned integer";
     231           0 :         case DFNT_INT64:
     232           0 :             return "64-bit integer";
     233           0 :         case DFNT_UINT64:
     234           0 :             return "64-bit unsigned integer";
     235          32 :         case DFNT_FLOAT32:
     236          32 :             return "32-bit floating-point";
     237          32 :         case DFNT_FLOAT64:
     238          32 :             return "64-bit floating-point";
     239           0 :         default:
     240             :         {
     241           0 :             CPLError(CE_Warning, CPLE_AppDefined, "Unknown type %d",
     242             :                      static_cast<int>(iNumType));
     243             : 
     244           0 :             return "unknown type";
     245             :         }
     246             :     }
     247             : }
     248             : 
     249             : /************************************************************************/
     250             : /*  Return the size of data type in bytes                               */
     251             : /************************************************************************/
     252             : 
     253        1723 : int HDF4Dataset::GetDataTypeSize(int32 iNumType)
     254             : {
     255        1723 :     switch (iNumType)
     256             :     {
     257        1723 :         case DFNT_CHAR8:   // The same as DFNT_CHAR
     258             :         case DFNT_UCHAR8:  // The same as DFNT_UCHAR
     259             :         case DFNT_INT8:
     260             :         case DFNT_UINT8:
     261        1723 :             return 1;
     262           0 :         case DFNT_INT16:
     263             :         case DFNT_UINT16:
     264           0 :             return 2;
     265           0 :         case DFNT_INT32:
     266             :         case DFNT_UINT32:
     267             :         case DFNT_FLOAT32:
     268           0 :             return 4;
     269           0 :         case DFNT_INT64:
     270             :         case DFNT_UINT64:
     271             :         case DFNT_FLOAT64:
     272           0 :             return 8;
     273           0 :         default:
     274             :         {
     275           0 :             CPLError(CE_Warning, CPLE_AppDefined, "Unknown type %d",
     276             :                      static_cast<int>(iNumType));
     277           0 :             return 0;
     278             :         }
     279             :     }
     280             : }
     281             : 
     282             : /************************************************************************/
     283             : /*  Convert value stored in the input buffer to double value.           */
     284             : /************************************************************************/
     285             : 
     286           0 : double HDF4Dataset::AnyTypeToDouble(int32 iNumType, void *pData)
     287             : {
     288           0 :     switch (iNumType)
     289             :     {
     290           0 :         case DFNT_INT8:
     291           0 :             return static_cast<double>(*reinterpret_cast<signed char *>(pData));
     292           0 :         case DFNT_UINT8:
     293           0 :             return static_cast<double>(*reinterpret_cast<GByte *>(pData));
     294           0 :         case DFNT_INT16:
     295           0 :             return static_cast<double>(*reinterpret_cast<GInt16 *>(pData));
     296           0 :         case DFNT_UINT16:
     297           0 :             return static_cast<double>(*reinterpret_cast<GUInt16 *>(pData));
     298           0 :         case DFNT_INT32:
     299           0 :             return static_cast<double>(*reinterpret_cast<GInt32 *>(pData));
     300           0 :         case DFNT_UINT32:
     301           0 :             return static_cast<double>(*reinterpret_cast<GUInt32 *>(pData));
     302           0 :         case DFNT_INT64:
     303           0 :             return static_cast<double>(*reinterpret_cast<GInt64 *>(pData));
     304           0 :         case DFNT_UINT64:
     305           0 :             return static_cast<double>(*reinterpret_cast<GUInt64 *>(pData));
     306           0 :         case DFNT_FLOAT32:
     307           0 :             return static_cast<double>(*reinterpret_cast<float *>(pData));
     308           0 :         case DFNT_FLOAT64:
     309           0 :             return static_cast<double>(*reinterpret_cast<double *>(pData));
     310           0 :         default:
     311             :         {
     312           0 :             CPLError(CE_Warning, CPLE_AppDefined, "Unknown type %d",
     313             :                      static_cast<int>(iNumType));
     314           0 :             return 0.0;
     315             :         }
     316             :     }
     317             : }
     318             : 
     319             : /************************************************************************/
     320             : /*         Tokenize HDF-EOS attributes.                                 */
     321             : /************************************************************************/
     322             : 
     323           0 : char **HDF4Dataset::HDF4EOSTokenizeAttrs(const char *pszString)
     324             : 
     325             : {
     326           0 :     const char *const pszDelimiters = " \t\n\r";
     327           0 :     char **papszRetList = nullptr;
     328             : 
     329           0 :     char *pszToken = static_cast<char *>(CPLCalloc(10, 1));
     330           0 :     int nTokenMax = 10;
     331             : 
     332           0 :     while (pszString != nullptr && *pszString != '\0')
     333             :     {
     334           0 :         bool bInString = false;
     335           0 :         bool bInBracket = false;
     336             : 
     337           0 :         int nTokenLen = 0;
     338             : 
     339             :         // Try to find the next delimiter, marking end of token.
     340           0 :         for (; *pszString != '\0'; pszString++)
     341             :         {
     342             : 
     343             :             // End if this is a delimiter skip it and break.
     344           0 :             if (!bInBracket && !bInString &&
     345           0 :                 strchr(pszDelimiters, *pszString) != nullptr)
     346             :             {
     347           0 :                 pszString++;
     348           0 :                 break;
     349             :             }
     350             : 
     351             :             // Sometimes in bracketed tokens we may found a sort of
     352             :             // paragraph formatting. We will remove unneeded spaces and new
     353             :             // lines.
     354           0 :             if (bInBracket)
     355           0 :                 if (strchr("\r\n", *pszString) != nullptr ||
     356           0 :                     (*pszString == ' ' &&
     357           0 :                      strchr(" \r\n", *(pszString - 1)) != nullptr))
     358           0 :                     continue;
     359             : 
     360           0 :             if (*pszString == '"')
     361             :             {
     362           0 :                 bInString = !bInString;
     363           0 :                 continue;
     364             :             }
     365           0 :             else if (*pszString == '(')
     366             :             {
     367           0 :                 bInBracket = true;
     368           0 :                 continue;
     369             :             }
     370           0 :             else if (*pszString == ')')
     371             :             {
     372           0 :                 bInBracket = false;
     373           0 :                 continue;
     374             :             }
     375             : 
     376           0 :             if (nTokenLen >= nTokenMax - 2)
     377             :             {
     378           0 :                 nTokenMax = nTokenMax * 2 + 10;
     379           0 :                 pszToken = static_cast<char *>(CPLRealloc(pszToken, nTokenMax));
     380             :             }
     381             : 
     382           0 :             pszToken[nTokenLen] = *pszString;
     383           0 :             nTokenLen++;
     384             :         }
     385             : 
     386           0 :         pszToken[nTokenLen] = '\0';
     387             : 
     388           0 :         if (pszToken[0] != '\0')
     389             :         {
     390           0 :             papszRetList = CSLAddString(papszRetList, pszToken);
     391             :         }
     392             : 
     393             :         // If the last token is an empty token, then we have to catch
     394             :         // it now, otherwise we won't reenter the loop and it will be lost.
     395           0 :         if (*pszString == '\0' && strchr(pszDelimiters, *(pszString - 1)))
     396             :         {
     397           0 :             papszRetList = CSLAddString(papszRetList, "");
     398             :         }
     399             :     }
     400             : 
     401           0 :     if (papszRetList == nullptr)
     402           0 :         papszRetList = static_cast<char **>(CPLCalloc(sizeof(char *), 1));
     403             : 
     404           0 :     CPLFree(pszToken);
     405             : 
     406           0 :     return papszRetList;
     407             : }
     408             : 
     409             : /************************************************************************/
     410             : /*     Find object name, class value in HDF-EOS attributes.             */
     411             : /*     Function returns pointer to the string in list next behind       */
     412             : /*     recognized object.                                               */
     413             : /************************************************************************/
     414             : 
     415           0 : char **HDF4Dataset::HDF4EOSGetObject(char **papszAttrList, char **ppszAttrName,
     416             :                                      char **ppszAttrClass, char **ppszAttrValue)
     417             : {
     418           0 :     *ppszAttrName = nullptr;
     419           0 :     *ppszAttrClass = nullptr;
     420           0 :     *ppszAttrValue = nullptr;
     421             : 
     422           0 :     const int iCount = CSLCount(papszAttrList);
     423           0 :     for (int i = 0; i < iCount - 2; i++)
     424             :     {
     425           0 :         if (EQUAL(papszAttrList[i], "OBJECT"))
     426             :         {
     427           0 :             i += 2;
     428           0 :             for (int j = 1; i + j < iCount - 2; j++)
     429             :             {
     430           0 :                 if (EQUAL(papszAttrList[i + j], "END_OBJECT") ||
     431           0 :                     EQUAL(papszAttrList[i + j], "OBJECT"))
     432           0 :                     return &papszAttrList[i + j];
     433           0 :                 else if (EQUAL(papszAttrList[i + j], "CLASS"))
     434             :                 {
     435           0 :                     *ppszAttrClass = papszAttrList[i + j + 2];
     436           0 :                     continue;
     437             :                 }
     438           0 :                 else if (EQUAL(papszAttrList[i + j], "VALUE"))
     439             :                 {
     440           0 :                     *ppszAttrName = papszAttrList[i];
     441           0 :                     *ppszAttrValue = papszAttrList[i + j + 2];
     442           0 :                     continue;
     443             :                 }
     444             :             }
     445             :         }
     446             :     }
     447             : 
     448           0 :     return nullptr;
     449             : }
     450             : 
     451             : /************************************************************************/
     452             : /*         Translate HDF4-EOS attributes in GDAL metadata items         */
     453             : /************************************************************************/
     454             : 
     455           0 : char **HDF4Dataset::TranslateHDF4EOSAttributes(int32 iHandle, int32 iAttribute,
     456             :                                                int32 nValues,
     457             :                                                char **papszMetadata)
     458             : {
     459             :     char *pszData =
     460           0 :         static_cast<char *>(CPLMalloc((nValues + 1) * sizeof(char)));
     461           0 :     pszData[nValues] = '\0';
     462           0 :     SDreadattr(iHandle, iAttribute, pszData);
     463             :     // HDF4-EOS attributes has followed structure:
     464             :     //
     465             :     // GROUP = <name>
     466             :     //   GROUPTYPE = <name>
     467             :     //
     468             :     //   GROUP = <name>
     469             :     //
     470             :     //     OBJECT = <name>
     471             :     //       CLASS = <string>
     472             :     //       NUM_VAL = <number>
     473             :     //       VALUE = <string> or <number>
     474             :     //     END_OBJECT = <name>
     475             :     //
     476             :     //     .......
     477             :     //     .......
     478             :     //     .......
     479             :     //
     480             :     //   END_GROUP = <name>
     481             :     //
     482             :     // .......
     483             :     // .......
     484             :     // .......
     485             :     //
     486             :     // END_GROUP = <name>
     487             :     // END
     488             :     //
     489             :     // Used data types:
     490             :     // <name>   --- unquoted character strings
     491             :     // <string> --- quoted character strings
     492             :     // <number> --- numerical value
     493             :     // If NUM_VAL != 1 then values combined in lists:
     494             :     // (<string>,<string>,...)
     495             :     // or
     496             :     // (<number>,<number>,...)
     497             :     //
     498             :     // Records within objects could come in any order, objects could contain
     499             :     // other objects (and lack VALUE record), groups could contain other groups
     500             :     // and objects. Names of groups and objects are not unique and may repeat.
     501             :     // Objects may contains other types of records.
     502             :     //
     503             :     // We are interested in OBJECTS structures only. To avoid multiple items
     504             :     // with the same name, names will be suffixed with the class values, e.g.
     505             :     //
     506             :     //  OBJECT                 = PARAMETERNAME
     507             :     //    CLASS                = "9"
     508             :     //    NUM_VAL              = 1
     509             :     //    VALUE                = "Spectral IR Surf Bidirect Reflectivity"
     510             :     //  END_OBJECT             = PARAMETERNAME
     511             :     //
     512             :     //  will be translated into metadata record:
     513             :     //
     514             :     //  PARAMETERNAME.9 = "Spectral IR Surf Bidirect Reflectivity"
     515             : 
     516           0 :     char *pszAttrName = nullptr;
     517           0 :     char *pszAttrClass = nullptr;
     518           0 :     char *pszAttrValue = nullptr;
     519           0 :     char *pszAddAttrName = nullptr;
     520             : 
     521           0 :     char **const papszAttrList = HDF4EOSTokenizeAttrs(pszData);
     522           0 :     char **papszAttrs = papszAttrList;
     523           0 :     while (papszAttrs)
     524             :     {
     525           0 :         papszAttrs = HDF4EOSGetObject(papszAttrs, &pszAttrName, &pszAttrClass,
     526             :                                       &pszAttrValue);
     527           0 :         if (pszAttrName && pszAttrValue)
     528             :         {
     529             :             // Now we should recognize special type of HDF EOS metastructures:
     530             :             // ADDITIONALATTRIBUTENAME = <name>
     531             :             // PARAMETERVALUE = <value>
     532           0 :             if (EQUAL(pszAttrName, "ADDITIONALATTRIBUTENAME"))
     533             :             {
     534           0 :                 pszAddAttrName = pszAttrValue;
     535             :             }
     536           0 :             else if (pszAddAttrName && EQUAL(pszAttrName, "PARAMETERVALUE"))
     537             :             {
     538           0 :                 papszMetadata = CSLAddNameValue(papszMetadata, pszAddAttrName,
     539             :                                                 pszAttrValue);
     540           0 :                 pszAddAttrName = nullptr;
     541             :             }
     542             :             else
     543             :             {
     544             :                 // Add class suffix to the key name if applicable.
     545           0 :                 papszMetadata = CSLAddNameValue(
     546             :                     papszMetadata,
     547             :                     pszAttrClass
     548           0 :                         ? CPLSPrintf("%s.%s", pszAttrName, pszAttrClass)
     549           0 :                         : pszAttrName,
     550             :                     pszAttrValue);
     551             :             }
     552             :         }
     553             :     }
     554             : 
     555           0 :     CSLDestroy(papszAttrList);
     556           0 :     CPLFree(pszData);
     557             : 
     558           0 :     return papszMetadata;
     559             : }
     560             : 
     561             : /************************************************************************/
     562             : /*         Translate HDF4 attributes in GDAL metadata items             */
     563             : /************************************************************************/
     564             : 
     565        1723 : char **HDF4Dataset::TranslateHDF4Attributes(int32 iHandle, int32 iAttribute,
     566             :                                             char *pszAttrName, int32 iNumType,
     567             :                                             int32 nValues, char **papszMetadata)
     568             : {
     569             : 
     570             :     /* -------------------------------------------------------------------- */
     571             :     /*     Allocate a buffer to hold the attribute data.                    */
     572             :     /* -------------------------------------------------------------------- */
     573        1723 :     void *pData = nullptr;
     574        1723 :     if (iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8)
     575        1723 :         pData = CPLMalloc((nValues + 1) * GetDataTypeSize(iNumType));
     576             :     else
     577           0 :         pData = CPLMalloc(nValues * GetDataTypeSize(iNumType));
     578             : 
     579             :     /* -------------------------------------------------------------------- */
     580             :     /*     Read the attribute data.                                         */
     581             :     /* -------------------------------------------------------------------- */
     582        1723 :     SDreadattr(iHandle, iAttribute, pData);
     583        1723 :     if (iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8)
     584             :     {
     585        1723 :         reinterpret_cast<char *>(pData)[nValues] = '\0';
     586        1723 :         papszMetadata = CSLAddNameValue(
     587             :             papszMetadata, pszAttrName,
     588             :             const_cast<const char *>(reinterpret_cast<char *>(pData)));
     589             :     }
     590             :     else
     591             :     {
     592             :         char *pszTemp =
     593           0 :             SPrintArray(GetDataType(iNumType), pData, nValues, ", ");
     594           0 :         papszMetadata = CSLAddNameValue(papszMetadata, pszAttrName, pszTemp);
     595           0 :         CPLFree(pszTemp);
     596             :     }
     597             : 
     598        1723 :     CPLFree(pData);
     599             : 
     600        1723 :     return papszMetadata;
     601             : }
     602             : 
     603             : /************************************************************************/
     604             : /*                       ReadGlobalAttributes()                         */
     605             : /************************************************************************/
     606             : 
     607         605 : CPLErr HDF4Dataset::ReadGlobalAttributes(int32 iHandler)
     608             : {
     609             :     /* -------------------------------------------------------------------- */
     610             :     /*     Obtain number of SDSs and global attributes in input file.       */
     611             :     /* -------------------------------------------------------------------- */
     612         605 :     int32 nDatasets = 0;
     613         605 :     int32 nAttributes = 0;
     614         605 :     if (SDfileinfo(iHandler, &nDatasets, &nAttributes) != 0)
     615           0 :         return CE_Failure;
     616             : 
     617         605 :     char szAttrName[H4_MAX_NC_NAME] = {};  // TODO: Get this off the stack.
     618             : 
     619             :     // Loop through the all attributes
     620        2328 :     for (int32 iAttribute = 0; iAttribute < nAttributes; iAttribute++)
     621             :     {
     622        1723 :         int32 iNumType = 0;
     623        1723 :         int32 nValues = 0;
     624             : 
     625             :         // Get information about the attribute. Note that the first
     626             :         // parameter is an SD interface identifier.
     627        1723 :         SDattrinfo(iHandler, iAttribute, szAttrName, &iNumType, &nValues);
     628             : 
     629        1723 :         if (STARTS_WITH_CI(szAttrName, "coremetadata") ||
     630        1723 :             STARTS_WITH_CI(szAttrName, "archivemetadata.") ||
     631        1723 :             STARTS_WITH_CI(szAttrName, "productmetadata.") ||
     632        1723 :             STARTS_WITH_CI(szAttrName, "badpixelinformation") ||
     633        1723 :             STARTS_WITH_CI(szAttrName, "product_summary") ||
     634        1723 :             STARTS_WITH_CI(szAttrName, "dem_specific") ||
     635        1723 :             STARTS_WITH_CI(szAttrName, "bts_specific") ||
     636        1723 :             STARTS_WITH_CI(szAttrName, "etse_specific") ||
     637        1723 :             STARTS_WITH_CI(szAttrName, "dst_specific") ||
     638        1723 :             STARTS_WITH_CI(szAttrName, "acv_specific") ||
     639        1723 :             STARTS_WITH_CI(szAttrName, "act_specific") ||
     640        1723 :             STARTS_WITH_CI(szAttrName, "etst_specific") ||
     641        1723 :             STARTS_WITH_CI(szAttrName, "level_1_carryover"))
     642             :         {
     643           0 :             bIsHDFEOS = true;
     644           0 :             papszGlobalMetadata = TranslateHDF4EOSAttributes(
     645             :                 iHandler, iAttribute, nValues, papszGlobalMetadata);
     646             :         }
     647             : 
     648             :         // Skip "StructMetadata.N" records. We will fetch information
     649             :         // from them using HDF-EOS API
     650        1723 :         else if (STARTS_WITH_CI(szAttrName, "structmetadata."))
     651             :         {
     652           0 :             bIsHDFEOS = true;
     653           0 :             continue;
     654             :         }
     655             : 
     656             :         else
     657             :         {
     658        1723 :             papszGlobalMetadata =
     659        1723 :                 TranslateHDF4Attributes(iHandler, iAttribute, szAttrName,
     660             :                                         iNumType, nValues, papszGlobalMetadata);
     661             :         }
     662             :     }
     663             : 
     664         605 :     return CE_None;
     665             : }
     666             : 
     667             : /************************************************************************/
     668             : /*                            QuoteIfNeeded()                           */
     669             : /************************************************************************/
     670             : 
     671           0 : static CPLString QuoteIfNeeded(const CPLString &osStr)
     672             : {
     673           0 :     if (osStr.find(' ') != std::string::npos ||
     674           0 :         osStr.find(':') != std::string::npos ||
     675           0 :         osStr.find('"') != std::string::npos ||
     676           0 :         osStr.find('\\') != std::string::npos)
     677             :     {
     678           0 :         CPLString osRet;
     679           0 :         for (size_t i = 0; i < osStr.size(); i++)
     680             :         {
     681           0 :             if (osStr[i] == '"')
     682           0 :                 osRet += "\\\"";
     683           0 :             else if (osStr[i] == '\\')
     684           0 :                 osRet += "\\\\";
     685             :             else
     686           0 :                 osRet += osStr[i];
     687             :         }
     688           0 :         return '"' + osRet + '"';
     689             :     }
     690           0 :     return osStr;
     691             : }
     692             : 
     693             : /************************************************************************/
     694             : /*                                Open()                                */
     695             : /************************************************************************/
     696             : 
     697         312 : GDALDataset *HDF4Dataset::Open(GDALOpenInfo *poOpenInfo)
     698             : 
     699             : {
     700             : #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     701             :     // During fuzzing, do not use Identify to reject crazy content.
     702         312 :     if (!HDF4DatasetIdentify(poOpenInfo))
     703           0 :         return nullptr;
     704             : #endif
     705             : 
     706         624 :     CPLMutexHolderD(&hHDF4Mutex);
     707             : 
     708             :     /* -------------------------------------------------------------------- */
     709             :     /*      Try opening the dataset.                                        */
     710             :     /* -------------------------------------------------------------------- */
     711             : 
     712             :     // Attempt to increase maximum number of opened HDF files.
     713             : #ifdef HDF4_HAS_MAXOPENFILES
     714         312 :     intn nCurrMax = 0;
     715         312 :     intn nSysLimit = 0;
     716             : 
     717         312 :     if (SDget_maxopenfiles(&nCurrMax, &nSysLimit) >= 0 && nCurrMax < nSysLimit)
     718             :     {
     719           2 :         /*intn res = */ SDreset_maxopenfiles(nSysLimit);
     720             :     }
     721             : #endif /* HDF4_HAS_MAXOPENFILES */
     722             : 
     723         312 :     int32 hHDF4 = Hopen(poOpenInfo->pszFilename, DFACC_READ, 0);
     724             : 
     725         312 :     if (hHDF4 <= 0)
     726           0 :         return nullptr;
     727             : 
     728         312 :     Hclose(hHDF4);
     729             : 
     730             :     /* -------------------------------------------------------------------- */
     731             :     /*      Create a corresponding GDALDataset.                             */
     732             :     /* -------------------------------------------------------------------- */
     733             :     // Release mutex otherwise we will deadlock with GDALDataset own mutex.
     734         312 :     CPLReleaseMutex(hHDF4Mutex);
     735         312 :     HDF4Dataset *poDS = new HDF4Dataset();
     736         312 :     CPLAcquireMutex(hHDF4Mutex, 1000.0);
     737             : 
     738         312 :     if (poOpenInfo->fpL != nullptr)
     739             :     {
     740         312 :         VSIFCloseL(poOpenInfo->fpL);
     741         312 :         poOpenInfo->fpL = nullptr;
     742             :     }
     743             : 
     744             :     /* -------------------------------------------------------------------- */
     745             :     /*          Open HDF SDS Interface.                                     */
     746             :     /* -------------------------------------------------------------------- */
     747         312 :     poDS->hSD = SDstart(poOpenInfo->pszFilename, DFACC_READ);
     748             : 
     749         312 :     if (poDS->hSD == -1)
     750             :     {
     751             :         // Release mutex otherwise we will deadlock with GDALDataset own mutex.
     752           0 :         CPLReleaseMutex(hHDF4Mutex);
     753           0 :         delete poDS;
     754           0 :         CPLAcquireMutex(hHDF4Mutex, 1000.0);
     755           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     756             :                  "Failed to open HDF4 file \"%s\" for SDS reading.",
     757             :                  poOpenInfo->pszFilename);
     758           0 :         return nullptr;
     759             :     }
     760             : 
     761         312 :     if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
     762             :     {
     763           6 :         poDS->OpenMultiDim(poOpenInfo->pszFilename,
     764           6 :                            poOpenInfo->papszOpenOptions);
     765           6 :         return poDS;
     766             :     }
     767             : 
     768             :     /* -------------------------------------------------------------------- */
     769             :     /*              Now read Global Attributes.                             */
     770             :     /* -------------------------------------------------------------------- */
     771         306 :     if (poDS->ReadGlobalAttributes(poDS->hSD) != CE_None)
     772             :     {
     773             :         // Release mutex otherwise we will deadlock with GDALDataset own mutex.
     774           0 :         CPLReleaseMutex(hHDF4Mutex);
     775           0 :         delete poDS;
     776           0 :         CPLAcquireMutex(hHDF4Mutex, 1000.0);
     777           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     778             :                  "Failed to read global attributes from HDF4 file \"%s\".",
     779             :                  poOpenInfo->pszFilename);
     780           0 :         return nullptr;
     781             :     }
     782             : 
     783         306 :     poDS->SetMetadata(poDS->papszGlobalMetadata, "");
     784             : 
     785             :     /* -------------------------------------------------------------------- */
     786             :     /*              Determine type of file we read.                         */
     787             :     /* -------------------------------------------------------------------- */
     788             :     const char *pszValue =
     789         306 :         CSLFetchNameValue(poDS->papszGlobalMetadata, "Signature");
     790             : 
     791         306 :     if (pszValue != nullptr && EQUAL(pszValue, pszGDALSignature))
     792             :     {
     793         299 :         poDS->iSubdatasetType = H4ST_GDAL;
     794         299 :         poDS->pszSubdatasetType = "GDAL_HDF4";
     795             :     }
     796             : 
     797           7 :     else if ((pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
     798           7 :                                            "Title")) != nullptr &&
     799           0 :              EQUAL(pszValue, "SeaWiFS Level-1A Data"))
     800             :     {
     801           0 :         poDS->iSubdatasetType = H4ST_SEAWIFS_L1A;
     802           0 :         poDS->pszSubdatasetType = "SEAWIFS_L1A";
     803             :     }
     804             : 
     805           7 :     else if ((pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
     806           7 :                                            "Title")) != nullptr &&
     807           0 :              EQUAL(pszValue, "SeaWiFS Level-2 Data"))
     808             :     {
     809           0 :         poDS->iSubdatasetType = H4ST_SEAWIFS_L2;
     810           0 :         poDS->pszSubdatasetType = "SEAWIFS_L2";
     811             :     }
     812             : 
     813           7 :     else if ((pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
     814           7 :                                            "Title")) != nullptr &&
     815           0 :              EQUAL(pszValue, "SeaWiFS Level-3 Standard Mapped Image"))
     816             :     {
     817           0 :         poDS->iSubdatasetType = H4ST_SEAWIFS_L3;
     818           0 :         poDS->pszSubdatasetType = "SEAWIFS_L3";
     819             :     }
     820             : 
     821           7 :     else if ((pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
     822             :                                            "L1 File Generated By")) !=
     823           7 :                  nullptr &&
     824           0 :              STARTS_WITH_CI(pszValue, "HYP version "))
     825             :     {
     826           0 :         poDS->iSubdatasetType = H4ST_HYPERION_L1;
     827           0 :         poDS->pszSubdatasetType = "HYPERION_L1";
     828             :     }
     829             : 
     830             :     else
     831             :     {
     832           7 :         poDS->iSubdatasetType = H4ST_UNKNOWN;
     833           7 :         poDS->pszSubdatasetType = "UNKNOWN";
     834             :     }
     835             : 
     836             :     /* -------------------------------------------------------------------- */
     837             :     /*  If we have HDF-EOS dataset, process it here.                        */
     838             :     /* -------------------------------------------------------------------- */
     839         306 :     int32 aiDimSizes[H4_MAX_VAR_DIMS] = {};  // TODO: Get this off of the stack.
     840         306 :     int32 iRank = 0;
     841         306 :     int32 iNumType = 0;
     842         306 :     int32 nAttrs = 0;
     843             : 
     844             :     // Sometimes "HDFEOSVersion" attribute is not defined and we will
     845             :     // determine HDF-EOS datasets using other records
     846             :     // (see ReadGlobalAttributes() method).
     847         612 :     if (poDS->bIsHDFEOS ||
     848         306 :         CSLFetchNameValue(poDS->papszGlobalMetadata, "HDFEOSVersion"))
     849             :     {
     850             :         /* --------------------------------------------------------------------
     851             :          */
     852             :         /*  Process swath layers. */
     853             :         /* --------------------------------------------------------------------
     854             :          */
     855           0 :         hHDF4 = SWopen(poOpenInfo->pszFilename, DFACC_READ);
     856           0 :         if (hHDF4 < 0)
     857             :         {
     858             :             // Release mutex otherwise we will deadlock with GDALDataset own
     859             :             // mutex.
     860           0 :             CPLReleaseMutex(hHDF4Mutex);
     861           0 :             delete poDS;
     862           0 :             CPLAcquireMutex(hHDF4Mutex, 1000.0);
     863           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     864             :                      "Failed to open HDF-EOS file \"%s\" for swath reading.",
     865             :                      poOpenInfo->pszFilename);
     866           0 :             return nullptr;
     867             :         }
     868           0 :         int32 nStrBufSize = 0;
     869             :         int32 nSubDatasets =
     870           0 :             SWinqswath(poOpenInfo->pszFilename, nullptr, &nStrBufSize);
     871             : 
     872             : #ifdef DEBUG
     873           0 :         CPLDebug("HDF4", "Number of HDF-EOS swaths: %d",
     874             :                  static_cast<int>(nSubDatasets));
     875             : #endif
     876             : 
     877           0 :         if (nSubDatasets > 0 && nStrBufSize > 0)
     878             :         {
     879             :             char *pszSwathList =
     880           0 :                 static_cast<char *>(CPLMalloc(nStrBufSize + 1));
     881           0 :             SWinqswath(poOpenInfo->pszFilename, pszSwathList, &nStrBufSize);
     882           0 :             pszSwathList[nStrBufSize] = '\0';
     883             : 
     884             : #ifdef DEBUG
     885           0 :             CPLDebug("HDF4", "List of HDF-EOS swaths: %s", pszSwathList);
     886             : #endif
     887             : 
     888             :             char **papszSwaths =
     889           0 :                 CSLTokenizeString2(pszSwathList, ",", CSLT_HONOURSTRINGS);
     890           0 :             CPLFree(pszSwathList);
     891             : 
     892           0 :             if (nSubDatasets != CSLCount(papszSwaths))
     893             :             {
     894           0 :                 CSLDestroy(papszSwaths);
     895             :                 // Release mutex otherwise we will deadlock with GDALDataset own
     896             :                 // mutex.
     897           0 :                 CPLReleaseMutex(hHDF4Mutex);
     898           0 :                 delete poDS;
     899           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
     900           0 :                 CPLDebug("HDF4", "Cannot parse list of HDF-EOS grids.");
     901           0 :                 return nullptr;
     902             :             }
     903             : 
     904           0 :             for (int32 i = 0; i < nSubDatasets; i++)
     905             :             {
     906           0 :                 const int32 hSW = SWattach(hHDF4, papszSwaths[i]);
     907             : 
     908             :                 const int32 nFields =
     909           0 :                     SWnentries(hSW, HDFE_NENTDFLD, &nStrBufSize);
     910             :                 char *pszFieldList =
     911           0 :                     static_cast<char *>(CPLMalloc(nStrBufSize + 1));
     912             :                 int32 *paiRank =
     913           0 :                     static_cast<int32 *>(CPLMalloc(nFields * sizeof(int32)));
     914             :                 int32 *paiNumType =
     915           0 :                     static_cast<int32 *>(CPLMalloc(nFields * sizeof(int32)));
     916             : 
     917           0 :                 SWinqdatafields(hSW, pszFieldList, paiRank, paiNumType);
     918             : 
     919             : #ifdef DEBUG
     920             :                 {
     921             :                     char *const pszTmp =
     922           0 :                         SPrintArray(GDT_UInt32, paiRank, nFields, ",");
     923             : 
     924           0 :                     CPLDebug("HDF4", "Number of data fields in swath %d: %d",
     925             :                              static_cast<int>(i), static_cast<int>(nFields));
     926           0 :                     CPLDebug("HDF4", "List of data fields in swath %d: %s",
     927             :                              static_cast<int>(i), pszFieldList);
     928           0 :                     CPLDebug("HDF4", "Data fields ranks: %s", pszTmp);
     929             : 
     930           0 :                     CPLFree(pszTmp);
     931             :                 }
     932             : #endif
     933             : 
     934             :                 char **papszFields =
     935           0 :                     CSLTokenizeString2(pszFieldList, ",", CSLT_HONOURSTRINGS);
     936             : 
     937           0 :                 char szTemp[256] = {'\0'};  // TODO: Get this off the stack.
     938           0 :                 for (int32 j = 0; j < nFields; j++)
     939             :                 {
     940           0 :                     SWfieldinfo(hSW, papszFields[j], &iRank, aiDimSizes,
     941             :                                 &iNumType, nullptr);
     942             : 
     943           0 :                     if (iRank < 2)
     944           0 :                         continue;
     945             : 
     946             :                     // Add field to the list of GDAL subdatasets.
     947           0 :                     const int nCount = CSLCount(poDS->papszSubDatasets) / 2;
     948           0 :                     snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME",
     949             :                              nCount + 1);
     950             :                     // We will use the field index as an identificator.
     951           0 :                     poDS->papszSubDatasets = CSLSetNameValue(
     952             :                         poDS->papszSubDatasets, szTemp,
     953             :                         CPLSPrintf("HDF4_EOS:EOS_SWATH:\"%s\":%s:%s",
     954             :                                    poOpenInfo->pszFilename,
     955           0 :                                    QuoteIfNeeded(papszSwaths[i]).c_str(),
     956           0 :                                    QuoteIfNeeded(papszFields[j]).c_str()));
     957             : 
     958           0 :                     snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC",
     959             :                              nCount + 1);
     960             :                     char *pszString =
     961           0 :                         SPrintArray(GDT_UInt32, aiDimSizes, iRank, "x");
     962           0 :                     poDS->papszSubDatasets = CSLSetNameValue(
     963             :                         poDS->papszSubDatasets, szTemp,
     964           0 :                         CPLSPrintf("[%s] %s %s (%s)", pszString, papszFields[j],
     965           0 :                                    papszSwaths[i],
     966             :                                    poDS->GetDataTypeName(iNumType)));
     967           0 :                     CPLFree(pszString);
     968           0 :                     szTemp[0] = '\0';
     969             :                 }
     970             : 
     971           0 :                 CSLDestroy(papszFields);
     972           0 :                 CPLFree(paiNumType);
     973           0 :                 CPLFree(paiRank);
     974           0 :                 CPLFree(pszFieldList);
     975           0 :                 SWdetach(hSW);
     976             :             }
     977             : 
     978           0 :             CSLDestroy(papszSwaths);
     979             :         }
     980           0 :         SWclose(hHDF4);
     981             : 
     982             :         /* --------------------------------------------------------------------
     983             :          */
     984             :         /*  Process grid layers. */
     985             :         /* --------------------------------------------------------------------
     986             :          */
     987           0 :         hHDF4 = GDopen(poOpenInfo->pszFilename, DFACC_READ);
     988             :         nSubDatasets =
     989           0 :             GDinqgrid(poOpenInfo->pszFilename, nullptr, &nStrBufSize);
     990             : 
     991             : #ifdef DEBUG
     992           0 :         CPLDebug("HDF4", "Number of HDF-EOS grids: %d",
     993             :                  static_cast<int>(nSubDatasets));
     994             : #endif
     995             : 
     996           0 :         if (nSubDatasets > 0 && nStrBufSize > 0)
     997             :         {
     998           0 :             char *pszGridList = static_cast<char *>(CPLMalloc(nStrBufSize + 1));
     999           0 :             GDinqgrid(poOpenInfo->pszFilename, pszGridList, &nStrBufSize);
    1000             : 
    1001             : #ifdef DEBUG
    1002           0 :             CPLDebug("HDF4", "List of HDF-EOS grids: %s", pszGridList);
    1003             : #endif
    1004             : 
    1005             :             char **papszGrids =
    1006           0 :                 CSLTokenizeString2(pszGridList, ",", CSLT_HONOURSTRINGS);
    1007           0 :             CPLFree(pszGridList);
    1008             : 
    1009           0 :             if (nSubDatasets != CSLCount(papszGrids))
    1010             :             {
    1011           0 :                 CSLDestroy(papszGrids);
    1012           0 :                 GDclose(hHDF4);
    1013             :                 // Release mutex otherwise we will deadlock with GDALDataset own
    1014             :                 // mutex.
    1015           0 :                 CPLReleaseMutex(hHDF4Mutex);
    1016           0 :                 delete poDS;
    1017           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    1018           0 :                 CPLDebug("HDF4", "Cannot parse list of HDF-EOS grids.");
    1019           0 :                 return nullptr;
    1020             :             }
    1021             : 
    1022           0 :             for (int32 i = 0; i < nSubDatasets; i++)
    1023             :             {
    1024           0 :                 const int32 hGD = GDattach(hHDF4, papszGrids[i]);
    1025             : 
    1026             :                 const int32 nFields =
    1027           0 :                     GDnentries(hGD, HDFE_NENTDFLD, &nStrBufSize);
    1028             :                 char *pszFieldList =
    1029           0 :                     static_cast<char *>(CPLMalloc(nStrBufSize + 1));
    1030             :                 int32 *paiRank =
    1031           0 :                     static_cast<int32 *>(CPLMalloc(nFields * sizeof(int32)));
    1032             :                 int32 *paiNumType =
    1033           0 :                     static_cast<int32 *>(CPLMalloc(nFields * sizeof(int32)));
    1034             : 
    1035           0 :                 GDinqfields(hGD, pszFieldList, paiRank, paiNumType);
    1036             : 
    1037             : #ifdef DEBUG
    1038             :                 {
    1039             :                     char *pszTmp =
    1040           0 :                         SPrintArray(GDT_UInt32, paiRank, nFields, ",");
    1041           0 :                     CPLDebug("HDF4", "Number of fields in grid %d: %d",
    1042             :                              static_cast<int>(i), static_cast<int>(nFields));
    1043           0 :                     CPLDebug("HDF4", "List of fields in grid %d: %s",
    1044             :                              static_cast<int>(i), pszFieldList);
    1045           0 :                     CPLDebug("HDF4", "Fields ranks: %s", pszTmp);
    1046           0 :                     CPLFree(pszTmp);
    1047             :                 }
    1048             : #endif
    1049             : 
    1050             :                 char **papszFields =
    1051           0 :                     CSLTokenizeString2(pszFieldList, ",", CSLT_HONOURSTRINGS);
    1052             : 
    1053             :                 char szTemp[256];
    1054           0 :                 for (int32 j = 0; j < nFields; j++)
    1055             :                 {
    1056           0 :                     GDfieldinfo(hGD, papszFields[j], &iRank, aiDimSizes,
    1057             :                                 &iNumType, nullptr);
    1058             : 
    1059           0 :                     if (iRank < 2)
    1060           0 :                         continue;
    1061             : 
    1062             :                     // Add field to the list of GDAL subdatasets
    1063           0 :                     const int nCount = CSLCount(poDS->papszSubDatasets) / 2;
    1064           0 :                     snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME",
    1065             :                              nCount + 1);
    1066             :                     // We will use the field index as an identificator.
    1067           0 :                     poDS->papszSubDatasets = CSLSetNameValue(
    1068             :                         poDS->papszSubDatasets, szTemp,
    1069             :                         CPLSPrintf("HDF4_EOS:EOS_GRID:\"%s\":%s:%s",
    1070             :                                    poOpenInfo->pszFilename,
    1071           0 :                                    QuoteIfNeeded(papszGrids[i]).c_str(),
    1072           0 :                                    QuoteIfNeeded(papszFields[j]).c_str()));
    1073             : 
    1074           0 :                     snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC",
    1075             :                              nCount + 1);
    1076             :                     char *pszString =
    1077           0 :                         SPrintArray(GDT_UInt32, aiDimSizes, iRank, "x");
    1078           0 :                     poDS->papszSubDatasets = CSLSetNameValue(
    1079             :                         poDS->papszSubDatasets, szTemp,
    1080           0 :                         CPLSPrintf("[%s] %s %s (%s)", pszString, papszFields[j],
    1081           0 :                                    papszGrids[i],
    1082             :                                    poDS->GetDataTypeName(iNumType)));
    1083           0 :                     CPLFree(pszString);
    1084             :                 }
    1085             : 
    1086           0 :                 CSLDestroy(papszFields);
    1087           0 :                 CPLFree(paiNumType);
    1088           0 :                 CPLFree(paiRank);
    1089           0 :                 CPLFree(pszFieldList);
    1090           0 :                 GDdetach(hGD);
    1091             :             }
    1092             : 
    1093           0 :             CSLDestroy(papszGrids);
    1094             :         }
    1095           0 :         GDclose(hHDF4);
    1096             :     }
    1097             : 
    1098             :     char szName[VSNAMELENMAX + 1];
    1099             : 
    1100             :     const char *pszListSDS =
    1101         306 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "LIST_SDS", "AUTO");
    1102         306 :     if ((poDS->papszSubDatasets == nullptr && EQUAL(pszListSDS, "AUTO")) ||
    1103           0 :         (!EQUAL(pszListSDS, "AUTO") && CPLTestBool(pszListSDS)))
    1104             :     {
    1105             : 
    1106             :         /* --------------------------------------------------------------------
    1107             :          */
    1108             :         /*  Make a list of subdatasets from SDSs contained in input HDF file. */
    1109             :         /* --------------------------------------------------------------------
    1110             :          */
    1111         306 :         int32 nDatasets = 0;
    1112             : 
    1113         306 :         if (SDfileinfo(poDS->hSD, &nDatasets, &nAttrs) != 0)
    1114           0 :             return nullptr;
    1115             : 
    1116         306 :         char szTemp[256] = {'\0'};  // TODO: Get this off the stack.
    1117         306 :         const char *pszName = nullptr;
    1118             : 
    1119         608 :         for (int32 i = 0; i < nDatasets; i++)
    1120             :         {
    1121         302 :             const int32 iSDS = SDselect(poDS->hSD, i);
    1122         302 :             if (SDgetinfo(iSDS, szName, &iRank, aiDimSizes, &iNumType,
    1123         302 :                           &nAttrs) != 0)
    1124           0 :                 return nullptr;
    1125             : 
    1126         302 :             if (iRank == 1)  // Skip 1D datasets
    1127           0 :                 continue;
    1128             : 
    1129             :             // Do sort of known datasets. We will display only image bands
    1130         302 :             if ((poDS->iSubdatasetType == H4ST_SEAWIFS_L1A) &&
    1131           0 :                 !STARTS_WITH_CI(szName, "l1a_data"))
    1132           0 :                 continue;
    1133             :             else
    1134         302 :                 pszName = szName;
    1135             : 
    1136             :             // Add datasets with multiple dimensions to the list of GDAL
    1137             :             // subdatasets.
    1138         302 :             const int nCount = CSLCount(poDS->papszSubDatasets) / 2;
    1139         302 :             snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME", nCount + 1);
    1140             :             // We will use SDS index as an identificator, because SDS names
    1141             :             // are not unique. Filename also needed for further file opening
    1142         302 :             poDS->papszSubDatasets = CSLSetNameValue(
    1143             :                 poDS->papszSubDatasets, szTemp,
    1144             :                 CPLSPrintf("HDF4_SDS:%s:\"%s\":%ld", poDS->pszSubdatasetType,
    1145             :                            poOpenInfo->pszFilename, static_cast<long>(i)));
    1146         302 :             snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC", nCount + 1);
    1147         302 :             char *pszString = SPrintArray(GDT_UInt32, aiDimSizes, iRank, "x");
    1148         302 :             poDS->papszSubDatasets =
    1149         302 :                 CSLSetNameValue(poDS->papszSubDatasets, szTemp,
    1150             :                                 CPLSPrintf("[%s] %s (%s)", pszString, pszName,
    1151             :                                            poDS->GetDataTypeName(iNumType)));
    1152         302 :             CPLFree(pszString);
    1153             : 
    1154         302 :             SDendaccess(iSDS);
    1155         302 :             szTemp[0] = '\0';
    1156             :         }
    1157             : 
    1158         306 :         SDend(poDS->hSD);
    1159         306 :         poDS->hSD = 0;
    1160             :     }
    1161             : 
    1162             :     /* -------------------------------------------------------------------- */
    1163             :     /*      Build a list of raster images. Note, that HDF-EOS dataset may   */
    1164             :     /*      contain a raster image as well.                                 */
    1165             :     /* -------------------------------------------------------------------- */
    1166             : 
    1167         306 :     hHDF4 = Hopen(poOpenInfo->pszFilename, DFACC_READ, 0);
    1168         306 :     poDS->hGR = GRstart(hHDF4);
    1169             : 
    1170         306 :     if (poDS->hGR != -1)
    1171             :     {
    1172         306 :         if (GRfileinfo(poDS->hGR, &poDS->nImages, &nAttrs) == -1)
    1173             :         {
    1174             :             // Release mutex otherwise we will deadlock with GDALDataset own
    1175             :             // mutex.
    1176           0 :             CPLReleaseMutex(hHDF4Mutex);
    1177           0 :             GRend(poDS->hGR);
    1178           0 :             poDS->hGR = 0;
    1179           0 :             Hclose(hHDF4);
    1180           0 :             delete poDS;
    1181           0 :             CPLAcquireMutex(hHDF4Mutex, 1000.0);
    1182           0 :             return nullptr;
    1183             :         }
    1184             : 
    1185         306 :         char szTemp[256] = {'\0'};  // TODO: Get this off the stack.
    1186         308 :         for (int32 i = 0; i < poDS->nImages; i++)
    1187             :         {
    1188           2 :             const int32 iGR = GRselect(poDS->hGR, i);
    1189             : 
    1190             :             // iRank in GR interface has another meaning. It represents number
    1191             :             // of samples per pixel. aiDimSizes has only two dimensions.
    1192           2 :             int32 iInterlaceMode = 0;
    1193           2 :             if (GRgetiminfo(iGR, szName, &iRank, &iNumType, &iInterlaceMode,
    1194           2 :                             aiDimSizes, &nAttrs) != 0)
    1195             :             {
    1196             :                 // Release mutex otherwise we will deadlock with GDALDataset
    1197             :                 // own mutex.
    1198           0 :                 CPLReleaseMutex(hHDF4Mutex);
    1199           0 :                 GRend(poDS->hGR);
    1200           0 :                 poDS->hGR = 0;
    1201           0 :                 Hclose(hHDF4);
    1202           0 :                 delete poDS;
    1203           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    1204           0 :                 return nullptr;
    1205             :             }
    1206           2 :             const int nCount = CSLCount(poDS->papszSubDatasets) / 2;
    1207           2 :             snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME", nCount + 1);
    1208           2 :             poDS->papszSubDatasets = CSLSetNameValue(
    1209             :                 poDS->papszSubDatasets, szTemp,
    1210             :                 CPLSPrintf("HDF4_GR:UNKNOWN:\"%s\":%ld",
    1211             :                            poOpenInfo->pszFilename, static_cast<long>(i)));
    1212           2 :             snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC", nCount + 1);
    1213           2 :             char *pszString = SPrintArray(GDT_UInt32, aiDimSizes, 2, "x");
    1214           2 :             poDS->papszSubDatasets =
    1215           2 :                 CSLSetNameValue(poDS->papszSubDatasets, szTemp,
    1216             :                                 CPLSPrintf("[%sx%ld] %s (%s)", pszString,
    1217             :                                            static_cast<long>(iRank), szName,
    1218             :                                            poDS->GetDataTypeName(iNumType)));
    1219           2 :             CPLFree(pszString);
    1220             : 
    1221           2 :             GRendaccess(iGR);
    1222           2 :             szTemp[0] = '\0';
    1223             :         }
    1224             : 
    1225         306 :         GRend(poDS->hGR);
    1226         306 :         poDS->hGR = 0;
    1227             :     }
    1228             : 
    1229         306 :     Hclose(hHDF4);
    1230             : 
    1231         306 :     poDS->nRasterXSize = 512;  // XXX: bogus value
    1232         306 :     poDS->nRasterYSize = 512;  // XXX: bogus value
    1233             : 
    1234             :     // Make sure we don't try to do any pam stuff with this dataset.
    1235         306 :     poDS->nPamFlags |= GPF_NOSAVE;
    1236             : 
    1237             :     /* -------------------------------------------------------------------- */
    1238             :     /*      If we have single subdataset only, open it immediately          */
    1239             :     /* -------------------------------------------------------------------- */
    1240         306 :     if (CSLCount(poDS->papszSubDatasets) / 2 == 1)
    1241             :     {
    1242         301 :         char *pszSDSName = CPLStrdup(
    1243         301 :             CSLFetchNameValue(poDS->papszSubDatasets, "SUBDATASET_1_NAME"));
    1244             :         // Release mutex otherwise we will deadlock with GDALDataset own mutex.
    1245         301 :         CPLReleaseMutex(hHDF4Mutex);
    1246         301 :         delete poDS;
    1247         301 :         poDS = nullptr;
    1248             : 
    1249             :         GDALDataset *poRetDS =
    1250         301 :             GDALDataset::FromHandle(GDALOpen(pszSDSName, poOpenInfo->eAccess));
    1251         301 :         CPLFree(pszSDSName);
    1252             : 
    1253         301 :         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    1254             : 
    1255         301 :         if (poRetDS)
    1256             :         {
    1257         301 :             poRetDS->SetDescription(poOpenInfo->pszFilename);
    1258             :         }
    1259             : 
    1260         301 :         return poRetDS;
    1261             :     }
    1262             :     else
    1263             :     {
    1264             :         /* --------------------------------------------------------------------
    1265             :          */
    1266             :         /*      Confirm the requested access is supported. */
    1267             :         /* --------------------------------------------------------------------
    1268             :          */
    1269           5 :         if (poOpenInfo->eAccess == GA_Update)
    1270             :         {
    1271             :             // Release mutex otherwise we will deadlock with GDALDataset own
    1272             :             // mutex.
    1273           0 :             CPLReleaseMutex(hHDF4Mutex);
    1274           0 :             delete poDS;
    1275           0 :             CPLAcquireMutex(hHDF4Mutex, 1000.0);
    1276             : 
    1277           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1278             :                      "The HDF4 driver does not support update access to "
    1279             :                      "existing datasets.");
    1280           0 :             return nullptr;
    1281             :         }
    1282             :     }
    1283             : 
    1284           5 :     return poDS;
    1285             : }
    1286             : 
    1287             : /************************************************************************/
    1288             : /*                           HDF4UnloadDriver()                         */
    1289             : /************************************************************************/
    1290             : 
    1291           5 : static void HDF4UnloadDriver(GDALDriver * /* poDriver */)
    1292             : {
    1293           5 :     if (hHDF4Mutex != nullptr)
    1294           0 :         CPLDestroyMutex(hHDF4Mutex);
    1295           5 :     hHDF4Mutex = nullptr;
    1296           5 : }
    1297             : 
    1298             : /************************************************************************/
    1299             : /*                        GDALRegister_HDF4()                           */
    1300             : /************************************************************************/
    1301             : 
    1302           9 : void GDALRegister_HDF4()
    1303             : 
    1304             : {
    1305           9 :     if (!GDAL_CHECK_VERSION("HDF4 driver"))
    1306           0 :         return;
    1307             : 
    1308           9 :     if (GDALGetDriverByName(HDF4_DRIVER_NAME) != nullptr)
    1309           0 :         return;
    1310             : 
    1311           9 :     GDALDriver *poDriver = new GDALDriver();
    1312           9 :     HDF4DriverSetCommonMetadata(poDriver);
    1313           9 :     poDriver->pfnOpen = HDF4Dataset::Open;
    1314           9 :     poDriver->pfnUnloadDriver = HDF4UnloadDriver;
    1315             : 
    1316           9 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1317             : 
    1318             : #ifdef HDF4_HAS_MAXOPENFILES
    1319           9 :     poDriver->SetMetadataItem("HDF4_HAS_MAXOPENFILES", "YES");
    1320             : #endif
    1321             : 
    1322             : #ifdef HDF4_PLUGIN
    1323           9 :     GDALRegister_HDF4Image();
    1324             : #endif
    1325             : }

Generated by: LCOV version 1.14