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