1 /* Copyright 2003-2018, University Corporation for Atmospheric
2  * Research. See the COPYRIGHT file for copying and redistribution
3  * conditions.
4  */
5 /**
6  * @file
7  * @internal
8  * Internal netcdf-4 functions.
9  *
10  * This file contains functions internal to the netcdf4 library. None of
11  * the functions in this file are exposed in the exetnal API. These
12  * functions all relate to the manipulation of netcdf-4's in-memory
13  * buffer of metadata information, i.e. the linked list of NC
14  * structs.
15  *
16  * @author Ed Hartnett, Dennis Heimbigner, Ward Fisher
17  */
18 #include "config.h"
19 #include "netcdf.h"
20 #include "netcdf_filter.h"
21 #include "nc4internal.h"
22 #include "nc.h" /* from libsrc */
23 #include "ncdispatch.h" /* from libdispatch */
24 #include "ncutf8.h"
25 
26 /* These hold the file caching settings for the library. */
27 size_t nc4_chunk_cache_size = CHUNK_CACHE_SIZE;            /**< Default chunk cache size. */
28 size_t nc4_chunk_cache_nelems = CHUNK_CACHE_NELEMS;        /**< Default chunk cache number of elements. */
29 float nc4_chunk_cache_preemption = CHUNK_CACHE_PREEMPTION; /**< Default chunk cache preemption. */
30 
31 static void freefilterlist(NClist* filters);
32 
33 #ifdef LOGGING
34 /* This is the severity level of messages which will be logged. Use
35    severity 0 for errors, 1 for important log messages, 2 for less
36    important, etc. */
37 int nc_log_level = NC_TURN_OFF_LOGGING;
38 #endif /* LOGGING */
39 
40 /**
41  * @internal Check and normalize and name.
42  *
43  * @param name Name to normalize.
44  * @param norm_name The normalized name.
45  *
46  * @return ::NC_NOERR No error.
47  * @return ::NC_EMAXNAME Name too long.
48  * @return ::NC_EINVAL NULL given for name.
49  * @return ::NC_ENOMEM Out of memory.
50  * @author Dennis Heimbigner
51  */
52 int
nc4_check_name(const char * name,char * norm_name)53 nc4_check_name(const char *name, char *norm_name)
54 {
55     char *temp;
56     int retval;
57 
58     assert(norm_name);
59 
60     /* Check for NULL. */
61     if (!name)
62         return NC_EINVAL;
63 
64     /* Make sure this is a valid netcdf name. This should be done
65      * before the name is normalized, because it gives better error
66      * codes for bad utf8 strings. */
67     if ((retval = NC_check_name(name)))
68         return retval;
69 
70     /* Normalize the name. */
71     if ((retval = nc_utf8_normalize((const unsigned char *)name,
72                                     (unsigned char **)&temp)))
73         return retval;
74 
75     /* Check length of normalized name. */
76     if (strlen(temp) > NC_MAX_NAME)
77     {
78         free(temp);
79         return NC_EMAXNAME;
80     }
81 
82     /* Copy the normalized name. */
83     strcpy(norm_name, temp);
84     free(temp);
85 
86     return NC_NOERR;
87 }
88 
89 /**
90  * @internal Add a file to the list of libsrc4 open files. This is
91  * used by dispatch layers that wish to use the libsrc4 metadata
92  * model, but don't know about struct NC. This is the same as
93  * nc4_nc4f_list_add(), except it takes an ncid instead of an NC *,
94  * and also passes back the dispatchdata pointer.
95  *
96  * @param ncid The (already-assigned) ncid of the file (aka ext_ncid).
97  * @param path The file name of the new file.
98  * @param mode The mode flag.
99  * @param dispatchdata Void * that gets pointer to dispatch data,
100  * which is the NC_FILE_INFO_T struct allocated for this file and its
101  * metadata. Ignored if NULL. (This is passed as a void to allow
102  * external user-defined formats to use this function.)
103  *
104  * @return ::NC_NOERR No error.
105  * @return ::NC_EBADID No NC struct with this ext_ncid.
106  * @return ::NC_ENOMEM Out of memory.
107  * @author Ed Hartnett
108  */
109 int
nc4_file_list_add(int ncid,const char * path,int mode,void ** dispatchdata)110 nc4_file_list_add(int ncid, const char *path, int mode, void **dispatchdata)
111 {
112     NC *nc;
113     int ret;
114 
115     /* Find NC pointer for this file. */
116     if ((ret = NC_check_id(ncid, &nc)))
117         return ret;
118 
119     /* Add necessary structs to hold netcdf-4 file data. This is where
120      * the NC_FILE_INFO_T struct is allocated for the file. */
121     if ((ret = nc4_nc4f_list_add(nc, path, mode)))
122         return ret;
123 
124     /* If the user wants a pointer to the NC_FILE_INFO_T, then provide
125      * it. */
126     if (dispatchdata)
127         *dispatchdata = nc->dispatchdata;
128 
129     return NC_NOERR;
130 }
131 
132 /**
133  * @internal Change the ncid of an open file. This is needed for PIO
134  * integration.
135  *
136  * @param ncid The ncid of the file (aka ext_ncid).
137  * @param new_ncid The new ncid index to use (i.e. the first two bytes
138  * of the ncid).
139  *
140  * @return ::NC_NOERR No error.
141  * @return ::NC_EBADID No NC struct with this ext_ncid.
142  * @return ::NC_ENOMEM Out of memory.
143  * @author Ed Hartnett
144  */
145 int
nc4_file_change_ncid(int ncid,unsigned short new_ncid_index)146 nc4_file_change_ncid(int ncid, unsigned short new_ncid_index)
147 {
148     NC *nc;
149     int ret;
150 
151     LOG((2, "%s: ncid %d new_ncid_index %d", __func__, ncid, new_ncid_index));
152 
153     /* Find NC pointer for this file. */
154     if ((ret = NC_check_id(ncid, &nc)))
155         return ret;
156 
157     /* Move it in the list. It will faile if list spot is already
158      * occupied. */
159     LOG((3, "moving nc->ext_ncid %d nc->ext_ncid >> ID_SHIFT %d",
160          nc->ext_ncid, nc->ext_ncid >> ID_SHIFT));
161     if (move_in_NCList(nc, new_ncid_index))
162         return NC_EIO;
163     LOG((3, "moved to new_ncid_index %d new nc->ext_ncid %d", new_ncid_index,
164          nc->ext_ncid));
165 
166     return NC_NOERR;
167 }
168 
169 /**
170  * @internal Get info about a file on the list of libsrc4 open
171  * files. This is used by dispatch layers that wish to use the libsrc4
172  * metadata model, but don't know about struct NC.
173  *
174  * @param ncid The ncid of the file (aka ext_ncid).
175  * @param path A pointer that gets file name (< NC_MAX_NAME). Ignored
176  * if NULL.
177  * @param mode A pointer that gets the mode flag. Ignored if NULL.
178  * @param dispatchdata Void * that gets pointer to dispatch data,
179  * which is the NC_FILE_INFO_T struct allocated for this file and its
180  * metadata. Ignored if NULL. (This is passed as a void to allow
181  * external user-defined formats to use this function.)
182  *
183  * @return ::NC_NOERR No error.
184  * @return ::NC_EBADID No NC struct with this ext_ncid.
185  * @return ::NC_ENOMEM Out of memory.
186  * @author Ed Hartnett
187  */
188 int
nc4_file_list_get(int ncid,char ** path,int * mode,void ** dispatchdata)189 nc4_file_list_get(int ncid, char **path, int *mode, void **dispatchdata)
190 {
191     NC *nc;
192     int ret;
193 
194     /* Find NC pointer for this file. */
195     if ((ret = NC_check_id(ncid, &nc)))
196         return ret;
197 
198     /* If the user wants path, give it. */
199     if (path)
200         strncpy(*path, nc->path, NC_MAX_NAME);
201 
202     /* If the user wants mode, give it. */
203     if (mode)
204         *mode = nc->mode;
205 
206     /* If the user wants dispatchdata, give it. */
207     if (dispatchdata)
208         *dispatchdata = nc->dispatchdata;
209 
210     return NC_NOERR;
211 }
212 
213 /**
214  * @internal Given an NC pointer, add the necessary stuff for a
215  * netcdf-4 file. This allocates the NC_FILE_INFO_T struct for the
216  * file, which is used by libhdf5 and libhdf4 (and perhaps other
217  * future dispatch layers) to hold the metadata for the file.
218  *
219  * @param nc Pointer to file's NC struct.
220  * @param path The file name of the new file.
221  * @param mode The mode flag.
222  *
223  * @return ::NC_NOERR No error.
224  * @return ::NC_ENOMEM Out of memory.
225  * @author Ed Hartnett, Dennis Heimbigner
226  */
227 int
nc4_nc4f_list_add(NC * nc,const char * path,int mode)228 nc4_nc4f_list_add(NC *nc, const char *path, int mode)
229 {
230     NC_FILE_INFO_T *h5;
231     int retval;
232 
233     assert(nc && !NC4_DATA(nc) && path);
234 
235     /* We need to malloc and initialize the substructure
236        NC_FILE_INFO_T. */
237     if (!(h5 = calloc(1, sizeof(NC_FILE_INFO_T))))
238         return NC_ENOMEM;
239     nc->dispatchdata = h5;
240     h5->controller = nc;
241 
242     /* Hang on to cmode, and note that we're in define mode. */
243     h5->cmode = mode | NC_INDEF;
244 
245     /* The next_typeid needs to be set beyond the end of our atomic
246      * types. */
247     h5->next_typeid = NC_FIRSTUSERTYPEID;
248 
249     /* Initialize lists for dimensions, types, and groups. */
250     h5->alldims = nclistnew();
251     h5->alltypes = nclistnew();
252     h5->allgroups = nclistnew();
253 
254     /* There's always at least one open group - the root
255      * group. Allocate space for one group's worth of information. Set
256      * its grp id, name, and allocate associated empty lists. */
257     if ((retval = nc4_grp_list_add(h5, NULL, NC_GROUP_NAME, &h5->root_grp)))
258         return retval;
259 
260     return NC_NOERR;
261 }
262 
263 /**
264  * @internal Given an ncid, find the relevant group and return a
265  * pointer to it.
266  *
267  * @param ncid File and group ID.
268  * @param grp Pointer that gets pointer to group info struct. Ignored
269  * if NULL.
270  *
271  * @return ::NC_NOERR No error.
272  * @return ::NC_ENOTNC4 Not a netCDF-4 file.
273  * @author Ed Hartnett
274  */
275 int
nc4_find_nc4_grp(int ncid,NC_GRP_INFO_T ** grp)276 nc4_find_nc4_grp(int ncid, NC_GRP_INFO_T **grp)
277 {
278     return nc4_find_nc_grp_h5(ncid, NULL, grp, NULL);
279 }
280 
281 /**
282  * @internal Given an ncid, find the relevant group and return a
283  * pointer to it, also set a pointer to the nc4_info struct of the
284  * related file.
285  *
286  * @param ncid File and group ID.
287  * @param grp Pointer that gets pointer to group info struct. Ignored
288  * if NULL.
289  * @param h5 Pointer that gets pointer to file info struct. Ignored if
290  * NULL.
291  *
292  * @return ::NC_NOERR No error.
293  * @return ::NC_EBADID Bad ncid.
294  * @author Ed Hartnett
295  */
296 int
nc4_find_grp_h5(int ncid,NC_GRP_INFO_T ** grp,NC_FILE_INFO_T ** h5)297 nc4_find_grp_h5(int ncid, NC_GRP_INFO_T **grp, NC_FILE_INFO_T **h5)
298 {
299     return nc4_find_nc_grp_h5(ncid, NULL, grp, h5);
300 }
301 
302 /**
303  * @internal Find info for this file and group, and set pointers.
304  *
305  * @param ncid File and group ID.
306  * @param nc Pointer that gets a pointer to the file's NC
307  * struct. Ignored if NULL.
308  * @param grp Pointer that gets a pointer to the group
309  * struct. Ignored if NULL.
310  * @param h5 Pointer that gets HDF5 file struct. Ignored if NULL.
311  *
312  * @return ::NC_NOERR No error.
313  * @return ::NC_EBADID Bad ncid.
314  * @author Ed Hartnett, Dennis Heimbigner
315  */
316 int
nc4_find_nc_grp_h5(int ncid,NC ** nc,NC_GRP_INFO_T ** grp,NC_FILE_INFO_T ** h5)317 nc4_find_nc_grp_h5(int ncid, NC **nc, NC_GRP_INFO_T **grp, NC_FILE_INFO_T **h5)
318 {
319     NC_GRP_INFO_T *my_grp = NULL;
320     NC_FILE_INFO_T *my_h5 = NULL;
321     NC *my_nc;
322     int retval;
323 
324     /* Look up file metadata. */
325     if ((retval = NC_check_id(ncid, &my_nc)))
326         return retval;
327     my_h5 = my_nc->dispatchdata;
328     assert(my_h5 && my_h5->root_grp);
329 
330     /* If we can't find it, the grp id part of ncid is bad. */
331     if (!(my_grp = nclistget(my_h5->allgroups, (ncid & GRP_ID_MASK))))
332         return NC_EBADID;
333 
334     /* Return pointers to caller, if desired. */
335     if (nc)
336         *nc = my_nc;
337     if (h5)
338         *h5 = my_h5;
339     if (grp)
340         *grp = my_grp;
341 
342     return NC_NOERR;
343 }
344 
345 /**
346  * @internal Given an ncid and varid, get pointers to the group and var
347  * metadata.
348  *
349  * @param ncid File ID.
350  * @param varid Variable ID.
351  * @param h5 Pointer that gets pointer to the NC_FILE_INFO_T struct
352  * for this file. Ignored if NULL.
353  * @param grp Pointer that gets pointer to group info. Ignored if
354  * NULL.
355  * @param var Pointer that gets pointer to var info. Ignored if NULL.
356  *
357  * @return ::NC_NOERR No error.
358  * @author Ed Hartnett
359  */
360 int
nc4_find_grp_h5_var(int ncid,int varid,NC_FILE_INFO_T ** h5,NC_GRP_INFO_T ** grp,NC_VAR_INFO_T ** var)361 nc4_find_grp_h5_var(int ncid, int varid, NC_FILE_INFO_T **h5, NC_GRP_INFO_T **grp,
362                     NC_VAR_INFO_T **var)
363 {
364     NC_FILE_INFO_T *my_h5;
365     NC_GRP_INFO_T *my_grp;
366     NC_VAR_INFO_T *my_var;
367     int retval;
368 
369     /* Look up file and group metadata. */
370     if ((retval = nc4_find_grp_h5(ncid, &my_grp, &my_h5)))
371         return retval;
372     assert(my_grp && my_h5);
373 
374     /* Find the var. */
375     if (!(my_var = (NC_VAR_INFO_T *)ncindexith(my_grp->vars, varid)))
376         return NC_ENOTVAR;
377     assert(my_var && my_var->hdr.id == varid);
378 
379     /* Return pointers that caller wants. */
380     if (h5)
381         *h5 = my_h5;
382     if (grp)
383         *grp = my_grp;
384     if (var)
385         *var = my_var;
386 
387     return NC_NOERR;
388 }
389 
390 /**
391  * @internal Find a dim in the file.
392  *
393  * @param grp Pointer to group info struct.
394  * @param dimid Dimension ID to find.
395  * @param dim Pointer that gets pointer to dim info if found.
396  * @param dim_grp Pointer that gets pointer to group info of group
397  * that contains dimension. Ignored if NULL.
398  *
399  * @return ::NC_NOERR No error.
400  * @return ::NC_EBADDIM Dimension not found.
401  * @author Ed Hartnett, Dennis Heimbigner
402  */
403 int
nc4_find_dim(NC_GRP_INFO_T * grp,int dimid,NC_DIM_INFO_T ** dim,NC_GRP_INFO_T ** dim_grp)404 nc4_find_dim(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T **dim,
405              NC_GRP_INFO_T **dim_grp)
406 {
407     assert(grp && grp->nc4_info && dim);
408     LOG((4, "%s: dimid %d", __func__, dimid));
409 
410     /* Find the dim info. */
411     if (!((*dim) = nclistget(grp->nc4_info->alldims, dimid)))
412         return NC_EBADDIM;
413 
414     /* Give the caller the group the dimension is in. */
415     if (dim_grp)
416         *dim_grp = (*dim)->container;
417 
418     return NC_NOERR;
419 }
420 
421 /**
422  * @internal Find a var (by name) in a grp.
423  *
424  * @param grp Pointer to group info.
425  * @param name Name of var to find.
426  * @param var Pointer that gets pointer to var info struct, if found.
427  *
428  * @return ::NC_NOERR No error.
429  * @author Ed Hartnett
430  */
431 int
nc4_find_var(NC_GRP_INFO_T * grp,const char * name,NC_VAR_INFO_T ** var)432 nc4_find_var(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var)
433 {
434     assert(grp && var && name);
435 
436     /* Find the var info. */
437     *var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name);
438     return NC_NOERR;
439 }
440 
441 /**
442  * @internal Locate netCDF type by name.
443  *
444  * @param start_grp Pointer to starting group info.
445  * @param name Name of type to find.
446  *
447  * @return Pointer to type info, or NULL if not found.
448  * @author Ed Hartnett, Dennis Heimbigner
449  */
450 NC_TYPE_INFO_T *
nc4_rec_find_named_type(NC_GRP_INFO_T * start_grp,char * name)451 nc4_rec_find_named_type(NC_GRP_INFO_T *start_grp, char *name)
452 {
453     NC_GRP_INFO_T *g;
454     NC_TYPE_INFO_T *type, *res;
455     int i;
456 
457     assert(start_grp);
458 
459     /* Does this group have the type we are searching for? */
460     type  = (NC_TYPE_INFO_T*)ncindexlookup(start_grp->type,name);
461     if(type != NULL)
462         return type;
463 
464     /* Search subgroups. */
465     for(i=0;i<ncindexsize(start_grp->children);i++) {
466         g = (NC_GRP_INFO_T*)ncindexith(start_grp->children,i);
467         if(g == NULL) continue;
468         if ((res = nc4_rec_find_named_type(g, name)))
469             return res;
470     }
471     /* Can't find it. Oh, woe is me! */
472     return NULL;
473 }
474 
475 /**
476  * @internal Use a netCDF typeid to find a type in a type_list.
477  *
478  * @param h5 Pointer to HDF5 file info struct.
479  * @param typeid The netCDF type ID.
480  * @param type Pointer to pointer to the list of type info structs.
481  *
482  * @return ::NC_NOERR No error.
483  * @return ::NC_EINVAL Invalid input.
484  * @author Ed Hartnett
485  */
486 int
nc4_find_type(const NC_FILE_INFO_T * h5,nc_type typeid,NC_TYPE_INFO_T ** type)487 nc4_find_type(const NC_FILE_INFO_T *h5, nc_type typeid, NC_TYPE_INFO_T **type)
488 {
489     /* Check inputs. */
490     assert(h5);
491     if (typeid < 0 || !type)
492         return NC_EINVAL;
493     *type = NULL;
494 
495     /* Atomic types don't have associated NC_TYPE_INFO_T struct, just
496      * return NOERR. */
497     if (typeid <= NC_STRING)
498         return NC_NOERR;
499 
500     /* Find the type. */
501     if (!(*type = nclistget(h5->alltypes,typeid)))
502         return NC_EBADTYPID;
503 
504     return NC_NOERR;
505 }
506 
507 /**
508  * @internal Given a group, find an att. If name is provided, use that,
509  * otherwise use the attnum.
510  *
511  * @param grp Pointer to group info struct.
512  * @param varid Variable ID.
513  * @param name Name to of attribute.
514  * @param attnum Number of attribute.
515  * @param att Pointer to pointer that gets attribute info struct.
516  *
517  * @return ::NC_NOERR No error.
518  * @return ::NC_ENOTVAR Variable not found.
519  * @return ::NC_ENOTATT Attribute not found.
520  * @author Ed Hartnett
521  */
522 int
nc4_find_grp_att(NC_GRP_INFO_T * grp,int varid,const char * name,int attnum,NC_ATT_INFO_T ** att)523 nc4_find_grp_att(NC_GRP_INFO_T *grp, int varid, const char *name, int attnum,
524                  NC_ATT_INFO_T **att)
525 {
526     NC_VAR_INFO_T *var;
527     NC_ATT_INFO_T *my_att;
528     NCindex *attlist = NULL;
529 
530     assert(grp && grp->hdr.name && att);
531 
532     LOG((4, "%s: grp->name %s varid %d attnum %d", __func__, grp->hdr.name,
533          varid, attnum));
534 
535     /* Get either the global or a variable attribute list. */
536     if (varid == NC_GLOBAL)
537     {
538         attlist = grp->att;
539     }
540     else
541     {
542         var = (NC_VAR_INFO_T*)ncindexith(grp->vars,varid);
543         if (!var) return NC_ENOTVAR;
544 
545         attlist = var->att;
546     }
547     assert(attlist);
548 
549     /* Now find the attribute by name or number. If a name is provided,
550      * ignore the attnum. */
551     if (name)
552         my_att = (NC_ATT_INFO_T *)ncindexlookup(attlist, name);
553     else
554         my_att = (NC_ATT_INFO_T *)ncindexith(attlist, attnum);
555 
556     if (!my_att)
557         return NC_ENOTATT;
558 
559     *att = my_att;
560     return NC_NOERR;
561 }
562 
563 /**
564  * @internal Given an ncid, varid, and name or attnum, find and return
565  * pointer to NC_ATT_INFO_T metadata.
566  *
567  * @param ncid File and group ID.
568  * @param varid Variable ID.
569  * @param name Name to of attribute.
570  * @param attnum Number of attribute.
571  * @param att Pointer to pointer that gets attribute info struct.
572 
573  *
574  * @return ::NC_NOERR No error.
575  * @return ::NC_ENOTVAR Variable not found.
576  * @return ::NC_ENOTATT Attribute not found.
577  * @author Ed Hartnett
578  */
579 int
nc4_find_nc_att(int ncid,int varid,const char * name,int attnum,NC_ATT_INFO_T ** att)580 nc4_find_nc_att(int ncid, int varid, const char *name, int attnum,
581                 NC_ATT_INFO_T **att)
582 {
583     NC_GRP_INFO_T *grp;
584     int retval;
585 
586     LOG((4, "nc4_find_nc_att: ncid 0x%x varid %d name %s attnum %d",
587          ncid, varid, name, attnum));
588 
589     /* Find info for this file and group, and set pointer to each. */
590     if ((retval = nc4_find_grp_h5(ncid, &grp, NULL)))
591         return retval;
592     assert(grp);
593 
594     return nc4_find_grp_att(grp, varid, name, attnum, att);
595 }
596 
597 /**
598  * @internal Add NC_OBJ to allXXX lists in a file
599  *
600  * @param file Pointer to the containing file
601  * @param obj Pointer to object to add.
602  *
603  * @author Dennis Heimbigner
604  */
605 static void
obj_track(NC_FILE_INFO_T * file,NC_OBJ * obj)606 obj_track(NC_FILE_INFO_T* file, NC_OBJ* obj)
607 {
608     NClist* list = NULL;
609     /* record the object in the file  */
610     switch (obj->sort) {
611     case NCDIM: list = file->alldims; break;
612     case NCTYP: list = file->alltypes; break;
613     case NCGRP: list = file->allgroups; break;
614     default:
615         assert(NC_FALSE);
616     }
617     /* Insert at the appropriate point in the list */
618     nclistset(list,obj->id,obj);
619 }
620 
621 /**
622  * @internal Create a new variable and insert into relevant
623  * lists. Dimensionality info need not be known.
624  *
625  * @param grp the containing group
626  * @param name the name for the new variable
627  * @param var Pointer in which to return a pointer to the new var.
628  *
629  * @param var Pointer to pointer that gets variable info struct.
630  *
631  * @return ::NC_NOERR No error.
632  * @return ::NC_ENOMEM Out of memory.
633  * @author Ed Hartnett
634  */
635 int
nc4_var_list_add2(NC_GRP_INFO_T * grp,const char * name,NC_VAR_INFO_T ** var)636 nc4_var_list_add2(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var)
637 {
638     NC_VAR_INFO_T *new_var = NULL;
639 
640     /* Allocate storage for new variable. */
641     if (!(new_var = calloc(1, sizeof(NC_VAR_INFO_T))))
642         return NC_ENOMEM;
643     new_var->hdr.sort = NCVAR;
644     new_var->container = grp;
645 
646     /* These are the HDF5-1.8.4 defaults. */
647     new_var->chunk_cache_size = nc4_chunk_cache_size;
648     new_var->chunk_cache_nelems = nc4_chunk_cache_nelems;
649     new_var->chunk_cache_preemption = nc4_chunk_cache_preemption;
650 
651     /* Now fill in the values in the var info structure. */
652     new_var->hdr.id = ncindexsize(grp->vars);
653     if (!(new_var->hdr.name = strdup(name))) {
654       if(new_var)
655         free(new_var);
656       return NC_ENOMEM;
657     }
658 
659     new_var->hdr.hashkey = NC_hashmapkey(new_var->hdr.name,
660                                          strlen(new_var->hdr.name));
661 
662     /* Create an indexed list for the attributes. */
663     new_var->att = ncindexnew(0);
664 
665     /* Officially track it */
666     ncindexadd(grp->vars, (NC_OBJ *)new_var);
667 
668     /* Set the var pointer, if one was given */
669     if (var)
670         *var = new_var;
671 
672     return NC_NOERR;
673 }
674 
675 /**
676  * @internal Set the number of dims in an NC_VAR_INFO_T struct.
677  *
678  * @param var Pointer to the var.
679  * @param ndims Number of dimensions for this var.
680  *
681  * @param var Pointer to pointer that gets variable info struct.
682  *
683  * @return ::NC_NOERR No error.
684  * @return ::NC_ENOMEM Out of memory.
685  * @author Ed Hartnett
686  */
687 int
nc4_var_set_ndims(NC_VAR_INFO_T * var,int ndims)688 nc4_var_set_ndims(NC_VAR_INFO_T *var, int ndims)
689 {
690     assert(var);
691 
692     /* Remember the number of dimensions. */
693     var->ndims = ndims;
694 
695     /* Allocate space for dimension information. */
696     if (ndims)
697     {
698         if (!(var->dim = calloc(ndims, sizeof(NC_DIM_INFO_T *))))
699             return NC_ENOMEM;
700         if (!(var->dimids = calloc(ndims, sizeof(int))))
701             return NC_ENOMEM;
702 
703         /* Initialize dimids to illegal values (-1). See the comment
704            in nc4_rec_match_dimscales(). */
705         memset(var->dimids, -1, ndims * sizeof(int));
706     }
707 
708     return NC_NOERR;
709 }
710 
711 /**
712  * @internal Create a new variable and insert int relevant list.
713  *
714  * @param grp the containing group
715  * @param name the name for the new variable
716  * @param ndims the rank of the new variable
717  * @param var Pointer in which to return a pointer to the new var.
718  *
719  * @param var Pointer to pointer that gets variable info struct.
720  *
721  * @return ::NC_NOERR No error.
722  * @return ::NC_ENOMEM Out of memory.
723  * @author Ed Hartnett
724  */
725 int
nc4_var_list_add(NC_GRP_INFO_T * grp,const char * name,int ndims,NC_VAR_INFO_T ** var)726 nc4_var_list_add(NC_GRP_INFO_T* grp, const char* name, int ndims,
727                  NC_VAR_INFO_T **var)
728 {
729     int retval;
730 
731     if ((retval = nc4_var_list_add2(grp, name, var)))
732         return retval;
733     if ((retval = nc4_var_set_ndims(*var, ndims)))
734         return retval;
735 
736     return NC_NOERR;
737 }
738 
739 /**
740  * @internal Add a dimension to the dimension list for a group.
741  *
742  * @param grp container for the dim
743  * @param name for the dim
744  * @param len for the dim
745  * @param assignedid override dimid if >= 0
746  * @param dim Pointer to pointer that gets the new dim info struct.
747  *
748  * @return ::NC_NOERR No error.
749  * @return ::NC_ENOMEM Out of memory.
750  * @author Ed Hartnett
751  */
752 int
nc4_dim_list_add(NC_GRP_INFO_T * grp,const char * name,size_t len,int assignedid,NC_DIM_INFO_T ** dim)753 nc4_dim_list_add(NC_GRP_INFO_T *grp, const char *name, size_t len,
754                  int assignedid, NC_DIM_INFO_T **dim)
755 {
756     NC_DIM_INFO_T *new_dim = NULL;
757 
758     assert(grp && name);
759 
760     /* Allocate memory for dim metadata. */
761     if (!(new_dim = calloc(1, sizeof(NC_DIM_INFO_T))))
762         return NC_ENOMEM;
763 
764     new_dim->hdr.sort = NCDIM;
765 
766     /* Assign the dimension ID. */
767     if (assignedid >= 0)
768         new_dim->hdr.id = assignedid;
769     else
770         new_dim->hdr.id = grp->nc4_info->next_dimid++;
771 
772     /* Remember the name and create a hash. */
773     if (!(new_dim->hdr.name = strdup(name))) {
774       if(new_dim)
775         free(new_dim);
776 
777       return NC_ENOMEM;
778     }
779     new_dim->hdr.hashkey = NC_hashmapkey(new_dim->hdr.name,
780                                          strlen(new_dim->hdr.name));
781 
782     /* Is dimension unlimited? */
783     new_dim->len = len;
784     if (len == NC_UNLIMITED)
785         new_dim->unlimited = NC_TRUE;
786 
787     /* Remember the containing group. */
788     new_dim->container = grp;
789 
790     /* Add object to dimension list for this group. */
791     ncindexadd(grp->dim, (NC_OBJ *)new_dim);
792     obj_track(grp->nc4_info, (NC_OBJ *)new_dim);
793 
794     /* Set the dim pointer, if one was given */
795     if (dim)
796         *dim = new_dim;
797 
798     return NC_NOERR;
799 }
800 
801 /**
802  * @internal Add to an attribute list.
803  *
804  * @param list NCindex of att info structs.
805  * @param name name of the new attribute
806  * @param att Pointer to pointer that gets the new att info
807  * struct. Ignored if NULL.
808  *
809  * @return ::NC_NOERR No error.
810  * @return ::NC_ENOMEM Out of memory.
811  * @author Ed Hartnett
812  */
813 int
nc4_att_list_add(NCindex * list,const char * name,NC_ATT_INFO_T ** att)814 nc4_att_list_add(NCindex *list, const char *name, NC_ATT_INFO_T **att)
815 {
816     NC_ATT_INFO_T *new_att = NULL;
817 
818     LOG((3, "%s: name %s ", __func__, name));
819 
820     if (!(new_att = calloc(1, sizeof(NC_ATT_INFO_T))))
821         return NC_ENOMEM;
822     new_att->hdr.sort = NCATT;
823 
824     /* Fill in the information we know. */
825     new_att->hdr.id = ncindexsize(list);
826     if (!(new_att->hdr.name = strdup(name))) {
827       if(new_att)
828         free(new_att);
829       return NC_ENOMEM;
830     }
831     /* Create a hash of the name. */
832     new_att->hdr.hashkey = NC_hashmapkey(name, strlen(name));
833 
834     /* Add object to list as specified by its number */
835     ncindexadd(list, (NC_OBJ *)new_att);
836 
837     /* Set the attribute pointer, if one was given */
838     if (att)
839         *att = new_att;
840 
841     return NC_NOERR;
842 }
843 
844 /**
845  * @internal Add a group to a group list.
846  *
847  * @param h5 Pointer to the file info.
848  * @param parent Pointer to the parent group. Will be NULL when adding
849  * the root group.
850  * @param name Name of the group.
851  * @param grp Pointer to pointer that gets new group info
852  * struct. Ignored if NULL.
853  *
854  * @return ::NC_NOERR No error.
855  * @return ::NC_ENOMEM Out of memory.
856  * @author Ed Hartnett, Dennis Heimbigner
857  */
858 int
nc4_grp_list_add(NC_FILE_INFO_T * h5,NC_GRP_INFO_T * parent,char * name,NC_GRP_INFO_T ** grp)859 nc4_grp_list_add(NC_FILE_INFO_T *h5, NC_GRP_INFO_T *parent, char *name,
860                  NC_GRP_INFO_T **grp)
861 {
862     NC_GRP_INFO_T *new_grp;
863 
864     /* Check inputs. */
865     assert(h5 && name);
866     LOG((3, "%s: name %s ", __func__, name));
867 
868     /* Get the memory to store this groups info. */
869     if (!(new_grp = calloc(1, sizeof(NC_GRP_INFO_T))))
870         return NC_ENOMEM;
871 
872     /* Fill in this group's information. */
873     new_grp->hdr.sort = NCGRP;
874     new_grp->nc4_info = h5;
875     new_grp->parent = parent;
876 
877     /* Assign the group ID. The root group will get id 0. */
878     new_grp->hdr.id = h5->next_nc_grpid++;
879     assert(parent || !new_grp->hdr.id);
880 
881     /* Handle the group name. */
882     if (!(new_grp->hdr.name = strdup(name)))
883     {
884         free(new_grp);
885         return NC_ENOMEM;
886     }
887     new_grp->hdr.hashkey = NC_hashmapkey(new_grp->hdr.name,
888                                          strlen(new_grp->hdr.name));
889 
890     /* Set up new indexed lists for stuff this group can contain. */
891     new_grp->children = ncindexnew(0);
892     new_grp->dim = ncindexnew(0);
893     new_grp->att = ncindexnew(0);
894     new_grp->type = ncindexnew(0);
895     new_grp->vars = ncindexnew(0);
896 
897     /* Add object to lists */
898     if (parent)
899         ncindexadd(parent->children, (NC_OBJ *)new_grp);
900     obj_track(h5, (NC_OBJ *)new_grp);
901 
902     /* Set the group pointer, if one was given */
903     if (grp)
904         *grp = new_grp;
905 
906     return NC_NOERR;
907 }
908 
909 /**
910  * @internal Names for groups, variables, and types must not be the
911  * same. This function checks that a proposed name is not already in
912  * use. Normalzation of UTF8 strings should happen before this
913  * function is called.
914  *
915  * @param grp Pointer to group info struct.
916  * @param name Name to check.
917  *
918  * @return ::NC_NOERR No error.
919  * @return ::NC_ENAMEINUSE Name is in use.
920  * @author Ed Hartnett, Dennis Heimbigner
921  */
922 int
nc4_check_dup_name(NC_GRP_INFO_T * grp,char * name)923 nc4_check_dup_name(NC_GRP_INFO_T *grp, char *name)
924 {
925     NC_TYPE_INFO_T *type;
926     NC_GRP_INFO_T *g;
927     NC_VAR_INFO_T *var;
928 
929     /* Any types of this name? */
930     type = (NC_TYPE_INFO_T*)ncindexlookup(grp->type,name);
931     if(type != NULL)
932         return NC_ENAMEINUSE;
933 
934     /* Any child groups of this name? */
935     g = (NC_GRP_INFO_T*)ncindexlookup(grp->children,name);
936     if(g != NULL)
937         return NC_ENAMEINUSE;
938 
939     /* Any variables of this name? */
940     var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name);
941     if(var != NULL)
942         return NC_ENAMEINUSE;
943 
944     return NC_NOERR;
945 }
946 
947 /**
948  * @internal Create a type, but do not add to various lists nor
949  * increment its ref count
950  *
951  * @param size Size of type in bytes.
952  * @param name Name of type.
953  * @param assignedid if >= 0 then override the default type id.
954  * @param type Pointer that gets pointer to new type info struct.
955  *
956  * @return ::NC_NOERR No error.
957  * @return ::NC_ENOMEM Out of memory.
958  * @author Ed Hartnett, Ward Fisher
959  */
960 int
nc4_type_new(size_t size,const char * name,int assignedid,NC_TYPE_INFO_T ** type)961 nc4_type_new(size_t size, const char *name, int assignedid,
962              NC_TYPE_INFO_T **type)
963 {
964     NC_TYPE_INFO_T *new_type;
965 
966     LOG((4, "%s: size %d name %s assignedid %d", __func__, size, name, assignedid));
967 
968     /* Check inputs. */
969     assert(type);
970 
971     /* Allocate memory for the type */
972     if (!(new_type = calloc(1, sizeof(NC_TYPE_INFO_T))))
973         return NC_ENOMEM;
974     new_type->hdr.sort = NCTYP;
975 
976     /* Remember info about this type. */
977     new_type->hdr.id = assignedid;
978     new_type->size = size;
979     if (!(new_type->hdr.name = strdup(name))) {
980         free(new_type);
981         return NC_ENOMEM;
982     }
983 
984     new_type->hdr.hashkey = NC_hashmapkey(name, strlen(name));
985 
986     /* Return a pointer to the new type. */
987     *type = new_type;
988 
989     return NC_NOERR;
990 }
991 
992 /**
993  * @internal Add to the type list.
994  *
995  * @param grp Pointer to group info struct.
996  * @param size Size of type in bytes.
997  * @param name Name of type.
998  * @param type Pointer that gets pointer to new type info
999  * struct.
1000  *
1001  * @return ::NC_NOERR No error.
1002  * @return ::NC_ENOMEM Out of memory.
1003  * @author Ed Hartnett
1004  */
1005 int
nc4_type_list_add(NC_GRP_INFO_T * grp,size_t size,const char * name,NC_TYPE_INFO_T ** type)1006 nc4_type_list_add(NC_GRP_INFO_T *grp, size_t size, const char *name,
1007                   NC_TYPE_INFO_T **type)
1008 {
1009     NC_TYPE_INFO_T *new_type;
1010     int retval;
1011 
1012     /* Check inputs. */
1013     assert(grp && name && type);
1014     LOG((4, "%s: size %d name %s", __func__, size, name));
1015 
1016     /* Create the new TYPE_INFO struct. */
1017     if ((retval = nc4_type_new(size, name, grp->nc4_info->next_typeid,
1018                                &new_type)))
1019         return retval;
1020     grp->nc4_info->next_typeid++;
1021 
1022     /* Increment the ref. count on the type */
1023     new_type->rc++;
1024 
1025     /* Add object to lists */
1026     ncindexadd(grp->type, (NC_OBJ *)new_type);
1027     obj_track(grp->nc4_info,(NC_OBJ*)new_type);
1028 
1029     /* Return a pointer to the new type. */
1030     *type = new_type;
1031 
1032     return NC_NOERR;
1033 }
1034 
1035 /**
1036  * @internal Add to the compound field list.
1037  *
1038  * @param parent parent type
1039  * @param name Name of the field.
1040  * @param offset Offset in bytes.
1041  * @param xtype The netCDF type of the field.
1042  * @param ndims The number of dimensions of the field.
1043  * @param dim_sizesp An array of dim sizes for the field.
1044  *
1045  * @return ::NC_NOERR No error.
1046  * @author Ed Hartnett, Dennis Heimbigner
1047  */
1048 int
nc4_field_list_add(NC_TYPE_INFO_T * parent,const char * name,size_t offset,nc_type xtype,int ndims,const int * dim_sizesp)1049 nc4_field_list_add(NC_TYPE_INFO_T *parent, const char *name,
1050                    size_t offset, nc_type xtype, int ndims,
1051                    const int *dim_sizesp)
1052 {
1053     NC_FIELD_INFO_T *field;
1054 
1055     /* Name has already been checked and UTF8 normalized. */
1056     if (!name)
1057         return NC_EINVAL;
1058 
1059     /* Allocate storage for this field information. */
1060     if (!(field = calloc(1, sizeof(NC_FIELD_INFO_T))))
1061         return NC_ENOMEM;
1062     field->hdr.sort = NCFLD;
1063 
1064     /* Store the information about this field. */
1065     if (!(field->hdr.name = strdup(name)))
1066     {
1067         free(field);
1068         return NC_ENOMEM;
1069     }
1070     field->hdr.hashkey = NC_hashmapkey(field->hdr.name,strlen(field->hdr.name));
1071     field->nc_typeid = xtype;
1072     field->offset = offset;
1073     field->ndims = ndims;
1074     if (ndims)
1075     {
1076         int i;
1077         if (!(field->dim_size = malloc(ndims * sizeof(int))))
1078         {
1079             free(field->hdr.name);
1080             free(field);
1081             return NC_ENOMEM;
1082         }
1083         for (i = 0; i < ndims; i++)
1084             field->dim_size[i] = dim_sizesp[i];
1085     }
1086 
1087     /* Add object to lists */
1088     field->hdr.id = nclistlength(parent->u.c.field);
1089     nclistpush(parent->u.c.field,field);
1090 
1091     return NC_NOERR;
1092 }
1093 
1094 /**
1095  * @internal Add a member to an enum type.
1096  *
1097  * @param parent Containing NC_TYPE_INFO_T object
1098  * @param size Size in bytes of new member.
1099  * @param name Name of the member.
1100  * @param value Value to associate with member.
1101  *
1102  * @return ::NC_NOERR No error.
1103  * @return ::NC_ENOMEM Out of memory.
1104  * @author Ed Hartnett
1105  */
1106 int
nc4_enum_member_add(NC_TYPE_INFO_T * parent,size_t size,const char * name,const void * value)1107 nc4_enum_member_add(NC_TYPE_INFO_T *parent, size_t size,
1108                     const char *name, const void *value)
1109 {
1110     NC_ENUM_MEMBER_INFO_T *member;
1111 
1112     /* Name has already been checked. */
1113     assert(name && size > 0 && value);
1114     LOG((4, "%s: size %d name %s", __func__, size, name));
1115 
1116     /* Allocate storage for this field information. */
1117     if (!(member = calloc(1, sizeof(NC_ENUM_MEMBER_INFO_T))))
1118         return NC_ENOMEM;
1119     if (!(member->value = malloc(size))) {
1120         free(member);
1121         return NC_ENOMEM;
1122     }
1123     if (!(member->name = strdup(name))) {
1124         free(member->value);
1125         free(member);
1126         return NC_ENOMEM;
1127     }
1128 
1129     /* Store the value for this member. */
1130     memcpy(member->value, value, size);
1131 
1132     /* Add object to list */
1133     nclistpush(parent->u.e.enum_member,member);
1134 
1135     return NC_NOERR;
1136 }
1137 
1138 /**
1139  * @internal Free up a field
1140  *
1141  * @param field Pointer to field info of field to delete.
1142  *
1143  * @return ::NC_NOERR No error.
1144  * @author Ed Hartnett
1145  */
1146 static void
field_free(NC_FIELD_INFO_T * field)1147 field_free(NC_FIELD_INFO_T *field)
1148 {
1149     /* Free some stuff. */
1150     if (field->hdr.name)
1151         free(field->hdr.name);
1152     if (field->dim_size)
1153         free(field->dim_size);
1154 
1155     /* Nc_Free the memory. */
1156     free(field);
1157 }
1158 
1159 /**
1160  * @internal Free allocated space for type information.
1161  *
1162  * @param type Pointer to type info struct.
1163  *
1164  * @return ::NC_NOERR No error.
1165  * @return ::NC_EHDFERR HDF5 error.
1166  * @author Ed Hartnett, Dennis Heimbigner
1167  */
1168 int
nc4_type_free(NC_TYPE_INFO_T * type)1169 nc4_type_free(NC_TYPE_INFO_T *type)
1170 {
1171     int i;
1172 
1173     assert(type && type->rc && type->hdr.name);
1174 
1175     /* Decrement the ref. count on the type */
1176     type->rc--;
1177 
1178     /* Release the type, if the ref. count drops to zero */
1179     if (type->rc == 0)
1180     {
1181         LOG((4, "%s: deleting type %s", __func__, type->hdr.name));
1182 
1183         /* Free the name. */
1184         free(type->hdr.name);
1185 
1186         /* Enums and compound types have lists of fields to clean up. */
1187         switch (type->nc_type_class)
1188         {
1189         case NC_COMPOUND:
1190         {
1191             NC_FIELD_INFO_T *field;
1192 
1193             /* Delete all the fields in this type (there will be some if its a
1194              * compound). */
1195             for(i=0;i<nclistlength(type->u.c.field);i++) {
1196                 field = nclistget(type->u.c.field,i);
1197                 field_free(field);
1198             }
1199             nclistfree(type->u.c.field);
1200         }
1201         break;
1202 
1203         case NC_ENUM:
1204         {
1205             NC_ENUM_MEMBER_INFO_T *enum_member;
1206 
1207             /* Delete all the enum_members, if any. */
1208             for(i=0;i<nclistlength(type->u.e.enum_member);i++) {
1209                 enum_member = nclistget(type->u.e.enum_member,i);
1210                 free(enum_member->value);
1211                 free(enum_member->name);
1212                 free(enum_member);
1213             }
1214             nclistfree(type->u.e.enum_member);
1215         }
1216         break;
1217 
1218         default:
1219             break;
1220         }
1221 
1222         /* Release any HDF5-specific type info. */
1223         if (type->format_type_info)
1224             free(type->format_type_info);
1225 
1226         /* Release the memory. */
1227         free(type);
1228     }
1229 
1230     return NC_NOERR;
1231 }
1232 
1233 /**
1234  * @internal Free memory of an attribute object
1235  *
1236  * @param att Pointer to attribute info struct.
1237  *
1238  * @return ::NC_NOERR No error.
1239  * @author Ed Hartnett
1240  */
1241 static int
att_free(NC_ATT_INFO_T * att)1242 att_free(NC_ATT_INFO_T *att)
1243 {
1244     int i;
1245 
1246     assert(att);
1247     LOG((3, "%s: name %s ", __func__, att->hdr.name));
1248 
1249     /* Free memory that was malloced to hold data for this
1250      * attribute. */
1251     if (att->data)
1252         free(att->data);
1253 
1254     /* Free the name. */
1255     if (att->hdr.name)
1256         free(att->hdr.name);
1257 
1258     /* If this is a string array attribute, delete all members of the
1259      * string array, then delete the array of pointers to strings. (The
1260      * array was filled with pointers by HDF5 when the att was read,
1261      * and memory for each string was allocated by HDF5. That's why I
1262      * use free and not nc_free, because the netCDF library didn't
1263      * allocate the memory that is being freed.) */
1264     if (att->stdata)
1265     {
1266         for (i = 0; i < att->len; i++)
1267             if(att->stdata[i])
1268                 free(att->stdata[i]);
1269         free(att->stdata);
1270     }
1271 
1272     /* If this att has vlen data, release it. */
1273     if (att->vldata)
1274     {
1275         for (i = 0; i < att->len; i++)
1276             nc_free_vlen(&att->vldata[i]);
1277         free(att->vldata);
1278     }
1279 
1280     /* Free any format-sepecific info. Some formats use this (ex. HDF5)
1281      * and some don't (ex. HDF4). So it may be NULL. */
1282     if (att->format_att_info)
1283         free(att->format_att_info);
1284 
1285     free(att);
1286     return NC_NOERR;
1287 }
1288 
1289 /**
1290  * @internal Delete a var, and free the memory. All HDF5 objects for
1291  * the var must be closed before this is called.
1292  *
1293  * @param var Pointer to the var info struct of var to delete.
1294  *
1295  * @return ::NC_NOERR No error.
1296  * @author Ed Hartnett, Dennis Heimbigner
1297  */
1298 static int
var_free(NC_VAR_INFO_T * var)1299 var_free(NC_VAR_INFO_T *var)
1300 {
1301     int i;
1302     int retval;
1303 
1304     assert(var);
1305     LOG((4, "%s: deleting var %s", __func__, var->hdr.name));
1306 
1307     /* First delete all the attributes attached to this var. */
1308     for (i = 0; i < ncindexsize(var->att); i++)
1309         if ((retval = att_free((NC_ATT_INFO_T *)ncindexith(var->att, i))))
1310             return retval;
1311     ncindexfree(var->att);
1312 
1313     /* Free some things that may be allocated. */
1314     if (var->chunksizes)
1315         free(var->chunksizes);
1316 
1317     if (var->hdf5_name)
1318         free(var->hdf5_name);
1319 
1320     if (var->hdr.name)
1321         free(var->hdr.name);
1322 
1323     if (var->dimids)
1324         free(var->dimids);
1325 
1326     if (var->dim)
1327         free(var->dim);
1328 
1329     /* Delete any fill value allocation. */
1330     if (var->fill_value)
1331         free(var->fill_value);
1332 
1333     /* Release type information */
1334     if (var->type_info)
1335         if ((retval = nc4_type_free(var->type_info)))
1336             return retval;
1337 
1338     /* Delete information about the attachment status of dimscales. */
1339     if (var->dimscale_attached)
1340         free(var->dimscale_attached);
1341 
1342     /* Release filter information. */
1343     freefilterlist(var->filters);
1344 
1345     /* Delete any format-specific info. */
1346     if (var->format_var_info)
1347         free(var->format_var_info);
1348 
1349     /* Delete the var. */
1350     free(var);
1351 
1352     return NC_NOERR;
1353 }
1354 
1355 /**
1356  * @internal  Delete a var, and free the memory.
1357  *
1358  * @param grp Pointer to the strct for the containing group.
1359  * @param var Pointer to the var info struct of var to delete.
1360  *
1361  * @return ::NC_NOERR No error.
1362  * @author Ed Hartnett, Dennis Heimbigner
1363  */
1364 int
nc4_var_list_del(NC_GRP_INFO_T * grp,NC_VAR_INFO_T * var)1365 nc4_var_list_del(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
1366 {
1367     int i;
1368 
1369     assert(var && grp);
1370 
1371     /* Remove from lists */
1372     i = ncindexfind(grp->vars, (NC_OBJ *)var);
1373     if (i >= 0)
1374         ncindexidel(grp->vars, i);
1375 
1376     return var_free(var);
1377 }
1378 
1379 /**
1380  * @internal Free a dim
1381  *
1382  * @param dim Pointer to dim info struct of type to delete.
1383  *
1384  * @return ::NC_NOERR No error.
1385  * @author Ed Hartnett, Ward Fisher
1386  */
1387 static int
dim_free(NC_DIM_INFO_T * dim)1388 dim_free(NC_DIM_INFO_T *dim)
1389 {
1390     assert(dim);
1391     LOG((4, "%s: deleting dim %s", __func__, dim->hdr.name));
1392 
1393     /* Free memory allocated for names. */
1394     if (dim->hdr.name)
1395         free(dim->hdr.name);
1396 
1397     /* Release any format-specific information. */
1398     if (dim->format_dim_info)
1399         free(dim->format_dim_info);
1400 
1401     free(dim);
1402     return NC_NOERR;
1403 }
1404 
1405 /**
1406  * @internal Free a dim and unlist it
1407  *
1408  * @param grp Pointer to dim's containing group
1409  * @param dim Pointer to dim info struct of type to delete.
1410  *
1411  * @return ::NC_NOERR No error.
1412  * @author Dennis Heimbigner
1413  */
1414 int
nc4_dim_list_del(NC_GRP_INFO_T * grp,NC_DIM_INFO_T * dim)1415 nc4_dim_list_del(NC_GRP_INFO_T *grp, NC_DIM_INFO_T *dim)
1416 {
1417     if (grp && dim)
1418     {
1419         int pos = ncindexfind(grp->dim, (NC_OBJ *)dim);
1420         if(pos >= 0)
1421             ncindexidel(grp->dim, pos);
1422     }
1423 
1424     return dim_free(dim);
1425 }
1426 
1427 /**
1428  * @internal Recursively delete the data for a group (and everything
1429  * it contains) in our internal metadata store.
1430  *
1431  * @param grp Pointer to group info struct.
1432  *
1433  * @return ::NC_NOERR No error.
1434  * @author Ed Hartnett, Dennis Heimbigner
1435  */
1436 int
nc4_rec_grp_del(NC_GRP_INFO_T * grp)1437 nc4_rec_grp_del(NC_GRP_INFO_T *grp)
1438 {
1439     int i;
1440     int retval;
1441 
1442     assert(grp);
1443     LOG((3, "%s: grp->name %s", __func__, grp->hdr.name));
1444 
1445     /* Recursively call this function for each child, if any, stopping
1446      * if there is an error. */
1447     for (i = 0; i < ncindexsize(grp->children); i++)
1448         if ((retval = nc4_rec_grp_del((NC_GRP_INFO_T *)ncindexith(grp->children,
1449                                                                   i))))
1450             return retval;
1451     ncindexfree(grp->children);
1452 
1453     /* Free attributes, but leave in parent list */
1454     for (i = 0; i < ncindexsize(grp->att); i++)
1455         if ((retval = att_free((NC_ATT_INFO_T *)ncindexith(grp->att, i))))
1456             return retval;
1457     ncindexfree(grp->att);
1458 
1459     /* Delete all vars. */
1460     for (i = 0; i < ncindexsize(grp->vars); i++) {
1461 	NC_VAR_INFO_T* v = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
1462         if ((retval = var_free(v)))
1463             return retval;
1464     }
1465     ncindexfree(grp->vars);
1466 
1467     /* Delete all dims, and free the list of dims. */
1468     for (i = 0; i < ncindexsize(grp->dim); i++)
1469         if ((retval = dim_free((NC_DIM_INFO_T *)ncindexith(grp->dim, i))))
1470             return retval;
1471     ncindexfree(grp->dim);
1472 
1473     /* Delete all types. */
1474     for (i = 0; i < ncindexsize(grp->type); i++)
1475         if ((retval = nc4_type_free((NC_TYPE_INFO_T *)ncindexith(grp->type, i))))
1476             return retval;
1477     ncindexfree(grp->type);
1478 
1479     /* Free the name. */
1480     free(grp->hdr.name);
1481 
1482     /* Release any format-specific information about this group. */
1483     if (grp->format_grp_info)
1484         free(grp->format_grp_info);
1485 
1486     /* Free up this group */
1487     free(grp);
1488 
1489     return NC_NOERR;
1490 }
1491 
1492 /**
1493  * @internal Remove a NC_ATT_INFO_T from an index.
1494  * This will nc_free the memory too.
1495  *
1496  * @param list Pointer to pointer of list.
1497  * @param att Pointer to attribute info struct.
1498  *
1499  * @return ::NC_NOERR No error.
1500  * @author Dennis Heimbigner
1501  */
1502 int
nc4_att_list_del(NCindex * list,NC_ATT_INFO_T * att)1503 nc4_att_list_del(NCindex *list, NC_ATT_INFO_T *att)
1504 {
1505     assert(att && list);
1506     ncindexidel(list, ((NC_OBJ *)att)->id);
1507     return att_free(att);
1508 }
1509 
1510 /**
1511  * @internal Free all resources and memory associated with a
1512  * NC_FILE_INFO_T. This is the same as nc4_nc4f_list_del(), except it
1513  * takes ncid. This function allows external dispatch layers, like
1514  * PIO, to manipulate the file list without needing to know about
1515  * internal netcdf structures.
1516  *
1517  * @param ncid The ncid of the file to release.
1518  *
1519  * @return ::NC_NOERR No error.
1520  * @return ::NC_EBADID Bad ncid.
1521  * @author Ed Hartnett
1522  */
1523 int
nc4_file_list_del(int ncid)1524 nc4_file_list_del(int ncid)
1525 {
1526     NC_FILE_INFO_T *h5;
1527     int retval;
1528 
1529     /* Find our metadata for this file. */
1530     if ((retval = nc4_find_grp_h5(ncid, NULL, &h5)))
1531         return retval;
1532     assert(h5);
1533 
1534     /* Delete the file resources. */
1535     if ((retval = nc4_nc4f_list_del(h5)))
1536         return retval;
1537 
1538     return NC_NOERR;
1539 }
1540 
1541 /**
1542  * @internal Free all resources and memory associated with a
1543  * NC_FILE_INFO_T.
1544  *
1545  * @param h5 Pointer to NC_FILE_INFO_T to be freed.
1546  *
1547  * @return ::NC_NOERR No error.
1548  * @author Ed Hartnett
1549  */
1550 int
nc4_nc4f_list_del(NC_FILE_INFO_T * h5)1551 nc4_nc4f_list_del(NC_FILE_INFO_T *h5)
1552 {
1553     int retval;
1554 
1555     assert(h5);
1556 
1557     /* Delete all the list contents for vars, dims, and atts, in each
1558      * group. */
1559     if ((retval = nc4_rec_grp_del(h5->root_grp)))
1560         return retval;
1561 
1562     /* Cleanup these (extra) lists of all dims, groups, and types. */
1563     nclistfree(h5->alldims);
1564     nclistfree(h5->allgroups);
1565     nclistfree(h5->alltypes);
1566 
1567     /* Free the NC_FILE_INFO_T struct. */
1568     free(h5);
1569 
1570     return NC_NOERR;
1571 }
1572 
1573 /**
1574  * @internal Normalize a UTF8 name. Put the result in norm_name, which
1575  * can be NC_MAX_NAME + 1 in size. This function makes sure the free()
1576  * gets called on the return from utf8proc_NFC, and also ensures that
1577  * the name is not too long.
1578  *
1579  * @param name Name to normalize.
1580  * @param norm_name The normalized name.
1581  *
1582  * @return ::NC_NOERR No error.
1583  * @return ::NC_EMAXNAME Name too long.
1584  * @author Dennis Heimbigner
1585  */
1586 int
nc4_normalize_name(const char * name,char * norm_name)1587 nc4_normalize_name(const char *name, char *norm_name)
1588 {
1589     char *temp_name;
1590     int stat = nc_utf8_normalize((const unsigned char *)name,(unsigned char **)&temp_name);
1591     if(stat != NC_NOERR)
1592         return stat;
1593     if (strlen(temp_name) > NC_MAX_NAME)
1594     {
1595         free(temp_name);
1596         return NC_EMAXNAME;
1597     }
1598     strcpy(norm_name, temp_name);
1599     free(temp_name);
1600     return NC_NOERR;
1601 }
1602 
1603 #ifdef ENABLE_SET_LOG_LEVEL
1604 
1605 /**
1606  * @internal Use this to set the global log level. Set it to
1607  * NC_TURN_OFF_LOGGING (-1) to turn off all logging. Set it to 0 to
1608  * show only errors, and to higher numbers to show more and more
1609  * logging details. If logging is not enabled with --enable-logging at
1610  * configure when building netCDF, this function will do nothing.
1611  * Note that it is possible to set the log level using the environment
1612  * variable named _NETCDF_LOG_LEVEL_ (e.g. _export NETCDF_LOG_LEVEL=4_).
1613  *
1614  * @param new_level The new logging level.
1615  *
1616  * @return ::NC_NOERR No error.
1617  * @author Ed Hartnett
1618  */
1619 int
nc_set_log_level(int new_level)1620 nc_set_log_level(int new_level)
1621 {
1622 #ifdef LOGGING
1623     /* Remember the new level. */
1624     nc_log_level = new_level;
1625     LOG((4, "log_level changed to %d", nc_log_level));
1626 #endif /*LOGGING */
1627     return 0;
1628 }
1629 #endif /* ENABLE_SET_LOG_LEVEL */
1630 
1631 #ifdef LOGGING
1632 #define MAX_NESTS 10
1633 /**
1634  * @internal Recursively print the metadata of a group.
1635  *
1636  * @param grp Pointer to group info struct.
1637  * @param tab_count Number of tabs.
1638  *
1639  * @return ::NC_NOERR No error.
1640  * @author Ed Hartnett, Dennis Heimbigner
1641  */
1642 static int
rec_print_metadata(NC_GRP_INFO_T * grp,int tab_count)1643 rec_print_metadata(NC_GRP_INFO_T *grp, int tab_count)
1644 {
1645     NC_ATT_INFO_T *att;
1646     NC_VAR_INFO_T *var;
1647     NC_DIM_INFO_T *dim;
1648     NC_TYPE_INFO_T *type;
1649     NC_FIELD_INFO_T *field;
1650     char tabs[MAX_NESTS+1] = "";
1651     char temp_string[10];
1652     int t, retval, d, i;
1653 
1654     /* Come up with a number of tabs relative to the group. */
1655     for (t = 0; t < tab_count && t < MAX_NESTS; t++)
1656         tabs[t] = '\t';
1657     tabs[t] = '\0';
1658 
1659     LOG((2, "%s GROUP - %s nc_grpid: %d nvars: %d natts: %d",
1660          tabs, grp->hdr.name, grp->hdr.id, ncindexsize(grp->vars), ncindexsize(grp->att)));
1661 
1662     for (i = 0; i < ncindexsize(grp->att); i++)
1663     {
1664         att = (NC_ATT_INFO_T *)ncindexith(grp->att, i);
1665         assert(att);
1666         LOG((2, "%s GROUP ATTRIBUTE - attnum: %d name: %s type: %d len: %d",
1667              tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len));
1668     }
1669 
1670     for (i = 0; i < ncindexsize(grp->dim); i++)
1671     {
1672         dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, i);
1673         assert(dim);
1674         LOG((2, "%s DIMENSION - dimid: %d name: %s len: %d unlimited: %d",
1675              tabs, dim->hdr.id, dim->hdr.name, dim->len, dim->unlimited));
1676     }
1677 
1678     for (i = 0; i < ncindexsize(grp->vars); i++)
1679     {
1680         int j;
1681         char storage_str[NC_MAX_NAME] = "";
1682         char *dims_string = NULL;
1683 
1684         var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
1685         assert(var);
1686         if (var->ndims > 0)
1687         {
1688             if (!(dims_string = malloc(sizeof(char) * var->ndims * 4)))
1689                 return NC_ENOMEM;
1690             strcpy(dims_string, "");
1691             for (d = 0; d < var->ndims; d++)
1692             {
1693                 sprintf(temp_string, " %d", var->dimids[d]);
1694                 strcat(dims_string, temp_string);
1695             }
1696         }
1697         if (!var->meta_read)
1698             strcat(storage_str, "unknown");
1699         else if (var->storage == NC_CONTIGUOUS)
1700             strcat(storage_str, "contiguous");
1701         else if (var->storage == NC_COMPACT)
1702             strcat(storage_str, "compact");
1703         else
1704             strcat(storage_str, "chunked");
1705         LOG((2, "%s VARIABLE - varid: %d name: %s ndims: %d dimscale: %d "
1706              "dimids:%s storage: %s", tabs, var->hdr.id, var->hdr.name,
1707              var->ndims, (int)var->dimscale,
1708              (dims_string ? dims_string : " -"), storage_str));
1709         for (j = 0; j < ncindexsize(var->att); j++)
1710         {
1711             att = (NC_ATT_INFO_T *)ncindexith(var->att, j);
1712             assert(att);
1713             LOG((2, "%s VAR ATTRIBUTE - attnum: %d name: %s type: %d len: %d",
1714                  tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len));
1715         }
1716         if (dims_string)
1717             free(dims_string);
1718     }
1719 
1720     for (i = 0; i < ncindexsize(grp->type); i++)
1721     {
1722         type = (NC_TYPE_INFO_T*)ncindexith(grp->type, i);
1723         assert(type);
1724         LOG((2, "%s TYPE - nc_typeid: %d size: %d committed: %d name: %s",
1725              tabs, type->hdr.id, type->size, (int)type->committed, type->hdr.name));
1726         /* Is this a compound type? */
1727         if (type->nc_type_class == NC_COMPOUND)
1728         {
1729             int j;
1730             LOG((3, "compound type"));
1731             for (j = 0; j < nclistlength(type->u.c.field); j++)
1732             {
1733                 field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, j);
1734                 LOG((4, "field %s offset %d nctype %d ndims %d", field->hdr.name,
1735                      field->offset, field->nc_typeid, field->ndims));
1736             }
1737         }
1738         else if (type->nc_type_class == NC_VLEN)
1739         {
1740             LOG((3, "VLEN type"));
1741             LOG((4, "base_nc_type: %d", type->u.v.base_nc_typeid));
1742         }
1743         else if (type->nc_type_class == NC_OPAQUE)
1744             LOG((3, "Opaque type"));
1745         else if (type->nc_type_class == NC_ENUM)
1746         {
1747             LOG((3, "Enum type"));
1748             LOG((4, "base_nc_type: %d", type->u.e.base_nc_typeid));
1749         }
1750         else
1751         {
1752             LOG((0, "Unknown class: %d", type->nc_type_class));
1753             return NC_EBADTYPE;
1754         }
1755     }
1756 
1757     /* Call self for each child of this group. */
1758     for (i = 0; i < ncindexsize(grp->children); i++)
1759         if ((retval = rec_print_metadata((NC_GRP_INFO_T *)ncindexith(grp->children, i),
1760                                          tab_count + 1)))
1761             return retval;
1762 
1763     return NC_NOERR;
1764 }
1765 
1766 /**
1767  * @internal Print out the internal metadata for a file. This is
1768  * useful to check that netCDF is working! Nonetheless, this function
1769  * will print nothing if logging is not set to at least two.
1770  *
1771  * @param Pointer to the file info struct.
1772  *
1773  * @return ::NC_NOERR No error.
1774  * @author Ed Hartnett
1775  */
1776 int
log_metadata_nc(NC_FILE_INFO_T * h5)1777 log_metadata_nc(NC_FILE_INFO_T *h5)
1778 {
1779     LOG((2, "*** NetCDF-4 Internal Metadata: int_ncid 0x%x ext_ncid 0x%x",
1780          h5->root_grp->nc4_info->controller->int_ncid,
1781          h5->root_grp->nc4_info->controller->ext_ncid));
1782     if (!h5)
1783     {
1784         LOG((2, "This is a netCDF-3 file."));
1785         return NC_NOERR;
1786     }
1787     LOG((2, "FILE - path: %s cmode: 0x%x parallel: %d redef: %d "
1788          "fill_mode: %d no_write: %d next_nc_grpid: %d", h5->root_grp->nc4_info->controller->path,
1789          h5->cmode, (int)h5->parallel, (int)h5->redef, h5->fill_mode, (int)h5->no_write,
1790          h5->next_nc_grpid));
1791     if(nc_log_level >= 2)
1792         return rec_print_metadata(h5->root_grp, 0);
1793     return NC_NOERR;
1794 }
1795 
1796 #endif /*LOGGING */
1797 
1798 /**
1799  * @internal Show the in-memory metadata for a netcdf file. This
1800  * function does nothing unless netCDF was built with
1801  * the configure option --enable-logging.
1802  *
1803  * @param ncid File and group ID.
1804  *
1805  * @return ::NC_NOERR No error.
1806  * @return ::NC_EBADID Bad ncid.
1807  * @author Ed Hartnett
1808  */
1809 int
NC4_show_metadata(int ncid)1810 NC4_show_metadata(int ncid)
1811 {
1812     int retval = NC_NOERR;
1813 #ifdef LOGGING
1814     NC_FILE_INFO_T *h5;
1815     int old_log_level = nc_log_level;
1816 
1817     /* Find file metadata. */
1818     if ((retval = nc4_find_grp_h5(ncid, NULL, &h5)))
1819         return retval;
1820 
1821     /* Log level must be 2 to see metadata. */
1822     nc_log_level = 2;
1823     retval = log_metadata_nc(h5);
1824     nc_log_level = old_log_level;
1825 #endif /*LOGGING*/
1826     return retval;
1827 }
1828 
1829 static void
freefilterlist(NClist * filters)1830 freefilterlist(NClist* filters)
1831 {
1832     int i;
1833     if(filters == NULL) return;
1834     for(i=0;i<nclistlength(filters);i++) {
1835 	NC_FILTER_SPEC_HDF5* f = nclistget(filters,i);
1836 	NC4_freefilterspec(f);
1837     }
1838     nclistfree(filters);
1839 }
1840