netcdf-c/ncdump/dumplib.c
2019-09-18 08:03:01 -06:00

1886 lines
49 KiB
C

/*********************************************************************
* Copyright 2018, University Corporation for Atmospheric Research
* See netcdf/README file for copying and redistribution conditions.
* $Header: /upc/share/CVS/netcdf-3/ncdump/dumplib.c,v 1.85 2010/05/05 22:15:39 dmh Exp $
*********************************************************************/
/*
* We potentially include <stdarg.h> before <stdio.h> in order to obtain a
* definition for va_list from the GNU C compiler.
*/
#include "config.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#ifndef NO_FLOAT_H
#include <float.h> /* for FLT_EPSILON, DBL_EPSILON */
#endif /* NO_FLOAT_H */
#include <math.h>
#include <netcdf.h>
#include "utils.h"
#include "nccomps.h"
#include "dumplib.h"
#include "ncdump.h"
#include "isnan.h"
#include "nctime0.h"
static float float_eps;
static double double_eps;
extern fspec_t formatting_specs; /* set from command-line options */
static float
float_epsilon(void)
{
float float_eps;
#ifndef NO_FLOAT_H
float_eps = FLT_EPSILON;
#else /* NO_FLOAT_H */
{
float etop, ebot, eps;
float one = 1.0;
float two = 2.0;
etop = 1.0;
ebot = 0.0;
eps = ebot + (etop - ebot)/two;
while (eps != ebot && eps != etop) {
float epsp1;
epsp1 = one + eps;
if (epsp1 > one)
etop = eps;
else
ebot = eps;
eps = ebot + (etop - ebot)/two;
}
float_eps = two * etop;
}
#endif /* NO_FLOAT_H */
return float_eps;
}
static double
double_epsilon(void)
{
double double_eps;
#ifndef NO_FLOAT_H
double_eps = DBL_EPSILON;
#else /* NO_FLOAT_H */
{
double etop, ebot, eps;
double one = 1.0;
double two = 2.0;
etop = 1.0;
ebot = 0.0;
eps = ebot + (etop - ebot)/two;
while (eps != ebot && eps != etop) {
double epsp1;
epsp1 = one + eps;
if (epsp1 > one)
etop = eps;
else
ebot = eps;
eps = ebot + (etop - ebot)/two;
}
double_eps = two * etop;
}
#endif /* NO_FLOAT_H */
return double_eps;
}
void
init_epsilons(void)
{
float_eps = float_epsilon();
double_eps = double_epsilon();
}
static char* has_c_format_att(int ncid, int varid);
int float_precision_specified = 0; /* -p option specified float precision */
int double_precision_specified = 0; /* -p option specified double precision */
char float_var_fmt[] = "%.NNg";
char double_var_fmt[] = "%.NNg";
char float_att_fmt[] = "%#.NNgf";
char float_attx_fmt[] = "%#.NNg";
char double_att_fmt[] = "%#.NNg";
/* magic number stored in a safebuf and checked, hoping it will be
* changed if buffer was overwritten inadvertently */
#define SAFEBUF_CERT 2147114711
/* expression for where SAFEBUF_CERT is stored within safebuf (at end
* of buffer, after data) */
#define SAFEBUF_EXPR(sbuf) (*(int *)((sbuf)->buf + (sbuf)->len))
/* expression to be checked whenever a safebuf is used */
#define SAFEBUF_CHECK(sbuf) (SAFEBUF_EXPR(sbuf) == SAFEBUF_CERT)
/* somewhat arbitrary initial size of safebufs, grow as needed */
#define SAFEBUF_INIT_LEN 128
/* initialize safe buffer */
safebuf_t *
sbuf_new() {
size_t len = SAFEBUF_INIT_LEN;
safebuf_t *sb;
sb = (safebuf_t *) emalloc(sizeof(safebuf_t));
sb->buf = (char *)emalloc(len + sizeof(int));
sb->len = len;
/* write a "stamp" in last 4 bytes of buffer for id and to check for overflow */
SAFEBUF_EXPR(sb) = SAFEBUF_CERT;
sb->buf[0] = 0;
sb->cl = strlen(sb->buf);
assert(SAFEBUF_CHECK(sb));
return sb;
}
/* grow buffer to at least len bytes, copying previous contents if
* necessary */
void
sbuf_grow(safebuf_t *sb, size_t len) {
size_t m = sb->len;
void *tmp;
assert(SAFEBUF_CHECK(sb));
if (len <= m)
return;
/* Make sure we at least double size of buffer to get what's
* needed. If we just used realloc(), no guarantee that length
* would be expanded by a multiple, which we want. */
while(len > m) {
m *= 2;
}
tmp = emalloc(m + sizeof(int));
memcpy(tmp, sb->buf, sb->len);
sb->len = m;
free(sb->buf);
sb->buf = tmp;
SAFEBUF_EXPR(sb) = SAFEBUF_CERT;
assert(SAFEBUF_CHECK(sb));
}
/* Copy string s2 to safe buffer, growing if necessary */
void
sbuf_cpy(safebuf_t *sb, const char *s2) {
size_t s2len;
assert(SAFEBUF_CHECK(sb));
s2len = strlen(s2);
sbuf_grow(sb, 1 + s2len);
strncpy(sb->buf, s2, sb->len);
sb->cl = s2len;
assert(SAFEBUF_CHECK(sb));
}
/* Concatenate string s2 to end of string in safe buffer, growing if necessary */
void
sbuf_cat(safebuf_t *sb, const char *s2) {
size_t s2len;
size_t res;
assert(SAFEBUF_CHECK(sb));
s2len = strlen(s2);
sbuf_grow(sb, 1 + sb->cl + s2len);
res = strlcat(sb->buf + sb->cl, s2, sb->len);
assert( res < sb->len );
sb->cl += s2len;
assert(SAFEBUF_CHECK(sb));
}
/* Concatenate string in safebuf s2 to end of string in safebuf s1,
* growing if necessary */
void
sbuf_catb(safebuf_t *s1, const safebuf_t *s2) {
size_t s2len;
size_t res;
assert(SAFEBUF_CHECK(s1));
assert(SAFEBUF_CHECK(s2));
s2len = sbuf_len(s2);
sbuf_grow(s1, 1 + s1->cl + s2len);
res = strlcat(s1->buf + s1->cl, s2->buf, s1->len);
assert( res < s1->len );
s1->cl += s2len;
assert(SAFEBUF_CHECK(s1));
}
/* Return length of string in sbuf */
size_t
sbuf_len(const safebuf_t *sb) {
assert(SAFEBUF_CHECK(sb));
return sb->cl;
}
/* Return C string in an sbuf */
char *
sbuf_str(const safebuf_t *sb) {
assert(SAFEBUF_CHECK(sb));
return sb->buf;
}
/* free safe buffer */
void
sbuf_free(safebuf_t *sb) {
assert(SAFEBUF_CHECK(sb));
free(sb->buf);
free(sb);
}
/* In case different formats specified with -d option, set them here. */
void
set_formats(int float_digits, int double_digits)
{
int res;
res = snprintf(float_var_fmt, strlen(float_var_fmt) + 1, "%%.%dg",
float_digits) + 1;
assert(res <= sizeof(float_var_fmt));
res = snprintf(double_var_fmt, strlen(double_var_fmt) + 1, "%%.%dg",
double_digits) + 1;
assert(res <= sizeof(double_var_fmt));
res = snprintf(float_att_fmt, strlen(float_att_fmt) + 1, "%%#.%dgf",
float_digits) + 1;
assert(res <= sizeof(float_att_fmt));
res = snprintf(float_attx_fmt, strlen(float_attx_fmt) + 1, "%%#.%dg",
float_digits) + 1;
assert(res <= sizeof(float_attx_fmt));
res = snprintf(double_att_fmt, strlen(double_att_fmt) + 1, "%%#.%dg",
double_digits) + 1;
assert(res <= sizeof(double_att_fmt));
}
static char *
has_c_format_att(
int ncid, /* netcdf id */
int varid /* variable id */
)
{
nc_type cfmt_type;
size_t cfmt_len;
#define C_FMT_NAME "C_format" /* name of C format attribute */
#define MAX_CFMT_LEN 100 /* max length of C format attribute */
static char cfmt[MAX_CFMT_LEN];
/* we expect nc_inq_att to fail if there is no "C_format" attribute */
int nc_stat = nc_inq_att(ncid, varid, "C_format", &cfmt_type, &cfmt_len);
switch(nc_stat) {
case NC_NOERR:
if (cfmt_type == NC_CHAR && cfmt_len != 0 && cfmt_len < MAX_CFMT_LEN) {
int nc_stat = nc_get_att_text(ncid, varid, "C_format", cfmt);
if(nc_stat != NC_NOERR) {
fprintf(stderr, "Getting 'C_format' attribute %s\n",
nc_strerror(nc_stat));
(void) fflush(stderr);
}
cfmt[cfmt_len] = '\0';
return &cfmt[0];
}
break;
case NC_ENOTATT:
break;
default:
fprintf(stderr, "Inquiring about 'C_format' attribute %s\n",
nc_strerror(nc_stat));
(void) fflush(stderr);
break;
}
return 0;
}
/* Return default format to use for a primitive type */
const char *
get_default_fmt(nc_type typeid) {
/* Otherwise return sensible default. */
switch (typeid) {
case NC_BYTE:
return "%d";
case NC_CHAR:
return "%s";
case NC_SHORT:
return "%d";
case NC_INT:
return "%d";
case NC_FLOAT:
return float_var_fmt;
case NC_DOUBLE:
return double_var_fmt;
case NC_UBYTE:
return "%u";
case NC_USHORT:
return "%u";
case NC_UINT:
return "%u";
case NC_INT64:
return "%lld";
case NC_UINT64:
return "%llu";
case NC_STRING:
return "\"%s\"";
default:
break;
}
return ""; /* user-defined types don't use fmt member */
}
/*
* Determine print format to use for each primitive value for this
* variable. Use value of attribute C_format if it exists, otherwise
* a sensible default.
*/
const char *
get_fmt(
int ncid,
int varid,
nc_type typeid
)
{
char *c_format_att;
/* float or double precision specified with -p option overrides any
C_format attribute value, so check for that first. */
if (float_precision_specified && typeid == NC_FLOAT)
return float_var_fmt;
if (double_precision_specified && typeid == NC_DOUBLE)
return double_var_fmt;
/* If C_format attribute exists, return it */
c_format_att = has_c_format_att(ncid, varid);
if (c_format_att)
return c_format_att;
return get_default_fmt(typeid);
}
/* Return primitive type name */
static const char *
prim_type_name(nc_type type)
{
switch (type) {
case NC_BYTE:
return "byte";
case NC_CHAR:
return "char";
case NC_SHORT:
return "short";
case NC_INT:
return "int";
case NC_FLOAT:
return "float";
case NC_DOUBLE:
return "double";
case NC_UBYTE:
return "ubyte";
case NC_USHORT:
return "ushort";
case NC_UINT:
return "uint";
case NC_INT64:
return "int64";
case NC_UINT64:
return "uint64";
case NC_STRING:
return "string";
default:
error("prim_type_name: bad type %d", type);
return "bogus";
}
}
static int max_type = 0;
static int max_atomic_type = 0;
static nctype_t **nctypes = 0; /* holds all types in a netCDF dataset */
#ifdef USE_NETCDF4
/* return number of user-defined types in a group and all its subgroups */
static int
count_udtypes(int ncid) {
int ntypes = 0;
int numgrps;
int *ncids;
int i;
int format;
NC_CHECK( nc_inq_format(ncid, &format) );
if (format == NC_FORMAT_NETCDF4) {
/* Get number of types in this group */
NC_CHECK( nc_inq_typeids(ncid, &ntypes, NULL) ) ;
NC_CHECK( nc_inq_grps(ncid, &numgrps, NULL) ) ;
ncids = (int *) emalloc(sizeof(int) * (numgrps + 1));
NC_CHECK( nc_inq_grps(ncid, NULL, ncids) ) ;
/* Add number of types in each subgroup, if any */
for (i=0; i < numgrps; i++) {
ntypes += count_udtypes(ncids[i]);
}
free(ncids);
}
return ntypes;
}
#endif /*USE_NETCDF4*/
/* This routine really is intended to return the max atomic typeid */
static int
max_typeid(int ncid) {
int maxtypes = NC_NAT;
int maxatomictypes = NC_NAT;
int format = 0;
int err = NC_NOERR;
/* get the file type */
err = nc_inq_format(ncid,&format);
if(err) {
fprintf(stderr,"%s: Cannot get file format.\n",nc_strerror(err));
return 0;
}
switch (format) {
case NC_FORMAT_CLASSIC:
case NC_FORMAT_NETCDF4_CLASSIC:
case NC_FORMAT_64BIT_OFFSET:
maxatomictypes = (maxtypes = NC_DOUBLE); /*ignore NC_NAT?*/
break;
case NC_FORMAT_64BIT_DATA:
maxatomictypes = (maxtypes = NC_UINT64);
break;
case NC_FORMAT_NETCDF4:
#ifdef USE_NETCDF4
{
int nuser = 0;
maxatomictypes = (maxtypes = NC_STRING); /* extra netCDF-4 primitive types */
maxtypes += 4; /* user-defined classes */
nuser = count_udtypes(ncid);
if(nuser > 0)
maxtypes = NC_FIRSTUSERTYPEID + (nuser - 1);
} break;
#else
/* fallthru */
#endif
default:
fprintf(stderr,"Unexpected file format: %d\n",format);
return 0;
}
max_type = maxtypes;
max_atomic_type = maxatomictypes;
return maxtypes;
}
void typeadd(nctype_t *typep) {
nctypes[typep->tid] = typep;
}
/* From type id, get full type info */
nctype_t *
get_typeinfo ( int typeid ) {
if(typeid < 0 || typeid > max_type)
error("ncdump: %d is an invalid type id", typeid);
return nctypes[typeid];
}
/* void */
/* xfree_typeinfo(int ncid) { */
/* int i; */
/* for (i = 0; i < number_of_types; i++) { */
/* nctype_t *tinfop = nctypes[i]; */
/* if (tinfop) { */
/* if(tinfop->name) */
/* free(tinfop->name); */
/* if(tinfop->grps) */
/* free(tinfop->grps); */
/* free(tinfop); */
/* } */
/* } */
/* } */
bool_t
ncbyte_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
return ( *(signed char* )v1p == *(signed char* )v2p);
}
bool_t
ncchar_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
return ( *(char* )v1p == *(char* )v2p);
}
bool_t
ncshort_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
return ( *(short* )v1p == *(short* )v2p);
}
bool_t
ncint_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
return ( *(int* )v1p == *(int* )v2p);
}
#define absval(x) ( (x) < 0 ? -(x) : (x) )
/*
* Return ( *(float* )v1p == *(float* )v2p);
* except use floating epsilon to compare very close vals as equal
* and handle IEEE NaNs and infinities.
*/
bool_t
ncfloat_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
float v1 = *(float* )v1p;
float v2 = *(float* )v2p;
if((v1 > 0.0f) != (v2 > 0.0f)) /* avoid overflow */
return false;
if(isfinite(v1) && isfinite(v2))
return (absval(v1 - v2) <= absval(float_eps * v2)) ;
if(isnan(v1) && isnan(v2))
return true;
if(isinf(v1) && isinf(v2))
return true;
return false;
}
/*
* Return ( *(double* )v1p == *(double* )v2p);
* except use floating epsilon to compare very close vals as equal
* and handle IEEE NaNs and infinities.
*/
bool_t
ncdouble_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
double v1 = *(double* )v1p;
double v2 = *(double* )v2p;
if((v1 > 0.0) != (v2 > 0.0)) /* avoid overflow */
return false;
if(isfinite(v1) && isfinite(v2))
return (absval(v1 - v2) <= absval(double_eps * v2)) ;
if(isnan(v1) && isnan(v2))
return true;
if(isinf(v1) && isinf(v2))
return true;
return false;
}
bool_t
ncubyte_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
return ( *(unsigned char* )v1p == *(unsigned char* )v2p);
}
bool_t
ncushort_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
return ( *(unsigned short* )v1p == *(unsigned short* )v2p);
}
bool_t
ncuint_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
return ( *(unsigned int* )v1p == *(unsigned int* )v2p);
}
bool_t
ncint64_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
return ( *(long long* )v1p == *(long long* )v2p);
}
bool_t
ncuint64_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
return ( *(unsigned long long* )v1p == *(unsigned long long* )v2p);
}
bool_t
ncstring_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
if (NULL == *((char **)v1p) && NULL == *((char **)v2p))
return(1);
else if (NULL != *((char **)v1p) && NULL == *((char **)v2p))
return(0);
else if (NULL == *((char **)v1p) && NULL != *((char **)v2p))
return(0);
return (strcmp(*((char **)v1p), *((char **)v2p)) == 0);
}
#ifdef USE_NETCDF4
bool_t
ncopaque_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
size_t nbytes = this->size;
const char *c1p = (const char *) v1p;
const char *c2p = (const char *) v2p;
int i;
for (i=0; i < nbytes; i++) {
if (*c1p++ != *c2p++)
return false;
}
return true;
}
bool_t
ncvlen_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
size_t v1len = ((nc_vlen_t *)v1p)->len;
size_t v2len = ((nc_vlen_t *)v2p)->len;
if (v1len != v2len)
return false;
{
size_t base_size = this->size;
nc_type base_type = this->base_tid;
nctype_t *base_info = get_typeinfo(base_type);
val_equals_func base_val_equals = base_info->val_equals;
const char *v1dat = ((nc_vlen_t *)v1p)->p;
const char *v2dat = ((nc_vlen_t *)v2p)->p;
size_t i;
for(i = 0; i < v1len; i++) {
if (base_val_equals(base_info, (const void *)v1dat,
(const void *)v2dat) != true)
return false;
v1dat += base_size;
v2dat += base_size;
}
}
return true;
}
/* Determine if two compound values are equal, by testing equality of
* each member field. */
bool_t
nccomp_val_equals(const nctype_t *this,
const void *v1p, const void *v2p) {
int nfields = this->nfields;
int fidx; /* field id */
for (fidx = 0; fidx < nfields; fidx++) {
size_t offset = this->offsets[fidx];
nc_type fid = this->fids[fidx]; /* field type id */
nctype_t *finfo = get_typeinfo(fid);
if(finfo->ranks == 0 || finfo->ranks[fidx] == 0) {
if(! finfo->val_equals(finfo,
(char *)v1p + offset, (char *)v2p + offset))
return false;
} else { /* this field is an array */
int i; /* array element counter when rank > 0 */
void *v1elem = (char *)v1p + offset;
void *v2elem = (char *)v2p + offset;
for(i = 0; i < finfo->nvals[fidx]; i++) {
if(! finfo->val_equals(finfo, v1elem, v2elem))
return false;
v1elem = (char *)v1elem + finfo->size;
v2elem = (char *)v1elem + finfo->size;
}
}
}
return true;
}
#endif /* USE_NETCDF4 */
int
ncbyte_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, *(signed char *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncchar_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, *(char *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncshort_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, *(short *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncint_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, *(int *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
/* CDL canonical representations of some special floating point values */
#define NCDL_NANF "NaNf"
#define NCDL_NAN "NaN"
#define NCDL_INFF "Infinityf"
#define NCDL_INF "Infinity"
/* Convert a float NaN or Infinity to an allocated string large enough
* to hold it (at least PRIM_LEN chars) */
static void
float_special_tostring(float vv, char *sout) {
if(isnan(vv)) {
snprintf(sout, PRIM_LEN, "%s", NCDL_NANF);
} else if(isinf(vv)) {
if(vv < 0.0) {
snprintf(sout, PRIM_LEN, "-%s", NCDL_INFF);
} else {
snprintf(sout, PRIM_LEN, "%s", NCDL_INFF);
}
} else
assert(false); /* vv was finite */
}
/* Convert a double NaN or Infinity to an allocated string large enough
* to hold it (at least PRIM_LEN chars) */
static void
double_special_tostring(double vv, char *sout) {
if(isnan(vv)) {
snprintf(sout, PRIM_LEN, "%s", NCDL_NAN);
} else if(isinf(vv)) {
if(vv < 0.0) {
snprintf(sout, PRIM_LEN, "-%s", NCDL_INF);
} else {
snprintf(sout, PRIM_LEN, "%s", NCDL_INF);
}
} else
assert(false); /* vv was finite */
}
int
ncfloat_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
float vv = *(float *)valp;
if(isfinite(vv)) {
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, vv);
assert(res < PRIM_LEN);
} else {
float_special_tostring(vv, sout);
}
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncdouble_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
double vv = *(double *)valp;
if(isfinite(vv)) {
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, vv);
assert(res < PRIM_LEN);
} else {
double_special_tostring(vv, sout);
}
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncubyte_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, *(unsigned char *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncushort_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, *(unsigned short *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncuint_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, *(unsigned int *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncint64_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, *(long long *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncuint64_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, typ->fmt, *(unsigned long long *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int ncstring_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp)
{
const char *cp;
cp = ((char **)valp)[0];
if(cp) {
size_t slen;
char *sout;
char *sp;
unsigned char uc;
slen = 4 + 5 * strlen(cp); /* need "'s around string, and extra space to escape control characters */
slen++; /* nul term */
sout = emalloc(slen);
sp = sout;
*sp++ = '"' ;
while(*cp) {
switch (uc = *cp++ & 0377) {
case '\b':
*sp++ = '\\';
*sp++ = 'b' ;
break;
case '\f':
*sp++ = '\\';
*sp++ = 'f';
break;
case '\n':
*sp++ = '\\';
*sp++ = 'n';
break;
case '\r':
*sp++ = '\\';
*sp++ = 'r';
break;
case '\t':
*sp++ = '\\';
*sp++ = 't';
break;
case '\v':
*sp++ = '\\';
*sp++ = 'n';
break;
case '\\':
*sp++ = '\\';
*sp++ = '\\';
break;
case '\'':
*sp++ = '\\';
*sp++ = '\'';
break;
case '\"':
*sp++ = '\\';
*sp++ = '\"';
break;
default:
if (iscntrl(uc)) {
snprintf(sp,4+1,"\\%03o",uc); /* +1 for nul */
sp += 4;
}
else
*sp++ = uc;
break;
}
}
*sp++ = '"' ;
*sp = '\0' ;
sbuf_cpy(sfbf, sout);
free(sout);
}
else {
sbuf_cpy(sfbf, "NIL");
}
return sbuf_len(sfbf);
}
#ifdef USE_NETCDF4
int
ncenum_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
char symbol[NC_MAX_NAME + 1];
long long val = 0;
switch (typ->base_tid) {
case NC_BYTE:
val = *(signed char *)valp;
break;
case NC_UBYTE:
val = *(unsigned char *)valp;
break;
case NC_SHORT:
val = *(short *)valp;
break;
case NC_USHORT:
val = *(unsigned short *)valp;
break;
case NC_INT:
val = *(int *)valp;
break;
case NC_UINT:
val = *(unsigned int *)valp;
break;
case NC_INT64:
val = *(long long *)valp;
break;
case NC_UINT64:
val = *(long long *)valp;
break;
default:
error("bad base type for enum");
break;
}
NC_CHECK( nc_inq_enum_ident(typ->ncid, typ->tid, val, symbol));
sbuf_cpy(sfbf, symbol);
return sbuf_len(sfbf);
}
/* Given an opaque type size and opaque value, convert to a string,
* represented as hexadecimal characters, returning number of chars in
* output string */
int
ncopaque_val_as_hex(size_t size, char *sout, const void *valp) {
const unsigned char *cp = valp;
char *sp = sout;
int i;
char *prefix = "0X";
int prelen = strlen(prefix);
snprintf(sp, prelen + 1, "%s", prefix);
sp += prelen;
for(i = 0; i < size; i++) {
int res;
res = snprintf(sp, prelen + 1, "%.2X", *cp++);
assert (res == 2);
sp += 2;
}
*sp = '\0';
return 2*size + prelen;
}
/* Convert an opaque value to a string, represented as hexadecimal
* characters */
int
ncopaque_typ_tostring(const nctype_t *typ, safebuf_t *sfbf,
const void *valp) {
char* sout = (char *) emalloc(2 * typ->size + strlen("0X") + 1);
(void) ncopaque_val_as_hex(typ->size, sout, valp);
sbuf_cpy(sfbf, sout);
free(sout);
return sbuf_len(sfbf);
}
/* Convert a vlen value to a string, by using tostring function for base type */
int
ncvlen_typ_tostring(const nctype_t *tinfo, safebuf_t *sfbf, const void *valp) {
nc_type base_type = tinfo->base_tid;
nctype_t *base_info = get_typeinfo(base_type);
size_t base_size = base_info->size;
size_t len = ((nc_vlen_t *)valp)->len;
typ_tostring_func base_typ_tostring = base_info->typ_tostring;
size_t i;
const char *vp; /* instead of void* so can increment to next */
safebuf_t* sout2 = sbuf_new();
sbuf_cpy(sfbf, "{");
/* put each val in sout2, then append sout2 to sfbf */
vp = ((nc_vlen_t *)valp)->p;
for(i = 0; i < len; i++) {
(void) base_typ_tostring(base_info, sout2, vp);
sbuf_catb(sfbf, sout2);
if(i < len - 1) {
sbuf_cat(sfbf, ", ");
}
vp += base_size;
}
sbuf_cat(sfbf, "}");
sbuf_free(sout2);
return sbuf_len(sfbf);
}
/*
* Print a number of char values as a text string.
*/
static int
chars_tostring(
safebuf_t *sbuf, /* for output */
size_t len, /* number of characters */
const char *vals /* pointer to block of values */
)
{
long iel;
const char *sp;
char *sout = (char *)emalloc(4*len + 5); /* max len of string */
char *cp = sout;
*cp++ = '"';
/* adjust len so trailing nulls don't get printed */
sp = vals + len;
while (len != 0 && *--sp == '\0')
len--;
for (iel = 0; iel < len; iel++) {
unsigned char uc;
switch (uc = *vals++ & 0377) {
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
case '\\':
case '\'':
case '\"':
*cp++ = '\\';
*cp++ = *(char *)&uc; /* just copy, even if char is signed */
break;
default:
if (isprint(uc))
*cp++ = *(char *)&uc; /* just copy, even if char is signed */
else {
sprintf(cp,"\\%.3o",uc);
cp += 4;
}
break;
}
}
*cp++ = '"';
*cp = '\0';
sbuf_cpy(sbuf, sout);
free(sout);
return sbuf_len(sbuf);
}
/* Convert a compound value to a string, by using tostring function for
each member field */
int
nccomp_typ_tostring(const nctype_t *tinfo, safebuf_t *sfbf, const void *valp) {
int nfields = tinfo->nfields;
int fidx; /* field id */
safebuf_t* sout2 = sbuf_new();
sbuf_cpy(sfbf, "{");
/* put each val in sout2, then append sout2 to sfbf if enough room */
for (fidx = 0; fidx < nfields; fidx++) {
size_t offset = tinfo->offsets[fidx];
nc_type fid = tinfo->fids[fidx]; /* field type id */
nctype_t *finfo = get_typeinfo(fid);
if(tinfo->ranks[fidx] == 0) {
if(finfo->tid == NC_CHAR) { /* aggregate char rows into strings */
chars_tostring(sout2, 1, ((char *)valp + offset));
} else {
finfo->typ_tostring(finfo, sout2, ((char *)valp + offset));
}
} else { /* this field is an array */
int i; /* array element counter when rank > 0 */
void *vp = (char *)valp + offset;
safebuf_t *sout3 = sbuf_new();
sbuf_cpy(sout2, "{");
if(finfo->tid == NC_CHAR) { /* aggregate char rows into strings */
int rank = tinfo->ranks[fidx];
size_t nstrings;
size_t slen;
int j;
slen = tinfo->sides[fidx][rank-1];
nstrings = 1; /* product of all but last array dimension */
for(j=0; j < rank-1; j++) {
nstrings *= tinfo->sides[fidx][j];
}
for(i=0; i < nstrings; i++) { /* loop on product of all but
last index of array */
chars_tostring(sout3, slen, (char *)vp);
vp = (char *)vp + slen;
if(i < nstrings - 1) {
sbuf_cat(sout3, ", ");
}
sbuf_catb(sout2, sout3);
}
} else {
for(i = 0; i < tinfo->nvals[fidx]; i++) {
(void) finfo->typ_tostring(finfo, sout3, vp);
vp = (char *)vp + finfo->size;
if(i < tinfo->nvals[fidx] - 1) {
sbuf_cat(sout3, ", ");
}
sbuf_catb(sout2, sout3);
}
}
sbuf_cat(sout2, "}");
sbuf_free(sout3);
}
sbuf_catb(sfbf, sout2);
if(fidx < nfields - 1) {
sbuf_cat(sfbf, ", ");
}
}
sbuf_cat(sfbf, "}");
sbuf_free(sout2);
return sbuf_len(sfbf);
}
#endif /* USE_NETCDF4 */
int
ncbyte_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, *(signed char *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncchar_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, *(char *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncshort_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, *(short *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncint_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, *(int *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncfloat_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
float vv = *(float *)valp;
if(isfinite(vv)) {
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, vv);
assert(res < PRIM_LEN);
} else {
float_special_tostring(vv, sout);
}
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncdouble_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
double vv = *(double *)valp;
if(isfinite(vv)) {
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, vv);
assert(res < PRIM_LEN);
} else {
double_special_tostring(vv, sout);
}
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
/* Convert value of any numeric type to a double. Beware, this may
* lose precision for values of type NC_INT64 or NC_UINT64 */
static
double to_double(const ncvar_t *varp, const void *valp) {
double dd = 0.0;
switch (varp->type) {
case NC_BYTE:
dd = *(signed char *)valp;
break;
case NC_SHORT:
dd = *(short *)valp;
break;
case NC_INT:
dd = *(int *)valp;
break;
case NC_FLOAT:
dd = *(float *)valp;
break;
case NC_DOUBLE:
dd = *(double *)valp;
break;
case NC_UBYTE:
dd = *(unsigned char *)valp;
break;
case NC_USHORT:
dd = *(unsigned short *)valp;
break;
case NC_UINT:
dd = *(unsigned int *)valp;
break;
case NC_INT64:
dd = *(long long *)valp;
break;
case NC_UINT64:
dd = *(unsigned long long *)valp;
break;
default:
error("to_double: type not numeric primitive");
}
return dd;
}
int
nctime_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
double vv = to_double(varp, valp);
int separator = formatting_specs.iso_separator ? 'T' : ' ';
if(isfinite(vv)) {
int oldopts = 0;
int newopts = 0;
int res;
sout[0]='"';
/* Make nctime dump error messages */
oldopts = cdSetErrOpts(0);
newopts = oldopts | CU_VERBOSE;
cdSetErrOpts(newopts);
cdRel2Iso(varp->timeinfo->calendar, varp->timeinfo->units, separator, vv, &sout[1]);
cdSetErrOpts(oldopts);
res = strlen(sout);
sout[res++] = '"';
sout[res] = '\0';
assert(res < PRIM_LEN);
} else {
double_special_tostring(vv, sout);
}
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncubyte_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, *(unsigned char *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncushort_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, *(unsigned short *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncuint_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, *(unsigned int *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncint64_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, *(long long *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncuint64_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
char sout[PRIM_LEN];
int res;
res = snprintf(sout, PRIM_LEN, varp->fmt, *(unsigned long long *)valp);
assert(res < PRIM_LEN);
sbuf_cpy(sfbf, sout);
return sbuf_len(sfbf);
}
int
ncstring_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
return ncstring_typ_tostring(varp->tinfo, sfbf, valp);
}
#ifdef USE_NETCDF4
int
ncenum_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
return ncenum_typ_tostring(varp->tinfo, sfbf, valp);
}
/* Convert an opaque value to a string, represented as hexadecimal
* characters */
int
ncopaque_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
return ncopaque_typ_tostring(varp->tinfo, sfbf, valp);
}
/* Convert a vlen value to a string, by using tostring function for base type */
int
ncvlen_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
return ncvlen_typ_tostring(varp->tinfo, sfbf, valp);
}
int
nccomp_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
return nccomp_typ_tostring(varp->tinfo, sfbf, valp);
}
#endif /*USE_NETCDF4*/
static val_equals_func eq_funcs[] = {
ncbyte_val_equals,
ncchar_val_equals,
ncshort_val_equals,
ncint_val_equals,
ncfloat_val_equals,
ncdouble_val_equals,
ncubyte_val_equals,
ncushort_val_equals,
ncuint_val_equals,
ncint64_val_equals,
ncuint64_val_equals,
ncstring_val_equals
};
static typ_tostring_func ts_funcs[] = {
ncbyte_typ_tostring,
ncchar_typ_tostring,
ncshort_typ_tostring,
ncint_typ_tostring,
ncfloat_typ_tostring,
ncdouble_typ_tostring,
ncubyte_typ_tostring,
ncushort_typ_tostring,
ncuint_typ_tostring,
ncint64_typ_tostring,
ncuint64_typ_tostring,
ncstring_typ_tostring
};
/* Set function pointer of function to convert a value to a string for
* the variable pointed to by varp. */
void
set_tostring_func(ncvar_t *varp) {
val_tostring_func tostring_funcs[] = {
ncbyte_val_tostring,
ncchar_val_tostring,
ncshort_val_tostring,
ncint_val_tostring,
ncfloat_val_tostring,
ncdouble_val_tostring,
ncubyte_val_tostring,
ncushort_val_tostring,
ncuint_val_tostring,
ncint64_val_tostring,
ncuint64_val_tostring,
ncstring_val_tostring
};
if(varp->has_timeval && formatting_specs.string_times) {
varp->val_tostring = (val_tostring_func) nctime_val_tostring;
return;
}
if( !is_user_defined_type(varp->type) ) {
varp->val_tostring = tostring_funcs[varp->type - 1];
return;
}
#ifdef USE_NETCDF4
switch(varp->tinfo->class) {
case NC_VLEN:
varp->val_tostring = (val_tostring_func) ncvlen_val_tostring;
break;
case NC_OPAQUE:
varp->val_tostring = (val_tostring_func) ncopaque_val_tostring;
break;
case NC_ENUM:
varp->val_tostring = (val_tostring_func) ncenum_val_tostring;
break;
case NC_COMPOUND:
varp->val_tostring = (val_tostring_func) nccomp_val_tostring;
break;
default:
error("unrecognized class of user defined type: %d",
varp->tinfo->class);
}
#endif /* USE_NETCDF4 */
return;
}
/* Initialize typelist with primitive types. For netCDF-3 only need primitive
types. */
static void
init_prim_types(int ncid) {
nctype_t *tp;
int i;
int types[] =
{
NC_BYTE,
NC_CHAR,
NC_SHORT,
NC_INT,
NC_FLOAT,
NC_DOUBLE,
NC_UBYTE,
NC_USHORT,
NC_UINT,
NC_INT64,
NC_UINT64,
NC_STRING
};
size_t sizes[] = {
sizeof(char),
sizeof(char),
sizeof(short),
sizeof(int),
sizeof(float),
sizeof(double),
sizeof(unsigned char),
sizeof(unsigned short),
sizeof(unsigned int),
sizeof(long long),
sizeof(unsigned long long),
sizeof(char **)
};
#if 0
for(i=0; i < sizeof(types)/sizeof(int); i++) {
#else
for(i=0; i < max_atomic_type; i++) {
#endif
tp = (nctype_t *)emalloc(sizeof(nctype_t));
tp->ncid = ncid;
tp->tid = types[i];
tp->name = strdup(prim_type_name(tp->tid));
tp->grps = 0;
tp->class = 0; /* primitive type */
tp->size = sizes[i];
tp->base_tid = NC_NAT; /* not used for primitive types */
tp->nfields = 0; /* not used for primitive types */
tp->fmt = get_default_fmt(types[i]);
tp->fids = 0; /* not used for primitive types */
tp->offsets = 0; /* not used for primitive types */
tp->ranks = 0; /* not used for primitive types */
tp->sides = 0; /* not used for primitive types */
tp->nvals = 0; /* not used for primitive types */
tp->val_equals = (val_equals_func) eq_funcs[i];
tp->typ_tostring = (typ_tostring_func) ts_funcs[i];
typeadd(tp);
}
}
/* Initialize typelist.
*
* This must be done over all groups in netCDF-4, because
* variables in one group may be declared using types in a
* different group. For netCDF-3, this is just the info about
* primitive types.
*/
void
init_types(int ncid) {
#ifdef USE_NETCDF4
int ntypes;
#endif
if (max_type == 0) { /* if called for first time */
int maxtype = max_typeid(ncid);
int i;
nctypes = (nctype_t **) emalloc((maxtype + 2) * sizeof(nctype_t *));
for(i=0; i < maxtype+1; i++)
nctypes[i] = NULL; /* so can later skip over unused type slots */
init_prim_types(ncid);
}
#ifdef USE_NETCDF4
/* Are there any user defined types in this group? */
NC_CHECK( nc_inq_typeids(ncid, &ntypes, NULL) );
if (ntypes)
{
int t;
int *typeids = emalloc((ntypes + 1) * sizeof(int));
NC_CHECK( nc_inq_typeids(ncid, NULL, typeids) );
for (t = 0; t < ntypes; t++) {
nctype_t *tinfo; /* details about the type */
char type_name[NC_MAX_NAME + 1];
size_t group_name_len;
char* group_name;
int fidx; /* for compound type, field index */
tinfo = (nctype_t *) emalloc(sizeof(nctype_t));
NC_CHECK( nc_inq_user_type(ncid, typeids[t], type_name, &tinfo->size,
&tinfo->base_tid, &tinfo->nfields,
&tinfo->class) );
tinfo->tid = typeids[t];
tinfo->ncid = ncid;
tinfo->name = strdup(type_name);
tinfo->grps = 0;
if(tinfo->class == NC_VLEN) {
tinfo->size = sizeof(nc_vlen_t); /* not size of base type */
}
NC_CHECK( nc_inq_grpname_full(ncid, &group_name_len, NULL) );
group_name = (char *) emalloc(group_name_len + 1);
NC_CHECK( nc_inq_grpname_full(ncid, &group_name_len, group_name) );
tinfo->grps = strdup(group_name);
free(group_name);
switch(tinfo->class) {
case NC_ENUM:
tinfo->val_equals = eq_funcs[tinfo->base_tid-1];
tinfo->typ_tostring = (typ_tostring_func) ncenum_typ_tostring;
break;
case NC_COMPOUND:
tinfo->val_equals = (val_equals_func) nccomp_val_equals;
tinfo->typ_tostring = (typ_tostring_func) nccomp_typ_tostring;
tinfo->fids = (nc_type *) emalloc((tinfo->nfields + 1)
* sizeof(nc_type));
tinfo->offsets = (size_t *) emalloc((tinfo->nfields + 1)
* sizeof(size_t));
tinfo->ranks = (int *) emalloc((tinfo->nfields + 1)
* sizeof(int));
tinfo->sides = (int **) emalloc((tinfo->nfields + 1)
* sizeof(int *));
tinfo->nvals = (int *) emalloc((tinfo->nfields + 1)
* sizeof(int));
for (fidx = 0; fidx < tinfo->nfields; fidx++) {
size_t offset;
nc_type ftype;
int rank;
int *sides;
int i;
sides = NULL;
NC_CHECK( nc_inq_compound_field(ncid, tinfo->tid, fidx, NULL,
&offset, &ftype, &rank,
sides) );
if(rank > 0) sides = (int *) emalloc(rank * sizeof(int));
NC_CHECK( nc_inq_compound_field(ncid, tinfo->tid, fidx, NULL,
NULL, NULL, NULL, sides) );
tinfo->fids[fidx] = ftype;
tinfo->offsets[fidx] = offset;
tinfo->ranks[fidx] = rank;
if (rank > 0)
tinfo->sides[fidx] = (int *) emalloc(rank * sizeof(int));
tinfo->nvals[fidx] = 1;
for(i = 0; i < rank; i++) {
tinfo->sides[fidx][i] = sides[i];
tinfo->nvals[fidx] *= sides[i];
}
if (rank > 0)
free(sides);
}
break;
case NC_VLEN:
tinfo->val_equals = (val_equals_func) ncvlen_val_equals;
tinfo->typ_tostring = (typ_tostring_func) ncvlen_typ_tostring;
break;
case NC_OPAQUE:
tinfo->val_equals = (val_equals_func) ncopaque_val_equals;
tinfo->typ_tostring = (typ_tostring_func) ncopaque_typ_tostring;
break;
default:
error("bad class: %d", tinfo->class);
break;
}
typeadd(tinfo);
}
free(typeids);
}
/* For netCDF-4, check to see if this group has any subgroups and call
* recursively on each of them. */
{
int g, numgrps, *ncids;
/* See how many groups there are. */
NC_CHECK( nc_inq_grps(ncid, &numgrps, NULL) );
if (numgrps > 0) {
ncids = (int *) emalloc(numgrps * sizeof(int));
/* Get the list of group ids. */
NC_CHECK( nc_inq_grps(ncid, NULL, ncids) );
/* Call this function for each group. */
for (g = 0; g < numgrps; g++) {
init_types(ncids[g]);
}
free(ncids);
}
}
#endif /* USE_NETCDF4 */
}
/*
* return 1 if varid identifies a coordinate variable
* else return 0
*/
int
iscoordvar(int ncid, int varid)
{
int ndims, ndims1;
int dimid;
int* dimids = 0;
ncdim_t *dims = 0;
#ifdef USE_NETCDF4
int include_parents = 1;
#endif
int is_coord = 0; /* true if variable is a coordinate variable */
char varname[NC_MAX_NAME];
int varndims;
do { /* be safe in case someone is currently adding
* dimensions */
#ifdef USE_NETCDF4
NC_CHECK( nc_inq_dimids(ncid, &ndims, NULL, include_parents ) );
#else
NC_CHECK( nc_inq_ndims(ncid, &ndims) );
#endif
if (dims)
free(dims);
dims = (ncdim_t *) emalloc((ndims + 1) * sizeof(ncdim_t));
if (dimids)
free(dimids);
dimids = (int *) emalloc((ndims + 1) * sizeof(int));
#ifdef USE_NETCDF4
NC_CHECK( nc_inq_dimids(ncid, &ndims1, dimids, include_parents ) );
#else
{
int i;
for(i = 0; i < ndims; i++) {
dimids[i] = i; /* for netCDF-3, dimids are 0, 1, ..., ndims-1 */
}
NC_CHECK( nc_inq_ndims(ncid, &ndims1) );
}
#endif /* USE_NETCDF4 */
} while (ndims != ndims1);
for (dimid = 0; dimid < ndims; dimid++) {
NC_CHECK( nc_inq_dimname(ncid, dimids[dimid], dims[dimid].name) );
}
NC_CHECK( nc_inq_varname(ncid, varid, varname) );
NC_CHECK( nc_inq_varndims(ncid, varid, &varndims) );
for (dimid = 0; dimid < ndims; dimid++) {
if (strcmp(dims[dimid].name, varname) == 0 && varndims == 1) {
is_coord = 1;
break;
}
}
if(dims)
free(dims);
if(dimids)
free(dimids);
return is_coord;
}
/* Return true if user-defined type */
int
is_user_defined_type(nc_type type) {
nctype_t *typeinfop = get_typeinfo(type);
return (typeinfop->class > 0);
}
/*
* Return name of type in user-allocated space, whether built-in
* primitive type or user-defined type. Note: name must have enough
* space allocated to hold type name.
*/
void
get_type_name(int ncid, nc_type type, char *name)
{
#ifdef USE_NETCDF4
if (is_user_defined_type(type)) {
NC_CHECK(nc_inq_user_type(ncid, type, name, NULL, NULL, NULL, NULL));
} else {
strncpy(name, prim_type_name(type), NC_MAX_NAME + 1);
}
#else
strncpy(name, prim_type_name(type), NC_MAX_NAME + 1);
#endif /* USE_NETCDF4 */
}
/*
* Print type name with CDL escapes for special characters. locid is
* the id of the group in which the type is referenced, which is
* needed to determine whether an absolute type name must be printed.
* If the type is defined in the referenced group or in some ancestor
* group, only the simple type name is printed. If the type is
* defined in some other non-ancestor group, an absolute path for the
* typename is printed instead.
*/
void
print_type_name(int locid, int typeid) {
char *ename;
#ifdef USE_NETCDF4
char name[NC_MAX_NAME+1];
int type_inherited = 0;
int curlocid; /* group we are searching in */
int parent_groupid = locid;
int ntypes;
int stat;
#endif
assert(typeid > 0 && typeid <= max_type);
ename = escaped_name(nctypes[typeid]->name);
#ifdef USE_NETCDF4
if(is_user_defined_type(typeid)) {
/* determine if type is inherited, that is if defined in this
* group or any ancestor group */
name[NC_MAX_NAME] = '\0';
strncpy(name,nctypes[typeid]->name,NC_MAX_NAME);
do {
curlocid = parent_groupid;
NC_CHECK( nc_inq_typeids(curlocid, &ntypes, NULL) );
if(ntypes > 0) {
int *typeids = (int *) emalloc((ntypes + 1) * sizeof(int));
int i;
NC_CHECK( nc_inq_typeids(curlocid, &ntypes, typeids) );
for(i = 0; i < ntypes; i++) {
char curname[NC_MAX_NAME];
NC_CHECK( nc_inq_type(curlocid, typeids[i], curname, NULL) );
if(strncmp(name, curname, NC_MAX_NAME) == 0) {
type_inherited = 1;
break;
}
}
free(typeids);
if(type_inherited)
break;
}
stat = nc_inq_grp_parent(curlocid, &parent_groupid);
} while (stat != NC_ENOGRP && stat != NC_ENOTNC4);
if (type_inherited == 0) {
char *gname = nctypes[typeid]->grps;
print_name(gname);
fputs("/", stdout);
}
}
#endif /* USE_NETCDF4 */
fputs(ename, stdout);
free(ename);
}
/* Allocate and initialize table of unlimited dimensions for ncid, for
* use by is_unlim_dim() function. If ncid is a subgroup of a netCDF
* dataset, the table will still be initialized for the whole dataset
* in which the subgroup resides. */
#ifdef USE_NETCDF4
static int
init_is_unlim(int ncid, int **is_unlim_p)
{
int num_grps; /* total number of groups */
int num_dims = 0; /* total number of dimensions in all groups */
int num_undims = 0; /* total number of unlimited dimensions in all groups */
int *grpids = NULL; /* temporary list of all grpids */
int igrp;
int grpid;
/* if ncid is not root group, find its ancestor root group id */
int status = nc_inq_grp_parent(ncid, &grpid);
while(status == NC_NOERR && grpid != ncid) {
ncid = grpid;
status = nc_inq_grp_parent(ncid, &grpid);
}
if (status != NC_ENOGRP)
return NC_EBADGRPID;
/* Now ncid is root group. Get total number of groups and their ids */
NC_CHECK( nc_inq_grps_full(ncid, &num_grps, NULL) );
grpids = emalloc((num_grps + 1) * sizeof(int));
NC_CHECK( nc_inq_grps_full(ncid, &num_grps, grpids) );
#define DONT_INCLUDE_PARENTS 0
/* Get all dimensions in groups and info about which ones are unlimited */
for(igrp = 0; igrp < num_grps; igrp++) {
int ndims;
grpid = grpids[igrp];
NC_CHECK( nc_inq_dimids(grpid, &ndims, NULL, DONT_INCLUDE_PARENTS) );
num_dims += ndims;
}
*is_unlim_p = emalloc((num_dims + 1) * sizeof(int));
for(igrp = 0; igrp < num_grps; igrp++) {
int ndims, idim, *dimids, nundims;
grpid = grpids[igrp];
NC_CHECK( nc_inq_dimids(grpid, &ndims, NULL, DONT_INCLUDE_PARENTS) );
dimids = emalloc((ndims + 1) * sizeof(int));
NC_CHECK( nc_inq_dimids(grpid, &ndims, dimids, DONT_INCLUDE_PARENTS) );
/* mark all dims in this group as fixed-size */
for(idim = 0; idim < ndims; idim++) {
(*is_unlim_p)[dimids[idim]] = 0;
}
NC_CHECK( nc_inq_unlimdims(grpid, &nundims, dimids) );
assert(nundims <= ndims);
/* mark the subset of dims in this group that are unlimited */
for(idim = 0; idim < nundims; idim++) {
(*is_unlim_p)[dimids[idim]] = 1;
num_undims++;
}
if(dimids)
free(dimids);
}
free(grpids);
return NC_NOERR;
}
#endif /* USE_NETCDF4 */
/* TODO: make list of these arrays for multiple open datasets, such as
* the idnode_t lists above. For now, we just have one of these, for
* the unique input dataset for this invocation of ncdump. */
#define UNLIM_NOT_INITIALIZED (-1)
/* Is dimid the dimension ID of an unlimited dimension? */
bool_t
is_unlim_dim(int ncid, int dimid) {
bool_t result; /* 0 if fixed, 1 if unlimited size */
static int for_ncid = UNLIM_NOT_INITIALIZED; /* ensure only ever called for one ncid */
#ifdef USE_NETCDF4
static int *is_unlim = NULL; /* gets allocated by init_is_unlim() */
if(for_ncid == UNLIM_NOT_INITIALIZED) {
NC_CHECK(init_is_unlim(ncid, &is_unlim));
for_ncid = ncid;
}
assert(is_unlim);
result = is_unlim[dimid]; /* 0 if fixed, 1 if unlimited size */
#else
static int unlimdimid;
if(for_ncid == UNLIM_NOT_INITIALIZED) {
NC_CHECK( nc_inq_unlimdim(ncid, &unlimdimid) );
for_ncid = ncid;
}
result = (dimid == unlimdimid) ;
#endif /* USE_NETCDF4 */
return result;
}