1 | /* Copyright 2010 University Corporation for Atmospheric |
---|
2 | Research/Unidata. See COPYRIGHT file for more info. |
---|
3 | |
---|
4 | This file has the var and att copy functions. |
---|
5 | |
---|
6 | "$Id: copy.c,v 1.1 2010/06/01 15:46:49 ed Exp $" |
---|
7 | */ |
---|
8 | |
---|
9 | #include "ncdispatch.h" |
---|
10 | #include <nc_logging.h> |
---|
11 | |
---|
12 | #ifdef USE_NETCDF4 |
---|
13 | /* Compare two netcdf types for equality. Must have the ncids as well, |
---|
14 | to find user-defined types. */ |
---|
15 | static int |
---|
16 | NC_compare_nc_types(int ncid1, int typeid1, int ncid2, int typeid2, |
---|
17 | int *equalp) |
---|
18 | { |
---|
19 | int ret = NC_NOERR; |
---|
20 | |
---|
21 | /* If you don't care about the answer, neither do I! */ |
---|
22 | if(equalp == NULL) |
---|
23 | return NC_NOERR; |
---|
24 | |
---|
25 | /* Assume the types are not equal. If we find any inequality, then |
---|
26 | exit with NC_NOERR and we're done. */ |
---|
27 | *equalp = 0; |
---|
28 | |
---|
29 | /* Atomic types are so easy! */ |
---|
30 | if (typeid1 <= NC_MAX_ATOMIC_TYPE) |
---|
31 | { |
---|
32 | if (typeid2 != typeid1) |
---|
33 | return NC_NOERR; |
---|
34 | *equalp = 1; |
---|
35 | } |
---|
36 | else |
---|
37 | { |
---|
38 | int i, ret, equal1; |
---|
39 | char name1[NC_MAX_NAME]; |
---|
40 | char name2[NC_MAX_NAME]; |
---|
41 | size_t size1, size2; |
---|
42 | nc_type base1, base2; |
---|
43 | size_t nelems1, nelems2; |
---|
44 | int class1, class2; |
---|
45 | void* value1 = NULL; |
---|
46 | void* value2 = NULL; |
---|
47 | size_t offset1, offset2; |
---|
48 | nc_type ftype1, ftype2; |
---|
49 | int ndims1, ndims2; |
---|
50 | int dimsizes1[NC_MAX_VAR_DIMS]; |
---|
51 | int dimsizes2[NC_MAX_VAR_DIMS]; |
---|
52 | |
---|
53 | /* Find out about the two types. */ |
---|
54 | if ((ret = nc_inq_user_type(ncid1, typeid1, name1, &size1, |
---|
55 | &base1, &nelems1, &class1))) |
---|
56 | return ret; |
---|
57 | if ((ret = nc_inq_user_type(ncid2, typeid2, name2, &size2, |
---|
58 | &base2, &nelems2, &class2))) |
---|
59 | return ret; |
---|
60 | |
---|
61 | /* Check the obvious. */ |
---|
62 | if(size1 != size2 || class1 != class2 || strcmp(name1,name2)) |
---|
63 | return NC_NOERR; |
---|
64 | |
---|
65 | /* Check user-defined types in detail. */ |
---|
66 | switch(class1) |
---|
67 | { |
---|
68 | case NC_VLEN: |
---|
69 | if((ret = NC_compare_nc_types(ncid1, base1, ncid2, |
---|
70 | base1, &equal1))) |
---|
71 | return ret; |
---|
72 | if(!equal1) |
---|
73 | return NC_NOERR; |
---|
74 | break; |
---|
75 | case NC_OPAQUE: |
---|
76 | /* Already checked size above. */ |
---|
77 | break; |
---|
78 | case NC_ENUM: |
---|
79 | if(base1 != base2 || nelems1 != nelems2) return NC_NOERR; |
---|
80 | |
---|
81 | if (!(value1 = malloc(size1))) |
---|
82 | return NC_ENOMEM; |
---|
83 | if (!(value2 = malloc(size2))) |
---|
84 | return NC_ENOMEM; |
---|
85 | |
---|
86 | for(i = 0; i < nelems1; i++) |
---|
87 | { |
---|
88 | if ((ret = nc_inq_enum_member(ncid1, typeid1, i, name1, |
---|
89 | value1)) || |
---|
90 | (ret = nc_inq_enum_member(ncid2, typeid2, i, name2, |
---|
91 | value2)) || |
---|
92 | strcmp(name1, name2) || memcmp(value1, value2, size1)) |
---|
93 | { |
---|
94 | free(value1); |
---|
95 | free(value2); |
---|
96 | return ret; |
---|
97 | } |
---|
98 | } |
---|
99 | free(value1); |
---|
100 | free(value2); |
---|
101 | break; |
---|
102 | case NC_COMPOUND: |
---|
103 | if(nelems1 != nelems2) |
---|
104 | return NC_NOERR; |
---|
105 | |
---|
106 | /* Compare each field. Each must be equal! */ |
---|
107 | for(i = 0; i < nelems1; i++) |
---|
108 | { |
---|
109 | int j; |
---|
110 | if ((ret = nc_inq_compound_field(ncid1, typeid1, i, name1, &offset1, |
---|
111 | &ftype1, &ndims1, dimsizes1))) |
---|
112 | return ret; |
---|
113 | if ((ret = nc_inq_compound_field(ncid2, typeid2, i, name2, &offset2, |
---|
114 | &ftype2, &ndims2, dimsizes2))) |
---|
115 | return ret; |
---|
116 | if(ndims1 != ndims2) |
---|
117 | return NC_NOERR; |
---|
118 | for(j = 0; j < ndims1;j++) |
---|
119 | if(dimsizes1[j] != dimsizes2[j]) |
---|
120 | return NC_NOERR; |
---|
121 | |
---|
122 | /* Compare user-defined field types. */ |
---|
123 | if((ret = NC_compare_nc_types(ncid1, ftype1, ncid2, ftype2, |
---|
124 | &equal1))) |
---|
125 | return ret; |
---|
126 | if(!equal1) |
---|
127 | return NC_NOERR; |
---|
128 | } |
---|
129 | break; |
---|
130 | default: |
---|
131 | return NC_EINVAL; |
---|
132 | } |
---|
133 | *equalp = 1; |
---|
134 | } |
---|
135 | return ret; |
---|
136 | } |
---|
137 | |
---|
138 | /* Recursively hunt for a netCDF type id. (Code from nc4internal.c); |
---|
139 | Return matching typeid or 0 if not found. */ |
---|
140 | static int |
---|
141 | NC_rec_find_nc_type(int ncid1, nc_type tid1, int ncid2, nc_type* tid2) |
---|
142 | { |
---|
143 | int i,ret = NC_NOERR; |
---|
144 | int nids; |
---|
145 | int* ids = NULL; |
---|
146 | |
---|
147 | /* Get all types in grp ncid2 */ |
---|
148 | if(tid2) |
---|
149 | *tid2 = 0; |
---|
150 | if ((ret = nc_inq_typeids(ncid2, &nids, NULL))) |
---|
151 | return ret; |
---|
152 | if (nids) |
---|
153 | { |
---|
154 | if (!(ids = (int *)malloc(nids * sizeof(int)))) |
---|
155 | return NC_ENOMEM; |
---|
156 | if ((ret = nc_inq_typeids(ncid2, &nids, ids))) |
---|
157 | return ret; |
---|
158 | for(i = 0; i < nids; i++) |
---|
159 | { |
---|
160 | int equal = 0; |
---|
161 | if ((ret = NC_compare_nc_types(ncid1, tid1, ncid2, ids[i], &equal))) |
---|
162 | return ret; |
---|
163 | if(equal) |
---|
164 | { |
---|
165 | if(tid2) |
---|
166 | *tid2 = ids[i]; |
---|
167 | free(ids); |
---|
168 | return NC_NOERR; |
---|
169 | } |
---|
170 | } |
---|
171 | free(ids); |
---|
172 | } |
---|
173 | |
---|
174 | /* recurse */ |
---|
175 | if ((ret = nc_inq_grps(ncid1, &nids, NULL))) |
---|
176 | return ret; |
---|
177 | if (nids) |
---|
178 | { |
---|
179 | if (!(ids = (int *)malloc(nids * sizeof(int)))) |
---|
180 | return NC_ENOMEM; |
---|
181 | if ((ret = nc_inq_grps(ncid1, &nids, ids))) |
---|
182 | { |
---|
183 | free(ids); |
---|
184 | return ret; |
---|
185 | } |
---|
186 | for (i = 0; i < nids; i++) |
---|
187 | { |
---|
188 | ret = NC_rec_find_nc_type(ncid1, tid1, ids[i], tid2); |
---|
189 | if (ret && ret != NC_EBADTYPE) |
---|
190 | break; |
---|
191 | if (tid2 && *tid2 != 0) /* found */ |
---|
192 | { |
---|
193 | free(ids); |
---|
194 | return NC_NOERR; |
---|
195 | } |
---|
196 | } |
---|
197 | free(ids); |
---|
198 | } |
---|
199 | return NC_EBADTYPE; /* not found */ |
---|
200 | } |
---|
201 | |
---|
202 | /* Given a type in one file, find its equal (if any) in another |
---|
203 | * file. It sounds so simple, but it's a real pain! */ |
---|
204 | static int |
---|
205 | NC_find_equal_type(int ncid1, nc_type xtype1, int ncid2, nc_type *xtype2) |
---|
206 | { |
---|
207 | int ret = NC_NOERR; |
---|
208 | |
---|
209 | /* Check input */ |
---|
210 | if(xtype1 <= NC_NAT) |
---|
211 | return NC_EINVAL; |
---|
212 | |
---|
213 | /* Handle atomic types. */ |
---|
214 | if (xtype1 <= NC_MAX_ATOMIC_TYPE) |
---|
215 | { |
---|
216 | if(xtype2) |
---|
217 | *xtype2 = xtype1; |
---|
218 | return NC_NOERR; |
---|
219 | } |
---|
220 | |
---|
221 | /* Recursively search group ncid2 and its children |
---|
222 | to find a type that is equal (using compare_type) |
---|
223 | to xtype1. */ |
---|
224 | ret = NC_rec_find_nc_type(ncid1, xtype1 , ncid2, xtype2); |
---|
225 | return ret; |
---|
226 | } |
---|
227 | |
---|
228 | #endif /* USE_NETCDF4 */ |
---|
229 | |
---|
230 | /* This will copy a variable from one file to another, assuming |
---|
231 | dimensions in output file are already defined and have same |
---|
232 | dimension ids. |
---|
233 | |
---|
234 | This function must work even if the files are different formats, |
---|
235 | (i.e. one old netcdf, the other hdf5-netcdf.) |
---|
236 | |
---|
237 | But if you're copying into a netcdf-3 file, from a netcdf-4 file, |
---|
238 | you must be copying a var of one of the six netcdf-3 |
---|
239 | types. Similarly for the attributes. */ |
---|
240 | int |
---|
241 | nc_copy_var(int ncid_in, int varid_in, int ncid_out) |
---|
242 | { |
---|
243 | char name[NC_MAX_NAME + 1]; |
---|
244 | char att_name[NC_MAX_NAME + 1]; |
---|
245 | nc_type xtype; |
---|
246 | int ndims, dimids[NC_MAX_VAR_DIMS], natts, real_ndims; |
---|
247 | int varid_out; |
---|
248 | int a, d; |
---|
249 | void *data = NULL; |
---|
250 | size_t *count = NULL, *start = NULL; |
---|
251 | size_t reclen = 1; |
---|
252 | size_t *dimlen = NULL; |
---|
253 | int retval = NC_NOERR; |
---|
254 | size_t type_size; |
---|
255 | int src_format, dest_format; |
---|
256 | char type_name[NC_MAX_NAME+1]; |
---|
257 | |
---|
258 | /* Learn about this var. */ |
---|
259 | if ((retval = nc_inq_var(ncid_in, varid_in, name, &xtype, |
---|
260 | &ndims, dimids, &natts))) |
---|
261 | return retval; |
---|
262 | |
---|
263 | #ifdef USE_NETCDF4 |
---|
264 | LOG((2, "nc_copy_var: ncid_in 0x%x varid_in %d ncid_out 0x%x", |
---|
265 | ncid_in, varid_in, ncid_out)); |
---|
266 | #endif |
---|
267 | |
---|
268 | /* Make sure we are not trying to write into a netcdf-3 file |
---|
269 | * anything that won't fit in netcdf-3. */ |
---|
270 | if ((retval = nc_inq_format(ncid_in, &src_format))) |
---|
271 | return retval; |
---|
272 | if ((retval = nc_inq_format(ncid_out, &dest_format))) |
---|
273 | return retval; |
---|
274 | if ((dest_format == NC_FORMAT_CLASSIC || dest_format == NC_FORMAT_64BIT) && |
---|
275 | src_format == NC_FORMAT_NETCDF4 && xtype > NC_DOUBLE) |
---|
276 | return NC_ENOTNC4; |
---|
277 | |
---|
278 | /* Later on, we will need to know the size of this type. */ |
---|
279 | if ((retval = nc_inq_type(ncid_in, xtype, type_name, &type_size))) |
---|
280 | return retval; |
---|
281 | #ifdef USE_NETCDF4 |
---|
282 | LOG((3, "type %s has size %d", type_name, type_size)); |
---|
283 | #endif |
---|
284 | |
---|
285 | /* Switch back to define mode, and create the output var. */ |
---|
286 | retval = nc_redef(ncid_out); |
---|
287 | if (retval && retval != NC_EINDEFINE) |
---|
288 | BAIL(retval); |
---|
289 | if ((retval = nc_def_var(ncid_out, name, xtype, |
---|
290 | ndims, dimids, &varid_out))) |
---|
291 | BAIL(retval); |
---|
292 | |
---|
293 | /* Copy the attributes. */ |
---|
294 | for (a=0; a<natts; a++) |
---|
295 | { |
---|
296 | if ((retval = nc_inq_attname(ncid_in, varid_in, a, att_name))) |
---|
297 | BAIL(retval); |
---|
298 | if ((retval = nc_copy_att(ncid_in, varid_in, att_name, |
---|
299 | ncid_out, varid_out))) |
---|
300 | BAIL(retval); |
---|
301 | } |
---|
302 | |
---|
303 | /* End define mode, to write metadata and create file. */ |
---|
304 | nc_enddef(ncid_out); |
---|
305 | nc_sync(ncid_out); |
---|
306 | |
---|
307 | /* Allocate memory for our start and count arrays. If ndims = 0 |
---|
308 | this is a scalar, which I will treat as a 1-D array with one |
---|
309 | element. */ |
---|
310 | real_ndims = ndims ? ndims : 1; |
---|
311 | if (!(start = malloc(real_ndims * sizeof(size_t)))) |
---|
312 | BAIL(NC_ENOMEM); |
---|
313 | if (!(count = malloc(real_ndims * sizeof(size_t)))) |
---|
314 | BAIL(NC_ENOMEM); |
---|
315 | |
---|
316 | /* The start array will be all zeros, except the first element, |
---|
317 | which will be the record number. Count will be the dimension |
---|
318 | size, except for the first element, which will be one, because |
---|
319 | we will copy one record at a time. For this we need the var |
---|
320 | shape. */ |
---|
321 | if (!(dimlen = malloc(real_ndims * sizeof(size_t)))) |
---|
322 | BAIL(NC_ENOMEM); |
---|
323 | |
---|
324 | /* Find out how much data. */ |
---|
325 | for (d=0; d<ndims; d++) |
---|
326 | { |
---|
327 | if ((retval = nc_inq_dimlen(ncid_in, dimids[d], &dimlen[d]))) |
---|
328 | BAIL(retval); |
---|
329 | #ifdef USE_NETCDF4 |
---|
330 | LOG((4, "nc_copy_var: there are %d data", dimlen[d])); |
---|
331 | #endif |
---|
332 | } |
---|
333 | |
---|
334 | /* If this is really a scalar, then set the dimlen to 1. */ |
---|
335 | if (ndims == 0) |
---|
336 | dimlen[0] = 1; |
---|
337 | |
---|
338 | for (d=0; d<real_ndims; d++) |
---|
339 | { |
---|
340 | start[d] = 0; |
---|
341 | count[d] = d ? dimlen[d] : 1; |
---|
342 | if (d) reclen *= dimlen[d]; |
---|
343 | } |
---|
344 | |
---|
345 | /* If there are no records, we're done. */ |
---|
346 | if (!dimlen[0]) |
---|
347 | goto exit; |
---|
348 | |
---|
349 | /* Allocate memory for one record. */ |
---|
350 | if (!(data = malloc(reclen * type_size))) |
---|
351 | return NC_ENOMEM; |
---|
352 | |
---|
353 | /* Copy the var data one record at a time. */ |
---|
354 | for (start[0]=0; !retval && start[0]<(size_t)dimlen[0]; start[0]++) |
---|
355 | { |
---|
356 | switch (xtype) |
---|
357 | { |
---|
358 | case NC_BYTE: |
---|
359 | retval = nc_get_vara_schar(ncid_in, varid_in, start, count, |
---|
360 | (signed char *)data); |
---|
361 | if (!retval) |
---|
362 | retval = nc_put_vara_schar(ncid_out, varid_out, start, count, |
---|
363 | (const signed char *)data); |
---|
364 | break; |
---|
365 | case NC_CHAR: |
---|
366 | retval = nc_get_vara_text(ncid_in, varid_in, start, count, |
---|
367 | (char *)data); |
---|
368 | if (!retval) |
---|
369 | retval = nc_put_vara_text(ncid_out, varid_out, start, count, |
---|
370 | (char *)data); |
---|
371 | break; |
---|
372 | case NC_SHORT: |
---|
373 | retval = nc_get_vara_short(ncid_in, varid_in, start, count, |
---|
374 | (short *)data); |
---|
375 | if (!retval) |
---|
376 | retval = nc_put_vara_short(ncid_out, varid_out, start, count, |
---|
377 | (short *)data); |
---|
378 | break; |
---|
379 | case NC_INT: |
---|
380 | retval = nc_get_vara_int(ncid_in, varid_in, start, count, |
---|
381 | (int *)data); |
---|
382 | if (!retval) |
---|
383 | retval = nc_put_vara_int(ncid_out, varid_out, start, count, |
---|
384 | (int *)data); |
---|
385 | break; |
---|
386 | case NC_FLOAT: |
---|
387 | retval = nc_get_vara_float(ncid_in, varid_in, start, count, |
---|
388 | (float *)data); |
---|
389 | if (!retval) |
---|
390 | retval = nc_put_vara_float(ncid_out, varid_out, start, count, |
---|
391 | (float *)data); |
---|
392 | break; |
---|
393 | case NC_DOUBLE: |
---|
394 | retval = nc_get_vara_double(ncid_in, varid_in, start, count, |
---|
395 | (double *)data); |
---|
396 | if (!retval) |
---|
397 | retval = nc_put_vara_double(ncid_out, varid_out, start, count, |
---|
398 | (double *)data); |
---|
399 | break; |
---|
400 | case NC_UBYTE: |
---|
401 | retval = nc_get_vara_uchar(ncid_in, varid_in, start, count, |
---|
402 | (unsigned char *)data); |
---|
403 | if (!retval) |
---|
404 | retval = nc_put_vara_uchar(ncid_out, varid_out, start, count, |
---|
405 | (unsigned char *)data); |
---|
406 | break; |
---|
407 | case NC_USHORT: |
---|
408 | retval = nc_get_vara_ushort(ncid_in, varid_in, start, count, |
---|
409 | (unsigned short *)data); |
---|
410 | if (!retval) |
---|
411 | retval = nc_put_vara_ushort(ncid_out, varid_out, start, count, |
---|
412 | (unsigned short *)data); |
---|
413 | break; |
---|
414 | case NC_UINT: |
---|
415 | retval = nc_get_vara_uint(ncid_in, varid_in, start, count, |
---|
416 | (unsigned int *)data); |
---|
417 | if (!retval) |
---|
418 | retval = nc_put_vara_uint(ncid_out, varid_out, start, count, |
---|
419 | (unsigned int *)data); |
---|
420 | break; |
---|
421 | case NC_INT64: |
---|
422 | retval = nc_get_vara_longlong(ncid_in, varid_in, start, count, |
---|
423 | (long long *)data); |
---|
424 | if (!retval) |
---|
425 | retval = nc_put_vara_longlong(ncid_out, varid_out, start, count, |
---|
426 | (long long *)data); |
---|
427 | break; |
---|
428 | case NC_UINT64: |
---|
429 | retval = nc_get_vara_ulonglong(ncid_in, varid_in, start, count, |
---|
430 | (unsigned long long *)data); |
---|
431 | if (!retval) |
---|
432 | retval = nc_put_vara_ulonglong(ncid_out, varid_out, start, count, |
---|
433 | (unsigned long long *)data); |
---|
434 | break; |
---|
435 | default: |
---|
436 | retval = NC_EBADTYPE; |
---|
437 | } |
---|
438 | } |
---|
439 | |
---|
440 | exit: |
---|
441 | if (data) free(data); |
---|
442 | if (dimlen) free(dimlen); |
---|
443 | if (start) free(start); |
---|
444 | if (count) free(count); |
---|
445 | return retval; |
---|
446 | } |
---|
447 | |
---|
448 | static int |
---|
449 | NC_copy_att(int ncid_in, int varid_in, const char *name, |
---|
450 | int ncid_out, int varid_out) |
---|
451 | { |
---|
452 | nc_type xtype; |
---|
453 | size_t len; |
---|
454 | void *data=NULL; |
---|
455 | int res; |
---|
456 | |
---|
457 | LOG((2, "nc_copy_att: ncid_in 0x%x varid_in %d name %s", |
---|
458 | ncid_in, varid_in, name)); |
---|
459 | |
---|
460 | /* Find out about the attribute to be copied. */ |
---|
461 | if ((res = nc_inq_att(ncid_in, varid_in, name, &xtype, &len))) |
---|
462 | return res; |
---|
463 | |
---|
464 | if (xtype < NC_STRING) |
---|
465 | { |
---|
466 | /* Handle non-string atomic types. */ |
---|
467 | if (len) |
---|
468 | if (!(data = malloc(len * NC_atomictypelen(xtype)))) |
---|
469 | return NC_ENOMEM; |
---|
470 | |
---|
471 | res = nc_get_att(ncid_in, varid_in, name, data); |
---|
472 | if (!res) |
---|
473 | res = nc_put_att(ncid_out, varid_out, name, xtype, |
---|
474 | len, data); |
---|
475 | if (len) |
---|
476 | free(data); |
---|
477 | } |
---|
478 | #ifdef USE_NETCDF4 |
---|
479 | else if (xtype == NC_STRING) |
---|
480 | { |
---|
481 | /* Copy string attributes. */ |
---|
482 | char **str_data; |
---|
483 | if (!(str_data = malloc(sizeof(char *) * len))) |
---|
484 | return NC_ENOMEM; |
---|
485 | res = nc_get_att_string(ncid_in, varid_in, name, str_data); |
---|
486 | if (!res) |
---|
487 | res = nc_put_att_string(ncid_out, varid_out, name, len, |
---|
488 | (const char **)str_data); |
---|
489 | nc_free_string(len, str_data); |
---|
490 | free(str_data); |
---|
491 | } |
---|
492 | else |
---|
493 | { |
---|
494 | /* Copy user-defined type attributes. */ |
---|
495 | int class; |
---|
496 | size_t size; |
---|
497 | void *data; |
---|
498 | nc_type xtype_out = NC_NAT; |
---|
499 | |
---|
500 | /* Find out if there is an equal type in the output file. */ |
---|
501 | /* Note: original code used a libsrc4 specific internal function |
---|
502 | which we had to "duplicate" here */ |
---|
503 | if ((res = NC_find_equal_type(ncid_in, xtype, ncid_out, &xtype_out))) |
---|
504 | return res; |
---|
505 | if (xtype_out) |
---|
506 | { |
---|
507 | /* We found an equal type! */ |
---|
508 | if ((res = nc_inq_user_type(ncid_in, xtype, NULL, &size, |
---|
509 | NULL, NULL, &class))) |
---|
510 | return res; |
---|
511 | if (class == NC_VLEN) /* VLENs are different... */ |
---|
512 | { |
---|
513 | nc_vlen_t *vldata; |
---|
514 | int i; |
---|
515 | if (!(vldata = malloc(sizeof(nc_vlen_t) * len))) |
---|
516 | return NC_ENOMEM; |
---|
517 | if ((res = nc_get_att(ncid_in, varid_in, name, vldata))) |
---|
518 | return res; |
---|
519 | if ((res = nc_put_att(ncid_out, varid_out, name, xtype_out, |
---|
520 | len, vldata))) |
---|
521 | return res; |
---|
522 | for (i = 0; i < len; i++) |
---|
523 | if((res = nc_free_vlen(&vldata[i]))) |
---|
524 | return res; |
---|
525 | free(vldata); |
---|
526 | } |
---|
527 | else /* not VLEN */ |
---|
528 | { |
---|
529 | if (!(data = malloc(size * len))) |
---|
530 | return NC_ENOMEM; |
---|
531 | res = nc_get_att(ncid_in, varid_in, name, data); |
---|
532 | if (!res) |
---|
533 | res = nc_put_att(ncid_out, varid_out, name, xtype_out, len, data); |
---|
534 | free(data); |
---|
535 | } |
---|
536 | } |
---|
537 | } |
---|
538 | #endif /*!USE_NETCDF4*/ |
---|
539 | return res; |
---|
540 | } |
---|
541 | |
---|
542 | /* Copy an attribute from one open file to another. |
---|
543 | |
---|
544 | Special programming challenge: this function must work even if one |
---|
545 | of the other of the files is a netcdf version 1.0 file (i.e. not |
---|
546 | HDF5). So only use top level netcdf api functions. |
---|
547 | |
---|
548 | From the netcdf-3 docs: The output netCDF dataset should be in |
---|
549 | define mode if the attribute to be copied does not already exist |
---|
550 | for the target variable, or if it would cause an existing target |
---|
551 | attribute to grow. |
---|
552 | */ |
---|
553 | int |
---|
554 | nc_copy_att(int ncid_in, int varid_in, const char *name, |
---|
555 | int ncid_out, int varid_out) |
---|
556 | { |
---|
557 | int format, target_natts, target_attid; |
---|
558 | char att_name[NC_MAX_NAME + 1]; |
---|
559 | int a, retval; |
---|
560 | |
---|
561 | /* What is the destination format? */ |
---|
562 | if ((retval = nc_inq_format(ncid_out, &format))) |
---|
563 | return retval; |
---|
564 | |
---|
565 | /* Can't copy to same var in same file. */ |
---|
566 | if (ncid_in == ncid_out && varid_in == varid_out) |
---|
567 | return NC_NOERR; |
---|
568 | |
---|
569 | /* For classic model netCDF-4 files, order of attributes must be |
---|
570 | * maintained during copies. We MUST MAINTAIN ORDER! */ |
---|
571 | if (format == NC_FORMAT_NETCDF4_CLASSIC) |
---|
572 | { |
---|
573 | /* Does this attribute already exist in the target file? */ |
---|
574 | retval = nc_inq_attid(ncid_out, varid_out, name, &target_attid); |
---|
575 | if (retval == NC_ENOTATT) |
---|
576 | { |
---|
577 | /* Attribute does not exist. No order to be preserved. */ |
---|
578 | return NC_copy_att(ncid_in, varid_in, name, ncid_out, varid_out); |
---|
579 | } |
---|
580 | else if (retval == NC_NOERR) |
---|
581 | { |
---|
582 | /* How many atts for this var? */ |
---|
583 | if ((retval = nc_inq_varnatts(ncid_out, varid_out, &target_natts))) |
---|
584 | return retval; |
---|
585 | |
---|
586 | /* If this is the last attribute in the target file, we are |
---|
587 | * off the hook. */ |
---|
588 | if (target_attid == target_natts - 1) |
---|
589 | return NC_copy_att(ncid_in, varid_in, name, ncid_out, varid_out); |
---|
590 | |
---|
591 | /* Order MUST BE MAINTAINED! Copy all existing atts in the target |
---|
592 | * file, stopping at our target att. */ |
---|
593 | for (a = 0; a < target_natts; a++) |
---|
594 | { |
---|
595 | if (a == target_attid) |
---|
596 | { |
---|
597 | if ((retval = NC_copy_att(ncid_in, varid_in, name, ncid_out, varid_out))) |
---|
598 | return retval; |
---|
599 | } |
---|
600 | else |
---|
601 | { |
---|
602 | if ((retval = nc_inq_attname(ncid_out, varid_out, a, att_name))) |
---|
603 | return retval; |
---|
604 | if ((retval = NC_copy_att(ncid_out, varid_out, att_name, |
---|
605 | ncid_out, varid_out))) |
---|
606 | return retval; |
---|
607 | } |
---|
608 | } |
---|
609 | } |
---|
610 | else |
---|
611 | return retval; /* Some other error occured. */ |
---|
612 | } |
---|
613 | else |
---|
614 | return NC_copy_att(ncid_in, varid_in, name, ncid_out, varid_out); |
---|
615 | |
---|
616 | return NC_NOERR; |
---|
617 | } |
---|
618 | |
---|
619 | |
---|