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