netCDF  4.2.1.1
/usr/src/RPM/BUILD/libnetcdf7-seq-4.2.1.1/libsrc4/nc4file.c
Go to the documentation of this file.
00001 
00011 #include "nc4internal.h"
00012 #include "nc.h"
00013 #include <H5DSpublic.h>
00014 #include "nc4dispatch.h"
00015 #include "ncdispatch.h"
00016 
00017 #ifdef USE_HDF4
00018 #include <mfhdf.h>
00019 #endif
00020 
00021 #ifdef USE_PNETCDF
00022 #include <pnetcdf.h>
00023 #endif
00024 
00025 /* This is to track opened HDF5 objects to make sure they are
00026  * closed. */
00027 #ifdef EXTRA_TESTS
00028 extern int num_plists;
00029 extern int num_spaces;
00030 #endif /* EXTRA_TESTS */
00031 
00032 #define MIN_DEFLATE_LEVEL 0
00033 #define MAX_DEFLATE_LEVEL 9
00034 
00035 /* These are the special attributes added by the HDF5 dimension scale
00036  * API. They will be ignored by netCDF-4. */
00037 #define REFERENCE_LIST "REFERENCE_LIST"
00038 #define CLASS "CLASS"
00039 #define DIMENSION_LIST "DIMENSION_LIST"
00040 #define NAME "NAME"
00041 
00042 /* Forward */
00043 static int NC4_enddef(int ncid);
00044 static int nc4_rec_read_types(NC_GRP_INFO_T *grp);
00045 static int nc4_rec_read_vars(NC_GRP_INFO_T *grp);
00046 
00047 #ifdef IGNORE
00048 /* This extern points to the pointer that holds the list of open
00049  * netCDF files. */
00050 extern NC_FILE_INFO_T *nc_file;
00051 #endif
00052 
00053 /* These are the default chunk cache sizes for HDF5 files created or
00054  * opened with netCDF-4. */
00055 size_t nc4_chunk_cache_size = CHUNK_CACHE_SIZE;
00056 size_t nc4_chunk_cache_nelems = CHUNK_CACHE_NELEMS;
00057 float nc4_chunk_cache_preemption = CHUNK_CACHE_PREEMPTION;
00058 
00059 /* This is set by nc_set_default_format in libsrc/nc.c. */
00060 extern int default_create_format;
00061 
00062 /* To turn off HDF5 error messages, I have to catch an early
00063    invocation of a netcdf function. */
00064 static int virgin = 1;
00065 
00066 /* For performance, fill this array only the first time, and keep it
00067  * in global memory for each further use. */
00068 #define NUM_TYPES 12
00069 static hid_t native_type_constant[NUM_TYPES];
00070 
00071 static char nc_type_name[NUM_TYPES][NC_MAX_NAME + 1] = {"char", "byte", "short", "int", "float",
00072                                                         "double", "ubyte", "ushort", "uint", 
00073                                                         "int64", "uint64", "string"};
00074 int nc4_free_global_hdf_string_typeid();
00075 
00076 /* Set chunk cache size. Only affects files opened/created *after* it
00077  * is called.  */
00078 int
00079 nc_set_chunk_cache(size_t size, size_t nelems, float preemption)
00080 {
00081    if (preemption < 0 || preemption > 1)
00082       return NC_EINVAL;
00083    nc4_chunk_cache_size = size;
00084    nc4_chunk_cache_nelems = nelems;
00085    nc4_chunk_cache_preemption = preemption;
00086    return NC_NOERR;
00087 }
00088 
00089 /* Get chunk cache size. Only affects files opened/created *after* it
00090  * is called.  */
00091 int
00092 nc_get_chunk_cache(size_t *sizep, size_t *nelemsp, float *preemptionp)
00093 {
00094    if (sizep)
00095       *sizep = nc4_chunk_cache_size;
00096 
00097    if (nelemsp)
00098       *nelemsp = nc4_chunk_cache_nelems;
00099    
00100    if (preemptionp)
00101       *preemptionp = nc4_chunk_cache_preemption;
00102    return NC_NOERR;
00103 }
00104 
00105 /* Required for fortran to avoid size_t issues. */
00106 int
00107 nc_set_chunk_cache_ints(int size, int nelems, int preemption)
00108 {
00109    if (size <= 0 || nelems <= 0 || preemption < 0 || preemption > 100)
00110       return NC_EINVAL;
00111    nc4_chunk_cache_size = size;
00112    nc4_chunk_cache_nelems = nelems;
00113    nc4_chunk_cache_preemption = (float)preemption / 100;
00114    return NC_NOERR;
00115 }
00116 
00117 int
00118 nc_get_chunk_cache_ints(int *sizep, int *nelemsp, int *preemptionp)
00119 {
00120    if (sizep)
00121       *sizep = (int)nc4_chunk_cache_size;
00122    if (nelemsp)
00123       *nelemsp = (int)nc4_chunk_cache_nelems;
00124    if (preemptionp)
00125       *preemptionp = (int)(nc4_chunk_cache_preemption * 100);
00126 
00127    return NC_NOERR;
00128 }
00129 
00130 /* This will return the length of a netcdf data type in bytes. */
00131 int
00132 nc4typelen(nc_type type)
00133 {
00134    switch(type){
00135       case NC_BYTE:
00136       case NC_CHAR:
00137       case NC_UBYTE:
00138          return 1;
00139       case NC_USHORT:
00140       case NC_SHORT:
00141          return 2;
00142       case NC_FLOAT:
00143       case NC_INT:
00144       case NC_UINT:
00145          return 4;
00146       case NC_DOUBLE: 
00147       case NC_INT64:
00148       case NC_UINT64:
00149          return 8;
00150    }
00151    return -1;
00152 }
00153 
00154 /* Given a filename, check to see if it is a HDF5 file. */
00155 #define MAGIC_NUMBER_LEN 4
00156 #define NC_HDF5_FILE 1
00157 #define NC_HDF4_FILE 2
00158 static int
00159 nc_check_for_hdf(const char *path, int use_parallel, MPI_Comm comm, MPI_Info info, 
00160                  int *hdf_file)
00161 {
00162    char blob[MAGIC_NUMBER_LEN];
00163    
00164    assert(hdf_file && path);
00165    LOG((3, "nc_check_for_hdf: path %s", path));
00166 
00167 /* Get the 4-byte blob from the beginning of the file. Don't use posix
00168  * for parallel, use the MPI functions instead. */
00169 #ifdef USE_PARALLEL
00170    if (use_parallel)
00171    {
00172       MPI_File fh;
00173       MPI_Status status;
00174       int retval;
00175       if ((retval = MPI_File_open(comm, (char *)path, MPI_MODE_RDONLY,
00176                                   info, &fh)) != MPI_SUCCESS)
00177          return NC_EPARINIT;
00178       if ((retval = MPI_File_read(fh, blob, MAGIC_NUMBER_LEN, MPI_CHAR,
00179                                   &status)) != MPI_SUCCESS)
00180          return NC_EPARINIT;
00181       if ((retval = MPI_File_close(&fh)) != MPI_SUCCESS)
00182          return NC_EPARINIT;
00183    }
00184    else
00185 #endif /* USE_PARALLEL */
00186    {
00187       FILE *fp;
00188       if (!(fp = fopen(path, "r")) ||
00189           fread(blob, MAGIC_NUMBER_LEN, 1, fp) != 1)
00190          return errno;
00191       fclose(fp);
00192    }
00193 
00194    /* Ignore the first byte for HDF5. */
00195    if (blob[1] == 'H' && blob[2] == 'D' && blob[3] == 'F')
00196       *hdf_file = NC_HDF5_FILE;
00197    else if (!strncmp(blob, "\016\003\023\001", MAGIC_NUMBER_LEN))
00198       *hdf_file = NC_HDF4_FILE;
00199    else
00200       *hdf_file = 0;
00201 
00202    return NC_NOERR;
00203 }
00204    
00205 /* Create a HDF5/netcdf-4 file. In this case, ncid has already been
00206  * selected in ncfunc.c. */
00207 static int
00208 nc4_create_file(const char *path, int cmode, MPI_Comm comm, MPI_Info info,
00209                 NC_FILE_INFO_T *nc) 
00210 {
00211    hid_t fcpl_id, fapl_id;
00212    unsigned flags;
00213    FILE *fp;
00214    int retval = NC_NOERR;
00215    int persist = 0; /* Should diskless try to persist its data into file?*/
00216 
00217    if(cmode & NC_DISKLESS)
00218        flags = H5F_ACC_TRUNC;
00219    else if(cmode & NC_NOCLOBBER)
00220        flags = H5F_ACC_EXCL;
00221    else
00222        flags = H5F_ACC_TRUNC;
00223 
00224    LOG((3, "nc4_create_file: path %s mode 0x%x", path, cmode));
00225    assert(nc && path);
00226 
00227 
00228    /* If this file already exists, and NC_NOCLOBBER is specified,
00229       return an error. */
00230    if (cmode & NC_DISKLESS) {
00231         if(cmode & NC_WRITE)
00232             persist = 1;
00233    } else if ((cmode & NC_NOCLOBBER) && (fp = fopen(path, "r"))) {
00234       fclose(fp);
00235       return NC_EEXIST;
00236    }
00237    
00238    /* Add necessary structs to hold netcdf-4 file data. */
00239    if ((retval = nc4_nc4f_list_add(nc, path, (NC_WRITE | cmode))))
00240       BAIL(retval);
00241    assert(nc->nc4_info && nc->nc4_info->root_grp);
00242 
00243    /* Need this access plist to control how HDF5 handles open onjects
00244     * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to
00245     * fail if there are any open objects in the file. */
00246    if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
00247       BAIL(NC_EHDFERR);
00248 #ifdef EXTRA_TESTS
00249    num_plists++;
00250 #endif
00251 #ifdef EXTRA_TESTS
00252    if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI))
00253       BAIL(NC_EHDFERR);
00254 #else
00255    if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_STRONG))
00256       BAIL(NC_EHDFERR);
00257 #endif /* EXTRA_TESTS */
00258 
00259 #ifdef USE_PARALLEL
00260    /* If this is a parallel file create, set up the file creation
00261       property list. */
00262    if ((cmode & NC_MPIIO) || (cmode & NC_MPIPOSIX))
00263    {
00264       nc->nc4_info->parallel++;
00265       if (cmode & NC_MPIIO)  /* MPI/IO */
00266       {
00267          LOG((4, "creating parallel file with MPI/IO"));
00268          if (H5Pset_fapl_mpio(fapl_id, comm, info) < 0)
00269             BAIL(NC_EPARINIT);
00270       }
00271       else /* MPI/POSIX */
00272       {
00273          LOG((4, "creating parallel file with MPI/posix"));
00274          if (H5Pset_fapl_mpiposix(fapl_id, comm, 0) < 0)
00275             BAIL(NC_EPARINIT);
00276       }
00277    }
00278 #else /* only set cache for non-parallel... */
00279    if(cmode & NC_DISKLESS) {
00280          if (H5Pset_fapl_core(fapl_id, 4096, persist))
00281             BAIL(NC_EDISKLESS);
00282    }
00283    if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size, 
00284                     nc4_chunk_cache_preemption) < 0)
00285       BAIL(NC_EHDFERR);
00286    LOG((4, "nc4_create_file: set HDF raw chunk cache to size %d nelems %d preemption %f", 
00287         nc4_chunk_cache_size, nc4_chunk_cache_nelems, nc4_chunk_cache_preemption));
00288 #endif /* USE_PARALLEL */
00289    
00290    if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_18, H5F_LIBVER_18) < 0)
00291       BAIL(NC_EHDFERR);
00292 
00293    /* Create the property list. */
00294    if ((fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0)
00295       BAIL(NC_EHDFERR);
00296 #ifdef EXTRA_TESTS
00297    num_plists++;
00298 #endif
00299 
00300    /* Set latest_format in access propertly list and
00301     * H5P_CRT_ORDER_TRACKED in the creation property list. This turns
00302     * on HDF5 creation ordering. */
00303    if (H5Pset_link_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED |
00304                                             H5P_CRT_ORDER_INDEXED)) < 0)
00305       BAIL(NC_EHDFERR);
00306    if (H5Pset_attr_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED |
00307                                             H5P_CRT_ORDER_INDEXED)) < 0)
00308       BAIL(NC_EHDFERR);
00309 
00310    /* Create the file. */
00311    if ((nc->nc4_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0) 
00312       BAIL(NC_EFILEMETA);
00313 
00314    /* Open the root group. */
00315    if ((nc->nc4_info->root_grp->hdf_grpid = H5Gopen2(nc->nc4_info->hdfid, "/", 
00316                                                      H5P_DEFAULT)) < 0)
00317       BAIL(NC_EFILEMETA);
00318 
00319    /* Release the property lists. */
00320    if (H5Pclose(fapl_id) < 0 ||
00321        H5Pclose(fcpl_id) < 0)
00322       BAIL(NC_EHDFERR);
00323 #ifdef EXTRA_TESTS
00324    num_plists--;
00325    num_plists--;
00326 #endif
00327 
00328    /* Define mode gets turned on automatically on create. */
00329    nc->nc4_info->flags |= NC_INDEF;
00330 
00331    return NC_NOERR;
00332 
00333   exit:
00334    if (nc->nc4_info->hdfid > 0) H5Fclose(nc->nc4_info->hdfid);
00335    return retval;
00336 }
00337 
00353 int
00354 NC4_create(const char* path, int cmode, size_t initialsz, int basepe, 
00355            size_t *chunksizehintp, int use_parallel, void *mpidata,
00356            NC_Dispatch *dispatch, NC **ncpp)
00357 {
00358    NC_FILE_INFO_T *nc_file = NULL;
00359 #ifdef USE_PARALLEL
00360    MPI_Comm comm = 0; 
00361    MPI_Info info = 0; 
00362 #else
00363    int comm = 0, info = 0;
00364 #endif /* USE_PARALLEL */
00365    int res;
00366 
00367    assert(ncpp && path);
00368 
00369    LOG((1, "nc4_create_file: path %s cmode 0x%x comm %d info %d",
00370         path, cmode, comm, info));
00371    
00372 #ifdef USE_PARALLEL
00373    if (mpidata) 
00374    { 
00375       comm = ((NC_MPI_INFO *)mpidata)->comm; 
00376       info = ((NC_MPI_INFO *)mpidata)->info;    
00377    }
00378 #endif /* USE_PARALLEL */
00379 
00380    /* If this is our first file, turn off HDF5 error messages. */
00381    if (virgin)
00382    {
00383       if (H5Eset_auto(NULL, NULL) < 0)
00384          LOG((0, "Couldn't turn off HDF5 error messages!"));
00385       LOG((1, "HDF5 error messages have been turned off."));
00386       virgin = 0;
00387    }
00388 
00389    /* Check the cmode for validity. */
00390    if (cmode & ~(NC_NOCLOBBER | NC_64BIT_OFFSET
00391                  | NC_NETCDF4 | NC_CLASSIC_MODEL
00392                  | NC_SHARE | NC_MPIIO | NC_MPIPOSIX | NC_LOCK | NC_PNETCDF
00393                  | NC_DISKLESS
00394                  | NC_WRITE /* to support diskless persistence */
00395                  )
00396        || (cmode & NC_MPIIO && cmode & NC_MPIPOSIX)
00397        || (cmode & NC_64BIT_OFFSET && cmode & NC_NETCDF4)
00398        || (cmode & (NC_MPIIO | NC_MPIPOSIX) && cmode & NC_DISKLESS)
00399       )
00400       return NC_EINVAL;
00401 
00402    /* Allocate the storage for this file info struct, and fill it with
00403       zeros. This add the file metadata to the front of the global
00404       nc_file list. */
00405    if ((res = nc4_file_list_add(&nc_file, dispatch)))
00406       return res;
00407 
00408    /* Apply default create format. */
00409    if (default_create_format == NC_FORMAT_64BIT)
00410       cmode |= NC_64BIT_OFFSET;
00411    else if (default_create_format == NC_FORMAT_NETCDF4)
00412       cmode |= NC_NETCDF4;
00413    else if (default_create_format == NC_FORMAT_NETCDF4_CLASSIC)
00414    {
00415       cmode |= NC_NETCDF4;
00416       cmode |= NC_CLASSIC_MODEL;
00417    }
00418    LOG((2, "cmode after applying default format: 0x%x", cmode));
00419 
00420    /* Check to see if we want a netcdf3 or netcdf4 file. Open it, and
00421       call the appropriate nc*_create. */
00422    if (cmode & NC_NETCDF4) 
00423    {
00424       nc_file->int_ncid = nc_file->ext_ncid;
00425       res = nc4_create_file(path, cmode, comm, info, nc_file);
00426    } 
00427 #ifdef USE_PNETCDF
00428    else if (cmode & NC_PNETCDF)
00429    {
00430       nc_file->pnetcdf_file++;
00431       res = ncmpi_create(comm, path, cmode, info, &(nc_file->int_ncid));      
00432    }
00433 #endif /* USE_PNETCDF */
00434    else 
00435    {
00436       return NC_EINVAL;
00437    }
00438    
00439    /* Delete this file list entry if there was a failure. */
00440    if (res)
00441    {
00442       if (nc_file) 
00443          nc4_file_list_del(nc_file);
00444    }
00445    else
00446       *ncpp = (NC *)nc_file;
00447 
00448    return res;
00449 }
00450 
00451 /* This function is called by read_dataset when a dimension scale
00452  * dataset is encountered. It reads in the dimension data (creating a
00453  * new NC_DIM_INFO_T object), and also checks to see if this is a
00454  * dimension without a variable - that is, a coordinate dimension
00455  * which does not have any coordinate data. */
00456 static int
00457 read_scale(NC_GRP_INFO_T *grp, hid_t datasetid, char *obj_name, 
00458            hsize_t scale_size, hsize_t max_scale_size, 
00459            int *dim_without_var)
00460 {
00461    /*char *start_of_len;*/
00462    char dimscale_name_att[NC_MAX_NAME + 1] = "";
00463    hid_t attid = 0;
00464    int max_len;
00465    int retval;
00466 
00467    /* Add a dimension for this scale. */
00468    if ((retval = nc4_dim_list_add(&grp->dim)))
00469       return retval;
00470 
00471    /* Assign dimid and increment number of dimensions. */
00472    grp->dim->dimid = grp->file->nc4_info->next_dimid++;
00473    grp->ndims++;
00474 
00475    /* Does this dataset have a hidden attribute that tells us its
00476     * dimid? If so, read it. */
00477    H5E_BEGIN_TRY { 
00478       if ((attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME, 
00479                                    H5P_DEFAULT, H5P_DEFAULT)) > 0)
00480       {
00481          if (H5Aread(attid, H5T_NATIVE_INT, &grp->dim->dimid) < 0)
00482             return NC_EHDFERR;
00483          if (H5Aclose(attid) < 0)
00484             return NC_EHDFERR;
00485       }
00486    } H5E_END_TRY;
00487 
00488    max_len = strlen(obj_name) > NC_MAX_NAME ? NC_MAX_NAME : strlen(obj_name);
00489    if (!(grp->dim->name = malloc((max_len + 1) * sizeof(char))))
00490       return NC_ENOMEM;
00491    strncpy(grp->dim->name, obj_name, max_len + 1);
00492    if (SIZEOF_SIZE_T < 8 && scale_size > NC_MAX_UINT)
00493    {
00494       grp->dim->len = NC_MAX_UINT;
00495       grp->dim->too_long = 1;
00496    }
00497    else
00498       grp->dim->len = scale_size;
00499    grp->dim->hdf_dimscaleid = datasetid;
00500 
00501    /* If the dimscale has an unlimited dimension, then this dimension
00502     * is unlimited. */
00503    if (max_scale_size == H5S_UNLIMITED)
00504       grp->dim->unlimited++;
00505 
00506    /* If the scale name is set to DIM_WITHOUT_VARIABLE, then this is a
00507     * dimension, but not a variable. (If get_scale_name returns an
00508     * error, just move on, there's no NAME.) */
00509    if (H5DSget_scale_name(datasetid, dimscale_name_att, NC_MAX_NAME) >= 0)
00510    {
00511       if (!strncmp(dimscale_name_att, DIM_WITHOUT_VARIABLE, 
00512                    strlen(DIM_WITHOUT_VARIABLE)))
00513       {
00514          if (grp->dim->unlimited)
00515          {
00516             size_t len = 0, *lenp = &len;
00517             if ((retval = nc4_find_dim_len(grp, grp->dim->dimid, &lenp)))
00518                return retval;
00519             grp->dim->len = *lenp;
00520          }
00521          (*dim_without_var)++;
00522       }
00523    }
00524 
00525    return NC_NOERR;
00526 }
00527 
00528 /* This function reads the hacked in coordinates attribute I use for
00529  * multi-dimensional coordinates. */
00530 static int
00531 read_coord_dimids(NC_VAR_INFO_T *var)
00532 {
00533    hid_t coord_att_typeid = -1, coord_attid = -1, spaceid = -1;
00534    hssize_t coord_array_size;
00535    int ret = 0;
00536 
00537    /* There is a hidden attribute telling us the ids of the
00538     * dimensions that apply to this multi-dimensional coordinate
00539     * variable. Read it. */
00540    if ((coord_attid = H5Aopen_name(var->hdf_datasetid, COORDINATES)) < 0) ret++;
00541    if (!ret && (coord_att_typeid = H5Aget_type(coord_attid)) < 0) ret++;
00542    if (!ret && H5Aread(coord_attid, coord_att_typeid, var->dimids) < 0) ret++;
00543    LOG((4, "dimscale %s is multidimensional and has coords", var->name));
00544 
00545    /* How many dimensions are there? */
00546    if ((spaceid = H5Aget_space(coord_attid)) < 0) ret++;
00547 #ifdef EXTRA_TESTS
00548    num_spaces++;
00549 #endif
00550    if ((coord_array_size = H5Sget_simple_extent_npoints(spaceid)) < 0) ret++;
00551    
00552    /* Malloc space to the array of pointers to dims. */
00553    
00554 
00555    /* Set my HDF5 IDs free! */
00556    if (spaceid >= 0 && H5Sclose(spaceid) < 0) ret++;
00557 #ifdef EXTRA_TESTS
00558    num_spaces--;
00559 #endif
00560    if (coord_att_typeid >= 0 && H5Tclose(coord_att_typeid) < 0) ret++;
00561    if (coord_attid >= 0 && H5Aclose(coord_attid) < 0) ret++;
00562    return ret ? NC_EATTMETA : NC_NOERR;
00563 }
00564 
00565 /* This function is called when reading a file's metadata for each
00566  * dimension scale attached to a variable.*/
00567 static herr_t 
00568 dimscale_visitor(hid_t did, unsigned dim, hid_t dsid, 
00569                  void *dimscale_hdf5_objids)
00570 {
00571    H5G_stat_t statbuf;
00572 
00573    /* Get more info on the dimscale object.*/
00574    if (H5Gget_objinfo(dsid, ".", 1, &statbuf) < 0)
00575       return -1;
00576 
00577    /* Pass this information back to caller. */
00578 /*   (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno = statbuf.fileno;
00579      (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno = statbuf.objno;*/
00580    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[0] = statbuf.fileno[0];
00581    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[1] = statbuf.fileno[1];
00582    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[0] = statbuf.objno[0];
00583    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[1] = statbuf.objno[1];
00584    return 0;
00585 }
00586 
00587 /* Given an HDF5 type, set a pointer to netcdf type. */
00588 static int
00589 get_netcdf_type(NC_HDF5_FILE_INFO_T *h5, hid_t native_typeid, 
00590                 nc_type *xtype)
00591 {
00592    NC_TYPE_INFO_T *type;
00593    hid_t class;
00594    htri_t is_str, equal = 0;
00595 
00596    assert(h5 && xtype);
00597 
00598    if ((class = H5Tget_class(native_typeid)) < 0)
00599       return NC_EHDFERR;
00600 
00601    /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
00602     * H5Tget_class will return H5T_STRING if this is a string. */
00603    if (class == H5T_STRING)
00604    {
00605       if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
00606          return NC_EHDFERR;
00607       if (is_str)
00608          *xtype = NC_STRING;
00609       else
00610          *xtype = NC_CHAR;
00611       return NC_NOERR;
00612    }
00613    else if (class == H5T_INTEGER || class == H5T_FLOAT)
00614    {
00615       /* For integers and floats, we don't have to worry about
00616        * endianness if we compare native types. */
00617       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SCHAR)) < 0)
00618          return NC_EHDFERR;
00619       if (equal)
00620       {
00621          *xtype = NC_BYTE;
00622          return NC_NOERR;
00623       }
00624       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SHORT)) < 0)
00625          return NC_EHDFERR;
00626       if (equal)
00627       {
00628          *xtype = NC_SHORT;
00629          return NC_NOERR;
00630       }
00631       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_INT)) < 0)
00632          return NC_EHDFERR;
00633       if (equal)
00634       {
00635          *xtype = NC_INT;
00636          return NC_NOERR;
00637       }
00638       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_FLOAT)) < 0)
00639          return NC_EHDFERR;
00640       if (equal)
00641       {
00642          *xtype = NC_FLOAT;
00643          return NC_NOERR;
00644       }
00645       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_DOUBLE)) < 0)
00646          return NC_EHDFERR;
00647       if (equal)
00648       {
00649          *xtype = NC_DOUBLE;
00650          return NC_NOERR;
00651       }
00652       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UCHAR)) < 0)
00653          return NC_EHDFERR;
00654       if (equal)
00655       {
00656          *xtype = NC_UBYTE;
00657          return NC_NOERR;
00658       }
00659       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_USHORT)) < 0)
00660          return NC_EHDFERR;
00661       if (equal)
00662       {
00663          *xtype = NC_USHORT;
00664          return NC_NOERR;
00665       }
00666       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UINT)) < 0)
00667          return NC_EHDFERR;
00668       if (equal)
00669       {
00670          *xtype = NC_UINT;
00671          return NC_NOERR;
00672       }
00673       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_LLONG)) < 0)
00674          return NC_EHDFERR;
00675       if (equal)
00676       {
00677          *xtype = NC_INT64;
00678          return NC_NOERR;
00679       }
00680       if ((equal = H5Tequal(native_typeid, H5T_NATIVE_ULLONG)) < 0)
00681          return NC_EHDFERR;
00682       if (equal)
00683       {
00684          *xtype = NC_UINT64;
00685          return NC_NOERR;
00686       }
00687    }
00688 
00689    /* Maybe we already know about this type. */
00690    if (!equal)
00691       if((type = nc4_rec_find_hdf_type(h5->root_grp, native_typeid)))
00692       {
00693          *xtype = type->nc_typeid;
00694          return NC_NOERR;
00695       }
00696    
00697    *xtype = NC_NAT;
00698    return NC_EBADTYPID;
00699 }
00700 
00701 /* Given an HDF5 type, set a pointer to netcdf type_info struct,
00702  * either an existing one (for user-defined types) or a newly created
00703  * one. */
00704 static int
00705 get_type_info2(NC_HDF5_FILE_INFO_T *h5, hid_t datasetid,
00706                nc_type *xtype, NC_TYPE_INFO_T **type_info)
00707 {
00708    NC_TYPE_INFO_T *type;
00709    htri_t is_str, equal = 0;
00710    hid_t class, native_typeid, hdf_typeid;
00711 #if 0
00712    nc_type my_nc_type = 0;
00713 #endif
00714    H5T_order_t order;
00715    int endianness;
00716    nc_type nc_type_constant[NUM_TYPES] = {NC_CHAR, NC_BYTE, NC_SHORT, NC_INT, NC_FLOAT,
00717                                           NC_DOUBLE, NC_UBYTE, NC_USHORT, NC_UINT,
00718                                           NC_INT64, NC_UINT64, NC_STRING};
00719    int type_size[NUM_TYPES] = {sizeof(char), sizeof(char), sizeof(short), 
00720                                sizeof(int), sizeof(float), sizeof(double),
00721                                sizeof(unsigned char), sizeof(unsigned short), 
00722                                sizeof(unsigned int), sizeof(long long), 
00723                                sizeof(unsigned long long), 0};
00724    int t;
00725 
00726    assert(h5 && xtype && type_info);
00727 
00728    /* Because these N5T_NATIVE_* constants are actually function calls
00729     * (!) in H5Tpublic.h, I can't initialize this array in the usual
00730     * way, because at least some C compilers (like Irix) complain
00731     * about calling functions when defining constants. So I have to do
00732     * it like this. Note that there's no native types for char or
00733     * string. Those are handled later. */
00734    if (!native_type_constant[1])
00735    {
00736       native_type_constant[1] = H5T_NATIVE_SCHAR;
00737       native_type_constant[2] = H5T_NATIVE_SHORT;
00738       native_type_constant[3] = H5T_NATIVE_INT;
00739       native_type_constant[4] = H5T_NATIVE_FLOAT;
00740       native_type_constant[5] = H5T_NATIVE_DOUBLE;
00741       native_type_constant[6] = H5T_NATIVE_UCHAR;
00742       native_type_constant[7] = H5T_NATIVE_USHORT;
00743       native_type_constant[8] = H5T_NATIVE_UINT;
00744       native_type_constant[9] = H5T_NATIVE_LLONG;
00745       native_type_constant[10] = H5T_NATIVE_ULLONG;
00746    }
00747    
00748    /* Get the HDF5 typeid - we'll need it later. */
00749    if ((hdf_typeid = H5Dget_type(datasetid)) < 0)
00750       return NC_EHDFERR;
00751 
00752    /* Get the native typeid. Will be equivalent to hdf_typeid when
00753     * creating but not necessarily when reading, a variable. */
00754    if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0) 
00755       return NC_EHDFERR;
00756 
00757    /* Is this type an integer, string, compound, or what? */
00758    if ((class = H5Tget_class(native_typeid)) < 0)
00759       return NC_EHDFERR;
00760 
00761    /* Is this an atomic type? */
00762    if (class == H5T_STRING || class == H5T_INTEGER || class == H5T_FLOAT)
00763    {
00764       /* Allocate a phony NC_TYPE_INFO_T struct to hold type info. */
00765       if (!(*type_info = calloc(1, sizeof(NC_TYPE_INFO_T))))
00766          return NC_ENOMEM;
00767       (*type_info)->class = class;
00768 
00769       /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
00770        * H5Tget_class will return H5T_STRING if this is a string. */
00771       if (class == H5T_STRING)
00772       {
00773          if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
00774             return NC_EHDFERR;
00775          /* Make sure fixed-len strings will work like variable-len strings */
00776          if (is_str || H5Tget_size(hdf_typeid) > 1)
00777             t = NUM_TYPES - 1;
00778          else
00779              t = 0;
00780       }
00781       else if (class == H5T_INTEGER || class == H5T_FLOAT)
00782       {
00783          for (t = 1; t < NUM_TYPES - 1; t++)
00784          {
00785             if ((equal = H5Tequal(native_typeid, native_type_constant[t])) < 0)
00786                return NC_EHDFERR;
00787             if (equal)
00788             {
00789 #if 0
00790                my_nc_type = nc_type_constant[t];
00791 #endif
00792                break;
00793             }
00794          }
00795 
00796          /* Find out about endianness. */
00797          if (class == H5T_INTEGER)
00798          {
00799             if ((order = H5Tget_order(hdf_typeid)) < 0) 
00800                return NC_EHDFERR;
00801             if (order == H5T_ORDER_LE)
00802                endianness = NC_ENDIAN_LITTLE;
00803             else if (order == H5T_ORDER_BE)
00804                endianness = NC_ENDIAN_BIG;
00805             else /* don't support H5T_ORDER_VAX, H5T_ORDER_MIXED, H5T_ORDER_NONE */
00806                 return NC_EBADTYPE;
00807             /* Copy this into the type_info struct. */
00808             (*type_info)->endianness = endianness;
00809          }
00810       }
00811       *xtype = nc_type_constant[t];
00812       (*type_info)->nc_typeid = nc_type_constant[t];
00813       (*type_info)->size = type_size[t];
00814       if (!((*type_info)->name = malloc((strlen(nc_type_name[t]) + 1) * sizeof(char))))
00815          return NC_ENOMEM;
00816       strcpy((*type_info)->name, nc_type_name[t]);
00817       (*type_info)->class = class;
00818       (*type_info)->hdf_typeid = hdf_typeid;
00819       (*type_info)->native_typeid = native_typeid;
00820       (*type_info)->close_hdf_typeid = 1;
00821       return NC_NOERR;
00822    }
00823    else
00824    {
00825       /* This is a user-defined type. */
00826       if((type = nc4_rec_find_hdf_type(h5->root_grp, native_typeid)))
00827       {
00828          *xtype = type->nc_typeid;
00829          *type_info = type;
00830       }
00831 
00832       /* The type entry in the array of user-defined types already has
00833        * an open data typeid (and native typeid), so close the ones we
00834        * opened above. */
00835       if (H5Tclose(native_typeid) < 0) 
00836          return NC_EHDFERR;
00837       if (H5Tclose(hdf_typeid) < 0) 
00838          return NC_EHDFERR;
00839 
00840       if (type)
00841          return NC_NOERR;
00842    }
00843 
00844    *xtype = NC_NAT;
00845    return NC_EBADTYPID;
00846 }
00847 
00848 /* Read an attribute. */
00849 static int 
00850 read_hdf5_att(NC_GRP_INFO_T *grp, hid_t attid, NC_ATT_INFO_T *att)
00851 {
00852    hid_t spaceid = 0, file_typeid = 0;
00853    hsize_t dims[1]; /* netcdf attributes always 1-D. */
00854    int retval = NC_NOERR;
00855    size_t type_size;
00856    int att_ndims;
00857    hssize_t att_npoints;
00858    H5T_class_t att_class;      
00859    int fixed_len_string = 0;
00860    size_t fixed_size = 0;
00861 
00862    assert(att->name);
00863    LOG((5, "read_hdf5_att: att->attnum %d att->name %s "
00864         "att->xtype %d att->len %d", att->attnum, att->name,
00865         att->xtype, att->len));
00866 
00867    /* Get type of attribute in file. */
00868    if ((file_typeid = H5Aget_type(attid)) < 0)
00869       return NC_EATTMETA;
00870    if ((att->native_typeid = H5Tget_native_type(file_typeid, H5T_DIR_DEFAULT)) < 0) 
00871       BAIL(NC_EHDFERR);
00872    if ((att_class = H5Tget_class(att->native_typeid)) < 0)
00873       BAIL(NC_EATTMETA);
00874    if (att_class == H5T_STRING && !H5Tis_variable_str(att->native_typeid))
00875    {
00876       fixed_len_string++;
00877       if (!(fixed_size = H5Tget_size(att->native_typeid)))
00878          BAIL(NC_EATTMETA);
00879    }
00880    if ((retval = get_netcdf_type(grp->file->nc4_info, att->native_typeid, &(att->xtype))))
00881       BAIL(retval);
00882 
00883 
00884    /* Get len. */
00885    if ((spaceid = H5Aget_space(attid)) < 0)
00886       BAIL(NC_EATTMETA); 
00887 #ifdef EXTRA_TESTS
00888    num_spaces++;
00889 #endif
00890    if ((att_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
00891       BAIL(NC_EATTMETA);
00892    if ((att_npoints = H5Sget_simple_extent_npoints(spaceid)) < 0)
00893       BAIL(NC_EATTMETA);
00894 
00895    /* If both att_ndims and att_npoints are zero, then this is a
00896     * zero length att. */
00897    if (att_ndims == 0 && att_npoints == 0)
00898    {
00899       dims[0] = 0;
00900    }
00901    else if (att->xtype == NC_STRING) {
00902        dims[0] = att_npoints;
00903    }
00904    else if (att->xtype == NC_CHAR)
00905    {
00906       /* NC_CHAR attributes are written as a scalar in HDF5, of type
00907        * H5T_C_S1, of variable length. */
00908       if (att_ndims == 0) 
00909       {
00910          if (!(dims[0] = H5Tget_size(file_typeid)))
00911             BAIL(NC_EATTMETA);
00912       }
00913       else
00914       {
00915          /* This is really a string type! */
00916          att->xtype = NC_STRING;
00917          dims[0] = att_npoints;
00918       }
00919    } 
00920    else
00921    {
00922       /* All netcdf attributes are scalar or 1-D only. */
00923       if (att_ndims > 1)
00924          BAIL(NC_EATTMETA);
00925 
00926       /* Read the size of this attribute. */
00927       if (H5Sget_simple_extent_dims(spaceid, dims, NULL) < 0)
00928          BAIL(NC_EATTMETA);
00929    }
00930       
00931    /* Tell the user what the length if this attribute is. */
00932    att->len = dims[0];
00933 
00934    /* Allocate some memory if the len is not zero, and read the
00935       attribute. */
00936    if (dims[0])
00937    {
00938       if ((retval = nc4_get_typelen_mem(grp->file->nc4_info, att->xtype, 0,
00939                                         &type_size)))
00940          return retval;
00941       if (att_class == H5T_VLEN)
00942       {
00943          if (!(att->vldata = malloc((unsigned int)(att->len * sizeof(hvl_t)))))
00944             BAIL(NC_ENOMEM);
00945          if (H5Aread(attid, att->native_typeid, att->vldata) < 0)
00946             BAIL(NC_EATTMETA);
00947       }
00948       else if (att->xtype == NC_STRING)
00949       {
00950          if (!(att->stdata = calloc(att->len, sizeof(char *))))
00951             BAIL(NC_ENOMEM);
00952          /* For a fixed length HDF5 string, the read requires
00953           * contiguous memory. Meanwhile, the netCDF API requires that
00954           * nc_free_string be called on string arrays, which would not
00955           * work if one contiguous memory block were used. So here I
00956           * convert the contiguous block of strings into an array of
00957           * malloced strings (each string with its own malloc). Then I
00958           * copy the data and free the contiguous memory. This
00959           * involves copying the data, which is bad, but this only
00960           * occurs for fixed length string attributes, and presumably
00961           * these are small. (And netCDF-4 does not create them - it
00962           * always uses variable length strings. */
00963          if (fixed_len_string)
00964          {
00965             int i;
00966             char *contig_buf, *cur;
00967 
00968             /* Alloc space for the contiguous memory read. */
00969             if (!(contig_buf = malloc(att->len * fixed_size * sizeof(char))))
00970                BAIL(NC_ENOMEM);
00971 
00972             /* Read the fixed-len strings as one big block. */
00973             if (H5Aread(attid, att->native_typeid, contig_buf) < 0)
00974                BAIL(NC_EATTMETA);
00975             
00976             /* Copy strings, one at a time, into their new home. Alloc
00977                space for each string. The user will later free this
00978                space with nc_free_string. */
00979             cur = contig_buf;
00980             for (i = 0; i < att->len; i++)
00981             {
00982                if (!(att->stdata[i] = malloc(fixed_size)))
00983                   BAIL(NC_ENOMEM);
00984                strncpy(att->stdata[i], cur, fixed_size);
00985                cur += fixed_size;
00986             }
00987             
00988             /* Free contiguous memory buffer. */
00989             free(contig_buf);
00990          }
00991          else
00992          {
00993             /* Read variable-length string atts. */
00994             if (H5Aread(attid, att->native_typeid, att->stdata) < 0)
00995                BAIL(NC_EATTMETA);
00996          }
00997       }
00998       else
00999       {
01000          if (!(att->data = malloc((unsigned int)(att->len * type_size))))
01001             BAIL(NC_ENOMEM);
01002          if (H5Aread(attid, att->native_typeid, att->data) < 0)
01003             BAIL(NC_EATTMETA);
01004       }
01005    }
01006 
01007    if (H5Tclose(file_typeid) < 0)
01008       BAIL(NC_EHDFERR);
01009    if (H5Sclose(spaceid) < 0)
01010       return NC_EHDFERR;
01011 #ifdef EXTRA_TESTS
01012    num_spaces--;
01013 #endif
01014    
01015    return NC_NOERR;
01016 
01017   exit:
01018    if (H5Tclose(file_typeid) < 0)
01019       BAIL2(NC_EHDFERR);
01020    if (spaceid > 0 && H5Sclose(spaceid) < 0)
01021       BAIL2(NC_EHDFERR);
01022 #ifdef EXTRA_TESTS
01023    num_spaces--;
01024 #endif
01025    return retval;
01026 }
01027 
01028 /* Read information about a user defined type from the HDF5 file, and
01029  * stash it in the group's list of types. Return the netcdf typeid
01030  * through a pointer, if caller desires it. */
01031 static int
01032 read_type(NC_GRP_INFO_T *grp, char *type_name)
01033 {
01034    NC_TYPE_INFO_T *type;
01035    H5T_class_t class;
01036    hid_t hdf_typeid, native_typeid = 0;
01037    int nmembers;
01038    hid_t member_hdf_typeid, base_hdf_typeid = 0;
01039    char *member_name = NULL;
01040    size_t type_size = 0, member_offset;
01041    unsigned int m;
01042    nc_type ud_type_type = NC_NAT, base_nc_type = NC_NAT, member_xtype;
01043    htri_t ret;
01044    int retval = NC_NOERR;
01045    void *value;
01046    int i;
01047 
01048    assert(grp && type_name);
01049 
01050    if (strlen(type_name) > NC_MAX_NAME)
01051       return NC_EBADNAME;
01052 
01053    LOG((4, "read_type: type_name %s grp->name %s", type_name, grp->name));
01054 
01055    if ((hdf_typeid = H5Topen2(grp->hdf_grpid, type_name, H5P_DEFAULT)) < 0)
01056       return NC_EHDFERR;
01057 
01058    /* What is the native type for this platform? */
01059    if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0) 
01060       return NC_EHDFERR;
01061    
01062    /* What is the size of this type on this platform. */
01063    if (!(type_size = H5Tget_size(native_typeid)))
01064       return NC_EHDFERR;
01065    LOG((5, "type_size %d", type_size));
01066 
01067    /* What is the class of this type, compound, vlen, etc. */
01068    if ((class = H5Tget_class(hdf_typeid)) < 0)
01069       return NC_EHDFERR;
01070    switch (class)
01071    {
01072       case H5T_STRING:
01073          ud_type_type = NC_STRING;
01074          break;
01075       case H5T_COMPOUND:
01076          ud_type_type = NC_COMPOUND; 
01077          break;
01078       case H5T_VLEN:
01079          /* For conveninence we allow user to pass vlens of strings
01080           * with null terminated strings. This means strings are
01081           * treated slightly differently by the API, although they are
01082           * really just VLENs of characters. */
01083          if ((ret = H5Tis_variable_str(hdf_typeid)) < 0)
01084             return NC_EHDFERR;
01085          if (ret)
01086             ud_type_type = NC_STRING;
01087          else
01088          {
01089             ud_type_type = NC_VLEN;
01090 
01091             /* Find the base type of this vlen (i.e. what is this a
01092              * vlen of?) */
01093             if (!(base_hdf_typeid = H5Tget_super(native_typeid)))
01094                return NC_EHDFERR;
01095 
01096             /* What size is this type? */
01097             if (!(type_size = H5Tget_size(base_hdf_typeid)))
01098                return NC_EHDFERR;
01099 
01100             /* What is the netcdf corresponding type. */
01101             if ((retval = get_netcdf_type(grp->file->nc4_info, base_hdf_typeid, 
01102                                           &base_nc_type)))
01103                return retval;
01104             LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d", 
01105                  base_hdf_typeid, type_size, base_nc_type));
01106          }
01107          break;
01108       case H5T_OPAQUE:
01109          ud_type_type = NC_OPAQUE;
01110          /* What size is this type? */
01111          if (!(type_size = H5Tget_size(hdf_typeid)))
01112             return NC_EHDFERR;
01113          LOG((5, "type_size %d", type_size));
01114          break;
01115       case H5T_ENUM:
01116          ud_type_type = NC_ENUM;
01117 
01118          /* Find the base type of this enum (i.e. what is this a
01119           * enum of?) */
01120          if (!(base_hdf_typeid = H5Tget_super(hdf_typeid)))
01121             return NC_EHDFERR;
01122          /* What size is this type? */
01123          if (!(type_size = H5Tget_size(base_hdf_typeid)))
01124             return NC_EHDFERR;
01125          /* What is the netcdf corresponding type. */
01126          if ((retval = get_netcdf_type(grp->file->nc4_info, base_hdf_typeid, 
01127                                        &base_nc_type)))
01128             return retval;
01129          LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d", 
01130               base_hdf_typeid, type_size, base_nc_type));
01131          break;
01132       default:
01133          LOG((0, "unknown class"));
01134          return NC_EBADCLASS;
01135    }
01136 
01137    /* Add to the list for this new type, and get a local pointer to it. */
01138    if ((retval = nc4_type_list_add(&grp->type, &type)))
01139       return retval;
01140    assert(type);
01141 
01142    /* Remember info about this type. */
01143    type->nc_typeid = grp->file->nc4_info->next_typeid++;
01144    type->size = type_size;
01145    if (!(type->name = malloc((strlen(type_name) + 1) * sizeof(char))))
01146       return NC_ENOMEM;
01147    strcpy(type->name, type_name);
01148    type->class = ud_type_type;
01149    type->base_nc_type = base_nc_type;
01150    type->committed++;
01151    type->hdf_typeid = hdf_typeid;
01152    type->native_typeid = native_typeid;
01153 
01154    /* Read info about each member of this compound type. */
01155    if (ud_type_type == NC_COMPOUND)
01156    {
01157       if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0)
01158          return NC_EHDFERR;
01159       LOG((5, "compound type has %d members", nmembers));
01160       for (m = 0; m < nmembers; m++)
01161       {
01162          H5T_class_t mem_class;
01163          hid_t member_native_typeid;
01164          int ndims = 0, dim_size[NC_MAX_VAR_DIMS];
01165          hsize_t dims[NC_MAX_VAR_DIMS];
01166          int d;
01167 
01168          /* Get the typeid and native typeid of this member of the
01169           * compound type. */
01170          if ((member_hdf_typeid = H5Tget_member_type(type->native_typeid, m)) < 0)
01171             return NC_EHDFERR;
01172          if ((member_native_typeid = H5Tget_native_type(member_hdf_typeid, H5T_DIR_DEFAULT)) < 0) 
01173             return NC_EHDFERR;
01174 
01175          /* Get the name of the member.*/
01176          member_name = H5Tget_member_name(type->native_typeid, m);
01177          if (!member_name || strlen(member_name) > NC_MAX_NAME)
01178             return NC_EBADNAME;
01179 
01180          /* Offset in bytes on *this* platform. */
01181          member_offset = H5Tget_member_offset(type->native_typeid, m);
01182 
01183          /* Get dimensional data if this member is an array of something. */
01184          if ((mem_class = H5Tget_class(member_hdf_typeid)) < 0)
01185             return NC_EHDFERR;
01186          if (mem_class == H5T_ARRAY)
01187          {
01188             if ((ndims = H5Tget_array_ndims(member_hdf_typeid)) < 0)
01189                return NC_EHDFERR;
01190             if (H5Tget_array_dims(member_hdf_typeid, dims, NULL) != ndims)
01191                return NC_EHDFERR;
01192             for (d = 0; d < ndims; d++)
01193                dim_size[d] = dims[d];
01194             /* What is the netCDF typeid of this member? */
01195             if ((retval = get_netcdf_type(grp->file->nc4_info, H5Tget_super(member_hdf_typeid), 
01196                                           &member_xtype)))
01197                return retval;
01198          }
01199          else
01200          {
01201             /* What is the netCDF typeid of this member? */
01202             if ((retval = get_netcdf_type(grp->file->nc4_info, member_native_typeid, 
01203                                           &member_xtype)))
01204                return retval;
01205          }
01206 
01207          /* Add this member to our list of fields in this compound type. */
01208          if (ndims)
01209          {
01210             if ((retval = nc4_field_list_add(&type->field, type->num_fields++, member_name, 
01211                                              member_offset, H5Tget_super(member_hdf_typeid), 
01212                                              H5Tget_super(member_native_typeid), 
01213                                              member_xtype, ndims, dim_size)))
01214                return retval;
01215          }
01216          else
01217          {
01218             if ((retval = nc4_field_list_add(&type->field, type->num_fields++, member_name, 
01219                                              member_offset, member_hdf_typeid, member_native_typeid, 
01220                                              member_xtype, 0, NULL)))
01221                return retval;
01222          } 
01223          
01224          /* HDF5 allocated this for us. */
01225          free(member_name);
01226       }
01227    }
01228    else if (ud_type_type == NC_VLEN)
01229    {
01230       type->base_hdf_typeid = base_hdf_typeid;
01231    }
01232    else if (ud_type_type == NC_ENUM)
01233    {
01234       /* Remember the base HDF5 type for this enum. */
01235       type->base_hdf_typeid = base_hdf_typeid;
01236 
01237       /* Find out how many member are in the enum. */
01238       if ((type->num_enum_members = H5Tget_nmembers(hdf_typeid)) < 0) 
01239          return NC_EHDFERR;
01240 
01241       /* Allocate space for one value. */
01242       if (!(value = calloc(1, type_size)))
01243          return NC_ENOMEM;
01244 
01245       /* Read each name and value defined in the enum. */
01246       for (i = 0; i < type->num_enum_members; i++)
01247       {
01248          /* Get the name and value from HDF5. */
01249          if (!(member_name = H5Tget_member_name(hdf_typeid, i)))
01250             return NC_EHDFERR;
01251          if (!member_name || strlen(member_name) > NC_MAX_NAME)
01252             return NC_EBADNAME;
01253          if (H5Tget_member_value(hdf_typeid, i, value) < 0) 
01254             return NC_EHDFERR;
01255 
01256          /* Insert new field into this type's list of fields. */
01257          if ((retval = nc4_enum_member_add(&type->enum_member, type->size, 
01258                                            member_name, value)))
01259             return retval;
01260          free(member_name);
01261       }
01262       
01263       /* Free the tempory memory for one value, and the member name
01264        * (which HDF5 allocated for us). */
01265       free(value);
01266    }
01267    
01268    return retval;
01269 }
01270 
01271 /* This function is called by read_dataset, (which is called by
01272  * nc4_rec_read_metadata) when a netCDF variable is found in the
01273  * file. This function reads in all the metadata about the var,
01274  * including the attributes. */
01275 static int
01276 read_var(NC_GRP_INFO_T *grp, hid_t datasetid, char *obj_name, 
01277          size_t ndims, int is_scale, int num_scales, hid_t access_pid)
01278 {
01279    NC_VAR_INFO_T *var;
01280    int natts, a, d;
01281 
01282    NC_ATT_INFO_T *att;
01283    hid_t attid = 0;
01284    char att_name[NC_MAX_HDF5_NAME + 1];
01285 
01286 #define CD_NELEMS_ZLIB 1
01287 #define CD_NELEMS_SZIP 4
01288    H5Z_filter_t filter;
01289    int num_filters;
01290    unsigned int cd_values[CD_NELEMS_SZIP];
01291    size_t cd_nelems = CD_NELEMS_SZIP;
01292    hid_t propid = 0;
01293    H5D_fill_value_t fill_status;
01294    H5D_layout_t layout;
01295    hsize_t chunksize[NC_MAX_VAR_DIMS];
01296    int retval = NC_NOERR;
01297    double rdcc_w0;
01298    int f;
01299 
01300    assert(obj_name && grp);
01301    LOG((4, "read_var: obj_name %s", obj_name));
01302 
01303    /* Add a variable to the end of the group's var list. */
01304    if ((retval = nc4_var_list_add(&grp->var, &var)))
01305       return retval;
01306             
01307    /* Fill in what we already know. */
01308    var->hdf_datasetid = datasetid;
01309    var->varid = grp->nvars++;
01310    var->created++;
01311    var->ndims = ndims;
01312 
01313    /* We need some room to store information about dimensions for this
01314     * var. */
01315    if (var->ndims)
01316    {
01317       if (!(var->dim = calloc(var->ndims, sizeof(NC_DIM_INFO_T *))))
01318          return NC_ENOMEM;
01319       if (!(var->dimids = calloc(var->ndims, sizeof(int))))
01320          return NC_ENOMEM;
01321    }
01322 
01323    /* Learn about current chunk cache settings. */
01324    if ((H5Pget_chunk_cache(access_pid, &(var->chunk_cache_nelems), 
01325                            &(var->chunk_cache_size), &rdcc_w0)) < 0)
01326       return NC_EHDFERR;
01327    var->chunk_cache_preemption = rdcc_w0;
01328 
01329    /* Allocate space for the name. */
01330    if (!(var->name = malloc((strlen(obj_name) + 1) * sizeof(char))))
01331       return NC_ENOMEM;
01332 
01333    /* Check for a weird case: a non-coordinate (and non-scalar)
01334     * variable that has the same name as a dimension. It's legal in
01335     * netcdf, and requires that the HDF5 dataset name be changed. */
01336    if (var->ndims && 
01337        !strncmp(obj_name, NON_COORD_PREPEND, strlen(NON_COORD_PREPEND)))
01338    {
01339       if (strlen(obj_name) > NC_MAX_NAME)
01340          return NC_EMAXNAME;
01341       strcpy(var->name, &obj_name[strlen(NON_COORD_PREPEND)]);
01342    }
01343    else
01344       strcpy(var->name, obj_name);
01345 
01346    /* Find out what filters are applied to this HDF5 dataset,
01347     * fletcher32, deflate, and/or shuffle. All other filters are
01348     * ignored. */
01349    if ((propid = H5Dget_create_plist(datasetid)) < 0) 
01350       BAIL(NC_EHDFERR);
01351 #ifdef EXTRA_TESTS
01352    num_plists++;
01353 #endif /* EXTRA_TESTS */
01354 
01355    /* Get the chunking info for non-scalar vars. */
01356    if ((layout = H5Pget_layout(propid)) < -1)
01357       BAIL(NC_EHDFERR);
01358    if (layout == H5D_CHUNKED)
01359    {
01360       if (H5Pget_chunk(propid, NC_MAX_VAR_DIMS, chunksize) < 0)
01361          BAIL(NC_EHDFERR);
01362       if (!(var->chunksizes = malloc(var->ndims * sizeof(size_t))))
01363          BAIL(NC_ENOMEM);
01364       for (d = 0; d < var->ndims; d++)
01365          var->chunksizes[d] = chunksize[d];
01366    }
01367    else if (layout == H5D_CONTIGUOUS)
01368       var->contiguous++;
01369 
01370    /* The possible values of filter (which is just an int) can be
01371     * found in H5Zpublic.h. */
01372    if ((num_filters = H5Pget_nfilters(propid)) < 0) 
01373       BAIL(NC_EHDFERR);
01374    for (f = 0; f < num_filters; f++)
01375    {
01376       if ((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems, 
01377                                    cd_values, 0, NULL, NULL)) < 0)
01378          BAIL(NC_EHDFERR);
01379       switch (filter)
01380       {
01381          case H5Z_FILTER_SHUFFLE:
01382             var->shuffle = 1;
01383             break;
01384          case H5Z_FILTER_FLETCHER32:
01385             var->fletcher32 = 1;
01386             break;
01387          case H5Z_FILTER_DEFLATE:
01388             var->deflate++;
01389             if (cd_nelems != CD_NELEMS_ZLIB ||
01390                 cd_values[0] > MAX_DEFLATE_LEVEL)
01391                BAIL(NC_EHDFERR);
01392             var->deflate_level = cd_values[0];
01393             break;
01394          case H5Z_FILTER_SZIP:
01395             var->deflate++;
01396             if (cd_nelems != CD_NELEMS_SZIP)
01397                BAIL(NC_EHDFERR);
01398             var->options_mask = cd_values[0];
01399             var->pixels_per_block = cd_values[1];
01400             break;
01401          default:
01402             LOG((1, "Yikes! Unknown filter type found on dataset!"));
01403             break;
01404       }
01405    }
01406                
01407    /* Learn all about the type of this variable. */
01408    if ((retval = get_type_info2(grp->file->nc4_info, datasetid, 
01409                                 &var->xtype, &var->type_info)))
01410       BAIL(retval);
01411 
01412    /* Is there a fill value associated with this dataset? */
01413    if (H5Pfill_value_defined(propid, &fill_status) < 0)
01414       BAIL(NC_EHDFERR);
01415 
01416    /* Get the fill value, if there is one defined. */
01417    if (fill_status == H5D_FILL_VALUE_USER_DEFINED)
01418    {
01419       /* Allocate space to hold the fill value. */
01420       if (!var->fill_value)
01421       {
01422          if (var->type_info->class == NC_VLEN)
01423          {
01424             if (!(var->fill_value = malloc(sizeof(nc_vlen_t))))
01425                BAIL(NC_ENOMEM);
01426          }
01427          else if (var->type_info->size)
01428          {
01429             if (!(var->fill_value = malloc(var->type_info->size)))
01430                BAIL(NC_ENOMEM);
01431          }
01432          else
01433          {
01434             if (!(var->fill_value = malloc(sizeof(char *))))
01435                BAIL(NC_ENOMEM);
01436          }
01437       }
01438       
01439       /* Get the fill value from the HDF5 property lust. */
01440       if (H5Pget_fill_value(propid, var->type_info->native_typeid, 
01441                             var->fill_value) < 0)
01442          BAIL(NC_EHDFERR);
01443    }
01444    else
01445       var->no_fill = 1;
01446 
01447    /* If it's a scale, mark it as such. If not, allocate space to
01448     * remember whether the dimscale has been attached for each
01449     * dimension. */
01450    if (is_scale)
01451    {
01452       assert(ndims);
01453       var->dimscale++;
01454       if (var->ndims > 1)
01455       {
01456          if ((retval = read_coord_dimids(var)))
01457             BAIL(retval);
01458       }
01459       else
01460       {
01461          var->dimids[0] = grp->dim->dimid;
01462          var->dim[0] = grp->dim;
01463       }
01464    }
01465    else
01466       if (num_scales && ndims && 
01467           !(var->dimscale_attached = calloc(ndims, sizeof(int))))
01468          BAIL(NC_ENOMEM);       
01469        
01470    /* If this is not a scale, and has scales, iterate
01471     * through them. (i.e. this is a variable that is not a
01472     * coordinate variable) */
01473    if (!is_scale && num_scales)
01474    {
01475       /* Store id information allowing us to match hdf5
01476        * dimscales to netcdf dimensions. */
01477       if (!(var->dimscale_hdf5_objids = malloc(ndims * sizeof(struct hdf5_objid))))
01478          BAIL(NC_ENOMEM);
01479       for (d = 0; d < var->ndims; d++)
01480       {
01481          LOG((5, "read_var: about to iterate over scales for dim %d", d));
01482          if (H5DSiterate_scales(var->hdf_datasetid, d, NULL, dimscale_visitor,
01483                                 &(var->dimscale_hdf5_objids[d])) < 0)
01484             BAIL(NC_EHDFERR);
01485 /*       LOG((5, "read_var: collected scale info for dim %d "
01486          "var %s fileno[0] %d objno[0] %d fileno[1] %d objno[1] %d", 
01487          d, var->name, var->dimscale_hdf5_objids[d].fileno[0], 
01488          var->dimscale_hdf5_objids[d].objno[0], 
01489          var->dimscale_hdf5_objids[d].fileno[1], 
01490          var->dimscale_hdf5_objids[d].objno[1]));*/
01491          var->dimscale_attached[d]++;
01492       }
01493    }
01494         
01495    /* Now read all the attributes of this variable, ignoring the
01496       ones that hold HDF5 dimension scale information. */
01497    if ((natts = H5Aget_num_attrs(var->hdf_datasetid)) < 0)
01498       BAIL(NC_EATTMETA);
01499    for (a = 0; a < natts; a++)
01500    {
01501       /* Close the attribute and try to move on with our
01502        * lives. Like bits through the network port, so
01503        * flows the Days of Our Lives! */
01504       if (attid && H5Aclose(attid) < 0)
01505          BAIL(NC_EHDFERR);
01506 
01507       /* Open the att and get its name. */
01508       if ((attid = H5Aopen_idx(var->hdf_datasetid, (unsigned int)a)) < 0)
01509          BAIL(NC_EATTMETA);
01510       if (H5Aget_name(attid, NC_MAX_HDF5_NAME, att_name) < 0)
01511          BAIL(NC_EATTMETA);
01512       LOG((4, "read_var: a %d att_name %s", a, att_name));
01513 
01514       /* Should we ignore this attribute? */    
01515       if (strcmp(att_name, REFERENCE_LIST) &&
01516           strcmp(att_name, CLASS) &&
01517           strcmp(att_name, DIMENSION_LIST) &&
01518           strcmp(att_name, NAME) &&
01519           strcmp(att_name, COORDINATES))
01520       {
01521          /* Is this the hidden attribute that holds the netCDF
01522           * dimension id for a coordinate variable? */
01523          if (!strcmp(att_name, NC_DIMID_ATT_NAME))
01524          {
01525             
01526          }
01527          else
01528          {
01529             /* Add to the end of the list of atts for this var. */
01530             if ((retval = nc4_att_list_add(&var->att)))
01531                BAIL(retval);
01532             for (att = var->att; att->next; att = att->next)
01533                ;
01534             
01535             /* Fill in the information we know. */
01536             att->attnum = var->natts++;
01537             if (!(att->name = malloc((strlen(att_name) + 1) * sizeof(char))))
01538                BAIL(NC_ENOMEM);
01539             strcpy(att->name, att_name);
01540             
01541             /* Read the rest of the info about the att,
01542              * including its values. */
01543             if ((retval = read_hdf5_att(grp, attid, att)))
01544                BAIL(retval);
01545             
01546             att->created++;
01547          } /* endif not HDF5 att */
01548       }
01549    } /* next attribute */
01550 
01551    /* Is this a deflated variable with a chunksize greater than the
01552     * current cache size? */
01553    if ((retval = nc4_adjust_var_cache(grp, var)))
01554       BAIL(retval);
01555 
01556   exit:
01557    if (propid > 0 && H5Pclose(propid) < 0)
01558       BAIL2(NC_EHDFERR);
01559 #ifdef EXTRA_TESTS
01560    num_plists--;
01561 #endif
01562    if (attid > 0 && H5Aclose(attid) < 0)
01563       BAIL2(NC_EHDFERR);
01564    return retval;
01565 }
01566 
01567 /* This function is called by nc4_rec_read_metadata to read all the
01568  * group level attributes (the NC_GLOBAL atts for this group). */
01569 static int
01570 read_grp_atts(NC_GRP_INFO_T *grp)
01571 {
01572    hid_t attid = 0;
01573    hsize_t num_obj, i;
01574    NC_ATT_INFO_T *att;
01575    NC_TYPE_INFO_T *type;
01576    char obj_name[NC_MAX_HDF5_NAME + 1];
01577    int max_len;
01578    int retval = NC_NOERR;
01579 
01580    num_obj = H5Aget_num_attrs(grp->hdf_grpid);
01581    for (i = 0; i < num_obj; i++)
01582    {
01583       if (attid > 0) 
01584          H5Aclose(attid);
01585       if ((attid = H5Aopen_idx(grp->hdf_grpid, (unsigned int)i)) < 0)
01586          BAIL(NC_EATTMETA);
01587       if (H5Aget_name(attid, NC_MAX_NAME + 1, obj_name) < 0)
01588          BAIL(NC_EATTMETA);
01589       LOG((3, "reading attribute of _netCDF group, named %s", obj_name));
01590 
01591       /* This may be an attribute telling us that strict netcdf-3
01592        * rules are in effect. If so, we will make note of the fact,
01593        * but not add this attribute to the metadata. It's not a user
01594        * attribute, but an internal netcdf-4 one. */
01595       if (!strcmp(obj_name, NC3_STRICT_ATT_NAME))
01596          grp->file->nc4_info->cmode |= NC_CLASSIC_MODEL;
01597       else
01598       {
01599          /* Add an att struct at the end of the list, and then go to it. */
01600          if ((retval = nc4_att_list_add(&grp->att)))
01601             BAIL(retval);
01602          for (att = grp->att; att->next; att = att->next)
01603             ;
01604 
01605          /* Add the info about this attribute. */
01606          max_len = strlen(obj_name) > NC_MAX_NAME ? NC_MAX_NAME : strlen(obj_name);
01607          if (!(att->name = malloc((max_len + 1) * sizeof(char))))
01608             BAIL(NC_ENOMEM);
01609          strncpy(att->name, obj_name, max_len);
01610          att->name[max_len] = 0;
01611          att->attnum = grp->natts++;
01612          if ((retval = read_hdf5_att(grp, attid, att)))
01613             BAIL(retval);
01614          att->created++;
01615          if ((retval = nc4_find_type(grp->file->nc4_info, att->xtype, &type)))
01616             BAIL(retval);
01617          if (type)
01618             att->class = type->class;
01619       }
01620    }
01621 
01622   exit:
01623    if (attid > 0 && H5Aclose(attid) < 0)
01624       BAIL2(NC_EHDFERR);
01625    return retval;
01626 }
01627 
01628 /* This function is called when nc4_rec_read_vars encounters an HDF5
01629  * dataset when reading a file. */
01630 static int
01631 read_dataset(NC_GRP_INFO_T *grp, char *obj_name)
01632 {
01633    hid_t datasetid = 0;   
01634    hid_t spaceid = 0, access_pid = 0;
01635    int ndims;
01636    hsize_t dims[NC_MAX_DIMS], max_dims[NC_MAX_DIMS];
01637    int is_scale = 0;
01638    int dim_without_var = 0;
01639    int num_scales = 0;            
01640    int retval = NC_NOERR;
01641 
01642    /* Open this dataset. */
01643    if ((datasetid = H5Dopen2(grp->hdf_grpid, obj_name, H5P_DEFAULT)) < 0)
01644       BAIL(NC_EVARMETA);
01645 
01646    /* Get the current chunk cache settings. */
01647    if ((access_pid = H5Dget_access_plist(datasetid)) < 0)
01648       BAIL(NC_EVARMETA);
01649 #ifdef EXTRA_TESTS
01650    num_plists++;
01651 #endif
01652 
01653    /* Get the dimension information for this dataset. */
01654    if ((spaceid = H5Dget_space(datasetid)) < 0)
01655       BAIL(NC_EHDFERR);
01656 #ifdef EXTRA_TESTS
01657    num_spaces++;
01658 #endif
01659    if ((ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
01660       BAIL(NC_EHDFERR);
01661    if (ndims > NC_MAX_DIMS)
01662       BAIL(NC_EMAXDIMS);
01663    if (H5Sget_simple_extent_dims(spaceid, dims, max_dims) < 0)
01664       BAIL(NC_EHDFERR);
01665 
01666    /* Is this a dimscale? */
01667    if ((is_scale = H5DSis_scale(datasetid)) < 0)
01668       BAIL(NC_EHDFERR);
01669    if (is_scale)
01670    {
01671       /* Read the scale information. */
01672       if ((retval = read_scale(grp, datasetid, obj_name, dims[0],
01673                                max_dims[0], &dim_without_var)))
01674          BAIL(retval);
01675    }
01676    else
01677    {
01678       /* Find out how many scales are attached to this
01679        * dataset. H5DSget_num_scales returns an error if there are no
01680        * scales, so convert a negative return value to zero. */
01681       num_scales = H5DSget_num_scales(datasetid, 0);
01682       if (num_scales < 0)
01683          num_scales = 0;
01684    }
01685 
01686    /* Add a var to the linked list, and get its metadata,
01687     * unless this is one of those funny dimscales that are a
01688     * dimension in netCDF but not a variable. (Spooky!) */
01689    if (!dim_without_var)
01690       if ((retval = read_var(grp, datasetid, obj_name, ndims,
01691                              is_scale, num_scales, access_pid)))
01692          BAIL(retval);
01693    
01694    if (access_pid && H5Pclose(access_pid) < 0)
01695       BAIL2(retval);
01696 #ifdef EXTRA_TESTS
01697    num_plists--;
01698 #endif
01699    if (spaceid && H5Sclose(spaceid) < 0)
01700       BAIL2(retval);
01701 #ifdef EXTRA_TESTS
01702    num_spaces--;
01703 #endif
01704    return NC_NOERR;
01705 
01706   exit:
01707    if (access_pid && H5Pclose(access_pid) < 0)
01708       BAIL2(retval);
01709 #ifdef EXTRA_TESTS
01710    num_plists--;
01711 #endif
01712    if (datasetid && H5Dclose(datasetid) < 0)
01713       BAIL2(retval);
01714    if (spaceid && H5Sclose(spaceid) <0)
01715       BAIL2(retval);
01716 #ifdef EXTRA_TESTS
01717    num_spaces--;
01718 #endif
01719    return retval;
01720 }
01721 
01722 /* Given index, get the HDF5 name of an object and the class of the
01723  * object (group, type, dataset, etc.). This function will try to use
01724  * creation ordering, but if that fails it will use default
01725  * (i.e. alphabetical) ordering. (This is necessary to read existing
01726  * HDF5 archives without creation ordering). */
01727 /* static int */
01728 /* get_name_by_idx(NC_HDF5_FILE_INFO_T *h5, hid_t hdf_grpid, int i, */
01729 /*              int *obj_class, char *obj_name) */
01730 /* { */
01731 /*    H5O_info_t obj_info; */
01732 /*    H5_index_t idx_field = H5_INDEX_CRT_ORDER; */
01733 /*    ssize_t size; */
01734 /*    herr_t res; */
01735 
01736 /*    /\* These HDF5 macros prevent an HDF5 error message when a */
01737 /*     * non-creation-ordered HDF5 file is opened. *\/ */
01738 /*    H5E_BEGIN_TRY { */
01739 /*       res = H5Oget_info_by_idx(hdf_grpid, ".", H5_INDEX_CRT_ORDER, H5_ITER_INC, */
01740 /*                             i, &obj_info, H5P_DEFAULT); */
01741 /*    } H5E_END_TRY; */
01742    
01743 /*    /\* Creation ordering not available, so make sure this file is */
01744 /*     * opened for read-only access. This is a plain old HDF5 file being */
01745 /*     * read by netCDF-4. *\/ */
01746 /*    if (res < 0) */
01747 /*    { */
01748 /*       if (H5Oget_info_by_idx(hdf_grpid, ".", H5_INDEX_NAME, H5_ITER_INC, */
01749 /*                           i, &obj_info, H5P_DEFAULT) < 0) */
01750 /*       return NC_EHDFERR; */
01751 /*       if (!h5->no_write) */
01752 /*       return NC_ECANTWRITE; */
01753 /*       h5->ignore_creationorder = 1; */
01754 /*       idx_field = H5_INDEX_NAME; */
01755 /*    } */
01756 
01757 /*    *obj_class = obj_info.type; */
01758 /*    if ((size = H5Lget_name_by_idx(hdf_grpid, ".", idx_field, H5_ITER_INC, i, */
01759 /*                                NULL, 0, H5P_DEFAULT)) < 0) */
01760 /*       return NC_EHDFERR; */
01761 /*    if (size > NC_MAX_NAME) */
01762 /*       return NC_EMAXNAME; */
01763 /*    if (H5Lget_name_by_idx(hdf_grpid, ".", idx_field, H5_ITER_INC, i, */
01764 /*                        obj_name, size+1, H5P_DEFAULT) < 0) */
01765 /*       return NC_EHDFERR; */
01766 
01767 /*    LOG((4, "get_name_by_idx: encountered HDF5 object obj_name %s", obj_name)); */
01768 
01769 /*    return NC_NOERR; */
01770 /* } */
01771 
01772 #define USE_ITERATE_CODE
01773 #ifdef  USE_ITERATE_CODE
01774 
01775 static int
01776 nc4_rec_read_types_cb(hid_t grpid, const char *name, const H5L_info_t *info,
01777                       void *_op_data)
01778 {
01779     hid_t oid=-1;
01780     H5I_type_t otype=-1;
01781     char oname[NC_MAX_NAME + 1];
01782     NC_GRP_INFO_T *child_grp;
01783     NC_GRP_INFO_T *grp = (NC_GRP_INFO_T *) (_op_data);
01784     NC_HDF5_FILE_INFO_T *h5 = grp->file->nc4_info;
01785 
01786     /* Open this critter. */
01787     if ((oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0) 
01788         return H5_ITER_ERROR;
01789           
01790     if ((otype = H5Iget_type( oid ))<0) {
01791         H5Oclose(oid);
01792         return H5_ITER_ERROR;
01793     }
01794     H5Oclose(oid);
01795         
01796     strncpy(oname, name, NC_MAX_NAME);
01797            
01798     /* Deal with groups and types; ignore the rest. */
01799     if (otype == H5I_GROUP)
01800     {
01801         LOG((3, "found group %s", oname));
01802         if (nc4_grp_list_add(&(grp->children), h5->next_nc_grpid++, 
01803                              grp, grp->file, oname, &child_grp))
01804             return H5_ITER_ERROR;
01805                         
01806         if (nc4_rec_read_types(child_grp))
01807             return H5_ITER_ERROR;
01808     }
01809     else if (otype == H5I_DATATYPE)
01810     {
01811         LOG((3, "found datatype %s", oname));
01812         if (read_type(grp, oname))
01813             return H5_ITER_ERROR;
01814     }
01815           
01816     return (H5_ITER_CONT);
01817 }
01818 
01819 static int
01820 nc4_rec_read_types(NC_GRP_INFO_T *grp)
01821 {
01822     hsize_t idx=0;
01823     int res = 0;
01824     hid_t pid = 0;
01825     unsigned crt_order_flags = 0;
01826     NC_HDF5_FILE_INFO_T *h5 = grp->file->nc4_info;
01827 
01828     assert(grp && grp->name);
01829     LOG((3, "nc4_rec_read_types: grp->name %s", grp->name));
01830 
01831     /* Open this HDF5 group and retain its grpid. It will remain open
01832      * with HDF5 until this file is nc_closed. */
01833     if (!grp->hdf_grpid)
01834     {
01835         if (grp->parent)
01836         {
01837           if ((grp->hdf_grpid = H5Gopen2(grp->parent->hdf_grpid, 
01838                                         grp->name, H5P_DEFAULT)) < 0)
01839             return NC_EHDFERR;
01840         }
01841         else
01842         {
01843             if ((grp->hdf_grpid = H5Gopen2(grp->file->nc4_info->hdfid, 
01844                                            "/", H5P_DEFAULT)) < 0)
01845                 return NC_EHDFERR;
01846         }
01847     }
01848     assert(grp->hdf_grpid > 0);
01849 
01850     pid = H5Gget_create_plist(grp->hdf_grpid);
01851     H5Pget_link_creation_order(pid, &crt_order_flags); 
01852     H5Pclose(pid);
01853     
01854     crt_order_flags = crt_order_flags & H5_INDEX_CRT_ORDER;
01855         
01856     if (crt_order_flags == H5_INDEX_CRT_ORDER)
01857     {
01858         res = H5Literate(grp->hdf_grpid, H5_INDEX_CRT_ORDER, H5_ITER_INC, 
01859                          &idx, nc4_rec_read_types_cb, (void *)grp);
01860     } else
01861     {
01862         /* Without creation ordering, file must be read-only. */
01863         if (!idx && !h5->no_write)
01864             return NC_ECANTWRITE;
01865          
01866         res = H5Literate(grp->hdf_grpid, H5_INDEX_NAME, H5_ITER_INC, 
01867                          &idx, nc4_rec_read_types_cb, (void *)grp);
01868     }
01869     if (res<0)
01870         return NC_EHDFERR;
01871     return NC_NOERR; /* everything worked! */
01872 }
01873 
01874 static int
01875 nc4_rec_read_vars_cb(hid_t grpid, const char *name, const H5L_info_t *info,
01876                      void *_op_data)
01877 {
01878     hid_t oid=-1;
01879     H5I_type_t otype=-1;
01880     char oname[NC_MAX_NAME + 1];
01881     NC_GRP_INFO_T *child_grp;
01882     NC_GRP_INFO_T *grp = (NC_GRP_INFO_T *) (_op_data);
01883 #if 0
01884     NC_HDF5_FILE_INFO_T *h5 = grp->file->nc4_info;
01885 #endif
01886 
01887     memset(oname, 0, NC_MAX_NAME);
01888     /* Open this critter. */
01889     if ((oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0) 
01890         return H5_ITER_ERROR;
01891           
01892     if ((otype = H5Iget_type( oid ))<0) {
01893         H5Oclose(oid);
01894         return H5_ITER_ERROR;
01895     }
01896     H5Oclose(oid);
01897         
01898     strncpy(oname, name, NC_MAX_NAME);
01899         
01900     /* Deal with datasets. */
01901     switch(otype)
01902     {
01903     case H5I_GROUP:
01904         LOG((3, "re-encountering group %s", oname));
01905         
01906         /* The NC_GROUP_INFO_T for this group already exists. Find it. */
01907         for (child_grp = grp->children; child_grp; child_grp = child_grp->next)
01908             if (!strcmp(child_grp->name, oname))
01909                 break;
01910         if (!child_grp)
01911             return H5_ITER_ERROR;
01912 
01913         /* Recursively read the child group's vars. */
01914         if (nc4_rec_read_vars(child_grp))
01915             return H5_ITER_ERROR;
01916         break;
01917     case H5I_DATASET:
01918         LOG((3, "found dataset %s", oname));
01919 
01920         /* Learn all about this dataset, which may be a dimscale
01921          * (i.e. dimension metadata), or real data. */
01922         if (read_dataset(grp, oname))
01923             return H5_ITER_ERROR;
01924         break;
01925     case H5I_DATATYPE:
01926         LOG((3, "already handled type %s", oname));
01927         break;
01928     default:
01929         LOG((0, "Unknown object class %d in nc4_rec_read_vars!", otype));
01930     }
01931     return (H5_ITER_CONT);
01932 }
01933 
01934 static int
01935 nc4_rec_read_vars(NC_GRP_INFO_T *grp)
01936 {
01937     hsize_t idx = 0;
01938     int retval = NC_NOERR;
01939     int res = 0;
01940     hid_t pid = 0;
01941     unsigned crt_order_flags = 0;
01942     NC_HDF5_FILE_INFO_T *h5 = grp->file->nc4_info;
01943     
01944     assert(grp && grp->name && grp->hdf_grpid > 0);
01945     LOG((3, "nc4_rec_read_vars: grp->name %s", grp->name));
01946 
01947     pid = H5Gget_create_plist(grp->hdf_grpid);
01948     H5Pget_link_creation_order(pid, &crt_order_flags); 
01949     H5Pclose(pid);
01950     
01951     crt_order_flags = crt_order_flags & H5_INDEX_CRT_ORDER;
01952 
01953     if (crt_order_flags == H5_INDEX_CRT_ORDER)
01954     {
01955         res = H5Literate(grp->hdf_grpid, H5_INDEX_CRT_ORDER, H5_ITER_INC, 
01956                          &idx, nc4_rec_read_vars_cb, (void *)grp);
01957     } else
01958     {
01959         /* Without creation ordering, file must be read-only. */
01960         if (!idx && !h5->no_write)
01961             return NC_ECANTWRITE;
01962          
01963         res = H5Literate(grp->hdf_grpid, H5_INDEX_NAME, H5_ITER_INC, 
01964                          &idx, nc4_rec_read_vars_cb, (void *)grp);
01965     }
01966     if (res<0)
01967         return NC_EHDFERR;
01968    
01969     /* Scan the group for global (i.e. group-level) attributes. */
01970     if ((retval = read_grp_atts(grp)))
01971         return retval;
01972     
01973     return NC_NOERR; /* everything worked! */
01974 }
01975 
01976 #else
01977 
01982 struct nc_hdf5_link_info 
01983 {
01984    char name[NC_MAX_NAME + 1];
01985    H5I_type_t obj_type;   
01986 };   
01987 
01988 /* This is a callback function for H5Literate(). 
01989 
01990 The parameters of this callback function have the following values or
01991 meanings:
01992 
01993 g_id Group that serves as root of the iteration; same value as the
01994 H5Lvisit group_id parameter
01995 
01996 name Name of link, relative to g_id, being examined at current step of
01997 the iteration
01998 
01999 info H5L_info_t struct containing information regarding that link
02000 
02001 op_data User-defined pointer to data required by the application in
02002 processing the link; a pass-through of the op_data pointer provided
02003 with the H5Lvisit function call
02004 
02005 */
02006 static herr_t
02007 visit_link(hid_t g_id, const char *name, const H5L_info_t *info, 
02008            void *op_data)  
02009 {
02010    /* A positive return value causes the visit iterator to immediately
02011     * return that positive value, indicating short-circuit
02012     * success. The iterator can be restarted at the next group
02013     * member. */
02014    int ret = 1;
02015    hid_t id;
02016 
02017    /* Get the name, truncating at NC_MAX_NAME. */
02018    strncpy(((struct nc_hdf5_link_info *)op_data)->name, name, 
02019            NC_MAX_NAME);
02020    
02021    /* Open this critter. */
02022    if ((id = H5Oopen_by_addr(g_id, info->u.address)) < 0) 
02023       return NC_EHDFERR;
02024    
02025    /* Is this critter a group, type, data, attribute, or what? */
02026    if ((((struct nc_hdf5_link_info *)op_data)->obj_type = H5Iget_type(id)) < 0)
02027       ret = NC_EHDFERR;
02028 
02029    /* Close the critter to release resouces. */
02030    if (H5Oclose(id) < 0)
02031       return NC_EHDFERR;
02032    
02033    return ret;
02034 }
02035 
02036 /* Iterate over one link in the group at a time, returning
02037  * link_info. The creation_ordering and idx pointers keep track of
02038  * whether creation ordering works and the most recently examined
02039  * link. */
02040 static int
02041 nc4_iterate_link(int *ordering_checked, int *creation_ordering, 
02042                  hid_t grpid, hsize_t *idx, struct nc_hdf5_link_info *link_info)
02043 {
02044    int res = 0;
02045 
02046    if (*creation_ordering)
02047    {
02048       /* These HDF5 macros prevent an HDF5 error message when a
02049        * non-creation-ordered HDF5 file is opened. */
02050       H5E_BEGIN_TRY {
02051          res = H5Literate(grpid, H5_INDEX_CRT_ORDER, H5_ITER_INC, 
02052                           idx, visit_link, (void *)link_info);
02053          if (res < 0 && *ordering_checked)
02054             return NC_EHDFERR;
02055       } H5E_END_TRY;
02056    }
02057 
02058    if (!*creation_ordering || res < 0)
02059    {
02060       if (H5Literate(grpid, H5_INDEX_NAME, H5_ITER_INC, idx, 
02061                      visit_link, link_info) != 1)
02062          return NC_EHDFERR;
02063       /* If it didn't work with creation ordering, but did without,
02064        * then we don't have creation ordering. */
02065       *creation_ordering = 0;
02066    }
02067    
02068    *ordering_checked = 1;
02069    return NC_NOERR;
02070 }
02071 
02072 /* Recursively open groups and read types. */
02073 int
02074 nc4_rec_read_types(NC_GRP_INFO_T *grp)
02075 {
02076    hsize_t num_obj, i;
02077    NC_HDF5_FILE_INFO_T *h5 = grp->file->nc4_info;
02078    NC_GRP_INFO_T *child_grp;
02079    hsize_t idx = 0;
02080    struct nc_hdf5_link_info link_info;
02081    int ordering_checked = 0;
02082    int creation_ordering = 1; /* Assume we have it. */
02083    int retval = NC_NOERR;
02084 
02085    assert(grp && grp->name);
02086    LOG((3, "nc4_rec_read_types: grp->name %s", grp->name));
02087 
02088    /* Open this HDF5 group and retain its grpid. It will remain open
02089     * with HDF5 until this file is nc_closed. */
02090    if (!grp->hdf_grpid)
02091    {
02092       if (grp->parent)
02093       {
02094          if ((grp->hdf_grpid = H5Gopen2(grp->parent->hdf_grpid, 
02095                                         grp->name, H5P_DEFAULT)) < 0)
02096             return NC_EHDFERR;
02097       }
02098       else
02099       {
02100          if ((grp->hdf_grpid = H5Gopen2(grp->file->nc4_info->hdfid, 
02101                                         "/", H5P_DEFAULT)) < 0)
02102             return NC_EHDFERR;
02103       }
02104    }
02105    assert(grp->hdf_grpid > 0);
02106 
02107    /* How many objects in this group? */
02108    if (H5Gget_num_objs(grp->hdf_grpid, &num_obj) < 0)
02109       return NC_EVARMETA;
02110 
02111    /* For each object in the group... */
02112    for (i = 0; i < num_obj; i++)
02113    {
02114       if ((retval = nc4_iterate_link(&ordering_checked, &creation_ordering, 
02115                                      grp->hdf_grpid, &idx, &link_info)))
02116          return retval;
02117 
02118       /* Without creation ordering, file must be read-only. */
02119       if (!i && !creation_ordering && !h5->no_write)
02120          return NC_ECANTWRITE;
02121 
02122       /* Deal with groups and types; ignore the rest. */
02123       if (link_info.obj_type == H5I_GROUP)
02124       {
02125          LOG((3, "found group %s", link_info.name));
02126          if ((retval = nc4_grp_list_add(&(grp->children), h5->next_nc_grpid++, 
02127                                         grp, grp->file, link_info.name, &child_grp)))
02128             return retval;
02129          if ((retval =  nc4_rec_read_types(child_grp)))
02130             return retval;
02131       }
02132       else if (link_info.obj_type == H5I_DATATYPE)
02133       {
02134          LOG((3, "found datatype %s", link_info.name));
02135          if ((retval = read_type(grp, link_info.name)))
02136             return retval;
02137       }
02138    }
02139 
02140    return NC_NOERR; /* everything worked! */
02141 }
02142 
02143 /* This function recursively reads all the var and attribute metadata
02144    in a HDF5 group, and creates and fills in the netCDF-4 global
02145    metadata structure. */
02146 int
02147 nc4_rec_read_vars(NC_GRP_INFO_T *grp)
02148 {
02149    hsize_t num_obj, i;
02150    NC_GRP_INFO_T *child_grp;
02151    struct nc_hdf5_link_info link_info;
02152    hsize_t idx = 0;
02153    int ordering_checked = 0;
02154    int creation_ordering = 1; /* Assume we have it. */
02155    int retval = NC_NOERR;
02156 
02157    assert(grp && grp->name && grp->hdf_grpid > 0);
02158    LOG((3, "nc4_rec_read_vars: grp->name %s", grp->name));
02159 
02160    /* How many objects in this group? */
02161    if (H5Gget_num_objs(grp->hdf_grpid, &num_obj) < 0)
02162       return NC_EVARMETA;
02163 
02164    /* For each object in the group... */
02165    for (i = 0; i < num_obj; i++)
02166    {
02167       if ((retval = nc4_iterate_link(&ordering_checked, &creation_ordering, 
02168                                      grp->hdf_grpid, &idx, &link_info)))
02169          return retval;
02170       
02171       /* Deal with datasets. */
02172       switch(link_info.obj_type)
02173       {
02174          case H5I_GROUP:
02175             LOG((3, "re-encountering group %s", link_info.name));
02176 
02177             /* The NC_GROUP_INFO_T for this group already exists. Find it. */
02178             for (child_grp = grp->children; child_grp; child_grp = child_grp->next)
02179                if (!strcmp(child_grp->name, link_info.name))
02180                   break;
02181             if (!child_grp)
02182                return NC_EHDFERR;
02183 
02184             /* Recursively read the child group's vars. */
02185             if ((retval =  nc4_rec_read_vars(child_grp)))
02186                return retval;
02187             break;
02188          case H5I_DATASET:
02189             LOG((3, "found dataset %s", link_info.name));
02190 
02191             /* Learn all about this dataset, which may be a dimscale
02192              * (i.e. dimension metadata), or real data. */
02193             if ((retval = read_dataset(grp, link_info.name)))
02194                return retval;
02195             break;
02196          case H5I_DATATYPE:
02197             LOG((3, "already handled type %s", link_info.name));
02198             break;
02199          default:
02200             LOG((0, "Unknown object class %d in nc4_rec_read_vars!", 
02201                  link_info.obj_type));
02202       }
02203    }
02204 
02205    /* Scan the group for global (i.e. group-level) attributes. */
02206    if ((retval = read_grp_atts(grp)))
02207       return retval;
02208 
02209    return NC_NOERR; /* everything worked! */
02210 }
02211 #endif
02212 
02213 /* Open a netcdf-4 file. Things have already been kicked off in
02214  * ncfunc.c in nc_open, but here the netCDF-4 part of opening a file
02215  * is handled. */
02216 static int
02217 nc4_open_file(const char *path, int mode, MPI_Comm comm,
02218               MPI_Info info, NC_FILE_INFO_T *nc)
02219 {
02220    hid_t fapl_id = H5P_DEFAULT;
02221    unsigned flags = (mode & NC_WRITE) ? 
02222       H5F_ACC_RDWR : H5F_ACC_RDONLY;
02223    int retval;
02224 
02225    LOG((3, "nc4_open_file: path %s mode %d", path, mode));
02226    assert(path && nc);
02227 
02228    /* Stop diskless open in its tracks */
02229    if(mode & NC_DISKLESS)
02230         return NC_EDISKLESS;
02231 
02232    /* Add necessary structs to hold netcdf-4 file data. */
02233    if ((retval = nc4_nc4f_list_add(nc, path, mode)))
02234       BAIL(retval);
02235    assert(nc->nc4_info && nc->nc4_info->root_grp);
02236    
02237    /* Need this access plist to control how HDF5 handles open onjects
02238     * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to
02239     * fail if there are any open objects in the file. */
02240    if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
02241       BAIL(NC_EHDFERR);
02242 #ifdef EXTRA_TESTS
02243    num_plists++;
02244 #endif      
02245 #ifdef EXTRA_TESTS
02246    if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI)) 
02247       BAIL(NC_EHDFERR);
02248 #else
02249    if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_STRONG))
02250       BAIL(NC_EHDFERR);
02251 #endif
02252 
02253 #ifdef USE_PARALLEL
02254    /* If this is a parallel file create, set up the file creation
02255       property list. */
02256    if (mode & NC_MPIIO || mode & NC_MPIPOSIX)
02257    {
02258       nc->nc4_info->parallel++;
02259       if (mode & NC_MPIIO)  /* MPI/IO */
02260       {
02261          LOG((4, "opening parallel file with MPI/IO"));
02262          if (H5Pset_fapl_mpio(fapl_id, comm, info) < 0)
02263             BAIL(NC_EPARINIT);
02264       }
02265       else /* MPI/POSIX */
02266       {
02267          LOG((4, "opening parallel file with MPI/posix"));
02268          if (H5Pset_fapl_mpiposix(fapl_id, comm, 0) < 0)
02269             BAIL(NC_EPARINIT);
02270       }
02271    }
02272 #else /* only set cache for non-parallel. */
02273    if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size, 
02274                     nc4_chunk_cache_preemption) < 0)
02275       BAIL(NC_EHDFERR);
02276    LOG((4, "nc4_open_file: set HDF raw chunk cache to size %d nelems %d preemption %f", 
02277         nc4_chunk_cache_size, nc4_chunk_cache_nelems, nc4_chunk_cache_preemption));
02278 #endif /* USE_PARALLEL */
02279    
02280    /* The NetCDF-3.x prototype contains an mode option NC_SHARE for
02281       multiple processes accessing the dataset concurrently.  As there
02282       is no HDF5 equivalent, NC_SHARE is treated as NC_NOWRITE. */
02283    if ((nc->nc4_info->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
02284       BAIL(NC_EHDFERR);
02285 
02286    /* Does the mode specify that this file is read-only? */
02287    if ((mode & NC_WRITE) == 0)
02288       nc->nc4_info->no_write++;
02289 
02290    /* Now read in all the metadata. Some types and dimscale
02291     * information may be difficult to resolve here, if, for example, a
02292     * dataset of user-defined type is encountered before the
02293     * definition of that type. */
02294    if ((retval = nc4_rec_read_types(nc->nc4_info->root_grp)))
02295       BAIL(retval);
02296    if ((retval = nc4_rec_read_vars(nc->nc4_info->root_grp)))
02297       BAIL(retval);
02298 
02299    /* Now figure out which netCDF dims are indicated by the dimscale
02300     * information. */
02301    if ((retval = nc4_rec_match_dimscales(nc->nc4_info->root_grp)))
02302       BAIL(retval);
02303 
02304 #ifdef LOGGING
02305    /* This will print out the names, types, lens, etc of the vars and
02306       atts in the file, if the logging level is 2 or greater. */ 
02307    log_metadata_nc(nc);
02308 #endif
02309 
02310    /* Close the property list. */ 
02311    if (H5Pclose(fapl_id) < 0)
02312       BAIL(NC_EHDFERR);
02313 #ifdef EXTRA_TESTS
02314    num_plists--;
02315 #endif
02316 
02317    return NC_NOERR;
02318 
02319   exit:
02320    if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id);
02321 #ifdef EXTRA_TESTS
02322    num_plists--;
02323 #endif
02324    if (nc->nc4_info->hdfid > 0) H5Fclose(nc->nc4_info->hdfid);
02325    if (nc->nc4_info) free(nc->nc4_info);
02326    return retval;
02327 }
02328 
02329 /* Given an HDF4 type, set a pointer to netcdf type. */
02330 #ifdef USE_HDF4   
02331 static int
02332 get_netcdf_type_from_hdf4(NC_HDF5_FILE_INFO_T *h5, int32 hdf4_typeid, 
02333                           nc_type *xtype, NC_TYPE_INFO_T *type_info)
02334 {
02335    int t;
02336    assert(h5 && xtype);
02337 
02338    switch(hdf4_typeid)
02339    {
02340       case DFNT_CHAR:
02341          *xtype = NC_CHAR;
02342          t = 0;
02343          break;
02344       case DFNT_UCHAR:
02345       case DFNT_UINT8:
02346          *xtype = NC_UBYTE;
02347          t = 6;
02348          break;
02349       case DFNT_INT8:
02350          *xtype = NC_BYTE;
02351          t = 1;
02352          break;
02353       case DFNT_INT16:
02354          *xtype = NC_SHORT;
02355          t = 2;
02356          break;
02357       case DFNT_UINT16:
02358          *xtype = NC_USHORT;
02359          t = 7;
02360          break;
02361       case DFNT_INT32:
02362          *xtype = NC_INT;
02363          t = 3;
02364          break;
02365       case DFNT_UINT32:
02366          *xtype = NC_UINT;
02367          t = 8;
02368          break;
02369       case DFNT_FLOAT32:
02370          *xtype = NC_FLOAT;
02371          t = 4;
02372          break;
02373       case DFNT_FLOAT64:
02374          *xtype = NC_DOUBLE;
02375          t = 5;
02376          break;
02377       default:
02378          *xtype = NC_NAT;
02379          return NC_EBADTYPID;
02380    }
02381 
02382    if (type_info)
02383    {
02384       if (hdf4_typeid == DFNT_FLOAT32 || hdf4_typeid == DFNT_FLOAT64)
02385          type_info->class = H5T_FLOAT;
02386       else if (hdf4_typeid == DFNT_CHAR)
02387          type_info->class = H5T_STRING;
02388       else
02389          type_info->class = H5T_INTEGER;
02390       type_info->endianness = NC_ENDIAN_BIG;
02391       type_info->nc_typeid = *xtype;
02392       if (type_info->name)
02393          free(type_info->name);
02394       if (!(type_info->name = malloc((strlen(nc_type_name[t]) + 1) * sizeof(char))))
02395          return NC_ENOMEM;
02396       strcpy(type_info->name, nc_type_name[t]);      
02397    }
02398 
02399    return NC_NOERR;
02400 }
02401 #endif /* USE_HDF4 */
02402 
02403 /* Open a HDF4 file. Things have already been kicked off in nc_open,
02404  * but here the netCDF-4 part of opening a file is handled. */
02405 static int
02406 nc4_open_hdf4_file(const char *path, int mode, NC_FILE_INFO_T *nc)
02407 {
02408 #ifdef USE_HDF4
02409    NC_HDF5_FILE_INFO_T *h5;
02410    NC_GRP_INFO_T *grp;
02411    NC_ATT_INFO_T *att;
02412    NC_VAR_INFO_T *var;
02413    int32 num_datasets, num_gatts;
02414    int32 rank;
02415    int v, d, a;
02416    int retval;
02417 
02418    LOG((3, "nc4_open_hdf4_file: path %s mode %d", path, mode));
02419    assert(path && nc);
02420 
02421    /* Must be read-only access to hdf4 files. */
02422    if (mode & NC_WRITE)
02423       return NC_EINVAL;
02424 
02425    /* Add necessary structs to hold netcdf-4 file data. */
02426    if ((retval = nc4_nc4f_list_add(nc, path, mode)))
02427       return retval;
02428    assert(nc->nc4_info && nc->nc4_info->root_grp);
02429    h5 = nc->nc4_info;
02430    h5->hdf4++;
02431    grp = h5->root_grp;
02432    h5->no_write++;
02433 
02434    /* Open the file and initialize SD interface. */
02435    if ((h5->sdid = SDstart(path, DFACC_READ)) == FAIL)
02436       return NC_EHDFERR;
02437 
02438    /* Learn how many datasets and global atts we have. */
02439    if (SDfileinfo(h5->sdid, &num_datasets, &num_gatts))
02440       return NC_EHDFERR;
02441 
02442    /* Read the atts. */
02443    for (a = 0; a < num_gatts; a++)
02444    {
02445       int32 att_data_type, att_count;
02446       size_t att_type_size;
02447 
02448       /* Add to the end of the list of atts for this var. */
02449       if ((retval = nc4_att_list_add(&h5->root_grp->att)))
02450          return retval;
02451       for (att = h5->root_grp->att; att->next; att = att->next)
02452          ;
02453       att->attnum = grp->natts++;
02454       att->created++;
02455 
02456       /* Learn about this attribute. */
02457       if (!(att->name = malloc(NC_MAX_HDF4_NAME * sizeof(char))))
02458          return NC_ENOMEM;
02459       if (SDattrinfo(h5->sdid, a, att->name, &att_data_type, &att_count)) 
02460          return NC_EATTMETA;
02461       if ((retval = get_netcdf_type_from_hdf4(h5, att_data_type, 
02462                                               &att->xtype, NULL)))
02463          return retval;
02464       att->len = att_count;
02465 
02466       /* Allocate memory to hold the data. */
02467       if ((retval = nc4_get_typelen_mem(h5, att->xtype, 0, &att_type_size)))
02468          return retval;
02469       if (!(att->data = malloc(att_type_size * att->len)))
02470          return NC_ENOMEM;
02471 
02472       /* Read the data. */
02473       if (SDreadattr(h5->sdid, a, att->data)) 
02474          return NC_EHDFERR;
02475    }
02476 
02477    /* Read each dataset. */
02478    for (v = 0; v < num_datasets; v++)
02479    {
02480       int32 data_type, num_atts;
02481       int32 dimsize[NC_MAX_DIMS];
02482       size_t var_type_size;
02483       int a;
02484 
02485       /* Add a variable to the end of the group's var list. */
02486       if ((retval = nc4_var_list_add(&grp->var, &var)))
02487          return retval;
02488       var->varid = grp->nvars++;
02489       var->created = 1;
02490       var->written_to = 1;
02491             
02492       /* Open this dataset in HDF4 file. */
02493       if ((var->sdsid = SDselect(h5->sdid, v)) == FAIL)
02494          return NC_EVARMETA;
02495 
02496       /* Get shape, name, type, and attribute info about this dataset. */
02497       if (!(var->name = malloc(NC_MAX_HDF4_NAME + 1)))
02498          return NC_ENOMEM;
02499       if (SDgetinfo(var->sdsid, var->name, &rank, dimsize, &data_type, &num_atts))
02500          return NC_EVARMETA;
02501       var->ndims = rank;
02502       var->hdf4_data_type = data_type;
02503 
02504       /* Fill special type_info struct for variable type information. */
02505       if (!(var->type_info = calloc(1, sizeof(NC_TYPE_INFO_T))))
02506          return NC_ENOMEM;
02507       if ((retval = get_netcdf_type_from_hdf4(h5, data_type, &var->xtype, var->type_info)))
02508          return retval;
02509       if ((retval = nc4_get_typelen_mem(h5, var->xtype, 0, &var_type_size)))
02510          return retval;
02511       var->type_info->size = var_type_size;
02512       LOG((3, "reading HDF4 dataset %s, rank %d netCDF type %d", var->name, 
02513            rank, var->xtype));
02514 
02515       /* Get the fill value. */
02516       if (!(var->fill_value = malloc(var_type_size)))
02517          return NC_ENOMEM;
02518       if (SDgetfillvalue(var->sdsid, var->fill_value))
02519       {
02520          /* Whoops! No fill value! */
02521          free(var->fill_value);
02522          var->fill_value = NULL;
02523       }
02524 
02525       /* Allocate storage for dimension info in this variable. */
02526       if (var->ndims)
02527       {
02528          if (!(var->dim = malloc(sizeof(NC_DIM_INFO_T *) * var->ndims)))
02529             return NC_ENOMEM;
02530          if (!(var->dimids = malloc(sizeof(int) * var->ndims)))
02531             return NC_ENOMEM;
02532       }
02533 
02534       /* Find its dimensions. */
02535       for (d = 0; d < var->ndims; d++)
02536       {
02537          int32 dimid, dim_len, dim_data_type, dim_num_attrs;
02538          char dim_name[NC_MAX_NAME + 1];
02539          NC_DIM_INFO_T *dim;
02540 
02541          if ((dimid = SDgetdimid(var->sdsid, d)) == FAIL)
02542             return NC_EDIMMETA;
02543          if (SDdiminfo(dimid, dim_name, &dim_len, &dim_data_type, 
02544                        &dim_num_attrs))
02545             return NC_EDIMMETA;
02546 
02547          /* Do we already have this dimension? HDF4 explicitly uses
02548           * the name to tell. */
02549          for (dim = grp->dim; dim; dim = dim->next)
02550             if (!strcmp(dim->name, dim_name))
02551                break;
02552 
02553          /* If we didn't find this dimension, add one. */
02554          if (!dim)
02555          {
02556             LOG((4, "adding dimension %s for HDF4 dataset %s", 
02557                  dim_name, var->name));
02558             if ((retval = nc4_dim_list_add(&grp->dim)))
02559                return retval;
02560             grp->ndims++;
02561             dim = grp->dim;
02562             dim->dimid = grp->file->nc4_info->next_dimid++;
02563             if (strlen(dim_name) > NC_MAX_HDF4_NAME)
02564                return NC_EMAXNAME;
02565             if (!(dim->name = malloc(NC_MAX_HDF4_NAME + 1)))
02566                return NC_ENOMEM;
02567             strcpy(dim->name, dim_name);
02568             if (dim_len)
02569                dim->len = dim_len;
02570             else
02571                dim->len = *dimsize;
02572          }
02573 
02574          /* Tell the variable the id of this dimension. */
02575          var->dimids[d] = dim->dimid;
02576       }
02577 
02578       /* Read the atts. */
02579       for (a = 0; a < num_atts; a++)
02580       {
02581          int32 att_data_type, att_count;
02582          size_t att_type_size;
02583 
02584          /* Add to the end of the list of atts for this var. */
02585          if ((retval = nc4_att_list_add(&var->att)))
02586             return retval;
02587          for (att = var->att; att->next; att = att->next)
02588             ;
02589          att->attnum = var->natts++;
02590          att->created++;
02591 
02592          /* Learn about this attribute. */
02593          if (!(att->name = malloc(NC_MAX_HDF4_NAME * sizeof(char))))
02594             return NC_ENOMEM;
02595          if (SDattrinfo(var->sdsid, a, att->name, &att_data_type, &att_count)) 
02596             return NC_EATTMETA;
02597          if ((retval = get_netcdf_type_from_hdf4(h5, att_data_type, 
02598                                                  &att->xtype, NULL)))
02599             return retval;
02600          att->len = att_count;
02601 
02602          /* Allocate memory to hold the data. */
02603          if ((retval = nc4_get_typelen_mem(h5, att->xtype, 0, &att_type_size)))
02604             return retval;
02605          if (!(att->data = malloc(att_type_size * att->len)))
02606             return NC_ENOMEM;
02607 
02608          /* Read the data. */
02609          if (SDreadattr(var->sdsid, a, att->data)) 
02610             return NC_EHDFERR;
02611       }
02612    } /* next var */
02613 
02614 #ifdef LOGGING
02615    /* This will print out the names, types, lens, etc of the vars and
02616       atts in the file, if the logging level is 2 or greater. */ 
02617    log_metadata_nc(h5->root_grp->file);
02618 #endif
02619    return NC_NOERR;   
02620 #endif /* USE_HDF4 */
02621    return NC_ENOTBUILT;
02622 }
02623 
02624 int
02625 NC4_open(const char *path, int mode, int basepe, size_t *chunksizehintp, 
02626          int use_parallel, void *mpidata, NC_Dispatch *dispatch, NC **ncpp)
02627 {
02628    int hdf_file = 0;
02629    NC_FILE_INFO_T *nc_file;
02630 #ifdef USE_PARALLEL
02631    MPI_Comm comm = 0; 
02632    MPI_Info info = 0;   
02633 #else
02634    int comm = 0, info = 0;
02635 #endif /* USE_PARALLEL */
02636    int res;
02637 
02638    assert(ncpp && path);
02639 
02640    LOG((1, "nc_open_file: path %s mode %d comm %d info %d", 
02641         path, mode, comm, info));
02642 
02643 #ifdef USE_PARALLEL
02644    if (mpidata) 
02645    { 
02646       NC_MPI_INFO *nmi = (NC_MPI_INFO *)mpidata; 
02647       comm = nmi->comm; info = nmi->info; 
02648    }
02649 #endif /* USE_PARALLEL */
02650     
02651    /* If this is our first file, turn off HDF5 error messages. */
02652    if (virgin)
02653    {
02654       if (H5Eset_auto(NULL, NULL) < 0)
02655          LOG((0, "Couldn't turn off HDF5 error messages!"));
02656       LOG((1, "HDF5 error messages turned off!"));
02657       virgin = 0;
02658    }
02659 
02660    /* Check the mode for validity. First make sure only certain bits
02661     * are turned on. Also MPI I/O and MPI POSIX cannot both be
02662     * selected at once. */
02663    if (mode & ~(NC_WRITE | NC_SHARE | NC_MPIIO | NC_MPIPOSIX | 
02664                 NC_PNETCDF | NC_NOCLOBBER | NC_NETCDF4 | NC_CLASSIC_MODEL) ||
02665        (mode & NC_MPIIO && mode & NC_MPIPOSIX))
02666       return NC_EINVAL;
02667 
02668    /* Figure out if this is a hdf4 or hdf5 file. */
02669    if ((res = nc_check_for_hdf(path, use_parallel, comm, info, &hdf_file)))
02670       return res;
02671 
02672    /* Allocate the storage for this file info struct, and fill it with
02673       zeros. */
02674    if ((res = nc4_file_list_add(&nc_file,dispatch)))
02675       return res;
02676 
02677    /* Depending on the type of file, open it. */
02678    if (hdf_file == NC_HDF5_FILE)
02679    {
02680       nc_file->int_ncid = nc_file->ext_ncid;
02681       res = nc4_open_file(path, mode, comm, info, nc_file);
02682    }
02683    else if (hdf_file == NC_HDF4_FILE)
02684    {
02685       nc_file->int_ncid = nc_file->ext_ncid;
02686       res = nc4_open_hdf4_file(path, mode, nc_file);
02687    }
02688 #ifdef USE_PNETCDF
02689    else if (mode & NC_PNETCDF)
02690    {
02691       int pnetcdf_nvars, i;
02692 
02693       res = ncmpi_open(comm, path, mode, info, &(nc_file->int_ncid));
02694       nc_file->pnetcdf_file++;
02695 
02696       /* Default to independent access, like netCDF-4/HDF5 files. */
02697       if (!res)
02698          res = ncmpi_begin_indep_data(nc_file->int_ncid);
02699 
02700       /* I need to keep track of the ndims of each var to translate
02701        * start, count, and stride arrays to MPI_Offset type. */
02702       if (!res)
02703       {
02704          res = ncmpi_inq_nvars(nc_file->int_ncid, &pnetcdf_nvars);
02705          for (i = 0; i < pnetcdf_nvars; i++)
02706             res = ncmpi_inq_varndims(nc_file->int_ncid, i, 
02707                                      &(nc_file->pnetcdf_ndims[i]));
02708 
02709       }
02710    }
02711 #endif /* USE_PNETCDF */
02712    else /* netcdf */
02713    {
02714       assert(0);
02715    }
02716 
02717    /* If it succeeds, pass back the new ncid. Otherwise, remove this
02718       file from the list. */
02719    if (res)
02720    {
02721       if(nc_file != NULL) nc4_file_list_del(nc_file);
02722    }
02723    else
02724    {
02725       *ncpp = (NC*)nc_file;
02726    }
02727 
02728    return res;
02729 }
02730 
02731 /* Unfortunately HDF only allows specification of fill value only when
02732    a dataset is created. Whereas in netcdf, you first create the
02733    variable and then (optionally) specify the fill value. To
02734    accomplish this in HDF5 I have to delete the dataset, and recreate
02735    it, with the fill value specified. */
02736 int 
02737 NC4_set_fill(int ncid, int fillmode, int *old_modep)
02738 {
02739    NC_FILE_INFO_T *nc;
02740  
02741    LOG((2, "nc_set_fill: ncid 0x%x fillmode %d", ncid, fillmode));
02742 
02743    if (!(nc = nc4_find_nc_file(ncid)))
02744       return NC_EBADID;
02745 
02746    /* Is this a netcdf-3 file? */
02747    assert(nc->nc4_info);
02748 
02749    /* Trying to set fill on a read-only file? You sicken me! */
02750    if (nc->nc4_info->no_write)
02751       return NC_EPERM;
02752 
02753    /* Did you pass me some weird fillmode? */
02754    if (fillmode != NC_FILL && fillmode != NC_NOFILL)
02755       return NC_EINVAL;
02756 
02757    /* If the user wants to know, tell him what the old mode was. */
02758    if (old_modep)
02759       *old_modep = nc->nc4_info->fill_mode;
02760 
02761    nc->nc4_info->fill_mode = fillmode;
02762 
02763    return NC_NOERR;
02764 }
02765 
02766 /* Put the file back in redef mode. This is done automatically for
02767  * netcdf-4 files, if the user forgets. */
02768 int
02769 NC4_redef(int ncid)
02770 {
02771    NC_FILE_INFO_T *nc;
02772 
02773    LOG((1, "nc_redef: ncid 0x%x", ncid));
02774 
02775    /* Find this file's metadata. */
02776    if (!(nc = nc4_find_nc_file(ncid)))
02777       return NC_EBADID;
02778 
02779 #ifdef USE_PNETCDF
02780    /* Take care of files created/opened with parallel-netcdf library. */
02781    if (nc->pnetcdf_file)
02782       return ncmpi_redef(nc->int_ncid);
02783 #endif /* USE_PNETCDF */
02784 
02785    /* Handle netcdf-3 files. */
02786    assert(nc->nc4_info);
02787 
02788    /* If we're already in define mode, return an error. */
02789    if (nc->nc4_info->flags & NC_INDEF)
02790       return NC_EINDEFINE;
02791 
02792    /* If the file is read-only, return an error. */
02793    if (nc->nc4_info->no_write)
02794       return NC_EPERM;
02795 
02796    /* Set define mode. */
02797    nc->nc4_info->flags |= NC_INDEF;
02798 
02799    /* For nc_abort, we need to remember if we're in define mode as a
02800       redef. */
02801    nc->nc4_info->redef++;
02802 
02803    return NC_NOERR;
02804 }
02805 
02806 /* For netcdf-4 files, this just calls nc_enddef, ignoring the extra
02807  * parameters. */
02808 int
02809 NC4__enddef(int ncid, size_t h_minfree, size_t v_align,
02810             size_t v_minfree, size_t r_align)
02811 {
02812    if (!nc4_find_nc_file(ncid))
02813       return NC_EBADID;
02814 
02815    return NC4_enddef(ncid);
02816 }
02817 
02818 /* Take the file out of define mode. This is called automatically for
02819  * netcdf-4 files, if the user forgets. */
02820 static int NC4_enddef(int ncid)
02821 {
02822    NC_FILE_INFO_T *nc;
02823 
02824    LOG((1, "nc_enddef: ncid 0x%x", ncid));
02825 
02826    if (!(nc = nc4_find_nc_file(ncid)))
02827       return NC_EBADID;
02828 
02829 #ifdef USE_PNETCDF
02830    if (nc->pnetcdf_file)
02831    {
02832       int res;
02833       res = ncmpi_enddef(nc->int_ncid);
02834       if (!res)
02835       {
02836          if (nc->pnetcdf_access_mode == NC_INDEPENDENT)
02837             res = ncmpi_begin_indep_data(nc->int_ncid);
02838       }
02839       return res;
02840    }
02841 #endif /* USE_PNETCDF */
02842 
02843    /* Take care of netcdf-3 files. */
02844    assert(nc->nc4_info);
02845 
02846    return nc4_enddef_netcdf4_file(nc->nc4_info);
02847 }
02848 
02849 /* This function will write all changed metadata, and (someday) reread
02850  * all metadata from the file. */
02851 static int
02852 sync_netcdf4_file(NC_HDF5_FILE_INFO_T *h5)
02853 {
02854    int retval;
02855 
02856    assert(h5);
02857    LOG((3, "sync_netcdf4_file"));
02858 
02859    /* If we're in define mode, that's an error, for strict nc3 rules,
02860     * otherwise, end define mode. */
02861    if (h5->flags & NC_INDEF)
02862    {
02863       if (h5->cmode & NC_CLASSIC_MODEL)
02864          return NC_EINDEFINE;
02865 
02866       /* Turn define mode off. */
02867       h5->flags ^= NC_INDEF;
02868       
02869       /* Redef mode needs to be tracked seperately for nc_abort. */
02870       h5->redef = 0;
02871    }
02872 
02873 #ifdef LOGGING
02874    /* This will print out the names, types, lens, etc of the vars and
02875       atts in the file, if the logging level is 2 or greater. */ 
02876    log_metadata_nc(h5->root_grp->file);
02877 #endif
02878 
02879    /* Write any metadata that has changed. */
02880    if (!(h5->cmode & NC_NOWRITE))
02881    {
02882       if ((retval = nc4_rec_write_types(h5->root_grp)))
02883          return retval;
02884       if ((retval = nc4_rec_write_metadata(h5->root_grp)))
02885          return retval;
02886    }
02887 
02888    H5Fflush(h5->hdfid, H5F_SCOPE_GLOBAL);
02889 
02890    /* Reread all the metadata. */
02891    /*if ((retval = nc4_rec_read_metadata(grp)))
02892      return retval;*/
02893 
02894    return retval;
02895 }
02896 
02897 /* Flushes all buffers associated with the file, after writing all
02898    changed metadata. This may only be called in data mode. */
02899 int
02900 NC4_sync(int ncid)
02901 {
02902    NC_FILE_INFO_T *nc;
02903    int retval;
02904 
02905    LOG((2, "nc_sync: ncid 0x%x", ncid));
02906 
02907    if (!(nc = nc4_find_nc_file(ncid)))
02908       return NC_EBADID;
02909 
02910 #ifdef USE_PNETCDF
02911    /* Take care of files created/opened with parallel-netcdf library. */
02912    if (nc->pnetcdf_file)
02913       return ncmpi_sync(nc->int_ncid);
02914 #endif /* USE_PNETCDF */
02915 
02916    /* Take care of netcdf-3 files. */
02917    assert(nc->nc4_info);
02918 
02919    /* If we're in define mode, we can't sync. */
02920    if (nc->nc4_info && nc->nc4_info->flags & NC_INDEF)
02921    {
02922       if (nc->nc4_info->cmode & NC_CLASSIC_MODEL)
02923          return NC_EINDEFINE;
02924       if ((retval = nc_enddef(ncid)))
02925          return retval;
02926    }
02927 
02928    return sync_netcdf4_file(nc->nc4_info);
02929 }
02930 
02931 /* This function will free all allocated metadata memory, and close
02932    the HDF5 file. The group that is passed in must be the root group
02933    of the file. */
02934 static int
02935 close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort)
02936 {
02937    int retval;
02938 
02939    assert(h5 && h5->root_grp);
02940    LOG((3, "close_netcdf4_file: h5->path %s abort %d", 
02941         h5->path, abort));
02942 
02943    /* According to the docs, always end define mode on close. */
02944    if (h5->flags & NC_INDEF)
02945       h5->flags ^= NC_INDEF;
02946 
02947    /* Sync the file, unless we're aborting, or this is a read-only
02948     * file. */
02949    if (!h5->no_write && !abort)
02950       if ((retval = sync_netcdf4_file(h5)))
02951          return retval;
02952 
02953    /* Delete all the list contents for vars, dims, and atts, in each
02954     * group. */
02955    if ((retval = nc4_rec_grp_del(&h5->root_grp, h5->root_grp)))
02956       return retval;
02957 
02958    /* Close hdf file. */
02959    if (h5->hdf4)
02960    {
02961 #ifdef USE_HDF4
02962       if (SDend(h5->sdid))
02963          return NC_EHDFERR;
02964 #endif /* USE_HDF4 */
02965    } 
02966    else
02967    {
02968       if (H5Fclose(h5->hdfid) < 0) 
02969       {
02970         int nobjs;
02971         nobjs = H5Fget_obj_count(h5->hdfid, H5F_OBJ_ALL);
02972         /* Apparently we can get an error even when nobjs == 0 */
02973         if(nobjs < 0) {
02974           return NC_EHDFERR;
02975         } else if(nobjs > 0) {
02976 #ifdef LOGGING
02977          /* If the close doesn't work, probably there are still some HDF5
02978           * objects open, which means there's a bug in the library. So
02979           * print out some info on to help the poor programmer figure it
02980           * out. */
02981          LOG((0, "There are %d HDF5 objects open!", nobjs));
02982 #endif      
02983          return NC_EHDFERR;
02984         }
02985       }
02986 /*      if (H5garbage_collect() < 0)
02987         return NC_EHDFERR;       */
02988    }
02989 
02990    /* Delete the memory for the path, if it's been allocated. */
02991    if (h5->path)
02992       free(h5->path);
02993 
02994    /* Free the nc4_info struct. */
02995    free(h5);
02996    return NC_NOERR;
02997 }
02998 
02999 /* From the netcdf-3 docs: The function nc_abort just closes the
03000    netCDF dataset, if not in define mode. If the dataset is being
03001    created and is still in define mode, the dataset is deleted. If
03002    define mode was entered by a call to nc_redef, the netCDF dataset
03003    is restored to its state before definition mode was entered and the
03004    dataset is closed. */
03005 int
03006 NC4_abort(int ncid)
03007 {
03008    NC_FILE_INFO_T *nc;
03009    int delete_file = 0;
03010    char path[NC_MAX_NAME + 1];
03011    int retval = NC_NOERR;
03012 
03013    LOG((2, "nc_abort: ncid 0x%x", ncid));
03014 
03015    /* Find metadata for this file. */
03016    if (!(nc = nc4_find_nc_file(ncid)))
03017       return NC_EBADID;
03018 
03019 #ifdef USE_PNETCDF
03020    /* Take care of files created/opened with parallel-netcdf library. */
03021    if (nc->pnetcdf_file)
03022       return ncmpi_abort(nc->int_ncid);
03023 #endif /* USE_PNETCDF */
03024 
03025    /* If this is a netcdf-3 file, let the netcdf-3 library handle it. */
03026    assert(nc->nc4_info);
03027 
03028    /* If we're in define mode, but not redefing the file, delete it. */
03029    if (nc->nc4_info->flags & NC_INDEF && !nc->nc4_info->redef)
03030    {
03031       delete_file++;
03032       strcpy(path, nc->nc4_info->path);
03033       /*strcpy(path, nc->path);*/
03034    }
03035 
03036    /* Free any resources the netcdf-4 library has for this file's
03037     * metadata. */
03038    if ((retval = close_netcdf4_file(nc->nc4_info, 1)))
03039       return retval;
03040    
03041    /* Delete the file, if we should. */
03042    if (delete_file)
03043       remove(path);
03044 
03045    /* Delete this entry from our list of open files. */
03046    nc4_file_list_del(nc);
03047 
03048    return retval;
03049 }
03050 
03051 /* Close the netcdf file, writing any changes first. */
03052 int
03053 NC4_close(int ncid)
03054 {
03055    NC_GRP_INFO_T *grp;
03056    NC_FILE_INFO_T *nc;
03057    NC_HDF5_FILE_INFO_T *h5;
03058    int retval;
03059 
03060    LOG((1, "nc_close: ncid 0x%x", ncid));
03061 
03062    /* Find our metadata for this file. */
03063    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
03064       return retval;
03065 
03066 #ifdef USE_PNETCDF
03067    /* Take care of files created/opened with parallel-netcdf library. */
03068    if (nc->pnetcdf_file)
03069       return ncmpi_close(nc->int_ncid);
03070 #endif /* USE_PNETCDF */
03071 
03072    assert(h5 && nc);
03073 
03074    /* This must be the root group. */
03075    if (grp->parent)
03076       return NC_EBADGRPID;
03077 
03078    /* Call the nc4 close. */
03079    if ((retval = close_netcdf4_file(grp->file->nc4_info, 0)))
03080       return retval;
03081 
03082    /* Delete this entry from our list of open files. */
03083    if (nc->path)
03084       free(nc->path);
03085    nc4_file_list_del(nc);
03086 
03087    /* Reset the ncid numbers if there are no more files open. */
03088    if(count_NCList() == 0)
03089       nc4_file_list_free();
03090 
03091    return NC_NOERR;
03092 }
03093 
03094 /* It's possible for any of these pointers to be NULL, in which case
03095    don't try to figure out that value. */
03096 int
03097 NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp)
03098 {
03099    NC_FILE_INFO_T *nc;
03100    NC_HDF5_FILE_INFO_T *h5;
03101    NC_GRP_INFO_T *grp;
03102    NC_DIM_INFO_T *dim;
03103    NC_ATT_INFO_T *att;
03104    NC_VAR_INFO_T *var;
03105    int retval;
03106 
03107    LOG((2, "nc_inq: ncid 0x%x", ncid)); 
03108 
03109    /* Find file metadata. */
03110    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
03111       return retval;
03112 
03113 #ifdef USE_PNETCDF
03114    /* Take care of files created/opened with parallel-netcdf library. */
03115    if (nc->pnetcdf_file)
03116       return ncmpi_inq(nc->int_ncid, ndimsp, nvarsp, nattsp, unlimdimidp);
03117 #endif /* USE_PNETCDF */
03118 
03119    /* Netcdf-3 files are already taken care of. */
03120    assert(h5 && grp && nc);
03121 
03122    /* Count the number of dims, vars, and global atts. */
03123    if (ndimsp)
03124    {
03125       *ndimsp = 0;
03126       for (dim = grp->dim; dim; dim = dim->next)
03127          (*ndimsp)++;
03128    }
03129    if (nvarsp)
03130    {
03131       *nvarsp = 0;
03132       for (var = grp->var; var; var= var->next)
03133          (*nvarsp)++;
03134    }
03135    if (nattsp)
03136    {
03137       *nattsp = 0;
03138       for (att = grp->att; att; att = att->next)
03139          (*nattsp)++;
03140    }
03141 
03142    if (unlimdimidp)
03143    {
03144       /* Default, no unlimited dimension */
03145       int found = 0;
03146       *unlimdimidp = -1;
03147 
03148       /* If there's more than one unlimited dim, which was not possible
03149          with netcdf-3, then only the last unlimited one will be reported
03150          back in xtendimp. */
03151       /* Note that this code is inconsistent with nc_inq_unlimid() */
03152       for (dim = grp->dim; dim; dim = dim->next)
03153          if (dim->unlimited)
03154          {
03155             *unlimdimidp = dim->dimid;
03156             break;
03157          }
03158    }
03159 
03160    return NC_NOERR;   
03161 }
03162 
03163 
03164 /* This function will do the enddef stuff for a netcdf-4 file. */
03165 int
03166 nc4_enddef_netcdf4_file(NC_HDF5_FILE_INFO_T *h5)
03167 {
03168    assert(h5);
03169    LOG((3, "nc4_enddef_netcdf4_file"));
03170 
03171    /* If we're not in define mode, return an error. */
03172    if (!(h5->flags & NC_INDEF))
03173       return NC_ENOTINDEFINE;
03174 
03175    /* Turn define mode off. */
03176    h5->flags ^= NC_INDEF;
03177 
03178    /* Redef mode needs to be tracked seperately for nc_abort. */
03179    h5->redef = 0;
03180 
03181    return sync_netcdf4_file(h5);
03182 }
03183 
03184 #ifdef EXTRA_TESTS
03185 int
03186 nc_exit()
03187 {
03188    if (num_plists || num_spaces)
03189       return NC_EHDFERR;
03190       
03191    return NC_NOERR;
03192 }
03193 #endif /* EXTRA_TESTS */
03194 
03195 #ifdef USE_PARALLEL
03196 int
03197 nc_use_parallel_enabled()
03198 {
03199    return 0;
03200 }
03201 #endif /* USE_PARALLEL */
03202 
03203 
 All Data Structures Files Functions Variables Typedefs Defines

Generated on Sat Sep 15 2012 12:44:33 for netCDF. NetCDF is a Unidata library.