/* Create a group. It's ncid is returned in the new_ncid pointer. */ int NC4_def_grp(int parent_ncid, const char *name, int *new_ncid) { NC_GRP_INFO_T *grp, *g; NC_HDF5_FILE_INFO_T *h5; char norm_name[NC_MAX_NAME + 1]; int retval; LOG((2, "%s: parent_ncid 0x%x name %s", __func__, parent_ncid, name)); /* Find info for this file and group, and set pointer to each. */ if ((retval = nc4_find_grp_h5(parent_ncid, &grp, &h5))) return retval; if (!h5) return NC_ENOTNC4; /* Check and normalize the name. */ if ((retval = nc4_check_name(name, norm_name))) return retval; /* Check that this name is not in use as a var, grp, or type. */ if ((retval = nc4_check_dup_name(grp, norm_name))) return retval; /* No groups in netcdf-3! */ if (h5->cmode & NC_CLASSIC_MODEL) return NC_ESTRICTNC3; /* If it's not in define mode, switch to define mode. */ if (!(h5->flags & NC_INDEF)) if ((retval = NC4_redef(parent_ncid))) return retval; /* Update internal lists to reflect new group. The actual HDF5 * group creation will be done when metadata is written by a * sync. */ if ((retval = nc4_grp_list_add(&(grp->children), h5->next_nc_grpid, grp, grp->nc4_info->controller, norm_name, &g))) return retval; if (new_ncid) *new_ncid = grp->nc4_info->controller->ext_ncid | h5->next_nc_grpid; h5->next_nc_grpid++; return NC_NOERR; }
/* Rename a group. */ int NC4_rename_grp(int grpid, const char *name) { NC_GRP_INFO_T *grp, *g; NC_HDF5_FILE_INFO_T *h5; char norm_name[NC_MAX_NAME + 1]; int retval; LOG((2, "nc_rename_grp: grpid 0x%x name %s", grpid, name)); /* Find info for this file and group, and set pointer to each. */ if ((retval = nc4_find_grp_h5(grpid, &grp, &h5))) return retval; if (!h5) return NC_ENOTNC4; if (h5->no_write) return NC_EPERM; /* attempt to write to a read-only file */ /* Do not allow renaming the root group */ if(grp->parent == NULL) return NC_EBADGRPID; /* Check and normalize the name. */ if ((retval = nc4_check_name(name, norm_name))) return retval; /* Check that this name is not in use as a var, grp, or type. */ if ((retval = nc4_check_dup_name(grp, norm_name))) return retval; /* If it's not in define mode, switch to define mode. */ if (!(h5->flags & NC_INDEF)) if ((retval = NC4_redef(grpid))) return retval; /* Rename the group, if it exists in the file */ if (grp->hdf_grpid) { /* Close the group */ if (H5Gclose(grp->hdf_grpid) < 0) return NC_EHDFERR; grp->hdf_grpid = 0; /* Attempt to rename & re-open the group, if the parent group is open */ if (grp->parent->hdf_grpid) { /* Rename the group */ if (H5Gmove(grp->parent->hdf_grpid, grp->name, name) < 0) return NC_EHDFERR; /* Reopen the group, with the new name */ if ((grp->hdf_grpid = H5Gopen2(grp->parent->hdf_grpid, name, H5P_DEFAULT)) < 0) return NC_EHDFERR; } } /* Give the group its new name in metadata. UTF8 normalization * has been done. */ free(grp->name); if (!(grp->name = malloc((strlen(norm_name) + 1) * sizeof(char)))) return NC_ENOMEM; strcpy(grp->name, norm_name); return NC_NOERR; }
/* Dimensions are defined in attributes attached to the appropriate group in the data file. */ int NC4_def_dim(int ncid, const char *name, size_t len, int *idp) { NC *nc; NC_GRP_INFO_T *grp; NC_HDF5_FILE_INFO_T *h5; NC_DIM_INFO_T *dim; char norm_name[NC_MAX_NAME + 1]; int retval = NC_NOERR; LOG((2, "nc_def_dim: ncid 0x%x name %s len %d", ncid, name, (int)len)); /* Find our global metadata structure. */ if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) return retval; assert(h5 && nc /*& grp*/); /* If the file is read-only, return an error. */ if (h5->no_write) return NC_EPERM; #ifdef USE_PNETCDF /* Take care of files created/opened with parallel-netcdf library. */ if (h5->pnetcdf_file) return ncmpi_def_dim(nc->int_ncid, name, len, idp); #endif /* USE_PNETCDF */ /* Check some stuff if strict nc3 rules are in effect. */ if (h5->cmode & NC_CLASSIC_MODEL) { /* Only one limited dimenson for strict nc3. */ if (len == NC_UNLIMITED) for (dim = grp->dim; dim; dim = dim->next) if (dim->unlimited) return NC_EUNLIMIT; /* Must be in define mode for stict nc3. */ if (!(h5->flags & NC_INDEF)) return NC_ENOTINDEFINE; } /* If it's not in define mode, enter define mode. */ if (!(h5->flags & NC_INDEF)) if ((retval = NC4_redef(ncid))) return retval; /* Make sure this is a valid netcdf name. */ if ((retval = nc4_check_name(name, norm_name))) return retval; /* For classic model: dim length has to fit in a 32-bit unsigned * int, as permitted for 64-bit offset format. */ if (h5->cmode & NC_CLASSIC_MODEL) if(len > X_UINT_MAX) /* Backward compat */ return NC_EDIMSIZE; /* Make sure the name is not already in use. */ for (dim = grp->dim; dim; dim = dim->next) if (!strncmp(dim->name, norm_name, NC_MAX_NAME)) return NC_ENAMEINUSE; /* Add a dimension to the list. The ID must come from the file * information, since dimids are visible in more than one group. */ nc4_dim_list_add(&grp->dim); grp->dim->dimid = grp->nc4_info->next_dimid++; /* Initialize the metadata for this dimension. */ if (!(grp->dim->name = malloc((strlen(norm_name) + 1) * sizeof(char)))) return NC_ENOMEM; strcpy(grp->dim->name, norm_name); grp->dim->len = len; grp->dim->dirty++; if (len == NC_UNLIMITED) grp->dim->unlimited++; /* Pass back the dimid. */ if (idp) *idp = grp->dim->dimid; return retval; }
/** * @internal * Write an attribute to a netCDF-4/HDF5 file, converting * data type if necessary. * * @param ncid File and group ID. * @param varid Variable ID. * @param name Name of attribute. * @param file_type Type of the attribute data in file. * @param len Number of elements in attribute array. * @param data Attribute data. * @param mem_type Type of data in memory. * @param force write even if the attribute is special * * @return ::NC_NOERR No error. * @return ::NC_EINVAL Invalid parameters. * @return ::NC_EBADID Bad ncid. * @return ::NC_ENOTVAR Variable not found. * @return ::NC_EBADNAME Name contains illegal characters. * @return ::NC_ENAMEINUSE Name already in use. * @author Ed Hartnett, Dennis Heimbigner */ int nc4_put_att(NC_GRP_INFO_T* grp, int varid, const char *name, nc_type file_type, size_t len, const void *data, nc_type mem_type, int force) { NC* nc; NC_FILE_INFO_T *h5; NC_VAR_INFO_T *var = NULL; NCindex* attlist = NULL; NC_ATT_INFO_T* att; char norm_name[NC_MAX_NAME + 1]; nc_bool_t new_att = NC_FALSE; int retval = NC_NOERR, range_error = 0; size_t type_size; int i; int ret; int ncid; h5 = grp->nc4_info; nc = h5->controller; assert(nc && grp && h5); ncid = nc->ext_ncid | grp->hdr.id; /* Find att, if it exists. (Must check varid first or nc_test will * break.) */ if ((ret = getattlist(grp, varid, &var, &attlist))) return ret; /* The length needs to be positive (cast needed for braindead systems with signed size_t). */ if((unsigned long) len > X_INT_MAX) return NC_EINVAL; /* Check name before LOG statement. */ if (!name || strlen(name) > NC_MAX_NAME) return NC_EBADNAME; LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d", __func__,ncid, varid, name, file_type, mem_type, len)); /* If len is not zero, then there must be some data. */ if (len && !data) return NC_EINVAL; /* If the file is read-only, return an error. */ if (h5->no_write) return NC_EPERM; /* Check and normalize the name. */ if ((retval = nc4_check_name(name, norm_name))) return retval; /* Check that a reserved att name is not being used improperly */ const NC_reservedatt* ra = NC_findreserved(name); if(ra != NULL && !force) { /* case 1: grp=root, varid==NC_GLOBAL, flags & READONLYFLAG */ if (nc->ext_ncid == ncid && varid == NC_GLOBAL && grp->parent == NULL && (ra->flags & READONLYFLAG)) return NC_ENAMEINUSE; /* case 2: grp=NA, varid!=NC_GLOBAL, flags & DIMSCALEFLAG */ if (varid != NC_GLOBAL && (ra->flags & DIMSCALEFLAG)) return NC_ENAMEINUSE; } /* See if there is already an attribute with this name. */ att = (NC_ATT_INFO_T*)ncindexlookup(attlist,norm_name); LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d", __func__, ncid, varid, name, file_type, mem_type, len)); if (!att) { /* If this is a new att, require define mode. */ if (!(h5->flags & NC_INDEF)) { if (h5->cmode & NC_CLASSIC_MODEL) return NC_ENOTINDEFINE; if ((retval = NC4_redef(ncid))) BAIL(retval); } new_att = NC_TRUE; } else { /* For an existing att, if we're not in define mode, the len must not be greater than the existing len for classic model. */ if (!(h5->flags & NC_INDEF) && len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid)) { if (h5->cmode & NC_CLASSIC_MODEL) return NC_ENOTINDEFINE; if ((retval = NC4_redef(ncid))) BAIL(retval); } } /* We must have two valid types to continue. */ if (file_type == NC_NAT || mem_type == NC_NAT) return NC_EBADTYPE; /* Get information about this type. */ if ((retval = nc4_get_typelen_mem(h5, file_type, &type_size))) return retval; /* No character conversions are allowed. */ if (file_type != mem_type && (file_type == NC_CHAR || mem_type == NC_CHAR || file_type == NC_STRING || mem_type == NC_STRING)) return NC_ECHAR; /* For classic mode file, only allow atts with classic types to be * created. */ if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE) return NC_ESTRICTNC3; /* Add to the end of the attribute list, if this att doesn't already exist. */ if (new_att) { LOG((3, "adding attribute %s to the list...", norm_name)); if ((ret = nc4_att_list_add(attlist, norm_name, &att))) BAIL(ret); /* Allocate storage for the HDF5 specific att info. */ if (!(att->format_att_info = calloc(1, sizeof(NC_HDF5_ATT_INFO_T)))) BAIL(NC_ENOMEM); } /* Now fill in the metadata. */ att->dirty = NC_TRUE; att->nc_typeid = file_type; /* If this att has vlen or string data, release it before we lose the length value. */ if (att->stdata) { for (i = 0; i < att->len; i++) if(att->stdata[i]) free(att->stdata[i]); free(att->stdata); att->stdata = NULL; } if (att->vldata) { for (i = 0; i < att->len; i++) nc_free_vlen(&att->vldata[i]); /* FIX: see warning of nc_free_vlen */ free(att->vldata); att->vldata = NULL; } att->len = len; /* If this is the _FillValue attribute, then we will also have to * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T * struct for this var. (But ignore a global _FillValue * attribute). */ if (!strcmp(att->hdr.name, _FillValue) && varid != NC_GLOBAL) { int size; /* Fill value must be same type and have exactly one value */ if (att->nc_typeid != var->type_info->hdr.id) return NC_EBADTYPE; if (att->len != 1) return NC_EINVAL; /* If we already wrote to the dataset, then return an error. */ if (var->written_to) return NC_ELATEFILL; /* Get the length of the veriable data type. */ if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id, &type_size))) return retval; /* Already set a fill value? Now I'll have to free the old * one. Make up your damn mind, would you? */ if (var->fill_value) { if (var->type_info->nc_type_class == NC_VLEN) { if ((retval = nc_free_vlen(var->fill_value))) return retval; } else if (var->type_info->nc_type_class == NC_STRING) { if (*(char **)var->fill_value) free(*(char **)var->fill_value); } free(var->fill_value); } /* Determine the size of the fill value in bytes. */ if (var->type_info->nc_type_class == NC_VLEN) size = sizeof(hvl_t); else if (var->type_info->nc_type_class == NC_STRING) size = sizeof(char *); else size = type_size; /* Allocate space for the fill value. */ if (!(var->fill_value = calloc(1, size))) return NC_ENOMEM; /* Copy the fill_value. */ LOG((4, "Copying fill value into metadata for variable %s", var->hdr.name)); if (var->type_info->nc_type_class == NC_VLEN) { nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value); NC_TYPE_INFO_T* basetype; size_t basetypesize = 0; /* get the basetype and its size */ basetype = var->type_info; if ((retval = nc4_get_typelen_mem(grp->nc4_info, basetype->hdr.id, &basetypesize))) return retval; /* shallow clone the content of the vlen; shallow because it has only a temporary existence */ fv_vlen->len = in_vlen->len; if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len))) return NC_ENOMEM; memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize); } else if (var->type_info->nc_type_class == NC_STRING) { if (*(char **)data) { if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1))) return NC_ENOMEM; strcpy(*(char **)var->fill_value, *(char **)data); } else *(char **)var->fill_value = NULL; } else memcpy(var->fill_value, data, type_size); /* Indicate that the fill value was changed, if the variable has already * been created in the file, so the dataset gets deleted and re-created. */ if (var->created) var->fill_val_changed = NC_TRUE; } /* Copy the attribute data, if there is any. VLENs and string * arrays have to be handled specially. */ if (att->len) { nc_type type_class; /* Class of attribute's type */ /* Get class for this type. */ if ((retval = nc4_get_typeclass(h5, file_type, &type_class))) return retval; assert(data); if (type_class == NC_VLEN) { const hvl_t *vldata1; NC_TYPE_INFO_T *vltype; size_t base_typelen; /* Get the type object for the attribute's type */ if ((retval = nc4_find_type(h5, file_type, &vltype))) BAIL(retval); /* Retrieve the size of the base type */ if ((retval = nc4_get_typelen_mem(h5, vltype->u.v.base_nc_typeid, &base_typelen))) BAIL(retval); vldata1 = data; if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t)))) BAIL(NC_ENOMEM); for (i = 0; i < att->len; i++) { att->vldata[i].len = vldata1[i].len; /* Warning, this only works for cases described for nc_free_vlen() */ if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len))) BAIL(NC_ENOMEM); memcpy(att->vldata[i].p, vldata1[i].p, base_typelen * att->vldata[i].len); } } else if (type_class == NC_STRING) { LOG((4, "copying array of NC_STRING")); if (!(att->stdata = malloc(sizeof(char *) * att->len))) { BAIL(NC_ENOMEM); } /* If we are overwriting an existing attribute, specifically an NC_CHAR, we need to clean up the pre-existing att->data. */ if (!new_att && att->data) { free(att->data); att->data = NULL; } for (i = 0; i < att->len; i++) { if(NULL != ((char **)data)[i]) { LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1)); if (!(att->stdata[i] = strdup(((char **)data)[i]))) BAIL(NC_ENOMEM); } else att->stdata[i] = ((char **)data)[i]; } } else { /* [Re]allocate memory for the attribute data */ if (!new_att) free (att->data); if (!(att->data = malloc(att->len * type_size))) BAIL(NC_ENOMEM); /* Just copy the data, for non-atomic types */ if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM) memcpy(att->data, data, len * type_size); else { /* Data types are like religions, in that one can convert. */ if ((retval = nc4_convert_type(data, att->data, mem_type, file_type, len, &range_error, NULL, (h5->cmode & NC_CLASSIC_MODEL)))) BAIL(retval); } } } att->dirty = NC_TRUE; att->created = NC_FALSE; /* Mark attributes on variable dirty, so they get written */ if(var) var->attr_dirty = NC_TRUE; exit: /* If there was an error return it, otherwise return any potential range error value. If none, return NC_NOERR as usual.*/ if (retval) return retval; if (range_error) return NC_ERANGE; return NC_NOERR; }
/** * @internal Delete an att. Rub it out. Push the button on * it. Liquidate it. Bump it off. Take it for a one-way * ride. Terminate it. * * @param ncid File and group ID. * @param varid Variable ID. * @param name Name of attribute to delete. * * @return ::NC_NOERR No error. * @return ::NC_EBADID Bad ncid. * @return ::NC_ENOTATT Attribute not found. * @return ::NC_EINVAL No name provided. * @return ::NC_EPERM File is read only. * @return ::NC_ENOTINDEFINE Classic model not in define mode. * @return ::NC_EINTERNAL Could not rebuild list. * @author Ed Hartnett, Dennis Heimbigner */ int NC4_del_att(int ncid, int varid, const char *name) { NC_GRP_INFO_T *grp; NC_VAR_INFO_T *var; NC_FILE_INFO_T *h5; NC_ATT_INFO_T *att; NCindex* attlist = NULL; hid_t locid = 0; int i; size_t deletedid; int retval; /* Name must be provided. */ if (!name) return NC_EINVAL; LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name)); /* Find info for this file, group, and h5 info. */ if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) return retval; assert(h5 && grp); /* If the file is read-only, return an error. */ if (h5->no_write) return NC_EPERM; /* If file is not in define mode, return error for classic model * files, otherwise switch to define mode. */ if (!(h5->flags & NC_INDEF)) { if (h5->cmode & NC_CLASSIC_MODEL) return NC_ENOTINDEFINE; if ((retval = NC4_redef(ncid))) return retval; } /* Get either the global or a variable attribute list. */ if ((retval = getattlist(grp, varid, &var, &attlist))) return retval; /* Determine the location id in the HDF5 file. */ if (varid == NC_GLOBAL) locid = ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid; else if (var->created) locid = var->hdf_datasetid; /* Now find the attribute by name. */ if (!(att = (NC_ATT_INFO_T*)ncindexlookup(attlist, name))) return NC_ENOTATT; /* Delete it from the HDF5 file, if it's been created. */ if (att->created) { assert(locid); if (H5Adelete(locid, att->hdr.name) < 0) return NC_EATTMETA; } deletedid = att->hdr.id; /* Remove this attribute in this list */ if ((retval = nc4_att_list_del(attlist, att))) return retval; /* Renumber all attributes with higher indices. */ for (i = 0; i < ncindexsize(attlist); i++) { NC_ATT_INFO_T *a; if (!(a = (NC_ATT_INFO_T *)ncindexith(attlist, i))) continue; if (a->hdr.id > deletedid) a->hdr.id--; } /* Rebuild the index. */ if (!ncindexrebuild(attlist)) return NC_EINTERNAL; return NC_NOERR; }
/* Delete an att. Rub it out. Push the button on it. Liquidate it. Bump it off. Take it for a one-way ride. Terminate it. Drop the bomb on it. You get the idea. Ed Hartnett, 10/1/3 */ int NC4_del_att(int ncid, int varid, const char *name) { NC *nc; NC_GRP_INFO_T *grp; NC_HDF5_FILE_INFO_T *h5; NC_ATT_INFO_T *att, *natt; NC_VAR_INFO_T *var; NC_ATT_INFO_T **attlist = NULL; hid_t locid = 0, datasetid = 0; int retval = NC_NOERR; if (!name) return NC_EINVAL; LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name)); /* Find metadata for this file. */ if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) return retval; assert(h5 && grp); /* If the file is read-only, return an error. */ if (h5->no_write) return NC_EPERM; #if 0 /*def USE_PNETCDF*/ /* Take care of files created/opened with parallel-netcdf library. */ if (h5->pnetcdf_file) return ncmpi_del_att(nc->int_ncid, varid, name); #endif /* USE_PNETCDF */ /* If it's not in define mode, forget it. */ if (!(h5->flags & NC_INDEF)) { if (h5->cmode & NC_CLASSIC_MODEL) return NC_ENOTINDEFINE; if ((retval = NC4_redef(ncid))) BAIL(retval); } /* Get either the global or a variable attribute list. Also figure out the HDF5 location it's attached to. */ if (varid == NC_GLOBAL) { attlist = &grp->att; locid = grp->hdf_grpid; } else { for(var = grp->var; var; var = var->l.next) { if (var->varid == varid) { attlist = &var->att; break; } } if (!var) return NC_ENOTVAR; if (var->created) locid = var->hdf_datasetid; } /* Now find the attribute by name or number. */ for (att = *attlist; att; att = att->l.next) if (!strcmp(att->name, name)) break; /* If att is NULL, we couldn't find the attribute. */ if (!att) BAIL_QUIET(NC_ENOTATT); /* Delete it from the HDF5 file, if it's been created. */ if (att->created) { assert(locid); if(H5Adelete(locid, att->name) < 0) BAIL(NC_EATTMETA); } /* Renumber all following attributes. */ for (natt = att->l.next; natt; natt = natt->l.next) natt->attnum--; /* Delete this attribute from this list. */ if ((retval = nc4_att_list_del(attlist, att))) BAIL(retval); exit: if (datasetid > 0) H5Dclose(datasetid); return retval; }
/* Put attribute metadata into our global metadata. */ static int nc4_put_att(int ncid, NC *nc, int varid, const char *name, nc_type file_type, nc_type mem_type, size_t len, int is_long, const void *data) { NC_GRP_INFO_T *grp; NC_HDF5_FILE_INFO_T *h5; NC_VAR_INFO_T *var = NULL; NC_ATT_INFO_T *att, **attlist = NULL; char norm_name[NC_MAX_NAME + 1]; nc_bool_t new_att = NC_FALSE; int retval = NC_NOERR, range_error = 0; size_t type_size; int i; int res; if (!name) return NC_EBADNAME; assert(nc && NC4_DATA(nc)); LOG((1, "nc4_put_att: ncid 0x%x varid %d name %s " "file_type %d mem_type %d len %d", ncid, varid, name, file_type, mem_type, len)); /* If len is not zero, then there must be some data. */ if (len && !data) return NC_EINVAL; /* Find info for this file and group, and set pointer to each. */ h5 = NC4_DATA(nc); if (!(grp = nc4_rec_find_grp(h5->root_grp, (ncid & GRP_ID_MASK)))) return NC_EBADGRPID; /* If the file is read-only, return an error. */ if (h5->no_write) return NC_EPERM; /* Check and normalize the name. */ if ((retval = nc4_check_name(name, norm_name))) return retval; /* Find att, if it exists. */ if (varid == NC_GLOBAL) attlist = &grp->att; else { for (var = grp->var; var; var = var->l.next) if (var->varid == varid) { attlist = &var->att; break; } if (!var) return NC_ENOTVAR; } for (att = *attlist; att; att = att->l.next) if (!strcmp(att->name, norm_name)) break; if (!att) { /* If this is a new att, require define mode. */ if (!(h5->flags & NC_INDEF)) { if (h5->cmode & NC_CLASSIC_MODEL) return NC_EINDEFINE; if ((retval = NC4_redef(ncid))) BAIL(retval); } new_att = NC_TRUE; } else { /* For an existing att, if we're not in define mode, the len must not be greater than the existing len for classic model. */ if (!(h5->flags & NC_INDEF) && len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid)) { if (h5->cmode & NC_CLASSIC_MODEL) return NC_EINDEFINE; if ((retval = NC4_redef(ncid))) BAIL(retval); } } /* We must have two valid types to continue. */ if (file_type == NC_NAT || mem_type == NC_NAT) return NC_EBADTYPE; /* Get information about this type. */ if ((retval = nc4_get_typelen_mem(h5, file_type, is_long, &type_size))) return retval; /* No character conversions are allowed. */ if (file_type != mem_type && (file_type == NC_CHAR || mem_type == NC_CHAR || file_type == NC_STRING || mem_type == NC_STRING)) return NC_ECHAR; /* For classic mode file, only allow atts with classic types to be * created. */ if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE) return NC_ESTRICTNC3; /* Add to the end of the attribute list, if this att doesn't already exist. */ if (new_att) { LOG((3, "adding attribute %s to the list...", norm_name)); if ((res = nc4_att_list_add(attlist, &att))) BAIL (res); if (!(att->name = strdup(norm_name))) return NC_ENOMEM; } /* Now fill in the metadata. */ att->dirty = NC_TRUE; att->nc_typeid = file_type; /* If this att has vlen or string data, release it before we lose the length value. */ if (att->stdata) { for (i = 0; i < att->len; i++) if(att->stdata[i]) free(att->stdata[i]); free(att->stdata); att->stdata = NULL; } if (att->vldata) { for (i = 0; i < att->len; i++) nc_free_vlen(&att->vldata[i]); free(att->vldata); att->vldata = NULL; } att->len = len; if (att->l.prev) att->attnum = ((NC_ATT_INFO_T *)att->l.prev)->attnum + 1; else att->attnum = 0; /* If this is the _FillValue attribute, then we will also have to * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T * struct for this var. (But ignore a global _FillValue * attribute). */ if (!strcmp(att->name, _FillValue) && varid != NC_GLOBAL) { NC_ATT_INFO_T *varatt; int size; /* Fill value must be same type and have exactly one value */ if (att->nc_typeid != var->type_info->nc_typeid) return NC_EBADTYPE; if (att->len != 1) return NC_EINVAL; /* If we already wrote to the dataset, then return an error. */ if (var->written_to) return NC_ELATEFILL; /* If fill value hasn't been set, allocate space. Of course, * vlens have to be different... */ if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->nc_typeid, 0, &type_size))) return retval; /* Already set a fill value? Now I'll have to free the old * one. Make up your damn mind, would you? */ if (var->fill_value) { if (var->type_info->nc_type_class == NC_VLEN) { if ((retval = nc_free_vlen(var->fill_value))) return retval; } else if (var->type_info->nc_type_class == NC_STRING) { if (*(char **)var->fill_value) free(*(char **)var->fill_value); } free(var->fill_value); } /* Allocate space for the fill value. */ if (var->type_info->nc_type_class == NC_VLEN) size = sizeof(hvl_t); else if (var->type_info->nc_type_class == NC_STRING) size = sizeof(char *); else size = type_size; if (!(var->fill_value = calloc(1, size))) return NC_ENOMEM; /* Copy the fill_value. */ LOG((4, "Copying fill value into metadata for variable %s", var->name)); if (var->type_info->nc_type_class == NC_VLEN) { nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value); fv_vlen->len = in_vlen->len; if (!(fv_vlen->p = malloc(size * in_vlen->len))) return NC_ENOMEM; memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * size); } else if (var->type_info->nc_type_class == NC_STRING) { if(NULL != (*(char **)data)) { if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1))) return NC_ENOMEM; strcpy(*(char **)var->fill_value, *(char **)data); } else *(char **)var->fill_value = NULL; } else memcpy(var->fill_value, data, type_size); /* Indicate that the fill value was changed, if the variable has already * been created in the file, so the dataset gets deleted and re-created. */ if (var->created) var->fill_val_changed = NC_TRUE; } /* Copy the attribute data, if there is any. VLENs and string * arrays have to be handled specially. */ if(att->len) { nc_type type_class; /* Class of attribute's type */ /* Get class for this type. */ if ((retval = nc4_get_typeclass(h5, file_type, &type_class))) return retval; assert(data); if (type_class == NC_VLEN) { const hvl_t *vldata1; NC_TYPE_INFO_T *type; size_t base_typelen; /* Get the type object for the attribute's type */ if ((retval = nc4_find_type(h5, file_type, &type))) BAIL(retval); /* Retrieve the size of the base type */ if ((retval = nc4_get_typelen_mem(h5, type->u.v.base_nc_typeid, 0, &base_typelen))) BAIL(retval); vldata1 = data; if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t)))) BAIL(NC_ENOMEM); for (i = 0; i < att->len; i++) { att->vldata[i].len = vldata1[i].len; if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len))) BAIL(NC_ENOMEM); memcpy(att->vldata[i].p, vldata1[i].p, base_typelen * att->vldata[i].len); } } else if (type_class == NC_STRING) { LOG((4, "copying array of NC_STRING")); if (!(att->stdata = malloc(sizeof(char *) * att->len))) BAIL(NC_ENOMEM); for (i = 0; i < att->len; i++) { if(NULL != ((char **)data)[i]) { LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1)); if (!(att->stdata[i] = strdup(((char **)data)[i]))) BAIL(NC_ENOMEM); } else att->stdata[i] = ((char **)data)[i]; } } else { /* [Re]allocate memory for the attribute data */ if (!new_att) free (att->data); if (!(att->data = malloc(att->len * type_size))) BAIL(NC_ENOMEM); /* Just copy the data, for non-atomic types */ if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM) memcpy(att->data, data, len * type_size); else { /* Data types are like religions, in that one can convert. */ if ((retval = nc4_convert_type(data, att->data, mem_type, file_type, len, &range_error, NULL, (h5->cmode & NC_CLASSIC_MODEL), is_long, 0))) BAIL(retval); } } } att->dirty = NC_TRUE; att->created = NC_FALSE; /* Mark attributes on variable dirty, so they get written */ if(var) var->attr_dirty = NC_TRUE; exit: /* If there was an error return it, otherwise return any potential range error value. If none, return NC_NOERR as usual.*/ if (retval) return retval; if (range_error) return NC_ERANGE; return NC_NOERR; }