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

Generated on Wed Aug 22 2012 14:39:39 for netCDF. NetCDF is a Unidata library.