2018-01-31 23:44:33 +08:00
|
|
|
/* Copyright 2003-2018, University Corporation for Atmospheric
|
|
|
|
* Research. See the COPYRIGHT file for copying and redistribution
|
|
|
|
* conditions.
|
|
|
|
*/
|
2017-12-03 19:06:56 +08:00
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
* @internal
|
|
|
|
* Internal netcdf-4 functions.
|
|
|
|
*
|
|
|
|
* This file contains functions internal to the netcdf4 library. None of
|
|
|
|
* the functions in this file are exposed in the exetnal API. These
|
|
|
|
* functions all relate to the manipulation of netcdf-4's in-memory
|
|
|
|
* buffer of metadata information, i.e. the linked list of NC
|
|
|
|
* structs.
|
2017-12-05 03:21:14 +08:00
|
|
|
*
|
2018-06-01 05:25:40 +08:00
|
|
|
* @author Ed Hartnett, Dennis Heimbigner, Ward Fisher
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
#include "config.h"
|
|
|
|
#include "nc4internal.h"
|
|
|
|
#include "nc.h" /* from libsrc */
|
2010-11-10 06:53:03 +08:00
|
|
|
#include "ncdispatch.h" /* from libdispatch */
|
2017-02-17 05:27:54 +08:00
|
|
|
#include "ncutf8.h"
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* These are the default chunk cache sizes for HDF5 files created or
|
|
|
|
* opened with netCDF-4. */
|
|
|
|
extern size_t nc4_chunk_cache_size;
|
|
|
|
extern size_t nc4_chunk_cache_nelems;
|
|
|
|
extern float nc4_chunk_cache_preemption;
|
|
|
|
|
|
|
|
#ifdef LOGGING
|
|
|
|
/* This is the severity level of messages which will be logged. Use
|
|
|
|
severity 0 for errors, 1 for important log messages, 2 for less
|
|
|
|
important, etc. */
|
2017-03-09 08:01:10 +08:00
|
|
|
int nc_log_level = NC_TURN_OFF_LOGGING;
|
2010-06-03 21:24:43 +08:00
|
|
|
#endif /* LOGGING */
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2017-12-05 03:21:14 +08:00
|
|
|
* @internal Check and normalize and name.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param name Name to normalize.
|
|
|
|
* @param norm_name The normalized name.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_EMAXNAME Name too long.
|
2018-01-18 22:36:52 +08:00
|
|
|
* @return ::NC_EINVAL NULL given for name.
|
2018-08-07 00:16:49 +08:00
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
2017-12-03 22:57:21 +08:00
|
|
|
* @author Dennis Heimbigner
|
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
|
|
|
nc4_check_name(const char *name, char *norm_name)
|
|
|
|
{
|
|
|
|
char *temp;
|
|
|
|
int retval;
|
2018-01-31 23:44:33 +08:00
|
|
|
|
2018-01-18 22:36:52 +08:00
|
|
|
assert(norm_name);
|
2018-01-31 23:44:33 +08:00
|
|
|
|
2018-01-18 22:36:52 +08:00
|
|
|
/* Check for NULL. */
|
|
|
|
if (!name)
|
|
|
|
return NC_EINVAL;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Make sure this is a valid netcdf name. This should be done
|
|
|
|
* before the name is normalized, because it gives better error
|
|
|
|
* codes for bad utf8 strings. */
|
|
|
|
if ((retval = NC_check_name(name)))
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
/* Normalize the name. */
|
2018-08-07 00:16:49 +08:00
|
|
|
if ((retval = nc_utf8_normalize((const unsigned char *)name,
|
|
|
|
(unsigned char **)&temp)))
|
2017-02-17 05:27:54 +08:00
|
|
|
return retval;
|
2017-08-09 03:18:53 +08:00
|
|
|
|
2018-08-07 00:16:49 +08:00
|
|
|
/* Check length of normalized name. */
|
|
|
|
if (strlen(temp) > NC_MAX_NAME)
|
|
|
|
{
|
2017-12-05 03:21:14 +08:00
|
|
|
free(temp);
|
|
|
|
return NC_EMAXNAME;
|
2017-08-09 03:18:53 +08:00
|
|
|
}
|
|
|
|
|
2018-08-07 00:16:49 +08:00
|
|
|
/* Copy the normalized name. */
|
2010-06-03 21:24:43 +08:00
|
|
|
strcpy(norm_name, temp);
|
|
|
|
free(temp);
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Given an NC pointer, add the necessary stuff for a
|
2017-12-05 03:21:14 +08:00
|
|
|
* netcdf-4 file.
|
|
|
|
*
|
|
|
|
* @param nc Pointer to file's NC struct.
|
|
|
|
* @param path The file name of the new file.
|
|
|
|
* @param mode The mode flag.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2012-09-07 03:44:03 +08:00
|
|
|
nc4_nc4f_list_add(NC *nc, const char *path, int mode)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-06-22 21:08:09 +08:00
|
|
|
NC_FILE_INFO_T *h5;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2012-09-07 03:44:03 +08:00
|
|
|
assert(nc && !NC4_DATA(nc) && path);
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2012-09-07 03:44:03 +08:00
|
|
|
/* We need to malloc and
|
2010-06-03 21:24:43 +08:00
|
|
|
initialize the substructure NC_HDF_FILE_INFO_T. */
|
2018-06-22 21:08:09 +08:00
|
|
|
if (!(h5 = calloc(1, sizeof(NC_FILE_INFO_T))))
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_ENOMEM;
|
2018-08-07 00:16:49 +08:00
|
|
|
nc->dispatchdata = h5;
|
2012-09-07 03:44:03 +08:00
|
|
|
h5->controller = nc;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Hang on to cmode, and note that we're in define mode. */
|
|
|
|
h5->cmode = mode | NC_INDEF;
|
|
|
|
|
|
|
|
/* The next_typeid needs to be set beyond the end of our atomic
|
|
|
|
* types. */
|
|
|
|
h5->next_typeid = NC_FIRSTUSERTYPEID;
|
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
h5->alldims = nclistnew();
|
|
|
|
h5->alltypes = nclistnew();
|
|
|
|
h5->allgroups = nclistnew();
|
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* There's always at least one open group - the root
|
|
|
|
* group. Allocate space for one group's worth of information. Set
|
|
|
|
* its hdf id, name, and a pointer to it's file structure. */
|
2018-08-07 00:16:49 +08:00
|
|
|
return nc4_grp_list_add(h5, NULL, NC_GROUP_NAME, &h5->root_grp);
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Given an ncid, find the relevant group and return a
|
|
|
|
* pointer to it, return an error of this is not a netcdf-4 file (or
|
2017-12-05 03:21:14 +08:00
|
|
|
* if strict nc3 is turned on for this file.)
|
|
|
|
*
|
|
|
|
* @param ncid File and group ID.
|
|
|
|
* @param grp Pointer that gets pointer to group info struct.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
2017-12-05 03:21:14 +08:00
|
|
|
* @return ::NC_ENOTNC4 Not a netCDF-4 file.
|
|
|
|
* @return ::NC_ESTRICTNC3 Not allowed for classic model.
|
2017-12-03 22:57:21 +08:00
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
|
|
|
nc4_find_nc4_grp(int ncid, NC_GRP_INFO_T **grp)
|
|
|
|
{
|
2018-06-22 21:08:09 +08:00
|
|
|
NC_FILE_INFO_T* h5;
|
2012-09-07 03:44:03 +08:00
|
|
|
NC *f = nc4_find_nc_file(ncid,&h5);
|
2010-06-03 21:24:43 +08:00
|
|
|
if(f == NULL) return NC_EBADID;
|
|
|
|
|
|
|
|
/* No netcdf-3 files allowed! */
|
2012-09-07 03:44:03 +08:00
|
|
|
if (!h5) return NC_ENOTNC4;
|
|
|
|
assert(h5->root_grp);
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* This function demands netcdf-4 files without strict nc3
|
|
|
|
* rules.*/
|
2012-09-07 03:44:03 +08:00
|
|
|
if (h5->cmode & NC_CLASSIC_MODEL) return NC_ESTRICTNC3;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* If we can't find it, the grp id part of ncid is bad. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if (!(*grp = nc4_rec_find_grp(h5, (ncid & GRP_ID_MASK))))
|
2010-09-24 23:55:54 +08:00
|
|
|
return NC_EBADID;
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Given an ncid, find the relevant group and return a
|
|
|
|
* pointer to it, also set a pointer to the nc4_info struct of the
|
2017-12-05 03:21:14 +08:00
|
|
|
* related file. For netcdf-3 files, *h5 will be set to NULL.
|
|
|
|
*
|
|
|
|
* @param ncid File and group ID.
|
|
|
|
* @param grpp Pointer that gets pointer to group info struct.
|
|
|
|
* @param h5p Pointer to HDF5 file struct.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
2017-12-05 03:21:14 +08:00
|
|
|
* @return ::NC_EBADID Bad ncid.
|
2017-12-03 22:57:21 +08:00
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-06-22 21:08:09 +08:00
|
|
|
nc4_find_grp_h5(int ncid, NC_GRP_INFO_T **grpp, NC_FILE_INFO_T **h5p)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-06-22 21:08:09 +08:00
|
|
|
NC_FILE_INFO_T *h5;
|
2017-12-05 03:21:14 +08:00
|
|
|
NC_GRP_INFO_T *grp;
|
|
|
|
NC *f = nc4_find_nc_file(ncid,&h5);
|
|
|
|
if(f == NULL) return NC_EBADID;
|
|
|
|
if (h5) {
|
|
|
|
assert(h5->root_grp);
|
|
|
|
/* If we can't find it, the grp id part of ncid is bad. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if (!(grp = nc4_rec_find_grp(h5, (ncid & GRP_ID_MASK))))
|
2017-12-05 03:21:14 +08:00
|
|
|
return NC_EBADID;
|
|
|
|
h5 = (grp)->nc4_info;
|
|
|
|
assert(h5);
|
|
|
|
} else {
|
|
|
|
h5 = NULL;
|
|
|
|
grp = NULL;
|
|
|
|
}
|
|
|
|
if(h5p) *h5p = h5;
|
|
|
|
if(grpp) *grpp = grp;
|
|
|
|
return NC_NOERR;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 19:06:56 +08:00
|
|
|
/**
|
2017-12-05 03:21:14 +08:00
|
|
|
* @internal Find info for this file and group, and set pointer to each.
|
2017-12-03 19:06:56 +08:00
|
|
|
*
|
|
|
|
* @param ncid File and group ID.
|
|
|
|
* @param nc Pointer that gets a pointer to the file's NC struct.
|
|
|
|
* @param grpp Pointer that gets a pointer to the group struct.
|
|
|
|
* @param h5p Pointer that gets HDF5 file struct.
|
|
|
|
*
|
2017-12-03 22:57:21 +08:00
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_EBADID Bad ncid.
|
2017-12-05 03:21:14 +08:00
|
|
|
* @author Ed Hartnett
|
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2012-09-07 03:44:03 +08:00
|
|
|
nc4_find_nc_grp_h5(int ncid, NC **nc, NC_GRP_INFO_T **grpp,
|
2018-06-22 21:08:09 +08:00
|
|
|
NC_FILE_INFO_T **h5p)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2017-12-05 03:21:14 +08:00
|
|
|
NC_GRP_INFO_T *grp;
|
2018-06-22 21:08:09 +08:00
|
|
|
NC_FILE_INFO_T* h5;
|
2017-12-05 03:21:14 +08:00
|
|
|
NC *f = nc4_find_nc_file(ncid,&h5);
|
|
|
|
|
|
|
|
if(f == NULL) return NC_EBADID;
|
2018-03-17 01:46:18 +08:00
|
|
|
if(nc) *nc = f;
|
2017-12-05 03:21:14 +08:00
|
|
|
|
|
|
|
if (h5) {
|
|
|
|
assert(h5->root_grp);
|
|
|
|
/* If we can't find it, the grp id part of ncid is bad. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if (!(grp = nc4_rec_find_grp(h5, (ncid & GRP_ID_MASK))))
|
2017-12-05 03:21:14 +08:00
|
|
|
return NC_EBADID;
|
|
|
|
|
|
|
|
h5 = (grp)->nc4_info;
|
|
|
|
assert(h5);
|
|
|
|
} else {
|
|
|
|
h5 = NULL;
|
|
|
|
grp = NULL;
|
|
|
|
}
|
|
|
|
if(h5p) *h5p = h5;
|
|
|
|
if(grpp) *grpp = grp;
|
|
|
|
return NC_NOERR;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 19:06:56 +08:00
|
|
|
/**
|
2018-06-22 21:08:09 +08:00
|
|
|
* @internal Use NC_FILE_INFO_T->allgroups to locate a group id.
|
2017-12-03 19:06:56 +08:00
|
|
|
*
|
2018-03-17 01:46:18 +08:00
|
|
|
* @param h5 Pointer to file info
|
2017-12-03 22:11:51 +08:00
|
|
|
* @param target_nc_grpid Group ID to be found.
|
2017-12-03 19:06:56 +08:00
|
|
|
*
|
2017-12-03 22:57:21 +08:00
|
|
|
* @return Pointer to group info struct, or NULL if not found.
|
2017-12-03 19:06:56 +08:00
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
NC_GRP_INFO_T *
|
2018-06-22 21:08:09 +08:00
|
|
|
nc4_rec_find_grp(NC_FILE_INFO_T *h5, int target_nc_grpid)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
NC_GRP_INFO_T *g;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
assert(h5);
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Is this the group we are searching for? */
|
2018-03-17 01:46:18 +08:00
|
|
|
g = nclistget(h5->allgroups,target_nc_grpid);
|
|
|
|
return g;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 19:06:56 +08:00
|
|
|
/**
|
|
|
|
* @internal Given an ncid and varid, get pointers to the group and var
|
2017-12-05 03:21:14 +08:00
|
|
|
* metadata.
|
2017-12-03 19:06:56 +08:00
|
|
|
*
|
|
|
|
* @param nc Pointer to file's NC struct.
|
|
|
|
* @param ncid File ID.
|
|
|
|
* @param varid Variable ID.
|
|
|
|
* @param grp Pointer that gets pointer to group info.
|
|
|
|
* @param var Pointer that gets pointer to var info.
|
|
|
|
*
|
2017-12-03 22:57:21 +08:00
|
|
|
* @return ::NC_NOERR No error.
|
2017-12-03 19:06:56 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2014-12-29 13:04:48 +08:00
|
|
|
nc4_find_g_var_nc(NC *nc, int ncid, int varid,
|
2017-12-05 03:21:14 +08:00
|
|
|
NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-06-22 21:08:09 +08:00
|
|
|
NC_FILE_INFO_T* h5 = NC4_DATA(nc);
|
2012-09-07 03:44:03 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Find the group info. */
|
2012-09-07 03:44:03 +08:00
|
|
|
assert(grp && var && h5 && h5->root_grp);
|
2018-03-17 01:46:18 +08:00
|
|
|
*grp = nc4_rec_find_grp(h5, (ncid & GRP_ID_MASK));
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2013-04-17 07:02:54 +08:00
|
|
|
/* It is possible for *grp to be NULL. If it is,
|
|
|
|
return an error. */
|
2014-12-29 13:04:48 +08:00
|
|
|
if(*grp == NULL)
|
2017-12-05 03:21:14 +08:00
|
|
|
return NC_EBADID;
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Find the var info. */
|
2018-03-17 01:46:18 +08:00
|
|
|
(*var) = (NC_VAR_INFO_T*)ncindexith((*grp)->vars,varid);
|
|
|
|
if((*var) == NULL)
|
2018-08-08 05:17:40 +08:00
|
|
|
return NC_ENOTVAR;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 19:06:56 +08:00
|
|
|
/**
|
2017-12-05 03:21:14 +08:00
|
|
|
* @internal Find a dim in a grp (or its parents).
|
2017-12-03 19:06:56 +08:00
|
|
|
*
|
|
|
|
* @param grp Pointer to group info struct.
|
|
|
|
* @param dimid Dimension ID to find.
|
|
|
|
* @param dim Pointer that gets pointer to dim info if found.
|
2018-04-24 06:38:08 +08:00
|
|
|
* @param dim_grp Pointer that gets pointer to group info of group that contains dimension.
|
2017-12-05 03:21:14 +08:00
|
|
|
*
|
2017-12-03 22:57:21 +08:00
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_EBADDIM Dimension not found.
|
2017-12-03 19:06:56 +08:00
|
|
|
* @author Ed Hartnett
|
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
|
|
|
nc4_find_dim(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T **dim,
|
2017-12-05 03:21:14 +08:00
|
|
|
NC_GRP_INFO_T **dim_grp)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
NC_GRP_INFO_T *g;
|
|
|
|
int found = 0;
|
2018-06-22 21:08:09 +08:00
|
|
|
NC_FILE_INFO_T* h5 = grp->nc4_info;
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
assert(h5 && grp && dim);
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Find the dim info. */
|
2018-03-17 01:46:18 +08:00
|
|
|
(*dim) = nclistget(h5->alldims,dimid);
|
2018-06-01 05:25:40 +08:00
|
|
|
if((*dim) == NULL)
|
2018-08-08 05:17:40 +08:00
|
|
|
return NC_EBADDIM;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
/* Redundant: Verify that this dim is in fact in the group or its parent */
|
|
|
|
for (found=0, g = grp; g ; g = g->parent) {
|
2018-08-08 05:17:40 +08:00
|
|
|
if(g == (*dim)->container) {found = 1; break;}
|
2018-03-17 01:46:18 +08:00
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
/* If we didn't find it, return an error. */
|
2018-03-17 01:46:18 +08:00
|
|
|
assert(found);
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Give the caller the group the dimension is in. */
|
|
|
|
if (dim_grp)
|
2018-03-17 01:46:18 +08:00
|
|
|
*dim_grp = (*dim)->container;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2017-12-05 03:21:14 +08:00
|
|
|
* @internal Find a var (by name) in a grp.
|
|
|
|
*
|
|
|
|
* @param grp Pointer to group info.
|
|
|
|
* @param name Name of var to find.
|
|
|
|
* @param var Pointer that gets pointer to var info struct, if found.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2013-12-01 13:20:28 +08:00
|
|
|
int
|
|
|
|
nc4_find_var(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var)
|
|
|
|
{
|
|
|
|
assert(grp && var && name);
|
|
|
|
|
|
|
|
/* Find the var info. */
|
2018-03-17 01:46:18 +08:00
|
|
|
*var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name);
|
2013-12-01 13:20:28 +08:00
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-03-17 01:46:18 +08:00
|
|
|
* @internal Locate netCDF type by name.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
2017-12-05 03:21:14 +08:00
|
|
|
* @param start_grp Pointer to starting group info.
|
|
|
|
* @param name Name of type to find.
|
|
|
|
*
|
|
|
|
* @return Pointer to type info, or NULL if not found.
|
2017-12-03 22:57:21 +08:00
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
NC_TYPE_INFO_T *
|
2014-02-12 07:12:08 +08:00
|
|
|
nc4_rec_find_named_type(NC_GRP_INFO_T *start_grp, char *name)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
|
|
|
NC_GRP_INFO_T *g;
|
|
|
|
NC_TYPE_INFO_T *type, *res;
|
2018-03-17 01:46:18 +08:00
|
|
|
int i;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
assert(start_grp);
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Does this group have the type we are searching for? */
|
2018-03-17 01:46:18 +08:00
|
|
|
type = (NC_TYPE_INFO_T*)ncindexlookup(start_grp->type,name);
|
|
|
|
if(type != NULL)
|
2018-08-08 05:17:40 +08:00
|
|
|
return type;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2014-02-12 07:12:08 +08:00
|
|
|
/* Search subgroups. */
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(start_grp->children);i++) {
|
|
|
|
g = (NC_GRP_INFO_T*)ncindexith(start_grp->children,i);
|
|
|
|
if(g == NULL) continue;
|
|
|
|
if ((res = nc4_rec_find_named_type(g, name)))
|
2018-08-08 05:17:40 +08:00
|
|
|
return res;
|
2018-03-17 01:46:18 +08:00
|
|
|
}
|
2014-02-12 07:12:08 +08:00
|
|
|
/* Can't find it. Oh, woe is me! */
|
2010-06-03 21:24:43 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-01-31 23:44:33 +08:00
|
|
|
* @internal Recursively hunt for a netCDF type id.
|
2018-03-17 01:46:18 +08:00
|
|
|
* Note using h5->alltypes, we no longer do recursion
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
2018-03-17 01:46:18 +08:00
|
|
|
* @param h5 the root file
|
2017-12-05 03:21:14 +08:00
|
|
|
* @param target_nc_typeid NetCDF type ID to find.
|
|
|
|
*
|
|
|
|
* @return Pointer to type info, or NULL if not found.
|
2017-12-03 22:57:21 +08:00
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
NC_TYPE_INFO_T *
|
2018-06-22 21:08:09 +08:00
|
|
|
nc4_rec_find_nc_type(NC_FILE_INFO_T *h5, nc_type target_nc_typeid)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
assert(h5);
|
|
|
|
return nclistget(h5->alltypes, target_nc_typeid);
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Use a netCDF typeid to find a type in a type_list.
|
|
|
|
*
|
|
|
|
* @param h5 Pointer to HDF5 file info struct.
|
|
|
|
* @param typeid The netCDF type ID.
|
|
|
|
* @param type Pointer to pointer to the list of type info structs.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_EINVAL Invalid input.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-06-22 21:08:09 +08:00
|
|
|
nc4_find_type(const NC_FILE_INFO_T *h5, nc_type typeid, NC_TYPE_INFO_T **type)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
|
|
|
if (typeid < 0 || !type)
|
|
|
|
return NC_EINVAL;
|
|
|
|
*type = NULL;
|
|
|
|
|
|
|
|
/* Atomic types don't have associated NC_TYPE_INFO_T struct, just
|
|
|
|
* return NOERR. */
|
|
|
|
if (typeid <= NC_STRING)
|
|
|
|
return NC_NOERR;
|
|
|
|
|
|
|
|
/* Find the type. */
|
2018-03-17 01:46:18 +08:00
|
|
|
*type = (NC_TYPE_INFO_T*)nclistget(h5->alltypes,typeid);
|
|
|
|
if((*type) == NULL)
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_EBADTYPID;
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-03-17 01:46:18 +08:00
|
|
|
* @internal Given a group, find an att. If name is provided, use that,
|
|
|
|
* otherwise use the attnum.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param grp Pointer to group info struct.
|
|
|
|
* @param varid Variable ID.
|
|
|
|
* @param name Name to of attribute.
|
|
|
|
* @param attnum Number of attribute.
|
|
|
|
* @param att Pointer to pointer that gets attribute info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_ENOTVAR Variable not found.
|
|
|
|
* @return ::NC_ENOTATT Attribute not found.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
|
|
|
nc4_find_grp_att(NC_GRP_INFO_T *grp, int varid, const char *name, int attnum,
|
2017-12-05 03:21:14 +08:00
|
|
|
NC_ATT_INFO_T **att)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
|
|
|
NC_VAR_INFO_T *var;
|
2018-03-17 01:46:18 +08:00
|
|
|
NCindex* attlist = NULL;
|
2018-06-19 21:39:42 +08:00
|
|
|
int retval;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
assert(grp && grp->hdr.name);
|
2014-12-29 13:04:48 +08:00
|
|
|
LOG((4, "nc4_find_grp_att: grp->name %s varid %d name %s attnum %d",
|
2018-03-17 01:46:18 +08:00
|
|
|
grp->hdr.name, varid, name, attnum));
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Get either the global or a variable attribute list. */
|
|
|
|
if (varid == NC_GLOBAL)
|
2018-06-19 21:39:42 +08:00
|
|
|
{
|
2010-06-03 21:24:43 +08:00
|
|
|
attlist = grp->att;
|
2018-06-19 21:39:42 +08:00
|
|
|
|
|
|
|
/* Do we need to read the atts? */
|
|
|
|
if (grp->atts_not_read)
|
2018-07-22 00:43:36 +08:00
|
|
|
if ((retval = nc4_read_atts(grp, NULL)))
|
2018-06-19 21:39:42 +08:00
|
|
|
return retval;
|
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
else
|
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,varid);
|
2016-07-07 22:28:24 +08:00
|
|
|
if (!var) return NC_ENOTVAR;
|
2018-06-19 22:25:19 +08:00
|
|
|
|
|
|
|
/* Do we need to read the var attributes? */
|
|
|
|
if (var->atts_not_read)
|
2018-07-22 00:43:36 +08:00
|
|
|
if ((retval = nc4_read_atts(grp, var)))
|
2018-06-19 22:25:19 +08:00
|
|
|
return retval;
|
|
|
|
|
2016-07-07 22:28:24 +08:00
|
|
|
attlist = var->att;
|
2018-03-17 01:46:18 +08:00
|
|
|
assert(var->hdr.id == varid);
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now find the attribute by name or number. If a name is provided,
|
|
|
|
* ignore the attnum. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if(attlist) {
|
|
|
|
NC_ATT_INFO_T* a;
|
|
|
|
if(name != NULL)
|
2018-08-08 05:17:40 +08:00
|
|
|
a = (NC_ATT_INFO_T*)ncindexlookup(attlist,name);
|
2018-03-17 01:46:18 +08:00
|
|
|
else
|
2018-08-08 05:17:40 +08:00
|
|
|
a = (NC_ATT_INFO_T*)ncindexith(attlist,attnum);
|
2018-03-17 01:46:18 +08:00
|
|
|
if(a != NULL) {
|
2018-08-08 05:17:40 +08:00
|
|
|
*att = a;
|
|
|
|
return NC_NOERR;
|
2017-12-05 03:21:14 +08:00
|
|
|
}
|
2018-06-01 05:25:40 +08:00
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* If we get here, we couldn't find the attribute. */
|
|
|
|
return NC_ENOTATT;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Given an ncid, varid, and name or attnum, find and return
|
|
|
|
* pointer to NC_ATT_INFO_T metadata.
|
|
|
|
*
|
|
|
|
* @param ncid File and group ID.
|
|
|
|
* @param varid Variable ID.
|
|
|
|
* @param name Name to of attribute.
|
|
|
|
* @param attnum Number of attribute.
|
|
|
|
* @param att Pointer to pointer that gets attribute info struct.
|
2018-03-17 01:46:18 +08:00
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_ENOTVAR Variable not found.
|
|
|
|
* @return ::NC_ENOTATT Attribute not found.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
|
|
|
nc4_find_nc_att(int ncid, int varid, const char *name, int attnum,
|
2017-12-05 03:21:14 +08:00
|
|
|
NC_ATT_INFO_T **att)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
|
|
|
NC_GRP_INFO_T *grp;
|
2018-06-22 21:08:09 +08:00
|
|
|
NC_FILE_INFO_T *h5;
|
2010-06-03 21:24:43 +08:00
|
|
|
int retval;
|
|
|
|
|
2014-12-29 13:04:48 +08:00
|
|
|
LOG((4, "nc4_find_nc_att: ncid 0x%x varid %d name %s attnum %d",
|
2017-12-05 03:21:14 +08:00
|
|
|
ncid, varid, name, attnum));
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Find info for this file and group, and set pointer to each. */
|
|
|
|
if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
|
|
|
|
return retval;
|
|
|
|
assert(grp && h5);
|
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
return nc4_find_grp_att(grp,varid,name,attnum,att);
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Given an id, walk the list and find the appropriate NC.
|
|
|
|
*
|
|
|
|
* @param ext_ncid File/group ID to find.
|
|
|
|
* @param h5p Pointer to pointer that gets the HDF5 file info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett, Dennis Heimbigner
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2012-09-07 03:44:03 +08:00
|
|
|
NC*
|
2018-06-22 21:08:09 +08:00
|
|
|
nc4_find_nc_file(int ext_ncid, NC_FILE_INFO_T** h5p)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2012-09-07 03:44:03 +08:00
|
|
|
NC* nc;
|
|
|
|
int stat;
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
2012-09-07 03:44:03 +08:00
|
|
|
stat = NC_check_id(ext_ncid,&nc);
|
|
|
|
if(stat != NC_NOERR)
|
2017-12-05 03:21:14 +08:00
|
|
|
nc = NULL;
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2013-08-06 04:36:33 +08:00
|
|
|
if(nc)
|
2018-06-22 21:08:09 +08:00
|
|
|
if(h5p) *h5p = (NC_FILE_INFO_T*)nc->dispatchdata;
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2012-09-07 03:44:03 +08:00
|
|
|
return nc;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
/**
|
|
|
|
* @internal Add NC_OBJ to allXXX lists in a file
|
|
|
|
*
|
|
|
|
* @param file Pointer to the containing file
|
|
|
|
* @param obj Pointer to object to add.
|
|
|
|
*
|
|
|
|
* @author Dennis Heimbigner
|
|
|
|
*/
|
|
|
|
static void
|
2018-06-22 21:08:09 +08:00
|
|
|
obj_track(NC_FILE_INFO_T* file, NC_OBJ* obj)
|
2018-03-17 01:46:18 +08:00
|
|
|
{
|
2018-08-08 05:17:40 +08:00
|
|
|
NClist* list = NULL;
|
|
|
|
/* record the object in the file */
|
|
|
|
switch (obj->sort) {
|
|
|
|
case NCDIM: list = file->alldims; break;
|
|
|
|
case NCTYP: list = file->alltypes; break;
|
|
|
|
case NCGRP: list = file->allgroups; break;
|
|
|
|
default:
|
|
|
|
assert(NC_FALSE);
|
|
|
|
}
|
|
|
|
/* Insert at the appropriate point in the list */
|
|
|
|
nclistset(list,obj->id,obj);
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-07-29 18:57:25 +08:00
|
|
|
* @internal Create a new variable and insert into relevant
|
|
|
|
* lists. Dimensionality info need not be known.
|
2018-03-17 01:46:18 +08:00
|
|
|
*
|
|
|
|
* @param grp the containing group
|
|
|
|
* @param name the name for the new variable
|
|
|
|
* @param var Pointer in which to return a pointer to the new var.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param var Pointer to pointer that gets variable info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-07-29 18:57:25 +08:00
|
|
|
nc4_var_list_add2(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-07-29 18:57:25 +08:00
|
|
|
NC_VAR_INFO_T *new_var;
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
|
|
|
/* Allocate storage for new variable. */
|
|
|
|
if (!(new_var = calloc(1, sizeof(NC_VAR_INFO_T))))
|
2018-07-29 18:57:25 +08:00
|
|
|
return NC_ENOMEM;
|
2018-03-17 01:46:18 +08:00
|
|
|
new_var->hdr.sort = NCVAR;
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
|
|
|
/* These are the HDF5-1.8.4 defaults. */
|
|
|
|
new_var->chunk_cache_size = nc4_chunk_cache_size;
|
|
|
|
new_var->chunk_cache_nelems = nc4_chunk_cache_nelems;
|
|
|
|
new_var->chunk_cache_preemption = nc4_chunk_cache_preemption;
|
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
/* Now fill in the values in the var info structure. */
|
|
|
|
new_var->hdr.id = ncindexsize(grp->vars);
|
|
|
|
if (!(new_var->hdr.name = strdup(name)))
|
2018-07-29 18:57:25 +08:00
|
|
|
return NC_ENOMEM;
|
|
|
|
new_var->hdr.hashkey = NC_hashmapkey(new_var->hdr.name,
|
|
|
|
strlen(new_var->hdr.name));
|
2018-03-17 01:46:18 +08:00
|
|
|
|
2018-07-29 18:57:25 +08:00
|
|
|
/* Create an indexed list for the attributes. */
|
2018-03-17 01:46:18 +08:00
|
|
|
new_var->att = ncindexnew(0);
|
|
|
|
|
|
|
|
/* Officially track it */
|
2018-07-29 18:57:25 +08:00
|
|
|
ncindexadd(grp->vars, (NC_OBJ *)new_var);
|
2018-03-17 01:46:18 +08:00
|
|
|
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
/* Set the var pointer, if one was given */
|
|
|
|
if (var)
|
|
|
|
*var = new_var;
|
|
|
|
|
2018-07-29 18:57:25 +08:00
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal Set the number of dims in an NC_VAR_INFO_T struct.
|
|
|
|
*
|
|
|
|
* @param var Pointer to the var.
|
|
|
|
* @param ndims Number of dimensions for this var.
|
|
|
|
*
|
|
|
|
* @param var Pointer to pointer that gets variable info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
|
|
|
* @author Ed Hartnett
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
nc4_var_set_ndims(NC_VAR_INFO_T *var, int ndims)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* Remember the number of dimensions. */
|
|
|
|
var->ndims = ndims;
|
|
|
|
|
|
|
|
/* Allocate space for dimension information. */
|
|
|
|
if (ndims)
|
|
|
|
{
|
|
|
|
if (!(var->dim = calloc(ndims, sizeof(NC_DIM_INFO_T *))))
|
|
|
|
return NC_ENOMEM;
|
|
|
|
if (!(var->dimids = calloc(ndims, sizeof(int))))
|
|
|
|
return NC_ENOMEM;
|
|
|
|
|
|
|
|
/* Initialize dimids to illegal values (-1). See the comment
|
|
|
|
in nc4hdf.c#nc4_rec_match_dimscales. */
|
|
|
|
memset(var->dimids, -1, ndims * sizeof(int));
|
2018-03-17 01:46:18 +08:00
|
|
|
}
|
|
|
|
|
2018-07-29 18:57:25 +08:00
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal Create a new variable and insert int relevant list.
|
|
|
|
*
|
|
|
|
* @param grp the containing group
|
|
|
|
* @param name the name for the new variable
|
|
|
|
* @param ndims the rank of the new variable
|
|
|
|
* @param var Pointer in which to return a pointer to the new var.
|
|
|
|
*
|
|
|
|
* @param var Pointer to pointer that gets variable info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
|
|
|
* @author Ed Hartnett
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
nc4_var_list_add(NC_GRP_INFO_T* grp, const char* name, int ndims,
|
|
|
|
NC_VAR_INFO_T **var)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
if ((retval = nc4_var_list_add2(grp, name, var)))
|
|
|
|
return retval;
|
|
|
|
if ((retval = nc4_var_set_ndims(*var, ndims)))
|
|
|
|
return retval;
|
2018-03-17 01:46:18 +08:00
|
|
|
|
2018-07-29 18:57:25 +08:00
|
|
|
return NC_NOERR;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-07-29 18:57:25 +08:00
|
|
|
* @internal Add a dimension to the dimension list for a group.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
2018-03-17 01:46:18 +08:00
|
|
|
* @param grp container for the dim
|
|
|
|
* @param name for the dim
|
|
|
|
* @param len for the dim
|
|
|
|
* @param assignedid override dimid if >= 0
|
2017-12-03 22:57:21 +08:00
|
|
|
* @param dim Pointer to pointer that gets the new dim info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
2018-07-29 18:57:25 +08:00
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
2017-12-03 22:57:21 +08:00
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-07-29 18:57:25 +08:00
|
|
|
nc4_dim_list_add(NC_GRP_INFO_T *grp, const char *name, size_t len,
|
|
|
|
int assignedid, NC_DIM_INFO_T **dim)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-07-29 18:57:25 +08:00
|
|
|
NC_DIM_INFO_T *new_dim;
|
|
|
|
|
|
|
|
assert(grp && name);
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
2018-07-29 18:57:25 +08:00
|
|
|
/* Allocate memory for dim metadata. */
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
if (!(new_dim = calloc(1, sizeof(NC_DIM_INFO_T))))
|
2018-07-29 18:57:25 +08:00
|
|
|
return NC_ENOMEM;
|
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
new_dim->hdr.sort = NCDIM;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-07-29 18:57:25 +08:00
|
|
|
/* Assign the dimension ID. */
|
|
|
|
if (assignedid >= 0)
|
2018-03-17 01:46:18 +08:00
|
|
|
new_dim->hdr.id = assignedid;
|
|
|
|
else
|
2018-07-29 18:57:25 +08:00
|
|
|
new_dim->hdr.id = grp->nc4_info->next_dimid++;
|
2018-03-17 01:46:18 +08:00
|
|
|
|
2018-07-29 18:57:25 +08:00
|
|
|
/* Remember the name and create a hash. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if (!(new_dim->hdr.name = strdup(name)))
|
2018-07-29 18:57:25 +08:00
|
|
|
return NC_ENOMEM;
|
|
|
|
new_dim->hdr.hashkey = NC_hashmapkey(new_dim->hdr.name,
|
|
|
|
strlen(new_dim->hdr.name));
|
|
|
|
|
|
|
|
/* Is dimension unlimited? */
|
2018-03-17 01:46:18 +08:00
|
|
|
new_dim->len = len;
|
|
|
|
if (len == NC_UNLIMITED)
|
|
|
|
new_dim->unlimited = NC_TRUE;
|
2018-07-29 18:57:25 +08:00
|
|
|
|
|
|
|
/* Remember the containing group. */
|
2018-03-17 01:46:18 +08:00
|
|
|
new_dim->container = grp;
|
|
|
|
|
2018-07-29 18:57:25 +08:00
|
|
|
/* Add object to dimension list for this group. */
|
2018-08-07 00:16:49 +08:00
|
|
|
ncindexadd(grp->dim, (NC_OBJ *)new_dim);
|
2018-07-29 18:57:25 +08:00
|
|
|
obj_track(grp->nc4_info, (NC_OBJ *)new_dim);
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
|
|
|
/* Set the dim pointer, if one was given */
|
|
|
|
if (dim)
|
|
|
|
*dim = new_dim;
|
|
|
|
|
2018-07-29 18:57:25 +08:00
|
|
|
return NC_NOERR;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Add to the end of an att list.
|
|
|
|
*
|
2018-03-17 01:46:18 +08:00
|
|
|
* @param list NCindex of att info structs.
|
|
|
|
* @param name name of the new attribute
|
2017-12-03 22:57:21 +08:00
|
|
|
* @param att Pointer to pointer that gets the new att info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
2018-08-07 00:16:49 +08:00
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
2017-12-03 22:57:21 +08:00
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-08-07 00:16:49 +08:00
|
|
|
nc4_att_list_add(NCindex *list, const char *name, NC_ATT_INFO_T **att)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-08-07 00:16:49 +08:00
|
|
|
NC_ATT_INFO_T *new_att;
|
2018-03-17 01:46:18 +08:00
|
|
|
|
|
|
|
LOG((3, "%s: name %s ", __func__, name));
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
|
|
|
if (!(new_att = calloc(1, sizeof(NC_ATT_INFO_T))))
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_ENOMEM;
|
2018-03-17 01:46:18 +08:00
|
|
|
new_att->hdr.sort = NCATT;
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
/* Fill in the information we know. */
|
|
|
|
new_att->hdr.id = ncindexsize(list);
|
|
|
|
if (!(new_att->hdr.name = strdup(name)))
|
2018-08-07 00:16:49 +08:00
|
|
|
return NC_ENOMEM;
|
2018-03-17 01:46:18 +08:00
|
|
|
|
|
|
|
/* Create a hash of the name. */
|
|
|
|
new_att->hdr.hashkey = NC_hashmapkey(name, strlen(name));
|
|
|
|
|
|
|
|
/* Add object to list as specified by its number */
|
2018-08-07 00:16:49 +08:00
|
|
|
ncindexadd(list, (NC_OBJ *)new_att);
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
|
|
|
/* Set the attribute pointer, if one was given */
|
|
|
|
if (att)
|
|
|
|
*att = new_att;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-08-07 00:16:49 +08:00
|
|
|
return NC_NOERR;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-08-07 00:16:49 +08:00
|
|
|
* @internal Add a group to a group list.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
2018-08-07 00:16:49 +08:00
|
|
|
* @param h5 Pointer to the file info.
|
|
|
|
* @param parent Pointer to the parent group. Will be NULL when adding
|
|
|
|
* the root group.
|
2017-12-03 22:57:21 +08:00
|
|
|
* @param name Name of the group.
|
|
|
|
* @param grp Pointer to pointer that gets new group info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
2018-08-07 00:16:49 +08:00
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
|
|
|
* @author Ed Hartnett, Dennis Heimbigner
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-08-07 00:16:49 +08:00
|
|
|
nc4_grp_list_add(NC_FILE_INFO_T *h5, NC_GRP_INFO_T *parent, char *name,
|
|
|
|
NC_GRP_INFO_T **grp)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
NC_GRP_INFO_T *new_grp;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-08-07 00:16:49 +08:00
|
|
|
/* Check inputs. */
|
|
|
|
assert(h5 && name);
|
2018-03-17 01:46:18 +08:00
|
|
|
LOG((3, "%s: name %s ", __func__, name));
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Get the memory to store this groups info. */
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
if (!(new_grp = calloc(1, sizeof(NC_GRP_INFO_T))))
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_ENOMEM;
|
|
|
|
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
/* Fill in this group's information. */
|
2018-08-07 00:16:49 +08:00
|
|
|
new_grp->hdr.sort = NCGRP;
|
|
|
|
new_grp->nc4_info = h5;
|
2018-03-17 01:46:18 +08:00
|
|
|
new_grp->parent = parent;
|
2018-08-07 00:16:49 +08:00
|
|
|
|
|
|
|
/* Assign the group ID. The root group will get id 0. */
|
|
|
|
new_grp->hdr.id = h5->next_nc_grpid++;
|
|
|
|
assert(parent || !new_grp->hdr.id);
|
|
|
|
|
|
|
|
/* Handle the group name. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if (!(new_grp->hdr.name = strdup(name)))
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
free(new_grp);
|
|
|
|
return NC_ENOMEM;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
2018-08-07 00:16:49 +08:00
|
|
|
new_grp->hdr.hashkey = NC_hashmapkey(new_grp->hdr.name,
|
|
|
|
strlen(new_grp->hdr.name));
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-08-07 00:16:49 +08:00
|
|
|
/* Set up new indexed lists for stuff this group can contain. */
|
2018-03-17 01:46:18 +08:00
|
|
|
new_grp->children = ncindexnew(0);
|
|
|
|
new_grp->dim = ncindexnew(0);
|
|
|
|
new_grp->att = ncindexnew(0);
|
|
|
|
new_grp->type = ncindexnew(0);
|
|
|
|
new_grp->vars = ncindexnew(0);
|
|
|
|
|
|
|
|
/* Add object to lists */
|
2018-08-07 00:16:49 +08:00
|
|
|
if (parent)
|
|
|
|
ncindexadd(parent->children, (NC_OBJ *)new_grp);
|
|
|
|
obj_track(h5, (NC_OBJ *)new_grp);
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
|
|
|
/* Set the group pointer, if one was given */
|
|
|
|
if (grp)
|
2017-12-05 03:21:14 +08:00
|
|
|
*grp = new_grp;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Names for groups, variables, and types must not be the
|
|
|
|
* same. This function checks that a proposed name is not already in
|
2010-06-03 21:24:43 +08:00
|
|
|
* use. Normalzation of UTF8 strings should happen before this
|
2017-12-03 22:57:21 +08:00
|
|
|
* function is called.
|
|
|
|
*
|
|
|
|
* @param grp Pointer to group info struct.
|
|
|
|
* @param name Name to check.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_ENAMEINUSE Name is in use.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
|
|
|
nc4_check_dup_name(NC_GRP_INFO_T *grp, char *name)
|
|
|
|
{
|
|
|
|
NC_TYPE_INFO_T *type;
|
|
|
|
NC_GRP_INFO_T *g;
|
|
|
|
NC_VAR_INFO_T *var;
|
2016-11-29 04:31:43 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Any types of this name? */
|
2018-03-17 01:46:18 +08:00
|
|
|
type = (NC_TYPE_INFO_T*)ncindexlookup(grp->type,name);
|
|
|
|
if(type != NULL)
|
2018-08-08 05:17:40 +08:00
|
|
|
return NC_ENAMEINUSE;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Any child groups of this name? */
|
2018-03-17 01:46:18 +08:00
|
|
|
g = (NC_GRP_INFO_T*)ncindexlookup(grp->children,name);
|
|
|
|
if(g != NULL)
|
2018-08-08 05:17:40 +08:00
|
|
|
return NC_ENAMEINUSE;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Any variables of this name? */
|
2018-03-17 01:46:18 +08:00
|
|
|
var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name);
|
|
|
|
if(var != NULL)
|
2018-08-08 05:17:40 +08:00
|
|
|
return NC_ENAMEINUSE;
|
2018-03-17 01:46:18 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-08-07 00:16:49 +08:00
|
|
|
* @internal Create a type, but do not add to various lists nor
|
|
|
|
* increment its ref count
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param grp Pointer to group info struct.
|
|
|
|
* @param size Size of type in bytes.
|
|
|
|
* @param name Name of type.
|
2018-08-07 00:16:49 +08:00
|
|
|
* @param assignedid if >= 0 then override the default type id.
|
|
|
|
* @param type Pointer that gets pointer to new type info struct.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
2018-06-01 05:25:40 +08:00
|
|
|
* @author Ed Hartnett, Ward Fisher
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-08-07 00:16:49 +08:00
|
|
|
nc4_type_new(NC_GRP_INFO_T *grp, size_t size, const char *name, int assignedid,
|
|
|
|
NC_TYPE_INFO_T **type)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-08-07 00:16:49 +08:00
|
|
|
NC_TYPE_INFO_T *new_type;
|
|
|
|
|
|
|
|
/* Check inputs. */
|
|
|
|
assert(type);
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-08-08 05:17:40 +08:00
|
|
|
/* Allocate memory for the type */
|
|
|
|
if (!(new_type = calloc(1, sizeof(NC_TYPE_INFO_T))))
|
|
|
|
return NC_ENOMEM;
|
|
|
|
new_type->hdr.sort = NCTYP;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-08-08 05:17:40 +08:00
|
|
|
/* Remember info about this type. */
|
|
|
|
new_type->hdr.id = assignedid;
|
|
|
|
new_type->size = size;
|
|
|
|
if (!(new_type->hdr.name = strdup(name))) {
|
|
|
|
free(new_type);
|
|
|
|
return NC_ENOMEM;
|
|
|
|
}
|
2014-02-12 07:12:08 +08:00
|
|
|
|
2018-08-07 00:16:49 +08:00
|
|
|
new_type->hdr.hashkey = NC_hashmapkey(name, strlen(name));
|
2018-03-17 01:46:18 +08:00
|
|
|
|
2018-08-07 00:16:49 +08:00
|
|
|
/* Return a pointer to the new type. */
|
|
|
|
*type = new_type;
|
2018-03-17 01:46:18 +08:00
|
|
|
|
2018-08-08 05:17:40 +08:00
|
|
|
return NC_NOERR;
|
2018-03-17 01:46:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal Add to the end of a type list.
|
|
|
|
*
|
|
|
|
* @param grp Pointer to group info struct.
|
|
|
|
* @param size Size of type in bytes.
|
|
|
|
* @param name Name of type.
|
|
|
|
* @param type Pointer that gets pointer to new type info
|
2018-08-07 00:16:49 +08:00
|
|
|
* struct.
|
2018-03-17 01:46:18 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
|
|
|
* @author Ed Hartnett
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
nc4_type_list_add(NC_GRP_INFO_T *grp, size_t size, const char *name,
|
|
|
|
NC_TYPE_INFO_T **type)
|
|
|
|
{
|
2018-08-07 00:16:49 +08:00
|
|
|
NC_TYPE_INFO_T *new_type;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
/* Check inputs. */
|
|
|
|
assert(type);
|
2018-03-17 01:46:18 +08:00
|
|
|
|
2018-08-07 00:16:49 +08:00
|
|
|
/* Create the new TYPE_INFO struct. */
|
|
|
|
if ((retval = nc4_type_new(grp, size, name, grp->nc4_info->next_typeid,
|
|
|
|
&new_type)))
|
|
|
|
return retval;
|
2018-03-17 01:46:18 +08:00
|
|
|
grp->nc4_info->next_typeid++;
|
|
|
|
|
2014-02-12 07:12:08 +08:00
|
|
|
/* Increment the ref. count on the type */
|
|
|
|
new_type->rc++;
|
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
/* Add object to lists */
|
2018-08-07 00:16:49 +08:00
|
|
|
ncindexadd(grp->type, (NC_OBJ *)new_type);
|
2018-03-17 01:46:18 +08:00
|
|
|
obj_track(grp->nc4_info,(NC_OBJ*)new_type);
|
|
|
|
|
2018-08-07 00:16:49 +08:00
|
|
|
/* Return a pointer to the new type. */
|
|
|
|
*type = new_type;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2017-12-05 03:21:14 +08:00
|
|
|
* @internal Add to the end of a compound field list.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
2018-03-17 01:46:18 +08:00
|
|
|
* @param parent parent type
|
2017-12-03 22:57:21 +08:00
|
|
|
* @param name Name of the field.
|
|
|
|
* @param offset Offset in bytes.
|
|
|
|
* @param field_hdf_typeid The HDF5 type ID of the field.
|
|
|
|
* @param native_typeid The HDF5 native type ID of the field.
|
|
|
|
* @param xtype The netCDF type of the field.
|
|
|
|
* @param ndims The number of dimensions of the field.
|
|
|
|
* @param dim_sizesp An array of dim sizes for the field.
|
2017-12-05 03:21:14 +08:00
|
|
|
*
|
2017-12-03 22:57:21 +08:00
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-03-17 01:46:18 +08:00
|
|
|
nc4_field_list_add(NC_TYPE_INFO_T *parent, const char *name,
|
2017-12-05 03:21:14 +08:00
|
|
|
size_t offset, hid_t field_hdf_typeid, hid_t native_typeid,
|
|
|
|
nc_type xtype, int ndims, const int *dim_sizesp)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
NC_FIELD_INFO_T *field;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Name has already been checked and UTF8 normalized. */
|
|
|
|
if (!name)
|
|
|
|
return NC_EINVAL;
|
|
|
|
|
|
|
|
/* Allocate storage for this field information. */
|
|
|
|
if (!(field = calloc(1, sizeof(NC_FIELD_INFO_T))))
|
|
|
|
return NC_ENOMEM;
|
2018-03-17 01:46:18 +08:00
|
|
|
field->hdr.sort = NCFLD;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Store the information about this field. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if (!(field->hdr.name = strdup(name)))
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
{
|
|
|
|
free(field);
|
2010-07-01 23:51:19 +08:00
|
|
|
return NC_ENOMEM;
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
}
|
2018-03-17 01:46:18 +08:00
|
|
|
field->hdr.hashkey = NC_hashmapkey(field->hdr.name,strlen(field->hdr.name));
|
2010-06-03 21:24:43 +08:00
|
|
|
field->hdf_typeid = field_hdf_typeid;
|
2014-02-12 07:12:08 +08:00
|
|
|
field->native_hdf_typeid = native_typeid;
|
|
|
|
field->nc_typeid = xtype;
|
2010-06-03 21:24:43 +08:00
|
|
|
field->offset = offset;
|
|
|
|
field->ndims = ndims;
|
2010-07-02 00:02:10 +08:00
|
|
|
if (ndims)
|
|
|
|
{
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
int i;
|
2010-07-02 00:02:10 +08:00
|
|
|
if (!(field->dim_size = malloc(ndims * sizeof(int))))
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
free(field->hdr.name);
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
free(field);
|
2017-12-05 03:21:14 +08:00
|
|
|
return NC_ENOMEM;
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
}
|
2010-07-02 00:02:10 +08:00
|
|
|
for (i = 0; i < ndims; i++)
|
2017-12-05 03:21:14 +08:00
|
|
|
field->dim_size[i] = dim_sizesp[i];
|
2010-07-02 00:02:10 +08:00
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
/* Add object to lists */
|
|
|
|
field->hdr.id = nclistlength(parent->u.c.field);
|
|
|
|
nclistpush(parent->u.c.field,field);
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2017-12-05 03:21:14 +08:00
|
|
|
* @internal Add a member to an enum type.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
2018-03-17 01:46:18 +08:00
|
|
|
* @param parent Containing NC_TYPE_INFO_T object
|
2017-12-03 22:57:21 +08:00
|
|
|
* @param size Size in bytes of new member.
|
|
|
|
* @param name Name of the member.
|
|
|
|
* @param value Value to associate with member.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_ENOMEM Out of memory.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-03-17 01:46:18 +08:00
|
|
|
nc4_enum_member_add(NC_TYPE_INFO_T *parent, size_t size,
|
2017-12-05 03:21:14 +08:00
|
|
|
const char *name, const void *value)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
NC_ENUM_MEMBER_INFO_T *member;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Name has already been checked. */
|
|
|
|
assert(name && size > 0 && value);
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
LOG((4, "%s: size %d name %s", __func__, size, name));
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Allocate storage for this field information. */
|
2013-08-18 01:53:29 +08:00
|
|
|
if (!(member = calloc(1, sizeof(NC_ENUM_MEMBER_INFO_T))))
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_ENOMEM;
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
if (!(member->value = malloc(size))) {
|
2013-08-18 01:53:29 +08:00
|
|
|
free(member);
|
|
|
|
return NC_ENOMEM;
|
|
|
|
}
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
if (!(member->name = strdup(name))) {
|
2013-08-18 01:53:29 +08:00
|
|
|
free(member->value);
|
|
|
|
free(member);
|
|
|
|
return NC_ENOMEM;
|
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
/* Store the value for this member. */
|
2010-06-03 21:24:43 +08:00
|
|
|
memcpy(member->value, value, size);
|
|
|
|
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
/* Add object to list */
|
2018-03-17 01:46:18 +08:00
|
|
|
nclistpush(parent->u.e.enum_member,member);
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-03-17 01:46:18 +08:00
|
|
|
* @internal Free up a field
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param field Pointer to field info of field to delete.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2014-02-12 07:12:08 +08:00
|
|
|
static void
|
2018-03-17 01:46:18 +08:00
|
|
|
field_free(NC_FIELD_INFO_T *field)
|
2014-02-12 07:12:08 +08:00
|
|
|
{
|
|
|
|
/* Free some stuff. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if (field->hdr.name)
|
|
|
|
free(field->hdr.name);
|
2014-02-12 07:12:08 +08:00
|
|
|
if (field->dim_size)
|
|
|
|
free(field->dim_size);
|
|
|
|
|
|
|
|
/* Nc_Free the memory. */
|
|
|
|
free(field);
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2017-12-05 03:21:14 +08:00
|
|
|
* @internal Free allocated space for type information.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param type Pointer to type info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2014-02-12 07:12:08 +08:00
|
|
|
int
|
|
|
|
nc4_type_free(NC_TYPE_INFO_T *type)
|
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
int i;
|
|
|
|
|
2014-02-12 07:12:08 +08:00
|
|
|
/* Decrement the ref. count on the type */
|
|
|
|
assert(type->rc);
|
|
|
|
type->rc--;
|
|
|
|
|
|
|
|
/* Release the type, if the ref. count drops to zero */
|
|
|
|
if (0 == type->rc)
|
|
|
|
{
|
|
|
|
/* Close any open user-defined HDF5 typeids. */
|
|
|
|
if (type->hdf_typeid && H5Tclose(type->hdf_typeid) < 0)
|
|
|
|
return NC_EHDFERR;
|
|
|
|
if (type->native_hdf_typeid && H5Tclose(type->native_hdf_typeid) < 0)
|
|
|
|
return NC_EHDFERR;
|
|
|
|
|
|
|
|
/* Free the name. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if (type->hdr.name)
|
|
|
|
free(type->hdr.name);
|
2014-02-12 07:12:08 +08:00
|
|
|
|
|
|
|
/* Class-specific cleanup */
|
|
|
|
switch (type->nc_type_class)
|
|
|
|
{
|
2017-12-05 03:21:14 +08:00
|
|
|
case NC_COMPOUND:
|
|
|
|
{
|
|
|
|
NC_FIELD_INFO_T *field;
|
2014-02-12 07:12:08 +08:00
|
|
|
|
2017-12-05 03:21:14 +08:00
|
|
|
/* Delete all the fields in this type (there will be some if its a
|
|
|
|
* compound). */
|
2018-08-08 05:17:40 +08:00
|
|
|
for(i=0;i<nclistlength(type->u.c.field);i++) {
|
2018-03-17 01:46:18 +08:00
|
|
|
field = nclistget(type->u.c.field,i);
|
|
|
|
field_free(field);
|
2017-12-05 03:21:14 +08:00
|
|
|
}
|
2018-08-08 05:17:40 +08:00
|
|
|
nclistfree(type->u.c.field);
|
|
|
|
type->u.c.field = NULL; /* belt and suspenders */
|
2017-12-05 03:21:14 +08:00
|
|
|
}
|
|
|
|
break;
|
2014-02-12 07:12:08 +08:00
|
|
|
|
2017-12-05 03:21:14 +08:00
|
|
|
case NC_ENUM:
|
|
|
|
{
|
|
|
|
NC_ENUM_MEMBER_INFO_T *enum_member;
|
|
|
|
|
|
|
|
/* Delete all the enum_members, if any. */
|
2018-08-08 05:17:40 +08:00
|
|
|
for(i=0;i<nclistlength(type->u.e.enum_member);i++) {
|
2018-03-17 01:46:18 +08:00
|
|
|
enum_member = nclistget(type->u.e.enum_member,i);
|
2017-12-05 03:21:14 +08:00
|
|
|
free(enum_member->value);
|
|
|
|
free(enum_member->name);
|
|
|
|
free(enum_member);
|
|
|
|
}
|
2018-08-08 05:17:40 +08:00
|
|
|
nclistfree(type->u.e.enum_member);
|
|
|
|
type->u.e.enum_member = NULL; /* belt and suspenders */
|
2017-12-05 03:21:14 +08:00
|
|
|
|
|
|
|
if (H5Tclose(type->u.e.base_hdf_typeid) < 0)
|
|
|
|
return NC_EHDFERR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NC_VLEN:
|
|
|
|
if (H5Tclose(type->u.v.base_hdf_typeid) < 0)
|
|
|
|
return NC_EHDFERR;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2014-02-12 07:12:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the memory. */
|
|
|
|
free(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2017-12-05 03:21:14 +08:00
|
|
|
* @internal Delete a var, and free the memory.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param var Pointer to the var info struct of var to delete.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2013-03-01 16:10:28 +08:00
|
|
|
int
|
2018-03-17 01:46:18 +08:00
|
|
|
nc4_var_free(NC_VAR_INFO_T *var)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
NC_ATT_INFO_T *att;
|
|
|
|
int ret = NC_NOERR;
|
|
|
|
int i;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
if(var == NULL)
|
2017-12-05 03:21:14 +08:00
|
|
|
return NC_NOERR;
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* First delete all the attributes attached to this var. */
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(var->att);i++) {
|
2018-07-29 18:57:25 +08:00
|
|
|
att = (NC_ATT_INFO_T*)ncindexith(var->att,i);
|
|
|
|
if(att == NULL) continue; /* might be a gap */
|
|
|
|
if ((ret = nc4_att_free(att)))
|
2017-12-05 03:21:14 +08:00
|
|
|
return ret;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
2018-03-17 01:46:18 +08:00
|
|
|
ncindexfree(var->att);
|
|
|
|
var->att = NULL;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2010-07-01 05:16:50 +08:00
|
|
|
/* Free some things that may be allocated. */
|
2014-12-29 13:04:48 +08:00
|
|
|
if (var->chunksizes)
|
2017-12-05 03:21:14 +08:00
|
|
|
{free(var->chunksizes);var->chunksizes = NULL;}
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-07-01 05:16:50 +08:00
|
|
|
if (var->hdf5_name)
|
2017-12-05 03:21:14 +08:00
|
|
|
{free(var->hdf5_name); var->hdf5_name = NULL;}
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
if (var->hdr.name)
|
|
|
|
{free(var->hdr.name); var->hdr.name = NULL;}
|
2013-04-03 06:09:31 +08:00
|
|
|
|
2010-07-01 20:52:44 +08:00
|
|
|
if (var->dimids)
|
2017-12-05 03:21:14 +08:00
|
|
|
{free(var->dimids); var->dimids = NULL;}
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-07-01 20:52:44 +08:00
|
|
|
if (var->dim)
|
2017-12-05 03:21:14 +08:00
|
|
|
{free(var->dim); var->dim = NULL;}
|
2010-07-01 05:05:11 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Delete any fill value allocation. This must be done before the
|
|
|
|
* type_info is freed. */
|
|
|
|
if (var->fill_value)
|
|
|
|
{
|
2010-11-30 06:23:16 +08:00
|
|
|
if (var->hdf_datasetid)
|
|
|
|
{
|
2013-03-01 16:10:28 +08:00
|
|
|
if (var->type_info)
|
Corrected "BAIL" macros to avoid infinite loop when logging is disabled and an
error occurs after an "exit:" label.
Corrected a dozen Coverity errors (mainly allocation issues, along with a few
other things):
711711, 711802, 711803, 711905, 970825, 996123, 996124, 1025787,
1047274, 1130013, 1130014, 1139538
Refactored internal fill-value code to correctly handle string types, and
especially to allow NULL pointers and null strings (ie. "") to be
distinguished. The code now avoids partially aliasing the two together
(which only happened on the 'write' side of things and wasn't reflected on
the 'read' side, adding to the previous confusion).
Probably still weak on handling fill-values of variable-length and compound
datatypes.
Refactored the recursive metadata reads a bit more, to process HDF5 named
datatypes and datasets immediately, avoiding chewing up memory for those
types of objects, etc.
Finished uncommenting and updating the nc_test4/tst_fills2.c code (as I'm
proceeding alphabetically through the nc_test4 code files).
2013-12-29 15:12:43 +08:00
|
|
|
{
|
2014-02-12 07:12:08 +08:00
|
|
|
if (var->type_info->nc_type_class == NC_VLEN)
|
2013-03-01 16:10:28 +08:00
|
|
|
nc_free_vlen((nc_vlen_t *)var->fill_value);
|
2014-02-12 07:12:08 +08:00
|
|
|
else if (var->type_info->nc_type_class == NC_STRING && *(char **)var->fill_value)
|
Corrected "BAIL" macros to avoid infinite loop when logging is disabled and an
error occurs after an "exit:" label.
Corrected a dozen Coverity errors (mainly allocation issues, along with a few
other things):
711711, 711802, 711803, 711905, 970825, 996123, 996124, 1025787,
1047274, 1130013, 1130014, 1139538
Refactored internal fill-value code to correctly handle string types, and
especially to allow NULL pointers and null strings (ie. "") to be
distinguished. The code now avoids partially aliasing the two together
(which only happened on the 'write' side of things and wasn't reflected on
the 'read' side, adding to the previous confusion).
Probably still weak on handling fill-values of variable-length and compound
datatypes.
Refactored the recursive metadata reads a bit more, to process HDF5 named
datatypes and datasets immediately, avoiding chewing up memory for those
types of objects, etc.
Finished uncommenting and updating the nc_test4/tst_fills2.c code (as I'm
proceeding alphabetically through the nc_test4 code files).
2013-12-29 15:12:43 +08:00
|
|
|
free(*(char **)var->fill_value);
|
|
|
|
}
|
2010-11-30 06:23:16 +08:00
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
free(var->fill_value);
|
2013-04-03 06:09:31 +08:00
|
|
|
var->fill_value = NULL;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2014-02-12 07:12:08 +08:00
|
|
|
/* Release type information */
|
|
|
|
if (var->type_info)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2014-02-12 07:12:08 +08:00
|
|
|
int retval;
|
2010-08-20 01:37:29 +08:00
|
|
|
|
2014-02-12 07:12:08 +08:00
|
|
|
if ((retval = nc4_type_free(var->type_info)))
|
2017-12-05 03:21:14 +08:00
|
|
|
return retval;
|
2013-04-03 06:09:31 +08:00
|
|
|
var->type_info = NULL;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Delete any HDF5 dimscale objid information. */
|
|
|
|
if (var->dimscale_hdf5_objids)
|
|
|
|
free(var->dimscale_hdf5_objids);
|
|
|
|
|
|
|
|
/* Delete information about the attachment status of dimscales. */
|
|
|
|
if (var->dimscale_attached)
|
|
|
|
free(var->dimscale_attached);
|
|
|
|
|
2017-11-15 00:15:13 +08:00
|
|
|
/* Release parameter information. */
|
|
|
|
if (var->params)
|
|
|
|
free(var->params);
|
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Delete the var. */
|
|
|
|
free(var);
|
|
|
|
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-03-17 01:46:18 +08:00
|
|
|
* @internal Delete a var, and free the memory.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
2018-03-17 01:46:18 +08:00
|
|
|
* @param grp the containing group
|
|
|
|
* @param var Pointer to the var info struct of var to delete.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2018-03-17 01:46:18 +08:00
|
|
|
int
|
|
|
|
nc4_var_list_del(NC_GRP_INFO_T* grp, NC_VAR_INFO_T *var)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
int i;
|
Refactored read_scale(), memio_new(), var_create_dataset() and makespecial()
to clean up resources properly on failure.
Refactored doubly-linked list code for objects in the libsrc4 directory,
cleaning up the add/del routines, breaking out the common next/prev
pointers into a struct and extracting the add/del operations on them,
changed the list of dims to add new dims in the same order as the other
types, made all add routines able to optionally return a pointer to the
newly created object.
Removed some dead code (pg_var(), nc4_pg_var1(), nc4_pg_varm(), misc. small
routines, etc)
Fixed fill value handling for string types in nc4_get_vara().
Changed many malloc()+strcpy() pairs into calls to strdup().
Cleaned up misc. other minor Coverity issues.
2013-12-08 17:29:26 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
if(var == NULL)
|
|
|
|
return NC_NOERR;
|
|
|
|
|
|
|
|
/* Remove from lists */
|
|
|
|
if(grp) {
|
|
|
|
i = ncindexfind(grp->vars,(NC_OBJ*)var);
|
|
|
|
if(i >= 0)
|
2018-08-07 00:16:49 +08:00
|
|
|
ncindexidel(grp->vars, i);
|
2018-03-17 01:46:18 +08:00
|
|
|
}
|
|
|
|
return nc4_var_free(var);
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-06-01 05:25:40 +08:00
|
|
|
* @internal Free a dim
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param dim Pointer to dim info struct of type to delete.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
2018-06-01 05:28:13 +08:00
|
|
|
* @author Ed Hartnett, Ward Fisher
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-03-17 01:46:18 +08:00
|
|
|
nc4_dim_free(NC_DIM_INFO_T *dim)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2010-07-01 21:36:41 +08:00
|
|
|
/* Free memory allocated for names. */
|
2018-08-08 05:17:40 +08:00
|
|
|
if(dim) {
|
|
|
|
if (dim->hdr.name)
|
|
|
|
free(dim->hdr.name);
|
2010-07-01 21:36:41 +08:00
|
|
|
|
2018-08-08 05:17:40 +08:00
|
|
|
free(dim);
|
|
|
|
}
|
|
|
|
return NC_NOERR;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-03-17 01:46:18 +08:00
|
|
|
* @internal Free a dim and unlist it
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
2018-03-17 01:46:18 +08:00
|
|
|
* @param grp Pointer to dim's containing group
|
|
|
|
* @param dim Pointer to dim info struct of type to delete.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
2018-03-17 01:46:18 +08:00
|
|
|
* @author Dennis Heimbigner
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2018-03-17 01:46:18 +08:00
|
|
|
int
|
|
|
|
nc4_dim_list_del(NC_GRP_INFO_T* grp, NC_DIM_INFO_T *dim)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
if(grp && dim) {
|
2018-08-08 05:17:40 +08:00
|
|
|
int pos = ncindexfind(grp->dim,(NC_OBJ*)dim);
|
|
|
|
if(pos >= 0)
|
|
|
|
ncindexidel(grp->dim,pos);
|
2018-03-17 01:46:18 +08:00
|
|
|
}
|
|
|
|
return nc4_dim_free(dim);
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Recursively delete the data for a group (and everything
|
|
|
|
* it contains) in our internal metadata store.
|
|
|
|
*
|
|
|
|
* @param grp Pointer to group info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2014-12-29 13:04:48 +08:00
|
|
|
int
|
2018-03-17 01:46:18 +08:00
|
|
|
nc4_rec_grp_del(NC_GRP_INFO_T *grp)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
NC_GRP_INFO_T *g;
|
2016-08-25 11:30:20 +08:00
|
|
|
NC_VAR_INFO_T *var;
|
2018-03-17 01:46:18 +08:00
|
|
|
NC_ATT_INFO_T *att;
|
|
|
|
NC_DIM_INFO_T *dim;
|
2010-06-03 21:24:43 +08:00
|
|
|
int retval;
|
2016-07-14 01:13:14 +08:00
|
|
|
int i;
|
2016-08-24 23:36:54 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
assert(grp);
|
2018-03-17 01:46:18 +08:00
|
|
|
LOG((3, "%s: grp->name %s", __func__, grp->hdr.name));
|
|
|
|
|
|
|
|
/* WARNING: for all these deletes, the nc4_xxx_del
|
|
|
|
functions will modify the index, so we need to
|
|
|
|
not assume any state about them.
|
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Recursively call this function for each child, if any, stopping
|
|
|
|
* if there is an error. */
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(grp->children);i++) {
|
2018-08-08 05:17:40 +08:00
|
|
|
g = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
|
|
|
|
if(g == NULL) continue;
|
|
|
|
if ((retval = nc4_rec_grp_del(g)))
|
2017-12-05 03:21:14 +08:00
|
|
|
return retval;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
2018-03-17 01:46:18 +08:00
|
|
|
ncindexfree(grp->children);
|
|
|
|
grp->children = NULL;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
/* Delete all the list contents for vars, dims, and atts, in this
|
2010-06-03 21:24:43 +08:00
|
|
|
* group. */
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(grp->att);i++) {
|
2018-08-08 05:17:40 +08:00
|
|
|
att = (NC_ATT_INFO_T*)ncindexith(grp->att,i);
|
|
|
|
if(att == NULL) continue;
|
|
|
|
LOG((4, "%s: deleting att %s", __func__, att->hdr.name));
|
|
|
|
if ((retval = nc4_att_free(att))) /* free but leave in parent list */
|
2017-12-05 03:21:14 +08:00
|
|
|
return retval;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
2018-03-17 01:46:18 +08:00
|
|
|
ncindexfree(grp->att);
|
|
|
|
grp->att = NULL;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Delete all vars. */
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(grp->vars);i++) {
|
|
|
|
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
|
|
|
|
if(var == NULL) continue;
|
|
|
|
LOG((4, "%s: deleting var %s", __func__, var->hdr.name));
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Close HDF5 dataset associated with this var, unless it's a
|
|
|
|
* scale. */
|
2018-08-07 00:16:49 +08:00
|
|
|
if (var->hdf_datasetid)
|
|
|
|
{
|
|
|
|
LOG((3, "closing dataset %lld", var->hdf_datasetid));
|
|
|
|
if (H5Dclose(var->hdf_datasetid) < 0)
|
|
|
|
return NC_EHDFERR;
|
|
|
|
}
|
2018-03-17 01:46:18 +08:00
|
|
|
if ((retval = nc4_var_free(var))) /* free but leave in parent list */
|
2017-12-05 03:21:14 +08:00
|
|
|
return retval;
|
2016-07-07 22:28:24 +08:00
|
|
|
}
|
2018-03-17 01:46:18 +08:00
|
|
|
ncindexfree(grp->vars);
|
|
|
|
grp->vars = NULL;
|
2016-07-07 22:28:24 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Delete all dims. */
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(grp->dim);i++) {
|
|
|
|
dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
|
|
|
|
if(dim == NULL) continue;
|
|
|
|
LOG((4, "%s: deleting dim %s", __func__, dim->hdr.name));
|
2018-01-05 21:01:22 +08:00
|
|
|
/* If this is a dim without a coordinate variable, then close
|
|
|
|
* the HDF5 DIM_WITHOUT_VARIABLE dataset associated with this
|
|
|
|
* dim. */
|
2010-06-03 21:24:43 +08:00
|
|
|
if (dim->hdf_dimscaleid && H5Dclose(dim->hdf_dimscaleid) < 0)
|
2017-12-05 03:21:14 +08:00
|
|
|
return NC_EHDFERR;
|
2018-03-17 01:46:18 +08:00
|
|
|
if ((retval = nc4_dim_free(dim))) /* free but leave in parent list */
|
2017-12-05 03:21:14 +08:00
|
|
|
return retval;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
2018-03-17 01:46:18 +08:00
|
|
|
ncindexfree(grp->dim);
|
|
|
|
grp->dim = NULL;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Delete all types. */
|
2018-03-17 01:46:18 +08:00
|
|
|
/* Is this code correct? I think it should do repeated passes
|
|
|
|
over h5->alltypes using the ref count to decide what to delete */
|
|
|
|
for(i=0;i<ncindexsize(grp->type);i++) {
|
|
|
|
NC_TYPE_INFO_T* type = (NC_TYPE_INFO_T*)ncindexith(grp->type,i);
|
|
|
|
if(type == NULL) continue;
|
|
|
|
LOG((4, "%s: deleting type %s", __func__, type->hdr.name));
|
|
|
|
if ((retval = nc4_type_free(type))) /* free but leave in parent list */
|
2017-12-05 03:21:14 +08:00
|
|
|
return retval;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
2018-03-17 01:46:18 +08:00
|
|
|
ncindexfree(grp->type);
|
|
|
|
grp->type = NULL;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2014-12-29 13:04:48 +08:00
|
|
|
/* Tell HDF5 we're closing this group. */
|
2018-03-17 01:46:18 +08:00
|
|
|
LOG((4, "%s: closing group %s", __func__, grp->hdr.name));
|
2014-12-29 13:04:48 +08:00
|
|
|
if (grp->hdf_grpid && H5Gclose(grp->hdf_grpid) < 0)
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_EHDFERR;
|
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
/* Free up this group */
|
2010-07-02 00:02:10 +08:00
|
|
|
/* Free the name. */
|
2018-03-17 01:46:18 +08:00
|
|
|
free(grp->hdr.name);
|
|
|
|
free(grp);
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-03-17 01:46:18 +08:00
|
|
|
* @internal Remove a NC_ATT_INFO_T from an index.
|
|
|
|
* This will nc_free the memory too.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param list Pointer to pointer of list.
|
|
|
|
* @param att Pointer to attribute info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
2018-03-17 01:46:18 +08:00
|
|
|
* @author Dennis Heimbigner
|
|
|
|
*/
|
|
|
|
int
|
2018-08-07 00:16:49 +08:00
|
|
|
nc4_att_list_del(NCindex *list, NC_ATT_INFO_T *att)
|
2018-03-17 01:46:18 +08:00
|
|
|
{
|
2018-08-07 00:16:49 +08:00
|
|
|
assert(att && list);
|
|
|
|
ncindexidel(list, ((NC_OBJ *)att)->id);
|
2018-03-17 01:46:18 +08:00
|
|
|
return nc4_att_free(att);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal Free memory of an attribute object
|
|
|
|
*
|
|
|
|
* @param att Pointer to attribute info struct.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
2017-12-03 22:57:21 +08:00
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2018-03-17 01:46:18 +08:00
|
|
|
nc4_att_free(NC_ATT_INFO_T *att)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
LOG((3, "%s: name %s ", __func__, att->hdr.name));
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Free memory that was malloced to hold data for this
|
|
|
|
* attribute. */
|
|
|
|
if (att->data)
|
|
|
|
free(att->data);
|
|
|
|
|
2010-07-01 21:08:28 +08:00
|
|
|
/* Free the name. */
|
2018-03-17 01:46:18 +08:00
|
|
|
if (att->hdr.name) {
|
|
|
|
free(att->hdr.name);
|
|
|
|
att->hdr.name = NULL;
|
|
|
|
}
|
2010-07-01 21:08:28 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Close the HDF5 typeid. */
|
2014-02-12 07:12:08 +08:00
|
|
|
if (att->native_hdf_typeid && H5Tclose(att->native_hdf_typeid) < 0)
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_EHDFERR;
|
|
|
|
|
|
|
|
/* If this is a string array attribute, delete all members of the
|
|
|
|
* string array, then delete the array of pointers to strings. (The
|
|
|
|
* array was filled with pointers by HDF5 when the att was read,
|
|
|
|
* and memory for each string was allocated by HDF5. That's why I
|
|
|
|
* use free and not nc_free, because the netCDF library didn't
|
|
|
|
* allocate the memory that is being freed.) */
|
|
|
|
if (att->stdata)
|
|
|
|
{
|
|
|
|
for (i = 0; i < att->len; i++)
|
2013-07-11 04:09:31 +08:00
|
|
|
if(att->stdata[i])
|
2017-12-05 03:21:14 +08:00
|
|
|
free(att->stdata[i]);
|
2010-06-03 21:24:43 +08:00
|
|
|
free(att->stdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If this att has vlen data, release it. */
|
|
|
|
if (att->vldata)
|
|
|
|
{
|
|
|
|
for (i = 0; i < att->len; i++)
|
2017-12-05 03:21:14 +08:00
|
|
|
nc_free_vlen(&att->vldata[i]);
|
2010-06-03 21:24:43 +08:00
|
|
|
free(att->vldata);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(att);
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Normalize a UTF8 name. Put the result in norm_name, which
|
|
|
|
* can be NC_MAX_NAME + 1 in size. This function makes sure the free()
|
|
|
|
* gets called on the return from utf8proc_NFC, and also ensures that
|
|
|
|
* the name is not too long.
|
|
|
|
*
|
|
|
|
* @param name Name to normalize.
|
|
|
|
* @param norm_name The normalized name.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @return ::NC_EMAXNAME Name too long.
|
|
|
|
* @author Dennis Heimbigner
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
|
|
|
nc4_normalize_name(const char *name, char *norm_name)
|
|
|
|
{
|
|
|
|
char *temp_name;
|
2017-02-17 05:27:54 +08:00
|
|
|
int stat = nc_utf8_normalize((const unsigned char *)name,(unsigned char **)&temp_name);
|
|
|
|
if(stat != NC_NOERR)
|
|
|
|
return stat;
|
2010-06-03 21:24:43 +08:00
|
|
|
if (strlen(temp_name) > NC_MAX_NAME)
|
|
|
|
{
|
|
|
|
free(temp_name);
|
|
|
|
return NC_EMAXNAME;
|
|
|
|
}
|
|
|
|
strcpy(norm_name, temp_name);
|
|
|
|
free(temp_name);
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2018-05-13 01:46:04 +08:00
|
|
|
#ifdef ENABLE_SET_LOG_LEVEL
|
2018-05-13 01:27:11 +08:00
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2018-05-13 01:27:11 +08:00
|
|
|
* @internal Use this to set the global log level. Set it to
|
|
|
|
* NC_TURN_OFF_LOGGING (-1) to turn off all logging. Set it to 0 to
|
|
|
|
* show only errors, and to higher numbers to show more and more
|
|
|
|
* logging details. If logging is not enabled with --enable-logging at
|
|
|
|
* configure when building netCDF, this function will do nothing.
|
2018-06-30 05:06:27 +08:00
|
|
|
* Note that it is possible to set the log level using the environment
|
|
|
|
* variable named _NETCDF_LOG_LEVEL_ (e.g. _export NETCDF_LOG_LEVEL=4_).
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param new_level The new logging level.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2014-12-29 13:04:48 +08:00
|
|
|
int
|
2010-06-03 21:24:43 +08:00
|
|
|
nc_set_log_level(int new_level)
|
|
|
|
{
|
2018-05-13 01:27:11 +08:00
|
|
|
#ifdef LOGGING
|
2016-01-29 06:03:40 +08:00
|
|
|
if(!nc4_hdf5_initialized)
|
2017-12-05 03:21:14 +08:00
|
|
|
nc4_hdf5_initialize();
|
2016-01-29 06:03:40 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Now remember the new level. */
|
|
|
|
nc_log_level = new_level;
|
|
|
|
LOG((4, "log_level changed to %d", nc_log_level));
|
2018-05-13 01:27:11 +08:00
|
|
|
#endif /*LOGGING */
|
2010-06-03 21:24:43 +08:00
|
|
|
return 0;
|
2014-12-29 13:04:48 +08:00
|
|
|
}
|
2018-05-13 01:46:04 +08:00
|
|
|
#endif /* ENABLE_SET_LOG_LEVEL */
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-05-13 01:27:11 +08:00
|
|
|
#ifdef LOGGING
|
2010-06-03 21:24:43 +08:00
|
|
|
#define MAX_NESTS 10
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
2017-12-05 03:21:14 +08:00
|
|
|
* @internal Recursively print the metadata of a group.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @param grp Pointer to group info struct.
|
|
|
|
* @param tab_count Number of tabs.
|
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
static int
|
2014-02-12 07:12:08 +08:00
|
|
|
rec_print_metadata(NC_GRP_INFO_T *grp, int tab_count)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
|
|
|
NC_GRP_INFO_T *g;
|
|
|
|
NC_ATT_INFO_T *att;
|
|
|
|
NC_VAR_INFO_T *var;
|
|
|
|
NC_DIM_INFO_T *dim;
|
|
|
|
NC_TYPE_INFO_T *type;
|
|
|
|
NC_FIELD_INFO_T *field;
|
2017-12-21 05:38:34 +08:00
|
|
|
char tabs[MAX_NESTS+1] = "";
|
2013-01-29 02:31:02 +08:00
|
|
|
char *dims_string = NULL;
|
2010-06-03 21:24:43 +08:00
|
|
|
char temp_string[10];
|
2016-07-14 01:13:14 +08:00
|
|
|
int t, retval, d, i;
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
/* Come up with a number of tabs relative to the group. */
|
2014-02-12 07:12:08 +08:00
|
|
|
for (t = 0; t < tab_count && t < MAX_NESTS; t++)
|
2017-12-21 05:38:34 +08:00
|
|
|
tabs[t] = '\t';
|
|
|
|
tabs[t] = '\0';
|
2010-06-03 21:24:43 +08:00
|
|
|
|
|
|
|
LOG((2, "%s GROUP - %s nc_grpid: %d nvars: %d natts: %d",
|
2018-03-17 01:46:18 +08:00
|
|
|
tabs, grp->hdr.name, grp->hdr.id, ncindexsize(grp->vars), ncindexsize(grp->att)));
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(grp->att);i++) {
|
|
|
|
att = (NC_ATT_INFO_T*)ncindexith(grp->att,i);
|
|
|
|
if(att == NULL) continue;
|
2010-06-03 21:24:43 +08:00
|
|
|
LOG((2, "%s GROUP ATTRIBUTE - attnum: %d name: %s type: %d len: %d",
|
2018-03-17 01:46:18 +08:00
|
|
|
tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len));
|
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(grp->dim);i++) {
|
|
|
|
dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
|
|
|
|
if(dim == NULL) continue;
|
2010-06-03 21:24:43 +08:00
|
|
|
LOG((2, "%s DIMENSION - dimid: %d name: %s len: %d unlimited: %d",
|
2018-03-17 01:46:18 +08:00
|
|
|
tabs, dim->hdr.id, dim->hdr.name, dim->len, dim->unlimited));
|
|
|
|
}
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(grp->vars);i++)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
int j;
|
|
|
|
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
|
|
|
|
if (var == NULL) continue;
|
2013-07-11 04:09:31 +08:00
|
|
|
if(var->ndims > 0)
|
|
|
|
{
|
|
|
|
dims_string = (char*)malloc(sizeof(char)*(var->ndims*4));
|
|
|
|
strcpy(dims_string, "");
|
|
|
|
for (d = 0; d < var->ndims; d++)
|
2017-12-05 03:21:14 +08:00
|
|
|
{
|
|
|
|
sprintf(temp_string, " %d", var->dimids[d]);
|
|
|
|
strcat(dims_string, temp_string);
|
|
|
|
}
|
2013-07-11 04:09:31 +08:00
|
|
|
}
|
2018-08-07 00:16:49 +08:00
|
|
|
LOG((2, "%s VARIABLE - varid: %d name: %s ndims: %d dimscale: %d dimids:%s",
|
|
|
|
tabs, var->hdr.id, var->hdr.name, var->ndims, (int)var->dimscale,
|
|
|
|
(dims_string ? dims_string : " -")));
|
2018-03-17 01:46:18 +08:00
|
|
|
for(j=0;j<ncindexsize(var->att);j++) {
|
|
|
|
att = (NC_ATT_INFO_T*)ncindexith(var->att,j);
|
2018-08-08 05:17:40 +08:00
|
|
|
if(att == NULL) continue;
|
2017-12-05 03:21:14 +08:00
|
|
|
LOG((2, "%s VAR ATTRIBUTE - attnum: %d name: %s type: %d len: %d",
|
2018-03-17 01:46:18 +08:00
|
|
|
tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len));
|
|
|
|
}
|
2013-07-11 04:09:31 +08:00
|
|
|
if(dims_string)
|
|
|
|
{
|
|
|
|
free(dims_string);
|
|
|
|
dims_string = NULL;
|
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(grp->type);i++)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
if((type = (NC_TYPE_INFO_T*)ncindexith(grp->type,i)) == NULL) continue;
|
|
|
|
LOG((2, "%s TYPE - nc_typeid: %d hdf_typeid: 0x%x committed: %d "
|
|
|
|
"name: %s num_fields: %d", tabs, type->hdr.id,
|
|
|
|
type->hdf_typeid, type->size, (int)type->committed, type->hdr.name));
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Is this a compound type? */
|
2014-02-12 07:12:08 +08:00
|
|
|
if (type->nc_type_class == NC_COMPOUND)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-08-08 05:17:40 +08:00
|
|
|
int j;
|
2017-12-05 03:21:14 +08:00
|
|
|
LOG((3, "compound type"));
|
2018-08-08 05:17:40 +08:00
|
|
|
for(j=0;j<nclistlength(type->u.c.field);j++) {
|
|
|
|
field = (NC_FIELD_INFO_T*)nclistget(type->u.c.field,j);
|
2018-03-17 01:46:18 +08:00
|
|
|
LOG((4, "field %s offset %d nctype %d ndims %d", field->hdr.name,
|
2017-12-05 03:21:14 +08:00
|
|
|
field->offset, field->nc_typeid, field->ndims));
|
2018-08-08 05:17:40 +08:00
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
2014-02-12 07:12:08 +08:00
|
|
|
else if (type->nc_type_class == NC_VLEN)
|
|
|
|
{
|
2017-12-05 03:21:14 +08:00
|
|
|
LOG((3, "VLEN type"));
|
2014-02-12 07:12:08 +08:00
|
|
|
LOG((4, "base_nc_type: %d", type->u.v.base_nc_typeid));
|
|
|
|
}
|
|
|
|
else if (type->nc_type_class == NC_OPAQUE)
|
2017-12-05 03:21:14 +08:00
|
|
|
LOG((3, "Opaque type"));
|
2014-02-12 07:12:08 +08:00
|
|
|
else if (type->nc_type_class == NC_ENUM)
|
|
|
|
{
|
2017-12-05 03:21:14 +08:00
|
|
|
LOG((3, "Enum type"));
|
2014-02-12 07:12:08 +08:00
|
|
|
LOG((4, "base_nc_type: %d", type->u.e.base_nc_typeid));
|
|
|
|
}
|
2010-06-03 21:24:43 +08:00
|
|
|
else
|
|
|
|
{
|
2017-12-05 03:21:14 +08:00
|
|
|
LOG((0, "Unknown class: %d", type->nc_type_class));
|
|
|
|
return NC_EBADTYPE;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
}
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Call self for each child of this group. */
|
2018-03-17 01:46:18 +08:00
|
|
|
for(i=0;i<ncindexsize(grp->children);i++)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-03-17 01:46:18 +08:00
|
|
|
if((g = (NC_GRP_INFO_T*)ncindexith(grp->children,i)) == NULL) continue;
|
|
|
|
if ((retval = rec_print_metadata(g, tab_count + 1)))
|
2018-08-08 05:17:40 +08:00
|
|
|
return retval;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Print out the internal metadata for a file. This is
|
|
|
|
* useful to check that netCDF is working! Nonetheless, this function
|
2017-12-05 03:21:14 +08:00
|
|
|
* will print nothing if logging is not set to at least two.
|
2017-12-03 22:57:21 +08:00
|
|
|
*
|
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
2012-09-07 03:44:03 +08:00
|
|
|
log_metadata_nc(NC *nc)
|
2010-06-03 21:24:43 +08:00
|
|
|
{
|
2018-06-22 21:08:09 +08:00
|
|
|
NC_FILE_INFO_T *h5 = NC4_DATA(nc);
|
2010-06-03 21:24:43 +08:00
|
|
|
|
2014-12-29 13:04:48 +08:00
|
|
|
LOG((2, "*** NetCDF-4 Internal Metadata: int_ncid 0x%x ext_ncid 0x%x",
|
2017-12-05 03:21:14 +08:00
|
|
|
nc->int_ncid, nc->ext_ncid));
|
2010-06-03 21:24:43 +08:00
|
|
|
if (!h5)
|
|
|
|
{
|
|
|
|
LOG((2, "This is a netCDF-3 file."));
|
|
|
|
return NC_NOERR;
|
|
|
|
}
|
2018-07-20 03:13:23 +08:00
|
|
|
LOG((2, "FILE - path: %s cmode: 0x%x parallel: %d redef: %d "
|
|
|
|
"fill_mode: %d no_write: %d next_nc_grpid: %d", nc->path,
|
2017-12-05 03:21:14 +08:00
|
|
|
h5->cmode, (int)h5->parallel, (int)h5->redef, h5->fill_mode, (int)h5->no_write,
|
|
|
|
h5->next_nc_grpid));
|
2018-03-17 01:46:18 +08:00
|
|
|
if(nc_log_level >= 2)
|
2018-08-08 05:17:40 +08:00
|
|
|
return rec_print_metadata(h5->root_grp, 0);
|
2018-03-17 01:46:18 +08:00
|
|
|
return NC_NOERR;
|
2010-06-03 21:24:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /*LOGGING */
|
|
|
|
|
2017-12-03 22:57:21 +08:00
|
|
|
/**
|
|
|
|
* @internal Show the in-memory metadata for a netcdf file.
|
|
|
|
*
|
2017-12-05 03:21:14 +08:00
|
|
|
* @param ncid File and group ID.
|
|
|
|
*
|
2017-12-03 22:57:21 +08:00
|
|
|
* @return ::NC_NOERR No error.
|
|
|
|
* @author Ed Hartnett
|
2017-12-05 03:21:14 +08:00
|
|
|
*/
|
2010-06-03 21:24:43 +08:00
|
|
|
int
|
|
|
|
NC4_show_metadata(int ncid)
|
|
|
|
{
|
|
|
|
int retval = NC_NOERR;
|
|
|
|
#ifdef LOGGING
|
2012-09-07 03:44:03 +08:00
|
|
|
NC *nc;
|
2010-06-03 21:24:43 +08:00
|
|
|
int old_log_level = nc_log_level;
|
2014-12-29 13:04:48 +08:00
|
|
|
|
2010-06-03 21:24:43 +08:00
|
|
|
/* Find file metadata. */
|
2012-09-07 03:44:03 +08:00
|
|
|
if (!(nc = nc4_find_nc_file(ncid,NULL)))
|
2010-06-03 21:24:43 +08:00
|
|
|
return NC_EBADID;
|
|
|
|
|
|
|
|
/* Log level must be 2 to see metadata. */
|
|
|
|
nc_log_level = 2;
|
|
|
|
retval = log_metadata_nc(nc);
|
|
|
|
nc_log_level = old_log_level;
|
|
|
|
#endif /*LOGGING*/
|
|
|
|
return retval;
|
|
|
|
}
|