1 | /** \file \internal |
---|
2 | Internal netcdf-4 functions. |
---|
3 | |
---|
4 | This file contains functions internal to the netcdf4 library. None of |
---|
5 | the functions in this file are exposed in the exetnal API. These |
---|
6 | functions all relate to the manipulation of netcdf-4's in-memory |
---|
7 | buffer of metadata information, i.e. the linked list of NC_FILE_INFO_T |
---|
8 | structs. |
---|
9 | |
---|
10 | Copyright 2003-2011, University Corporation for Atmospheric |
---|
11 | Research. See the COPYRIGHT file for copying and redistribution |
---|
12 | conditions. |
---|
13 | |
---|
14 | */ |
---|
15 | #include "config.h" |
---|
16 | #include "nc4internal.h" |
---|
17 | #include "nc.h" /* from libsrc */ |
---|
18 | #include "ncdispatch.h" /* from libdispatch */ |
---|
19 | #include <utf8proc.h> |
---|
20 | |
---|
21 | #define MEGABYTE 1048576 |
---|
22 | |
---|
23 | /* These are the default chunk cache sizes for HDF5 files created or |
---|
24 | * opened with netCDF-4. */ |
---|
25 | extern size_t nc4_chunk_cache_size; |
---|
26 | extern size_t nc4_chunk_cache_nelems; |
---|
27 | extern float nc4_chunk_cache_preemption; |
---|
28 | |
---|
29 | /* This is to track opened HDF5 objects to make sure they are |
---|
30 | * closed. */ |
---|
31 | #ifdef EXTRA_TESTS |
---|
32 | extern int num_spaces; |
---|
33 | #endif /* EXTRA_TESTS */ |
---|
34 | |
---|
35 | #ifdef LOGGING |
---|
36 | /* This is the severity level of messages which will be logged. Use |
---|
37 | severity 0 for errors, 1 for important log messages, 2 for less |
---|
38 | important, etc. */ |
---|
39 | int nc_log_level = -1; |
---|
40 | |
---|
41 | #endif /* LOGGING */ |
---|
42 | |
---|
43 | /* Check and normalize and name. */ |
---|
44 | int |
---|
45 | nc4_check_name(const char *name, char *norm_name) |
---|
46 | { |
---|
47 | char *temp; |
---|
48 | int retval; |
---|
49 | |
---|
50 | /* Check the length. */ |
---|
51 | if (strlen(name) > NC_MAX_NAME) |
---|
52 | return NC_EMAXNAME; |
---|
53 | |
---|
54 | /* Make sure this is a valid netcdf name. This should be done |
---|
55 | * before the name is normalized, because it gives better error |
---|
56 | * codes for bad utf8 strings. */ |
---|
57 | if ((retval = NC_check_name(name))) |
---|
58 | return retval; |
---|
59 | |
---|
60 | /* Normalize the name. */ |
---|
61 | if (!(temp = (char *)utf8proc_NFC((const unsigned char *)name))) |
---|
62 | return NC_EINVAL; |
---|
63 | strcpy(norm_name, temp); |
---|
64 | free(temp); |
---|
65 | |
---|
66 | return NC_NOERR; |
---|
67 | } |
---|
68 | |
---|
69 | /* Given a varid, find its shape. For unlimited dimensions, return |
---|
70 | the current number of records. */ |
---|
71 | static int |
---|
72 | find_var_shape_grp(NC_GRP_INFO_T *grp, int varid, int *ndims, |
---|
73 | int *dimid, size_t *dimlen) |
---|
74 | { |
---|
75 | hid_t datasetid = 0, spaceid = 0; |
---|
76 | NC_VAR_INFO_T *var; |
---|
77 | hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL; |
---|
78 | int d, dataset_ndims = 0; |
---|
79 | int retval = NC_NOERR; |
---|
80 | |
---|
81 | /* Find this var. */ |
---|
82 | for (var = grp->var; var; var = var->next) |
---|
83 | if (var->varid == varid) |
---|
84 | break; |
---|
85 | if (!var) |
---|
86 | return NC_ENOTVAR; |
---|
87 | |
---|
88 | /* Get the dimids and the ndims for this var. */ |
---|
89 | if (ndims) |
---|
90 | *ndims = var->ndims; |
---|
91 | |
---|
92 | if (dimid) |
---|
93 | for (d = 0; d < var->ndims; d++) |
---|
94 | dimid[d] = var->dimids[d]; |
---|
95 | |
---|
96 | if (dimlen) |
---|
97 | { |
---|
98 | /* If the var hasn't been created yet, its size is 0. */ |
---|
99 | if (!var->created) |
---|
100 | { |
---|
101 | for (d = 0; d < var->ndims; d++) |
---|
102 | dimlen[d] = 0; |
---|
103 | } |
---|
104 | else |
---|
105 | { |
---|
106 | /* Get the number of records in the dataset. */ |
---|
107 | if ((retval = nc4_open_var_grp2(grp, var->varid, &datasetid))) |
---|
108 | BAIL(retval); |
---|
109 | if ((spaceid = H5Dget_space(datasetid)) < 0) |
---|
110 | BAIL(NC_EHDFERR); |
---|
111 | #ifdef EXTRA_TESTS |
---|
112 | num_spaces++; |
---|
113 | #endif |
---|
114 | /* If it's a scalar dataset, it has length one. */ |
---|
115 | if (H5Sget_simple_extent_type(spaceid) == H5S_SCALAR) |
---|
116 | { |
---|
117 | dimlen[0] = 1; |
---|
118 | } |
---|
119 | else |
---|
120 | { |
---|
121 | /* Check to make sure ndims is right, then get the len of each |
---|
122 | dim in the space. */ |
---|
123 | if ((dataset_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0) |
---|
124 | BAIL(NC_EHDFERR); |
---|
125 | if (ndims && dataset_ndims != *ndims) |
---|
126 | BAIL(NC_EHDFERR); |
---|
127 | if (!(h5dimlen = malloc(dataset_ndims * sizeof(hsize_t)))) |
---|
128 | BAIL(NC_ENOMEM); |
---|
129 | if (!(h5dimlenmax = malloc(dataset_ndims * sizeof(hsize_t)))) |
---|
130 | BAIL(NC_ENOMEM); |
---|
131 | if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, |
---|
132 | h5dimlen, h5dimlenmax)) < 0) |
---|
133 | BAIL(NC_EHDFERR); |
---|
134 | LOG((5, "find_var_shape_nc: varid %d len %d max: %d", |
---|
135 | varid, (int)h5dimlen[0], (int)h5dimlenmax[0])); |
---|
136 | for (d=0; d<dataset_ndims; d++) |
---|
137 | dimlen[d] = h5dimlen[d]; |
---|
138 | } |
---|
139 | } |
---|
140 | } |
---|
141 | |
---|
142 | exit: |
---|
143 | if (spaceid > 0 && H5Sclose(spaceid) < 0) |
---|
144 | BAIL2(NC_EHDFERR); |
---|
145 | #ifdef EXTRA_TESTS |
---|
146 | num_spaces--; |
---|
147 | #endif |
---|
148 | if (h5dimlen) free(h5dimlen); |
---|
149 | if (h5dimlenmax) free(h5dimlenmax); |
---|
150 | return retval; |
---|
151 | } |
---|
152 | |
---|
153 | /* Given an NC_FILE_INFO_T pointer, add the necessary stuff for a |
---|
154 | * netcdf-4 file. */ |
---|
155 | int |
---|
156 | nc4_nc4f_list_add(NC_FILE_INFO_T *nc, const char *path, int mode) |
---|
157 | { |
---|
158 | NC_HDF5_FILE_INFO_T *h5; |
---|
159 | NC_GRP_INFO_T *grp; |
---|
160 | |
---|
161 | assert(nc && !nc->nc4_info && path); |
---|
162 | |
---|
163 | /* The NC_FILE_INFO_T was allocated and inited by |
---|
164 | ncfunc.c before this function is called. We need to malloc and |
---|
165 | initialize the substructure NC_HDF_FILE_INFO_T. */ |
---|
166 | if (!(nc->nc4_info = calloc(1, sizeof(NC_HDF5_FILE_INFO_T)))) |
---|
167 | return NC_ENOMEM; |
---|
168 | h5 = nc->nc4_info; |
---|
169 | |
---|
170 | /* Hang on to the filename for nc_abort. */ |
---|
171 | if (!(h5->path = malloc((strlen(path) + 1) * sizeof(char)))) |
---|
172 | return NC_ENOMEM; |
---|
173 | strcpy(h5->path, path); |
---|
174 | |
---|
175 | /* Hang on to cmode, and note that we're in define mode. */ |
---|
176 | h5->cmode = mode | NC_INDEF; |
---|
177 | |
---|
178 | /* The next_typeid needs to be set beyond the end of our atomic |
---|
179 | * types. */ |
---|
180 | h5->next_typeid = NC_FIRSTUSERTYPEID; |
---|
181 | |
---|
182 | /* There's always at least one open group - the root |
---|
183 | * group. Allocate space for one group's worth of information. Set |
---|
184 | * its hdf id, name, and a pointer to it's file structure. */ |
---|
185 | return nc4_grp_list_add(&(h5->root_grp), h5->next_nc_grpid++, |
---|
186 | NULL, nc, NC_GROUP_NAME, &grp); |
---|
187 | } |
---|
188 | /* /\* Given an ncid, find the relevant group and return a pointer to */ |
---|
189 | /* * it. *\/ */ |
---|
190 | /* NC_GRP_INFO_T * */ |
---|
191 | /* find_nc_grp(int ncid) */ |
---|
192 | /* { */ |
---|
193 | /* NC_FILE_INFO_T *f; */ |
---|
194 | |
---|
195 | /* for (f = nc_file; f; f = f->next) */ |
---|
196 | /* { */ |
---|
197 | /* if (f->ext_ncid == (ncid & FILE_ID_MASK)) */ |
---|
198 | /* { */ |
---|
199 | /* assert(f->nc4_info && f->nc4_info->root_grp); */ |
---|
200 | /* return nc4_rec_find_grp(f->nc4_info->root_grp, (ncid & GRP_ID_MASK)); */ |
---|
201 | /* } */ |
---|
202 | /* } */ |
---|
203 | |
---|
204 | /* return NULL; */ |
---|
205 | /* } */ |
---|
206 | |
---|
207 | /* Given an ncid, find the relevant group and return a pointer to it, |
---|
208 | * return an error of this is not a netcdf-4 file (or if strict nc3 is |
---|
209 | * turned on for this file.) */ |
---|
210 | |
---|
211 | |
---|
212 | int |
---|
213 | nc4_find_nc4_grp(int ncid, NC_GRP_INFO_T **grp) |
---|
214 | { |
---|
215 | NC_FILE_INFO_T *f = nc4_find_nc_file(ncid); |
---|
216 | if(f == NULL) return NC_EBADID; |
---|
217 | |
---|
218 | /* No netcdf-3 files allowed! */ |
---|
219 | if (!f->nc4_info) return NC_ENOTNC4; |
---|
220 | assert(f->nc4_info->root_grp); |
---|
221 | |
---|
222 | /* This function demands netcdf-4 files without strict nc3 |
---|
223 | * rules.*/ |
---|
224 | if (f->nc4_info->cmode & NC_CLASSIC_MODEL) return NC_ESTRICTNC3; |
---|
225 | |
---|
226 | /* If we can't find it, the grp id part of ncid is bad. */ |
---|
227 | if (!(*grp = nc4_rec_find_grp(f->nc4_info->root_grp, (ncid & GRP_ID_MASK)))) |
---|
228 | return NC_EBADID; |
---|
229 | return NC_NOERR; |
---|
230 | } |
---|
231 | |
---|
232 | /* Given an ncid, find the relevant group and return a pointer to it, |
---|
233 | * also set a pointer to the nc4_info struct of the related file. For |
---|
234 | * netcdf-3 files, *h5 will be set to NULL. */ |
---|
235 | int |
---|
236 | nc4_find_grp_h5(int ncid, NC_GRP_INFO_T **grp, NC_HDF5_FILE_INFO_T **h5) |
---|
237 | { |
---|
238 | NC_FILE_INFO_T *f = nc4_find_nc_file(ncid); |
---|
239 | if(f == NULL) return NC_EBADID; |
---|
240 | if (f->nc4_info) { |
---|
241 | assert(f->nc4_info->root_grp); |
---|
242 | /* If we can't find it, the grp id part of ncid is bad. */ |
---|
243 | if (!(*grp = nc4_rec_find_grp(f->nc4_info->root_grp, (ncid & GRP_ID_MASK)))) |
---|
244 | return NC_EBADID; |
---|
245 | *h5 = (*grp)->file->nc4_info; |
---|
246 | assert(*h5); |
---|
247 | } else { |
---|
248 | *h5 = NULL; |
---|
249 | *grp = NULL; |
---|
250 | } |
---|
251 | return NC_NOERR; |
---|
252 | } |
---|
253 | |
---|
254 | int |
---|
255 | nc4_find_nc_grp_h5(int ncid, NC_FILE_INFO_T **nc, NC_GRP_INFO_T **grp, |
---|
256 | NC_HDF5_FILE_INFO_T **h5) |
---|
257 | { |
---|
258 | NC_FILE_INFO_T *f = nc4_find_nc_file(ncid); |
---|
259 | if(f == NULL) return NC_EBADID; |
---|
260 | *nc = f; |
---|
261 | if (f->nc4_info) { |
---|
262 | assert(f->nc4_info->root_grp); |
---|
263 | /* If we can't find it, the grp id part of ncid is bad. */ |
---|
264 | if (!(*grp = nc4_rec_find_grp(f->nc4_info->root_grp, (ncid & GRP_ID_MASK)))) |
---|
265 | return NC_EBADID; |
---|
266 | |
---|
267 | *h5 = (*grp)->file->nc4_info; |
---|
268 | assert(*h5); |
---|
269 | } else { |
---|
270 | *h5 = NULL; |
---|
271 | *grp = NULL; |
---|
272 | } |
---|
273 | return NC_NOERR; |
---|
274 | } |
---|
275 | |
---|
276 | /* Recursively hunt for a group id. */ |
---|
277 | NC_GRP_INFO_T * |
---|
278 | nc4_rec_find_grp(NC_GRP_INFO_T *start_grp, int target_nc_grpid) |
---|
279 | { |
---|
280 | NC_GRP_INFO_T *g, *res; |
---|
281 | |
---|
282 | assert(start_grp); |
---|
283 | |
---|
284 | /* Is this the group we are searching for? */ |
---|
285 | if (start_grp->nc_grpid == target_nc_grpid) |
---|
286 | return start_grp; |
---|
287 | |
---|
288 | /* Shake down the kids. */ |
---|
289 | if (start_grp->children) |
---|
290 | for (g = start_grp->children; g; g = g->next) |
---|
291 | if ((res = nc4_rec_find_grp(g, target_nc_grpid))) |
---|
292 | return res; |
---|
293 | |
---|
294 | /* Can't find if. Fate, why do you mock me? */ |
---|
295 | return NULL; |
---|
296 | } |
---|
297 | |
---|
298 | /* Given an ncid and varid, get pointers to the group and var |
---|
299 | * metadata. */ |
---|
300 | int |
---|
301 | nc4_find_g_var_nc(NC_FILE_INFO_T *nc, int ncid, int varid, |
---|
302 | NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var) |
---|
303 | { |
---|
304 | /* Find the group info. */ |
---|
305 | assert(grp && var && nc && nc->nc4_info && nc->nc4_info->root_grp); |
---|
306 | *grp = nc4_rec_find_grp(nc->nc4_info->root_grp, (ncid & GRP_ID_MASK)); |
---|
307 | |
---|
308 | /* Find the var info. */ |
---|
309 | for ((*var) = (*grp)->var; (*var); (*var) = (*var)->next) |
---|
310 | if ((*var)->varid == varid) |
---|
311 | break; |
---|
312 | if (!(*var)) |
---|
313 | return NC_ENOTVAR; |
---|
314 | |
---|
315 | return NC_NOERR; |
---|
316 | } |
---|
317 | |
---|
318 | /* Find a dim in a grp (or parents). */ |
---|
319 | int |
---|
320 | nc4_find_dim(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T **dim, |
---|
321 | NC_GRP_INFO_T **dim_grp) |
---|
322 | { |
---|
323 | NC_GRP_INFO_T *g, *dg = NULL; |
---|
324 | int finished = 0; |
---|
325 | |
---|
326 | assert(grp && dim); |
---|
327 | |
---|
328 | /* Find the dim info. */ |
---|
329 | for (g = grp; g && !finished; g = g->parent) |
---|
330 | for ((*dim) = g->dim; (*dim); (*dim) = (*dim)->next) |
---|
331 | if ((*dim)->dimid == dimid) |
---|
332 | { |
---|
333 | dg = g; |
---|
334 | finished++; |
---|
335 | break; |
---|
336 | } |
---|
337 | |
---|
338 | /* If we didn't find it, return an error. */ |
---|
339 | if (!(*dim)) |
---|
340 | return NC_EBADDIM; |
---|
341 | |
---|
342 | /* Give the caller the group the dimension is in. */ |
---|
343 | if (dim_grp) |
---|
344 | *dim_grp = dg; |
---|
345 | |
---|
346 | return NC_NOERR; |
---|
347 | } |
---|
348 | |
---|
349 | /* Recursively hunt for a HDF type id. */ |
---|
350 | NC_TYPE_INFO_T * |
---|
351 | nc4_rec_find_hdf_type(NC_GRP_INFO_T *start_grp, hid_t target_hdf_typeid) |
---|
352 | { |
---|
353 | NC_GRP_INFO_T *g; |
---|
354 | NC_TYPE_INFO_T *type, *res; |
---|
355 | htri_t equal; |
---|
356 | |
---|
357 | assert(start_grp); |
---|
358 | |
---|
359 | /* Does this group have the type we are searching for? */ |
---|
360 | for (type = start_grp->type; type; type = type->next) |
---|
361 | { |
---|
362 | if ((equal = H5Tequal(type->native_typeid ? type->native_typeid : type->hdf_typeid, target_hdf_typeid)) < 0) |
---|
363 | return NULL; |
---|
364 | if (equal) |
---|
365 | return type; |
---|
366 | } |
---|
367 | |
---|
368 | /* Shake down the kids. */ |
---|
369 | if (start_grp->children) |
---|
370 | for (g = start_grp->children; g; g = g->next) |
---|
371 | if ((res = nc4_rec_find_hdf_type(g, target_hdf_typeid))) |
---|
372 | return res; |
---|
373 | |
---|
374 | /* Can't find if. Fate, why do you mock me? */ |
---|
375 | return NULL; |
---|
376 | } |
---|
377 | |
---|
378 | /* Recursively hunt for a netCDF type id. */ |
---|
379 | NC_TYPE_INFO_T * |
---|
380 | nc4_rec_find_nc_type(NC_GRP_INFO_T *start_grp, nc_type target_nc_typeid) |
---|
381 | { |
---|
382 | NC_GRP_INFO_T *g; |
---|
383 | NC_TYPE_INFO_T *type, *res; |
---|
384 | |
---|
385 | assert(start_grp); |
---|
386 | |
---|
387 | /* Does this group have the type we are searching for? */ |
---|
388 | for (type = start_grp->type; type; type = type->next) |
---|
389 | if (type->nc_typeid == target_nc_typeid) |
---|
390 | return type; |
---|
391 | |
---|
392 | /* Shake down the kids. */ |
---|
393 | if (start_grp->children) |
---|
394 | for (g = start_grp->children; g; g = g->next) |
---|
395 | if ((res = nc4_rec_find_nc_type(g, target_nc_typeid))) |
---|
396 | return res; |
---|
397 | |
---|
398 | /* Can't find if. Fate, why do you mock me? */ |
---|
399 | return NULL; |
---|
400 | } |
---|
401 | |
---|
402 | /* Recursively hunt for a netCDF type by name. */ |
---|
403 | NC_TYPE_INFO_T * |
---|
404 | nc4_rec_find_named_type(NC_GRP_INFO_T *start_grp, char *name) |
---|
405 | { |
---|
406 | NC_GRP_INFO_T *g; |
---|
407 | NC_TYPE_INFO_T *type, *res; |
---|
408 | |
---|
409 | assert(start_grp); |
---|
410 | |
---|
411 | /* Does this group have the type we are searching for? */ |
---|
412 | for (type = start_grp->type; type; type = type->next) |
---|
413 | if (!strcmp(type->name, name)) |
---|
414 | return type; |
---|
415 | |
---|
416 | /* Search subgroups. */ |
---|
417 | if (start_grp->children) |
---|
418 | for (g = start_grp->children; g; g = g->next) |
---|
419 | if ((res = nc4_rec_find_named_type(g, name))) |
---|
420 | return res; |
---|
421 | |
---|
422 | /* Can't find if. Oh, woe is me! */ |
---|
423 | return NULL; |
---|
424 | } |
---|
425 | |
---|
426 | /* Use a netCDF typeid to find a type in a type_list. */ |
---|
427 | int |
---|
428 | nc4_find_type(NC_HDF5_FILE_INFO_T *h5, nc_type typeid, NC_TYPE_INFO_T **type) |
---|
429 | { |
---|
430 | if (typeid < 0 || !type) |
---|
431 | return NC_EINVAL; |
---|
432 | *type = NULL; |
---|
433 | |
---|
434 | /* Atomic types don't have associated NC_TYPE_INFO_T struct, just |
---|
435 | * return NOERR. */ |
---|
436 | if (typeid <= NC_STRING) |
---|
437 | return NC_NOERR; |
---|
438 | |
---|
439 | /* Find the type. */ |
---|
440 | if(!(*type = nc4_rec_find_nc_type(h5->root_grp, typeid))) |
---|
441 | return NC_EBADTYPID; |
---|
442 | |
---|
443 | return NC_NOERR; |
---|
444 | } |
---|
445 | |
---|
446 | /* Find the actual length of a dim by checking the length of that dim |
---|
447 | * in all variables that use it, in grp or children. *len must be |
---|
448 | * initialized to zero before this function is called. */ |
---|
449 | int |
---|
450 | nc4_find_dim_len(NC_GRP_INFO_T *grp, int dimid, size_t **len) |
---|
451 | { |
---|
452 | NC_GRP_INFO_T *g; |
---|
453 | NC_VAR_INFO_T *var; |
---|
454 | int d, ndims, dimids[NC_MAX_DIMS]; |
---|
455 | size_t dimlen[NC_MAX_DIMS]; |
---|
456 | int retval; |
---|
457 | |
---|
458 | assert(grp && len); |
---|
459 | LOG((3, "nc4_find_dim_len: grp->name %s dimid %d", grp->name, dimid)); |
---|
460 | |
---|
461 | /* If there are any groups, call this function recursively on |
---|
462 | * them. */ |
---|
463 | for (g = grp->children; g; g = g->next) |
---|
464 | if ((retval = nc4_find_dim_len(g, dimid, len))) |
---|
465 | return retval; |
---|
466 | |
---|
467 | /* For all variables in this group, find the ones that use this |
---|
468 | * dimension, and remember the max length. */ |
---|
469 | for (var = grp->var; var; var = var->next) |
---|
470 | { |
---|
471 | /* Find dimensions of this var. */ |
---|
472 | if ((retval = find_var_shape_grp(grp, var->varid, &ndims, |
---|
473 | dimids, dimlen))) |
---|
474 | return retval; |
---|
475 | |
---|
476 | /* Check for any dimension that matches dimid. If found, check |
---|
477 | * if its length is longer than *lenp. */ |
---|
478 | for (d = 0; d < ndims; d++) |
---|
479 | { |
---|
480 | if (dimids[d] == dimid) |
---|
481 | { |
---|
482 | /* Remember the max length in *lenp. */ |
---|
483 | **len = dimlen[d] > **len ? dimlen[d] : **len; |
---|
484 | break; |
---|
485 | } |
---|
486 | } |
---|
487 | } |
---|
488 | |
---|
489 | return NC_NOERR; |
---|
490 | } |
---|
491 | |
---|
492 | /* Given a group, find an att. */ |
---|
493 | int |
---|
494 | nc4_find_grp_att(NC_GRP_INFO_T *grp, int varid, const char *name, int attnum, |
---|
495 | NC_ATT_INFO_T **att) |
---|
496 | { |
---|
497 | NC_VAR_INFO_T *var; |
---|
498 | NC_ATT_INFO_T *attlist = NULL; |
---|
499 | |
---|
500 | assert(grp && grp->name); |
---|
501 | LOG((4, "nc4_find_grp_att: grp->name %s varid %d name %s attnum %d", |
---|
502 | grp->name, varid, name, attnum)); |
---|
503 | |
---|
504 | /* Get either the global or a variable attribute list. */ |
---|
505 | if (varid == NC_GLOBAL) |
---|
506 | attlist = grp->att; |
---|
507 | else |
---|
508 | { |
---|
509 | for(var = grp->var; var; var = var->next) |
---|
510 | { |
---|
511 | if (var->varid == varid) |
---|
512 | { |
---|
513 | attlist = var->att; |
---|
514 | break; |
---|
515 | } |
---|
516 | } |
---|
517 | if (!var) |
---|
518 | return NC_ENOTVAR; |
---|
519 | } |
---|
520 | |
---|
521 | /* Now find the attribute by name or number. If a name is provided, |
---|
522 | * ignore the attnum. */ |
---|
523 | for (*att = attlist; *att; *att = (*att)->next) |
---|
524 | if ((name && !strcmp((*att)->name, name)) || |
---|
525 | (!name && (*att)->attnum == attnum)) |
---|
526 | return NC_NOERR; |
---|
527 | |
---|
528 | /* If we get here, we couldn't find the attribute. */ |
---|
529 | return NC_ENOTATT; |
---|
530 | } |
---|
531 | |
---|
532 | /* Given an ncid, varid, and name or attnum, find and return pointer |
---|
533 | to NC_ATT_INFO_T metadata. */ |
---|
534 | int |
---|
535 | nc4_find_nc_att(int ncid, int varid, const char *name, int attnum, |
---|
536 | NC_ATT_INFO_T **att) |
---|
537 | { |
---|
538 | NC_GRP_INFO_T *grp; |
---|
539 | NC_HDF5_FILE_INFO_T *h5; |
---|
540 | NC_VAR_INFO_T *var; |
---|
541 | NC_ATT_INFO_T *attlist = NULL; |
---|
542 | int retval; |
---|
543 | |
---|
544 | LOG((4, "nc4_find_nc_att: ncid 0x%x varid %d name %s attnum %d", |
---|
545 | ncid, varid, name, attnum)); |
---|
546 | |
---|
547 | /* Find info for this file and group, and set pointer to each. */ |
---|
548 | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
---|
549 | return retval; |
---|
550 | assert(grp && h5); |
---|
551 | |
---|
552 | /* Get either the global or a variable attribute list. */ |
---|
553 | if (varid == NC_GLOBAL) |
---|
554 | attlist = grp->att; |
---|
555 | else |
---|
556 | { |
---|
557 | for(var = grp->var; var; var = var->next) |
---|
558 | { |
---|
559 | if (var->varid == varid) |
---|
560 | { |
---|
561 | attlist = var->att; |
---|
562 | break; |
---|
563 | } |
---|
564 | } |
---|
565 | if (!var) |
---|
566 | return NC_ENOTVAR; |
---|
567 | } |
---|
568 | |
---|
569 | /* Now find the attribute by name or number. If a name is provided, ignore the attnum. */ |
---|
570 | for (*att = attlist; *att; *att = (*att)->next) |
---|
571 | if ((name && !strcmp((*att)->name, name)) || |
---|
572 | (!name && (*att)->attnum == attnum)) |
---|
573 | return NC_NOERR; |
---|
574 | |
---|
575 | /* If we get here, we couldn't find the attribute. */ |
---|
576 | return NC_ENOTATT; |
---|
577 | } |
---|
578 | |
---|
579 | void |
---|
580 | nc4_file_list_free(void) |
---|
581 | { |
---|
582 | free_NCList(); |
---|
583 | } |
---|
584 | |
---|
585 | |
---|
586 | int |
---|
587 | NC4_new_nc(NC** ncpp) |
---|
588 | { |
---|
589 | NC_FILE_INFO_T** ncp; |
---|
590 | /* Allocate memory for this info. */ |
---|
591 | if (!(ncp = calloc(1, sizeof(NC_FILE_INFO_T)))) |
---|
592 | return NC_ENOMEM; |
---|
593 | if(ncpp) *ncpp = (NC*)ncp; |
---|
594 | return NC_NOERR; |
---|
595 | } |
---|
596 | |
---|
597 | int |
---|
598 | nc4_file_list_add(NC_FILE_INFO_T** ncp, NC_Dispatch* dispatch) |
---|
599 | { |
---|
600 | NC_FILE_INFO_T *nc; |
---|
601 | int status = NC_NOERR; |
---|
602 | |
---|
603 | /* Allocate memory for this info; use the dispatcher to do this */ |
---|
604 | status = dispatch->new_nc((NC**)&nc); |
---|
605 | if(status) return status; |
---|
606 | |
---|
607 | /* Add this file to the list. */ |
---|
608 | if ((status = add_to_NCList((NC *)nc))) |
---|
609 | { |
---|
610 | if(nc && nc->ext_ncid > 0) |
---|
611 | { |
---|
612 | del_from_NCList((NC *)nc); |
---|
613 | free(nc); |
---|
614 | } |
---|
615 | return status; |
---|
616 | } |
---|
617 | |
---|
618 | /* Return a pointer to the new struct. */ |
---|
619 | if(ncp) |
---|
620 | *ncp = nc; |
---|
621 | |
---|
622 | return NC_NOERR; |
---|
623 | } |
---|
624 | |
---|
625 | /* Remove a NC_FILE_INFO_T from the linked list. This will nc_free the |
---|
626 | memory too. */ |
---|
627 | void |
---|
628 | nc4_file_list_del(NC_FILE_INFO_T *nc) |
---|
629 | { |
---|
630 | /* Remove file from master list. */ |
---|
631 | del_from_NCList((NC *)nc); |
---|
632 | free(nc); |
---|
633 | } |
---|
634 | |
---|
635 | |
---|
636 | /* Given an id, walk the list and find the appropriate |
---|
637 | NC_FILE_INFO_T. */ |
---|
638 | NC_FILE_INFO_T* |
---|
639 | nc4_find_nc_file(int ext_ncid) |
---|
640 | { |
---|
641 | return (NC_FILE_INFO_T*)find_in_NCList(ext_ncid); |
---|
642 | } |
---|
643 | |
---|
644 | |
---|
645 | /* Add to the end of a var list. Return a pointer to the newly |
---|
646 | * added var. */ |
---|
647 | int |
---|
648 | nc4_var_list_add(NC_VAR_INFO_T **list, NC_VAR_INFO_T **var) |
---|
649 | { |
---|
650 | NC_VAR_INFO_T *v; |
---|
651 | |
---|
652 | /* Allocate storage for new variable. */ |
---|
653 | if (!(*var = calloc(1, sizeof(NC_VAR_INFO_T)))) |
---|
654 | return NC_ENOMEM; |
---|
655 | |
---|
656 | /* Go to the end of the list and set the last one to point at our |
---|
657 | * new var, or, if the list is empty, our new var becomes the |
---|
658 | * list. */ |
---|
659 | if(*list) |
---|
660 | { |
---|
661 | for (v = *list; v; v = v->next) |
---|
662 | if (!v->next) |
---|
663 | break; |
---|
664 | v->next = *var; |
---|
665 | (*var)->prev = v; |
---|
666 | } |
---|
667 | else |
---|
668 | *list = *var; |
---|
669 | |
---|
670 | /* These are the HDF5-1.8.4 defaults. */ |
---|
671 | (*var)->chunk_cache_size = nc4_chunk_cache_size; |
---|
672 | (*var)->chunk_cache_nelems = nc4_chunk_cache_nelems; |
---|
673 | (*var)->chunk_cache_preemption = nc4_chunk_cache_preemption; |
---|
674 | |
---|
675 | return NC_NOERR; |
---|
676 | } |
---|
677 | |
---|
678 | /* Add to the beginning of a dim list. */ |
---|
679 | int |
---|
680 | nc4_dim_list_add(NC_DIM_INFO_T **list) |
---|
681 | { |
---|
682 | NC_DIM_INFO_T *dim; |
---|
683 | if (!(dim = calloc(1, sizeof(NC_DIM_INFO_T)))) |
---|
684 | return NC_ENOMEM; |
---|
685 | if(*list) |
---|
686 | (*list)->prev = dim; |
---|
687 | dim->next = *list; |
---|
688 | *list = dim; |
---|
689 | return NC_NOERR; |
---|
690 | } |
---|
691 | |
---|
692 | /* Add to the beginning of a dim list. */ |
---|
693 | int |
---|
694 | nc4_dim_list_add2(NC_DIM_INFO_T **list, NC_DIM_INFO_T **new_dim) |
---|
695 | { |
---|
696 | NC_DIM_INFO_T *dim; |
---|
697 | if (!(dim = calloc(1, sizeof(NC_DIM_INFO_T)))) |
---|
698 | return NC_ENOMEM; |
---|
699 | if(*list) |
---|
700 | (*list)->prev = dim; |
---|
701 | dim->next = *list; |
---|
702 | *list = dim; |
---|
703 | |
---|
704 | /* Return pointer to new dimension. */ |
---|
705 | if (new_dim) |
---|
706 | *new_dim = dim; |
---|
707 | return NC_NOERR; |
---|
708 | } |
---|
709 | |
---|
710 | /* Add to the end of an att list. */ |
---|
711 | int |
---|
712 | nc4_att_list_add(NC_ATT_INFO_T **list) |
---|
713 | { |
---|
714 | NC_ATT_INFO_T *att, *a1; |
---|
715 | if (!(att = calloc(1, sizeof(NC_ATT_INFO_T)))) |
---|
716 | return NC_ENOMEM; |
---|
717 | if (*list) |
---|
718 | { |
---|
719 | for (a1 = *list; a1; a1 = a1->next) |
---|
720 | if (!a1->next) |
---|
721 | break; |
---|
722 | a1->next = att; |
---|
723 | att->prev = a1; |
---|
724 | } |
---|
725 | else |
---|
726 | { |
---|
727 | *list = att; |
---|
728 | } |
---|
729 | |
---|
730 | return NC_NOERR; |
---|
731 | } |
---|
732 | |
---|
733 | /* Add to the end of a group list. Can't use 0 as a new_nc_grpid - |
---|
734 | * it's reserverd for the root group. */ |
---|
735 | int |
---|
736 | nc4_grp_list_add(NC_GRP_INFO_T **list, int new_nc_grpid, |
---|
737 | NC_GRP_INFO_T *parent_grp, NC_FILE_INFO_T *nc, |
---|
738 | char *name, NC_GRP_INFO_T **grp) |
---|
739 | { |
---|
740 | NC_GRP_INFO_T *g; |
---|
741 | |
---|
742 | LOG((3, "grp_list_add: new_nc_grpid %d name %s ", |
---|
743 | new_nc_grpid, name)); |
---|
744 | |
---|
745 | /* Get the memory to store this groups info. */ |
---|
746 | if (!(*grp = calloc(1, sizeof(NC_GRP_INFO_T)))) |
---|
747 | return NC_ENOMEM; |
---|
748 | |
---|
749 | /* If the list is not NULL, add this group to it. Otherwise, this |
---|
750 | * group structure becomes the list. */ |
---|
751 | if (*list) |
---|
752 | { |
---|
753 | /* Move to end of the list. */ |
---|
754 | for (g = *list; g; g = g->next) |
---|
755 | if (!g->next) |
---|
756 | break; |
---|
757 | g->next = *grp; /* Add grp to end of list. */ |
---|
758 | (*grp)->prev = g; |
---|
759 | } |
---|
760 | else |
---|
761 | { |
---|
762 | *list = *grp; |
---|
763 | } |
---|
764 | |
---|
765 | /* Fill in this group's information. */ |
---|
766 | (*grp)->nc_grpid = new_nc_grpid; |
---|
767 | (*grp)->parent = parent_grp; |
---|
768 | if (!((*grp)->name = malloc((strlen(name) + 1) * sizeof(char)))) |
---|
769 | return NC_ENOMEM; |
---|
770 | strcpy((*grp)->name, name); |
---|
771 | (*grp)->file = nc; |
---|
772 | |
---|
773 | return NC_NOERR; |
---|
774 | } |
---|
775 | |
---|
776 | /* Names for groups, variables, and types must not be the same. This |
---|
777 | * function checks that a proposed name is not already in |
---|
778 | * use. Normalzation of UTF8 strings should happen before this |
---|
779 | * function is called. */ |
---|
780 | int |
---|
781 | nc4_check_dup_name(NC_GRP_INFO_T *grp, char *name) |
---|
782 | { |
---|
783 | NC_TYPE_INFO_T *type; |
---|
784 | NC_GRP_INFO_T *g; |
---|
785 | NC_VAR_INFO_T *var; |
---|
786 | |
---|
787 | /* Any types of this name? */ |
---|
788 | for (type = grp->type; type; type = type->next) |
---|
789 | if (!strcmp(type->name, name)) |
---|
790 | return NC_ENAMEINUSE; |
---|
791 | |
---|
792 | /* Any child groups of this name? */ |
---|
793 | for (g = grp->children; g; g = g->next) |
---|
794 | if (!strcmp(g->name, name)) |
---|
795 | return NC_ENAMEINUSE; |
---|
796 | |
---|
797 | /* Any variables of this name? */ |
---|
798 | for (var = grp->var; var; var = var->next) |
---|
799 | if (!strcmp(var->name, name)) |
---|
800 | return NC_ENAMEINUSE; |
---|
801 | |
---|
802 | return NC_NOERR; |
---|
803 | } |
---|
804 | |
---|
805 | /* Add to the end of a type list. */ |
---|
806 | int |
---|
807 | nc4_type_list_add(NC_TYPE_INFO_T **list, NC_TYPE_INFO_T **new_type) |
---|
808 | { |
---|
809 | NC_TYPE_INFO_T *type, *t; |
---|
810 | |
---|
811 | if (!(type = calloc(1, sizeof(NC_TYPE_INFO_T)))) |
---|
812 | return NC_ENOMEM; |
---|
813 | |
---|
814 | if (*list) |
---|
815 | { |
---|
816 | for (t = *list; t; t = t->next) |
---|
817 | if (!t->next) |
---|
818 | break; |
---|
819 | t->next = type; |
---|
820 | type->prev = t; |
---|
821 | } |
---|
822 | else |
---|
823 | { |
---|
824 | *list = type; |
---|
825 | } |
---|
826 | |
---|
827 | if (new_type) |
---|
828 | *new_type = type; |
---|
829 | |
---|
830 | return NC_NOERR; |
---|
831 | } |
---|
832 | |
---|
833 | /* Add to the end of a compound field list. */ |
---|
834 | int |
---|
835 | nc4_field_list_add(NC_FIELD_INFO_T **list, int fieldid, const char *name, |
---|
836 | size_t offset, hid_t field_hdf_typeid, hid_t native_typeid, |
---|
837 | nc_type xtype, int ndims, const int *dim_sizesp) |
---|
838 | { |
---|
839 | NC_FIELD_INFO_T *field, *f; |
---|
840 | int i; |
---|
841 | |
---|
842 | /* Name has already been checked and UTF8 normalized. */ |
---|
843 | if (!name) |
---|
844 | return NC_EINVAL; |
---|
845 | |
---|
846 | /* Allocate storage for this field information. */ |
---|
847 | if (!(field = calloc(1, sizeof(NC_FIELD_INFO_T)))) |
---|
848 | return NC_ENOMEM; |
---|
849 | |
---|
850 | /* Add this field to list. */ |
---|
851 | if (*list) |
---|
852 | { |
---|
853 | for (f = *list; f; f = f->next) |
---|
854 | if (!f->next) |
---|
855 | break; |
---|
856 | f->next = field; |
---|
857 | field->prev = f; |
---|
858 | } |
---|
859 | else |
---|
860 | { |
---|
861 | *list = field; |
---|
862 | } |
---|
863 | |
---|
864 | /* Store the information about this field. */ |
---|
865 | field->fieldid = fieldid; |
---|
866 | if (!(field->name = malloc((strlen(name) + 1) * sizeof(char)))) |
---|
867 | return NC_ENOMEM; |
---|
868 | strcpy(field->name, name); |
---|
869 | field->hdf_typeid = field_hdf_typeid; |
---|
870 | field->native_typeid = native_typeid; |
---|
871 | field->nctype = xtype; |
---|
872 | field->offset = offset; |
---|
873 | field->ndims = ndims; |
---|
874 | if (ndims) |
---|
875 | { |
---|
876 | if (!(field->dim_size = malloc(ndims * sizeof(int)))) |
---|
877 | return NC_ENOMEM; |
---|
878 | for (i = 0; i < ndims; i++) |
---|
879 | field->dim_size[i] = dim_sizesp[i]; |
---|
880 | } |
---|
881 | |
---|
882 | return NC_NOERR; |
---|
883 | } |
---|
884 | |
---|
885 | /* Add a member to an enum type. */ |
---|
886 | int |
---|
887 | nc4_enum_member_add(NC_ENUM_MEMBER_INFO_T **list, size_t size, |
---|
888 | const char *name, const void *value) |
---|
889 | { |
---|
890 | NC_ENUM_MEMBER_INFO_T *member, *m; |
---|
891 | |
---|
892 | /* Name has already been checked. */ |
---|
893 | assert(name && size > 0 && value); |
---|
894 | LOG((4, "nc4_enum_member_add: size %d name %s", size, name)); |
---|
895 | |
---|
896 | /* Allocate storage for this field information. */ |
---|
897 | if (!(member = calloc(1, sizeof(NC_ENUM_MEMBER_INFO_T))) || |
---|
898 | !(member->value = calloc(1, size))) |
---|
899 | return NC_ENOMEM; |
---|
900 | |
---|
901 | /* Add this field to list. */ |
---|
902 | if (*list) |
---|
903 | { |
---|
904 | for (m = *list; m; m = m->next) |
---|
905 | if (!m->next) |
---|
906 | break; |
---|
907 | m->next = member; |
---|
908 | member->prev = m; |
---|
909 | } |
---|
910 | else |
---|
911 | { |
---|
912 | *list = member; |
---|
913 | } |
---|
914 | |
---|
915 | /* Store the information about this member. */ |
---|
916 | if (!(member->name = malloc((strlen(name) + 1) * sizeof(char)))) |
---|
917 | return NC_ENOMEM; |
---|
918 | strcpy(member->name, name); |
---|
919 | memcpy(member->value, value, size); |
---|
920 | |
---|
921 | return NC_NOERR; |
---|
922 | } |
---|
923 | |
---|
924 | /* Delete a var from a var list, and free the memory. */ |
---|
925 | static int |
---|
926 | var_list_del(NC_VAR_INFO_T **list, NC_VAR_INFO_T *var) |
---|
927 | { |
---|
928 | NC_ATT_INFO_T *a, *att; |
---|
929 | int ret; |
---|
930 | |
---|
931 | /* First delete all the attributes attached to this var. */ |
---|
932 | att = (*list)->att; |
---|
933 | while (att) |
---|
934 | { |
---|
935 | a = att->next; |
---|
936 | if ((ret = nc4_att_list_del(&var->att, att))) |
---|
937 | return ret; |
---|
938 | att = a; |
---|
939 | } |
---|
940 | |
---|
941 | /* Free some things that may be allocated. */ |
---|
942 | if (var->chunksizes) |
---|
943 | free(var->chunksizes); |
---|
944 | if (var->hdf5_name) |
---|
945 | free(var->hdf5_name); |
---|
946 | if (var->name) |
---|
947 | free(var->name); |
---|
948 | if (var->dimids) |
---|
949 | free(var->dimids); |
---|
950 | if (var->dim) |
---|
951 | free(var->dim); |
---|
952 | |
---|
953 | /* Remove the var from the linked list. */ |
---|
954 | if(*list == var) |
---|
955 | *list = var->next; |
---|
956 | else |
---|
957 | var->prev->next = var->next; |
---|
958 | |
---|
959 | if(var->next) |
---|
960 | var->next->prev = var->prev; |
---|
961 | |
---|
962 | /* Delete any fill value allocation. This must be done before the |
---|
963 | * type_info is freed. */ |
---|
964 | if (var->fill_value) |
---|
965 | { |
---|
966 | if (var->hdf_datasetid) |
---|
967 | { |
---|
968 | if (var->type_info->class == NC_VLEN) |
---|
969 | nc_free_vlen((nc_vlen_t *)var->fill_value); |
---|
970 | else if (var->type_info->nc_typeid == NC_STRING) |
---|
971 | free(*(char **)var->fill_value); |
---|
972 | } |
---|
973 | free(var->fill_value); |
---|
974 | } |
---|
975 | |
---|
976 | /* For atomic types we have allocated space for type information. */ |
---|
977 | /* if (var->hdf_datasetid && var->xtype <= NC_STRING)*/ |
---|
978 | if (var->xtype <= NC_STRING) |
---|
979 | { |
---|
980 | if (var->type_info->native_typeid) |
---|
981 | if ((H5Tclose(var->type_info->native_typeid)) < 0) |
---|
982 | return NC_EHDFERR; |
---|
983 | |
---|
984 | /* Only need to close the hdf_typeid when it was obtained with |
---|
985 | * H5Dget_type (which happens when reading a file, but not when |
---|
986 | * creating a variable). */ |
---|
987 | if (var->type_info->close_hdf_typeid || var->xtype == NC_STRING) |
---|
988 | if ((H5Tclose(var->type_info->hdf_typeid)) < 0) |
---|
989 | return NC_EHDFERR; |
---|
990 | |
---|
991 | /* Free the name. */ |
---|
992 | if (var->type_info->name) |
---|
993 | free(var->type_info->name); |
---|
994 | |
---|
995 | free(var->type_info); |
---|
996 | } |
---|
997 | |
---|
998 | /* Delete any HDF5 dimscale objid information. */ |
---|
999 | if (var->dimscale_hdf5_objids) |
---|
1000 | free(var->dimscale_hdf5_objids); |
---|
1001 | |
---|
1002 | /* Delete information about the attachment status of dimscales. */ |
---|
1003 | if (var->dimscale_attached) |
---|
1004 | free(var->dimscale_attached); |
---|
1005 | |
---|
1006 | /* Delete the var. */ |
---|
1007 | free(var); |
---|
1008 | |
---|
1009 | return NC_NOERR; |
---|
1010 | } |
---|
1011 | |
---|
1012 | /* Delete a field from a field list, and nc_free the memory. */ |
---|
1013 | static void |
---|
1014 | field_list_del(NC_FIELD_INFO_T **list, NC_FIELD_INFO_T *field) |
---|
1015 | { |
---|
1016 | |
---|
1017 | /* Take this field out of the list. */ |
---|
1018 | if(*list == field) |
---|
1019 | *list = field->next; |
---|
1020 | else |
---|
1021 | field->prev->next = field->next; |
---|
1022 | |
---|
1023 | if(field->next) |
---|
1024 | field->next->prev = field->prev; |
---|
1025 | |
---|
1026 | /* Free some stuff. */ |
---|
1027 | if (field->name) |
---|
1028 | free(field->name); |
---|
1029 | if (field->dim_size) |
---|
1030 | free(field->dim_size); |
---|
1031 | |
---|
1032 | /* Nc_Free the memory. */ |
---|
1033 | free(field); |
---|
1034 | } |
---|
1035 | |
---|
1036 | /* Delete a type from a type list, and nc_free the memory. */ |
---|
1037 | int |
---|
1038 | type_list_del(NC_TYPE_INFO_T **list, NC_TYPE_INFO_T *type) |
---|
1039 | { |
---|
1040 | NC_FIELD_INFO_T *field, *f; |
---|
1041 | NC_ENUM_MEMBER_INFO_T *enum_member, *em; |
---|
1042 | |
---|
1043 | /* Close any open user-defined HDF5 typieds. */ |
---|
1044 | if (type->hdf_typeid) |
---|
1045 | { |
---|
1046 | if (H5Tclose(type->hdf_typeid) < 0) |
---|
1047 | return NC_EHDFERR; |
---|
1048 | } |
---|
1049 | if (type->native_typeid) |
---|
1050 | { |
---|
1051 | if (H5Tclose(type->native_typeid) < 0) |
---|
1052 | return NC_EHDFERR; |
---|
1053 | } |
---|
1054 | |
---|
1055 | /* Free the name. */ |
---|
1056 | if (type->name) |
---|
1057 | free(type->name); |
---|
1058 | |
---|
1059 | /* Delete all the fields in this type (there will be some if its a |
---|
1060 | * compound). */ |
---|
1061 | field = type->field; |
---|
1062 | while (field) |
---|
1063 | { |
---|
1064 | f = field->next; |
---|
1065 | field_list_del(&type->field, field); |
---|
1066 | field = f; |
---|
1067 | } |
---|
1068 | |
---|
1069 | /* Delete all the enum_members, if any. */ |
---|
1070 | enum_member = type->enum_member; |
---|
1071 | while (enum_member) |
---|
1072 | { |
---|
1073 | em = enum_member->next; |
---|
1074 | free(enum_member->value); |
---|
1075 | free(enum_member->name); |
---|
1076 | free(enum_member); |
---|
1077 | enum_member = em; |
---|
1078 | } |
---|
1079 | |
---|
1080 | /* Take this type out of the list. */ |
---|
1081 | if(*list == type) |
---|
1082 | *list = type->next; |
---|
1083 | else |
---|
1084 | type->prev->next = type->next; |
---|
1085 | |
---|
1086 | if(type->next) |
---|
1087 | type->next->prev = type->prev; |
---|
1088 | |
---|
1089 | /* Nc_Free the memory. */ |
---|
1090 | free(type); |
---|
1091 | |
---|
1092 | return NC_NOERR; |
---|
1093 | } |
---|
1094 | |
---|
1095 | /* Delete a del from a var list, and nc_free the memory. */ |
---|
1096 | int |
---|
1097 | nc4_dim_list_del(NC_DIM_INFO_T **list, NC_DIM_INFO_T *dim) |
---|
1098 | { |
---|
1099 | /* Take this dimension out of the list. */ |
---|
1100 | if(*list == dim) |
---|
1101 | *list = dim->next; |
---|
1102 | else |
---|
1103 | dim->prev->next = dim->next; |
---|
1104 | |
---|
1105 | if(dim->next) |
---|
1106 | dim->next->prev = dim->prev; |
---|
1107 | |
---|
1108 | /* Free memory allocated for names. */ |
---|
1109 | if (dim->name) |
---|
1110 | free(dim->name); |
---|
1111 | if (dim->old_name) |
---|
1112 | free(dim->old_name); |
---|
1113 | |
---|
1114 | free(dim); |
---|
1115 | return NC_NOERR; |
---|
1116 | } |
---|
1117 | |
---|
1118 | /* Remove a NC_GRP_INFO_T from the linked list. This will nc_free the |
---|
1119 | memory too. */ |
---|
1120 | static void |
---|
1121 | grp_list_del(NC_GRP_INFO_T **list, NC_GRP_INFO_T *grp) |
---|
1122 | { |
---|
1123 | if(*list == grp) |
---|
1124 | *list = grp->next; |
---|
1125 | else |
---|
1126 | grp->prev->next = grp->next; |
---|
1127 | |
---|
1128 | if(grp->next) |
---|
1129 | grp->next->prev = grp->prev; |
---|
1130 | |
---|
1131 | free(grp); |
---|
1132 | } |
---|
1133 | |
---|
1134 | /* Recursively delete the data for a group (and everything it |
---|
1135 | * contains) in our internal metadata store. */ |
---|
1136 | int |
---|
1137 | nc4_rec_grp_del(NC_GRP_INFO_T **list, NC_GRP_INFO_T *grp) |
---|
1138 | { |
---|
1139 | NC_GRP_INFO_T *g, *c; |
---|
1140 | NC_VAR_INFO_T *v, *var; |
---|
1141 | NC_ATT_INFO_T *a, *att; |
---|
1142 | NC_DIM_INFO_T *d, *dim; |
---|
1143 | NC_TYPE_INFO_T *type, *t; |
---|
1144 | int retval; |
---|
1145 | |
---|
1146 | assert(grp); |
---|
1147 | LOG((3, "nc4_rec_grp_del: grp->name %s", grp->name)); |
---|
1148 | |
---|
1149 | /* Recursively call this function for each child, if any, stopping |
---|
1150 | * if there is an error. */ |
---|
1151 | g = grp->children; |
---|
1152 | while(g) |
---|
1153 | { |
---|
1154 | c = g->next; |
---|
1155 | if ((retval = nc4_rec_grp_del(&(grp->children), g))) |
---|
1156 | return retval; |
---|
1157 | g = c; |
---|
1158 | } |
---|
1159 | |
---|
1160 | /* Delete all the list contents for vars, dims, and atts, in each |
---|
1161 | * group. */ |
---|
1162 | att = grp->att; |
---|
1163 | while (att) |
---|
1164 | { |
---|
1165 | LOG((4, "nc4_rec_grp_del: deleting att %s", att->name)); |
---|
1166 | a = att->next; |
---|
1167 | if ((retval = nc4_att_list_del(&grp->att, att))) |
---|
1168 | return retval; |
---|
1169 | att = a; |
---|
1170 | } |
---|
1171 | |
---|
1172 | /* Delete all vars. */ |
---|
1173 | var = grp->var; |
---|
1174 | while (var) |
---|
1175 | { |
---|
1176 | LOG((4, "nc4_rec_grp_del: deleting var %s", var->name)); |
---|
1177 | /* Close HDF5 dataset associated with this var, unless it's a |
---|
1178 | * scale. */ |
---|
1179 | if (var->hdf_datasetid && !var->dimscale && |
---|
1180 | H5Dclose(var->hdf_datasetid) < 0) |
---|
1181 | return NC_EHDFERR; |
---|
1182 | v = var->next; |
---|
1183 | if ((retval = var_list_del(&grp->var, var))) |
---|
1184 | return retval; |
---|
1185 | var = v; |
---|
1186 | } |
---|
1187 | |
---|
1188 | /* Delete all dims. */ |
---|
1189 | dim = grp->dim; |
---|
1190 | while (dim) |
---|
1191 | { |
---|
1192 | LOG((4, "nc4_rec_grp_del: deleting dim %s", dim->name)); |
---|
1193 | /* Close HDF5 dataset associated with this dim. */ |
---|
1194 | if (dim->hdf_dimscaleid && H5Dclose(dim->hdf_dimscaleid) < 0) |
---|
1195 | return NC_EHDFERR; |
---|
1196 | d = dim->next; |
---|
1197 | if ((retval = nc4_dim_list_del(&grp->dim, dim))) |
---|
1198 | return retval; |
---|
1199 | dim = d; |
---|
1200 | } |
---|
1201 | |
---|
1202 | /* Delete all types. */ |
---|
1203 | type = grp->type; |
---|
1204 | while (type) |
---|
1205 | { |
---|
1206 | LOG((4, "nc4_rec_grp_del: deleting type %s", type->name)); |
---|
1207 | t = type->next; |
---|
1208 | if ((retval = type_list_del(&grp->type, type))) |
---|
1209 | return retval; |
---|
1210 | type = t; |
---|
1211 | } |
---|
1212 | |
---|
1213 | /* Tell HDF5 we're closing this group. */ |
---|
1214 | LOG((4, "nc4_rec_grp_del: closing group %s", grp->name)); |
---|
1215 | if (grp->hdf_grpid && H5Gclose(grp->hdf_grpid) < 0) |
---|
1216 | return NC_EHDFERR; |
---|
1217 | |
---|
1218 | /* Free the name. */ |
---|
1219 | free(grp->name); |
---|
1220 | |
---|
1221 | /* Finally, redirect pointers around this entry in the list, and |
---|
1222 | * nc_free its memory. */ |
---|
1223 | grp_list_del(list, grp); |
---|
1224 | |
---|
1225 | return NC_NOERR; |
---|
1226 | } |
---|
1227 | |
---|
1228 | /* Remove a NC_ATT_INFO_T from the linked list. This will nc_free the |
---|
1229 | memory too. |
---|
1230 | */ |
---|
1231 | int |
---|
1232 | nc4_att_list_del(NC_ATT_INFO_T **list, NC_ATT_INFO_T *att) |
---|
1233 | { |
---|
1234 | int i; |
---|
1235 | |
---|
1236 | /* Take this att out of the list. */ |
---|
1237 | if(*list == att) |
---|
1238 | *list = att->next; |
---|
1239 | else |
---|
1240 | att->prev->next = att->next; |
---|
1241 | |
---|
1242 | if(att->next) |
---|
1243 | att->next->prev = att->prev; |
---|
1244 | |
---|
1245 | /* Free memory that was malloced to hold data for this |
---|
1246 | * attribute. */ |
---|
1247 | if (att->data) |
---|
1248 | free(att->data); |
---|
1249 | |
---|
1250 | /* Free the name. */ |
---|
1251 | if (att->name) |
---|
1252 | free(att->name); |
---|
1253 | |
---|
1254 | /* Close the HDF5 typeid. */ |
---|
1255 | if (att->native_typeid && H5Tclose(att->native_typeid) < 0) |
---|
1256 | return NC_EHDFERR; |
---|
1257 | |
---|
1258 | /* If this is a string array attribute, delete all members of the |
---|
1259 | * string array, then delete the array of pointers to strings. (The |
---|
1260 | * array was filled with pointers by HDF5 when the att was read, |
---|
1261 | * and memory for each string was allocated by HDF5. That's why I |
---|
1262 | * use free and not nc_free, because the netCDF library didn't |
---|
1263 | * allocate the memory that is being freed.) */ |
---|
1264 | if (att->stdata) |
---|
1265 | { |
---|
1266 | for (i = 0; i < att->len; i++) |
---|
1267 | free(att->stdata[i]); |
---|
1268 | free(att->stdata); |
---|
1269 | } |
---|
1270 | |
---|
1271 | /* If this att has vlen data, release it. */ |
---|
1272 | if (att->vldata) |
---|
1273 | { |
---|
1274 | for (i = 0; i < att->len; i++) |
---|
1275 | nc_free_vlen(&att->vldata[i]); |
---|
1276 | free(att->vldata); |
---|
1277 | } |
---|
1278 | |
---|
1279 | free(att); |
---|
1280 | return NC_NOERR; |
---|
1281 | } |
---|
1282 | |
---|
1283 | /* Normalize a UTF8 name. Put the result in norm_name, which can be |
---|
1284 | * NC_MAX_NAME + 1 in size. This function makes sure the free() gets |
---|
1285 | * called on the return from utf8proc_NFC, and also ensures that the |
---|
1286 | * name is not too long. */ |
---|
1287 | int |
---|
1288 | nc4_normalize_name(const char *name, char *norm_name) |
---|
1289 | { |
---|
1290 | char *temp_name; |
---|
1291 | if (!(temp_name = (char *)utf8proc_NFC((const unsigned char *)name))) |
---|
1292 | return NC_EINVAL; |
---|
1293 | if (strlen(temp_name) > NC_MAX_NAME) |
---|
1294 | { |
---|
1295 | free(temp_name); |
---|
1296 | return NC_EMAXNAME; |
---|
1297 | } |
---|
1298 | strcpy(norm_name, temp_name); |
---|
1299 | free(temp_name); |
---|
1300 | return NC_NOERR; |
---|
1301 | } |
---|
1302 | |
---|
1303 | /* Print out a bunch of info to stderr about the metadata for |
---|
1304 | debugging purposes. */ |
---|
1305 | #ifdef LOGGING |
---|
1306 | /* Use this to set the global log level. Set it to NC_TURN_OFF_LOGGING |
---|
1307 | (-1) to turn off all logging. Set it to 0 to show only errors, and |
---|
1308 | to higher numbers to show more and more logging details. */ |
---|
1309 | int |
---|
1310 | nc_set_log_level(int new_level) |
---|
1311 | { |
---|
1312 | /* If the user wants to completely turn off logging, turn off HDF5 |
---|
1313 | logging too. Now I truely can't think of what to do if this |
---|
1314 | fails, so just ignore the return code. */ |
---|
1315 | if (new_level == NC_TURN_OFF_LOGGING) |
---|
1316 | { |
---|
1317 | H5Eset_auto(NULL, NULL); |
---|
1318 | LOG((1, "HDF5 error messages turned off!")); |
---|
1319 | } |
---|
1320 | |
---|
1321 | /* Do we need to turn HDF5 logging back on? */ |
---|
1322 | if (new_level > NC_TURN_OFF_LOGGING && |
---|
1323 | nc_log_level <= NC_TURN_OFF_LOGGING) |
---|
1324 | { |
---|
1325 | if (H5Eset_auto((H5E_auto_t)&H5Eprint, stderr) < 0) |
---|
1326 | LOG((0, "H5Eset_auto failed!")); |
---|
1327 | LOG((1, "HDF5 error messages turned on.")); |
---|
1328 | } |
---|
1329 | |
---|
1330 | /* Now remember the new level. */ |
---|
1331 | nc_log_level = new_level; |
---|
1332 | LOG((4, "log_level changed to %d", nc_log_level)); |
---|
1333 | return 0; |
---|
1334 | } |
---|
1335 | |
---|
1336 | /* Recursively print the metadata of a group. */ |
---|
1337 | #define MAX_NESTS 10 |
---|
1338 | static int |
---|
1339 | rec_print_metadata(NC_GRP_INFO_T *grp, int *tab_count) |
---|
1340 | { |
---|
1341 | NC_GRP_INFO_T *g; |
---|
1342 | NC_ATT_INFO_T *att; |
---|
1343 | NC_VAR_INFO_T *var; |
---|
1344 | NC_DIM_INFO_T *dim; |
---|
1345 | NC_TYPE_INFO_T *type; |
---|
1346 | NC_FIELD_INFO_T *field; |
---|
1347 | char tabs[MAX_NESTS] = ""; |
---|
1348 | char dims_string[NC_MAX_DIMS*4]; |
---|
1349 | char temp_string[10]; |
---|
1350 | int t, retval, d; |
---|
1351 | |
---|
1352 | /* Come up with a number of tabs relative to the group. */ |
---|
1353 | for (t = 0; t < *tab_count && t < MAX_NESTS; t++) |
---|
1354 | strcat(tabs, "\t"); |
---|
1355 | |
---|
1356 | LOG((2, "%s GROUP - %s nc_grpid: %d nvars: %d natts: %d", |
---|
1357 | tabs, grp->name, grp->nc_grpid, grp->nvars, grp->natts)); |
---|
1358 | |
---|
1359 | for(att = grp->att; att; att = att->next) |
---|
1360 | LOG((2, "%s GROUP ATTRIBUTE - attnum: %d name: %s type: %d len: %d", |
---|
1361 | tabs, att->attnum, att->name, att->xtype, att->len)); |
---|
1362 | |
---|
1363 | /* To display dims starting with 0 and going up, go through list is |
---|
1364 | * reverse order. */ |
---|
1365 | for(dim = grp->dim; dim && dim->next; dim = dim->next) |
---|
1366 | ; |
---|
1367 | for( ; dim; dim = dim->prev) |
---|
1368 | LOG((2, "%s DIMENSION - dimid: %d name: %s len: %d unlimited: %d", |
---|
1369 | tabs, dim->dimid, dim->name, dim->len, dim->unlimited)); |
---|
1370 | |
---|
1371 | /* To display vars starting with 0 and going up, go through list is |
---|
1372 | * reverse order. */ |
---|
1373 | for(var = grp->var; var && var->next; var = var->next) |
---|
1374 | ; |
---|
1375 | for( ; var; var = var->prev) |
---|
1376 | { |
---|
1377 | strcpy(dims_string, ""); |
---|
1378 | for (d = 0; d < var->ndims; d++) |
---|
1379 | { |
---|
1380 | sprintf(temp_string, " %d", var->dimids[d]); |
---|
1381 | strcat(dims_string, temp_string); |
---|
1382 | } |
---|
1383 | LOG((2, "%s VARIABLE - varid: %d name: %s type: %d ndims: %d dimscale: %d dimids:%s", |
---|
1384 | tabs, var->varid, var->name, var->xtype, var->ndims, var->dimscale, |
---|
1385 | dims_string)); |
---|
1386 | for(att = var->att; att; att = att->next) |
---|
1387 | LOG((2, "%s VAR ATTRIBUTE - attnum: %d name: %s type: %d len: %d", |
---|
1388 | tabs, att->attnum, att->name, att->xtype, att->len)); |
---|
1389 | } |
---|
1390 | |
---|
1391 | for (type = grp->type; type; type = type->next) |
---|
1392 | { |
---|
1393 | LOG((2, "%s TYPE - nc_typeid: %d hdf_typeid: 0x%x size: %d committed: %d " |
---|
1394 | "name: %s num_fields: %d base_nc_type: %d", tabs, type->nc_typeid, |
---|
1395 | type->hdf_typeid, type->size, type->committed, type->name, |
---|
1396 | type->num_fields, type->base_nc_type)); |
---|
1397 | /* Is this a compound type? */ |
---|
1398 | if (type->class == NC_COMPOUND) |
---|
1399 | { |
---|
1400 | LOG((3, "compound type")); |
---|
1401 | for (field = type->field; field; field = field->next) |
---|
1402 | LOG((4, "field %s offset %d nctype %d ndims %d", field->name, |
---|
1403 | field->offset, field->nctype, field->ndims)); |
---|
1404 | } |
---|
1405 | else if (type->class == NC_VLEN) |
---|
1406 | LOG((3, "VLEN type")); |
---|
1407 | else if (type->class == NC_OPAQUE) |
---|
1408 | LOG((3, "Opaque type")); |
---|
1409 | else if (type->class == NC_ENUM) |
---|
1410 | LOG((3, "Enum type")); |
---|
1411 | else |
---|
1412 | { |
---|
1413 | LOG((0, "Unknown class: %d", type->class)); |
---|
1414 | return NC_EBADTYPE; |
---|
1415 | } |
---|
1416 | } |
---|
1417 | |
---|
1418 | /* Call self for each child of this group. */ |
---|
1419 | if (grp->children) |
---|
1420 | { |
---|
1421 | (*tab_count)++; |
---|
1422 | for (g = grp->children; g; g = g->next) |
---|
1423 | if ((retval = rec_print_metadata(g, tab_count))) |
---|
1424 | return retval; |
---|
1425 | (*tab_count)--; |
---|
1426 | } |
---|
1427 | |
---|
1428 | return NC_NOERR; |
---|
1429 | } |
---|
1430 | |
---|
1431 | /* Print out the internal metadata for a file. This is useful to check |
---|
1432 | * that netCDF is working! Nonetheless, this function will print |
---|
1433 | * nothing if logging is not set to at least two. */ |
---|
1434 | int |
---|
1435 | log_metadata_nc(NC_FILE_INFO_T *nc) |
---|
1436 | { |
---|
1437 | NC_HDF5_FILE_INFO_T *h5 = nc->nc4_info; |
---|
1438 | int tab_count = 0; |
---|
1439 | |
---|
1440 | LOG((2, "*** NetCDF-4 Internal Metadata: int_ncid 0x%x ext_ncid 0x%x", |
---|
1441 | nc->int_ncid, nc->ext_ncid)); |
---|
1442 | if (!h5) |
---|
1443 | { |
---|
1444 | LOG((2, "This is a netCDF-3 file.")); |
---|
1445 | return NC_NOERR; |
---|
1446 | } |
---|
1447 | LOG((2, "FILE - hdfid: 0x%x path: %s cmode: 0x%x parallel: %d redef: %d " |
---|
1448 | "fill_mode: %d no_write: %d next_nc_grpid: %d", h5->hdfid, h5->path, |
---|
1449 | h5->cmode, h5->parallel, h5->redef, h5->fill_mode, h5->no_write, |
---|
1450 | h5->next_nc_grpid)); |
---|
1451 | return rec_print_metadata(h5->root_grp, &tab_count); |
---|
1452 | } |
---|
1453 | |
---|
1454 | #endif /*LOGGING */ |
---|
1455 | |
---|
1456 | /* Show the in-memory metadata for a netcdf file. */ |
---|
1457 | int |
---|
1458 | NC4_show_metadata(int ncid) |
---|
1459 | { |
---|
1460 | int retval = NC_NOERR; |
---|
1461 | #ifdef LOGGING |
---|
1462 | NC_FILE_INFO_T *nc; |
---|
1463 | int old_log_level = nc_log_level; |
---|
1464 | |
---|
1465 | /* Find file metadata. */ |
---|
1466 | if (!(nc = nc4_find_nc_file(ncid))) |
---|
1467 | return NC_EBADID; |
---|
1468 | |
---|
1469 | /* Log level must be 2 to see metadata. */ |
---|
1470 | nc_log_level = 2; |
---|
1471 | retval = log_metadata_nc(nc); |
---|
1472 | nc_log_level = old_log_level; |
---|
1473 | #endif /*LOGGING*/ |
---|
1474 | return retval; |
---|
1475 | } |
---|
1476 | |
---|