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