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

Generated by: LCOV version 1.14