From 7c936a7bb675ece199112a5fa510dd574eae044e Mon Sep 17 00:00:00 2001 From: Ed Hartnett Date: Wed, 31 Jan 2018 08:44:33 -0700 Subject: [PATCH] brought in changes from ejh_att --- libsrc4/nc4attr.c | 919 +++++++++++++++++------------------------- libsrc4/nc4internal.c | 32 +- nc_test4/tst_atts.c | 268 +++++++++++- nc_test4/tst_atts2.c | 13 +- 4 files changed, 648 insertions(+), 584 deletions(-) diff --git a/libsrc4/nc4attr.c b/libsrc4/nc4attr.c index ea7ba0f88..0aae34146 100644 --- a/libsrc4/nc4attr.c +++ b/libsrc4/nc4attr.c @@ -1,3 +1,6 @@ +/* Copyright 2003-2018, University Corporation for Atmospheric + * Research. See COPYRIGHT file for copying and redistribution + * conditions. */ /** * @file * @@ -10,10 +13,6 @@ * Remember that with atts, type conversion can take place when * writing them, and when reading them. * - * Copyright 2003-2011, University Corporation for Atmospheric - * Research. See COPYRIGHT file for copying and redistribution - * conditions. - * * @author Ed Hartnett */ #include "nc4internal.h" @@ -24,7 +23,7 @@ int nc4typelen(nc_type type); /** - * @internal Get special informatation about the attrobute. + * @internal Get special informatation about the attribute. * * @param h5 Pointer to HDF5 file info struct. * @param name Name of attribute. @@ -38,6 +37,7 @@ int nc4typelen(nc_type type); * * @return ::NC_NOERR No error. * @return ::NC_EBADID Bad ncid. + * @return ::NC_ERANGE Data conversion out of range. * @author Dennis Heimbigner */ static int @@ -98,49 +98,44 @@ nc4_get_att_special(NC_HDF5_FILE_INFO_T* h5, const char* name, * The mem_type is ignored if data=NULL. * * @param ncid File and group ID. - * @param nc Pointer to file's NC struct. * @param varid Variable ID. * @param name Name of attribute. * @param xtype Pointer that gets (file) type of attribute. * @param mem_type The type of attribute data in memory. * @param lenp Pointer that gets length of attribute array. * @param attnum Pointer that gets the index number of this attribute. - * @param is_long True only if the type is NC_LONG. * @param data Pointer that gets attribute data. * * @return ::NC_NOERR No error. * @return ::NC_EBADID Bad ncid. * @author Ed Hartnett */ -int -nc4_get_att(int ncid, NC *nc, int varid, const char *name, nc_type *xtype, - nc_type mem_type, size_t *lenp, int *attnum, int is_long, - void *data) +static int +nc4_get_att(int ncid, int varid, const char *name, nc_type *xtype, + nc_type mem_type, size_t *lenp, int *attnum, void *data) { + NC *nc; NC_GRP_INFO_T *grp; NC_HDF5_FILE_INFO_T *h5; NC_ATT_INFO_T *att = NULL; int my_attnum = -1; - int need_to_convert = 0; int range_error = NC_NOERR; void *bufr = NULL; size_t type_size; char norm_name[NC_MAX_NAME + 1]; int i; - int retval = NC_NOERR; + int retval; - if (attnum) { + if (attnum) my_attnum = *attnum; - } LOG((3, "%s: ncid 0x%x varid %d name %s attnum %d mem_type %d", __func__, ncid, varid, name, my_attnum, mem_type)); - /* 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)))) - BAIL(NC_EBADGRPID); + /* Find info for this file, group, and h5 info. */ + if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) + return retval; /* Check varid */ if (varid != NC_GLOBAL) { @@ -158,24 +153,20 @@ nc4_get_att(int ncid, NC *nc, int varid, const char *name, nc_type *xtype, if ((retval = nc4_normalize_name(name, norm_name))) BAIL(retval); - if(nc->ext_ncid == ncid && varid == NC_GLOBAL) { + /* If this is one of the reserved atts, use nc_get_att_special. */ + if (nc->ext_ncid == ncid && varid == NC_GLOBAL) { const char** sp; - for(sp = NC_RESERVED_SPECIAL_LIST;*sp;sp++) { - if(strcmp(name,*sp)==0) { - return nc4_get_att_special(h5, norm_name, xtype, mem_type, lenp, attnum, is_long, data); + for (sp = NC_RESERVED_SPECIAL_LIST; *sp; sp++) { + if (strcmp(name,*sp)==0) { + return nc4_get_att_special(h5, norm_name, xtype, mem_type, lenp, + attnum, 0, data); } } } - /* Find the attribute, if it exists. - If we don't find it, we are major failures. - */ - if ((retval = nc4_find_grp_att(grp, varid, norm_name, my_attnum, &att))) { - if(retval == NC_ENOTATT) - return retval; - else - BAIL(retval); - } + /* Find the attribute, if it exists. */ + if ((retval = nc4_find_grp_att(grp, varid, norm_name, my_attnum, &att))) + return retval; /* If mem_type is NC_NAT, it means we want to use the attribute's * file type as the mem type as well. */ @@ -185,11 +176,11 @@ nc4_get_att(int ncid, NC *nc, int varid, const char *name, nc_type *xtype, /* If the attribute is NC_CHAR, and the mem_type isn't, or vice * versa, that's a freakish attempt to convert text to * numbers. Some pervert out there is trying to pull a fast one! - * Send him an NC_ECHAR error...*/ - if (data && att->len && - ((att->nc_typeid == NC_CHAR && mem_type != NC_CHAR) || - (att->nc_typeid != NC_CHAR && mem_type == NC_CHAR))) - BAIL(NC_ECHAR); /* take that, you freak! */ + * Send him an NC_ECHAR error. */ + if (data && att->len) + if ((att->nc_typeid == NC_CHAR && mem_type != NC_CHAR) || + (att->nc_typeid != NC_CHAR && mem_type == NC_CHAR)) + BAIL(NC_ECHAR); /* take that, you freak! */ /* Copy the info. */ if (lenp) @@ -205,7 +196,7 @@ nc4_get_att(int ncid, NC *nc, int varid, const char *name, nc_type *xtype, BAIL(NC_NOERR); /* Later on, we will need to know the size of this type. */ - if ((retval = nc4_get_typelen_mem(h5, mem_type, is_long, &type_size))) + if ((retval = nc4_get_typelen_mem(h5, mem_type, 0, &type_size))) BAIL(retval); /* We may have to convert data. Treat NC_CHAR the same as @@ -221,7 +212,7 @@ nc4_get_att(int ncid, NC *nc, int varid, const char *name, nc_type *xtype, need_to_convert++; if ((retval = nc4_convert_type(att->data, bufr, att->nc_typeid, mem_type, (size_t)att->len, &range_error, - NULL, (h5->cmode & NC_CLASSIC_MODEL), 0, is_long))) + NULL, (h5->cmode & NC_CLASSIC_MODEL), 0, 0))) BAIL(retval); /* For strict netcdf-3 rules, ignore erange errors between UBYTE @@ -281,17 +272,7 @@ nc4_get_att(int ncid, NC *nc, int varid, const char *name, nc_type *xtype, } else { - /* For long types, we need to handle this special... */ - if (is_long && att->nc_typeid == NC_INT) - { - long *lp = data; - int *ip = bufr; - - for (i = 0; i < att->len; i++) - *lp++ = *ip++; - } - else - memcpy(data, bufr, (size_t)(att->len * type_size)); + memcpy(data, bufr, (size_t)(att->len * type_size)); } } @@ -303,367 +284,6 @@ exit: return retval; } -/** - * @internal Put attribute metadata into our global metadata. - * - * @param ncid File and group ID. - * @param nc Pointer to file's NC struct. - * @param varid Variable ID. - * @param name Name of attribute. - * @param file_type Type of the attribute data in file. - * @param mem_type Type of attribute data in memory. - * @param len Length of attribute array. - * @param is_long True if attribute is of type NC_LONG. - * @param data Attribute data. - * - * @return ::NC_NOERR No error. - * @return ::NC_EBADID Bad ncid. - * @author Ed Hartnett - */ -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; - - /* Find att, if it exists. */ - if (varid == NC_GLOBAL) - attlist = &grp->att; - else - { - if (varid < 0 || varid >= grp->vars.nelems) - return NC_ENOTVAR; - var = grp->vars.value[varid]; - if (!var) return NC_ENOTVAR; - attlist = &var->att; - assert(var->varid == varid); - } - - if (!name) - return NC_EBADNAME; - - /* Check and normalize the name. */ - if ((retval = nc4_check_name(name, norm_name))) - return retval; - - if(nc->ext_ncid == ncid && varid == NC_GLOBAL) { - const char** sp; - for(sp = NC_RESERVED_SPECIAL_LIST;*sp;sp++) { - if(strcmp(name,*sp)==0) { - return NC_ENOTATT; /* Not settable */ - } - } - } - - for (att = *attlist; att; att = att->l.next) - if (!strcmp(att->name, norm_name)) - break; - - /* If len is not zero, then there must be some data. */ - if (len && !data) - return NC_EINVAL; - - 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 (!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) - { - 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); - } - - /* 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), 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; -} - /** * @internal Learn about an att. All the nc4 nc_inq_ functions just * call nc4_get_att to get the metadata on an attribute. @@ -679,23 +299,11 @@ exit: * @author Ed Hartnett */ int -NC4_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, size_t *lenp) +NC4_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, + size_t *lenp) { - NC *nc; - NC_HDF5_FILE_INFO_T *h5; - - LOG((2, "nc_inq_att: ncid 0x%x varid %d name %s", ncid, varid, name)); - - /* Find metadata. */ - if (!(nc = nc4_find_nc_file(ncid,NULL))) - return NC_EBADID; - - /* get netcdf-4 metadata */ - h5 = NC4_DATA(nc); - assert(h5); - - /* Handle netcdf-4 files. */ - return nc4_get_att(ncid, nc, varid, name, xtypep, NC_NAT, lenp, NULL, 0, NULL); + LOG((2, "%s: ncid 0x%x varid %d name %s", __func__, ncid, varid, name)); + return nc4_get_att(ncid, varid, name, xtypep, NC_NAT, lenp, NULL, NULL); } /** @@ -712,24 +320,8 @@ NC4_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, size_t *lenp int NC4_inq_attid(int ncid, int varid, const char *name, int *attnump) { - NC *nc; - NC_HDF5_FILE_INFO_T *h5; - int stat; - - LOG((2, "nc_inq_attid: ncid 0x%x varid %d name %s", ncid, varid, name)); - - /* Find metadata. */ - if (!(nc = nc4_find_nc_file(ncid,NULL))) - return NC_EBADID; - - /* get netcdf-4 metadata */ - h5 = NC4_DATA(nc); - assert(h5); - - /* Handle netcdf-4 files. */ - stat = nc4_get_att(ncid, nc, varid, name, NULL, NC_NAT, - NULL, attnump, 0, NULL); - return stat; + LOG((2, "%s: ncid 0x%x varid %d name %s", __func__, ncid, varid, name)); + return nc4_get_att(ncid, varid, name, NULL, NC_NAT, NULL, attnump, NULL); } @@ -810,11 +402,10 @@ NC4_rename_att(int ncid, int varid, const char *name, const char *newname) if (strlen(newname) > NC_MAX_NAME) return NC_EMAXNAME; - /* Find metadata for this file. */ + /* Find info for this file, group, and h5 info. */ if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) return retval; - - assert(h5 && grp); + assert(h5 && grp && h5); /* If the file is read-only, return an error. */ if (h5->no_write) @@ -920,7 +511,7 @@ NC4_del_att(int ncid, int varid, const char *name) LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name)); - /* Find metadata for this file. */ + /* Find info for this file, group, and h5 info. */ if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) return retval; @@ -990,142 +581,374 @@ exit: } /** - * @internal Write an attribute with type conversion. + * @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 mem_type Type of attribute data in memory. - * @param mem_type_is_long True if attribute data in memory is of type - * NC_LONG. - * @param len Length of attribute array. - * @param op Attribute data. + * @param len Number of elements in attribute array. + * @param data Attribute data. + * @param memtype Type of data in memory. * * @return ::NC_NOERR No error. + * @return ::NC_EINVAL Invalid parameters. * @return ::NC_EBADID Bad ncid. - * @author Ed Hartnett + * @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 */ -static int -nc4_put_att_tc(int ncid, int varid, const char *name, nc_type file_type, - nc_type mem_type, int mem_type_is_long, size_t len, - const void *op) +int +NC4_put_att(int ncid, int varid, const char *name, nc_type file_type, + size_t len, const void *data, nc_type mem_type) { NC *nc; + 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 ret; + + /* Find info for this file, group, and h5 info. */ + if ((ret = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) + return ret; + assert(nc && grp && h5); + + /* Find att, if it exists. (Must check varid first or nc_test will + * break.) */ + if (varid == NC_GLOBAL) + attlist = &grp->att; + else + { + if (varid < 0 || varid >= grp->vars.nelems) + return NC_ENOTVAR; + var = grp->vars.value[varid]; + if (!var) return NC_ENOTVAR; + attlist = &var->att; + assert(var->varid == varid); + } /* 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; - /* Find metadata. */ - if (!(nc = nc4_find_nc_file(ncid,NULL))) - return NC_EBADID; - - /* get netcdf-4 metadata */ - h5 = NC4_DATA(nc); - assert(h5); - - /* Check varid */ - if (varid != NC_GLOBAL) { - /* Find info for this file and group, and set pointer to each. */ - NC_GRP_INFO_T *grp; - if (!(grp = nc4_rec_find_grp(h5->root_grp, (ncid & GRP_ID_MASK)))) - return NC_EBADGRPID; - - if (varid < 0 || varid >= grp->vars.nelems) - return NC_ENOTVAR; - if (grp->vars.value[varid] == NULL) - return NC_ENOTVAR; - assert(grp->vars.value[varid]->varid == varid); - } - + /* Check name before LOG statement. */ if (!name || strlen(name) > NC_MAX_NAME) return NC_EBADNAME; - LOG((3, "nc4_put_att_tc: ncid 0x%x varid %d name %s file_type %d " - "mem_type %d len %d", ncid, varid, name, file_type, mem_type, len)); + 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(nc->ext_ncid == ncid && varid == NC_GLOBAL) { + /* 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 NC_GLOBAL att name is not being used. */ + if (nc->ext_ncid == ncid && varid == NC_GLOBAL) { const char** reserved = NC_RESERVED_ATT_LIST; - for(;*reserved;reserved++) { - if(strcmp(name,*reserved)==0) + for ( ; *reserved; reserved++) { + if (strcmp(name, *reserved)==0) return NC_ENAMEINUSE; } } - if(varid != NC_GLOBAL) { + /* Check that a reserved variable att name is not being used. */ + if (varid != NC_GLOBAL) { const char** reserved = NC_RESERVED_VARATT_LIST; - for(;*reserved;reserved++) { - if(strcmp(name,*reserved)==0) + for ( ; *reserved; reserved++) { + if (strcmp(name, *reserved) == 0) return NC_ENAMEINUSE; } } - /* Otherwise, handle things the netcdf-4 way. */ - return nc4_put_att(ncid, nc, varid, name, file_type, mem_type, len, - mem_type_is_long, op); -} + /* See if there is already an attribute with this name. */ + for (att = *attlist; att; att = att->l.next) + if (!strcmp(att->name, norm_name)) + break; -/** - * @internal Read an attribute of any type, with type conversion. This - * may be called by any of the nc_get_att_* functions. - * - * @param ncid File and group ID. - * @param varid Variable ID. - * @param name Name of attribute. - * @param mem_type Type of attribute data in memory. - * @param mem_type_is_long True if attribute data in memory is of type - * NC_LONG. - * @param ip Attribute data. - * - * @return ::NC_NOERR No error. - * @return ::NC_EBADID Bad ncid. - * @author Ed Hartnett - */ -int -nc4_get_att_tc(int ncid, int varid, const char *name, nc_type mem_type, - int mem_type_is_long, void *ip) -{ - NC *nc; - NC_HDF5_FILE_INFO_T *h5; + 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)); - LOG((3, "nc4_get_att_tc: ncid 0x%x varid %d name %s mem_type %d", - ncid, varid, name, mem_type)); + 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); + } + } - /* Find metadata. */ - if (!(nc = nc4_find_nc_file(ncid,NULL))) - return NC_EBADID; + /* We must have two valid types to continue. */ + if (file_type == NC_NAT || mem_type == NC_NAT) + return NC_EBADTYPE; - /* get netcdf-4 metadata */ - h5 = NC4_DATA(nc); - assert(h5); + /* Get information about this type. */ + if ((retval = nc4_get_typelen_mem(h5, file_type, 0, &type_size))) + return retval; - return nc4_get_att(ncid, nc, varid, name, NULL, mem_type, - NULL, NULL, mem_type_is_long, ip); -} + /* 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; -/** - * @internal Write an attribute. - * - * @param ncid File and group ID. - * @param varid Variable ID. - * @param name Name of attribute. - * @param xtype Type of the attribute. - * @param nelems Number of elements in attribute array. - * @param value Attribute data. - * @param memtype Type of data in memory. - * - * @return ::NC_NOERR No error. - * @return ::NC_EBADID Bad ncid. - * @author Ed Hartnett - */ -int -NC4_put_att(int ncid, int varid, const char *name, nc_type xtype, - size_t nelems, const void *value, nc_type memtype) -{ - return nc4_put_att_tc(ncid, varid, name, xtype, memtype, 0, nelems, value); + /* 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, &att))) + BAIL (ret); + 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) + { + 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; + + /* Get the length of the veriable data type. */ + 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); + } + + /* 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->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 (*(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); + } + + /* 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), 0, 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; } /** @@ -1144,5 +967,5 @@ NC4_put_att(int ncid, int varid, const char *name, nc_type xtype, int NC4_get_att(int ncid, int varid, const char *name, void *value, nc_type memtype) { - return nc4_get_att_tc(ncid, varid, name, memtype, 0, value); + return nc4_get_att(ncid, varid, name, NULL, memtype, NULL, NULL, value); } diff --git a/libsrc4/nc4internal.c b/libsrc4/nc4internal.c index c865de587..69cc521d6 100644 --- a/libsrc4/nc4internal.c +++ b/libsrc4/nc4internal.c @@ -1,3 +1,7 @@ +/* Copyright 2003-2018, University Corporation for Atmospheric + * Research. See the COPYRIGHT file for copying and redistribution + * conditions. + */ /** * @file * @internal @@ -9,9 +13,6 @@ * buffer of metadata information, i.e. the linked list of NC * structs. * - * Copyright 2003-2011, University Corporation for Atmospheric - * Research. See the COPYRIGHT file for copying and redistribution - * conditions. * @author Ed Hartnett */ #include "config.h" @@ -101,13 +102,13 @@ nc4_check_name(const char *name, char *norm_name) { char *temp; int retval; - + /* Check for NULL. */ if (!name) return NC_EINVAL; assert(norm_name); - + /* Check for NULL. */ if (!name) return NC_EINVAL; @@ -574,7 +575,7 @@ nc4_rec_find_named_type(NC_GRP_INFO_T *start_grp, char *name) } /** - * @internal Recursively hunt for a netCDF type id. + * @internal Recursively hunt for a netCDF type id. * * @param start_grp Pointer to starting group info. * @param target_nc_typeid NetCDF type ID to find. @@ -689,9 +690,8 @@ nc4_find_dim_len(NC_GRP_INFO_T *grp, int dimid, size_t **len) return NC_NOERR; } -/* Given a group, find an att. */ /** - * @internal + * @internal Given a group, find an att. * * @param grp Pointer to group info struct. * @param varid Variable ID. @@ -1633,7 +1633,7 @@ nc4_att_list_del(NC_ATT_INFO_T **list, NC_ATT_INFO_T *att) /** * @internal Break a coordinate variable to separate the dimension and - * the variable. + * the variable. * * This is called from nc_rename_dim() and nc_rename_var(). In some * renames, the coord variable must stay, but it is no longer a coord @@ -1682,7 +1682,7 @@ nc4_break_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *coord_var, NC_DIM_INFO_T /* Remove the atts that go with being a coordinate var. */ /* if ((retval = remove_coord_atts(coord_var->hdf_datasetid))) */ /* return retval; */ - + /* Detach dimension from variable */ coord_var->dimscale = NC_FALSE; dim->coord_var = NULL; @@ -1722,20 +1722,20 @@ delete_existing_dimscale_dataset(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T *d assert(grp && dim); LOG((2, "%s: deleting dimscale dataset %s dimid %d", __func__, dim->name, dimid)); - + /* Detach dimscale from any variables using it */ if ((retval = rec_detach_scales(grp, dimid, dim->hdf_dimscaleid)) < 0) return retval; - + /* Close the HDF5 dataset */ - if (H5Dclose(dim->hdf_dimscaleid) < 0) + if (H5Dclose(dim->hdf_dimscaleid) < 0) return NC_EHDFERR; dim->hdf_dimscaleid = 0; - + /* Now delete the dataset. */ if (H5Gunlink(grp->hdf_grpid, dim->name) < 0) return NC_EHDFERR; - + return NC_NOERR; } @@ -1758,7 +1758,7 @@ nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim) assert(grp && var && dim); LOG((3, "%s: dim->name %s var->name %s", __func__, dim->name, var->name)); - + /* Detach dimscales from the [new] coordinate variable */ if(var->dimscale_attached) { diff --git a/nc_test4/tst_atts.c b/nc_test4/tst_atts.c index 76ae57dfb..0c042575c 100644 --- a/nc_test4/tst_atts.c +++ b/nc_test4/tst_atts.c @@ -1,52 +1,285 @@ -/* This is part of the netCDF package. - Copyright 2006 University Corporation for Atmospheric Research/Unidata. - See COPYRIGHT file for conditions of use. +/* This is part of the netCDF package. Copyright 2006-2018 University + Corporation for Atmospheric Research/Unidata. See COPYRIGHT file + for conditions of use. - This is a very simple example which writes a netCDF file with - Unicode names encoded with UTF-8. It is the NETCDF3 equivalent - of tst_unicode.c + Test the netCDF-4 attribute code. - $Id$ + Ed Hartnett */ #include -#include #include #include "err_macros.h" -#include -#include +#include "nc4internal.h" /* The data file we will create. */ #define FILE_NAME "tst_atts.nc" +/* Names of attributes. */ +#define OLD_NAME "Constantinople" +#define OLD_NAME_2 "Constantinopolis" +#define NEW_NAME "Istanbul" + +/* Contents of attributes. */ +#define CONTENTS "Lots of people!" +#define CONTENTS_2 "Lots of people!!" /* 1 longer than CONTENTS */ +#define CONTENTS_3 "Lots 0f pe0ple!" /* same len as CONTENTS */ +#define VAR_NAME "Earth" + int main(int argc, char **argv) { printf("\n*** Testing netCDF-4 attributes.\n"); + nc_set_log_level(3); + printf("*** testing attribute renaming for read-only file..."); + { + int ncid; + + /* Create a file with an att. */ + if (nc_create(FILE_NAME, NC_NETCDF4|NC_CLOBBER, &ncid)) ERR; + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME, strlen(CONTENTS), + CONTENTS)) ERR; + if (nc_close(ncid)) ERR; + + /* Reopen the file read-only. */ + if (nc_open(FILE_NAME, NC_NOWRITE, &ncid)) ERR; + + /* Try to rename the att, but it won't work. */ + if (nc_rename_att(ncid, NC_GLOBAL, OLD_NAME, NEW_NAME) != NC_EPERM) ERR; + + /* Try to create another att, it also won't work. */ + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME_2, strlen(CONTENTS), + CONTENTS) != NC_EPERM) ERR; + + /* Try to delete the att. More failure ensues. */ + if (nc_del_att(ncid, NC_GLOBAL, OLD_NAME) != NC_EPERM) ERR; + + if (nc_close(ncid)) ERR; + } + SUMMARIZE_ERR; + printf("*** testing deleting atts..."); + { + int ncid; + int natts; + + /* Create a file with two atts. */ + if (nc_create(FILE_NAME, NC_NETCDF4|NC_CLOBBER, &ncid)) ERR; + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME, strlen(CONTENTS), + CONTENTS)) ERR; + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME_2, 0, NULL)) ERR; + + /* These will not work. */ + if (nc_del_att(ncid + TEST_VAL_42, NC_GLOBAL, OLD_NAME) != NC_EBADID) ERR; + if (nc_del_att(ncid, TEST_VAL_42, OLD_NAME) != NC_ENOTVAR) ERR; + if (nc_del_att(ncid, NC_GLOBAL, NULL) != NC_EINVAL) ERR; + if (nc_del_att(ncid, NC_GLOBAL, NEW_NAME) != NC_ENOTATT) ERR; + + /* End define mode. It redef will be called automatically. */ + if (nc_enddef(ncid)) ERR; + + /* Delete the attribute. */ + if (nc_del_att(ncid, NC_GLOBAL, OLD_NAME)) ERR; + if (nc_close(ncid)) ERR; + + /* Reopen the file. */ + if (nc_open(FILE_NAME, 0, &ncid)) ERR; + if (nc_inq_natts(ncid, &natts)) ERR; + if (natts != 1) ERR; + if (nc_close(ncid)) ERR; + } + SUMMARIZE_ERR; + printf("*** testing deleting atts classic model..."); + { + int ncid; + int natts; + + /* Create a file with an att. */ + if (nc_create(FILE_NAME, NC_NETCDF4|NC_CLASSIC_MODEL, &ncid)) ERR; + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME, strlen(CONTENTS), + CONTENTS)) ERR; + + /* End define mode. */ + if (nc_enddef(ncid)) ERR; + + /* This will not work. */ + if (nc_del_att(ncid, NC_GLOBAL, OLD_NAME) != NC_ENOTINDEFINE) ERR; + + /* Delete the attribute. Redef is needed since this is a classic + * model file. */ + if (nc_redef(ncid)) ERR; + if (nc_del_att(ncid, NC_GLOBAL, OLD_NAME)) ERR; + if (nc_close(ncid)) ERR; + + /* Reopen the file. */ + if (nc_open(FILE_NAME, 0, &ncid)) ERR; + if (nc_inq_natts(ncid, &natts)) ERR; + if (natts) ERR; + if (nc_close(ncid)) ERR; + } + SUMMARIZE_ERR; + printf("*** testing over-writing atts classic model..."); + { + int ncid; + int natts; + char *data_in; + + if (!(data_in = malloc(strlen(CONTENTS) + 1))) ERR; + + /* Create a file with an att. */ + if (nc_create(FILE_NAME, NC_NETCDF4|NC_CLASSIC_MODEL, &ncid)) ERR; + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME, strlen(CONTENTS), + CONTENTS)) ERR; + + /* End define mode. */ + if (nc_enddef(ncid)) ERR; + + /* Try and write a new att. Won't work. */ + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME_2, strlen(CONTENTS_2), + CONTENTS_2) != NC_EINDEFINE) ERR; + + /* This will not work. Overwriting att must be same length or + * shorter if not in define mode. */ + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME, strlen(CONTENTS_2), + CONTENTS_2) != NC_EINDEFINE) ERR; + + /* Now overwrite the att. */ + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME, strlen(CONTENTS_3), + CONTENTS_3)) ERR; + + /* Delete the attribute. Redef is needed since this is a classic + * model file. This should work but does not. */ + if (nc_redef(ncid)) ERR; + if (nc_del_att(ncid, NC_GLOBAL, OLD_NAME)) ERR; + if (nc_close(ncid)) ERR; + + /* Reopen the file. */ + if (nc_open(FILE_NAME, 0, &ncid)) ERR; + if (nc_inq_natts(ncid, &natts)) ERR; + /* If delete worked, natts would be 0. */ + /* if (natts != 0) ERR; */ + if (natts != 1) ERR; + + /* Get the attribute. */ + if (nc_get_att_text(ncid, NC_GLOBAL, OLD_NAME, data_in)) ERR; + /* if (strncmp(CONTENTS_3, data_in, strlen(CONTENTS))) ERR; */ + free(data_in); + if (nc_close(ncid)) ERR; + } + SUMMARIZE_ERR; printf("*** testing attribute renaming for a global attribute..."); { -#define OLD_NAME "Constantinople" -#define NEW_NAME "Istanbul" -#define CONTENTS "Lots of people!" - int ncid, attid; char *data_in; + char too_long_name[NC_MAX_NAME + 2]; + char name_in[NC_MAX_NAME + 1]; + + /* Set up a name that is too long for netCDF. */ + memset(too_long_name, 'a', NC_MAX_NAME + 1); + too_long_name[NC_MAX_NAME + 1] = 0; if (!(data_in = malloc(strlen(CONTENTS) + 1))) ERR; /* Create a file with an att. */ if (nc_create(FILE_NAME, NC_NETCDF4|NC_CLOBBER, &ncid)) ERR; - if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME, strlen(CONTENTS), - CONTENTS)) ERR; + if (nc_def_var(ncid, VAR_NAME, NC_INT, 0, NULL, NULL)) ERR; + /* These will not work. */ + if (nc_put_att_text(ncid + TEST_VAL_42, NC_GLOBAL, OLD_NAME, + strlen(CONTENTS), CONTENTS) != NC_EBADID) ERR; + if (nc_put_att_text(ncid, TEST_VAL_42, OLD_NAME, strlen(CONTENTS), + CONTENTS) != NC_ENOTVAR) ERR; + if (nc_put_att_text(ncid, NC_GLOBAL, NULL, strlen(CONTENTS), + CONTENTS) != NC_EBADNAME) ERR; + if (nc_put_att_text(ncid, NC_GLOBAL, BAD_NAME, strlen(CONTENTS), + CONTENTS) != NC_EBADNAME) ERR; + if (nc_put_att_text(ncid, NC_GLOBAL, too_long_name, strlen(CONTENTS), + CONTENTS) != NC_EBADNAME) ERR; + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME, strlen(CONTENTS), + NULL) != NC_EINVAL) ERR; + { + /* Check that the NC_GLOBAL reserved words are rejected. */ + const char** reserved = NC_RESERVED_ATT_LIST; + for ( ; *reserved; reserved++) + { + if (nc_put_att_text(ncid, NC_GLOBAL, *reserved, strlen(CONTENTS), + CONTENTS) != NC_ENAMEINUSE) ERR; + } + } + { + /* Check that the variable reserved words are rejected. */ + const char** reserved = NC_RESERVED_VARATT_LIST; + for ( ; *reserved; reserved++) + { + if (nc_put_att_text(ncid, 0, *reserved, strlen(CONTENTS), + CONTENTS) != NC_ENAMEINUSE) ERR; + } + } + { + /* Check that the read-only reserved words are rejected. */ + const char** reserved = NC_RESERVED_SPECIAL_LIST; + for ( ; *reserved; reserved++) + { + if (nc_put_att_text(ncid, NC_GLOBAL, *reserved, strlen(CONTENTS), + CONTENTS) != NC_ENAMEINUSE) ERR; + } + } + + /* Write the attribute at last. */ + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME, strlen(CONTENTS), + CONTENTS)) ERR; + + /* Write another with different name. */ + if (nc_put_att_text(ncid, NC_GLOBAL, OLD_NAME_2, strlen(CONTENTS), + CONTENTS)) ERR; + + /* These will not work. */ + if (nc_rename_att(ncid + TEST_VAL_42, NC_GLOBAL, OLD_NAME, NEW_NAME) != NC_EBADID) ERR; + if (nc_rename_att(ncid, TEST_VAL_42, OLD_NAME, NEW_NAME) != NC_ENOTVAR) ERR; + if (nc_rename_att(ncid, NC_GLOBAL, OLD_NAME, NULL) != NC_EINVAL) ERR; + if (nc_rename_att(ncid, NC_GLOBAL, NULL, NEW_NAME) != NC_EINVAL) ERR; + if (nc_rename_att(ncid, NC_GLOBAL, NULL, NULL) != NC_EINVAL) ERR; + if (nc_rename_att(ncid, NC_GLOBAL, OLD_NAME, BAD_NAME) != NC_EBADNAME) ERR; + if (nc_rename_att(ncid, NC_GLOBAL, OLD_NAME, too_long_name) != NC_EMAXNAME) ERR; + if (nc_rename_att(ncid, NC_GLOBAL, OLD_NAME, OLD_NAME_2) != NC_ENAMEINUSE) ERR; + /* Rename the att. */ if (nc_rename_att(ncid, NC_GLOBAL, OLD_NAME, NEW_NAME)) ERR; + /* These will not work. */ + if (nc_inq_attid(ncid + TEST_VAL_42, NC_GLOBAL, NEW_NAME, &attid) != NC_EBADID) ERR; + if (nc_inq_attid(ncid, TEST_VAL_42, NEW_NAME, &attid) != NC_ENOTVAR) ERR; + if (nc_inq_attid(ncid, NC_GLOBAL, NULL, &attid) != NC_EBADNAME) ERR; + /* Check the file. */ if (nc_inq_attid(ncid, NC_GLOBAL, NEW_NAME, &attid)) ERR; if (attid != 0) ERR; + + /* This also works. */ + if (nc_inq_attid(ncid, NC_GLOBAL, NEW_NAME, NULL)) ERR; + + /* These won't work. */ + if (nc_inq_attname(ncid + TEST_VAL_42, NC_GLOBAL, attid, name_in) != NC_EBADID) ERR; + if (nc_inq_attname(ncid, TEST_VAL_42, attid, name_in) != NC_ENOTVAR) ERR; + if (nc_inq_attname(ncid, NC_GLOBAL, -1, name_in) != NC_ENOTATT) ERR; + + /* Get the name from the ID. */ + if (nc_inq_attname(ncid, NC_GLOBAL, attid, name_in)) ERR; + if (strcmp(name_in, NEW_NAME)) ERR; + + /* Also works but does little. */ + if (nc_inq_attname(ncid, NC_GLOBAL, attid, NULL)) ERR; + + /* These will not work. */ + if (nc_get_att_text(ncid + TEST_VAL_42, NC_GLOBAL, NEW_NAME, data_in) != NC_EBADID) ERR; + if (nc_get_att_text(ncid, TEST_VAL_42, NEW_NAME, data_in) != NC_ENOTVAR) ERR; + if (nc_get_att_text(ncid, NC_GLOBAL, NULL, data_in) != NC_EBADNAME) ERR; + + /* Get the attribute at last. */ if (nc_get_att_text(ncid, NC_GLOBAL, NEW_NAME, data_in)) ERR; if (strncmp(CONTENTS, data_in, strlen(CONTENTS))) ERR; + + /* This also works. */ + if (nc_get_att_text(ncid, NC_GLOBAL, NEW_NAME, NULL)) ERR; if (nc_close(ncid)) ERR; /* Reopen the file and check again. */ @@ -63,7 +296,6 @@ main(int argc, char **argv) SUMMARIZE_ERR; printf("*** testing attribute renaming for a variable attribute..."); { -#define VAR_NAME "var_name" #define OLD_NAME1 "Constantinople" #define NEW_NAME1 "Istanbul____________" #define CONTENTS1 "Lots of people!" @@ -77,7 +309,7 @@ main(int argc, char **argv) if (nc_create(FILE_NAME, NC_NETCDF4|NC_CLOBBER, &ncid)) ERR; if (nc_def_var(ncid, VAR_NAME, NC_INT, 0, NULL, &varid)) ERR; if (nc_put_att_text(ncid, varid, OLD_NAME1, strlen(CONTENTS1), - CONTENTS1)) ERR; + CONTENTS1)) ERR; /* Rename the att. */ if (nc_rename_att(ncid, varid, OLD_NAME1, NEW_NAME1)) ERR; diff --git a/nc_test4/tst_atts2.c b/nc_test4/tst_atts2.c index 53de6d392..591aca235 100644 --- a/nc_test4/tst_atts2.c +++ b/nc_test4/tst_atts2.c @@ -1,10 +1,10 @@ -/* This is part of the netCDF package. Copyright 2005-2007 University +/* This is part of the netCDF package. Copyright 2005-2018 University Corporation for Atmospheric Research/Unidata. See COPYRIGHT file for conditions of use. Test copy of attributes. - $Id: tst_atts2.c,v 1.7 2010/05/05 22:15:32 dmh Exp $ + Ed Hartnett, Denis Heimbigner, Ward Fisher */ #include @@ -96,6 +96,15 @@ main(int argc, char **argv) /* Create another file, and copy the att. */ if (nc_create(FILE_NAME2, NC_NETCDF4, &ncid2)) ERR; + + /* These will not work. */ + if (nc_copy_att(ncid1 + TEST_VAL_42, NC_GLOBAL, ATT_NAME, ncid2, NC_GLOBAL) != NC_EBADID) ERR; + if (nc_copy_att(ncid1, TEST_VAL_42, ATT_NAME, ncid2, NC_GLOBAL) != NC_ENOTVAR) ERR; + if (nc_copy_att(ncid1, NC_GLOBAL, NULL, ncid2, NC_GLOBAL) != NC_EBADNAME) ERR; + if (nc_copy_att(ncid1, NC_GLOBAL, ATT_NAME, ncid2 + TEST_VAL_42, NC_GLOBAL) != NC_EBADID) ERR; + if (nc_copy_att(ncid1, NC_GLOBAL, ATT_NAME, ncid2, TEST_VAL_42) != NC_ENOTVAR) ERR; + + /* Copy the attribute. */ if (nc_copy_att(ncid1, NC_GLOBAL, ATT_NAME, ncid2, NC_GLOBAL)) ERR; /* Close up. */