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