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 : }
|