/src/netcdf-c/libnczarr/zattr.c
| Line | Count | Source | 
| 1 |  | /* Copyright 2003-2018, University Corporation for Atmospheric | 
| 2 |  |  * Research. See COPYRIGHT file for copying and redistribution | 
| 3 |  |  * conditions. */ | 
| 4 |  |  | 
| 5 |  | /** | 
| 6 |  |  * @file | 
| 7 |  |  * @internal This file handles ZARR attributes. | 
| 8 |  |  * | 
| 9 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 10 |  |  */ | 
| 11 |  |  | 
| 12 |  | #include "zincludes.h" | 
| 13 |  | #include "zfilter.h" | 
| 14 |  |  | 
| 15 |  | #undef ADEBUG | 
| 16 |  |  | 
| 17 |  | /** | 
| 18 |  |  * @internal Get the attribute list for either a varid or NC_GLOBAL | 
| 19 |  |  * | 
| 20 |  |  * @param grp Group | 
| 21 |  |  * @param varid Variable ID | NC_BLOGAL | 
| 22 |  |  * @param varp Pointer that gets pointer to NC_VAR_INFO_T | 
| 23 |  |  * instance. Ignored if NULL. | 
| 24 |  |  * @param attlist Pointer that gets pointer to attribute list. | 
| 25 |  |  * | 
| 26 |  |  * @return NC_NOERR No error. | 
| 27 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 28 |  |  * [Candidate for moving to libsrc4] | 
| 29 |  |  */ | 
| 30 |  | int | 
| 31 |  | ncz_getattlist(NC_GRP_INFO_T *grp, int varid, NC_VAR_INFO_T **varp, NCindex **attlist) | 
| 32 | 0 | { | 
| 33 | 0 |     int retval; | 
| 34 | 0 |     NC_FILE_INFO_T* file = grp->nc4_info; | 
| 35 | 0 |     NCZ_FILE_INFO_T* zinfo = file->format_file_info; | 
| 36 |  | 
 | 
| 37 | 0 |     assert(grp && attlist && file && zinfo); | 
| 38 |  | 
 | 
| 39 | 0 |     if (varid == NC_GLOBAL) | 
| 40 | 0 |     { | 
| 41 |  |         /* Do we need to read the atts? */ | 
| 42 | 0 |         if (!grp->atts_read) | 
| 43 | 0 |             if ((retval = ncz_read_atts(file, (NC_OBJ*)grp))) | 
| 44 | 0 |                 return retval; | 
| 45 |  |  | 
| 46 | 0 |         if (varp) | 
| 47 | 0 |             *varp = NULL; | 
| 48 | 0 |         *attlist = grp->att; | 
| 49 | 0 |     } | 
| 50 | 0 |     else | 
| 51 | 0 |     { | 
| 52 | 0 |         NC_VAR_INFO_T *var; | 
| 53 |  | 
 | 
| 54 | 0 |         if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, (size_t)varid))) | 
| 55 | 0 |             return NC_ENOTVAR; | 
| 56 | 0 |         assert(var->hdr.id == varid); | 
| 57 |  |  | 
| 58 |  |         /* Do we need to read the atts? */ | 
| 59 | 0 |         if (!var->atts_read) | 
| 60 | 0 |             if ((retval = ncz_read_atts(file, (NC_OBJ*)var))) | 
| 61 | 0 |                 return retval; | 
| 62 |  |  | 
| 63 | 0 |         if (varp) | 
| 64 | 0 |             *varp = var; | 
| 65 | 0 |         *attlist = var->att; | 
| 66 | 0 |     } | 
| 67 | 0 |     return NC_NOERR; | 
| 68 | 0 | } | 
| 69 |  |  | 
| 70 |  | /** | 
| 71 |  |  * @internal Get one of the special attributes: | 
| 72 |  |  * See the reserved attribute table in libsrc4/nc4internal.c. | 
| 73 |  |  * The special attributes are the ones marked with NAMEONLYFLAG. | 
| 74 |  |  * For example: NCPROPS, ISNETCDF4ATT, and SUPERBLOCKATT, and CODECS. | 
| 75 |  |  * These atts are not all really in the file, they are constructed on the fly. | 
| 76 |  |  * | 
| 77 |  |  * @param h5 Pointer to ZARR file info struct. | 
| 78 |  |  * @param var Pointer to var info struct; NULL signals global. | 
| 79 |  |  * @param name Name of attribute. | 
| 80 |  |  * @param filetypep Pointer that gets type of the attribute data in | 
| 81 |  |  * file. | 
| 82 |  |  * @param mem_type Type of attribute data in memory. | 
| 83 |  |  * @param lenp Pointer that gets length of attribute array. | 
| 84 |  |  * @param attnump Pointer that gets the attribute number. | 
| 85 |  |  * @param data Attribute data. | 
| 86 |  |  * | 
| 87 |  |  * @return ::NC_NOERR No error. | 
| 88 |  |  * @return ::NC_EBADID Bad ncid. | 
| 89 |  |  * @return ::NC_ERANGE Data conversion out of range. | 
| 90 |  |  * @author Dennis Heimbigner | 
| 91 |  |  */ | 
| 92 |  | int | 
| 93 |  | ncz_get_att_special(NC_FILE_INFO_T* h5, NC_VAR_INFO_T* var, const char* name, | 
| 94 |  |                     nc_type* filetypep, nc_type mem_type, size_t* lenp, | 
| 95 |  |                     int* attnump, void* data) | 
| 96 | 0 | { | 
| 97 | 0 |     int stat = NC_NOERR; | 
| 98 |  |      | 
| 99 |  |     /* Fail if asking for att id */ | 
| 100 | 0 |     if(attnump) | 
| 101 | 0 |         {stat = NC_EATTMETA; goto done;} | 
| 102 |  |  | 
| 103 |  |     /* Handle the per-var case(s) first */ | 
| 104 | 0 |     if(var != NULL) { | 
| 105 |  | #ifdef NETCDF_ENABLE_NCZARR_FILTERS | 
| 106 |  |         if(strcmp(name,NC_ATT_CODECS)==0) {  | 
| 107 |  |             NClist* filters = (NClist*)var->filters; | 
| 108 |  |  | 
| 109 |  |             if(mem_type == NC_NAT) mem_type = NC_CHAR; | 
| 110 |  |             if(mem_type != NC_CHAR) | 
| 111 |  |                 {stat = NC_ECHAR; goto done;} | 
| 112 |  |             if(filetypep) *filetypep = NC_CHAR; | 
| 113 |  |       if(lenp) *lenp = 0; | 
| 114 |  |       if(filters == NULL) goto done;     | 
| 115 |  |       if((stat = NCZ_codec_attr(var,lenp,data))) goto done; | 
| 116 |  |   } | 
| 117 |  | #endif | 
| 118 | 0 |   goto done; | 
| 119 | 0 |     } | 
| 120 |  |  | 
| 121 |  |     /* The global reserved attributes */ | 
| 122 | 0 |     if(strcmp(name,NCPROPS)==0) { | 
| 123 | 0 |         size_t len; | 
| 124 | 0 |         if(h5->provenance.ncproperties == NULL) | 
| 125 | 0 |             {stat = NC_ENOTATT; goto done;} | 
| 126 | 0 |         if(mem_type == NC_NAT) mem_type = NC_CHAR; | 
| 127 | 0 |         if(mem_type != NC_CHAR) | 
| 128 | 0 |             {stat = NC_ECHAR; goto done;} | 
| 129 | 0 |         if(filetypep) *filetypep = NC_CHAR; | 
| 130 | 0 |   len = strlen(h5->provenance.ncproperties); | 
| 131 | 0 |         if(lenp) *lenp = len; | 
| 132 | 0 |         if(data) strncpy((char*)data,h5->provenance.ncproperties,len+1); | 
| 133 | 0 |     } else if(strcmp(name,ISNETCDF4ATT)==0 | 
| 134 | 0 |               || strcmp(name,SUPERBLOCKATT)==0) { | 
| 135 | 0 |         unsigned long long iv = 0; | 
| 136 | 0 |         if(filetypep) *filetypep = NC_INT; | 
| 137 | 0 |         if(lenp) *lenp = 1; | 
| 138 | 0 |         if(strcmp(name,SUPERBLOCKATT)==0) | 
| 139 | 0 |             iv = (unsigned long long)h5->provenance.superblockversion; | 
| 140 | 0 |         else /* strcmp(name,ISNETCDF4ATT)==0 */ | 
| 141 | 0 |             iv = (unsigned long long)NCZ_isnetcdf4(h5); | 
| 142 | 0 |         if(mem_type == NC_NAT) mem_type = NC_INT; | 
| 143 | 0 |         if(data) | 
| 144 | 0 |             switch (mem_type) { | 
| 145 | 0 |             case NC_BYTE: *((char*)data) = (char)iv; break; | 
| 146 | 0 |             case NC_SHORT: *((short*)data) = (short)iv; break; | 
| 147 | 0 |             case NC_INT: *((int*)data) = (int)iv; break; | 
| 148 | 0 |             case NC_UBYTE: *((unsigned char*)data) = (unsigned char)iv; break; | 
| 149 | 0 |             case NC_USHORT: *((unsigned short*)data) = (unsigned short)iv; break; | 
| 150 | 0 |             case NC_UINT: *((unsigned int*)data) = (unsigned int)iv; break; | 
| 151 | 0 |             case NC_INT64: *((long long*)data) = (long long)iv; break; | 
| 152 | 0 |             case NC_UINT64: *((unsigned long long*)data) = (unsigned long long)iv; break; | 
| 153 | 0 |             default: | 
| 154 | 0 |                 {stat = NC_ERANGE; goto done;} | 
| 155 | 0 |             } | 
| 156 | 0 |     } | 
| 157 | 0 | done: | 
| 158 | 0 |     return stat; | 
| 159 |  | 
 | 
| 160 | 0 | } | 
| 161 |  |  | 
| 162 |  | /** | 
| 163 |  |  * @internal I think all atts should be named the exact same thing, to | 
| 164 |  |  * avoid confusion! | 
| 165 |  |  * | 
| 166 |  |  * @param ncid File and group ID. | 
| 167 |  |  * @param varid Variable ID. | 
| 168 |  |  * @param name Name of attribute. | 
| 169 |  |  * @param newname New name for attribute. | 
| 170 |  |  * | 
| 171 |  |  * @return ::NC_NOERR No error. | 
| 172 |  |  * @return ::NC_EBADID Bad ncid. | 
| 173 |  |  * @return ::NC_EMAXNAME New name too long. | 
| 174 |  |  * @return ::NC_EPERM File is read-only. | 
| 175 |  |  * @return ::NC_ENAMEINUSE New name already in use. | 
| 176 |  |  * @return ::NC_ENOTINDEFINE Classic model file not in define mode. | 
| 177 |  |  * @return ::NC_EHDFERR HDF error. | 
| 178 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 179 |  |  * @return ::NC_EINTERNAL Could not rebuild list. | 
| 180 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 181 |  |  */ | 
| 182 |  | int | 
| 183 |  | NCZ_rename_att(int ncid, int varid, const char *name, const char *newname) | 
| 184 | 0 | { | 
| 185 | 0 |     NC_GRP_INFO_T *grp; | 
| 186 | 0 |     NC_FILE_INFO_T *h5; | 
| 187 | 0 |     NC_VAR_INFO_T *var = NULL; | 
| 188 | 0 |     NC_ATT_INFO_T *att; | 
| 189 | 0 |     NCindex *list; | 
| 190 | 0 |     char norm_newname[NC_MAX_NAME + 1], norm_name[NC_MAX_NAME + 1]; | 
| 191 | 0 |     int retval = NC_NOERR; | 
| 192 |  | 
 | 
| 193 | 0 |     if (!name || !newname) | 
| 194 | 0 |         return NC_EINVAL; | 
| 195 |  |  | 
| 196 | 0 |     LOG((2, "nc_rename_att: ncid 0x%x varid %d name %s newname %s", | 
| 197 | 0 |          ncid, varid, name, newname)); | 
| 198 |  |  | 
| 199 |  |     /* If the new name is too long, that's an error. */ | 
| 200 | 0 |     if (strlen(newname) > NC_MAX_NAME) | 
| 201 | 0 |         return NC_EMAXNAME; | 
| 202 |  |  | 
| 203 |  |     /* Find info for this file, group, and h5 info. */ | 
| 204 | 0 |     if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) | 
| 205 | 0 |         return retval; | 
| 206 | 0 |     assert(h5 && grp); | 
| 207 |  |  | 
| 208 |  |     /* If the file is read-only, return an error. */ | 
| 209 | 0 |     if (h5->no_write) | 
| 210 | 0 |         return NC_EPERM; | 
| 211 |  |  | 
| 212 |  |     /* Check and normalize the name. */ | 
| 213 | 0 |     if ((retval = nc4_check_name(newname, norm_newname))) | 
| 214 | 0 |         return retval; | 
| 215 |  |  | 
| 216 |  |     /* Get the list of attributes. */ | 
| 217 | 0 |     if ((retval = ncz_getattlist(grp, varid, &var, &list))) | 
| 218 | 0 |         return retval; | 
| 219 |  |  | 
| 220 |  |     /* Is new name in use? */ | 
| 221 | 0 |     att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_newname); | 
| 222 | 0 |     if(att != NULL) | 
| 223 | 0 |         return NC_ENAMEINUSE; | 
| 224 |  |  | 
| 225 |  |     /* Normalize name and find the attribute. */ | 
| 226 | 0 |     if ((retval = nc4_normalize_name(name, norm_name))) | 
| 227 | 0 |         return retval; | 
| 228 |  |  | 
| 229 | 0 |     att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_name); | 
| 230 | 0 |     if (!att) | 
| 231 | 0 |         return NC_ENOTATT; | 
| 232 |  |  | 
| 233 |  |     /* If we're not in define mode, new name must be of equal or | 
| 234 |  |        less size, if complying with strict NC3 rules. */ | 
| 235 | 0 |     if (!(h5->flags & NC_INDEF) && strlen(norm_newname) > strlen(att->hdr.name) && | 
| 236 | 0 |         (h5->cmode & NC_CLASSIC_MODEL)) | 
| 237 | 0 |         return NC_ENOTINDEFINE; | 
| 238 |  |  | 
| 239 |  |     /* Copy the new name into our metadata. */ | 
| 240 | 0 |     if(att->hdr.name) free(att->hdr.name); | 
| 241 | 0 |     if (!(att->hdr.name = strdup(norm_newname))) | 
| 242 | 0 |         return NC_ENOMEM; | 
| 243 |  |  | 
| 244 | 0 |     att->dirty = NC_TRUE; | 
| 245 |  |  | 
| 246 |  |     /* Rehash the attribute list so that the new name is used */ | 
| 247 | 0 |     if(!ncindexrebuild(list)) | 
| 248 | 0 |         return NC_EINTERNAL; | 
| 249 |  |  | 
| 250 |  |     /* Mark attributes on variable dirty, so they get written */ | 
| 251 | 0 |     if(var) | 
| 252 | 0 |         var->attr_dirty = NC_TRUE; | 
| 253 | 0 |     return retval; | 
| 254 | 0 | } | 
| 255 |  |  | 
| 256 |  | /** | 
| 257 |  |  * @internal Delete an att. Rub it out. Push the button on | 
| 258 |  |  * it. Liquidate it. Bump it off. Take it for a one-way | 
| 259 |  |  * ride. Terminate it. | 
| 260 |  |  * | 
| 261 |  |  * @param ncid File and group ID. | 
| 262 |  |  * @param varid Variable ID. | 
| 263 |  |  * @param name Name of attribute to delete. | 
| 264 |  |  * | 
| 265 |  |  * @return ::NC_NOERR No error. | 
| 266 |  |  * @return ::NC_EBADID Bad ncid. | 
| 267 |  |  * @return ::NC_ENOTATT Attribute not found. | 
| 268 |  |  * @return ::NC_EINVAL No name provided. | 
| 269 |  |  * @return ::NC_EPERM File is read only. | 
| 270 |  |  * @return ::NC_ENOTINDEFINE Classic model not in define mode. | 
| 271 |  |  * @return ::NC_EINTERNAL Could not rebuild list. | 
| 272 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 273 |  |  */ | 
| 274 |  | int | 
| 275 |  | NCZ_del_att(int ncid, int varid, const char *name) | 
| 276 | 0 | { | 
| 277 | 0 |     NC_GRP_INFO_T *grp; | 
| 278 | 0 |     NC_VAR_INFO_T *var; | 
| 279 | 0 |     NC_FILE_INFO_T *h5; | 
| 280 | 0 |     NC_ATT_INFO_T *att; | 
| 281 | 0 |     NCindex* attlist = NULL; | 
| 282 | 0 |     size_t i; | 
| 283 | 0 |     int deletedid; | 
| 284 | 0 |     int retval; | 
| 285 |  |  | 
| 286 |  |     /* Name must be provided. */ | 
| 287 | 0 |     if (!name) | 
| 288 | 0 |         return NC_EINVAL; | 
| 289 |  |  | 
| 290 | 0 |     LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name)); | 
| 291 |  |  | 
| 292 |  |     /* Find info for this file, group, and h5 info. */ | 
| 293 | 0 |     if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) | 
| 294 | 0 |         return retval; | 
| 295 | 0 |     assert(h5 && grp); | 
| 296 |  |  | 
| 297 |  |     /* If the file is read-only, return an error. */ | 
| 298 | 0 |     if (h5->no_write) | 
| 299 | 0 |         return NC_EPERM; | 
| 300 |  |  | 
| 301 |  |     /* If file is not in define mode, return error for classic model | 
| 302 |  |      * files, otherwise switch to define mode. */ | 
| 303 | 0 |     if (!(h5->flags & NC_INDEF)) | 
| 304 | 0 |     { | 
| 305 | 0 |         if (h5->cmode & NC_CLASSIC_MODEL) | 
| 306 | 0 |             return NC_ENOTINDEFINE; | 
| 307 | 0 |         if ((retval = NCZ_redef(ncid))) | 
| 308 | 0 |             return retval; | 
| 309 | 0 |     } | 
| 310 |  |  | 
| 311 |  |     /* Get either the global or a variable attribute list. */ | 
| 312 | 0 |     if ((retval = ncz_getattlist(grp, varid, &var, &attlist))) | 
| 313 | 0 |         return retval; | 
| 314 |  |  | 
| 315 |  | #ifdef LOOK | 
| 316 |  |     /* Determine the location id in the ZARR file. */ | 
| 317 |  |     if (varid == NC_GLOBAL) | 
| 318 |  |         locid = ((NCZ_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid; | 
| 319 |  |     else if (var->created) | 
| 320 |  |         locid = ((NCZ_VAR_INFO_T *)(var->format_var_info))->hdf_datasetid; | 
| 321 |  | #endif | 
| 322 |  |  | 
| 323 |  |     /* Now find the attribute by name. */ | 
| 324 | 0 |     if (!(att = (NC_ATT_INFO_T*)ncindexlookup(attlist, name))) | 
| 325 | 0 |         return NC_ENOTATT; | 
| 326 |  |  | 
| 327 |  |     /* Reclaim the content of the attribute */ | 
| 328 | 0 |     if(att->data) { | 
| 329 | 0 |   if((retval = NC_reclaim_data_all(h5->controller,att->nc_typeid,att->data,att->len))) return retval; | 
| 330 | 0 |     } | 
| 331 | 0 |     att->data = NULL; | 
| 332 | 0 |     att->len = 0; | 
| 333 |  |  | 
| 334 |  |     /* Delete it from the ZARR file, if it's been created. */ | 
| 335 | 0 |     if (att->created) | 
| 336 | 0 |     { | 
| 337 |  | #ifdef LOOK | 
| 338 |  |         assert(locid); | 
| 339 |  |         if (H5Adelete(locid, att->hdr.name) < 0) | 
| 340 |  |             return NC_EATTMETA; | 
| 341 |  | #endif | 
| 342 | 0 |     } | 
| 343 |  | 
 | 
| 344 | 0 |     deletedid = att->hdr.id; | 
| 345 |  |  | 
| 346 |  |     /* reclaim associated NCZarr info */ | 
| 347 | 0 |     { | 
| 348 | 0 |   NCZ_ATT_INFO_T* za = (NCZ_ATT_INFO_T*)att->format_att_info; | 
| 349 | 0 |   nullfree(za); | 
| 350 | 0 |     } | 
| 351 |  |  | 
| 352 |  |     /* Remove this attribute in this list */ | 
| 353 | 0 |     if ((retval = nc4_att_list_del(attlist, att))) | 
| 354 | 0 |         return retval; | 
| 355 |  |  | 
| 356 |  |     /* Renumber all attributes with higher indices. */ | 
| 357 | 0 |     for (i = 0; i < ncindexsize(attlist); i++) | 
| 358 | 0 |     { | 
| 359 | 0 |         NC_ATT_INFO_T *a; | 
| 360 | 0 |         if (!(a = (NC_ATT_INFO_T *)ncindexith(attlist, i))) | 
| 361 | 0 |             continue; | 
| 362 | 0 |         if (a->hdr.id > deletedid) | 
| 363 | 0 |             a->hdr.id--; | 
| 364 | 0 |     } | 
| 365 |  |  | 
| 366 |  |     /* Rebuild the index. */ | 
| 367 | 0 |     if (!ncindexrebuild(attlist)) | 
| 368 | 0 |         return NC_EINTERNAL; | 
| 369 |  |  | 
| 370 | 0 |     return NC_NOERR; | 
| 371 | 0 | } | 
| 372 |  |  | 
| 373 |  | /** | 
| 374 |  |  * @internal This will return the length of a netcdf atomic data type | 
| 375 |  |  * in bytes. | 
| 376 |  |  * | 
| 377 |  |  * @param type A netcdf atomic type. | 
| 378 |  |  * | 
| 379 |  |  * @return Type size in bytes, or -1 if type not found. | 
| 380 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 381 |  |  */ | 
| 382 |  | static int | 
| 383 |  | nc4typelen(nc_type type) | 
| 384 | 0 | { | 
| 385 | 0 |     switch(type){ | 
| 386 | 0 |     case NC_BYTE: | 
| 387 | 0 |     case NC_CHAR: | 
| 388 | 0 |     case NC_UBYTE: | 
| 389 | 0 |         return 1; | 
| 390 | 0 |     case NC_USHORT: | 
| 391 | 0 |     case NC_SHORT: | 
| 392 | 0 |         return 2; | 
| 393 | 0 |     case NC_FLOAT: | 
| 394 | 0 |     case NC_INT: | 
| 395 | 0 |     case NC_UINT: | 
| 396 | 0 |         return 4; | 
| 397 | 0 |     case NC_DOUBLE: | 
| 398 | 0 |     case NC_INT64: | 
| 399 | 0 |     case NC_UINT64: | 
| 400 | 0 |         return 8; | 
| 401 | 0 |     } | 
| 402 | 0 |     return -1; | 
| 403 | 0 | } | 
| 404 |  |  | 
| 405 |  | /** | 
| 406 |  |  * @internal | 
| 407 |  |  * Write an attribute to a netCDF-4/NCZ file, converting | 
| 408 |  |  * data type if necessary. | 
| 409 |  |  * | 
| 410 |  |  * @param ncid File and group ID. | 
| 411 |  |  * @param varid Variable ID. | 
| 412 |  |  * @param name Name of attribute. | 
| 413 |  |  * @param file_type Type of the attribute data in file. | 
| 414 |  |  * @param len Number of elements in attribute array. | 
| 415 |  |  * @param data Attribute data. | 
| 416 |  |  * @param mem_type Type of data in memory. | 
| 417 |  |  * @param force write even if the attribute is special | 
| 418 |  |  * | 
| 419 |  |  * @return ::NC_NOERR No error. | 
| 420 |  |  * @return ::NC_EINVAL Invalid parameters. | 
| 421 |  |  * @return ::NC_EBADID Bad ncid. | 
| 422 |  |  * @return ::NC_ENOTVAR Variable not found. | 
| 423 |  |  * @return ::NC_EBADNAME Name contains illegal characters. | 
| 424 |  |  * @return ::NC_ENAMEINUSE Name already in use. | 
| 425 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 426 |  |  */ | 
| 427 |  | int | 
| 428 |  | ncz_put_att(NC_GRP_INFO_T* grp, int varid, const char *name, nc_type file_type, | 
| 429 |  |             size_t len, const void *data, nc_type mem_type, int force) | 
| 430 | 0 | { | 
| 431 | 0 |     NC* nc; | 
| 432 | 0 |     NC_FILE_INFO_T *h5 = NULL; | 
| 433 | 0 |     NC_VAR_INFO_T *var = NULL; | 
| 434 | 0 |     NCindex* attlist = NULL; | 
| 435 | 0 |     NC_ATT_INFO_T* att; | 
| 436 | 0 |     char norm_name[NC_MAX_NAME + 1]; | 
| 437 | 0 |     nc_bool_t new_att = NC_FALSE; | 
| 438 | 0 |     int retval = NC_NOERR, range_error = 0; | 
| 439 | 0 |     size_t type_size; | 
| 440 | 0 |     int ret; | 
| 441 | 0 |     int ncid; | 
| 442 | 0 |     void* copy = NULL; | 
| 443 |  |     /* Save the old att data and length and old fillvalue in case we need to rollback on error */ | 
| 444 | 0 |     struct Save { | 
| 445 | 0 |   size_t len; | 
| 446 | 0 |   void* data; | 
| 447 | 0 |         nc_type type; /* In case we change the type of the attribute */ | 
| 448 | 0 |     } attsave = {0,NULL,-1}; | 
| 449 | 0 |     struct Save fillsave = {0,NULL,-1}; | 
| 450 |  | 
 | 
| 451 | 0 |     h5 = grp->nc4_info; | 
| 452 | 0 |     nc = h5->controller; | 
| 453 | 0 |     assert(nc && grp && h5); | 
| 454 |  | 
 | 
| 455 | 0 |     ncid = nc->ext_ncid | grp->hdr.id; | 
| 456 |  |  | 
| 457 |  |     /* Find att, if it exists. (Must check varid first or nc_test will | 
| 458 |  |      * break.) This also does lazy att reads if needed. */ | 
| 459 | 0 |     if ((ret = ncz_getattlist(grp, varid, &var, &attlist))) | 
| 460 | 0 |         return ret; | 
| 461 |  |  | 
| 462 |  |     /* The length needs to be positive (cast needed for braindead | 
| 463 |  |        systems with signed size_t). */ | 
| 464 | 0 |     if((unsigned long) len > X_INT_MAX) | 
| 465 | 0 |         return NC_EINVAL; | 
| 466 |  |  | 
| 467 |  |     /* Check name before LOG statement. */ | 
| 468 | 0 |     if (!name || strlen(name) > NC_MAX_NAME) | 
| 469 | 0 |         return NC_EBADNAME; | 
| 470 |  |  | 
| 471 | 0 |     LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d", | 
| 472 | 0 |          __func__,ncid, varid, name, file_type, mem_type, len)); | 
| 473 |  |  | 
| 474 |  |     /* If len is not zero, then there must be some data. */ | 
| 475 | 0 |     if (len && !data) | 
| 476 | 0 |         return NC_EINVAL; | 
| 477 |  |  | 
| 478 |  |     /* If the file is read-only, return an error. */ | 
| 479 | 0 |     if (h5->no_write) | 
| 480 | 0 |         return NC_EPERM; | 
| 481 |  |  | 
| 482 |  |     /* Check and normalize the name. */ | 
| 483 | 0 |     if ((retval = nc4_check_name(name, norm_name))) | 
| 484 | 0 |         return retval; | 
| 485 |  |  | 
| 486 |  |     /* Check that a reserved att name is not being used improperly */ | 
| 487 | 0 |     const NC_reservedatt* ra = NC_findreserved(name); | 
| 488 | 0 |     if(ra != NULL && !force) { | 
| 489 |  |         /* case 1: grp=root, varid==NC_GLOBAL, flags & READONLYFLAG */ | 
| 490 | 0 |         if (nc->ext_ncid == ncid && varid == NC_GLOBAL && grp->parent == NULL | 
| 491 | 0 |             && (ra->flags & READONLYFLAG)) | 
| 492 | 0 |             return NC_ENAMEINUSE; | 
| 493 |  |         /* case 2: grp=NA, varid!=NC_GLOBAL, flags & HIDDENATTRFLAG */ | 
| 494 | 0 |         if (varid != NC_GLOBAL && (ra->flags & HIDDENATTRFLAG)) | 
| 495 | 0 |             return NC_ENAMEINUSE; | 
| 496 | 0 |     } | 
| 497 |  |  | 
| 498 |  |     /* See if there is already an attribute with this name. */ | 
| 499 | 0 |     att = (NC_ATT_INFO_T*)ncindexlookup(attlist,norm_name); | 
| 500 |  | 
 | 
| 501 | 0 |     if (!att) | 
| 502 | 0 |     { | 
| 503 |  |         /* If this is a new att, require define mode. */ | 
| 504 | 0 |         if (!(h5->flags & NC_INDEF)) | 
| 505 | 0 |         { | 
| 506 |  | 
 | 
| 507 | 0 |             if (h5->cmode & NC_CLASSIC_MODEL) | 
| 508 | 0 |                 return NC_ENOTINDEFINE; | 
| 509 | 0 |             if ((retval = NCZ_redef(ncid))) | 
| 510 | 0 |                 BAIL(retval); | 
| 511 | 0 |         } | 
| 512 | 0 |         new_att = NC_TRUE; | 
| 513 | 0 |     } | 
| 514 | 0 |     else | 
| 515 | 0 |     { | 
| 516 |  |         /* For an existing att, if we're not in define mode, the len | 
| 517 |  |            must not be greater than the existing len for classic model. */ | 
| 518 | 0 |         if (!(h5->flags & NC_INDEF) && | 
| 519 | 0 |             len * (size_t)nc4typelen(file_type) > (size_t)att->len * (size_t)nc4typelen(att->nc_typeid)) | 
| 520 | 0 |         { | 
| 521 | 0 |             if (h5->cmode & NC_CLASSIC_MODEL) | 
| 522 | 0 |                 return NC_ENOTINDEFINE; | 
| 523 | 0 |             if ((retval = NCZ_redef(ncid))) | 
| 524 | 0 |                 BAIL(retval); | 
| 525 | 0 |         } | 
| 526 | 0 |     } | 
| 527 |  |  | 
| 528 |  |     /* We must have two valid types to continue. */ | 
| 529 | 0 |     if (file_type == NC_NAT || mem_type == NC_NAT) | 
| 530 | 0 |         return NC_EBADTYPE; | 
| 531 |  |  | 
| 532 |  |     /* No character conversions are allowed. */ | 
| 533 | 0 |     if (file_type != mem_type && | 
| 534 | 0 |         (file_type == NC_CHAR || mem_type == NC_CHAR || | 
| 535 | 0 |          file_type == NC_STRING || mem_type == NC_STRING)) | 
| 536 | 0 |         return NC_ECHAR; | 
| 537 |  |  | 
| 538 |  |     /* For classic mode file, only allow atts with classic types to be | 
| 539 |  |      * created. */ | 
| 540 | 0 |     if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE) | 
| 541 | 0 |         return NC_ESTRICTNC3; | 
| 542 |  |  | 
| 543 |  |     /* Add to the end of the attribute list, if this att doesn't | 
| 544 |  |        already exist. */ | 
| 545 | 0 |     if (new_att) | 
| 546 | 0 |     { | 
| 547 | 0 |         LOG((3, "adding attribute %s to the list...", norm_name)); | 
| 548 | 0 |         if ((ret = nc4_att_list_add(attlist, norm_name, &att))) | 
| 549 | 0 |             BAIL(ret); | 
| 550 |  |  | 
| 551 |  |         /* Allocate storage for the ZARR specific att info. */ | 
| 552 | 0 |         if (!(att->format_att_info = calloc(1, sizeof(NCZ_ATT_INFO_T)))) | 
| 553 | 0 |             BAIL(NC_ENOMEM); | 
| 554 |  |  | 
| 555 | 0 |   if(varid == NC_GLOBAL) | 
| 556 | 0 |       att->container = (NC_OBJ*)grp; | 
| 557 | 0 |   else | 
| 558 | 0 |       att->container = (NC_OBJ*)var; | 
| 559 | 0 |     } | 
| 560 |  |  | 
| 561 |  |     /* Now fill in the metadata. */ | 
| 562 | 0 |     att->dirty = NC_TRUE; | 
| 563 |  |  | 
| 564 |  |     /* When we reclaim existing data, make sure to use the right type */  | 
| 565 | 0 |     if(new_att) attsave.type = file_type; else attsave.type = att->nc_typeid; | 
| 566 | 0 |     att->nc_typeid = file_type; | 
| 567 |  |  | 
| 568 |  |     /* Get information about this (possibly new) type. */ | 
| 569 | 0 |     if ((retval = nc4_get_typelen_mem(h5, file_type, &type_size))) | 
| 570 | 0 |         return retval; | 
| 571 |  |  | 
| 572 | 0 |     if (att->data) | 
| 573 | 0 |     { | 
| 574 | 0 |   assert(attsave.data == NULL); | 
| 575 | 0 |   attsave.data = att->data; | 
| 576 | 0 |   attsave.len = att->len; | 
| 577 | 0 |         att->data = NULL; | 
| 578 | 0 |     } | 
| 579 |  |  | 
| 580 |  |     /* If this is the _FillValue attribute, then we will also have to | 
| 581 |  |      * copy the value to the fill_value pointer of the NC_VAR_INFO_T | 
| 582 |  |      * struct for this var. (But ignore a global _FillValue | 
| 583 |  |      * attribute). Also kill the cache fillchunk as no longer valid */ | 
| 584 | 0 |     if (!strcmp(att->hdr.name, NC_FillValue) && varid != NC_GLOBAL) | 
| 585 | 0 |     { | 
| 586 |  |         /* Fill value must have exactly one value */ | 
| 587 | 0 |         if (len != 1) | 
| 588 | 0 |             return NC_EINVAL; | 
| 589 |  |  | 
| 590 |  |         /* If we already wrote to the dataset, then return an error. */ | 
| 591 | 0 |         if (var->written_to) | 
| 592 | 0 |             return NC_ELATEFILL; | 
| 593 |  |  | 
| 594 |  |         /* Get the length of the veriable data type. */ | 
| 595 | 0 |         if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id, | 
| 596 | 0 |                                           &type_size))) | 
| 597 | 0 |             return retval; | 
| 598 |  |  | 
| 599 |  |         /* Already set a fill value? Now I'll have to free the old | 
| 600 |  |          * one. Make up your damn mind, would you? */ | 
| 601 | 0 |         if (var->fill_value) | 
| 602 | 0 |         { | 
| 603 |  |       /* reclaim later */ | 
| 604 | 0 |       fillsave.data = var->fill_value; | 
| 605 | 0 |       fillsave.type = var->type_info->hdr.id; | 
| 606 | 0 |       fillsave.len = 1; | 
| 607 | 0 |       var->fill_value = NULL; | 
| 608 | 0 |         } | 
| 609 |  |  | 
| 610 |  |         /* Determine the size of the fill value in bytes. */ | 
| 611 |  |    | 
| 612 | 0 |   { | 
| 613 | 0 |       nc_type var_type = var->type_info->hdr.id; | 
| 614 | 0 |         size_t var_type_size = var->type_info->size; | 
| 615 |  |       /* The old code used the var's type as opposed to the att's type; normally same, | 
| 616 |  |          but not required. Now we need to convert from the att's type to the var's type. | 
| 617 |  |          Note that we use mem_type rather than file_type because our data is in the form | 
| 618 |  |          of the memory data. When we later capture the memory data for the actual | 
| 619 |  |          attribute, we will use file_type as the target of the conversion. */ | 
| 620 | 0 |       if(mem_type != var_type && mem_type < NC_STRING && var_type < NC_STRING) { | 
| 621 |  |     /* Need to convert from memory data into copy buffer */ | 
| 622 | 0 |     if((copy = malloc(len*var_type_size))==NULL) BAIL(NC_ENOMEM); | 
| 623 | 0 |                 if ((retval = nc4_convert_type(data, copy, mem_type, var_type, | 
| 624 | 0 |                                                len, &range_error, NULL, | 
| 625 | 0 |                                                (h5->cmode & NC_CLASSIC_MODEL), | 
| 626 | 0 |                  NC_NOQUANTIZE, 0))) | 
| 627 | 0 |                     BAIL(retval); | 
| 628 | 0 |       } else { /* no conversion */ | 
| 629 |  |     /* Still need a copy of the input data */ | 
| 630 | 0 |     copy = NULL; | 
| 631 | 0 |           if((retval = NC_copy_data_all(h5->controller, mem_type, data, 1, ©))) | 
| 632 | 0 |         BAIL(retval); | 
| 633 | 0 |       } | 
| 634 | 0 |       var->fill_value = copy; | 
| 635 | 0 |       copy = NULL; | 
| 636 | 0 |   } | 
| 637 |  |  | 
| 638 |  |         /* Indicate that the fill value was changed, if the variable has already | 
| 639 |  |          * been created in the file, so the dataset gets deleted and re-created. */ | 
| 640 | 0 |         if (var->created) | 
| 641 | 0 |             var->fill_val_changed = NC_TRUE; | 
| 642 |  |         /* Reclaim any existing fill_chunk */ | 
| 643 | 0 |         if((retval = NCZ_reclaim_fill_chunk(((NCZ_VAR_INFO_T*)var->format_var_info)->cache))) BAIL(retval); | 
| 644 | 0 |     } | 
| 645 |  |  | 
| 646 |  |     /* Copy the attribute data, if there is any. */ | 
| 647 | 0 |     if (len) | 
| 648 | 0 |     { | 
| 649 | 0 |         nc_type type_class;    /* Class of attribute's type */ | 
| 650 |  |  | 
| 651 |  |         /* Get class for this type. */ | 
| 652 | 0 |         if ((retval = nc4_get_typeclass(h5, file_type, &type_class))) | 
| 653 | 0 |             return retval; | 
| 654 |  |  | 
| 655 | 0 |         assert(data); | 
| 656 | 0 |         { | 
| 657 |  |       /* Allocate top level of the copy */ | 
| 658 | 0 |       if (!(copy = malloc(len * type_size))) | 
| 659 | 0 |                 BAIL(NC_ENOMEM); | 
| 660 |  |       /* Special case conversion from memory to file type */ | 
| 661 | 0 |       if(mem_type != file_type && mem_type < NC_STRING && file_type < NC_STRING) { | 
| 662 | 0 |                 if ((retval = nc4_convert_type(data, copy, mem_type, file_type, | 
| 663 | 0 |                                                len, &range_error, NULL, | 
| 664 | 0 |                                                (h5->cmode & NC_CLASSIC_MODEL), | 
| 665 | 0 |                  NC_NOQUANTIZE, 0))) | 
| 666 | 0 |                     BAIL(retval); | 
| 667 | 0 |       } else if(mem_type == file_type) { /* General case: no conversion */ | 
| 668 | 0 |           if((retval = NC_copy_data(h5->controller,file_type,data,len,copy))) | 
| 669 | 0 |         BAIL(retval); | 
| 670 | 0 |       } else | 
| 671 | 0 |         BAIL(NC_EURL); | 
| 672 |  |       /* Store it */ | 
| 673 | 0 |       att->data = copy; copy = NULL; | 
| 674 | 0 |   } | 
| 675 | 0 |     } | 
| 676 |  |  | 
| 677 |  |     /* If this is a maxstrlen attribute, then we will also have to | 
| 678 |  |      * sync the value to NCZ_VAR_INFO_T or NCZ_FILE_INFO_T structure */ | 
| 679 | 0 |     { | 
| 680 | 0 |   if(strcmp(att->hdr.name,NC_NCZARR_DEFAULT_MAXSTRLEN_ATTR)==0 && varid == NC_GLOBAL && len == 1) { | 
| 681 | 0 |       NCZ_FILE_INFO_T* zfile = (NCZ_FILE_INFO_T*)h5->format_file_info; | 
| 682 | 0 |       if((retval = nc4_convert_type(att->data, &zfile->default_maxstrlen, file_type, NC_INT, | 
| 683 | 0 |               len, &range_error, NULL, NC_CLASSIC_MODEL, NC_NOQUANTIZE, 0))) | 
| 684 | 0 |           BAIL(retval); | 
| 685 | 0 |   } else if(strcmp(att->hdr.name,NC_NCZARR_MAXSTRLEN_ATTR)==0 && varid != NC_GLOBAL && len == 1) { | 
| 686 | 0 |       NCZ_VAR_INFO_T* zvar = (NCZ_VAR_INFO_T*)var->format_var_info; | 
| 687 | 0 |   if((retval = nc4_convert_type(att->data, &zvar->maxstrlen, file_type, NC_INT, | 
| 688 | 0 |               len, &range_error, NULL, NC_CLASSIC_MODEL, NC_NOQUANTIZE, 0))) | 
| 689 | 0 |           BAIL(retval); | 
| 690 | 0 |   } | 
| 691 | 0 |     } | 
| 692 |  |  | 
| 693 | 0 |     att->dirty = NC_TRUE; | 
| 694 | 0 |     att->created = NC_FALSE; | 
| 695 | 0 |     att->len = len; | 
| 696 |  |      | 
| 697 |  |     /* Mark attributes on variable dirty, so they get written */ | 
| 698 | 0 |     if(var) | 
| 699 | 0 |         var->attr_dirty = NC_TRUE; | 
| 700 |  |     /* Reclaim saved data */ | 
| 701 | 0 |     if(attsave.data != NULL) { | 
| 702 | 0 |         assert(attsave.len > 0); | 
| 703 | 0 |         (void)NC_reclaim_data_all(h5->controller,attsave.type,attsave.data,attsave.len); | 
| 704 | 0 |   attsave.len = 0; attsave.data = NULL; | 
| 705 | 0 |     } | 
| 706 | 0 |     if(fillsave.data != NULL) { | 
| 707 | 0 |         assert(fillsave.len > 0); | 
| 708 | 0 |         (void)NC_reclaim_data_all(h5->controller,fillsave.type,fillsave.data,fillsave.len); | 
| 709 | 0 |   fillsave.len = 0; fillsave.data = NULL; | 
| 710 | 0 |     } | 
| 711 |  | 
 | 
| 712 | 0 | exit: | 
| 713 | 0 |     if(copy) | 
| 714 | 0 |         (void)NC_reclaim_data_all(h5->controller,file_type,copy,len); | 
| 715 | 0 |     if(retval) { | 
| 716 |  |   /* Rollback */ | 
| 717 | 0 |         if(attsave.data != NULL) { | 
| 718 | 0 |             assert(attsave.len > 0); | 
| 719 | 0 |       if(att->data) | 
| 720 | 0 |                 (void)NC_reclaim_data_all(h5->controller,attsave.type,att->data,att->len); | 
| 721 | 0 |       att->len = attsave.len; att->data = attsave.data; | 
| 722 | 0 |         } | 
| 723 | 0 |         if(fillsave.data != NULL) { | 
| 724 | 0 |             assert(fillsave.len > 0); | 
| 725 | 0 |       if(att->data) | 
| 726 | 0 |       (void)NC_reclaim_data_all(h5->controller,fillsave.type,var->fill_value,1); | 
| 727 | 0 |       var->fill_value = fillsave.data; | 
| 728 | 0 |         } | 
| 729 | 0 |     }     | 
| 730 |  |     /* If there was an error return it, otherwise return any potential | 
| 731 |  |        range error value. If none, return NC_NOERR as usual.*/ | 
| 732 | 0 |     if (range_error) | 
| 733 | 0 |         return NC_ERANGE; | 
| 734 | 0 |     if (retval) | 
| 735 | 0 |         return retval; | 
| 736 | 0 |     return NC_NOERR; | 
| 737 | 0 | } | 
| 738 |  |  | 
| 739 |  | /** | 
| 740 |  |  * @internal Write an attribute to a netCDF-4/NCZ file, converting | 
| 741 |  |  * data type if necessary. | 
| 742 |  |  * | 
| 743 |  |  * @param ncid File and group ID. | 
| 744 |  |  * @param varid Variable ID. | 
| 745 |  |  * @param name Name of attribute. | 
| 746 |  |  * @param file_type Type of the attribute data in file. | 
| 747 |  |  * @param len Number of elements in attribute array. | 
| 748 |  |  * @param data Attribute data. | 
| 749 |  |  * @param mem_type Type of data in memory. | 
| 750 |  |  * | 
| 751 |  |  * @return ::NC_NOERR No error. | 
| 752 |  |  * @return ::NC_EINVAL Invalid parameters. | 
| 753 |  |  * @return ::NC_EBADID Bad ncid. | 
| 754 |  |  * @return ::NC_ENOTVAR Variable not found. | 
| 755 |  |  * @return ::NC_EBADNAME Name contains illegal characters. | 
| 756 |  |  * @return ::NC_ENAMEINUSE Name already in use. | 
| 757 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 758 |  |  */ | 
| 759 |  | int | 
| 760 |  | NCZ_put_att(int ncid, int varid, const char *name, nc_type file_type, | 
| 761 |  |                  size_t len, const void *data, nc_type mem_type) | 
| 762 | 0 | { | 
| 763 | 0 |     NC_FILE_INFO_T *h5; | 
| 764 | 0 |     NC_GRP_INFO_T *grp; | 
| 765 | 0 |     int ret; | 
| 766 |  |  | 
| 767 |  |     /* Find info for this file, group, and h5 info. */ | 
| 768 | 0 |     if ((ret = nc4_find_grp_h5(ncid, &grp, &h5))) | 
| 769 | 0 |         return ret; | 
| 770 | 0 |     assert(grp && h5); | 
| 771 |  | 
 | 
| 772 | 0 |     return ncz_put_att(grp, varid, name, file_type, len, data, mem_type, 0); | 
| 773 | 0 | } | 
| 774 |  |  | 
| 775 |  | /** | 
| 776 |  |  * @internal Learn about an att. All the nc4 nc_inq_ functions just | 
| 777 |  |  * call ncz_get_att to get the metadata on an attribute. | 
| 778 |  |  * | 
| 779 |  |  * @param ncid File and group ID. | 
| 780 |  |  * @param varid Variable ID. | 
| 781 |  |  * @param name Name of attribute. | 
| 782 |  |  * @param xtypep Pointer that gets type of attribute. | 
| 783 |  |  * @param lenp Pointer that gets length of attribute data array. | 
| 784 |  |  * | 
| 785 |  |  * @return ::NC_NOERR No error. | 
| 786 |  |  * @return ::NC_EBADID Bad ncid. | 
| 787 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 788 |  |  */ | 
| 789 |  | int | 
| 790 |  | NCZ_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, | 
| 791 |  |                  size_t *lenp) | 
| 792 | 0 | { | 
| 793 | 0 |     NC_FILE_INFO_T *h5; | 
| 794 | 0 |     NC_GRP_INFO_T *grp; | 
| 795 | 0 |     NC_VAR_INFO_T *var = NULL; | 
| 796 | 0 |     char norm_name[NC_MAX_NAME + 1]; | 
| 797 | 0 |     int retval; | 
| 798 |  | 
 | 
| 799 | 0 |     LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); | 
| 800 |  |  | 
| 801 |  |     /* Find the file, group, and var info, and do lazy att read if | 
| 802 |  |      * needed. */ | 
| 803 | 0 |     if ((retval = ncz_find_grp_var_att(ncid, varid, name, 0, 1, norm_name, | 
| 804 | 0 |                                             &h5, &grp, &var, NULL))) | 
| 805 | 0 |         return retval; | 
| 806 |  |  | 
| 807 |  |     /* If this is one of the reserved atts, use nc_get_att_special. */ | 
| 808 | 0 |     { | 
| 809 | 0 |         const NC_reservedatt *ra = NC_findreserved(norm_name); | 
| 810 | 0 |         if (ra  && ra->flags & NAMEONLYFLAG) | 
| 811 | 0 |             return ncz_get_att_special(h5, var, norm_name, xtypep, NC_NAT, lenp, NULL, | 
| 812 | 0 |                                        NULL); | 
| 813 | 0 |     } | 
| 814 |  |  | 
| 815 | 0 |     return nc4_get_att_ptrs(h5, grp, var, norm_name, xtypep, NC_NAT, | 
| 816 | 0 |                             lenp, NULL, NULL); | 
| 817 | 0 | } | 
| 818 |  |  | 
| 819 |  | /** | 
| 820 |  |  * @internal Learn an attnum, given a name. | 
| 821 |  |  * | 
| 822 |  |  * @param ncid File and group ID. | 
| 823 |  |  * @param varid Variable ID. | 
| 824 |  |  * @param name Name of attribute. | 
| 825 |  |  * @param attnump Pointer that gets the attribute index number. | 
| 826 |  |  * | 
| 827 |  |  * @return ::NC_NOERR No error. | 
| 828 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 829 |  |  */ | 
| 830 |  | int | 
| 831 |  | NCZ_inq_attid(int ncid, int varid, const char *name, int *attnump) | 
| 832 | 0 | { | 
| 833 | 0 |     NC_FILE_INFO_T *h5; | 
| 834 | 0 |     NC_GRP_INFO_T *grp; | 
| 835 | 0 |     NC_VAR_INFO_T *var = NULL; | 
| 836 | 0 |     char norm_name[NC_MAX_NAME + 1]; | 
| 837 | 0 |     int retval; | 
| 838 |  | 
 | 
| 839 | 0 |     LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); | 
| 840 |  |  | 
| 841 |  |     /* Find the file, group, and var info, and do lazy att read if | 
| 842 |  |      * needed. */ | 
| 843 | 0 |     if ((retval = ncz_find_grp_var_att(ncid, varid, name, 0, 1, norm_name, | 
| 844 | 0 |                                             &h5, &grp, &var, NULL))) | 
| 845 | 0 |         return retval; | 
| 846 |  |  | 
| 847 |  |     /* If this is one of the reserved atts, use nc_get_att_special. */ | 
| 848 | 0 |     { | 
| 849 | 0 |         const NC_reservedatt *ra = NC_findreserved(norm_name); | 
| 850 | 0 |         if (ra  && ra->flags & NAMEONLYFLAG) | 
| 851 | 0 |             return ncz_get_att_special(h5, var, norm_name, NULL, NC_NAT, NULL, attnump, | 
| 852 | 0 |                                        NULL); | 
| 853 | 0 |     } | 
| 854 |  |  | 
| 855 | 0 |     return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, NC_NAT, | 
| 856 | 0 |                             NULL, attnump, NULL); | 
| 857 | 0 | } | 
| 858 |  |  | 
| 859 |  | /** | 
| 860 |  |  * @internal Given an attnum, find the att's name. | 
| 861 |  |  * | 
| 862 |  |  * @param ncid File and group ID. | 
| 863 |  |  * @param varid Variable ID. | 
| 864 |  |  * @param attnum The index number of the attribute. | 
| 865 |  |  * @param name Pointer that gets name of attrribute. | 
| 866 |  |  * | 
| 867 |  |  * @return ::NC_NOERR No error. | 
| 868 |  |  * @return ::NC_EBADID Bad ncid. | 
| 869 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 870 |  |  */ | 
| 871 |  | int | 
| 872 |  | NCZ_inq_attname(int ncid, int varid, int attnum, char *name) | 
| 873 | 0 | { | 
| 874 | 0 |     NC_ATT_INFO_T *att; | 
| 875 | 0 |     int retval = NC_NOERR; | 
| 876 |  | 
 | 
| 877 | 0 |     ZTRACE(1,"ncid=%d varid=%d attnum=%d",ncid,varid,attnum); | 
| 878 | 0 |     LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); | 
| 879 |  |  | 
| 880 |  |     /* Find the file, group, and var info, and do lazy att read if | 
| 881 |  |      * needed. */ | 
| 882 | 0 |     if ((retval = ncz_find_grp_var_att(ncid, varid, NULL, attnum, 0, NULL, | 
| 883 | 0 |                                             NULL, NULL, NULL, &att))) | 
| 884 | 0 |   goto done; | 
| 885 | 0 |     assert(att); | 
| 886 |  |  | 
| 887 |  |     /* Get the name. */ | 
| 888 | 0 |     if (name) | 
| 889 | 0 |         strcpy(name, att->hdr.name); | 
| 890 | 0 | done: | 
| 891 | 0 |     return ZUNTRACEX(retval,"name=%s",(retval?"":name)); | 
| 892 | 0 | } | 
| 893 |  |  | 
| 894 |  | /** | 
| 895 |  |  * @internal Get an attribute. | 
| 896 |  |  * | 
| 897 |  |  * @param ncid File and group ID. | 
| 898 |  |  * @param varid Variable ID. | 
| 899 |  |  * @param name Name of attribute. | 
| 900 |  |  * @param value Pointer that gets attribute data. | 
| 901 |  |  * @param memtype The type the data should be converted to as it is | 
| 902 |  |  * read. | 
| 903 |  |  * | 
| 904 |  |  * @return ::NC_NOERR No error. | 
| 905 |  |  * @return ::NC_EBADID Bad ncid. | 
| 906 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 907 |  |  */ | 
| 908 |  | int | 
| 909 |  | NCZ_get_att(int ncid, int varid, const char *name, void *value, | 
| 910 |  |                  nc_type memtype) | 
| 911 | 0 | { | 
| 912 | 0 |     NC_FILE_INFO_T *h5; | 
| 913 | 0 |     NC_GRP_INFO_T *grp; | 
| 914 | 0 |     NC_VAR_INFO_T *var = NULL; | 
| 915 | 0 |     char norm_name[NC_MAX_NAME + 1]; | 
| 916 | 0 |     int retval; | 
| 917 |  | 
 | 
| 918 | 0 |     LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); | 
| 919 |  |  | 
| 920 |  |     /* Find the file, group, and var info, and do lazy att read if | 
| 921 |  |      * needed. */ | 
| 922 | 0 |     if ((retval = ncz_find_grp_var_att(ncid, varid, name, 0, 1, norm_name, | 
| 923 | 0 |                                             &h5, &grp, &var, NULL))) | 
| 924 | 0 |         return retval; | 
| 925 |  |  | 
| 926 |  |     /* If this is one of the reserved global atts, use nc_get_att_special. */ | 
| 927 | 0 |     { | 
| 928 | 0 |         const NC_reservedatt *ra = NC_findreserved(norm_name); | 
| 929 | 0 |         if (ra  && ra->flags & NAMEONLYFLAG) | 
| 930 | 0 |             return ncz_get_att_special(h5, var, norm_name, NULL, NC_NAT, NULL, NULL, | 
| 931 | 0 |                                        value); | 
| 932 | 0 |     } | 
| 933 |  |  | 
| 934 | 0 |     return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, memtype, | 
| 935 | 0 |                             NULL, NULL, value); | 
| 936 | 0 | } | 
| 937 |  |  | 
| 938 |  | #if 0 | 
| 939 |  | static int | 
| 940 |  | ncz_del_attr(NC_FILE_INFO_T* file, NC_OBJ* container, const char* name) | 
| 941 |  | { | 
| 942 |  |     int i,stat = NC_NOERR; | 
| 943 |  |  | 
| 944 |  |     ZTRACE(); | 
| 945 |  |  | 
| 946 |  |     if(container->sort == NCGRP) | 
| 947 |  |   stat = ncz_getattlist((NC_GRP_INFO_T*)container,NC_GLOBAL,NULL,&attlist); | 
| 948 |  |     else | 
| 949 |  |   stat = ncz_getattlist((NC_VAR_INFO_T*)container,NC_GLOBAL,NULL,&attlist); | 
| 950 |  |  | 
| 951 |  |   goto done; | 
| 952 |  |  | 
| 953 |  |     /* Iterate over the attributes to locate the matching attribute */ | 
| 954 |  |     for(i=0;i<nclistlength(jattrs->dict);i+=2) { | 
| 955 |  |   NCjson* key = nclistget(jattrs->dict,i); | 
| 956 |  |   assert(key->sort == NCJ_STRING); | 
| 957 |  |   if(strcmp(key->value,name)==0) { | 
| 958 |  |       /* Remove and reclaim */ | 
| 959 |  |       NCjson* value = nclistget(jattrs->dict,i+1); | 
| 960 |  |       nclistremove(jattrs->dict,i); | 
| 961 |  |       nclistremove(jattrs->dict,i+1); | 
| 962 |  |       NCJreclaim(key); | 
| 963 |  |       NCJreclaim(value); | 
| 964 |  |       break; | 
| 965 |  |   }     | 
| 966 |  |     } | 
| 967 |  |     /* Write the json back out */ | 
| 968 |  |     if((stat = ncz_unload_jatts(zinfo, container, jattrs, jtypes))) | 
| 969 |  |   goto done; | 
| 970 |  |  | 
| 971 |  | done: | 
| 972 |  |     NCJreclaim(jattrs); | 
| 973 |  |     NCJreclaim(jtypes); | 
| 974 |  |     return stat; | 
| 975 |  | } | 
| 976 |  | #endif | 
| 977 |  |  | 
| 978 |  | /* If we do not have a _FillValue, then go ahead and create it */ | 
| 979 |  | int | 
| 980 |  | ncz_create_fillvalue(NC_VAR_INFO_T* var) | 
| 981 | 0 | { | 
| 982 | 0 |     int stat = NC_NOERR; | 
| 983 | 0 |     size_t i; | 
| 984 | 0 |     NC_ATT_INFO_T* fv = NULL; | 
| 985 |  |  | 
| 986 |  |     /* Have the var's attributes been read? */ | 
| 987 | 0 |     if(!var->atts_read) goto done; /* above my pay grade */ | 
| 988 |  |  | 
| 989 |  |     /* Is FillValue warranted? */ | 
| 990 | 0 |     if(!var->no_fill && var->fill_value != NULL) { | 
| 991 |  |         /* Make sure _FillValue does not exist */ | 
| 992 | 0 |   for(i=0;i<ncindexsize(var->att);i++) { | 
| 993 | 0 |       fv = (NC_ATT_INFO_T*)ncindexith(var->att,i); | 
| 994 | 0 |       if(strcmp(fv->hdr.name,NC_FillValue)==0) break; | 
| 995 | 0 |       fv = NULL; | 
| 996 | 0 |         } | 
| 997 | 0 |   if(fv == NULL) { | 
| 998 |  |       /* Create it */ | 
| 999 | 0 |       if((stat = ncz_makeattr((NC_OBJ*)var,var->att,NC_FillValue,var->type_info->hdr.id,1,var->fill_value,&fv))) | 
| 1000 | 0 |       goto done; | 
| 1001 | 0 |   } | 
| 1002 | 0 |     } | 
| 1003 | 0 | done: | 
| 1004 | 0 |     return THROW(stat); | 
| 1005 | 0 | } | 
| 1006 |  |  | 
| 1007 |  | /* Create an attribute; This is an abbreviated form | 
| 1008 |  |    of ncz_put_att above */ | 
| 1009 |  | int | 
| 1010 |  | ncz_makeattr(NC_OBJ* container, NCindex* attlist, const char* name, nc_type typeid, size_t len, void* values, NC_ATT_INFO_T** attp) | 
| 1011 | 0 | { | 
| 1012 | 0 |     int stat = NC_NOERR; | 
| 1013 | 0 |     NC_ATT_INFO_T* att = NULL; | 
| 1014 | 0 |     NCZ_ATT_INFO_T* zatt = NULL; | 
| 1015 | 0 |     void* clone = NULL; | 
| 1016 | 0 |     size_t typesize, clonesize; | 
| 1017 | 0 |     NC_GRP_INFO_T* grp = (container->sort == NCGRP ? (NC_GRP_INFO_T*)container | 
| 1018 | 0 |                                                    : ((NC_VAR_INFO_T*)container)->container); | 
| 1019 |  |  | 
| 1020 |  |     /* Duplicate the values */ | 
| 1021 | 0 |     if ((stat = nc4_get_typelen_mem(grp->nc4_info, typeid, &typesize))) goto done; | 
| 1022 | 0 |     clonesize = len*typesize; | 
| 1023 | 0 |     if((clone = malloc(clonesize))==NULL) {stat = NC_ENOMEM; goto done;} | 
| 1024 | 0 |     if((stat = NC_copy_data(grp->nc4_info->controller, typeid, values, len, clone))) goto done; | 
| 1025 | 0 |     if((stat=nc4_att_list_add(attlist,name,&att))) | 
| 1026 | 0 |   goto done; | 
| 1027 | 0 |     if((zatt = calloc(1,sizeof(NCZ_ATT_INFO_T))) == NULL) | 
| 1028 | 0 |   {stat = NC_ENOMEM; goto done;} | 
| 1029 | 0 |     if(container->sort == NCGRP) { | 
| 1030 | 0 |         zatt->common.file = ((NC_GRP_INFO_T*)container)->nc4_info; | 
| 1031 | 0 |     } else if(container->sort == NCVAR) { | 
| 1032 | 0 |         zatt->common.file = ((NC_VAR_INFO_T*)container)->container->nc4_info; | 
| 1033 | 0 |     } else | 
| 1034 | 0 |   abort(); | 
| 1035 | 0 |     att->container = container; | 
| 1036 | 0 |     att->format_att_info = zatt; | 
| 1037 |  |     /* Fill in the attribute's type and value  */ | 
| 1038 | 0 |     att->nc_typeid = typeid; | 
| 1039 | 0 |     att->len = len; | 
| 1040 | 0 |     att->data = clone; clone = NULL; | 
| 1041 | 0 |     att->dirty = NC_TRUE; | 
| 1042 | 0 |     if(attp) {*attp = att; att = NULL;} | 
| 1043 |  | 
 | 
| 1044 | 0 | done: | 
| 1045 | 0 |     nullfree(clone); | 
| 1046 | 0 |     if(stat) { | 
| 1047 | 0 |   if(att) nc4_att_list_del(attlist,att); | 
| 1048 | 0 |   nullfree(zatt); | 
| 1049 | 0 |     } | 
| 1050 | 0 |     return THROW(stat); | 
| 1051 | 0 | } | 
| 1052 |  |  |