netcdf-c/libdap2/common34.c
Quincey Koziol cd71eb525c 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 01:12:43 -06:00

969 lines
29 KiB
C

/*********************************************************************
* Copyright 1993, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
* $Header: /upc/share/CVS/netcdf-3/libncdap3/common34.c,v 1.29 2010/05/25 13:53:02 ed Exp $
*********************************************************************/
#include "ncdap3.h"
#ifdef HAVE_GETRLIMIT
# ifdef HAVE_SYS_RESOURCE_H
# include <sys/time.h>
# endif
# ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
# endif
#endif
#include "dapdump.h"
extern CDFnode* v4node;
/* Define the set of protocols known to be constrainable */
static char* constrainableprotocols[] = {"http", "https",NULL};
static NCerror buildcdftree34r(NCDAPCOMMON*,OCddsnode,CDFnode*,CDFtree*,CDFnode**);
static void defdimensions(OCddsnode, CDFnode*, NCDAPCOMMON*, CDFtree*);
static NCerror attachsubset34r(CDFnode*, CDFnode*);
static void free1cdfnode34(CDFnode* node);
/* Define Procedures that are common to both
libncdap3 and libncdap4
*/
/* Ensure every node has an initial base name defined and fullname */
/* Exceptions: anonymous dimensions. */
static NCerror
fix1node34(NCDAPCOMMON* nccomm, CDFnode* node)
{
if(node->nctype == NC_Dimension && node->ocname == NULL) return NC_NOERR;
ASSERT((node->ocname != NULL));
nullfree(node->ncbasename);
node->ncbasename = cdflegalname3(node->ocname);
if(node->ncbasename == NULL) return NC_ENOMEM;
nullfree(node->ncfullname);
node->ncfullname = makecdfpathstring3(node,nccomm->cdf.separator);
if(node->ncfullname == NULL) return NC_ENOMEM;
if(node->nctype == NC_Atomic)
node->externaltype = nctypeconvert(nccomm,node->etype);
return NC_NOERR;
}
NCerror
fixnodes34(NCDAPCOMMON* nccomm, NClist* cdfnodes)
{
int i;
for(i=0;i<nclistlength(cdfnodes);i++) {
CDFnode* node = (CDFnode*)nclistget(cdfnodes,i);
NCerror err = fix1node34(nccomm,node);
if(err) return err;
}
return NC_NOERR;
}
NCerror
fixgrid34(NCDAPCOMMON* nccomm, CDFnode* grid)
{
unsigned int i,glen;
CDFnode* array;
glen = nclistlength(grid->subnodes);
array = (CDFnode*)nclistget(grid->subnodes,0);
if(nccomm->controls.flags & (NCF_NC3)) {
/* Rename grid Array: variable, but leave its oc base name alone */
nullfree(array->ncbasename);
array->ncbasename = nulldup(grid->ncbasename);
if(!array->ncbasename) return NC_ENOMEM;
}
/* validate and modify the grid structure */
if((glen-1) != nclistlength(array->array.dimset0)) goto invalid;
for(i=1;i<glen;i++) {
CDFnode* arraydim = (CDFnode*)nclistget(array->array.dimset0,i-1);
CDFnode* map = (CDFnode*)nclistget(grid->subnodes,i);
CDFnode* mapdim;
/* map must have 1 dimension */
if(nclistlength(map->array.dimset0) != 1) goto invalid;
/* and the map name must match the ith array dimension */
if(arraydim->ocname != NULL && map->ocname != NULL
&& strcmp(arraydim->ocname,map->ocname) != 0)
goto invalid;
/* and the map name must match its dim name (if any) */
mapdim = (CDFnode*)nclistget(map->array.dimset0,0);
if(mapdim->ocname != NULL && map->ocname != NULL
&& strcmp(mapdim->ocname,map->ocname) != 0)
goto invalid;
/* Add appropriate names for the anonymous dimensions */
/* Do the map name first, so the array dim may inherit */
if(mapdim->ocname == NULL) {
nullfree(mapdim->ncbasename);
mapdim->ocname = nulldup(map->ocname);
if(!mapdim->ocname) return NC_ENOMEM;
mapdim->ncbasename = cdflegalname3(mapdim->ocname);
if(!mapdim->ncbasename) return NC_ENOMEM;
}
if(arraydim->ocname == NULL) {
nullfree(arraydim->ncbasename);
arraydim->ocname = nulldup(map->ocname);
if(!arraydim->ocname) return NC_ENOMEM;
arraydim->ncbasename = cdflegalname3(arraydim->ocname);
if(!arraydim->ncbasename) return NC_ENOMEM;
}
if(FLAGSET(nccomm->controls,(NCF_NCDAP|NCF_NC3))) {
char tmp[3*NC_MAX_NAME];
/* Add the grid name to the basename of the map */
snprintf(tmp,sizeof(tmp),"%s%s%s",map->container->ncbasename,
nccomm->cdf.separator,
map->ncbasename);
nullfree(map->ncbasename);
map->ncbasename = nulldup(tmp);
if(!map->ncbasename) return NC_ENOMEM;
}
}
return NC_NOERR;
invalid:
return NC_EINVAL; /* mal-formed grid */
}
/**
* Given an anonymous dimension, compute the
* effective 0-based index wrt to the specified var.
* The result should mimic the libnc-dap indices.
*/
static void
computedimindexanon3(CDFnode* dim, CDFnode* var)
{
int i;
NClist* dimset = var->array.dimsetall;
for(i=0;i<nclistlength(dimset);i++) {
CDFnode* candidate = (CDFnode*)nclistget(dimset,i);
if(dim == candidate) {
dim->dim.index1=i+1;
return;
}
}
}
/* Replace dims in a list with their corresponding basedim */
static void
replacedims(NClist* dims)
{
int i;
for(i=0;i<nclistlength(dims);i++) {
CDFnode* dim = (CDFnode*)nclistget(dims,i);
CDFnode* basedim = dim->dim.basedim;
if(basedim == NULL) continue;
nclistset(dims,i,(void*)basedim);
}
}
/**
Two dimensions are equivalent if
1. they have the same size
2. neither are anonymous
3. they ave the same names.
*/
static int
equivalentdim(CDFnode* basedim, CDFnode* dupdim)
{
if(dupdim->dim.declsize != basedim->dim.declsize) return 0;
if(basedim->ocname == NULL && dupdim->ocname == NULL) return 0;
if(basedim->ocname == NULL || dupdim->ocname == NULL) return 0;
if(strcmp(dupdim->ocname,basedim->ocname) != 0) return 0;
return 1;
}
/*
Provide short and/or unified names for dimensions.
This must mimic lib-ncdap, which is difficult.
*/
NCerror
computecdfdimnames34(NCDAPCOMMON* nccomm)
{
int i,j;
char tmp[NC_MAX_NAME*2];
NClist* conflicts = nclistnew();
NClist* varnodes = nccomm->cdf.ddsroot->tree->varnodes;
NClist* alldims;
NClist* basedims;
/* Collect all dimension nodes from dimsetall lists */
alldims = getalldims34(nccomm,0);
/* Assign an index to all anonymous dimensions
vis-a-vis its containing variable
*/
for(i=0;i<nclistlength(varnodes);i++) {
CDFnode* var = (CDFnode*)nclistget(varnodes,i);
for(j=0;j<nclistlength(var->array.dimsetall);j++) {
CDFnode* dim = (CDFnode*)nclistget(var->array.dimsetall,j);
if(dim->ocname != NULL) continue; /* not anonymous */
computedimindexanon3(dim,var);
}
}
/* Unify dimensions by defining one dimension as the "base"
dimension, and make all "equivalent" dimensions point to the
base dimension.
1. Equivalent means: same size and both have identical non-null names.
2. Dims with same name but different sizes will be handled separately
*/
for(i=0;i<nclistlength(alldims);i++) {
CDFnode* dupdim = NULL;
CDFnode* basedim = (CDFnode*)nclistget(alldims,i);
if(basedim == NULL) continue;
if(basedim->dim.basedim != NULL) continue; /* already processed*/
for(j=i+1;j<nclistlength(alldims);j++) { /* Sigh, n**2 */
dupdim = (CDFnode*)nclistget(alldims,j);
if(basedim == dupdim) continue;
if(dupdim == NULL) continue;
if(dupdim->dim.basedim != NULL) continue; /* already processed */
if(!equivalentdim(basedim,dupdim))
continue;
dupdim->dim.basedim = basedim; /* equate */
#ifdef DEBUG1
fprintf(stderr,"assign: %s/%s -> %s/%s\n",
basedim->dim.array->ocname,basedim->ocname,
dupdim->dim.array->ocname,dupdim->ocname
);
#endif
}
}
/* Next case: same name and different sizes*/
/* => rename second dim */
for(i=0;i<nclistlength(alldims);i++) {
CDFnode* basedim = (CDFnode*)nclistget(alldims,i);
if(basedim->dim.basedim != NULL) continue;
/* Collect all conflicting dimensions */
nclistclear(conflicts);
for(j=i+1;j<nclistlength(alldims);j++) {
CDFnode* dim = (CDFnode*)nclistget(alldims,j);
if(dim->dim.basedim != NULL) continue;
if(dim->ocname == NULL && basedim->ocname == NULL) continue;
if(dim->ocname == NULL || basedim->ocname == NULL) continue;
if(strcmp(dim->ocname,basedim->ocname)!=0) continue;
if(dim->dim.declsize == basedim->dim.declsize) continue;
#ifdef DEBUG2
fprintf(stderr,"conflict: %s[%lu] %s[%lu]\n",
basedim->ncfullname,(unsigned long)basedim->dim.declsize,
dim->ncfullname,(unsigned long)dim->dim.declsize);
#endif
nclistpush(conflicts,(void*)dim);
}
/* Give all the conflicting dimensions an index */
for(j=0;j<nclistlength(conflicts);j++) {
CDFnode* dim = (CDFnode*)nclistget(conflicts,j);
dim->dim.index1 = j+1;
}
}
nclistfree(conflicts);
/* Replace all non-base dimensions with their base dimension */
for(i=0;i<nclistlength(varnodes);i++) {
CDFnode* node = (CDFnode*)nclistget(varnodes,i);
replacedims(node->array.dimsetall);
replacedims(node->array.dimsetplus);
replacedims(node->array.dimset0);
}
/* Collect list of all basedims */
basedims = nclistnew();
for(i=0;i<nclistlength(alldims);i++) {
CDFnode* dim = (CDFnode*)nclistget(alldims,i);
if(dim->dim.basedim == NULL) {
if(!nclistcontains(basedims,(void*)dim)) {
nclistpush(basedims,(void*)dim);
}
}
}
nccomm->cdf.ddsroot->tree->dimnodes = basedims;
/* cleanup */
nclistfree(alldims);
/* Assign ncbasenames and ncfullnames to base dimensions */
for(i=0;i<nclistlength(basedims);i++) {
CDFnode* dim = (CDFnode*)nclistget(basedims,i);
CDFnode* var = dim->dim.array;
if(dim->dim.basedim != NULL) PANIC1("nonbase basedim: %s\n",dim->ocname);
/* stringdim names are already assigned */
if(dim->ocname == NULL) { /* anonymous: use the index to compute the name */
snprintf(tmp,sizeof(tmp),"%s_%d",
var->ncfullname,dim->dim.index1-1);
nullfree(dim->ncbasename);
dim->ncbasename = cdflegalname3(tmp);
nullfree(dim->ncfullname);
dim->ncfullname = nulldup(dim->ncbasename);
} else { /* !anonymous; use index1 if defined */
char* legalname = cdflegalname3(dim->ocname);
nullfree(dim->ncbasename);
if(dim->dim.index1 > 0) {/* need to fix conflicting names (see above) */
char sindex[64];
snprintf(sindex,sizeof(sindex),"_%d",dim->dim.index1);
dim->ncbasename = (char*)malloc(strlen(sindex)+strlen(legalname)+1);
if(dim->ncbasename == NULL) {nullfree(legalname); return NC_ENOMEM;}
strcpy(dim->ncbasename,legalname);
strcat(dim->ncbasename,sindex);
nullfree(legalname);
} else {/* standard case */
dim->ncbasename = legalname;
}
nullfree(dim->ncfullname);
dim->ncfullname = nulldup(dim->ncbasename);
}
}
/* Verify unique and defined names for dimensions*/
for(i=0;i<nclistlength(basedims);i++) {
CDFnode* dim1 = (CDFnode*)nclistget(basedims,i);
if(dim1->dim.basedim != NULL) PANIC1("nonbase basedim: %s\n",dim1->ncbasename);
if(dim1->ncbasename == NULL || dim1->ncfullname == NULL)
PANIC1("missing dim names: %s",dim1->ocname);
/* search backward so we can delete duplicates */
for(j=nclistlength(basedims)-1;j>i;j--) {
CDFnode* dim2 = (CDFnode*)nclistget(basedims,j);
if(strcmp(dim1->ncfullname,dim2->ncfullname)==0) {
/* complain and suppress one of them */
fprintf(stderr,"duplicate dim names: %s[%lu] %s[%lu]\n",
dim1->ncfullname,(unsigned long)dim1->dim.declsize,
dim2->ncfullname,(unsigned long)dim2->dim.declsize);
nclistremove(basedims,j);
}
}
}
#ifdef DEBUG
for(i=0;i<nclistlength(basedims);i++) {
CDFnode* dim = (CDFnode*)nclistget(basedims,i);
fprintf(stderr,"basedim: %s=%ld\n",dim->ncfullname,(long)dim->dim.declsize);
}
#endif
return NC_NOERR;
}
NCerror
makegetvar34(NCDAPCOMMON* nccomm, CDFnode* var, void* data, nc_type dsttype, Getvara** getvarp)
{
NCerror ncstat = NC_NOERR;
if(getvarp)
{
Getvara* getvar;
getvar = (Getvara*)calloc(1,sizeof(Getvara));
MEMCHECK(getvar,NC_ENOMEM);
getvar->target = var;
getvar->memory = data;
getvar->dsttype = dsttype;
*getvarp = getvar;
}
return ncstat;
}
int
constrainable34(NCURI* durl)
{
char** protocol = constrainableprotocols;
for(;*protocol;protocol++) {
if(strcmp(durl->protocol,*protocol)==0)
return 1;
}
return 0;
}
CDFnode*
makecdfnode34(NCDAPCOMMON* nccomm, char* ocname, OCtype octype,
/*optional*/ OCddsnode ocnode, CDFnode* container)
{
CDFnode* node;
assert(nccomm != NULL);
node = (CDFnode*)calloc(1,sizeof(CDFnode));
if(node == NULL) return (CDFnode*)NULL;
node->ocname = NULL;
if(ocname) {
size_t len = strlen(ocname);
if(len >= NC_MAX_NAME) len = NC_MAX_NAME-1;
node->ocname = (char*)malloc(len+1);
if(node->ocname == NULL) { nullfree(node); return NULL;}
memcpy(node->ocname,ocname,len);
node->ocname[len] = '\0';
}
node->nctype = octypetonc(octype);
node->ocnode = ocnode;
node->subnodes = nclistnew();
node->container = container;
if(ocnode != NULL) {
oc_dds_atomictype(nccomm->oc.conn,ocnode,&octype);
node->etype = octypetonc(octype);
}
return node;
}
/* Given an OCnode tree, mimic it as a CDFnode tree;
Add DAS attributes if DAS is available. Accumulate set
of all nodes in preorder.
*/
NCerror
buildcdftree34(NCDAPCOMMON* nccomm, OCddsnode ocroot, OCdxd occlass, CDFnode** cdfrootp)
{
CDFnode* root = NULL;
CDFtree* tree = (CDFtree*)calloc(1,sizeof(CDFtree));
NCerror err = NC_NOERR;
tree->ocroot = ocroot;
tree->nodes = nclistnew();
tree->occlass = occlass;
tree->owner = nccomm;
err = buildcdftree34r(nccomm,ocroot,NULL,tree,&root);
if(!err) {
if(occlass != OCDAS)
fixnodes34(nccomm,tree->nodes);
if(cdfrootp) *cdfrootp = root;
}
return err;
}
static NCerror
buildcdftree34r(NCDAPCOMMON* nccomm, OCddsnode ocnode, CDFnode* container,
CDFtree* tree, CDFnode** cdfnodep)
{
size_t i,ocrank,ocnsubnodes;
OCtype octype;
OCtype ocatomtype;
char* ocname = NULL;
NCerror ncerr = NC_NOERR;
CDFnode* cdfnode;
oc_dds_class(nccomm->oc.conn,ocnode,&octype);
if(octype == OC_Atomic)
oc_dds_atomictype(nccomm->oc.conn,ocnode,&ocatomtype);
else
ocatomtype = OC_NAT;
oc_dds_name(nccomm->oc.conn,ocnode,&ocname);
oc_dds_rank(nccomm->oc.conn,ocnode,&ocrank);
oc_dds_nsubnodes(nccomm->oc.conn,ocnode,&ocnsubnodes);
#ifdef DEBUG1
if(ocatomtype == OC_NAT)
fprintf(stderr,"buildcdftree: connect: %s %s\n",oc_typetostring(octype),ocname);
else
fprintf(stderr,"buildcdftree: connect: %s %s\n",oc_typetostring(ocatomtype),ocname);
#endif
switch (octype) {
case OC_Dataset:
case OC_Grid:
case OC_Structure:
case OC_Sequence:
cdfnode = makecdfnode34(nccomm,ocname,octype,ocnode,container);
nclistpush(tree->nodes,(void*)cdfnode);
if(tree->root == NULL) {
tree->root = cdfnode;
cdfnode->tree = tree;
}
break;
case OC_Atomic:
cdfnode = makecdfnode34(nccomm,ocname,octype,ocnode,container);
nclistpush(tree->nodes,(void*)cdfnode);
if(tree->root == NULL) {
tree->root = cdfnode;
cdfnode->tree = tree;
}
break;
case OC_Dimension:
default: PANIC1("buildcdftree: unexpect OC node type: %d",(int)octype);
}
/* cross link */
cdfnode->root = tree->root;
if(ocrank > 0) defdimensions(ocnode,cdfnode,nccomm,tree);
for(i=0;i<ocnsubnodes;i++) {
OCddsnode ocsubnode;
CDFnode* subnode;
oc_dds_ithfield(nccomm->oc.conn,ocnode,i,&ocsubnode);
ncerr = buildcdftree34r(nccomm,ocsubnode,cdfnode,tree,&subnode);
if(ncerr) return ncerr;
nclistpush(cdfnode->subnodes,(void*)subnode);
}
nullfree(ocname);
if(cdfnodep) *cdfnodep = cdfnode;
return ncerr;
}
static void
defdimensions(OCddsnode ocnode, CDFnode* cdfnode, NCDAPCOMMON* nccomm, CDFtree* tree)
{
size_t i,ocrank;
oc_dds_rank(nccomm->oc.conn,ocnode,&ocrank);
assert(ocrank > 0);
for(i=0;i<ocrank;i++) {
CDFnode* cdfdim;
OCddsnode ocdim;
char* ocname;
size_t declsize;
oc_dds_ithdimension(nccomm->oc.conn,ocnode,i,&ocdim);
oc_dimension_properties(nccomm->oc.conn,ocdim,&declsize,&ocname);
cdfdim = makecdfnode34(nccomm,ocname,OC_Dimension,
ocdim,cdfnode->container);
nullfree(ocname);
nclistpush(tree->nodes,(void*)cdfdim);
/* Initially, constrained and unconstrained are same */
cdfdim->dim.declsize = declsize;
cdfdim->dim.array = cdfnode;
if(cdfnode->array.dimset0 == NULL)
cdfnode->array.dimset0 = nclistnew();
nclistpush(cdfnode->array.dimset0,(void*)cdfdim);
}
}
/* Note: this routine only applies some common
client parameters, other routines may apply
specific ones.
*/
NCerror
applyclientparams34(NCDAPCOMMON* nccomm)
{
int i,len;
int dfaltstrlen = DEFAULTSTRINGLENGTH;
int dfaltseqlim = DEFAULTSEQLIMIT;
const char* value;
char tmpname[NC_MAX_NAME+32];
char* pathstr;
OClink conn = nccomm->oc.conn;
unsigned long limit;
ASSERT(nccomm->oc.url != NULL);
nccomm->cdf.cache->cachelimit = DFALTCACHELIMIT;
value = oc_clientparam_get(conn,"cachelimit");
limit = getlimitnumber(value);
if(limit > 0) nccomm->cdf.cache->cachelimit = limit;
nccomm->cdf.fetchlimit = DFALTFETCHLIMIT;
value = oc_clientparam_get(conn,"fetchlimit");
limit = getlimitnumber(value);
if(limit > 0) nccomm->cdf.fetchlimit = limit;
nccomm->cdf.smallsizelimit = DFALTSMALLLIMIT;
value = oc_clientparam_get(conn,"smallsizelimit");
limit = getlimitnumber(value);
if(limit > 0) nccomm->cdf.smallsizelimit = limit;
nccomm->cdf.cache->cachecount = DFALTCACHECOUNT;
#ifdef HAVE_GETRLIMIT
{ struct rlimit rl;
if(getrlimit(RLIMIT_NOFILE, &rl) >= 0) {
nccomm->cdf.cache->cachecount = (size_t)(rl.rlim_cur / 2);
}
}
#endif
value = oc_clientparam_get(conn,"cachecount");
limit = getlimitnumber(value);
if(limit > 0) nccomm->cdf.cache->cachecount = limit;
/* Ignore limit if not caching */
if(!FLAGSET(nccomm->controls,NCF_CACHE))
nccomm->cdf.cache->cachecount = 0;
if(oc_clientparam_get(conn,"nolimit") != NULL)
dfaltseqlim = 0;
value = oc_clientparam_get(conn,"limit");
if(value != NULL && strlen(value) != 0) {
if(sscanf(value,"%d",&len) && len > 0) dfaltseqlim = len;
}
nccomm->cdf.defaultsequencelimit = dfaltseqlim;
/* allow embedded _ */
value = oc_clientparam_get(conn,"stringlength");
if(value != NULL && strlen(value) != 0) {
if(sscanf(value,"%d",&len) && len > 0) dfaltstrlen = len;
}
nccomm->cdf.defaultstringlength = dfaltstrlen;
/* String dimension limits apply to variables */
for(i=0;i<nclistlength(nccomm->cdf.ddsroot->tree->varnodes);i++) {
CDFnode* var = (CDFnode*)nclistget(nccomm->cdf.ddsroot->tree->varnodes,i);
/* Define the client param stringlength for this variable*/
var->maxstringlength = 0; /* => use global dfalt */
strcpy(tmpname,"stringlength_");
pathstr = makeocpathstring3(conn,var->ocnode,".");
strncat(tmpname,pathstr,NC_MAX_NAME);
nullfree(pathstr);
value = oc_clientparam_get(conn,tmpname);
if(value != NULL && strlen(value) != 0) {
if(sscanf(value,"%d",&len) && len > 0) var->maxstringlength = len;
}
}
/* Sequence limits apply to sequences */
for(i=0;i<nclistlength(nccomm->cdf.ddsroot->tree->nodes);i++) {
CDFnode* var = (CDFnode*)nclistget(nccomm->cdf.ddsroot->tree->nodes,i);
if(var->nctype != NC_Sequence) continue;
var->sequencelimit = dfaltseqlim;
strcpy(tmpname,"nolimit_");
pathstr = makeocpathstring3(conn,var->ocnode,".");
strncat(tmpname,pathstr,NC_MAX_NAME);
if(oc_clientparam_get(conn,tmpname) != NULL)
var->sequencelimit = 0;
strcpy(tmpname,"limit_");
strncat(tmpname,pathstr,NC_MAX_NAME);
value = oc_clientparam_get(conn,tmpname);
if(value != NULL && strlen(value) != 0) {
if(sscanf(value,"%d",&len) && len > 0)
var->sequencelimit = len;
}
nullfree(pathstr);
}
/* test for the appropriate fetch flags */
value = oc_clientparam_get(conn,"fetch");
if(value != NULL && strlen(value) > 0) {
if(value[0] == 'd' || value[0] == 'D') {
SETFLAG(nccomm->controls,NCF_ONDISK);
}
}
/* test for the force-whole-var flag */
value = oc_clientparam_get(conn,"wholevar");
if(value != NULL) {
SETFLAG(nccomm->controls,NCF_WHOLEVAR);
}
return NC_NOERR;
}
void
freecdfroot34(CDFnode* root)
{
int i;
CDFtree* tree;
NCDAPCOMMON* nccomm;
if(root == NULL) return;
tree = root->tree;
ASSERT((tree != NULL));
/* Explicitly FREE the ocroot */
nccomm = tree->owner;
oc_root_free(nccomm->oc.conn,tree->ocroot);
tree->ocroot = NULL;
for(i=0;i<nclistlength(tree->nodes);i++) {
CDFnode* node = (CDFnode*)nclistget(tree->nodes,i);
free1cdfnode34(node);
}
nclistfree(tree->nodes);
nclistfree(tree->varnodes);
nclistfree(tree->seqnodes);
nclistfree(tree->gridnodes);
nullfree(tree);
}
/* Free up a single node, but not any
nodes it points to.
*/
static void
free1cdfnode34(CDFnode* node)
{
unsigned int j,k;
if(node == NULL) return;
nullfree(node->ocname);
nullfree(node->ncbasename);
nullfree(node->ncfullname);
if(node->attributes != NULL) {
for(j=0;j<nclistlength(node->attributes);j++) {
NCattribute* att = (NCattribute*)nclistget(node->attributes,j);
nullfree(att->name);
for(k=0;k<nclistlength(att->values);k++)
nullfree((char*)nclistget(att->values,k));
nclistfree(att->values);
nullfree(att);
}
}
nullfree(node->dodsspecial.dimname);
nclistfree(node->subnodes);
nclistfree(node->attributes);
nclistfree(node->array.dimsetplus);
nclistfree(node->array.dimsetall);
nclistfree(node->array.dimset0);
/* Clean up the ncdap4 fields also */
nullfree(node->typename);
nullfree(node->vlenname);
nullfree(node);
}
/* Return true if node and node1 appear to refer to the same thing;
takes grid->structure changes into account.
*/
int
nodematch34(CDFnode* node1, CDFnode* node2)
{
return simplenodematch34(node1,node2);
}
/*
Try to figure out if two nodes
are the "related" =>
same name && same nc_type and same arity
but: Allow Grid == Structure
*/
int
simplenodematch34(CDFnode* node1, CDFnode* node2)
{
/* Test all the obvious stuff */
if(node1 == NULL || node2 == NULL)
return 0;
/* Add hack to address the screwed up Columbia server
which returns different Dataset {...} names
depending on the constraint.
*/
if(FLAGSET(node1->root->tree->owner->controls,NCF_COLUMBIA)
&& node1->nctype == NC_Dataset) return 1;
if(strcmp(node1->ocname,node2->ocname)!=0) /* same names */
return 0;
if(nclistlength(node1->array.dimset0)
!= nclistlength(node2->array.dimset0)) /* same arity */
return 0;
if(node1->nctype != node2->nctype) {
/* test for struct-grid match */
int structgrid = ((node1->nctype == NC_Grid && node2->nctype == NC_Structure)
|| (node1->nctype == NC_Structure && node2->nctype == NC_Grid) ? 1 : 0);
if(!structgrid)
return 0;
}
if(node1->nctype == NC_Atomic && node1->etype != node2->etype)
return 0;
return 1;
}
/*
Given DDS node, locate the node
in a DATADDS that matches the DDS node.
Return NULL if no node found
*/
void
unattach34(CDFnode* root)
{
unsigned int i;
CDFtree* xtree = root->tree;
for(i=0;i<nclistlength(xtree->nodes);i++) {
CDFnode* xnode = (CDFnode*)nclistget(xtree->nodes,i);
/* break bi-directional link */
xnode->attachment = NULL;
}
}
static void
setattach(CDFnode* target, CDFnode* template)
{
target->attachment = template;
template->attachment = target;
/* Transfer important information */
target->externaltype = template->externaltype;
target->maxstringlength = template->maxstringlength;
target->sequencelimit = template->sequencelimit;
target->ncid = template->ncid;
/* also transfer libncdap4 info */
target->typeid = template->typeid;
target->typesize = template->typesize;
}
static NCerror
attachdims34(CDFnode* xnode, CDFnode* template)
{
unsigned int i;
for(i=0;i<nclistlength(xnode->array.dimsetall);i++) {
CDFnode* xdim = (CDFnode*)nclistget(xnode->array.dimsetall,i);
CDFnode* tdim = (CDFnode*)nclistget(template->array.dimsetall,i);
setattach(xdim,tdim);
#ifdef DEBUG2
fprintf(stderr,"attachdim: %s->%s\n",xdim->ocname,tdim->ocname);
#endif
}
return NC_NOERR;
}
/*
Match a DATADDS node to a DDS node.
It is assumed that both trees have been re-struct'ed if necessary.
*/
static NCerror
attach34r(CDFnode* xnode, NClist* templatepath, int depth)
{
unsigned int i,plen,lastnode,gridable;
NCerror ncstat = NC_NOERR;
CDFnode* templatepathnode;
CDFnode* templatepathnext;
plen = nclistlength(templatepath);
if(depth >= plen) {THROWCHK(ncstat=NC_EINVAL); goto done;}
lastnode = (depth == (plen-1));
templatepathnode = (CDFnode*)nclistget(templatepath,depth);
ASSERT((simplenodematch34(xnode,templatepathnode)));
setattach(xnode,templatepathnode);
#ifdef DEBUG2
fprintf(stderr,"attachnode: %s->%s\n",xnode->ocname,templatepathnode->ocname);
#endif
if(lastnode) goto done; /* We have the match and are done */
if(nclistlength(xnode->array.dimsetall) > 0) {
attachdims34(xnode,templatepathnode);
}
ASSERT((!lastnode));
templatepathnext = (CDFnode*)nclistget(templatepath,depth+1);
gridable = (templatepathnext->nctype == NC_Grid && depth+2 < plen);
/* Try to find an xnode subnode that matches templatepathnext */
for(i=0;i<nclistlength(xnode->subnodes);i++) {
CDFnode* xsubnode = (CDFnode*)nclistget(xnode->subnodes,i);
if(simplenodematch34(xsubnode,templatepathnext)) {
ncstat = attach34r(xsubnode,templatepath,depth+1);
if(ncstat) goto done;
} else if(gridable && xsubnode->nctype == NC_Atomic) {
/* grids may or may not appear in the datadds;
try to match the xnode subnodes against the parts of the grid
*/
CDFnode* templatepathnext2 = (CDFnode*)nclistget(templatepath,depth+2);
if(simplenodematch34(xsubnode,templatepathnext2)) {
ncstat = attach34r(xsubnode,templatepath,depth+2);
if(ncstat) goto done;
}
}
}
done:
return THROW(ncstat);
}
NCerror
attach34(CDFnode* xroot, CDFnode* template)
{
NCerror ncstat = NC_NOERR;
NClist* templatepath = nclistnew();
CDFnode* ddsroot = template->root;
if(xroot->attachment) unattach34(xroot);
if(ddsroot != NULL && ddsroot->attachment) unattach34(ddsroot);
if(!simplenodematch34(xroot,ddsroot))
{THROWCHK(ncstat=NC_EINVAL); goto done;}
collectnodepath3(template,templatepath,WITHDATASET);
ncstat = attach34r(xroot,templatepath,0);
done:
nclistfree(templatepath);
return ncstat;
}
/*
Match nodes in template tree to nodes in target tree;
template tree is typically a structural superset of target tree.
WARNING: Dimensions are not attached
*/
NCerror
attachsubset34(CDFnode* target, CDFnode* template)
{
NCerror ncstat = NC_NOERR;
if(template == NULL) {THROWCHK(ncstat=NC_NOERR); goto done;}
if(!nodematch34(target,template)) {THROWCHK(ncstat=NC_EINVAL); goto done;}
#ifdef DEBUG2
fprintf(stderr,"attachsubset: target=%s\n",dumptree(target));
fprintf(stderr,"attachsubset: template=%s\n",dumptree(template));
#endif
ncstat = attachsubset34r(target,template);
done:
return ncstat;
}
static NCerror
attachsubset34r(CDFnode* target, CDFnode* template)
{
unsigned int i;
NCerror ncstat = NC_NOERR;
int fieldindex;
#ifdef DEBUG2
fprintf(stderr,"attachsubsetr: attach: target=%s template=%s\n",
target->ocname,template->ocname);
#endif
ASSERT((nodematch34(target,template)));
setattach(target,template);
/* Try to match target subnodes against template subnodes */
fieldindex = 0;
for(fieldindex=0,i=0;i<nclistlength(template->subnodes) && fieldindex<nclistlength(target->subnodes);i++) {
CDFnode* templatesubnode = (CDFnode*)nclistget(template->subnodes,i);
CDFnode* targetsubnode = (CDFnode*)nclistget(target->subnodes,fieldindex);
if(nodematch34(targetsubnode,templatesubnode)) {
#ifdef DEBUG2
fprintf(stderr,"attachsubsetr: match: %s :: %s\n",targetsubnode->ocname,templatesubnode->ocname);
#endif
ncstat = attachsubset34r(targetsubnode,templatesubnode);
if(ncstat) goto done;
fieldindex++;
}
}
done:
return THROW(ncstat);
}
static void
getalldims34a(NClist* dimset, NClist* alldims)
{
int i;
for(i=0;i<nclistlength(dimset);i++) {
CDFnode* dim = (CDFnode*)nclistget(dimset,i);
if(!nclistcontains(alldims,(void*)dim)) {
#ifdef DEBUG3
fprintf(stderr,"getalldims: %s[%lu]\n",
dim->ncfullname,(unsigned long)dim->dim.declsize);
#endif
nclistpush(alldims,(void*)dim);
}
}
}
/* Accumulate a set of all the known dimensions
vis-a-vis defined variables
*/
NClist*
getalldims34(NCDAPCOMMON* nccomm, int visibleonly)
{
int i;
NClist* alldims = nclistnew();
NClist* varnodes = nccomm->cdf.ddsroot->tree->varnodes;
/* get bag of all dimensions */
for(i=0;i<nclistlength(varnodes);i++) {
CDFnode* node = (CDFnode*)nclistget(varnodes,i);
if(!visibleonly || !node->invisible) {
getalldims34a(node->array.dimsetall,alldims);
}
}
return alldims;
}