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