source: XIOS/trunk/extern/src_netcdf4/nc4file.c @ 409

Last change on this file since 409 was 409, checked in by ymipsl, 11 years ago

Add improved nectdf internal library src

YM

  • Property svn:eol-style set to native
File size: 91.7 KB
Line 
1/** \file
2The netCDF-4 file functions.
3
4This file is part of netcdf-4, a netCDF-like interface for HDF5, or
5a HDF5 backend for netCDF, depending on your point of view.
6
7Copyright 2003, University Corporation for Atmospheric Research. See
8COPYRIGHT file for copying and redistribution conditions. 
9*/
10
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
28extern int num_plists;
29extern 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 */
43static int NC4_enddef(int ncid);
44static int nc4_rec_read_types(NC_GRP_INFO_T *grp);
45static 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. */
50extern 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. */
55size_t nc4_chunk_cache_size = CHUNK_CACHE_SIZE;
56size_t nc4_chunk_cache_nelems = CHUNK_CACHE_NELEMS;
57float nc4_chunk_cache_preemption = CHUNK_CACHE_PREEMPTION;
58
59/* This is set by nc_set_default_format in libsrc/nc.c. */
60extern int default_create_format;
61
62/* To turn off HDF5 error messages, I have to catch an early
63   invocation of a netcdf function. */
64static 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
69static hid_t native_type_constant[NUM_TYPES];
70
71static char nc_type_name[NUM_TYPES][NC_MAX_NAME + 1] = {"char", "byte", "short", "int", "float",
72                                                        "double", "ubyte", "ushort", "uint", 
73                                                        "int64", "uint64", "string"};
74int nc4_free_global_hdf_string_typeid();
75
76/* Set chunk cache size. Only affects files opened/created *after* it
77 * is called.  */
78int
79nc_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.  */
91int
92nc_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. */
106int
107nc_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
117int
118nc_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. */
131int
132nc4typelen(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
158static int
159nc_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. */
207static int
208nc4_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
338/** \ingroup netcdf4
339Create a netCDF-4/HDF5 file.
340
341\param path The file name of the new file.
342\param cmode The creation mode flag.
343\param initialsz Ignored by this function.
344\param basepe Ignored by this function.
345\param chunksizehintp Ignored by this function.
346\param use_parallel 0 for sequential, non-zero for parallel I/O.
347\param mpidata pointer to struct holdind data for parallel I/O
348layer. Ignored if NULL.
349\param dispatch Pointer to the dispatch table for this file.
350\param ncpp Pointer to start of linked list of open files.
351\return NC_INVAL Invalid input (check cmode).
352*/
353int
354NC4_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
391                 | NC_NETCDF4 | NC_CLASSIC_MODEL
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. */
456static int
457read_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. */
530static int
531read_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.*/
567static herr_t 
568dimscale_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. */
588static int
589get_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. */
704static int
705get_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,
717                                          NC_DOUBLE, NC_UBYTE, NC_USHORT, NC_UINT,
718                                          NC_INT64, NC_UINT64, NC_STRING};
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. */
849static int 
850read_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. */
1031static int
1032read_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. */
1275static int
1276read_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). */
1569static int
1570read_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. */
1630static int
1631read_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
1775static int
1776nc4_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
1819static int
1820nc4_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
1874static int
1875nc4_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
1934static int
1935nc4_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
1978/** \internal
1979This struct is used to pass information back from the callback
1980function used with H5Literate.
1981*/
1982struct 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
1990The parameters of this callback function have the following values or
1991meanings:
1992
1993g_id Group that serves as root of the iteration; same value as the
1994H5Lvisit group_id parameter
1995
1996name Name of link, relative to g_id, being examined at current step of
1997the iteration
1998
1999info H5L_info_t struct containing information regarding that link
2000
2001op_data User-defined pointer to data required by the application in
2002processing the link; a pass-through of the op_data pointer provided
2003with the H5Lvisit function call
2004
2005*/
2006static herr_t
2007visit_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. */
2040static int
2041nc4_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. */
2073int
2074nc4_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. */
2146int
2147nc4_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. */
2216static int
2217nc4_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   
2331static int
2332get_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. */
2405static int
2406nc4_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
2624int
2625NC4_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. */
2736int 
2737NC4_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. */
2768int
2769NC4_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. */
2808int
2809NC4__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. */
2820static 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. */
2851static int
2852sync_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. */
2899int
2900NC4_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. */
2934static int
2935close_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. */
3005int
3006NC4_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. */
3052int
3053NC4_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. */
3096int
3097NC4_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. */
3165int
3166nc4_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
3185int
3186nc_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
3196int
3197nc_use_parallel_enabled()
3198{
3199   return 0;
3200}
3201#endif /* USE_PARALLEL */
3202
3203
Note: See TracBrowser for help on using the repository browser.