netCDF  4.3.0
 All Data Structures Files Functions Variables Typedefs Macros Groups Pages
nc4file.c
Go to the documentation of this file.
1 
11 #include "config.h"
12 #include <errno.h> /* netcdf functions sometimes return system errors */
13 
14 #include "nc.h"
15 #include "nc4internal.h"
16 #include "nc4dispatch.h"
17 
18 #include <H5DSpublic.h>
19 
20 #ifdef USE_HDF4
21 #include <mfhdf.h>
22 #endif
23 
24 #ifdef USE_PNETCDF
25 #include <pnetcdf.h>
26 #endif
27 
28 /* This is to track opened HDF5 objects to make sure they are
29  * closed. */
30 #ifdef EXTRA_TESTS
31 extern int num_plists;
32 extern int num_spaces;
33 #endif /* EXTRA_TESTS */
34 
35 #define MIN_DEFLATE_LEVEL 0
36 #define MAX_DEFLATE_LEVEL 9
37 
38 /* These are the special attributes added by the HDF5 dimension scale
39  * API. They will be ignored by netCDF-4. */
40 #define REFERENCE_LIST "REFERENCE_LIST"
41 #define CLASS "CLASS"
42 #define DIMENSION_LIST "DIMENSION_LIST"
43 #define NAME "NAME"
44 
45 /* Forward */
46 static int NC4_enddef(int ncid);
47 static int nc4_rec_read_types(NC_GRP_INFO_T *grp);
48 static int nc4_rec_read_vars(NC_GRP_INFO_T *grp);
49 static int close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort);
50 
51 #ifdef IGNORE
52 /* This extern points to the pointer that holds the list of open
53  * netCDF files. */
54 extern NC *nc_file;
55 #endif
56 
57 /* These are the default chunk cache sizes for HDF5 files created or
58  * opened with netCDF-4. */
59 size_t nc4_chunk_cache_size = CHUNK_CACHE_SIZE;
60 size_t nc4_chunk_cache_nelems = CHUNK_CACHE_NELEMS;
61 float nc4_chunk_cache_preemption = CHUNK_CACHE_PREEMPTION;
62 
63 /* To turn off HDF5 error messages, I have to catch an early
64  invocation of a netcdf function. */
65 static int virgin = 1;
66 
67 /* For performance, fill this array only the first time, and keep it
68  * in global memory for each further use. */
69 #define NUM_TYPES 12
70 static hid_t native_type_constant[NUM_TYPES];
71 
72 static char nc_type_name[NUM_TYPES][NC_MAX_NAME + 1] = {"char", "byte", "short", "int", "float",
73  "double", "ubyte", "ushort", "uint",
74  "int64", "uint64", "string"};
75 int nc4_free_global_hdf_string_typeid();
76 
77 /* Set chunk cache size. Only affects files opened/created *after* it
78  * is called. */
79 int
80 nc_set_chunk_cache(size_t size, size_t nelems, float preemption)
81 {
82  if (preemption < 0 || preemption > 1)
83  return NC_EINVAL;
84  nc4_chunk_cache_size = size;
85  nc4_chunk_cache_nelems = nelems;
86  nc4_chunk_cache_preemption = preemption;
87  return NC_NOERR;
88 }
89 
90 /* Get chunk cache size. Only affects files opened/created *after* it
91  * is called. */
92 int
93 nc_get_chunk_cache(size_t *sizep, size_t *nelemsp, float *preemptionp)
94 {
95  if (sizep)
96  *sizep = nc4_chunk_cache_size;
97 
98  if (nelemsp)
99  *nelemsp = nc4_chunk_cache_nelems;
100 
101  if (preemptionp)
102  *preemptionp = nc4_chunk_cache_preemption;
103  return NC_NOERR;
104 }
105 
106 /* Required for fortran to avoid size_t issues. */
107 int
108 nc_set_chunk_cache_ints(int size, int nelems, int preemption)
109 {
110  if (size <= 0 || nelems <= 0 || preemption < 0 || preemption > 100)
111  return NC_EINVAL;
112  nc4_chunk_cache_size = size;
113  nc4_chunk_cache_nelems = nelems;
114  nc4_chunk_cache_preemption = (float)preemption / 100;
115  return NC_NOERR;
116 }
117 
118 int
119 nc_get_chunk_cache_ints(int *sizep, int *nelemsp, int *preemptionp)
120 {
121  if (sizep)
122  *sizep = (int)nc4_chunk_cache_size;
123  if (nelemsp)
124  *nelemsp = (int)nc4_chunk_cache_nelems;
125  if (preemptionp)
126  *preemptionp = (int)(nc4_chunk_cache_preemption * 100);
127 
128  return NC_NOERR;
129 }
130 
131 /* This will return the length of a netcdf data type in bytes. */
132 int
133 nc4typelen(nc_type type)
134 {
135  switch(type){
136  case NC_BYTE:
137  case NC_CHAR:
138  case NC_UBYTE:
139  return 1;
140  case NC_USHORT:
141  case NC_SHORT:
142  return 2;
143  case NC_FLOAT:
144  case NC_INT:
145  case NC_UINT:
146  return 4;
147  case NC_DOUBLE:
148  case NC_INT64:
149  case NC_UINT64:
150  return 8;
151  }
152  return -1;
153 }
154 
155 /* Given a filename, check to see if it is a HDF5 file. */
156 #define MAGIC_NUMBER_LEN 4
157 #define NC_HDF5_FILE 1
158 #define NC_HDF4_FILE 2
159 static int
160 nc_check_for_hdf(const char *path, int use_parallel, MPI_Comm comm, MPI_Info info,
161  int *hdf_file)
162 {
163  char blob[MAGIC_NUMBER_LEN];
164 
165  assert(hdf_file && path);
166  LOG((3, "nc_check_for_hdf: path %s", path));
167 
168  /* HDF5 function handles possible user block at beginning of file */
169  if(H5Fis_hdf5(path))
170  {
171  *hdf_file = NC_HDF5_FILE;
172  } else {
173 
174 /* Get the 4-byte blob from the beginning of the file. Don't use posix
175  * for parallel, use the MPI functions instead. */
176 #ifdef USE_PARALLEL
177  if (use_parallel)
178  {
179  MPI_File fh;
180  MPI_Status status;
181  int retval;
182  if ((retval = MPI_File_open(comm, (char *)path, MPI_MODE_RDONLY,
183  info, &fh)) != MPI_SUCCESS)
184  return NC_EPARINIT;
185  if ((retval = MPI_File_read(fh, blob, MAGIC_NUMBER_LEN, MPI_CHAR,
186  &status)) != MPI_SUCCESS)
187  return NC_EPARINIT;
188  if ((retval = MPI_File_close(&fh)) != MPI_SUCCESS)
189  return NC_EPARINIT;
190  }
191  else
192 #endif /* USE_PARALLEL */
193  {
194  FILE *fp;
195  if (!(fp = fopen(path, "r")) ||
196  fread(blob, MAGIC_NUMBER_LEN, 1, fp) != 1) {
197 
198  if(fp) fclose(fp);
199  return errno;
200  }
201  fclose(fp);
202  }
203 
204  /* Check for HDF4. */
205  if (!strncmp(blob, "\016\003\023\001", MAGIC_NUMBER_LEN))
206  *hdf_file = NC_HDF4_FILE;
207  else
208  *hdf_file = 0;
209  }
210  return NC_NOERR;
211 }
212 
213 /* Create a HDF5/netcdf-4 file. */
214 
215 static int
216 nc4_create_file(const char *path, int cmode, MPI_Comm comm, MPI_Info info,
217  NC *nc)
218 {
219  hid_t fcpl_id, fapl_id;
220  unsigned flags;
221  FILE *fp;
222  int retval = NC_NOERR;
223  NC_HDF5_FILE_INFO_T* nc4_info = NULL;
224 #ifndef USE_PARALLEL
225  int persist = 0; /* Should diskless try to persist its data into file?*/
226 #endif
227 
228  assert(nc);
229 
230  if(cmode & NC_DISKLESS)
231  flags = H5F_ACC_TRUNC;
232  else if(cmode & NC_NOCLOBBER)
233  flags = H5F_ACC_EXCL;
234  else
235  flags = H5F_ACC_TRUNC;
236 
237  LOG((3, "nc4_create_file: path %s mode 0x%x", path, cmode));
238  assert(nc && path);
239 
240  /* If this file already exists, and NC_NOCLOBBER is specified,
241  return an error. */
242  if (cmode & NC_DISKLESS) {
243 #ifndef USE_PARALLEL
244  if(cmode & NC_WRITE)
245  persist = 1;
246 #endif
247  } else if ((cmode & NC_NOCLOBBER) && (fp = fopen(path, "r"))) {
248  fclose(fp);
249  return NC_EEXIST;
250  }
251 
252  /* Add necessary structs to hold netcdf-4 file data. */
253  if ((retval = nc4_nc4f_list_add(nc, path, (NC_WRITE | cmode))))
254  BAIL(retval);
255  nc4_info = NC4_DATA(nc);
256  assert(nc4_info && nc4_info->root_grp);
257 
258 #ifdef USE_PNETCDF
259  if (cmode & NC_PNETCDF)
260  return NC_NOERR;
261 #endif
262 
263  /* Need this access plist to control how HDF5 handles open onjects
264  * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to
265  * fail if there are any open objects in the file. */
266  if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
267  BAIL(NC_EHDFERR);
268 #ifdef EXTRA_TESTS
269  num_plists++;
270 #endif
271 #ifdef EXTRA_TESTS
272  if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI))
273  BAIL(NC_EHDFERR);
274 #else
275  if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_STRONG))
276  BAIL(NC_EHDFERR);
277 #endif /* EXTRA_TESTS */
278 
279 #ifdef USE_PARALLEL
280  /* If this is a parallel file create, set up the file creation
281  property list. */
282  if ((cmode & NC_MPIIO) || (cmode & NC_MPIPOSIX))
283  {
284  nc4_info->parallel++;
285  if (cmode & NC_MPIIO) /* MPI/IO */
286  {
287  LOG((4, "creating parallel file with MPI/IO"));
288  if (H5Pset_fapl_mpio(fapl_id, comm, info) < 0)
289  BAIL(NC_EPARINIT);
290  }
291  else /* MPI/POSIX */
292  {
293  LOG((4, "creating parallel file with MPI/posix"));
294  if (H5Pset_fapl_mpiposix(fapl_id, comm, 0) < 0)
295  BAIL(NC_EPARINIT);
296  }
297  }
298 #else /* only set cache for non-parallel... */
299  if(cmode & NC_DISKLESS) {
300  if (H5Pset_fapl_core(fapl_id, 4096, persist))
301  BAIL(NC_EDISKLESS);
302  }
303  if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size,
304  nc4_chunk_cache_preemption) < 0)
305  BAIL(NC_EHDFERR);
306  LOG((4, "nc4_create_file: set HDF raw chunk cache to size %d nelems %d preemption %f",
307  nc4_chunk_cache_size, nc4_chunk_cache_nelems, nc4_chunk_cache_preemption));
308 #endif /* USE_PARALLEL */
309 
310  if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
311  BAIL(NC_EHDFERR);
312 
313  /* Create the property list. */
314  if ((fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0)
315  BAIL(NC_EHDFERR);
316 #ifdef EXTRA_TESTS
317  num_plists++;
318 #endif
319 
320  /* Set latest_format in access propertly list and
321  * H5P_CRT_ORDER_TRACKED in the creation property list. This turns
322  * on HDF5 creation ordering. */
323  if (H5Pset_link_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED |
324  H5P_CRT_ORDER_INDEXED)) < 0)
325  BAIL(NC_EHDFERR);
326  if (H5Pset_attr_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED |
327  H5P_CRT_ORDER_INDEXED)) < 0)
328  BAIL(NC_EHDFERR);
329 
330  /* Create the file. */
331  if ((nc4_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0)
332  /*Change the return error from NC_EFILEMETADATA to
333  System error EACCES because that is the more likely problem */
334  BAIL(EACCES);
335 
336  /* Open the root group. */
337  if ((nc4_info->root_grp->hdf_grpid = H5Gopen2(nc4_info->hdfid, "/",
338  H5P_DEFAULT)) < 0)
339  BAIL(NC_EFILEMETA);
340 
341  /* Release the property lists. */
342  if (H5Pclose(fapl_id) < 0 ||
343  H5Pclose(fcpl_id) < 0)
344  BAIL(NC_EHDFERR);
345 #ifdef EXTRA_TESTS
346  num_plists--;
347  num_plists--;
348 #endif
349 
350  /* Define mode gets turned on automatically on create. */
351  nc4_info->flags |= NC_INDEF;
352 
353  return NC_NOERR;
354 
355 exit: /*failure exit*/
356 #ifdef EXTRA_TESTS
357  num_plists--;
358 #endif
359  if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id);
360  if(!nc4_info) return retval;
361  close_netcdf4_file(nc4_info,1); // treat like abort
362 #if 0
363  if (nc4_info->hdfid > 0) H5Fclose(nc4_info->hdfid);
364  if (nc4_info->root_grp) {
365  free(nc4_info->root_grp->name);
366  free(nc4_info->root_grp);
367  }
368  free(nc4_info);
369 #endif
370  return retval;
371 }
372 
388 int
389 NC4_create(const char* path, int cmode, size_t initialsz, int basepe,
390  size_t *chunksizehintp, int use_parallel, void *mpidata,
391  NC_Dispatch *dispatch, NC* nc_file)
392 {
393 #ifdef USE_PARALLEL
394  MPI_Comm comm = 0;
395  MPI_Info info = 0;
396 #else
397  int comm = 0, info = 0;
398 #endif /* USE_PARALLEL */
399  int res;
400 
401  assert(nc_file && path);
402 
403  LOG((1, "nc4_create_file: path %s cmode 0x%x comm %d info %d",
404  path, cmode, comm, info));
405 
406 #ifdef USE_PARALLEL
407  if (mpidata)
408  {
409  comm = ((NC_MPI_INFO *)mpidata)->comm;
410  info = ((NC_MPI_INFO *)mpidata)->info;
411  }
412 #endif /* USE_PARALLEL */
413 
414  /* If this is our first file, turn off HDF5 error messages. */
415  if (virgin)
416  {
417  if (H5Eset_auto(NULL, NULL) < 0)
418  LOG((0, "Couldn't turn off HDF5 error messages!"));
419  LOG((1, "HDF5 error messages have been turned off."));
420  virgin = 0;
421  }
422 
423  /* Check the cmode for validity. */
424  if (cmode & ~(NC_NOCLOBBER | NC_64BIT_OFFSET
426  | NC_SHARE | NC_MPIIO | NC_MPIPOSIX | NC_LOCK | NC_PNETCDF
427  | NC_DISKLESS
428  | NC_WRITE /* to support diskless persistence */
429  )
430  || (cmode & NC_MPIIO && cmode & NC_MPIPOSIX)
431  || (cmode & NC_64BIT_OFFSET && cmode & NC_NETCDF4)
432  || (cmode & (NC_MPIIO | NC_MPIPOSIX) && cmode & NC_DISKLESS)
433  )
434  return NC_EINVAL;
435 
436  cmode |= NC_NETCDF4;
437 
438  /* Apply default create format. */
439  if (nc_get_default_format() == NC_FORMAT_64BIT)
440  cmode |= NC_64BIT_OFFSET;
441  else if (nc_get_default_format() == NC_FORMAT_NETCDF4_CLASSIC)
442  cmode |= NC_CLASSIC_MODEL;
443 
444  LOG((2, "cmode after applying default format: 0x%x", cmode));
445 
446  nc_file->int_ncid = nc_file->ext_ncid;
447  res = nc4_create_file(path, cmode, comm, info, nc_file);
448 
449 #ifdef USE_PNETCDF
450  if (cmode & NC_PNETCDF)
451  {
452  NC_HDF5_FILE_INFO_T* nc4_info;
453  nc4_info = NC4_DATA(nc_file);
454  assert(nc4_info);
455 
456  nc4_info->pnetcdf_file++;
457  res = ncmpi_create(comm, path, cmode, info, &(nc_file->int_ncid));
458  }
459 #endif /* USE_PNETCDF */
460 
461  return res;
462 }
463 
464 /* This function is called by read_dataset when a dimension scale
465  * dataset is encountered. It reads in the dimension data (creating a
466  * new NC_DIM_INFO_T object), and also checks to see if this is a
467  * dimension without a variable - that is, a coordinate dimension
468  * which does not have any coordinate data. */
469 static int
470 read_scale(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
471  hsize_t scale_size, hsize_t max_scale_size,
472  int *dim_without_var)
473 {
474  /*char *start_of_len;*/
475  char dimscale_name_att[NC_MAX_NAME + 1] = "";
476  hid_t attid = 0;
477  int max_len;
478  int retval;
479 
480  /* Add a dimension for this scale. */
481  if ((retval = nc4_dim_list_add(&grp->dim)))
482  return retval;
483 
484  /* Assign dimid and increment number of dimensions. */
485  grp->dim->dimid = grp->nc4_info->next_dimid++;
486  grp->ndims++;
487 
488  /* Does this dataset have a hidden attribute that tells us its
489  * dimid? If so, read it. */
490  H5E_BEGIN_TRY {
491  if ((attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME,
492  H5P_DEFAULT, H5P_DEFAULT)) > 0)
493  {
494  if (H5Aread(attid, H5T_NATIVE_INT, &grp->dim->dimid) < 0)
495  return NC_EHDFERR;
496  if (H5Aclose(attid) < 0)
497  return NC_EHDFERR;
498  }
499  } H5E_END_TRY;
500 
501  max_len = strlen(obj_name) > NC_MAX_NAME ? NC_MAX_NAME : strlen(obj_name);
502  if (!(grp->dim->name = malloc((max_len + 1) * sizeof(char))))
503  return NC_ENOMEM;
504  strncpy(grp->dim->name, obj_name, max_len + 1);
505  if (SIZEOF_SIZE_T < 8 && scale_size > NC_MAX_UINT)
506  {
507  grp->dim->len = NC_MAX_UINT;
508  grp->dim->too_long = 1;
509  }
510  else
511  grp->dim->len = scale_size;
512  grp->dim->hdf_dimscaleid = datasetid;
513 
514  /* If the dimscale has an unlimited dimension, then this dimension
515  * is unlimited. */
516  if (max_scale_size == H5S_UNLIMITED)
517  grp->dim->unlimited++;
518 
519  /* If the scale name is set to DIM_WITHOUT_VARIABLE, then this is a
520  * dimension, but not a variable. (If get_scale_name returns an
521  * error, just move on, there's no NAME.) */
522  if (H5DSget_scale_name(datasetid, dimscale_name_att, NC_MAX_NAME) >= 0)
523  {
524  if (!strncmp(dimscale_name_att, DIM_WITHOUT_VARIABLE,
525  strlen(DIM_WITHOUT_VARIABLE)))
526  {
527  if (grp->dim->unlimited)
528  {
529  size_t len = 0, *lenp = &len;
530  if ((retval = nc4_find_dim_len(grp, grp->dim->dimid, &lenp)))
531  return retval;
532  grp->dim->len = *lenp;
533  }
534  (*dim_without_var)++;
535  }
536  }
537 
538  return NC_NOERR;
539 }
540 
541 /* This function reads the hacked in coordinates attribute I use for
542  * multi-dimensional coordinates. */
543 static int
544 read_coord_dimids(NC_VAR_INFO_T *var)
545 {
546  hid_t coord_att_typeid = -1, coord_attid = -1, spaceid = -1;
547  int ret = 0;
548 
549  /* There is a hidden attribute telling us the ids of the
550  * dimensions that apply to this multi-dimensional coordinate
551  * variable. Read it. */
552  if ((coord_attid = H5Aopen_name(var->hdf_datasetid, COORDINATES)) < 0) ret++;
553  if (!ret && (coord_att_typeid = H5Aget_type(coord_attid)) < 0) ret++;
554  if (!ret && H5Aread(coord_attid, coord_att_typeid, var->dimids) < 0) ret++;
555  LOG((4, "dimscale %s is multidimensional and has coords", var->name));
556 
557  /* How many dimensions are there? */
558  if ((spaceid = H5Aget_space(coord_attid)) < 0) ret++;
559 #ifdef EXTRA_TESTS
560  num_spaces++;
561 #endif
562  if (H5Sget_simple_extent_npoints(spaceid) < 0) ret++;
563 
564  /* Set my HDF5 IDs free! */
565  if (spaceid >= 0 && H5Sclose(spaceid) < 0) ret++;
566 #ifdef EXTRA_TESTS
567  num_spaces--;
568 #endif
569  if (coord_att_typeid >= 0 && H5Tclose(coord_att_typeid) < 0) ret++;
570  if (coord_attid >= 0 && H5Aclose(coord_attid) < 0) ret++;
571  return ret ? NC_EATTMETA : NC_NOERR;
572 }
573 
574 /* This function is called when reading a file's metadata for each
575  * dimension scale attached to a variable.*/
576 static herr_t
577 dimscale_visitor(hid_t did, unsigned dim, hid_t dsid,
578  void *dimscale_hdf5_objids)
579 {
580  H5G_stat_t statbuf;
581 
582  /* Get more info on the dimscale object.*/
583  if (H5Gget_objinfo(dsid, ".", 1, &statbuf) < 0)
584  return -1;
585 
586  /* Pass this information back to caller. */
587 /* (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno = statbuf.fileno;
588  (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno = statbuf.objno;*/
589  (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[0] = statbuf.fileno[0];
590  (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[1] = statbuf.fileno[1];
591  (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[0] = statbuf.objno[0];
592  (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[1] = statbuf.objno[1];
593  return 0;
594 }
595 
596 /* Given an HDF5 type, set a pointer to netcdf type. */
597 static int
598 get_netcdf_type(NC_HDF5_FILE_INFO_T *h5, hid_t native_typeid,
599  nc_type *xtype)
600 {
601  NC_TYPE_INFO_T *type;
602  hid_t class;
603  htri_t is_str, equal = 0;
604 
605  assert(h5 && xtype);
606 
607  if ((class = H5Tget_class(native_typeid)) < 0)
608  return NC_EHDFERR;
609 
610  /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
611  * H5Tget_class will return H5T_STRING if this is a string. */
612  if (class == H5T_STRING)
613  {
614  if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
615  return NC_EHDFERR;
616  if (is_str)
617  *xtype = NC_STRING;
618  else
619  *xtype = NC_CHAR;
620  return NC_NOERR;
621  }
622  else if (class == H5T_INTEGER || class == H5T_FLOAT)
623  {
624  /* For integers and floats, we don't have to worry about
625  * endianness if we compare native types. */
626  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SCHAR)) < 0)
627  return NC_EHDFERR;
628  if (equal)
629  {
630  *xtype = NC_BYTE;
631  return NC_NOERR;
632  }
633  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SHORT)) < 0)
634  return NC_EHDFERR;
635  if (equal)
636  {
637  *xtype = NC_SHORT;
638  return NC_NOERR;
639  }
640  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_INT)) < 0)
641  return NC_EHDFERR;
642  if (equal)
643  {
644  *xtype = NC_INT;
645  return NC_NOERR;
646  }
647  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_FLOAT)) < 0)
648  return NC_EHDFERR;
649  if (equal)
650  {
651  *xtype = NC_FLOAT;
652  return NC_NOERR;
653  }
654  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_DOUBLE)) < 0)
655  return NC_EHDFERR;
656  if (equal)
657  {
658  *xtype = NC_DOUBLE;
659  return NC_NOERR;
660  }
661  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UCHAR)) < 0)
662  return NC_EHDFERR;
663  if (equal)
664  {
665  *xtype = NC_UBYTE;
666  return NC_NOERR;
667  }
668  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_USHORT)) < 0)
669  return NC_EHDFERR;
670  if (equal)
671  {
672  *xtype = NC_USHORT;
673  return NC_NOERR;
674  }
675  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UINT)) < 0)
676  return NC_EHDFERR;
677  if (equal)
678  {
679  *xtype = NC_UINT;
680  return NC_NOERR;
681  }
682  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_LLONG)) < 0)
683  return NC_EHDFERR;
684  if (equal)
685  {
686  *xtype = NC_INT64;
687  return NC_NOERR;
688  }
689  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_ULLONG)) < 0)
690  return NC_EHDFERR;
691  if (equal)
692  {
693  *xtype = NC_UINT64;
694  return NC_NOERR;
695  }
696  }
697 
698  /* Maybe we already know about this type. */
699  if (!equal)
700  if((type = nc4_rec_find_hdf_type(h5->root_grp, native_typeid)))
701  {
702  *xtype = type->nc_typeid;
703  return NC_NOERR;
704  }
705 
706  *xtype = NC_NAT;
707  return NC_EBADTYPID;
708 }
709 
710 /* Given an HDF5 type, set a pointer to netcdf type_info struct,
711  * either an existing one (for user-defined types) or a newly created
712  * one. */
713 static int
714 get_type_info2(NC_HDF5_FILE_INFO_T *h5, hid_t datasetid,
715  nc_type *xtype, NC_TYPE_INFO_T **type_info)
716 {
717  NC_TYPE_INFO_T *type;
718  htri_t is_str, equal = 0;
719  hid_t class, native_typeid, hdf_typeid;
720 #if 0
721  nc_type my_nc_type = 0;
722 #endif
723  H5T_order_t order;
724  int endianness;
725  nc_type nc_type_constant[NUM_TYPES] = {NC_CHAR, NC_BYTE, NC_SHORT, NC_INT, NC_FLOAT,
728  int type_size[NUM_TYPES] = {sizeof(char), sizeof(char), sizeof(short),
729  sizeof(int), sizeof(float), sizeof(double),
730  sizeof(unsigned char), sizeof(unsigned short),
731  sizeof(unsigned int), sizeof(long long),
732  sizeof(unsigned long long), 0};
733  int t;
734 
735  assert(h5 && xtype && type_info);
736 
737  /* Because these N5T_NATIVE_* constants are actually function calls
738  * (!) in H5Tpublic.h, I can't initialize this array in the usual
739  * way, because at least some C compilers (like Irix) complain
740  * about calling functions when defining constants. So I have to do
741  * it like this. Note that there's no native types for char or
742  * string. Those are handled later. */
743  if (!native_type_constant[1])
744  {
745  native_type_constant[1] = H5T_NATIVE_SCHAR;
746  native_type_constant[2] = H5T_NATIVE_SHORT;
747  native_type_constant[3] = H5T_NATIVE_INT;
748  native_type_constant[4] = H5T_NATIVE_FLOAT;
749  native_type_constant[5] = H5T_NATIVE_DOUBLE;
750  native_type_constant[6] = H5T_NATIVE_UCHAR;
751  native_type_constant[7] = H5T_NATIVE_USHORT;
752  native_type_constant[8] = H5T_NATIVE_UINT;
753  native_type_constant[9] = H5T_NATIVE_LLONG;
754  native_type_constant[10] = H5T_NATIVE_ULLONG;
755  }
756 
757  /* Get the HDF5 typeid - we'll need it later. */
758  if ((hdf_typeid = H5Dget_type(datasetid)) < 0)
759  return NC_EHDFERR;
760 
761  /* Get the native typeid. Will be equivalent to hdf_typeid when
762  * creating but not necessarily when reading, a variable. */
763  if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0)
764  return NC_EHDFERR;
765 
766  /* Is this type an integer, string, compound, or what? */
767  if ((class = H5Tget_class(native_typeid)) < 0)
768  return NC_EHDFERR;
769 
770  /* Is this an atomic type? */
771  if (class == H5T_STRING || class == H5T_INTEGER || class == H5T_FLOAT)
772  {
773  /* Allocate a phony NC_TYPE_INFO_T struct to hold type info. */
774  if (!(*type_info = calloc(1, sizeof(NC_TYPE_INFO_T))))
775  return NC_ENOMEM;
776  (*type_info)->class = class;
777 
778  /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
779  * H5Tget_class will return H5T_STRING if this is a string. */
780  if (class == H5T_STRING)
781  {
782  if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
783  return NC_EHDFERR;
784  /* Make sure fixed-len strings will work like variable-len strings */
785  if (is_str || H5Tget_size(hdf_typeid) > 1)
786  t = NUM_TYPES - 1;
787  else
788  t = 0;
789  }
790  else if (class == H5T_INTEGER || class == H5T_FLOAT)
791  {
792  for (t = 1; t < NUM_TYPES - 1; t++)
793  {
794  if ((equal = H5Tequal(native_typeid, native_type_constant[t])) < 0)
795  return NC_EHDFERR;
796  if (equal)
797  {
798 #if 0
799  my_nc_type = nc_type_constant[t];
800 #endif
801  break;
802  }
803  }
804 
805  /* Find out about endianness. */
806  if (class == H5T_INTEGER)
807  {
808  if ((order = H5Tget_order(hdf_typeid)) < 0)
809  return NC_EHDFERR;
810  if (order == H5T_ORDER_LE)
811  endianness = NC_ENDIAN_LITTLE;
812  else if (order == H5T_ORDER_BE)
813  endianness = NC_ENDIAN_BIG;
814  else /* don't support H5T_ORDER_VAX, H5T_ORDER_MIXED, H5T_ORDER_NONE */
815  return NC_EBADTYPE;
816  /* Copy this into the type_info struct. */
817  (*type_info)->endianness = endianness;
818  }
819  }
820  *xtype = nc_type_constant[t];
821  (*type_info)->nc_typeid = nc_type_constant[t];
822  (*type_info)->size = type_size[t];
823  if (!((*type_info)->name = malloc((strlen(nc_type_name[t]) + 1) * sizeof(char))))
824  return NC_ENOMEM;
825  strcpy((*type_info)->name, nc_type_name[t]);
826  (*type_info)->class = class;
827  (*type_info)->hdf_typeid = hdf_typeid;
828  (*type_info)->native_typeid = native_typeid;
829  (*type_info)->close_hdf_typeid = 1;
830  return NC_NOERR;
831  }
832  else
833  {
834  /* This is a user-defined type. */
835  if((type = nc4_rec_find_hdf_type(h5->root_grp, native_typeid)))
836  {
837  *xtype = type->nc_typeid;
838  *type_info = type;
839  }
840 
841  /* The type entry in the array of user-defined types already has
842  * an open data typeid (and native typeid), so close the ones we
843  * opened above. */
844  if (H5Tclose(native_typeid) < 0)
845  return NC_EHDFERR;
846  if (H5Tclose(hdf_typeid) < 0)
847  return NC_EHDFERR;
848 
849  if (type)
850  return NC_NOERR;
851  }
852 
853  *xtype = NC_NAT;
854  return NC_EBADTYPID;
855 }
856 
857 /* Read an attribute. */
858 static int
859 read_hdf5_att(NC_GRP_INFO_T *grp, hid_t attid, NC_ATT_INFO_T *att)
860 {
861  hid_t spaceid = 0, file_typeid = 0;
862  hsize_t dims[1] = {0}; /* netcdf attributes always 1-D. */
863  int retval = NC_NOERR;
864  size_t type_size;
865  int att_ndims;
866  hssize_t att_npoints;
867  H5T_class_t att_class;
868  int fixed_len_string = 0;
869  size_t fixed_size = 0;
870 
871  assert(att->name);
872  LOG((5, "read_hdf5_att: att->attnum %d att->name %s "
873  "att->xtype %d att->len %d", att->attnum, att->name,
874  att->xtype, att->len));
875 
876  /* Get type of attribute in file. */
877  if ((file_typeid = H5Aget_type(attid)) < 0)
878  return NC_EATTMETA;
879  if ((att->native_typeid = H5Tget_native_type(file_typeid, H5T_DIR_DEFAULT)) < 0)
880  BAIL(NC_EHDFERR);
881  if ((att_class = H5Tget_class(att->native_typeid)) < 0)
882  BAIL(NC_EATTMETA);
883  if (att_class == H5T_STRING && !H5Tis_variable_str(att->native_typeid))
884  {
885  fixed_len_string++;
886  if (!(fixed_size = H5Tget_size(att->native_typeid)))
887  BAIL(NC_EATTMETA);
888  }
889  if ((retval = get_netcdf_type(grp->nc4_info, att->native_typeid, &(att->xtype))))
890  BAIL(retval);
891 
892 
893  /* Get len. */
894  if ((spaceid = H5Aget_space(attid)) < 0)
895  BAIL(NC_EATTMETA);
896 #ifdef EXTRA_TESTS
897  num_spaces++;
898 #endif
899  if ((att_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
900  BAIL(NC_EATTMETA);
901  if ((att_npoints = H5Sget_simple_extent_npoints(spaceid)) < 0)
902  BAIL(NC_EATTMETA);
903 
904  /* If both att_ndims and att_npoints are zero, then this is a
905  * zero length att. */
906  if (att_ndims == 0 && att_npoints == 0)
907  {
908  dims[0] = 0;
909  }
910  else if (att->xtype == NC_STRING) {
911  dims[0] = att_npoints;
912  }
913  else if (att->xtype == NC_CHAR)
914  {
915  /* NC_CHAR attributes are written as a scalar in HDF5, of type
916  * H5T_C_S1, of variable length. */
917  if (att_ndims == 0)
918  {
919  if (!(dims[0] = H5Tget_size(file_typeid)))
920  BAIL(NC_EATTMETA);
921  }
922  else
923  {
924  /* This is really a string type! */
925  att->xtype = NC_STRING;
926  dims[0] = att_npoints;
927  }
928  }
929  else
930  {
931  /* All netcdf attributes are scalar or 1-D only. */
932  if (att_ndims > 1)
933  BAIL(NC_EATTMETA);
934 
935  /* Read the size of this attribute. */
936  if (H5Sget_simple_extent_dims(spaceid, dims, NULL) < 0)
937  BAIL(NC_EATTMETA);
938  }
939 
940  /* Tell the user what the length if this attribute is. */
941  att->len = dims[0];
942 
943  /* Allocate some memory if the len is not zero, and read the
944  attribute. */
945  if (dims[0])
946  {
947  if ((retval = nc4_get_typelen_mem(grp->nc4_info, att->xtype, 0,
948  &type_size)))
949  return retval;
950  if (att_class == H5T_VLEN)
951  {
952  if (!(att->vldata = malloc((unsigned int)(att->len * sizeof(hvl_t)))))
953  BAIL(NC_ENOMEM);
954  if (H5Aread(attid, att->native_typeid, att->vldata) < 0)
955  BAIL(NC_EATTMETA);
956  }
957  else if (att->xtype == NC_STRING)
958  {
959  if (!(att->stdata = calloc(att->len, sizeof(char *))))
960  BAIL(NC_ENOMEM);
961  /* For a fixed length HDF5 string, the read requires
962  * contiguous memory. Meanwhile, the netCDF API requires that
963  * nc_free_string be called on string arrays, which would not
964  * work if one contiguous memory block were used. So here I
965  * convert the contiguous block of strings into an array of
966  * malloced strings (each string with its own malloc). Then I
967  * copy the data and free the contiguous memory. This
968  * involves copying the data, which is bad, but this only
969  * occurs for fixed length string attributes, and presumably
970  * these are small. (And netCDF-4 does not create them - it
971  * always uses variable length strings. */
972  if (fixed_len_string)
973  {
974  int i;
975  char *contig_buf, *cur;
976 
977  /* Alloc space for the contiguous memory read. */
978  if (!(contig_buf = malloc(att->len * fixed_size * sizeof(char))))
979  BAIL(NC_ENOMEM);
980 
981  /* Read the fixed-len strings as one big block. */
982  if (H5Aread(attid, att->native_typeid, contig_buf) < 0)
983  BAIL(NC_EATTMETA);
984 
985  /* Copy strings, one at a time, into their new home. Alloc
986  space for each string. The user will later free this
987  space with nc_free_string. */
988  cur = contig_buf;
989  for (i = 0; i < att->len; i++)
990  {
991  if (!(att->stdata[i] = malloc(fixed_size)))
992  BAIL(NC_ENOMEM);
993  strncpy(att->stdata[i], cur, fixed_size);
994  cur += fixed_size;
995  }
996 
997  /* Free contiguous memory buffer. */
998  free(contig_buf);
999  }
1000  else
1001  {
1002  /* Read variable-length string atts. */
1003  if (H5Aread(attid, att->native_typeid, att->stdata) < 0)
1004  BAIL(NC_EATTMETA);
1005  }
1006  }
1007  else
1008  {
1009  if (!(att->data = malloc((unsigned int)(att->len * type_size))))
1010  BAIL(NC_ENOMEM);
1011  if (H5Aread(attid, att->native_typeid, att->data) < 0)
1012  BAIL(NC_EATTMETA);
1013  }
1014  }
1015 
1016  if (H5Tclose(file_typeid) < 0)
1017  BAIL(NC_EHDFERR);
1018  if (H5Sclose(spaceid) < 0)
1019  return NC_EHDFERR;
1020 #ifdef EXTRA_TESTS
1021  num_spaces--;
1022 #endif
1023 
1024  return NC_NOERR;
1025 
1026  exit:
1027  if (H5Tclose(file_typeid) < 0)
1028  BAIL2(NC_EHDFERR);
1029  if (spaceid > 0 && H5Sclose(spaceid) < 0)
1030  BAIL2(NC_EHDFERR);
1031 #ifdef EXTRA_TESTS
1032  num_spaces--;
1033 #endif
1034  return retval;
1035 }
1036 
1037 /* Read information about a user defined type from the HDF5 file, and
1038  * stash it in the group's list of types. Return the netcdf typeid
1039  * through a pointer, if caller desires it. */
1040 static int
1041 read_type(NC_GRP_INFO_T *grp, char *type_name)
1042 {
1043  NC_TYPE_INFO_T *type;
1044  H5T_class_t class;
1045  hid_t hdf_typeid, native_typeid = 0;
1046  int nmembers;
1047  hid_t member_hdf_typeid, base_hdf_typeid = 0;
1048  char *member_name = NULL;
1049  size_t type_size = 0, member_offset;
1050  unsigned int m;
1051  nc_type ud_type_type = NC_NAT, base_nc_type = NC_NAT, member_xtype;
1052  htri_t ret;
1053  int retval = NC_NOERR;
1054  void *value = NULL;
1055  int i;
1056 
1057  assert(grp && type_name);
1058 
1059  if (strlen(type_name) > NC_MAX_NAME)
1060  return NC_EBADNAME;
1061 
1062  LOG((4, "read_type: type_name %s grp->name %s", type_name, grp->name));
1063 
1064  if ((hdf_typeid = H5Topen2(grp->hdf_grpid, type_name, H5P_DEFAULT)) < 0)
1065  return NC_EHDFERR;
1066 
1067  /* What is the native type for this platform? */
1068  if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0)
1069  return NC_EHDFERR;
1070 
1071  /* What is the size of this type on this platform. */
1072  if (!(type_size = H5Tget_size(native_typeid)))
1073  return NC_EHDFERR;
1074  LOG((5, "type_size %d", type_size));
1075 
1076  /* What is the class of this type, compound, vlen, etc. */
1077  if ((class = H5Tget_class(hdf_typeid)) < 0)
1078  return NC_EHDFERR;
1079  switch (class)
1080  {
1081  case H5T_STRING:
1082  ud_type_type = NC_STRING;
1083  break;
1084  case H5T_COMPOUND:
1085  ud_type_type = NC_COMPOUND;
1086  break;
1087  case H5T_VLEN:
1088  /* For conveninence we allow user to pass vlens of strings
1089  * with null terminated strings. This means strings are
1090  * treated slightly differently by the API, although they are
1091  * really just VLENs of characters. */
1092  if ((ret = H5Tis_variable_str(hdf_typeid)) < 0)
1093  return NC_EHDFERR;
1094  if (ret)
1095  ud_type_type = NC_STRING;
1096  else
1097  {
1098  ud_type_type = NC_VLEN;
1099 
1100  /* Find the base type of this vlen (i.e. what is this a
1101  * vlen of?) */
1102  if (!(base_hdf_typeid = H5Tget_super(native_typeid)))
1103  return NC_EHDFERR;
1104 
1105  /* What size is this type? */
1106  if (!(type_size = H5Tget_size(base_hdf_typeid)))
1107  return NC_EHDFERR;
1108 
1109  /* What is the netcdf corresponding type. */
1110  if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid,
1111  &base_nc_type)))
1112  return retval;
1113  LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d",
1114  base_hdf_typeid, type_size, base_nc_type));
1115  }
1116  break;
1117  case H5T_OPAQUE:
1118  ud_type_type = NC_OPAQUE;
1119  /* What size is this type? */
1120  if (!(type_size = H5Tget_size(hdf_typeid)))
1121  return NC_EHDFERR;
1122  LOG((5, "type_size %d", type_size));
1123  break;
1124  case H5T_ENUM:
1125  ud_type_type = NC_ENUM;
1126 
1127  /* Find the base type of this enum (i.e. what is this a
1128  * enum of?) */
1129  if (!(base_hdf_typeid = H5Tget_super(hdf_typeid)))
1130  return NC_EHDFERR;
1131  /* What size is this type? */
1132  if (!(type_size = H5Tget_size(base_hdf_typeid)))
1133  return NC_EHDFERR;
1134  /* What is the netcdf corresponding type. */
1135  if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid,
1136  &base_nc_type)))
1137  return retval;
1138  LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d",
1139  base_hdf_typeid, type_size, base_nc_type));
1140  break;
1141  default:
1142  LOG((0, "unknown class"));
1143  return NC_EBADCLASS;
1144  }
1145 
1146  /* Add to the list for this new type, and get a local pointer to it. */
1147  if ((retval = nc4_type_list_add(&grp->type, &type)))
1148  return retval;
1149  assert(type);
1150 
1151  /* Remember info about this type. */
1152  type->nc_typeid = grp->nc4_info->next_typeid++;
1153  type->size = type_size;
1154  if (!(type->name = malloc((strlen(type_name) + 1) * sizeof(char))))
1155  return NC_ENOMEM;
1156  strcpy(type->name, type_name);
1157  type->class = ud_type_type;
1158  type->base_nc_type = base_nc_type;
1159  type->committed++;
1160  type->hdf_typeid = hdf_typeid;
1161  type->native_typeid = native_typeid;
1162 
1163  /* Read info about each member of this compound type. */
1164  if (ud_type_type == NC_COMPOUND)
1165  {
1166  if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0)
1167  return NC_EHDFERR;
1168  LOG((5, "compound type has %d members", nmembers));
1169  for (m = 0; m < nmembers; m++)
1170  {
1171  H5T_class_t mem_class;
1172  hid_t member_native_typeid;
1173  int ndims = 0, dim_size[NC_MAX_VAR_DIMS];
1174  hsize_t dims[NC_MAX_VAR_DIMS];
1175  int d;
1176 
1177  /* Get the typeid and native typeid of this member of the
1178  * compound type. */
1179  if ((member_hdf_typeid = H5Tget_member_type(type->native_typeid, m)) < 0)
1180  return NC_EHDFERR;
1181  if ((member_native_typeid = H5Tget_native_type(member_hdf_typeid, H5T_DIR_DEFAULT)) < 0)
1182  return NC_EHDFERR;
1183 
1184  /* Get the name of the member.*/
1185  member_name = H5Tget_member_name(type->native_typeid, m);
1186  if (!member_name || strlen(member_name) > NC_MAX_NAME)
1187  return NC_EBADNAME;
1188 
1189  /* Offset in bytes on *this* platform. */
1190  member_offset = H5Tget_member_offset(type->native_typeid, m);
1191 
1192  /* Get dimensional data if this member is an array of something. */
1193  if ((mem_class = H5Tget_class(member_hdf_typeid)) < 0)
1194  return NC_EHDFERR;
1195  if (mem_class == H5T_ARRAY)
1196  {
1197  if ((ndims = H5Tget_array_ndims(member_hdf_typeid)) < 0)
1198  return NC_EHDFERR;
1199  if (H5Tget_array_dims(member_hdf_typeid, dims, NULL) != ndims)
1200  return NC_EHDFERR;
1201  for (d = 0; d < ndims; d++)
1202  dim_size[d] = dims[d];
1203  /* What is the netCDF typeid of this member? */
1204  if ((retval = get_netcdf_type(grp->nc4_info, H5Tget_super(member_hdf_typeid),
1205  &member_xtype)))
1206  return retval;
1207  }
1208  else
1209  {
1210  /* What is the netCDF typeid of this member? */
1211  if ((retval = get_netcdf_type(grp->nc4_info, member_native_typeid,
1212  &member_xtype)))
1213  return retval;
1214  }
1215 
1216  /* Add this member to our list of fields in this compound type. */
1217  if (ndims)
1218  {
1219  if ((retval = nc4_field_list_add(&type->field, type->num_fields++, member_name,
1220  member_offset, H5Tget_super(member_hdf_typeid),
1221  H5Tget_super(member_native_typeid),
1222  member_xtype, ndims, dim_size)))
1223  return retval;
1224  }
1225  else
1226  {
1227  if ((retval = nc4_field_list_add(&type->field, type->num_fields++, member_name,
1228  member_offset, member_hdf_typeid, member_native_typeid,
1229  member_xtype, 0, NULL)))
1230  return retval;
1231  }
1232 
1233  /* HDF5 allocated this for us. */
1234  free(member_name);
1235  }
1236  }
1237  else if (ud_type_type == NC_VLEN)
1238  {
1239  type->base_hdf_typeid = base_hdf_typeid;
1240  }
1241  else if (ud_type_type == NC_ENUM)
1242  {
1243  /* Remember the base HDF5 type for this enum. */
1244  type->base_hdf_typeid = base_hdf_typeid;
1245 
1246  /* Find out how many member are in the enum. */
1247  if ((type->num_enum_members = H5Tget_nmembers(hdf_typeid)) < 0)
1248  return NC_EHDFERR;
1249 
1250  /* Allocate space for one value. */
1251  if (!(value = calloc(1, type_size)))
1252  return NC_ENOMEM;
1253 
1254  /* Read each name and value defined in the enum. */
1255  for (i = 0; i < type->num_enum_members; i++)
1256  {
1257  /* Get the name and value from HDF5. */
1258  if (!(member_name = H5Tget_member_name(hdf_typeid, i))) {
1259  if(value) free(value);
1260  return NC_EHDFERR;
1261  }
1262  if (!member_name || strlen(member_name) > NC_MAX_NAME) {
1263  if(value) free(value);
1264  return NC_EBADNAME;
1265  }
1266  if (H5Tget_member_value(hdf_typeid, i, value) < 0)
1267  return NC_EHDFERR;
1268 
1269  /* Insert new field into this type's list of fields. */
1270  if ((retval = nc4_enum_member_add(&type->enum_member, type->size,
1271  member_name, value)))
1272  return retval;
1273  free(member_name);
1274 
1275  }
1276 
1277  /* Free the tempory memory for one value, and the member name
1278  * (which HDF5 allocated for us). */
1279  free(value);
1280  }
1281 
1282  return retval;
1283 }
1284 
1285 /* This function is called by read_dataset, (which is called by
1286  * nc4_rec_read_metadata) when a netCDF variable is found in the
1287  * file. This function reads in all the metadata about the var,
1288  * including the attributes. */
1289 static int
1290 read_var(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
1291  size_t ndims, int is_scale, int num_scales, hid_t access_pid)
1292 {
1293  NC_VAR_INFO_T *var = NULL;
1294  int natts, a, d;
1295 
1296  NC_ATT_INFO_T *att;
1297  hid_t attid = 0;
1298  char att_name[NC_MAX_HDF5_NAME + 1];
1299 
1300 #define CD_NELEMS_ZLIB 1
1301 #define CD_NELEMS_SZIP 4
1302  H5Z_filter_t filter;
1303  int num_filters;
1304  unsigned int cd_values[CD_NELEMS_SZIP];
1305  size_t cd_nelems = CD_NELEMS_SZIP;
1306  hid_t propid = 0;
1307  H5D_fill_value_t fill_status;
1308  H5D_layout_t layout;
1309  hsize_t chunksize[NC_MAX_VAR_DIMS];
1310  int retval = NC_NOERR;
1311  double rdcc_w0;
1312  int f;
1313 
1314  assert(obj_name && grp);
1315  LOG((4, "read_var: obj_name %s", obj_name));
1316 
1317  /* Add a variable to the end of the group's var list. */
1318  if ((retval = nc4_var_list_add(&grp->var, &var)))
1319  return retval;
1320 
1321  if(!var)
1322  return NC_ENOMEM;
1323 
1324  /* Fill in what we already know. */
1325  var->hdf_datasetid = datasetid;
1326  var->varid = grp->nvars++;
1327  var->created++;
1328  var->ndims = ndims;
1329 
1330  /* We need some room to store information about dimensions for this
1331  * var. */
1332  if (var->ndims)
1333  {
1334  if (!(var->dim = calloc(var->ndims, sizeof(NC_DIM_INFO_T *))))
1335  return NC_ENOMEM;
1336  if (!(var->dimids = calloc(var->ndims, sizeof(int))))
1337  return NC_ENOMEM;
1338  }
1339 
1340  /* Learn about current chunk cache settings. */
1341  if ((H5Pget_chunk_cache(access_pid, &(var->chunk_cache_nelems),
1342  &(var->chunk_cache_size), &rdcc_w0)) < 0)
1343  return NC_EHDFERR;
1344  var->chunk_cache_preemption = rdcc_w0;
1345 
1346  /* Allocate space for the name. */
1347  if (!(var->name = malloc((strlen(obj_name) + 1) * sizeof(char))))
1348  return NC_ENOMEM;
1349 
1350  /* Check for a weird case: a non-coordinate variable that has the
1351  * same name as a dimension. It's legal in netcdf, and requires
1352  * that the HDF5 dataset name be changed. */
1353  if (!strncmp(obj_name, NON_COORD_PREPEND, strlen(NON_COORD_PREPEND)))
1354  {
1355  if (strlen(obj_name) > NC_MAX_NAME)
1356  return NC_EMAXNAME;
1357  strcpy(var->name, &obj_name[strlen(NON_COORD_PREPEND)]);
1358  }
1359  else
1360  strcpy(var->name, obj_name);
1361 
1362  /* Find out what filters are applied to this HDF5 dataset,
1363  * fletcher32, deflate, and/or shuffle. All other filters are
1364  * ignored. */
1365  if ((propid = H5Dget_create_plist(datasetid)) < 0)
1366  BAIL(NC_EHDFERR);
1367 #ifdef EXTRA_TESTS
1368  num_plists++;
1369 #endif /* EXTRA_TESTS */
1370 
1371  /* Get the chunking info for non-scalar vars. */
1372  if ((layout = H5Pget_layout(propid)) < -1)
1373  BAIL(NC_EHDFERR);
1374  if (layout == H5D_CHUNKED)
1375  {
1376  if (H5Pget_chunk(propid, NC_MAX_VAR_DIMS, chunksize) < 0)
1377  BAIL(NC_EHDFERR);
1378  if (!(var->chunksizes = malloc(var->ndims * sizeof(size_t))))
1379  BAIL(NC_ENOMEM);
1380  for (d = 0; d < var->ndims; d++)
1381  var->chunksizes[d] = chunksize[d];
1382  }
1383  else if (layout == H5D_CONTIGUOUS)
1384  var->contiguous++;
1385 
1386  /* The possible values of filter (which is just an int) can be
1387  * found in H5Zpublic.h. */
1388  if ((num_filters = H5Pget_nfilters(propid)) < 0)
1389  BAIL(NC_EHDFERR);
1390  for (f = 0; f < num_filters; f++)
1391  {
1392  if ((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems,
1393  cd_values, 0, NULL, NULL)) < 0)
1394  BAIL(NC_EHDFERR);
1395  switch (filter)
1396  {
1397  case H5Z_FILTER_SHUFFLE:
1398  var->shuffle = 1;
1399  break;
1400  case H5Z_FILTER_FLETCHER32:
1401  var->fletcher32 = 1;
1402  break;
1403  case H5Z_FILTER_DEFLATE:
1404  var->deflate++;
1405  if (cd_nelems != CD_NELEMS_ZLIB ||
1406  cd_values[0] > MAX_DEFLATE_LEVEL)
1407  BAIL(NC_EHDFERR);
1408  var->deflate_level = cd_values[0];
1409  break;
1410  case H5Z_FILTER_SZIP:
1411  var->deflate++;
1412  if (cd_nelems != CD_NELEMS_SZIP)
1413  BAIL(NC_EHDFERR);
1414  var->options_mask = cd_values[0];
1415  var->pixels_per_block = cd_values[1];
1416  break;
1417  default:
1418  LOG((1, "Yikes! Unknown filter type found on dataset!"));
1419  break;
1420  }
1421  }
1422 
1423  /* Learn all about the type of this variable. */
1424  if ((retval = get_type_info2(grp->nc4_info, datasetid,
1425  &var->xtype, &var->type_info)))
1426  BAIL(retval);
1427 
1428  /* Is there a fill value associated with this dataset? */
1429  if (H5Pfill_value_defined(propid, &fill_status) < 0)
1430  BAIL(NC_EHDFERR);
1431 
1432  /* Get the fill value, if there is one defined. */
1433  if (fill_status == H5D_FILL_VALUE_USER_DEFINED)
1434  {
1435  /* Allocate space to hold the fill value. */
1436  if (!var->fill_value)
1437  {
1438  if (var->type_info->class == NC_VLEN)
1439  {
1440  if (!(var->fill_value = malloc(sizeof(nc_vlen_t))))
1441  BAIL(NC_ENOMEM);
1442  }
1443  else if (var->type_info->size)
1444  {
1445  if (!(var->fill_value = malloc(var->type_info->size)))
1446  BAIL(NC_ENOMEM);
1447  }
1448  else
1449  {
1450  if (!(var->fill_value = malloc(sizeof(char *))))
1451  BAIL(NC_ENOMEM);
1452  }
1453  }
1454 
1455  /* Get the fill value from the HDF5 property lust. */
1456  if (H5Pget_fill_value(propid, var->type_info->native_typeid,
1457  var->fill_value) < 0)
1458  BAIL(NC_EHDFERR);
1459  }
1460  else
1461  var->no_fill = 1;
1462 
1463  /* If it's a scale, mark it as such. If not, allocate space to
1464  * remember whether the dimscale has been attached for each
1465  * dimension. */
1466  if (is_scale)
1467  {
1468  assert(ndims);
1469  var->dimscale++;
1470  if (var->ndims > 1)
1471  {
1472  if ((retval = read_coord_dimids(var)))
1473  BAIL(retval);
1474  }
1475  else
1476  {
1477  var->dimids[0] = grp->dim->dimid;
1478  var->dim[0] = grp->dim;
1479  }
1480  }
1481  else
1482  if (num_scales && ndims &&
1483  !(var->dimscale_attached = calloc(ndims, sizeof(int))))
1484  BAIL(NC_ENOMEM);
1485 
1486  /* If this is not a scale, and has scales, iterate
1487  * through them. (i.e. this is a variable that is not a
1488  * coordinate variable) */
1489  if (!is_scale && num_scales)
1490  {
1491  /* Store id information allowing us to match hdf5
1492  * dimscales to netcdf dimensions. */
1493  if (!(var->dimscale_hdf5_objids = malloc(ndims * sizeof(struct hdf5_objid))))
1494  BAIL(NC_ENOMEM);
1495  for (d = 0; d < var->ndims; d++)
1496  {
1497  LOG((5, "read_var: about to iterate over scales for dim %d", d));
1498  if (H5DSiterate_scales(var->hdf_datasetid, d, NULL, dimscale_visitor,
1499  &(var->dimscale_hdf5_objids[d])) < 0)
1500  BAIL(NC_EHDFERR);
1501 /* LOG((5, "read_var: collected scale info for dim %d "
1502  "var %s fileno[0] %d objno[0] %d fileno[1] %d objno[1] %d",
1503  d, var->name, var->dimscale_hdf5_objids[d].fileno[0],
1504  var->dimscale_hdf5_objids[d].objno[0],
1505  var->dimscale_hdf5_objids[d].fileno[1],
1506  var->dimscale_hdf5_objids[d].objno[1]));*/
1507  var->dimscale_attached[d]++;
1508  }
1509  }
1510 
1511  /* Now read all the attributes of this variable, ignoring the
1512  ones that hold HDF5 dimension scale information. */
1513  if ((natts = H5Aget_num_attrs(var->hdf_datasetid)) < 0)
1514  BAIL(NC_EATTMETA);
1515  for (a = 0; a < natts; a++)
1516  {
1517  /* Close the attribute and try to move on with our
1518  * lives. Like bits through the network port, so
1519  * flows the Days of Our Lives! */
1520  if (attid && H5Aclose(attid) < 0)
1521  BAIL(NC_EHDFERR);
1522 
1523  /* Open the att and get its name. */
1524  if ((attid = H5Aopen_idx(var->hdf_datasetid, (unsigned int)a)) < 0)
1525  BAIL(NC_EATTMETA);
1526  if (H5Aget_name(attid, NC_MAX_HDF5_NAME, att_name) < 0)
1527  BAIL(NC_EATTMETA);
1528  LOG((4, "read_var: a %d att_name %s", a, att_name));
1529 
1530  /* Should we ignore this attribute? */
1531  if (strcmp(att_name, REFERENCE_LIST) &&
1532  strcmp(att_name, CLASS) &&
1533  strcmp(att_name, DIMENSION_LIST) &&
1534  strcmp(att_name, NAME) &&
1535  strcmp(att_name, COORDINATES))
1536  {
1537  /* Is this the hidden attribute that holds the netCDF
1538  * dimension id for a coordinate variable? */
1539  if (!strcmp(att_name, NC_DIMID_ATT_NAME))
1540  {
1541 
1542  }
1543  else
1544  {
1545  /* Add to the end of the list of atts for this var. */
1546  if ((retval = nc4_att_list_add(&var->att)))
1547  BAIL(retval);
1548  for (att = var->att; att->next; att = att->next)
1549  ;
1550 
1551  /* Fill in the information we know. */
1552  att->attnum = var->natts++;
1553  if (!(att->name = malloc((strlen(att_name) + 1) * sizeof(char))))
1554  BAIL(NC_ENOMEM);
1555  strcpy(att->name, att_name);
1556 
1557  /* Read the rest of the info about the att,
1558  * including its values. */
1559  if ((retval = read_hdf5_att(grp, attid, att)))
1560  {
1561  if (NC_EBADTYPID == retval)
1562  {
1563  if ((retval = nc4_att_list_del(&var->att, att)))
1564  BAIL(retval);
1565  continue;
1566  }
1567  else
1568  BAIL(retval);
1569  }
1570 
1571  att->created++;
1572  } /* endif not HDF5 att */
1573  }
1574  } /* next attribute */
1575 
1576  /* Is this a deflated variable with a chunksize greater than the
1577  * current cache size? */
1578  if ((retval = nc4_adjust_var_cache(grp, var)))
1579  BAIL(retval);
1580 
1581  exit:
1582  if (var && retval)
1583  {
1584  if(nc4_var_list_del(&grp->var, var))
1585  BAIL2(NC_EHDFERR);
1586  }
1587  if (propid > 0 && H5Pclose(propid) < 0)
1588  BAIL2(NC_EHDFERR);
1589 #ifdef EXTRA_TESTS
1590  num_plists--;
1591 #endif
1592  if (attid > 0 && H5Aclose(attid) < 0)
1593  BAIL2(NC_EHDFERR);
1594  return retval;
1595 }
1596 
1597 /* This function is called by nc4_rec_read_metadata to read all the
1598  * group level attributes (the NC_GLOBAL atts for this group). */
1599 static int
1600 read_grp_atts(NC_GRP_INFO_T *grp)
1601 {
1602  hid_t attid = 0;
1603  hsize_t num_obj, i;
1604  NC_ATT_INFO_T *att;
1605  NC_TYPE_INFO_T *type;
1606  char obj_name[NC_MAX_HDF5_NAME + 1];
1607  int max_len;
1608  int retval = NC_NOERR;
1609 
1610  num_obj = H5Aget_num_attrs(grp->hdf_grpid);
1611  for (i = 0; i < num_obj; i++)
1612  {
1613  if (attid > 0)
1614  H5Aclose(attid);
1615  if ((attid = H5Aopen_idx(grp->hdf_grpid, (unsigned int)i)) < 0)
1616  BAIL(NC_EATTMETA);
1617  if (H5Aget_name(attid, NC_MAX_NAME + 1, obj_name) < 0)
1618  BAIL(NC_EATTMETA);
1619  LOG((3, "reading attribute of _netCDF group, named %s", obj_name));
1620 
1621  /* This may be an attribute telling us that strict netcdf-3
1622  * rules are in effect. If so, we will make note of the fact,
1623  * but not add this attribute to the metadata. It's not a user
1624  * attribute, but an internal netcdf-4 one. */
1625  if (!strcmp(obj_name, NC3_STRICT_ATT_NAME))
1626  grp->nc4_info->cmode |= NC_CLASSIC_MODEL;
1627  else
1628  {
1629  /* Add an att struct at the end of the list, and then go to it. */
1630  if ((retval = nc4_att_list_add(&grp->att)))
1631  BAIL(retval);
1632  for (att = grp->att; att->next; att = att->next)
1633  ;
1634 
1635  /* Add the info about this attribute. */
1636  max_len = strlen(obj_name) > NC_MAX_NAME ? NC_MAX_NAME : strlen(obj_name);
1637  if (!(att->name = malloc((max_len + 1) * sizeof(char))))
1638  BAIL(NC_ENOMEM);
1639  strncpy(att->name, obj_name, max_len);
1640  att->name[max_len] = 0;
1641  att->attnum = grp->natts++;
1642  if ((retval = read_hdf5_att(grp, attid, att)))
1643  {
1644  if (NC_EBADTYPID == retval)
1645  {
1646  if ((retval = nc4_att_list_del(&grp->att, att)))
1647  BAIL(retval);
1648  continue;
1649  }
1650  else
1651  BAIL(retval);
1652  }
1653  att->created++;
1654  if ((retval = nc4_find_type(grp->nc4_info, att->xtype, &type)))
1655  BAIL(retval);
1656  if (type)
1657  att->class = type->class;
1658  }
1659  }
1660 
1661  exit:
1662  if (attid > 0 && H5Aclose(attid) < 0)
1663  BAIL2(NC_EHDFERR);
1664  return retval;
1665 }
1666 
1667 /* This function is called when nc4_rec_read_vars encounters an HDF5
1668  * dataset when reading a file. */
1669 static int
1670 read_dataset(NC_GRP_INFO_T *grp, const char *obj_name)
1671 {
1672  hid_t datasetid = 0;
1673  hid_t spaceid = 0, access_pid = 0;
1674  int ndims;
1675  hsize_t *dims = NULL;
1676  hsize_t *max_dims = NULL;
1677  int is_scale = 0;
1678  int dim_without_var = 0;
1679  int num_scales = 0;
1680  int retval = NC_NOERR;
1681 
1682  /* Open this dataset. */
1683  if ((datasetid = H5Dopen2(grp->hdf_grpid, obj_name, H5P_DEFAULT)) < 0)
1684  BAIL(NC_EVARMETA);
1685 
1686  /* Get the current chunk cache settings. */
1687  if ((access_pid = H5Dget_access_plist(datasetid)) < 0)
1688  BAIL(NC_EVARMETA);
1689 #ifdef EXTRA_TESTS
1690  num_plists++;
1691 #endif
1692 
1693  /* Get the dimension information for this dataset. */
1694  if ((spaceid = H5Dget_space(datasetid)) < 0)
1695  BAIL(NC_EHDFERR);
1696 #ifdef EXTRA_TESTS
1697  num_spaces++;
1698 #endif
1699  if ((ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
1700  BAIL(NC_EHDFERR);
1701 
1702  /* Removed check to remove nc4 dependency on
1703  maximum dimensions. */
1704  //if (ndims > NC_MAX_DIMS)
1705  // BAIL(NC_EMAXDIMS);
1706 
1707  if( (dims = (hsize_t*)malloc(sizeof(hsize_t)*ndims)) == NULL)
1708  BAIL(errno);
1709  if( (max_dims = (hsize_t*)malloc(sizeof(hsize_t)*ndims)) == NULL)
1710  BAIL(errno);
1711 
1712  if (H5Sget_simple_extent_dims(spaceid, dims, max_dims) < 0)
1713  BAIL(NC_EHDFERR);
1714 
1715  /* Is this a dimscale? */
1716  if ((is_scale = H5DSis_scale(datasetid)) < 0)
1717  BAIL(NC_EHDFERR);
1718  if (is_scale)
1719  {
1720  /* Read the scale information. */
1721  if ((retval = read_scale(grp, datasetid, obj_name, dims[0],
1722  max_dims[0], &dim_without_var)))
1723  BAIL(retval);
1724  }
1725  else
1726  {
1727  /* Find out how many scales are attached to this
1728  * dataset. H5DSget_num_scales returns an error if there are no
1729  * scales, so convert a negative return value to zero. */
1730  num_scales = H5DSget_num_scales(datasetid, 0);
1731  if (num_scales < 0)
1732  num_scales = 0;
1733  }
1734 
1735  /* Add a var to the linked list, and get its metadata,
1736  * unless this is one of those funny dimscales that are a
1737  * dimension in netCDF but not a variable. (Spooky!) */
1738  if (!dim_without_var)
1739  if ((retval = read_var(grp, datasetid, obj_name, ndims,
1740  is_scale, num_scales, access_pid)))
1741  BAIL(retval);
1742 
1743  if (access_pid && H5Pclose(access_pid) < 0)
1744  BAIL2(retval);
1745 #ifdef EXTRA_TESTS
1746  num_plists--;
1747 #endif
1748  if (spaceid && H5Sclose(spaceid) < 0)
1749  BAIL2(retval);
1750 #ifdef EXTRA_TESTS
1751  num_spaces--;
1752 #endif
1753 
1754  if(dims) free(dims);
1755  if(max_dims) free(max_dims);
1756 
1757  return NC_NOERR;
1758 
1759  exit:
1760  if (access_pid && H5Pclose(access_pid) < 0)
1761  BAIL2(retval);
1762 #ifdef EXTRA_TESTS
1763  num_plists--;
1764 #endif
1765  if (datasetid && H5Dclose(datasetid) < 0)
1766  BAIL2(retval);
1767  if (spaceid && H5Sclose(spaceid) <0)
1768  BAIL2(retval);
1769 #ifdef EXTRA_TESTS
1770  num_spaces--;
1771 #endif
1772 
1773  if(dims) free(dims);
1774  if(max_dims) free(max_dims);
1775  return retval;
1776 }
1777 
1778 /* Given index, get the HDF5 name of an object and the class of the
1779  * object (group, type, dataset, etc.). This function will try to use
1780  * creation ordering, but if that fails it will use default
1781  * (i.e. alphabetical) ordering. (This is necessary to read existing
1782  * HDF5 archives without creation ordering). */
1783 /* static int */
1784 /* get_name_by_idx(NC_HDF5_FILE_INFO_T *h5, hid_t hdf_grpid, int i, */
1785 /* int *obj_class, char *obj_name) */
1786 /* { */
1787 /* H5O_info_t obj_info; */
1788 /* H5_index_t idx_field = H5_INDEX_CRT_ORDER; */
1789 /* ssize_t size; */
1790 /* herr_t res; */
1791 
1792 /* /\* These HDF5 macros prevent an HDF5 error message when a */
1793 /* * non-creation-ordered HDF5 file is opened. *\/ */
1794 /* H5E_BEGIN_TRY { */
1795 /* res = H5Oget_info_by_idx(hdf_grpid, ".", H5_INDEX_CRT_ORDER, H5_ITER_INC, */
1796 /* i, &obj_info, H5P_DEFAULT); */
1797 /* } H5E_END_TRY; */
1798 
1799 /* /\* Creation ordering not available, so make sure this file is */
1800 /* * opened for read-only access. This is a plain old HDF5 file being */
1801 /* * read by netCDF-4. *\/ */
1802 /* if (res < 0) */
1803 /* { */
1804 /* if (H5Oget_info_by_idx(hdf_grpid, ".", H5_INDEX_NAME, H5_ITER_INC, */
1805 /* i, &obj_info, H5P_DEFAULT) < 0) */
1806 /* return NC_EHDFERR; */
1807 /* if (!h5->no_write) */
1808 /* return NC_ECANTWRITE; */
1809 /* h5->ignore_creationorder = 1; */
1810 /* idx_field = H5_INDEX_NAME; */
1811 /* } */
1812 
1813 /* *obj_class = obj_info.type; */
1814 /* if ((size = H5Lget_name_by_idx(hdf_grpid, ".", idx_field, H5_ITER_INC, i, */
1815 /* NULL, 0, H5P_DEFAULT)) < 0) */
1816 /* return NC_EHDFERR; */
1817 /* if (size > NC_MAX_NAME) */
1818 /* return NC_EMAXNAME; */
1819 /* if (H5Lget_name_by_idx(hdf_grpid, ".", idx_field, H5_ITER_INC, i, */
1820 /* obj_name, size+1, H5P_DEFAULT) < 0) */
1821 /* return NC_EHDFERR; */
1822 
1823 /* LOG((4, "get_name_by_idx: encountered HDF5 object obj_name %s", obj_name)); */
1824 
1825 /* return NC_NOERR; */
1826 /* } */
1827 
1828 #define USE_ITERATE_CODE
1829 #ifdef USE_ITERATE_CODE
1830 
1831 static int
1832 nc4_rec_read_types_cb(hid_t grpid, const char *name, const H5L_info_t *info,
1833  void *_op_data)
1834 {
1835  hid_t oid=-1;
1836  H5I_type_t otype=-1;
1837  char oname[NC_MAX_NAME + 1];
1838  NC_GRP_INFO_T *child_grp;
1839  NC_GRP_INFO_T *grp = (NC_GRP_INFO_T *) (_op_data);
1840  NC_HDF5_FILE_INFO_T *h5 = grp->nc4_info;
1841 
1842  /* Open this critter. */
1843  if ((oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0)
1844  return H5_ITER_ERROR;
1845 
1846  if ((otype = H5Iget_type( oid ))<0) {
1847  H5Oclose(oid);
1848  return H5_ITER_ERROR;
1849  }
1850  H5Oclose(oid);
1851 
1852  strncpy(oname, name, NC_MAX_NAME);
1853 
1854  /* Deal with groups and types; ignore the rest. */
1855  if (otype == H5I_GROUP)
1856  {
1857  LOG((3, "found group %s", oname));
1858  if (nc4_grp_list_add(&(grp->children), h5->next_nc_grpid++,
1859  grp, grp->nc4_info->controller, oname, &child_grp))
1860  return H5_ITER_ERROR;
1861 
1862  if (nc4_rec_read_types(child_grp))
1863  return H5_ITER_ERROR;
1864  }
1865  else if (otype == H5I_DATATYPE)
1866  {
1867  LOG((3, "found datatype %s", oname));
1868  if (read_type(grp, oname))
1869  return H5_ITER_ERROR;
1870  }
1871 
1872  return (H5_ITER_CONT);
1873 }
1874 
1875 static int
1876 nc4_rec_read_types(NC_GRP_INFO_T *grp)
1877 {
1878  hsize_t idx=0;
1879  int res = 0;
1880  hid_t pid = 0;
1881  unsigned crt_order_flags = 0;
1882  NC_HDF5_FILE_INFO_T *h5 = grp->nc4_info;
1883 
1884  assert(grp && grp->name);
1885  LOG((3, "nc4_rec_read_types: grp->name %s", grp->name));
1886 
1887  /* Open this HDF5 group and retain its grpid. It will remain open
1888  * with HDF5 until this file is nc_closed. */
1889  if (!grp->hdf_grpid)
1890  {
1891  if (grp->parent)
1892  {
1893  if ((grp->hdf_grpid = H5Gopen2(grp->parent->hdf_grpid,
1894  grp->name, H5P_DEFAULT)) < 0)
1895  return NC_EHDFERR;
1896  }
1897  else
1898  {
1899  if ((grp->hdf_grpid = H5Gopen2(grp->nc4_info->hdfid,
1900  "/", H5P_DEFAULT)) < 0)
1901  return NC_EHDFERR;
1902  }
1903  }
1904  assert(grp->hdf_grpid > 0);
1905 
1906  pid = H5Gget_create_plist(grp->hdf_grpid);
1907  H5Pget_link_creation_order(pid, &crt_order_flags);
1908  if(H5Pclose(pid) < 0)
1909  return NC_EHDFERR;
1910 
1911  crt_order_flags = crt_order_flags & H5_INDEX_CRT_ORDER;
1912 
1913  if (crt_order_flags == H5_INDEX_CRT_ORDER)
1914  {
1915  res = H5Literate(grp->hdf_grpid, H5_INDEX_CRT_ORDER, H5_ITER_INC,
1916  &idx, nc4_rec_read_types_cb, (void *)grp);
1917  } else
1918  {
1919  /* Without creation ordering, file must be read-only. */
1920  if (!idx && !h5->no_write)
1921  return NC_ECANTWRITE;
1922 
1923  res = H5Literate(grp->hdf_grpid, H5_INDEX_NAME, H5_ITER_INC,
1924  &idx, nc4_rec_read_types_cb, (void *)grp);
1925  }
1926  if (res<0)
1927  return NC_EHDFERR;
1928  return NC_NOERR; /* everything worked! */
1929 }
1930 
1931 static int
1932 nc4_rec_read_vars_cb(hid_t grpid, const char *name, const H5L_info_t *info,
1933  void *_op_data)
1934 {
1935  hid_t oid=-1;
1936  H5I_type_t otype=-1;
1937  char oname[NC_MAX_NAME + 1];
1938  NC_GRP_INFO_T *child_grp;
1939  NC_GRP_INFO_T *grp = (NC_GRP_INFO_T *) (_op_data);
1940 #if 0
1941  NC_HDF5_FILE_INFO_T *h5 = grp->nc4_info;
1942 #endif
1943 
1944  memset(oname, 0, NC_MAX_NAME);
1945  /* Open this critter. */
1946  if ((oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0)
1947  return H5_ITER_ERROR;
1948 
1949  if ((otype = H5Iget_type( oid ))<0) {
1950  H5Oclose(oid);
1951  return H5_ITER_ERROR;
1952  }
1953  H5Oclose(oid);
1954 
1955  strncpy(oname, name, NC_MAX_NAME);
1956 
1957  /* Deal with datasets. */
1958  switch(otype)
1959  {
1960  case H5I_GROUP:
1961  LOG((3, "re-encountering group %s", oname));
1962 
1963  /* The NC_GROUP_INFO_T for this group already exists. Find it. */
1964  for (child_grp = grp->children; child_grp; child_grp = child_grp->next)
1965  if (!strcmp(child_grp->name, oname))
1966  break;
1967  if (!child_grp)
1968  return H5_ITER_ERROR;
1969 
1970  /* Recursively read the child group's vars. */
1971  if (nc4_rec_read_vars(child_grp))
1972  return H5_ITER_ERROR;
1973  break;
1974  case H5I_DATASET:
1975  {
1976  int retval = NC_NOERR;
1977 
1978  LOG((3, "found dataset %s", oname));
1979 
1980  /* Learn all about this dataset, which may be a dimscale
1981  * (i.e. dimension metadata), or real data. */
1982  if ((retval = read_dataset(grp, oname))) {
1983  if(NC_EBADTYPID == retval)
1984  return H5_ITER_CONT;
1985  else
1986  return H5_ITER_ERROR;
1987  }
1988  }
1989  break;
1990  case H5I_DATATYPE:
1991  LOG((3, "already handled type %s", oname));
1992  break;
1993  default:
1994  LOG((0, "Unknown object class %d in nc4_rec_read_vars!", otype));
1995  }
1996  return (H5_ITER_CONT);
1997 }
1998 
1999 static int
2000 nc4_rec_read_vars(NC_GRP_INFO_T *grp)
2001 {
2002  hsize_t idx = 0;
2003  int retval = NC_NOERR;
2004  int res = 0;
2005  hid_t pid = 0;
2006  unsigned crt_order_flags = 0;
2007  NC_HDF5_FILE_INFO_T *h5 = grp->nc4_info;
2008 
2009  assert(grp && grp->name && grp->hdf_grpid > 0);
2010  LOG((3, "nc4_rec_read_vars: grp->name %s", grp->name));
2011 
2012  pid = H5Gget_create_plist(grp->hdf_grpid);
2013  H5Pget_link_creation_order(pid, &crt_order_flags);
2014  if(H5Pclose(pid) < 0)
2015  return NC_EHDFERR;
2016 
2017  crt_order_flags = crt_order_flags & H5_INDEX_CRT_ORDER;
2018 
2019  if (crt_order_flags == H5_INDEX_CRT_ORDER)
2020  {
2021  res = H5Literate(grp->hdf_grpid, H5_INDEX_CRT_ORDER, H5_ITER_INC,
2022  &idx, nc4_rec_read_vars_cb, (void *)grp);
2023  } else
2024  {
2025  /* Without creation ordering, file must be read-only. */
2026  if (!idx && !h5->no_write)
2027  return NC_ECANTWRITE;
2028 
2029  res = H5Literate(grp->hdf_grpid, H5_INDEX_NAME, H5_ITER_INC,
2030  &idx, nc4_rec_read_vars_cb, (void *)grp);
2031  }
2032  if (res<0)
2033  return NC_EHDFERR;
2034 
2035  /* Scan the group for global (i.e. group-level) attributes. */
2036  if ((retval = read_grp_atts(grp)))
2037  return retval;
2038 
2039  return NC_NOERR; /* everything worked! */
2040 }
2041 
2042 #else
2043 
2048 struct nc_hdf5_link_info
2049 {
2050  char name[NC_MAX_NAME + 1];
2051  H5I_type_t obj_type;
2052 };
2053 
2054 /* This is a callback function for H5Literate().
2055 
2056 The parameters of this callback function have the following values or
2057 meanings:
2058 
2059 g_id Group that serves as root of the iteration; same value as the
2060 H5Lvisit group_id parameter
2061 
2062 name Name of link, relative to g_id, being examined at current step of
2063 the iteration
2064 
2065 info H5L_info_t struct containing information regarding that link
2066 
2067 op_data User-defined pointer to data required by the application in
2068 processing the link; a pass-through of the op_data pointer provided
2069 with the H5Lvisit function call
2070 
2071 */
2072 static herr_t
2073 visit_link(hid_t g_id, const char *name, const H5L_info_t *info,
2074  void *op_data)
2075 {
2076  /* A positive return value causes the visit iterator to immediately
2077  * return that positive value, indicating short-circuit
2078  * success. The iterator can be restarted at the next group
2079  * member. */
2080  int ret = 1;
2081  hid_t id;
2082 
2083  /* Get the name, truncating at NC_MAX_NAME. */
2084  strncpy(((struct nc_hdf5_link_info *)op_data)->name, name,
2085  NC_MAX_NAME);
2086 
2087  /* Open this critter. */
2088  if ((id = H5Oopen_by_addr(g_id, info->u.address)) < 0)
2089  return NC_EHDFERR;
2090 
2091  /* Is this critter a group, type, data, attribute, or what? */
2092  if ((((struct nc_hdf5_link_info *)op_data)->obj_type = H5Iget_type(id)) < 0)
2093  ret = NC_EHDFERR;
2094 
2095  /* Close the critter to release resouces. */
2096  if (H5Oclose(id) < 0)
2097  return NC_EHDFERR;
2098 
2099  return ret;
2100 }
2101 
2102 /* Iterate over one link in the group at a time, returning
2103  * link_info. The creation_ordering and idx pointers keep track of
2104  * whether creation ordering works and the most recently examined
2105  * link. */
2106 static int
2107 nc4_iterate_link(int *ordering_checked, int *creation_ordering,
2108  hid_t grpid, hsize_t *idx, struct nc_hdf5_link_info *link_info)
2109 {
2110  int res = 0;
2111 
2112  if (*creation_ordering)
2113  {
2114  /* These HDF5 macros prevent an HDF5 error message when a
2115  * non-creation-ordered HDF5 file is opened. */
2116  H5E_BEGIN_TRY {
2117  res = H5Literate(grpid, H5_INDEX_CRT_ORDER, H5_ITER_INC,
2118  idx, visit_link, (void *)link_info);
2119  if (res < 0 && *ordering_checked)
2120  return NC_EHDFERR;
2121  } H5E_END_TRY;
2122  }
2123 
2124  if (!*creation_ordering || res < 0)
2125  {
2126  if (H5Literate(grpid, H5_INDEX_NAME, H5_ITER_INC, idx,
2127  visit_link, link_info) != 1)
2128  return NC_EHDFERR;
2129  /* If it didn't work with creation ordering, but did without,
2130  * then we don't have creation ordering. */
2131  *creation_ordering = 0;
2132  }
2133 
2134  *ordering_checked = 1;
2135  return NC_NOERR;
2136 }
2137 
2138 /* Recursively open groups and read types. */
2139 int
2140 nc4_rec_read_types(NC_GRP_INFO_T *grp)
2141 {
2142  hsize_t num_obj, i;
2143  NC_HDF5_FILE_INFO_T *h5 = grp->nc4_info;
2144  NC_GRP_INFO_T *child_grp;
2145  hsize_t idx = 0;
2146  struct nc_hdf5_link_info link_info;
2147  int ordering_checked = 0;
2148  int creation_ordering = 1; /* Assume we have it. */
2149  int retval = NC_NOERR;
2150 
2151  assert(grp && grp->name);
2152  LOG((3, "nc4_rec_read_types: grp->name %s", grp->name));
2153 
2154  /* Open this HDF5 group and retain its grpid. It will remain open
2155  * with HDF5 until this file is nc_closed. */
2156  if (!grp->hdf_grpid)
2157  {
2158  if (grp->parent)
2159  {
2160  if ((grp->hdf_grpid = H5Gopen2(grp->parent->hdf_grpid,
2161  grp->name, H5P_DEFAULT)) < 0)
2162  return NC_EHDFERR;
2163  }
2164  else
2165  {
2166  if ((grp->hdf_grpid = H5Gopen2(grp->nc4_info->hdfid,
2167  "/", H5P_DEFAULT)) < 0)
2168  return NC_EHDFERR;
2169  }
2170  }
2171  assert(grp->hdf_grpid > 0);
2172 
2173  /* How many objects in this group? */
2174  if (H5Gget_num_objs(grp->hdf_grpid, &num_obj) < 0)
2175  return NC_EVARMETA;
2176 
2177  /* For each object in the group... */
2178  for (i = 0; i < num_obj; i++)
2179  {
2180  if ((retval = nc4_iterate_link(&ordering_checked, &creation_ordering,
2181  grp->hdf_grpid, &idx, &link_info)))
2182  return retval;
2183 
2184  /* Without creation ordering, file must be read-only. */
2185  if (!i && !creation_ordering && !h5->no_write)
2186  return NC_ECANTWRITE;
2187 
2188  /* Deal with groups and types; ignore the rest. */
2189  if (link_info.obj_type == H5I_GROUP)
2190  {
2191  LOG((3, "found group %s", link_info.name));
2192  if ((retval = nc4_grp_list_add(&(grp->children), h5->next_nc_grpid++,
2193  grp, grp->file, link_info.name, &child_grp)))
2194  return retval;
2195  if ((retval = nc4_rec_read_types(child_grp)))
2196  return retval;
2197  }
2198  else if (link_info.obj_type == H5I_DATATYPE)
2199  {
2200  LOG((3, "found datatype %s", link_info.name));
2201  if ((retval = read_type(grp, link_info.name)))
2202  return retval;
2203  }
2204  }
2205 
2206  return NC_NOERR; /* everything worked! */
2207 }
2208 
2209 /* This function recursively reads all the var and attribute metadata
2210  in a HDF5 group, and creates and fills in the netCDF-4 global
2211  metadata structure. */
2212 int
2213 nc4_rec_read_vars(NC_GRP_INFO_T *grp)
2214 {
2215  hsize_t num_obj, i;
2216  NC_GRP_INFO_T *child_grp;
2217  struct nc_hdf5_link_info link_info;
2218  hsize_t idx = 0;
2219  int ordering_checked = 0;
2220  int creation_ordering = 1; /* Assume we have it. */
2221  int retval = NC_NOERR;
2222 
2223  assert(grp && grp->name && grp->hdf_grpid > 0);
2224  LOG((3, "nc4_rec_read_vars: grp->name %s", grp->name));
2225 
2226  /* How many objects in this group? */
2227  if (H5Gget_num_objs(grp->hdf_grpid, &num_obj) < 0)
2228  return NC_EVARMETA;
2229 
2230  /* For each object in the group... */
2231  for (i = 0; i < num_obj; i++)
2232  {
2233  if ((retval = nc4_iterate_link(&ordering_checked, &creation_ordering,
2234  grp->hdf_grpid, &idx, &link_info)))
2235  return retval;
2236 
2237  /* Deal with datasets. */
2238  switch(link_info.obj_type)
2239  {
2240  case H5I_GROUP:
2241  LOG((3, "re-encountering group %s", link_info.name));
2242 
2243  /* The NC_GROUP_INFO_T for this group already exists. Find it. */
2244  for (child_grp = grp->children; child_grp; child_grp = child_grp->next)
2245  if (!strcmp(child_grp->name, link_info.name))
2246  break;
2247  if (!child_grp)
2248  return NC_EHDFERR;
2249 
2250  /* Recursively read the child group's vars. */
2251  if ((retval = nc4_rec_read_vars(child_grp)))
2252  return retval;
2253  break;
2254  case H5I_DATASET:
2255  LOG((3, "found dataset %s", link_info.name));
2256 
2257  /* Learn all about this dataset, which may be a dimscale
2258  * (i.e. dimension metadata), or real data. */
2259  if ((retval = read_dataset(grp, link_info.name)))
2260  return retval;
2261  break;
2262  case H5I_DATATYPE:
2263  LOG((3, "already handled type %s", link_info.name));
2264  break;
2265  default:
2266  LOG((0, "Unknown object class %d in nc4_rec_read_vars!",
2267  link_info.obj_type));
2268  }
2269  }
2270 
2271  /* Scan the group for global (i.e. group-level) attributes. */
2272  if ((retval = read_grp_atts(grp)))
2273  return retval;
2274 
2275  return NC_NOERR; /* everything worked! */
2276 }
2277 #endif
2278 
2279 /* Open a netcdf-4 file. Things have already been kicked off in
2280  * ncfunc.c in nc_open, but here the netCDF-4 part of opening a file
2281  * is handled. */
2282 static int
2283 nc4_open_file(const char *path, int mode, MPI_Comm comm,
2284  MPI_Info info, NC *nc)
2285 {
2286  hid_t fapl_id = H5P_DEFAULT;
2287  unsigned flags = (mode & NC_WRITE) ?
2288  H5F_ACC_RDWR : H5F_ACC_RDONLY;
2289  int retval;
2290  NC_HDF5_FILE_INFO_T* nc4_info = NULL;
2291 
2292  LOG((3, "nc4_open_file: path %s mode %d", path, mode));
2293  assert(path && nc);
2294  /* Stop diskless open in its tracks */
2295  if(mode & NC_DISKLESS)
2296  return NC_EDISKLESS;
2297 
2298  /* Add necessary structs to hold netcdf-4 file data. */
2299  if ((retval = nc4_nc4f_list_add(nc, path, mode)))
2300  BAIL(retval);
2301  nc4_info = NC4_DATA(nc);
2302  assert(nc4_info && nc4_info->root_grp);
2303 
2304  /* Need this access plist to control how HDF5 handles open onjects
2305  * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to
2306  * fail if there are any open objects in the file. */
2307  if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
2308  BAIL(NC_EHDFERR);
2309 #ifdef EXTRA_TESTS
2310  num_plists++;
2311 #endif
2312 #ifdef EXTRA_TESTS
2313  if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI))
2314  BAIL(NC_EHDFERR);
2315 #else
2316  if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_STRONG))
2317  BAIL(NC_EHDFERR);
2318 #endif
2319 
2320 #ifdef USE_PARALLEL
2321  /* If this is a parallel file create, set up the file creation
2322  property list. */
2323  if (mode & NC_MPIIO || mode & NC_MPIPOSIX)
2324  {
2325  nc4_info->parallel++;
2326  if (mode & NC_MPIIO) /* MPI/IO */
2327  {
2328  LOG((4, "opening parallel file with MPI/IO"));
2329  if (H5Pset_fapl_mpio(fapl_id, comm, info) < 0)
2330  BAIL(NC_EPARINIT);
2331  }
2332  else /* MPI/POSIX */
2333  {
2334  LOG((4, "opening parallel file with MPI/posix"));
2335  if (H5Pset_fapl_mpiposix(fapl_id, comm, 0) < 0)
2336  BAIL(NC_EPARINIT);
2337  }
2338  }
2339 #else /* only set cache for non-parallel. */
2340  if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size,
2341  nc4_chunk_cache_preemption) < 0)
2342  BAIL(NC_EHDFERR);
2343  LOG((4, "nc4_open_file: set HDF raw chunk cache to size %d nelems %d preemption %f",
2344  nc4_chunk_cache_size, nc4_chunk_cache_nelems, nc4_chunk_cache_preemption));
2345 #endif /* USE_PARALLEL */
2346 
2347  /* The NetCDF-3.x prototype contains an mode option NC_SHARE for
2348  multiple processes accessing the dataset concurrently. As there
2349  is no HDF5 equivalent, NC_SHARE is treated as NC_NOWRITE. */
2350  if ((nc4_info->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
2351  BAIL(NC_EHDFERR);
2352 
2353  /* Does the mode specify that this file is read-only? */
2354  if ((mode & NC_WRITE) == 0)
2355  nc4_info->no_write++;
2356 
2357  /* Now read in all the metadata. Some types and dimscale
2358  * information may be difficult to resolve here, if, for example, a
2359  * dataset of user-defined type is encountered before the
2360  * definition of that type. */
2361  if ((retval = nc4_rec_read_types(nc4_info->root_grp)))
2362  BAIL(retval);
2363  if ((retval = nc4_rec_read_vars(nc4_info->root_grp)))
2364  BAIL(retval);
2365 
2366  /* Now figure out which netCDF dims are indicated by the dimscale
2367  * information. */
2368  if ((retval = nc4_rec_match_dimscales(nc4_info->root_grp)))
2369  BAIL(retval);
2370 
2371 #ifdef LOGGING
2372  /* This will print out the names, types, lens, etc of the vars and
2373  atts in the file, if the logging level is 2 or greater. */
2374  log_metadata_nc(nc);
2375 #endif
2376 
2377  /* Close the property list. */
2378  if (H5Pclose(fapl_id) < 0)
2379  BAIL(NC_EHDFERR);
2380 #ifdef EXTRA_TESTS
2381  num_plists--;
2382 #endif
2383 
2384  return NC_NOERR;
2385 
2386  exit:
2387  if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id);
2388 #ifdef EXTRA_TESTS
2389  num_plists--;
2390 #endif
2391  if (!nc4_info) return retval;
2392  close_netcdf4_file(nc4_info,1); // treat like abort
2393 #if 0
2394  if (nc4_info->hdfid > 0) H5Fclose(nc4_info->hdfid);
2395  if (nc4_info->root_grp) {
2396  free(nc4_info->root_grp->name);
2397  free(nc4_info->root_grp);
2398  }
2399  free(nc4_info);
2400 #endif
2401  return retval;
2402 }
2403 
2404 /* Given an HDF4 type, set a pointer to netcdf type. */
2405 #ifdef USE_HDF4
2406 static int
2407 get_netcdf_type_from_hdf4(NC_HDF5_FILE_INFO_T *h5, int32 hdf4_typeid,
2408  nc_type *xtype, NC_TYPE_INFO_T *type_info)
2409 {
2410  int t;
2411  assert(h5 && xtype);
2412 
2413  switch(hdf4_typeid)
2414  {
2415  case DFNT_CHAR:
2416  *xtype = NC_CHAR;
2417  t = 0;
2418  break;
2419  case DFNT_UCHAR:
2420  case DFNT_UINT8:
2421  *xtype = NC_UBYTE;
2422  t = 6;
2423  break;
2424  case DFNT_INT8:
2425  *xtype = NC_BYTE;
2426  t = 1;
2427  break;
2428  case DFNT_INT16:
2429  *xtype = NC_SHORT;
2430  t = 2;
2431  break;
2432  case DFNT_UINT16:
2433  *xtype = NC_USHORT;
2434  t = 7;
2435  break;
2436  case DFNT_INT32:
2437  *xtype = NC_INT;
2438  t = 3;
2439  break;
2440  case DFNT_UINT32:
2441  *xtype = NC_UINT;
2442  t = 8;
2443  break;
2444  case DFNT_FLOAT32:
2445  *xtype = NC_FLOAT;
2446  t = 4;
2447  break;
2448  case DFNT_FLOAT64:
2449  *xtype = NC_DOUBLE;
2450  t = 5;
2451  break;
2452  default:
2453  *xtype = NC_NAT;
2454  return NC_EBADTYPID;
2455  }
2456 
2457  if (type_info)
2458  {
2459  if (hdf4_typeid == DFNT_FLOAT32 || hdf4_typeid == DFNT_FLOAT64)
2460  type_info->class = H5T_FLOAT;
2461  else if (hdf4_typeid == DFNT_CHAR)
2462  type_info->class = H5T_STRING;
2463  else
2464  type_info->class = H5T_INTEGER;
2465  type_info->endianness = NC_ENDIAN_BIG;
2466  type_info->nc_typeid = *xtype;
2467  if (type_info->name)
2468  free(type_info->name);
2469  if (!(type_info->name = malloc((strlen(nc_type_name[t]) + 1) * sizeof(char))))
2470  return NC_ENOMEM;
2471  strcpy(type_info->name, nc_type_name[t]);
2472  }
2473 
2474  return NC_NOERR;
2475 }
2476 #endif /* USE_HDF4 */
2477 
2478 /* Open a HDF4 file. Things have already been kicked off in nc_open,
2479  * but here the netCDF-4 part of opening a file is handled. */
2480 static int
2481 nc4_open_hdf4_file(const char *path, int mode, NC *nc)
2482 {
2483 #ifdef USE_HDF4
2484  NC_HDF5_FILE_INFO_T *h5;
2485  NC_GRP_INFO_T *grp;
2486  NC_ATT_INFO_T *att;
2487  NC_VAR_INFO_T *var;
2488  int32 num_datasets, num_gatts;
2489  int32 rank;
2490  int v, d, a;
2491  int retval;
2492  NC_HDF5_FILE_INFO_T* nc4_info = NULL;
2493 
2494  LOG((3, "nc4_open_hdf4_file: path %s mode %d", path, mode));
2495  assert(path && nc);
2496 
2497  /* Must be read-only access to hdf4 files. */
2498  if (mode & NC_WRITE)
2499  return NC_EINVAL;
2500 
2501  /* Add necessary structs to hold netcdf-4 file data. */
2502  if ((retval = nc4_nc4f_list_add(nc, path, mode)))
2503  return retval;
2504  nc4_info = NC4_DATA(nc);
2505  assert(nc4_info && nc4_info->root_grp);
2506  h5 = nc4_info;
2507  h5->hdf4++;
2508  grp = h5->root_grp;
2509  h5->no_write++;
2510 
2511  /* Open the file and initialize SD interface. */
2512  if ((h5->sdid = SDstart(path, DFACC_READ)) == FAIL)
2513  return NC_EHDFERR;
2514 
2515  /* Learn how many datasets and global atts we have. */
2516  if (SDfileinfo(h5->sdid, &num_datasets, &num_gatts))
2517  return NC_EHDFERR;
2518 
2519  /* Read the atts. */
2520  for (a = 0; a < num_gatts; a++)
2521  {
2522  int32 att_data_type, att_count;
2523  size_t att_type_size;
2524 
2525  /* Add to the end of the list of atts for this var. */
2526  if ((retval = nc4_att_list_add(&h5->root_grp->att)))
2527  return retval;
2528  for (att = h5->root_grp->att; att->next; att = att->next)
2529  ;
2530  att->attnum = grp->natts++;
2531  att->created++;
2532 
2533  /* Learn about this attribute. */
2534  if (!(att->name = malloc(NC_MAX_HDF4_NAME * sizeof(char))))
2535  return NC_ENOMEM;
2536  if (SDattrinfo(h5->sdid, a, att->name, &att_data_type, &att_count))
2537  return NC_EATTMETA;
2538  if ((retval = get_netcdf_type_from_hdf4(h5, att_data_type,
2539  &att->xtype, NULL)))
2540  return retval;
2541  att->len = att_count;
2542 
2543  /* Allocate memory to hold the data. */
2544  if ((retval = nc4_get_typelen_mem(h5, att->xtype, 0, &att_type_size)))
2545  return retval;
2546  if (!(att->data = malloc(att_type_size * att->len)))
2547  return NC_ENOMEM;
2548 
2549  /* Read the data. */
2550  if (SDreadattr(h5->sdid, a, att->data))
2551  return NC_EHDFERR;
2552  }
2553 
2554  /* Read each dataset. */
2555  for (v = 0; v < num_datasets; v++)
2556  {
2557  int32 data_type, num_atts;
2558  /* Problem: Number of dims is returned by the call that requires
2559  a pre-allocated array, 'dimsize'.
2560  From SDS_SD website:
2561  http://www.hdfgroup.org/training/HDFtraining/UsersGuide/SDS_SD.fm3.html
2562  The maximum rank is 32, or MAX_VAR_DIMS (as defined in netcdf.h).
2563 
2564  int32 dimsize[MAX_VAR_DIMS];
2565  */
2566  int32 *dimsize = NULL;
2567  size_t var_type_size;
2568  int a;
2569 
2570  /* Add a variable to the end of the group's var list. */
2571  if ((retval = nc4_var_list_add(&grp->var, &var))) {
2572  return retval;
2573  }
2574 
2575  var->varid = grp->nvars++;
2576  var->created = 1;
2577  var->written_to = 1;
2578 
2579  /* Open this dataset in HDF4 file. */
2580  if ((var->sdsid = SDselect(h5->sdid, v)) == FAIL) {
2581  return NC_EVARMETA;
2582  }
2583 
2584  /* Get shape, name, type, and attribute info about this dataset. */
2585  if (!(var->name = malloc(NC_MAX_HDF4_NAME + 1))) {
2586  return NC_ENOMEM;
2587  }
2588 
2589  /* Invoke SDgetInfo with null dimsize to get rank. */
2590  if (SDgetinfo(var->sdsid, var->name, &rank, NULL, &data_type, &num_atts))
2591  return NC_EVARMETA;
2592 
2593  if(!(dimsize = (int32*)malloc(sizeof(int32)*rank))) {
2594  return NC_ENOMEM;
2595  }
2596 
2597 
2598  if (SDgetinfo(var->sdsid, var->name, &rank, dimsize, &data_type, &num_atts)) {
2599  if(dimsize) free(dimsize);
2600  return NC_EVARMETA;
2601  }
2602 
2603  var->ndims = rank;
2604  var->hdf4_data_type = data_type;
2605 
2606  /* Fill special type_info struct for variable type information. */
2607  if (!(var->type_info = calloc(1, sizeof(NC_TYPE_INFO_T)))) {
2608  if(dimsize) free(dimsize);
2609  return NC_ENOMEM;
2610  }
2611 
2612  if ((retval = get_netcdf_type_from_hdf4(h5, data_type, &var->xtype, var->type_info))) {
2613  if(dimsize) free(dimsize);
2614  return retval;
2615  }
2616 
2617  if ((retval = nc4_get_typelen_mem(h5, var->xtype, 0, &var_type_size))) {
2618  if(dimsize) free(dimsize);
2619  return retval;
2620  }
2621 
2622  var->type_info->size = var_type_size;
2623  LOG((3, "reading HDF4 dataset %s, rank %d netCDF type %d", var->name,
2624  rank, var->xtype));
2625 
2626  /* Get the fill value. */
2627  if (!(var->fill_value = malloc(var_type_size))) {
2628  if(dimsize) free(dimsize);
2629  return NC_ENOMEM;
2630  }
2631 
2632  if (SDgetfillvalue(var->sdsid, var->fill_value))
2633  {
2634  /* Whoops! No fill value! */
2635  free(var->fill_value);
2636  var->fill_value = NULL;
2637  }
2638 
2639  /* Allocate storage for dimension info in this variable. */
2640  if (var->ndims)
2641  {
2642  if (!(var->dim = malloc(sizeof(NC_DIM_INFO_T *) * var->ndims))) {
2643  if(dimsize) free(dimsize);
2644  return NC_ENOMEM;
2645  }
2646 
2647  if (!(var->dimids = malloc(sizeof(int) * var->ndims))) {
2648  if(dimsize) free(dimsize);
2649  return NC_ENOMEM;
2650  }
2651  }
2652 
2653 
2654  /* Find its dimensions. */
2655  for (d = 0; d < var->ndims; d++)
2656  {
2657  int32 dimid, dim_len, dim_data_type, dim_num_attrs;
2658  char dim_name[NC_MAX_NAME + 1];
2659  NC_DIM_INFO_T *dim;
2660 
2661  if ((dimid = SDgetdimid(var->sdsid, d)) == FAIL) {
2662  if(dimsize) free(dimsize);
2663  return NC_EDIMMETA;
2664  }
2665  if (SDdiminfo(dimid, dim_name, &dim_len, &dim_data_type,
2666  &dim_num_attrs))
2667  {
2668  if(dimsize) free(dimsize);
2669  return NC_EDIMMETA;
2670  }
2671 
2672  /* Do we already have this dimension? HDF4 explicitly uses
2673  * the name to tell. */
2674  for (dim = grp->dim; dim; dim = dim->next)
2675  if (!strcmp(dim->name, dim_name))
2676  break;
2677 
2678  /* If we didn't find this dimension, add one. */
2679  if (!dim)
2680  {
2681  LOG((4, "adding dimension %s for HDF4 dataset %s",
2682  dim_name, var->name));
2683  if ((retval = nc4_dim_list_add(&grp->dim)))
2684  return retval;
2685  grp->ndims++;
2686  dim = grp->dim;
2687  dim->dimid = grp->nc4_info->next_dimid++;
2688  if (strlen(dim_name) > NC_MAX_HDF4_NAME)
2689  return NC_EMAXNAME;
2690  if (!(dim->name = malloc(NC_MAX_HDF4_NAME + 1)))
2691  return NC_ENOMEM;
2692  strcpy(dim->name, dim_name);
2693  if (dim_len)
2694  dim->len = dim_len;
2695  else
2696  dim->len = *dimsize;
2697  }
2698 
2699  /* Tell the variable the id of this dimension. */
2700  var->dimids[d] = dim->dimid;
2701  }
2702 
2703  /* Read the atts. */
2704  for (a = 0; a < num_atts; a++)
2705  {
2706  int32 att_data_type, att_count;
2707  size_t att_type_size;
2708 
2709  /* Add to the end of the list of atts for this var. */
2710  if ((retval = nc4_att_list_add(&var->att))) {
2711  if(dimsize) free(dimsize);
2712  return retval;
2713  }
2714  for (att = var->att; att->next; att = att->next)
2715  ;
2716  att->attnum = var->natts++;
2717  att->created++;
2718 
2719  /* Learn about this attribute. */
2720  if (!(att->name = malloc(NC_MAX_HDF4_NAME * sizeof(char)))) {
2721  if(dimsize) free(dimsize);
2722  return NC_ENOMEM;
2723  }
2724  if (SDattrinfo(var->sdsid, a, att->name, &att_data_type, &att_count)) {
2725  if(dimsize) free(dimsize);
2726  return NC_EATTMETA;
2727  }
2728  if ((retval = get_netcdf_type_from_hdf4(h5, att_data_type,
2729  &att->xtype, NULL))) {
2730  if(dimsize) free(dimsize);
2731  return retval;
2732  }
2733 
2734  att->len = att_count;
2735 
2736  /* Allocate memory to hold the data. */
2737  if ((retval = nc4_get_typelen_mem(h5, att->xtype, 0, &att_type_size))) {
2738  if(dimsize) free(dimsize);
2739  return retval;
2740  }
2741  if (!(att->data = malloc(att_type_size * att->len))) {
2742  if(dimsize) free(dimsize);
2743  return NC_ENOMEM;
2744  }
2745 
2746  /* Read the data. */
2747  if (SDreadattr(var->sdsid, a, att->data)) {
2748  if(dimsize) free(dimsize);
2749  return NC_EHDFERR;
2750  }
2751  }
2752  if(dimsize) free(dimsize);
2753  } /* next var */
2754 
2755 #ifdef LOGGING
2756  /* This will print out the names, types, lens, etc of the vars and
2757  atts in the file, if the logging level is 2 or greater. */
2758  log_metadata_nc(h5->root_grp->nc4_info->controller);
2759 #endif
2760  return NC_NOERR;
2761 #endif /* USE_HDF4 */
2762  return NC_ENOTBUILT;
2763 }
2764 
2765 int
2766 NC4_open(const char *path, int mode, int basepe, size_t *chunksizehintp,
2767  int use_parallel, void *mpidata, NC_Dispatch *dispatch, NC *nc_file)
2768 {
2769  int hdf_file = 0;
2770 #ifdef USE_PARALLEL
2771  MPI_Comm comm = 0;
2772  MPI_Info info = 0;
2773 #else
2774  int comm = 0, info = 0;
2775 #endif /* USE_PARALLEL */
2776  int res;
2777 
2778  assert(nc_file && path);
2779 
2780  LOG((1, "nc_open_file: path %s mode %d comm %d info %d",
2781  path, mode, comm, info));
2782 
2783 #ifdef USE_PARALLEL
2784  if (mpidata)
2785  {
2786  NC_MPI_INFO *nmi = (NC_MPI_INFO *)mpidata;
2787  comm = nmi->comm; info = nmi->info;
2788  }
2789 #endif /* USE_PARALLEL */
2790 
2791  /* If this is our first file, turn off HDF5 error messages. */
2792  if (virgin)
2793  {
2794  if (H5Eset_auto(NULL, NULL) < 0)
2795  LOG((0, "Couldn't turn off HDF5 error messages!"));
2796  LOG((1, "HDF5 error messages turned off!"));
2797  virgin = 0;
2798  }
2799 
2800  /* Check the mode for validity. First make sure only certain bits
2801  * are turned on. Also MPI I/O and MPI POSIX cannot both be
2802  * selected at once. */
2803  if (mode & ~(NC_WRITE | NC_SHARE | NC_MPIIO | NC_MPIPOSIX |
2804  NC_PNETCDF | NC_NOCLOBBER | NC_NETCDF4 | NC_CLASSIC_MODEL) ||
2805  (mode & NC_MPIIO && mode & NC_MPIPOSIX))
2806  return NC_EINVAL;
2807 
2808 
2809  /* Depending on the type of file, open it. */
2810 
2811 #ifdef USE_PNETCDF
2812  if(mode & NC_PNETCDF) {
2813  /* this is not really an hdf file */
2814  int pnetcdf_nvars, i;
2815  NC_HDF5_FILE_INFO_T* nc4_info;
2816 
2817  /* Create the fake nc4_info data */
2818  res = nc4_nc4f_list_add(nc_file, path, mode);
2819 
2820  nc4_info = NC4_DATA(nc_file);
2821  assert(nc4_info);
2822 
2823  res = ncmpi_open(comm, path, mode, info, &(nc_file->int_ncid));
2824  nc4_info->pnetcdf_file++;
2825 
2826  /* Default to independent access, like netCDF-4/HDF5 files. */
2827  if (!res)
2828  res = ncmpi_begin_indep_data(nc_file->int_ncid);
2829 
2830  /* I need to keep track of the ndims of each var to translate
2831  * start, count, and stride arrays to MPI_Offset type. */
2832  if (!res)
2833  {
2834  res = ncmpi_inq_nvars(nc_file->int_ncid, &pnetcdf_nvars);
2835  for (i = 0; i < pnetcdf_nvars; i++)
2836  res = ncmpi_inq_varndims(nc_file->int_ncid, i,
2837  &(nc4_info->pnetcdf_ndims[i]));
2838 
2839  }
2840  } else
2841 #endif
2842  {
2843  /* Figure out if this is a hdf4 or hdf5 file. */
2844  if ((res = nc_check_for_hdf(path, use_parallel, comm, info, &hdf_file)))
2845  return res;
2846 
2847  if (hdf_file == NC_HDF5_FILE)
2848  {
2849  nc_file->int_ncid = nc_file->ext_ncid;
2850  res = nc4_open_file(path, mode, comm, info, nc_file);
2851  }
2852  else if (hdf_file == NC_HDF4_FILE)
2853  {
2854  nc_file->int_ncid = nc_file->ext_ncid;
2855  res = nc4_open_hdf4_file(path, mode, nc_file);
2856  } else /* netcdf */
2857  {
2858  assert(0); /* should never happen */
2859  }
2860  }
2861 
2862  return res;
2863 }
2864 
2865 /* Unfortunately HDF only allows specification of fill value only when
2866  a dataset is created. Whereas in netcdf, you first create the
2867  variable and then (optionally) specify the fill value. To
2868  accomplish this in HDF5 I have to delete the dataset, and recreate
2869  it, with the fill value specified. */
2870 int
2871 NC4_set_fill(int ncid, int fillmode, int *old_modep)
2872 {
2873  NC *nc;
2874  NC_HDF5_FILE_INFO_T* nc4_info;
2875 
2876  LOG((2, "nc_set_fill: ncid 0x%x fillmode %d", ncid, fillmode));
2877 
2878  if (!(nc = nc4_find_nc_file(ncid,&nc4_info)))
2879  return NC_EBADID;
2880  assert(nc4_info);
2881 
2882  /* Trying to set fill on a read-only file? You sicken me! */
2883  if (nc4_info->no_write)
2884  return NC_EPERM;
2885 
2886  /* Did you pass me some weird fillmode? */
2887  if (fillmode != NC_FILL && fillmode != NC_NOFILL)
2888  return NC_EINVAL;
2889 
2890  /* If the user wants to know, tell him what the old mode was. */
2891  if (old_modep)
2892  *old_modep = nc4_info->fill_mode;
2893 
2894  nc4_info->fill_mode = fillmode;
2895 
2896 #ifdef USE_PNETCDF
2897  /* Take care of files created/opened with parallel-netcdf library. */
2898  if (nc4_info->pnetcdf_file)
2899  return ncmpi_set_fill(nc->int_ncid, fillmode, old_modep);
2900 #endif /* USE_PNETCDF */
2901 
2902 
2903  return NC_NOERR;
2904 }
2905 
2906 /* Put the file back in redef mode. This is done automatically for
2907  * netcdf-4 files, if the user forgets. */
2908 int
2909 NC4_redef(int ncid)
2910 {
2911  NC *nc;
2912  NC_HDF5_FILE_INFO_T* nc4_info;
2913 
2914  LOG((1, "nc_redef: ncid 0x%x", ncid));
2915 
2916  /* Find this file's metadata. */
2917  if (!(nc = nc4_find_nc_file(ncid,&nc4_info)))
2918  return NC_EBADID;
2919  assert(nc4_info);
2920 
2921 #ifdef USE_PNETCDF
2922  /* Take care of files created/opened with parallel-netcdf library. */
2923  if (nc4_info->pnetcdf_file)
2924  return ncmpi_redef(nc->int_ncid);
2925 #endif /* USE_PNETCDF */
2926 
2927  /* If we're already in define mode, return an error. */
2928  if (nc4_info->flags & NC_INDEF)
2929  return NC_EINDEFINE;
2930 
2931  /* If the file is read-only, return an error. */
2932  if (nc4_info->no_write)
2933  return NC_EPERM;
2934 
2935  /* Set define mode. */
2936  nc4_info->flags |= NC_INDEF;
2937 
2938  /* For nc_abort, we need to remember if we're in define mode as a
2939  redef. */
2940  nc4_info->redef++;
2941 
2942  return NC_NOERR;
2943 }
2944 
2945 /* For netcdf-4 files, this just calls nc_enddef, ignoring the extra
2946  * parameters. */
2947 int
2948 NC4__enddef(int ncid, size_t h_minfree, size_t v_align,
2949  size_t v_minfree, size_t r_align)
2950 {
2951  if (nc4_find_nc_file(ncid,NULL) == NULL)
2952  return NC_EBADID;
2953 
2954  return NC4_enddef(ncid);
2955 }
2956 
2957 /* Take the file out of define mode. This is called automatically for
2958  * netcdf-4 files, if the user forgets. */
2959 static int NC4_enddef(int ncid)
2960 {
2961  NC *nc;
2962  NC_HDF5_FILE_INFO_T* nc4_info;
2963 
2964  LOG((1, "nc_enddef: ncid 0x%x", ncid));
2965 
2966  if (!(nc = nc4_find_nc_file(ncid,&nc4_info)))
2967  return NC_EBADID;
2968  assert(nc4_info);
2969 
2970 #ifdef USE_PNETCDF
2971  if (nc4_info->pnetcdf_file)
2972  {
2973  int res;
2974  res = ncmpi_enddef(nc->int_ncid);
2975  if (!res)
2976  {
2977  if (nc4_info->pnetcdf_access_mode == NC_INDEPENDENT)
2978  res = ncmpi_begin_indep_data(nc->int_ncid);
2979  }
2980  return res;
2981  }
2982 #endif /* USE_PNETCDF */
2983 
2984  return nc4_enddef_netcdf4_file(nc4_info);
2985 }
2986 
2987 /* This function will write all changed metadata, and (someday) reread
2988  * all metadata from the file. */
2989 static int
2990 sync_netcdf4_file(NC_HDF5_FILE_INFO_T *h5)
2991 {
2992  int retval;
2993 
2994  assert(h5);
2995  LOG((3, "sync_netcdf4_file"));
2996 
2997  /* If we're in define mode, that's an error, for strict nc3 rules,
2998  * otherwise, end define mode. */
2999  if (h5->flags & NC_INDEF)
3000  {
3001  if (h5->cmode & NC_CLASSIC_MODEL)
3002  return NC_EINDEFINE;
3003 
3004  /* Turn define mode off. */
3005  h5->flags ^= NC_INDEF;
3006 
3007  /* Redef mode needs to be tracked seperately for nc_abort. */
3008  h5->redef = 0;
3009  }
3010 
3011 #ifdef LOGGING
3012  /* This will print out the names, types, lens, etc of the vars and
3013  atts in the file, if the logging level is 2 or greater. */
3014  log_metadata_nc(h5->root_grp->nc4_info->controller);
3015 #endif
3016 
3017  /* Write any metadata that has changed. */
3018  if (!(h5->cmode & NC_NOWRITE))
3019  {
3020  int bad_coord_order = 0; /* if detected, propagate to all groups to consistently store dimids */
3021  if ((retval = nc4_rec_write_types(h5->root_grp)))
3022  return retval;
3023  if ((retval = nc4_rec_detect_need_to_preserve_dimids(h5->root_grp, &bad_coord_order)))
3024  return retval;
3025  if ((retval = nc4_rec_write_metadata(h5->root_grp, bad_coord_order)))
3026  return retval;
3027  }
3028 
3029  H5Fflush(h5->hdfid, H5F_SCOPE_GLOBAL);
3030 
3031  /* Reread all the metadata. */
3032  /*if ((retval = nc4_rec_read_metadata(grp)))
3033  return retval;*/
3034 
3035  return retval;
3036 }
3037 
3038 /* Flushes all buffers associated with the file, after writing all
3039  changed metadata. This may only be called in data mode. */
3040 int
3041 NC4_sync(int ncid)
3042 {
3043  NC *nc;
3044  int retval;
3045  NC_HDF5_FILE_INFO_T* nc4_info;
3046 
3047  LOG((2, "nc_sync: ncid 0x%x", ncid));
3048 
3049  if (!(nc = nc4_find_nc_file(ncid,&nc4_info)))
3050  return NC_EBADID;
3051  assert(nc4_info);
3052 
3053 #ifdef USE_PNETCDF
3054  /* Take care of files created/opened with parallel-netcdf library. */
3055  if (nc4_info->pnetcdf_file)
3056  return ncmpi_sync(nc->int_ncid);
3057 #endif /* USE_PNETCDF */
3058 
3059  /* If we're in define mode, we can't sync. */
3060  if (nc4_info && nc4_info->flags & NC_INDEF)
3061  {
3062  if (nc4_info->cmode & NC_CLASSIC_MODEL)
3063  return NC_EINDEFINE;
3064  if ((retval = NC4_enddef(ncid)))
3065  return retval;
3066  }
3067 
3068  return sync_netcdf4_file(nc4_info);
3069 }
3070 
3071 /* This function will free all allocated metadata memory, and close
3072  the HDF5 file. The group that is passed in must be the root group
3073  of the file. */
3074 static int
3075 close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort)
3076 {
3077  int retval = NC_NOERR;
3078 
3079  assert(h5 && h5->root_grp);
3080  LOG((3, "close_netcdf4_file: h5->path %s abort %d",
3081  h5->controller->path, abort));
3082 
3083  /* According to the docs, always end define mode on close. */
3084  if (h5->flags & NC_INDEF)
3085  h5->flags ^= NC_INDEF;
3086 
3087  /* Sync the file, unless we're aborting, or this is a read-only
3088  * file. */
3089  if (!h5->no_write && !abort)
3090  if ((retval = sync_netcdf4_file(h5)))
3091  goto done;
3092 
3093  /* Delete all the list contents for vars, dims, and atts, in each
3094  * group. */
3095  if ((retval = nc4_rec_grp_del(&h5->root_grp, h5->root_grp)))
3096  goto done;
3097 
3098  /* Close hdf file. */
3099  if (h5->hdf4)
3100  {
3101 #ifdef USE_HDF4
3102  if (SDend(h5->sdid))
3103  {retval = NC_EHDFERR; goto done;}
3104 #endif /* USE_HDF4 */
3105  }
3106  else
3107  {
3108  if (H5Fclose(h5->hdfid) < 0)
3109  {
3110  int nobjs;
3111  nobjs = H5Fget_obj_count(h5->hdfid, H5F_OBJ_ALL);
3112  /* Apparently we can get an error even when nobjs == 0 */
3113  if(nobjs < 0) {
3114  {retval = NC_EHDFERR; goto done;}
3115  } else if(nobjs > 0) {
3116 #ifdef LOGGING
3117  /* If the close doesn't work, probably there are still some HDF5
3118  * objects open, which means there's a bug in the library. So
3119  * print out some info on to help the poor programmer figure it
3120  * out. */
3121  LOG((0, "There are %d HDF5 objects open!", nobjs));
3122 #endif
3123  retval = NC_EHDFERR; goto done;
3124  }
3125  }
3126 #if 0
3127  if (H5garbage_collect() < 0)
3128  {retval = NC_EHDFERR; goto done;
3129 #endif
3130  }
3131 
3132 done:
3133  /* Free the nc4_info struct; above code should have reclaimed
3134  everything else */
3135  if(h5 != NULL)
3136  free(h5);
3137 
3138  return retval;
3139 }
3140 
3141 /* From the netcdf-3 docs: The function nc_abort just closes the
3142  netCDF dataset, if not in define mode. If the dataset is being
3143  created and is still in define mode, the dataset is deleted. If
3144  define mode was entered by a call to nc_redef, the netCDF dataset
3145  is restored to its state before definition mode was entered and the
3146  dataset is closed. */
3147 int
3148 NC4_abort(int ncid)
3149 {
3150  NC *nc;
3151  int delete_file = 0;
3152  char path[NC_MAX_NAME + 1];
3153  int retval = NC_NOERR;
3154  NC_HDF5_FILE_INFO_T* nc4_info;
3155 
3156  LOG((2, "nc_abort: ncid 0x%x", ncid));
3157 
3158  /* Find metadata for this file. */
3159  if (!(nc = nc4_find_nc_file(ncid,&nc4_info)))
3160  return NC_EBADID;
3161 
3162  assert(nc4_info);
3163 
3164 #ifdef USE_PNETCDF
3165  /* Take care of files created/opened with parallel-netcdf library. */
3166  if (nc4_info->pnetcdf_file)
3167  return ncmpi_abort(nc->int_ncid);
3168 #endif /* USE_PNETCDF */
3169 
3170  /* If we're in define mode, but not redefing the file, delete it. */
3171  if (nc4_info->flags & NC_INDEF && !nc4_info->redef)
3172  {
3173  delete_file++;
3174  strncpy(path, nc->path,NC_MAX_NAME);
3175  }
3176 
3177  /* Free any resources the netcdf-4 library has for this file's
3178  * metadata. */
3179  if ((retval = close_netcdf4_file(nc4_info, 1)))
3180  return retval;
3181 
3182  /* Delete the file, if we should. */
3183  if (delete_file)
3184  remove(path);
3185 
3186  return retval;
3187 }
3188 
3189 /* Close the netcdf file, writing any changes first. */
3190 int
3191 NC4_close(int ncid)
3192 {
3193  NC_GRP_INFO_T *grp;
3194  NC *nc;
3195  NC_HDF5_FILE_INFO_T *h5;
3196  int retval;
3197 
3198  LOG((1, "nc_close: ncid 0x%x", ncid));
3199 
3200  /* Find our metadata for this file. */
3201  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
3202  return retval;
3203 
3204  assert(nc && h5 && grp);
3205 
3206  /* This must be the root group. */
3207  if (grp->parent)
3208  return NC_EBADGRPID;
3209 
3210 #ifdef USE_PNETCDF
3211  /* Take care of files created/opened with parallel-netcdf library. */
3212  if (h5->pnetcdf_file)
3213  return ncmpi_close(nc->int_ncid);
3214 #endif /* USE_PNETCDF */
3215 
3216  /* Call the nc4 close. */
3217  if ((retval = close_netcdf4_file(grp->nc4_info, 0)))
3218  return retval;
3219 
3220  return NC_NOERR;
3221 }
3222 
3223 /* It's possible for any of these pointers to be NULL, in which case
3224  don't try to figure out that value. */
3225 int
3226 NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp)
3227 {
3228  NC *nc;
3229  NC_HDF5_FILE_INFO_T *h5;
3230  NC_GRP_INFO_T *grp;
3231  NC_DIM_INFO_T *dim;
3232  NC_ATT_INFO_T *att;
3233  NC_VAR_INFO_T *var;
3234  int retval;
3235 
3236  LOG((2, "nc_inq: ncid 0x%x", ncid));
3237 
3238  /* Find file metadata. */
3239  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
3240  return retval;
3241 
3242  assert(h5 && grp && nc);
3243 
3244 #ifdef USE_PNETCDF
3245  /* Take care of files created/opened with parallel-netcdf library. */
3246  if (h5->pnetcdf_file)
3247  return ncmpi_inq(nc->int_ncid, ndimsp, nvarsp, nattsp, unlimdimidp);
3248 #endif /* USE_PNETCDF */
3249 
3250  /* Count the number of dims, vars, and global atts. */
3251  if (ndimsp)
3252  {
3253  *ndimsp = 0;
3254  for (dim = grp->dim; dim; dim = dim->next)
3255  (*ndimsp)++;
3256  }
3257  if (nvarsp)
3258  {
3259  *nvarsp = 0;
3260  for (var = grp->var; var; var= var->next)
3261  (*nvarsp)++;
3262  }
3263  if (nattsp)
3264  {
3265  *nattsp = 0;
3266  for (att = grp->att; att; att = att->next)
3267  (*nattsp)++;
3268  }
3269 
3270  if (unlimdimidp)
3271  {
3272  /* Default, no unlimited dimension */
3273  *unlimdimidp = -1;
3274 
3275  /* If there's more than one unlimited dim, which was not possible
3276  with netcdf-3, then only the last unlimited one will be reported
3277  back in xtendimp. */
3278  /* Note that this code is inconsistent with nc_inq_unlimid() */
3279  for (dim = grp->dim; dim; dim = dim->next)
3280  if (dim->unlimited)
3281  {
3282  *unlimdimidp = dim->dimid;
3283  break;
3284  }
3285  }
3286 
3287  return NC_NOERR;
3288 }
3289 
3290 
3291 /* This function will do the enddef stuff for a netcdf-4 file. */
3292 int
3293 nc4_enddef_netcdf4_file(NC_HDF5_FILE_INFO_T *h5)
3294 {
3295  assert(h5);
3296  LOG((3, "nc4_enddef_netcdf4_file"));
3297 
3298  /* If we're not in define mode, return an error. */
3299  if (!(h5->flags & NC_INDEF))
3300  return NC_ENOTINDEFINE;
3301 
3302  /* Turn define mode off. */
3303  h5->flags ^= NC_INDEF;
3304 
3305  /* Redef mode needs to be tracked seperately for nc_abort. */
3306  h5->redef = 0;
3307 
3308  return sync_netcdf4_file(h5);
3309 }
3310 
3311 #ifdef EXTRA_TESTS
3312 int
3313 nc_exit()
3314 {
3315  if (num_plists || num_spaces)
3316  return NC_EHDFERR;
3317 
3318  return NC_NOERR;
3319 }
3320 #endif /* EXTRA_TESTS */
3321 
3322 #ifdef USE_PARALLEL
3323 int
3324 nc_use_parallel_enabled()
3325 {
3326  return 0;
3327 }
3328 #endif /* USE_PARALLEL */
3329 
3330 

Generated on Tue Jul 9 2013 19:17:27 for netCDF. NetCDF is a Unidata library.