1 /* Copyright 2018, UCAR/Unidata See netcdf/COPYRIGHT file for copying
2  * and redistribution conditions.*/
3 /**
4  * @file
5  * @internal The HDF4 file functions. These provide a read-only
6  * interface to HDF4 SD files.
7  *
8  * @author Ed Hartnett
9  */
10 
11 #include "config.h"
12 #include "nc4internal.h"
13 #include "hdf4dispatch.h"
14 #include <mfhdf.h>
15 
16 #define NUM_TYPES 12 /**< Number of netCDF atomic types. */
17 
18 extern int nc4_vararray_add(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var);
19 
20 /** @internal These flags may not be set for open mode. */
21 static const int
22 ILLEGAL_OPEN_FLAGS = (NC_MMAP|NC_64BIT_OFFSET|NC_DISKLESS|NC_WRITE);
23 
24 /** @internal NetCDF atomic type names. */
25 static const char*
26 nc_type_name_g[NUM_TYPES] = {"char", "byte", "short", "int", "float", "double",
27                              "ubyte", "ushort", "uint", "int64", "uint64",
28                              "string"};
29 
30 /** @internal NetCDF atomic type sizes. */
31 static const size_t
32 nc_type_size_g[NUM_TYPES] = {sizeof(char), sizeof(char), sizeof(short),
33                              sizeof(int), sizeof(float), sizeof(double),
34                              sizeof(unsigned char), sizeof(unsigned short),
35                              sizeof(unsigned int), sizeof(long long),
36                              sizeof(unsigned long long), sizeof(char *)};
37 
38 /**
39  * @internal Recursively delete the data for a group (and everything
40  * it contains) in our internal metadata store.
41  *
42  * @param grp Pointer to group info struct.
43  *
44  * @return ::NC_NOERR No error.
45  * @author Ed Hartnett
46  */
47 static int
hdf4_rec_grp_del(NC_GRP_INFO_T * grp)48 hdf4_rec_grp_del(NC_GRP_INFO_T *grp)
49 {
50     NC_VAR_INFO_T *var;
51     int i;
52 
53     assert(grp);
54     LOG((3, "%s: grp->name %s", __func__, grp->hdr.name));
55 
56     /* Delete all vars. */
57     for (i = 0; i < ncindexsize(grp->vars); i++)
58     {
59         NC_VAR_HDF4_INFO_T *hdf4_var;
60 
61         var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
62         assert(var);
63         LOG((4, "%s: deleting var %s", __func__, var->hdr.name));
64 
65         /* Get the HDF4 specific var metadata. */
66         hdf4_var = (NC_VAR_HDF4_INFO_T *)var->format_var_info;
67 
68         /* Close HDF4 dataset associated with this var, unless it's a
69          * scale. */
70         if (hdf4_var->sdsid && SDendaccess(hdf4_var->sdsid) < 0)
71             return NC_EHDFERR;
72     }
73 
74     return NC_NOERR;
75 }
76 
77 /**
78  * @internal Given an HDF4 type, set a pointer to netcdf type.
79  *
80  * See http://www.hdfgroup.org/training/HDFtraining/UsersGuide/Fundmtls.fm3.html
81  * for more information re: HDF4 types.
82  *
83  * @param h5 Pointer to HDF5 file info struct.
84  * @param hdf4_typeid Type ID for hdf4 datatype.
85  * @param xtype Pointer that gets netcdf type. Ignored if NULL.
86  * @param endniannessp Pointer that gets endianness. Ignored if NULL.
87  * @param type_sizep Pointer that gets type size. Ignored if NULL.
88  * @param type_name Pointer that gets the type name. Ignored if NULL.
89  *
90  * @return ::NC_NOERR No error.
91  * @author Ed Hartnett
92  */
93 static int
hdf4_type_info(NC_FILE_INFO_T * h5,int32 hdf4_typeid,nc_type * xtypep,int * endiannessp,size_t * type_sizep,char * type_name)94 hdf4_type_info(NC_FILE_INFO_T *h5, int32 hdf4_typeid, nc_type* xtypep,
95                int *endiannessp, size_t *type_sizep, char *type_name)
96 {
97     int t = 0;
98     int endianness = NC_ENDIAN_BIG;
99     nc_type xtype;
100 
101     assert(h5);
102 
103     switch(hdf4_typeid)
104     {
105     case DFNT_CHAR:
106         xtype = NC_CHAR;
107         t = 0;
108         break;
109     case DFNT_UCHAR:
110     case DFNT_UINT8:
111         xtype = NC_UBYTE;
112         t = 6;
113         break;
114     case DFNT_LUINT8:
115         xtype = NC_UBYTE;
116         t = 6;
117         endianness = NC_ENDIAN_LITTLE;
118         break;
119     case DFNT_INT8:
120         xtype = NC_BYTE;
121         t = 1;
122         break;
123     case DFNT_LINT8:
124         xtype = NC_BYTE;
125         t = 1;
126         endianness = NC_ENDIAN_LITTLE;
127         break;
128     case DFNT_INT16:
129         xtype = NC_SHORT;
130         t = 2;
131         break;
132     case DFNT_LINT16:
133         xtype = NC_SHORT;
134         t = 2;
135         endianness = NC_ENDIAN_LITTLE;
136         break;
137     case DFNT_UINT16:
138         xtype = NC_USHORT;
139         t = 7;
140         break;
141     case DFNT_LUINT16:
142         xtype = NC_USHORT;
143         t = 7;
144         endianness = NC_ENDIAN_LITTLE;
145         break;
146     case DFNT_INT32:
147         xtype = NC_INT;
148         t = 3;
149         break;
150     case DFNT_LINT32:
151         xtype = NC_INT;
152         t = 3;
153         endianness = NC_ENDIAN_LITTLE;
154         break;
155     case DFNT_UINT32:
156         xtype = NC_UINT;
157         t = 8;
158         break;
159     case DFNT_LUINT32:
160         xtype = NC_UINT;
161         t = 8;
162         endianness = NC_ENDIAN_LITTLE;
163         break;
164     case DFNT_FLOAT32:
165         xtype = NC_FLOAT;
166         t = 4;
167         break;
168     case DFNT_LFLOAT32:
169         xtype = NC_FLOAT;
170         t = 4;
171         endianness = NC_ENDIAN_LITTLE;
172         break;
173     case DFNT_FLOAT64:
174         xtype = NC_DOUBLE;
175         t = 5;
176         break;
177     case DFNT_LFLOAT64:
178         xtype = NC_DOUBLE;
179         t = 5;
180         endianness = NC_ENDIAN_LITTLE;
181         break;
182     default:
183         return NC_EBADTYPID;
184     }
185 
186     /* Return results to caller. */
187     if (xtypep)
188         *xtypep = xtype;
189     if (endiannessp)
190         *endiannessp = endianness;
191     if (type_sizep)
192         *type_sizep = nc_type_size_g[t];
193     if (type_name)
194         strncpy(type_name, nc_type_name_g[t], NC_MAX_NAME);
195 
196     return NC_NOERR;
197 }
198 
199 /**
200  * @internal Set the type of a netCDF-4 variable.
201  *
202  * @param xtype A netcdf type.
203  * @param endianness The endianness of the data.
204  * @param type_size The size in bytes of one element of this type.
205  * @param type_name A name for the type.
206  * @param typep Pointer to a pointer that gets the TYPE_INFO_T struct.
207  *
208  * @return ::NC_NOERR No error.
209  * @author Ed Hartnett
210  */
211 static int
nc4_set_var_type(nc_type xtype,int endianness,size_t type_size,char * type_name,NC_TYPE_INFO_T ** typep)212 nc4_set_var_type(nc_type xtype, int endianness, size_t type_size, char *type_name,
213                  NC_TYPE_INFO_T **typep)
214 {
215     NC_TYPE_INFO_T *type;
216 
217     /* Check inputs. */
218     assert(typep);
219 
220     /* Allocate space for the type info struct. */
221     if (!(type = calloc(1, sizeof(NC_TYPE_INFO_T))))
222         return NC_ENOMEM;
223     if (!(type->hdr.name = strdup(type_name)))
224     {
225         free(type);
226         return NC_ENOMEM;
227     }
228     type->hdr.sort = NCTYP;
229 
230     /* Determine the type class. */
231     if (xtype == NC_FLOAT)
232         type->nc_type_class = NC_FLOAT;
233     else if (xtype == NC_DOUBLE)
234         type->nc_type_class = NC_DOUBLE;
235     else if (xtype == NC_CHAR)
236         type->nc_type_class = NC_STRING;
237     else
238         type->nc_type_class = NC_INT;
239 
240     /* Set other type info values. */
241     type->endianness = endianness;
242     type->size = type_size;
243     type->hdr.id = (size_t)xtype;
244     type->hdr.hashkey = NC_hashmapkey(type->hdr.name, strlen(type->hdr.name));
245 
246     /* Return to caller. */
247     *typep = type;
248 
249     return NC_NOERR;
250 }
251 
252 /**
253  * @internal Read an attribute from a HDF4 file.
254  *
255  * @param h5 Pointer to the file metadata struct.
256  * @param var Pointer to variable metadata struct or NULL for global
257  * attributes.
258  * @param a Index of attribute to read.
259  *
260  * @return ::NC_NOERR No error.
261  * @return ::NC_EHDFERR HDF4 error.
262  * @return ::NC_EATTMETA Error reading HDF4 attribute.
263  * @return ::NC_ENOMEM Out of memory.
264  * @author Ed Hartnett
265  */
266 static int
hdf4_read_att(NC_FILE_INFO_T * h5,NC_VAR_INFO_T * var,int a)267 hdf4_read_att(NC_FILE_INFO_T *h5, NC_VAR_INFO_T *var, int a)
268 {
269     NC_HDF4_FILE_INFO_T *hdf4_file;
270     NC_ATT_INFO_T *att;
271     NCindex *att_list;
272     int32 att_data_type, att_count;
273     size_t att_type_size;
274     char name[NC_MAX_HDF4_NAME+1];
275     int sd_id;
276     int retval;
277 
278     LOG((3, "%s: a %d var %s", __func__, a, var ? var->hdr.name : "global"));
279 
280     /* Check inputs. */
281     assert(h5 && h5->format_file_info);
282 
283     /* Get the HDF4 file info. */
284     hdf4_file = h5->format_file_info;
285 
286     /* Decide what att list to use, global or from a var. */
287     if (var)
288     {
289         NC_VAR_HDF4_INFO_T *hdf4_var;
290         assert(var->format_var_info);
291         att_list = var->att;
292         hdf4_var = var->format_var_info;
293         sd_id = hdf4_var->sdsid;
294     } else {
295         att_list = h5->root_grp->att;
296         sd_id = hdf4_file->sdid;
297     }
298 
299     /* Learn about this attribute. */
300     if (SDattrinfo(sd_id, a, name, &att_data_type, &att_count))
301         return NC_EATTMETA;
302 
303     /* Get information about the attribute type. */
304     nc_type xtype;
305     if ((retval = hdf4_type_info(h5, att_data_type, &xtype, NULL,
306                                  &att_type_size, NULL)))
307         return retval;
308 
309     /* Add to the end of the list of atts for this var. */
310     if ((retval = nc4_att_list_add(att_list, name, &att)))
311         return retval;
312     att->nc_typeid = xtype;
313     att->created = NC_TRUE;
314     att->len = att_count;
315 
316     /* Allocate memory to hold the data. */
317     if (att->len)
318         if (!(att->data = malloc(att_type_size * att->len)))
319             return NC_ENOMEM;
320 
321     /* Read the data. */
322     if (SDreadattr(sd_id, a, att->data))
323         return NC_EHDFERR;
324 
325     return NC_NOERR;
326 }
327 
328 /**
329  * @internal Read a HDF4 dimension. As new dimensions are found, add
330  * them to the metadata list of dimensions.
331  *
332  * @param h5 Pointer to the file metadata struct.
333  * @param var Pointer to variable metadata struct or NULL for global
334  * attributes.
335  * @param rec_dim_len Actual length of first dim for this SD.
336  * @param d Dimension index for this SD.
337  *
338  * @return ::NC_NOERR No error.
339  * @return ::NC_EHDFERR HDF4 error.
340  * @return ::NC_EDIMMETA Error reading HDF4 dimension info.
341  * @return ::NC_ENOMEM Out of memory.
342  * @return ::NC_EMAXNAME Name too long.
343  * @author Ed Hartnett
344  */
345 static int
hdf4_read_dim(NC_FILE_INFO_T * h5,NC_VAR_INFO_T * var,int rec_dim_len,int d)346 hdf4_read_dim(NC_FILE_INFO_T *h5, NC_VAR_INFO_T *var, int rec_dim_len, int d)
347 {
348     NC_VAR_HDF4_INFO_T *hdf4_var;
349     NC_DIM_INFO_T *dim = NULL;
350     int32 dimid, dim_len, dim_data_type, dim_num_attrs;
351     char dim_name[NC_MAX_NAME + 1];
352     int i;
353     int retval;
354 
355     assert(h5 && h5->format_file_info && var && var->format_var_info);
356     hdf4_var = var->format_var_info;
357 
358     /* Get information about the dimension. */
359     if ((dimid = SDgetdimid(hdf4_var->sdsid, d)) == FAIL)
360         return NC_EDIMMETA;
361     if (SDdiminfo(dimid, dim_name, &dim_len, &dim_data_type, &dim_num_attrs))
362         return NC_EDIMMETA;
363     if (strlen(dim_name) > NC_MAX_HDF4_NAME)
364         return NC_EMAXNAME;
365 
366     /* Do we already have this dimension? HDF4 explicitly uses
367      * the name to tell. */
368     for (i = 0; i < ncindexsize(h5->root_grp->dim); i++)
369     {
370         dim = (NC_DIM_INFO_T*)ncindexith(h5->root_grp->dim, i);
371         if (!strcmp(dim->hdr.name, dim_name))
372             break;
373         dim = NULL;
374     }
375 
376     /* If we didn't find this dimension, add one. */
377     if (!dim)
378     {
379         LOG((4, "adding dim %s for dataset %s", dim_name, var->hdr.name));
380         if ((retval = nc4_dim_list_add(h5->root_grp, dim_name,
381                                        (dim_len ? dim_len : rec_dim_len), -1, &dim)))
382             return retval;
383     }
384 
385     /* Tell the variable the id of this dimension. */
386     var->dimids[d] = dim->hdr.id;
387     var->dim[d] = dim;
388 
389     return NC_NOERR;
390 }
391 
392 /**
393  * @internal Create a new variable and insert int relevant lists
394  *
395  * @param grp the containing group
396  * @param name the name for the new variable
397  * @param ndims the rank of the new variable
398  * @param format_var_info Pointer to format-specific var info struct.
399  * @param var Pointer in which to return a pointer to the new var.
400  *
401  * @return ::NC_NOERR No error.
402  * @return ::NC_ENOMEM Out of memory.
403  * @author Ed Hartnett
404  */
405 static int
nc4_var_list_add_full(NC_GRP_INFO_T * grp,const char * name,int ndims,nc_type xtype,int endianness,size_t type_size,char * type_name,void * fill_value,int contiguous,size_t * chunksizes,void * format_var_info,NC_VAR_INFO_T ** var)406 nc4_var_list_add_full(NC_GRP_INFO_T* grp, const char* name, int ndims, nc_type xtype,
407                       int endianness, size_t type_size, char *type_name, void *fill_value,
408                       int contiguous, size_t *chunksizes, void *format_var_info,
409                       NC_VAR_INFO_T **var)
410 {
411     int d;
412     int retval;
413 
414     /* Add the VAR_INFO_T struct to our list of vars. */
415     if ((retval = nc4_var_list_add(grp, name, ndims, var)))
416         return retval;
417     (*var)->created = NC_TRUE;
418     (*var)->written_to = NC_TRUE;
419     (*var)->format_var_info = format_var_info;
420     (*var)->atts_read = 1;
421 
422     /* Fill special type_info struct for variable type information. */
423     if ((retval = nc4_set_var_type(xtype, endianness, type_size, type_name,
424                                    &(*var)->type_info)))
425         return retval;
426 
427     (*var)->type_info->rc++;
428 
429     /* Handle fill value, if provided. */
430     if (fill_value)
431     {
432         if (!((*var)->fill_value = malloc(type_size)))
433             return NC_ENOMEM;
434         memcpy((*var)->fill_value, fill_value, type_size);
435     }
436 
437     /* Var contiguous or chunked? */
438     if (contiguous)
439 	(*var)->storage = NC_CONTIGUOUS;
440     else
441 	(*var)->storage = NC_CHUNKED;
442 
443     /* Were chunksizes provided? */
444     if (chunksizes)
445     {
446         if (!((*var)->chunksizes = malloc(ndims * sizeof(size_t))))
447             return NC_ENOMEM;
448         for (d = 0; d < ndims; d++)
449             (*var)->chunksizes[d] = chunksizes[d];
450     }
451 
452     return NC_NOERR;
453 }
454 
455 /**
456  * @internal Read a HDF4 variable, including its associated dimensions
457  * and attributes.
458  *
459  * @param h5 Pointer to the file metadata struct.
460  * @param v Index of variable to read.
461  *
462  * @return ::NC_NOERR No error.
463  * @return ::NC_EHDFERR HDF4 error.
464  * @return ::NC_EDIMMETA Error reading HDF4 dimension info.
465  * @return ::NC_EVARMETA Error reading HDF4 dataset or att.
466  * @return ::NC_EATTMETA Error reading HDF4 attribute.
467  * @return ::NC_ENOMEM Out of memory.
468  * @return ::NC_EMAXNAME Name too long.
469  * @author Ed Hartnett
470  */
471 static int
hdf4_read_var(NC_FILE_INFO_T * h5,int v)472 hdf4_read_var(NC_FILE_INFO_T *h5, int v)
473 {
474     NC_HDF4_FILE_INFO_T *hdf4_file;
475     NC_VAR_INFO_T *var;
476     NC_VAR_HDF4_INFO_T *hdf4_var;
477     HDF_CHUNK_DEF chunkdefs;
478     int32 data_type, num_atts;
479     int32 dimsize[NC_MAX_HDF4_DIMS];
480     int32 rec_dim_len;
481     int32 rank;
482     int32 sdsid;
483     int contiguous;
484     int d, a;
485     int flag;
486     char name[NC_MAX_HDF4_NAME+1];
487     int xtype;
488     char type_name[NC_MAX_NAME + 1];
489     int endianness;
490     size_t type_size;
491     void *fill_value;
492     size_t *chunksizes = NULL;
493     int retval;
494 
495     /* Check inputs. */
496     assert(h5 && h5->format_file_info);
497 
498     /* Get HDF4 file metadata. */
499     hdf4_file = h5->format_file_info;
500 
501     /* Open this dataset in HDF4 file. */
502     if ((sdsid = SDselect(hdf4_file->sdid, v)) == FAIL)
503         return  NC_EVARMETA;
504 
505     /* Learn about this dataset. */
506     if (SDgetinfo(sdsid, name, &rank, dimsize, &data_type, &num_atts))
507         return NC_EVARMETA;
508     rec_dim_len = dimsize[0];
509 
510     /* Get chunking info from HDF4 file. */
511     if (SDgetchunkinfo(sdsid, &chunkdefs, &flag))
512         return NC_EVARMETA;
513 
514     /* Learn about the HDF4 type. */
515     if ((retval = hdf4_type_info(h5, data_type, &xtype, &endianness, &type_size,
516                                  type_name)))
517         return retval;
518 
519     /* Get the fill value. */
520     if (!(fill_value = malloc(type_size)))
521         return NC_ENOMEM;
522     if (SDgetfillvalue(sdsid, fill_value))
523     {
524         /* Whoops! No fill value! */
525         free(fill_value);
526         fill_value = NULL;
527     }
528 
529     /* Is variable chunked or contiguous? */
530     if (flag == HDF_NONE)
531         contiguous = NC_TRUE;
532     else if (flag & HDF_CHUNK)
533     {
534         contiguous = NC_FALSE;
535         if (!(chunksizes = malloc(rank * sizeof(size_t))))
536             return NC_ENOMEM;
537         for (d = 0; d < rank; d++)
538             chunksizes[d] = chunkdefs.chunk_lengths[d];
539     }
540 
541     /* Malloc a struct to hold HDF4-specific variable
542      * information. */
543     if (!(hdf4_var = malloc(sizeof(NC_VAR_HDF4_INFO_T))))
544         return NC_ENOMEM;
545 
546     /* Remember these values. */
547     hdf4_var->hdf4_data_type = data_type;
548     hdf4_var->sdsid = sdsid;
549 
550     /* Add a variable to metadata structures. */
551     LOG((3, "adding var for HDF4 dataset %s, rank %d netCDF type %d", name,
552          rank, xtype));
553     retval = nc4_var_list_add_full(h5->root_grp, name, (int)rank,
554                                    xtype, endianness, type_size, type_name,
555                                    fill_value, contiguous, chunksizes, hdf4_var,
556                                    &var);
557 
558     /* Free resources. */
559     if (chunksizes)
560         free(chunksizes);
561     if (fill_value)
562         free(fill_value);
563 
564     /* Did the add fail? */
565     if (retval)
566     {
567         free(hdf4_var);
568         return retval;
569     }
570 
571     /* Find the variable's dimensions. */
572     for (d = 0; d < var->ndims; d++)
573         if ((retval = hdf4_read_dim(h5, var, rec_dim_len, d)))
574             return retval;
575 
576     /* Read the variable's attributes. */
577     for (a = 0; a < num_atts; a++)
578         if ((retval = hdf4_read_att(h5, var, a)))
579             return retval;
580 
581     return NC_NOERR;
582 }
583 
584 /**
585  * @internal Open a HDF4 SD file for read-only access.
586  *
587  * @param path The file name of the file.
588  * @param mode The open mode flag.
589  * @param basepe Ignored by this function.
590  * @param chunksizehintp Ignored by this function.
591  * @param parameters pointer to struct holding extra data (e.g. for
592  * parallel I/O) layer. Ignored if NULL. Ignored by this function.
593  * @param dispatch Pointer to the dispatch table for this file.
594  * @param nc_file Pointer to an instance of NC. The ncid has already
595  * been assigned, and is in nc_file->ext_ncid.
596  *
597  * @return ::NC_NOERR No error.
598  * @return ::NC_EINVAL Invalid input.
599  * @return ::NC_EHDFERR Error from HDF4 layer.
600  * @return ::NC_ENOMEM Out of memory.
601  * @author Ed Hartnett
602  */
603 int
NC_HDF4_open(const char * path,int mode,int basepe,size_t * chunksizehintp,void * parameters,const NC_Dispatch * dispatch,int ncid)604 NC_HDF4_open(const char *path, int mode, int basepe, size_t *chunksizehintp,
605              void *parameters, const NC_Dispatch *dispatch, int ncid)
606 {
607     NC_FILE_INFO_T *h5;
608     NC_HDF4_FILE_INFO_T *hdf4_file;
609     NC *nc;
610     int32 num_datasets, num_gatts;
611     int32 sdid;
612     int v, a;
613     int retval;
614 
615     /* Check inputs. */
616     assert(path);
617 
618     LOG((1, "%s: path %s mode %d params %x", __func__, path, mode, parameters));
619 
620     /* Find pointer to NC. */
621     if ((retval = NC_check_id(ncid, &nc)))
622         return retval;
623 
624     /* Check the mode for validity */
625     if (mode & ILLEGAL_OPEN_FLAGS)
626         return NC_EINVAL;
627 
628     /* Open the file and initialize SD interface. */
629     if ((sdid = SDstart(path, DFACC_READ)) == FAIL)
630         return NC_EHDFERR;
631 
632     /* Learn how many datasets and global atts we have. */
633     if (SDfileinfo(sdid, &num_datasets, &num_gatts))
634         return NC_EHDFERR;
635 
636     /* Add necessary structs to hold netcdf-4 file data. */
637     if ((retval = nc4_file_list_add(ncid, path, mode, (void **)&h5)))
638         return retval;
639     assert(h5 && h5->root_grp);
640     h5->no_write = NC_TRUE;
641     h5->root_grp->atts_read = 1;
642 
643     /* Allocate data to hold HDF4 specific file data. */
644     if (!(hdf4_file = malloc(sizeof(NC_HDF4_FILE_INFO_T))))
645         return NC_ENOMEM;
646     h5->format_file_info = hdf4_file;
647     hdf4_file->sdid = sdid;
648 
649     /* Read the global atts. */
650     for (a = 0; a < num_gatts; a++)
651         if ((retval = hdf4_read_att(h5, NULL, a)))
652             break;
653 
654     /* Read each dataset. */
655     if (!retval)
656         for (v = 0; v < num_datasets; v++)
657             if ((retval = hdf4_read_var(h5, v)))
658                 break;
659 
660     /* If there is an error, free resources. */
661     if (retval)
662         free(hdf4_file);
663 
664 #ifdef LOGGING
665     /* This will print out the names, types, lens, etc of the vars and
666        atts in the file, if the logging level is 2 or greater. */
667     log_metadata_nc(h5);
668 #endif
669 
670     return retval;
671 }
672 
673 /**
674  * @internal Abort (close) the HDF4 file.
675  *
676  * @param ncid File ID.
677  *
678  * @return ::NC_NOERR No error.
679  * @return ::NC_EBADID Bad ncid.
680  * @return ::NC_EHDFERR Error from HDF4 layer.
681  * @author Ed Hartnett
682  */
683 int
NC_HDF4_abort(int ncid)684 NC_HDF4_abort(int ncid)
685 {
686     return NC_HDF4_close(ncid, NULL);
687 }
688 
689 /**
690  * @internal Close the HDF4 file.
691  *
692  * @param ncid File ID.
693  * @param ignore Ignore this pointer.
694  *
695  * @return ::NC_NOERR No error.
696  * @return ::NC_EBADID Bad ncid.
697  * @return ::NC_EHDFERR Error from HDF4 layer.
698  * @author Ed Hartnett
699  */
700 int
NC_HDF4_close(int ncid,void * ignore)701 NC_HDF4_close(int ncid, void *ignore)
702 {
703     NC_GRP_INFO_T *grp;
704     NC *nc;
705     NC_FILE_INFO_T *h5;
706     NC_HDF4_FILE_INFO_T *hdf4_file;
707     int retval;
708 
709     LOG((1, "%s: ncid 0x%x", __func__, ncid));
710 
711     /* Find our metadata for this file. */
712     if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
713         return retval;
714     assert(nc && h5 && grp && !grp->parent);
715 
716     /* Clean up HDF4 specific allocations. */
717     if ((retval = hdf4_rec_grp_del(h5->root_grp)))
718         return retval;
719 
720     /* Close hdf4 file and free HDF4 file info. */
721     hdf4_file = (NC_HDF4_FILE_INFO_T *)h5->format_file_info;
722     if (SDend(hdf4_file->sdid))
723         return NC_EHDFERR;
724     free(hdf4_file);
725 
726     /* Free the NC_FILE_INFO_T struct. */
727     if ((retval = nc4_nc4f_list_del(h5)))
728         return retval;
729 
730     return NC_NOERR;
731 }
732