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