/src/netcdf-c/libnczarr/zattr.c
Line | Count | Source (jump to first uncovered line) |
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, 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 | 0 | #ifdef ENABLE_NCZARR_FILTERS |
106 | 0 | if(strcmp(name,NC_ATT_CODECS)==0) { |
107 | 0 | NClist* filters = (NClist*)var->filters; |
108 | |
|
109 | 0 | if(mem_type == NC_NAT) mem_type = NC_CHAR; |
110 | 0 | if(mem_type != NC_CHAR) |
111 | 0 | {stat = NC_ECHAR; goto done;} |
112 | 0 | if(filetypep) *filetypep = NC_CHAR; |
113 | 0 | if(lenp) *lenp = 0; |
114 | 0 | if(filters == NULL) goto done; |
115 | 0 | if((stat = NCZ_codec_attr(var,lenp,data))) goto done; |
116 | 0 | } |
117 | 0 | #endif |
118 | 0 | goto done; |
119 | 0 | } |
120 | | |
121 | | /* The global reserved attributes */ |
122 | 0 | if(strcmp(name,NCPROPS)==0) { |
123 | 0 | int 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 = 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 | int i; |
283 | 0 | size_t 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(ncid,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 * nc4typelen(file_type) > (size_t)att->len * 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, _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->ext_ncid, 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->ext_ncid,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->ext_ncid,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->ext_ncid,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->ext_ncid,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->ext_ncid,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->ext_ncid,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 | int 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,_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,_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 | int ncid; |
1018 | 0 | NC* nc = NULL; |
1019 | 0 | NC_GRP_INFO_T* grp = (container->sort == NCGRP ? (NC_GRP_INFO_T*)container |
1020 | 0 | : ((NC_VAR_INFO_T*)container)->container); |
1021 | |
|
1022 | 0 | nc = grp->nc4_info->controller; |
1023 | 0 | ncid = nc->ext_ncid | grp->hdr.id; |
1024 | | |
1025 | | /* Duplicate the values */ |
1026 | 0 | if ((stat = nc4_get_typelen_mem(grp->nc4_info, typeid, &typesize))) goto done; |
1027 | 0 | clonesize = len*typesize; |
1028 | 0 | if((clone = malloc(clonesize))==NULL) {stat = NC_ENOMEM; goto done;} |
1029 | 0 | if((stat = nc_copy_data(ncid, typeid, values, len, clone))) goto done; |
1030 | | |
1031 | 0 | if((stat=nc4_att_list_add(attlist,name,&att))) |
1032 | 0 | goto done; |
1033 | 0 | if((zatt = calloc(1,sizeof(NCZ_ATT_INFO_T))) == NULL) |
1034 | 0 | {stat = NC_ENOMEM; goto done;} |
1035 | 0 | if(container->sort == NCGRP) { |
1036 | 0 | zatt->common.file = ((NC_GRP_INFO_T*)container)->nc4_info; |
1037 | 0 | } else if(container->sort == NCVAR) { |
1038 | 0 | zatt->common.file = ((NC_VAR_INFO_T*)container)->container->nc4_info; |
1039 | 0 | } else |
1040 | 0 | abort(); |
1041 | 0 | att->container = container; |
1042 | 0 | att->format_att_info = zatt; |
1043 | | /* Fill in the attribute's type and value */ |
1044 | 0 | att->nc_typeid = typeid; |
1045 | 0 | att->len = len; |
1046 | 0 | att->data = clone; clone = NULL; |
1047 | 0 | att->dirty = NC_TRUE; |
1048 | 0 | if(attp) {*attp = att; att = NULL;} |
1049 | |
|
1050 | 0 | done: |
1051 | 0 | nullfree(clone); |
1052 | 0 | if(stat) { |
1053 | 0 | if(att) nc4_att_list_del(attlist,att); |
1054 | 0 | nullfree(zatt); |
1055 | 0 | } |
1056 | 0 | return THROW(stat); |
1057 | 0 | } |
1058 | | |