/src/gdal/netcdf-c-4.7.4/libhdf5/hdf5attr.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 | | * @file |
6 | | * @internal This file handles HDF5 attributes. |
7 | | * |
8 | | * @author Ed Hartnett |
9 | | */ |
10 | | |
11 | | #include "config.h" |
12 | | #include "hdf5internal.h" |
13 | | |
14 | | /** |
15 | | * @internal Get the attribute list for either a varid or NC_GLOBAL |
16 | | * |
17 | | * @param grp Group |
18 | | * @param varid Variable ID | NC_BLOGAL |
19 | | * @param varp Pointer that gets pointer to NC_VAR_INFO_T |
20 | | * instance. Ignored if NULL. |
21 | | * @param attlist Pointer that gets pointer to attribute list. |
22 | | * |
23 | | * @return NC_NOERR No error. |
24 | | * @author Dennis Heimbigner, Ed Hartnett |
25 | | */ |
26 | | static int |
27 | | getattlist(NC_GRP_INFO_T *grp, int varid, NC_VAR_INFO_T **varp, |
28 | | NCindex **attlist) |
29 | 137k | { |
30 | 137k | int retval; |
31 | | |
32 | 137k | assert(grp && attlist); |
33 | | |
34 | 137k | if (varid == NC_GLOBAL) |
35 | 113k | { |
36 | | /* Do we need to read the atts? */ |
37 | 113k | if (!grp->atts_read) |
38 | 0 | if ((retval = nc4_read_atts(grp, NULL))) |
39 | 0 | return retval; |
40 | | |
41 | 113k | if (varp) |
42 | 113k | *varp = NULL; |
43 | 113k | *attlist = grp->att; |
44 | 113k | } |
45 | 23.3k | else |
46 | 23.3k | { |
47 | 23.3k | NC_VAR_INFO_T *var; |
48 | | |
49 | 23.3k | if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid))) |
50 | 0 | return NC_ENOTVAR; |
51 | 23.3k | assert(var->hdr.id == varid); |
52 | | |
53 | | /* Do we need to read the atts? */ |
54 | 23.3k | if (!var->atts_read) |
55 | 0 | if ((retval = nc4_read_atts(grp, var))) |
56 | 0 | return retval; |
57 | | |
58 | 23.3k | if (varp) |
59 | 23.3k | *varp = var; |
60 | 23.3k | *attlist = var->att; |
61 | 23.3k | } |
62 | 137k | return NC_NOERR; |
63 | 137k | } |
64 | | |
65 | | /** |
66 | | * @internal Get one of three special attributes, NCPROPS, |
67 | | * ISNETCDF4ATT, and SUPERBLOCKATT. These atts are not all really in |
68 | | * the file, they are constructed on the fly. |
69 | | * |
70 | | * @param h5 Pointer to HDF5 file info struct. |
71 | | * @param name Name of attribute. |
72 | | * @param filetypep Pointer that gets type of the attribute data in |
73 | | * file. |
74 | | * @param mem_type Type of attribute data in memory. |
75 | | * @param lenp Pointer that gets length of attribute array. |
76 | | * @param attnump Pointer that gets the attribute number. |
77 | | * @param data Attribute data. |
78 | | * |
79 | | * @return ::NC_NOERR No error. |
80 | | * @return ::NC_EBADID Bad ncid. |
81 | | * @return ::NC_ERANGE Data conversion out of range. |
82 | | * @author Dennis Heimbigner |
83 | | */ |
84 | | int |
85 | | nc4_get_att_special(NC_FILE_INFO_T* h5, const char* name, |
86 | | nc_type* filetypep, nc_type mem_type, size_t* lenp, |
87 | | int* attnump, void* data) |
88 | 0 | { |
89 | | /* Fail if asking for att id */ |
90 | 0 | if(attnump) |
91 | 0 | return NC_EATTMETA; |
92 | | |
93 | 0 | if(strcmp(name,NCPROPS)==0) { |
94 | 0 | int len; |
95 | 0 | if(h5->provenance.ncproperties == NULL) |
96 | 0 | return NC_ENOTATT; |
97 | 0 | if(mem_type == NC_NAT) mem_type = NC_CHAR; |
98 | 0 | if(mem_type != NC_CHAR) |
99 | 0 | return NC_ECHAR; |
100 | 0 | if(filetypep) *filetypep = NC_CHAR; |
101 | 0 | len = strlen(h5->provenance.ncproperties); |
102 | 0 | if(lenp) *lenp = len; |
103 | 0 | if(data) strncpy((char*)data,h5->provenance.ncproperties,len+1); |
104 | 0 | } else if(strcmp(name,ISNETCDF4ATT)==0 |
105 | 0 | || strcmp(name,SUPERBLOCKATT)==0) { |
106 | 0 | unsigned long long iv = 0; |
107 | 0 | if(filetypep) *filetypep = NC_INT; |
108 | 0 | if(lenp) *lenp = 1; |
109 | 0 | if(strcmp(name,SUPERBLOCKATT)==0) |
110 | 0 | iv = (unsigned long long)h5->provenance.superblockversion; |
111 | 0 | else /* strcmp(name,ISNETCDF4ATT)==0 */ |
112 | 0 | iv = NC4_isnetcdf4(h5); |
113 | 0 | if(mem_type == NC_NAT) mem_type = NC_INT; |
114 | 0 | if(data) |
115 | 0 | switch (mem_type) { |
116 | 0 | case NC_BYTE: *((char*)data) = (char)iv; break; |
117 | 0 | case NC_SHORT: *((short*)data) = (short)iv; break; |
118 | 0 | case NC_INT: *((int*)data) = (int)iv; break; |
119 | 0 | case NC_UBYTE: *((unsigned char*)data) = (unsigned char)iv; break; |
120 | 0 | case NC_USHORT: *((unsigned short*)data) = (unsigned short)iv; break; |
121 | 0 | case NC_UINT: *((unsigned int*)data) = (unsigned int)iv; break; |
122 | 0 | case NC_INT64: *((long long*)data) = (long long)iv; break; |
123 | 0 | case NC_UINT64: *((unsigned long long*)data) = (unsigned long long)iv; break; |
124 | 0 | default: |
125 | 0 | return NC_ERANGE; |
126 | 0 | } |
127 | 0 | } |
128 | 0 | return NC_NOERR; |
129 | 0 | } |
130 | | |
131 | | /** |
132 | | * @internal I think all atts should be named the exact same thing, to |
133 | | * avoid confusion! |
134 | | * |
135 | | * @param ncid File and group ID. |
136 | | * @param varid Variable ID. |
137 | | * @param name Name of attribute. |
138 | | * @param newname New name for attribute. |
139 | | * |
140 | | * @return ::NC_NOERR No error. |
141 | | * @return ::NC_EBADID Bad ncid. |
142 | | * @return ::NC_EMAXNAME New name too long. |
143 | | * @return ::NC_EPERM File is read-only. |
144 | | * @return ::NC_ENAMEINUSE New name already in use. |
145 | | * @return ::NC_ENOTINDEFINE Classic model file not in define mode. |
146 | | * @return ::NC_EHDFERR HDF error. |
147 | | * @return ::NC_ENOMEM Out of memory. |
148 | | * @return ::NC_EINTERNAL Could not rebuild list. |
149 | | * @author Ed Hartnett |
150 | | */ |
151 | | int |
152 | | NC4_HDF5_rename_att(int ncid, int varid, const char *name, const char *newname) |
153 | 0 | { |
154 | 0 | NC_GRP_INFO_T *grp; |
155 | 0 | NC_FILE_INFO_T *h5; |
156 | 0 | NC_VAR_INFO_T *var = NULL; |
157 | 0 | NC_ATT_INFO_T *att; |
158 | 0 | NCindex *list; |
159 | 0 | char norm_newname[NC_MAX_NAME + 1], norm_name[NC_MAX_NAME + 1]; |
160 | 0 | hid_t datasetid = 0; |
161 | 0 | int retval = NC_NOERR; |
162 | |
|
163 | 0 | if (!name || !newname) |
164 | 0 | return NC_EINVAL; |
165 | | |
166 | 0 | LOG((2, "nc_rename_att: ncid 0x%x varid %d name %s newname %s", |
167 | 0 | ncid, varid, name, newname)); |
168 | | |
169 | | /* If the new name is too long, that's an error. */ |
170 | 0 | if (strlen(newname) > NC_MAX_NAME) |
171 | 0 | return NC_EMAXNAME; |
172 | | |
173 | | /* Find info for this file, group, and h5 info. */ |
174 | 0 | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
175 | 0 | return retval; |
176 | 0 | assert(h5 && grp); |
177 | | |
178 | | /* If the file is read-only, return an error. */ |
179 | 0 | if (h5->no_write) |
180 | 0 | return NC_EPERM; |
181 | | |
182 | | /* Check and normalize the name. */ |
183 | 0 | if ((retval = nc4_check_name(newname, norm_newname))) |
184 | 0 | return retval; |
185 | | |
186 | | /* Get the list of attributes. */ |
187 | 0 | if ((retval = getattlist(grp, varid, &var, &list))) |
188 | 0 | return retval; |
189 | | |
190 | | /* Is new name in use? */ |
191 | 0 | att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_newname); |
192 | 0 | if(att != NULL) |
193 | 0 | return NC_ENAMEINUSE; |
194 | | |
195 | | /* Normalize name and find the attribute. */ |
196 | 0 | if ((retval = nc4_normalize_name(name, norm_name))) |
197 | 0 | return retval; |
198 | | |
199 | 0 | att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_name); |
200 | 0 | if (!att) |
201 | 0 | return NC_ENOTATT; |
202 | | |
203 | | /* If we're not in define mode, new name must be of equal or |
204 | | less size, if complying with strict NC3 rules. */ |
205 | 0 | if (!(h5->flags & NC_INDEF) && strlen(norm_newname) > strlen(att->hdr.name) && |
206 | 0 | (h5->cmode & NC_CLASSIC_MODEL)) |
207 | 0 | return NC_ENOTINDEFINE; |
208 | | |
209 | | /* Delete the original attribute, if it exists in the HDF5 file. */ |
210 | 0 | if (att->created) |
211 | 0 | { |
212 | 0 | if (varid == NC_GLOBAL) |
213 | 0 | { |
214 | 0 | if (H5Adelete(((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid, |
215 | 0 | att->hdr.name) < 0) |
216 | 0 | return NC_EHDFERR; |
217 | 0 | } |
218 | 0 | else |
219 | 0 | { |
220 | 0 | if ((retval = nc4_open_var_grp2(grp, varid, &datasetid))) |
221 | 0 | return retval; |
222 | 0 | if (H5Adelete(datasetid, att->hdr.name) < 0) |
223 | 0 | return NC_EHDFERR; |
224 | 0 | } |
225 | 0 | att->created = NC_FALSE; |
226 | 0 | } |
227 | | |
228 | | /* Copy the new name into our metadata. */ |
229 | 0 | if(att->hdr.name) free(att->hdr.name); |
230 | 0 | if (!(att->hdr.name = strdup(norm_newname))) |
231 | 0 | return NC_ENOMEM; |
232 | 0 | att->hdr.hashkey = NC_hashmapkey(att->hdr.name,strlen(att->hdr.name)); /* Fix hash key */ |
233 | |
|
234 | 0 | att->dirty = NC_TRUE; |
235 | | |
236 | | /* Rehash the attribute list so that the new name is used */ |
237 | 0 | if(!ncindexrebuild(list)) |
238 | 0 | return NC_EINTERNAL; |
239 | | |
240 | | /* Mark attributes on variable dirty, so they get written */ |
241 | 0 | if(var) |
242 | 0 | var->attr_dirty = NC_TRUE; |
243 | |
|
244 | 0 | return retval; |
245 | 0 | } |
246 | | |
247 | | /** |
248 | | * @internal Delete an att. Rub it out. Push the button on |
249 | | * it. Liquidate it. Bump it off. Take it for a one-way |
250 | | * ride. Terminate it. |
251 | | * |
252 | | * @param ncid File and group ID. |
253 | | * @param varid Variable ID. |
254 | | * @param name Name of attribute to delete. |
255 | | * |
256 | | * @return ::NC_NOERR No error. |
257 | | * @return ::NC_EBADID Bad ncid. |
258 | | * @return ::NC_ENOTATT Attribute not found. |
259 | | * @return ::NC_EINVAL No name provided. |
260 | | * @return ::NC_EPERM File is read only. |
261 | | * @return ::NC_ENOTINDEFINE Classic model not in define mode. |
262 | | * @return ::NC_EINTERNAL Could not rebuild list. |
263 | | * @author Ed Hartnett, Dennis Heimbigner |
264 | | */ |
265 | | int |
266 | | NC4_HDF5_del_att(int ncid, int varid, const char *name) |
267 | 0 | { |
268 | 0 | NC_GRP_INFO_T *grp; |
269 | 0 | NC_VAR_INFO_T *var; |
270 | 0 | NC_FILE_INFO_T *h5; |
271 | 0 | NC_ATT_INFO_T *att; |
272 | 0 | NCindex* attlist = NULL; |
273 | 0 | hid_t locid = 0; |
274 | 0 | int i; |
275 | 0 | size_t deletedid; |
276 | 0 | int retval; |
277 | | |
278 | | /* Name must be provided. */ |
279 | 0 | if (!name) |
280 | 0 | return NC_EINVAL; |
281 | | |
282 | 0 | LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name)); |
283 | | |
284 | | /* Find info for this file, group, and h5 info. */ |
285 | 0 | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
286 | 0 | return retval; |
287 | 0 | assert(h5 && grp); |
288 | | |
289 | | /* If the file is read-only, return an error. */ |
290 | 0 | if (h5->no_write) |
291 | 0 | return NC_EPERM; |
292 | | |
293 | | /* If file is not in define mode, return error for classic model |
294 | | * files, otherwise switch to define mode. */ |
295 | 0 | if (!(h5->flags & NC_INDEF)) |
296 | 0 | { |
297 | 0 | if (h5->cmode & NC_CLASSIC_MODEL) |
298 | 0 | return NC_ENOTINDEFINE; |
299 | 0 | if ((retval = NC4_redef(ncid))) |
300 | 0 | return retval; |
301 | 0 | } |
302 | | |
303 | | /* Get either the global or a variable attribute list. */ |
304 | 0 | if ((retval = getattlist(grp, varid, &var, &attlist))) |
305 | 0 | return retval; |
306 | | |
307 | | /* Determine the location id in the HDF5 file. */ |
308 | 0 | if (varid == NC_GLOBAL) |
309 | 0 | locid = ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid; |
310 | 0 | else if (var->created) |
311 | 0 | locid = ((NC_HDF5_VAR_INFO_T *)(var->format_var_info))->hdf_datasetid; |
312 | | |
313 | | /* Now find the attribute by name. */ |
314 | 0 | if (!(att = (NC_ATT_INFO_T*)ncindexlookup(attlist, name))) |
315 | 0 | return NC_ENOTATT; |
316 | | |
317 | | /* Delete it from the HDF5 file, if it's been created. */ |
318 | 0 | if (att->created) |
319 | 0 | { |
320 | 0 | assert(locid); |
321 | 0 | if (H5Adelete(locid, att->hdr.name) < 0) |
322 | 0 | return NC_EATTMETA; |
323 | 0 | } |
324 | | |
325 | 0 | deletedid = att->hdr.id; |
326 | | |
327 | | /* Remove this attribute in this list */ |
328 | 0 | if ((retval = nc4_att_list_del(attlist, att))) |
329 | 0 | return retval; |
330 | | |
331 | | /* Renumber all attributes with higher indices. */ |
332 | 0 | for (i = 0; i < ncindexsize(attlist); i++) |
333 | 0 | { |
334 | 0 | NC_ATT_INFO_T *a; |
335 | 0 | if (!(a = (NC_ATT_INFO_T *)ncindexith(attlist, i))) |
336 | 0 | continue; |
337 | 0 | if (a->hdr.id > deletedid) |
338 | 0 | a->hdr.id--; |
339 | 0 | } |
340 | | |
341 | | /* Rebuild the index. */ |
342 | 0 | if (!ncindexrebuild(attlist)) |
343 | 0 | return NC_EINTERNAL; |
344 | | |
345 | 0 | return NC_NOERR; |
346 | 0 | } |
347 | | |
348 | | /** |
349 | | * @internal This will return the length of a netcdf atomic data type |
350 | | * in bytes. |
351 | | * |
352 | | * @param type A netcdf atomic type. |
353 | | * |
354 | | * @return Type size in bytes, or -1 if type not found. |
355 | | * @author Ed Hartnett |
356 | | */ |
357 | | static int |
358 | | nc4typelen(nc_type type) |
359 | 0 | { |
360 | 0 | switch(type){ |
361 | 0 | case NC_BYTE: |
362 | 0 | case NC_CHAR: |
363 | 0 | case NC_UBYTE: |
364 | 0 | return 1; |
365 | 0 | case NC_USHORT: |
366 | 0 | case NC_SHORT: |
367 | 0 | return 2; |
368 | 0 | case NC_FLOAT: |
369 | 0 | case NC_INT: |
370 | 0 | case NC_UINT: |
371 | 0 | return 4; |
372 | 0 | case NC_DOUBLE: |
373 | 0 | case NC_INT64: |
374 | 0 | case NC_UINT64: |
375 | 0 | return 8; |
376 | 0 | } |
377 | 0 | return -1; |
378 | 0 | } |
379 | | |
380 | | /** |
381 | | * @internal |
382 | | * Write an attribute to a netCDF-4/HDF5 file, converting |
383 | | * data type if necessary. |
384 | | * |
385 | | * @param ncid File and group ID. |
386 | | * @param varid Variable ID. |
387 | | * @param name Name of attribute. |
388 | | * @param file_type Type of the attribute data in file. |
389 | | * @param len Number of elements in attribute array. |
390 | | * @param data Attribute data. |
391 | | * @param mem_type Type of data in memory. |
392 | | * @param force write even if the attribute is special |
393 | | * |
394 | | * @return ::NC_NOERR No error. |
395 | | * @return ::NC_EINVAL Invalid parameters. |
396 | | * @return ::NC_EBADID Bad ncid. |
397 | | * @return ::NC_ENOTVAR Variable not found. |
398 | | * @return ::NC_EBADNAME Name contains illegal characters. |
399 | | * @return ::NC_ENAMEINUSE Name already in use. |
400 | | * @author Ed Hartnett, Dennis Heimbigner |
401 | | */ |
402 | | int |
403 | | nc4_put_att(NC_GRP_INFO_T* grp, int varid, const char *name, nc_type file_type, |
404 | | size_t len, const void *data, nc_type mem_type, int force) |
405 | 137k | { |
406 | 137k | NC* nc; |
407 | 137k | NC_FILE_INFO_T *h5; |
408 | 137k | NC_VAR_INFO_T *var = NULL; |
409 | 137k | NCindex* attlist = NULL; |
410 | 137k | NC_ATT_INFO_T* att; |
411 | 137k | char norm_name[NC_MAX_NAME + 1]; |
412 | 137k | nc_bool_t new_att = NC_FALSE; |
413 | 137k | int retval = NC_NOERR, range_error = 0; |
414 | 137k | size_t type_size; |
415 | 137k | int i; |
416 | 137k | int ret; |
417 | 137k | int ncid; |
418 | | |
419 | 137k | h5 = grp->nc4_info; |
420 | 137k | nc = h5->controller; |
421 | 137k | assert(nc && grp && h5); |
422 | | |
423 | 137k | ncid = nc->ext_ncid | grp->hdr.id; |
424 | | |
425 | | /* Find att, if it exists. (Must check varid first or nc_test will |
426 | | * break.) This also does lazy att reads if needed. */ |
427 | 137k | if ((ret = getattlist(grp, varid, &var, &attlist))) |
428 | 0 | return ret; |
429 | | |
430 | | /* The length needs to be positive (cast needed for braindead |
431 | | systems with signed size_t). */ |
432 | 137k | if((unsigned long) len > X_INT_MAX) |
433 | 0 | return NC_EINVAL; |
434 | | |
435 | | /* Check name before LOG statement. */ |
436 | 137k | if (!name || strlen(name) > NC_MAX_NAME) |
437 | 1.39k | return NC_EBADNAME; |
438 | | |
439 | 135k | LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d", |
440 | 135k | __func__,ncid, varid, name, file_type, mem_type, len)); |
441 | | |
442 | | /* If len is not zero, then there must be some data. */ |
443 | 135k | if (len && !data) |
444 | 0 | return NC_EINVAL; |
445 | | |
446 | | /* If the file is read-only, return an error. */ |
447 | 135k | if (h5->no_write) |
448 | 0 | return NC_EPERM; |
449 | | |
450 | | /* Check and normalize the name. */ |
451 | 135k | if ((retval = nc4_check_name(name, norm_name))) |
452 | 60.7k | return retval; |
453 | | |
454 | | /* Check that a reserved att name is not being used improperly */ |
455 | 75.0k | const NC_reservedatt* ra = NC_findreserved(name); |
456 | 75.0k | if(ra != NULL && !force) { |
457 | | /* case 1: grp=root, varid==NC_GLOBAL, flags & READONLYFLAG */ |
458 | 10.3k | if (nc->ext_ncid == ncid && varid == NC_GLOBAL && grp->parent == NULL |
459 | 478 | && (ra->flags & READONLYFLAG)) |
460 | 478 | return NC_ENAMEINUSE; |
461 | | /* case 2: grp=NA, varid!=NC_GLOBAL, flags & DIMSCALEFLAG */ |
462 | 9.84k | if (varid != NC_GLOBAL && (ra->flags & DIMSCALEFLAG)) |
463 | 9.84k | return NC_ENAMEINUSE; |
464 | 9.84k | } |
465 | | |
466 | | /* See if there is already an attribute with this name. */ |
467 | 64.7k | att = (NC_ATT_INFO_T*)ncindexlookup(attlist,norm_name); |
468 | | |
469 | 64.7k | if (!att) |
470 | 64.7k | { |
471 | | /* If this is a new att, require define mode. */ |
472 | 64.7k | if (!(h5->flags & NC_INDEF)) |
473 | 0 | { |
474 | 0 | if (h5->cmode & NC_CLASSIC_MODEL) |
475 | 0 | return NC_ENOTINDEFINE; |
476 | 0 | if ((retval = NC4_redef(ncid))) |
477 | 0 | BAIL(retval); |
478 | 0 | } |
479 | 64.7k | new_att = NC_TRUE; |
480 | 64.7k | } |
481 | 0 | else |
482 | 0 | { |
483 | | /* For an existing att, if we're not in define mode, the len |
484 | | must not be greater than the existing len for classic model. */ |
485 | 0 | if (!(h5->flags & NC_INDEF) && |
486 | 0 | len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid)) |
487 | 0 | { |
488 | 0 | if (h5->cmode & NC_CLASSIC_MODEL) |
489 | 0 | return NC_ENOTINDEFINE; |
490 | 0 | if ((retval = NC4_redef(ncid))) |
491 | 0 | BAIL(retval); |
492 | 0 | } |
493 | 0 | } |
494 | | |
495 | | /* We must have two valid types to continue. */ |
496 | 64.7k | if (file_type == NC_NAT || mem_type == NC_NAT) |
497 | 0 | return NC_EBADTYPE; |
498 | | |
499 | | /* Get information about this type. */ |
500 | 64.7k | if ((retval = nc4_get_typelen_mem(h5, file_type, &type_size))) |
501 | 0 | return retval; |
502 | | |
503 | | /* No character conversions are allowed. */ |
504 | 64.7k | if (file_type != mem_type && |
505 | 0 | (file_type == NC_CHAR || mem_type == NC_CHAR || |
506 | 0 | file_type == NC_STRING || mem_type == NC_STRING)) |
507 | 0 | return NC_ECHAR; |
508 | | |
509 | | /* For classic mode file, only allow atts with classic types to be |
510 | | * created. */ |
511 | 64.7k | if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE) |
512 | 0 | return NC_ESTRICTNC3; |
513 | | |
514 | | /* Add to the end of the attribute list, if this att doesn't |
515 | | already exist. */ |
516 | 64.7k | if (new_att) |
517 | 64.7k | { |
518 | 64.7k | LOG((3, "adding attribute %s to the list...", norm_name)); |
519 | 64.7k | if ((ret = nc4_att_list_add(attlist, norm_name, &att))) |
520 | 0 | BAIL(ret); |
521 | | |
522 | | /* Allocate storage for the HDF5 specific att info. */ |
523 | 64.7k | if (!(att->format_att_info = calloc(1, sizeof(NC_HDF5_ATT_INFO_T)))) |
524 | 0 | BAIL(NC_ENOMEM); |
525 | 64.7k | } |
526 | | |
527 | | /* Now fill in the metadata. */ |
528 | 64.7k | att->dirty = NC_TRUE; |
529 | 64.7k | att->nc_typeid = file_type; |
530 | | |
531 | | /* If this att has vlen or string data, release it before we lose the length value. */ |
532 | 64.7k | if (att->stdata) |
533 | 0 | { |
534 | 0 | for (i = 0; i < att->len; i++) |
535 | 0 | if(att->stdata[i]) |
536 | 0 | free(att->stdata[i]); |
537 | 0 | free(att->stdata); |
538 | 0 | att->stdata = NULL; |
539 | 0 | } |
540 | 64.7k | if (att->vldata) |
541 | 0 | { |
542 | 0 | for (i = 0; i < att->len; i++) |
543 | 0 | nc_free_vlen(&att->vldata[i]); /* FIX: see warning of nc_free_vlen */ |
544 | 0 | free(att->vldata); |
545 | 0 | att->vldata = NULL; |
546 | 0 | } |
547 | | |
548 | 64.7k | att->len = len; |
549 | | |
550 | | /* If this is the _FillValue attribute, then we will also have to |
551 | | * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T |
552 | | * struct for this var. (But ignore a global _FillValue |
553 | | * attribute). */ |
554 | 64.7k | if (!strcmp(att->hdr.name, _FillValue) && varid != NC_GLOBAL) |
555 | 153 | { |
556 | 153 | int size; |
557 | | |
558 | | /* Fill value must be same type and have exactly one value */ |
559 | 153 | if (att->nc_typeid != var->type_info->hdr.id) |
560 | 0 | return NC_EBADTYPE; |
561 | 153 | if (att->len != 1) |
562 | 0 | return NC_EINVAL; |
563 | | |
564 | | /* If we already wrote to the dataset, then return an error. */ |
565 | 153 | if (var->written_to) |
566 | 0 | return NC_ELATEFILL; |
567 | | |
568 | | /* Get the length of the veriable data type. */ |
569 | 153 | if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id, |
570 | 153 | &type_size))) |
571 | 0 | return retval; |
572 | | |
573 | | /* Already set a fill value? Now I'll have to free the old |
574 | | * one. Make up your damn mind, would you? */ |
575 | 153 | if (var->fill_value) |
576 | 0 | { |
577 | 0 | if (var->type_info->nc_type_class == NC_VLEN) |
578 | 0 | { |
579 | 0 | if ((retval = nc_free_vlen(var->fill_value))) |
580 | 0 | return retval; |
581 | 0 | } |
582 | 0 | else if (var->type_info->nc_type_class == NC_STRING) |
583 | 0 | { |
584 | 0 | if (*(char **)var->fill_value) |
585 | 0 | free(*(char **)var->fill_value); |
586 | 0 | } |
587 | 0 | free(var->fill_value); |
588 | 0 | } |
589 | | |
590 | | /* Determine the size of the fill value in bytes. */ |
591 | 153 | if (var->type_info->nc_type_class == NC_VLEN) |
592 | 0 | size = sizeof(hvl_t); |
593 | 153 | else if (var->type_info->nc_type_class == NC_STRING) |
594 | 0 | size = sizeof(char *); |
595 | 153 | else |
596 | 153 | size = type_size; |
597 | | |
598 | | /* Allocate space for the fill value. */ |
599 | 153 | if (!(var->fill_value = calloc(1, size))) |
600 | 0 | return NC_ENOMEM; |
601 | | |
602 | | /* Copy the fill_value. */ |
603 | 153 | LOG((4, "Copying fill value into metadata for variable %s", var->hdr.name)); |
604 | 153 | if (var->type_info->nc_type_class == NC_VLEN) |
605 | 0 | { |
606 | 0 | nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value); |
607 | 0 | NC_TYPE_INFO_T* basetype; |
608 | 0 | size_t basetypesize = 0; |
609 | | |
610 | | /* get the basetype and its size */ |
611 | 0 | basetype = var->type_info; |
612 | 0 | if ((retval = nc4_get_typelen_mem(grp->nc4_info, basetype->hdr.id, &basetypesize))) |
613 | 0 | return retval; |
614 | | /* shallow clone the content of the vlen; shallow because it has only a temporary existence */ |
615 | 0 | fv_vlen->len = in_vlen->len; |
616 | 0 | if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len))) |
617 | 0 | return NC_ENOMEM; |
618 | 0 | memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize); |
619 | 0 | } |
620 | 153 | else if (var->type_info->nc_type_class == NC_STRING) |
621 | 0 | { |
622 | 0 | if (*(char **)data) |
623 | 0 | { |
624 | 0 | if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1))) |
625 | 0 | return NC_ENOMEM; |
626 | 0 | strcpy(*(char **)var->fill_value, *(char **)data); |
627 | 0 | } |
628 | 0 | else |
629 | 0 | *(char **)var->fill_value = NULL; |
630 | 0 | } |
631 | 153 | else |
632 | 153 | memcpy(var->fill_value, data, type_size); |
633 | | |
634 | | /* Indicate that the fill value was changed, if the variable has already |
635 | | * been created in the file, so the dataset gets deleted and re-created. */ |
636 | 153 | if (var->created) |
637 | 0 | var->fill_val_changed = NC_TRUE; |
638 | 153 | } |
639 | | |
640 | | /* Copy the attribute data, if there is any. VLENs and string |
641 | | * arrays have to be handled specially. */ |
642 | 64.7k | if (att->len) |
643 | 54.7k | { |
644 | 54.7k | nc_type type_class; /* Class of attribute's type */ |
645 | | |
646 | | /* Get class for this type. */ |
647 | 54.7k | if ((retval = nc4_get_typeclass(h5, file_type, &type_class))) |
648 | 0 | return retval; |
649 | | |
650 | 54.7k | assert(data); |
651 | 54.7k | if (type_class == NC_VLEN) |
652 | 0 | { |
653 | 0 | const hvl_t *vldata1; |
654 | 0 | NC_TYPE_INFO_T *vltype; |
655 | 0 | size_t base_typelen; |
656 | | |
657 | | /* Get the type object for the attribute's type */ |
658 | 0 | if ((retval = nc4_find_type(h5, file_type, &vltype))) |
659 | 0 | BAIL(retval); |
660 | | |
661 | | /* Retrieve the size of the base type */ |
662 | 0 | if ((retval = nc4_get_typelen_mem(h5, vltype->u.v.base_nc_typeid, &base_typelen))) |
663 | 0 | BAIL(retval); |
664 | | |
665 | 0 | vldata1 = data; |
666 | 0 | if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t)))) |
667 | 0 | BAIL(NC_ENOMEM); |
668 | 0 | for (i = 0; i < att->len; i++) |
669 | 0 | { |
670 | 0 | att->vldata[i].len = vldata1[i].len; |
671 | | /* Warning, this only works for cases described for nc_free_vlen() */ |
672 | 0 | if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len))) |
673 | 0 | BAIL(NC_ENOMEM); |
674 | 0 | memcpy(att->vldata[i].p, vldata1[i].p, base_typelen * att->vldata[i].len); |
675 | 0 | } |
676 | 0 | } |
677 | 54.7k | else if (type_class == NC_STRING) |
678 | 0 | { |
679 | 0 | LOG((4, "copying array of NC_STRING")); |
680 | 0 | if (!(att->stdata = malloc(sizeof(char *) * att->len))) { |
681 | 0 | BAIL(NC_ENOMEM); |
682 | 0 | } |
683 | | |
684 | | /* If we are overwriting an existing attribute, |
685 | | specifically an NC_CHAR, we need to clean up |
686 | | the pre-existing att->data. */ |
687 | 0 | if (!new_att && att->data) { |
688 | 0 | free(att->data); |
689 | 0 | att->data = NULL; |
690 | 0 | } |
691 | |
|
692 | 0 | for (i = 0; i < att->len; i++) |
693 | 0 | { |
694 | 0 | if(NULL != ((char **)data)[i]) { |
695 | 0 | LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1)); |
696 | 0 | if (!(att->stdata[i] = strdup(((char **)data)[i]))) |
697 | 0 | BAIL(NC_ENOMEM); |
698 | 0 | } |
699 | 0 | else |
700 | 0 | att->stdata[i] = ((char **)data)[i]; |
701 | 0 | } |
702 | 0 | } |
703 | 54.7k | else |
704 | 54.7k | { |
705 | | /* [Re]allocate memory for the attribute data */ |
706 | 54.7k | if (!new_att) |
707 | 0 | free (att->data); |
708 | 54.7k | if (!(att->data = malloc(att->len * type_size))) |
709 | 0 | BAIL(NC_ENOMEM); |
710 | | |
711 | | /* Just copy the data, for non-atomic types */ |
712 | 54.7k | if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM) |
713 | 0 | memcpy(att->data, data, len * type_size); |
714 | 54.7k | else |
715 | 54.7k | { |
716 | | /* Data types are like religions, in that one can convert. */ |
717 | 54.7k | if ((retval = nc4_convert_type(data, att->data, mem_type, file_type, |
718 | 54.7k | len, &range_error, NULL, |
719 | 54.7k | (h5->cmode & NC_CLASSIC_MODEL)))) |
720 | 0 | BAIL(retval); |
721 | 54.7k | } |
722 | 54.7k | } |
723 | 54.7k | } |
724 | 64.7k | att->dirty = NC_TRUE; |
725 | 64.7k | att->created = NC_FALSE; |
726 | | |
727 | | /* Mark attributes on variable dirty, so they get written */ |
728 | 64.7k | if(var) |
729 | 13.1k | var->attr_dirty = NC_TRUE; |
730 | | |
731 | 64.7k | exit: |
732 | | /* If there was an error return it, otherwise return any potential |
733 | | range error value. If none, return NC_NOERR as usual.*/ |
734 | 64.7k | if (retval) |
735 | 0 | return retval; |
736 | 64.7k | if (range_error) |
737 | 0 | return NC_ERANGE; |
738 | 64.7k | return NC_NOERR; |
739 | 64.7k | } |
740 | | |
741 | | /** |
742 | | * @internal Write an attribute to a netCDF-4/HDF5 file, converting |
743 | | * data type if necessary. |
744 | | * |
745 | | * @param ncid File and group ID. |
746 | | * @param varid Variable ID. |
747 | | * @param name Name of attribute. |
748 | | * @param file_type Type of the attribute data in file. |
749 | | * @param len Number of elements in attribute array. |
750 | | * @param data Attribute data. |
751 | | * @param mem_type Type of data in memory. |
752 | | * |
753 | | * @return ::NC_NOERR No error. |
754 | | * @return ::NC_EINVAL Invalid parameters. |
755 | | * @return ::NC_EBADID Bad ncid. |
756 | | * @return ::NC_ENOTVAR Variable not found. |
757 | | * @return ::NC_EBADNAME Name contains illegal characters. |
758 | | * @return ::NC_ENAMEINUSE Name already in use. |
759 | | * @author Ed Hartnett, Dennis Heimbigner |
760 | | */ |
761 | | int |
762 | | NC4_HDF5_put_att(int ncid, int varid, const char *name, nc_type file_type, |
763 | | size_t len, const void *data, nc_type mem_type) |
764 | 137k | { |
765 | 137k | NC_FILE_INFO_T *h5; |
766 | 137k | NC_GRP_INFO_T *grp; |
767 | 137k | int ret; |
768 | | |
769 | | /* Find info for this file, group, and h5 info. */ |
770 | 137k | if ((ret = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5))) |
771 | 0 | return ret; |
772 | 137k | assert(grp && h5); |
773 | | |
774 | 137k | return nc4_put_att(grp, varid, name, file_type, len, data, mem_type, 0); |
775 | 137k | } |
776 | | |
777 | | /** |
778 | | * @internal Learn about an att. All the nc4 nc_inq_ functions just |
779 | | * call nc4_get_att to get the metadata on an attribute. |
780 | | * |
781 | | * @param ncid File and group ID. |
782 | | * @param varid Variable ID. |
783 | | * @param name Name of attribute. |
784 | | * @param xtypep Pointer that gets type of attribute. |
785 | | * @param lenp Pointer that gets length of attribute data array. |
786 | | * |
787 | | * @return ::NC_NOERR No error. |
788 | | * @return ::NC_EBADID Bad ncid. |
789 | | * @author Ed Hartnett |
790 | | */ |
791 | | int |
792 | | NC4_HDF5_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, |
793 | | size_t *lenp) |
794 | 704k | { |
795 | 704k | NC_FILE_INFO_T *h5; |
796 | 704k | NC_GRP_INFO_T *grp; |
797 | 704k | NC_VAR_INFO_T *var = NULL; |
798 | 704k | char norm_name[NC_MAX_NAME + 1]; |
799 | 704k | int retval; |
800 | | |
801 | 704k | LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); |
802 | | |
803 | | /* Find the file, group, and var info, and do lazy att read if |
804 | | * needed. */ |
805 | 704k | if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name, |
806 | 704k | &h5, &grp, &var, NULL))) |
807 | 0 | return retval; |
808 | | |
809 | | /* If this is one of the reserved atts, use nc_get_att_special. */ |
810 | 704k | if (!var) |
811 | 92.5k | { |
812 | 92.5k | const NC_reservedatt *ra = NC_findreserved(norm_name); |
813 | 92.5k | if (ra && ra->flags & NAMEONLYFLAG) |
814 | 0 | return nc4_get_att_special(h5, norm_name, xtypep, NC_NAT, lenp, NULL, |
815 | 0 | NULL); |
816 | 92.5k | } |
817 | | |
818 | 704k | return nc4_get_att_ptrs(h5, grp, var, norm_name, xtypep, NC_NAT, |
819 | 704k | lenp, NULL, NULL); |
820 | 704k | } |
821 | | |
822 | | /** |
823 | | * @internal Learn an attnum, given a name. |
824 | | * |
825 | | * @param ncid File and group ID. |
826 | | * @param varid Variable ID. |
827 | | * @param name Name of attribute. |
828 | | * @param attnump Pointer that gets the attribute index number. |
829 | | * |
830 | | * @return ::NC_NOERR No error. |
831 | | * @author Ed Hartnett |
832 | | */ |
833 | | int |
834 | | NC4_HDF5_inq_attid(int ncid, int varid, const char *name, int *attnump) |
835 | 131k | { |
836 | 131k | NC_FILE_INFO_T *h5; |
837 | 131k | NC_GRP_INFO_T *grp; |
838 | 131k | NC_VAR_INFO_T *var = NULL; |
839 | 131k | char norm_name[NC_MAX_NAME + 1]; |
840 | 131k | int retval; |
841 | | |
842 | 131k | LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); |
843 | | |
844 | | /* Find the file, group, and var info, and do lazy att read if |
845 | | * needed. */ |
846 | 131k | if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name, |
847 | 131k | &h5, &grp, &var, NULL))) |
848 | 0 | return retval; |
849 | | |
850 | | /* If this is one of the reserved atts, use nc_get_att_special. */ |
851 | 131k | if (!var) |
852 | 2.33k | { |
853 | 2.33k | const NC_reservedatt *ra = NC_findreserved(norm_name); |
854 | 2.33k | if (ra && ra->flags & NAMEONLYFLAG) |
855 | 0 | return nc4_get_att_special(h5, norm_name, NULL, NC_NAT, NULL, attnump, |
856 | 0 | NULL); |
857 | 2.33k | } |
858 | | |
859 | 131k | return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, NC_NAT, |
860 | 131k | NULL, attnump, NULL); |
861 | 131k | } |
862 | | |
863 | | /** |
864 | | * @internal Given an attnum, find the att's name. |
865 | | * |
866 | | * @param ncid File and group ID. |
867 | | * @param varid Variable ID. |
868 | | * @param attnum The index number of the attribute. |
869 | | * @param name Pointer that gets name of attribute. |
870 | | * |
871 | | * @return ::NC_NOERR No error. |
872 | | * @return ::NC_EBADID Bad ncid. |
873 | | * @author Ed Hartnett |
874 | | */ |
875 | | int |
876 | | NC4_HDF5_inq_attname(int ncid, int varid, int attnum, char *name) |
877 | 71.4k | { |
878 | 71.4k | NC_ATT_INFO_T *att; |
879 | 71.4k | int retval; |
880 | | |
881 | 71.4k | LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); |
882 | | |
883 | | /* Find the file, group, and var info, and do lazy att read if |
884 | | * needed. */ |
885 | 71.4k | if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, NULL, attnum, 0, NULL, |
886 | 71.4k | NULL, NULL, NULL, &att))) |
887 | 0 | return retval; |
888 | 71.4k | assert(att); |
889 | | |
890 | | /* Get the name. */ |
891 | 71.4k | if (name) |
892 | 71.4k | strcpy(name, att->hdr.name); |
893 | | |
894 | 71.4k | return NC_NOERR; |
895 | 71.4k | } |
896 | | |
897 | | /** |
898 | | * @internal Get an attribute. |
899 | | * |
900 | | * @param ncid File and group ID. |
901 | | * @param varid Variable ID. |
902 | | * @param name Name of attribute. |
903 | | * @param value Pointer that gets attribute data. |
904 | | * @param memtype The type the data should be converted to as it is |
905 | | * read. |
906 | | * |
907 | | * @return ::NC_NOERR No error. |
908 | | * @return ::NC_EBADID Bad ncid. |
909 | | * @author Ed Hartnett |
910 | | */ |
911 | | int |
912 | | NC4_HDF5_get_att(int ncid, int varid, const char *name, void *value, |
913 | | nc_type memtype) |
914 | 86.6k | { |
915 | 86.6k | NC_FILE_INFO_T *h5; |
916 | 86.6k | NC_GRP_INFO_T *grp; |
917 | 86.6k | NC_VAR_INFO_T *var = NULL; |
918 | 86.6k | char norm_name[NC_MAX_NAME + 1]; |
919 | 86.6k | int retval; |
920 | | |
921 | 86.6k | LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); |
922 | | |
923 | | /* Find the file, group, and var info, and do lazy att read if |
924 | | * needed. */ |
925 | 86.6k | if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name, |
926 | 86.6k | &h5, &grp, &var, NULL))) |
927 | 0 | return retval; |
928 | | |
929 | | /* If this is one of the reserved atts, use nc_get_att_special. */ |
930 | 86.6k | if (!var) |
931 | 55.6k | { |
932 | 55.6k | const NC_reservedatt *ra = NC_findreserved(norm_name); |
933 | 55.6k | if (ra && ra->flags & NAMEONLYFLAG) |
934 | 0 | return nc4_get_att_special(h5, norm_name, NULL, NC_NAT, NULL, NULL, |
935 | 0 | value); |
936 | 55.6k | } |
937 | | |
938 | 86.6k | return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, memtype, |
939 | 86.6k | NULL, NULL, value); |
940 | 86.6k | } |