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

Return to the Main Unidata NetCDF page.
Generated on Mon Jul 13 2015 07:53:10 for NetCDF. NetCDF is a Unidata library.