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


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed