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