netcdf-c/libdap2/dapattr.c
Dennis Heimbigner dc9e31df68 Fix for github issue https://github.com/Unidata/netcdf-c/issues/310.
The problem was that for opendap, it is possible to use keywords
as identifiers
 when there is no ambiguity. However, the DAP2
parser lost the case of the identifier used the lower case version.
Fix is to use the actual text of the symbol when it is used as an identifier.
Also added a test case for this (kwcase.*).

Additionally cleaned up some misc. dap2 testing problems.
1. ncdap_test/tst_ncdap3.sh was using an empty test set.
   restored the testing of datasets.
2. as a consequence of #1, some tests needed to be updated with minor
   tweeks.
3. fix dapmerge to handle multiple DODS_EXTRAS attributes.
4. modify buildattribute to suppress nul characters and terminate
   the name at the first nul.
5. clean up various test scripts to remove residual, unused
   references to obsolete netcdf-4 translation.
6. export e.g. NCDUMP from test_common.in so that non-top-level
   shell scripts can access it.
2017-06-28 13:51:01 -06:00

519 lines
16 KiB
C

/*********************************************************************
* Copyright 1993, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*********************************************************************/
#include "dapincludes.h"
#define OCCHECK(exp) if((ocstat = (exp))) {THROWCHK(ocstat); goto done;}
/* Forward */
static NCerror buildattribute(char*,nc_type,size_t,char**,NCattribute**);
/*
Invoke oc_merge_das and then extract special
attributes such as "strlen" and "dimname"
and stuff from DODS_EXTRA.
*/
int
dapmerge(NCDAPCOMMON* nccomm, CDFnode* ddsroot, OCddsnode dasroot)
{
int i,j;
NCerror ncstat = NC_NOERR;
OCerror ocstat = OC_NOERR;
NClist* allnodes;
OClink conn;
char* ocname = NULL;
char** values = NULL;
conn = nccomm->oc.conn;
if(ddsroot == NULL || dasroot == NULL)
return NC_NOERR;
/* Merge the das tree onto the dds tree */
ocstat = oc_merge_das(nccomm->oc.conn,dasroot,ddsroot->ocnode);
if(ocstat != OC_NOERR) goto done;
/* Create attributes on CDFnodes */
allnodes = ddsroot->tree->nodes;
for(i=0;i<nclistlength(allnodes);i++) {
CDFnode* node = (CDFnode*)nclistget(allnodes,i);
OCddsnode ocnode = node->ocnode;
size_t attrcount;
OCtype ocetype;
OCCHECK(oc_dds_attr_count(conn,ocnode,&attrcount));
for(j=0;j<attrcount;j++) {
size_t nvalues;
NCattribute* att = NULL;
if(ocname != NULL) {
free(ocname); ocname = NULL;
} /* from last loop */
OCCHECK(oc_dds_attr(conn,ocnode,j,&ocname,&ocetype,&nvalues,NULL));
if(nvalues > 0) {
values = (char**)malloc(sizeof(char*)*nvalues);
if(values == NULL) {ncstat = NC_ENOMEM; goto done;}
OCCHECK(oc_dds_attr(conn,ocnode,j,NULL,NULL,NULL,values));
}
ncstat = buildattribute(ocname,octypetonc(ocetype),nvalues,values,&att);
if(ncstat != NC_NOERR) goto done;
if(node->attributes == NULL)
node->attributes = nclistnew();
nclistpush(node->attributes,(void*)att);
if(strncmp(ocname,"DODS",strlen("DODS"))==0) {
att->invisible = 1;
/* Define extra semantics associated with
DODS and DODS_EXTRA attributes */
if(strcmp(ocname,"DODS.strlen")==0
|| strcmp(ocname,"DODS_EXTRA.strlen")==0) {
unsigned int maxstrlen = 0;
if(values != NULL) {
if(0==sscanf(values[0],"%u",&maxstrlen))
maxstrlen = 0;
}
node->dodsspecial.maxstrlen = maxstrlen;
#ifdef DEBUG
fprintf(stderr,"%s.maxstrlen=%d\n",node->ocname,(int)node->dodsspecial.maxstrlen);
#endif
} else if(strcmp(ocname,"DODS.dimName")==0
|| strcmp(ocname,"DODS_EXTRA.dimName")==0) {
if(values != NULL) {
node->dodsspecial.dimname = nulldup(values[0]);
#ifdef DEBUG
fprintf(stderr,"%s.dimname=%s\n",node->ocname,node->dodsspecial.dimname);
#endif
} else node->dodsspecial.dimname = NULL;
} else if(strcmp(ocname,"DODS.Unlimited_Dimension")==0
|| strcmp(ocname,"DODS_EXTRA.Unlimited_Dimension")==0) {
char* val0 = NULL;
if(values != NULL)
val0 = values[0];
if(val0 != NULL) {
if(nccomm->cdf.recorddimname != NULL) {
if(strcmp(nccomm->cdf.recorddimname,val0)!=0)
nclog(NCLOGWARN,"Duplicate DODS_EXTRA:Unlimited_Dimension specifications");
} else {
nccomm->cdf.recorddimname = nulldup(values[0]);
#ifdef DEBUG
fprintf(stderr,"%s.Unlimited_Dimension=%s\n",node->ocname,nccomm->cdf.recorddimname);
#endif
}
}
}
}
/* clean up */
if(values) {
oc_reclaim_strings(nvalues,values);
free(values);
values = NULL;
}
}
}
done:
if(values != NULL) free(values);
if(ocname != NULL) free(ocname);
if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat);
return THROW(ncstat);
}
/*
Build an NCattribute
from a DAP attribute.
As of Jun 27, 2017, we modify
to suppress nul characters and terminate
the name at the first nul.
*/
static NCerror
buildattribute(char* name, nc_type ptype,
size_t nvalues, char** values, NCattribute** attp)
{
int i;
NCerror ncstat = NC_NOERR;
NCattribute* att = NULL;
att = (NCattribute*)calloc(1,sizeof(NCattribute));
MEMCHECK(att,NC_ENOMEM);
att->name = nulldup(name);
att->etype = ptype;
att->values = nclistnew();
for(i=0;i<nvalues;i++) {
char* copy = nulldup(values[i]);
nclistpush(att->values,(void*)copy);
}
if(attp) *attp = att;
else
free(att);
return THROW(ncstat);
}
#if 0
/*
Given a das attribute walk it to see if it
has at least 1 actual attribute (no recursion)
*/
static int
hasattribute(OClink conn, OCdasnode dasnode)
{
int i;
OCerror ocstat = OC_NOERR;
int tf = 0; /* assume false */
OCtype ocsubtype;
NClist* subnodes = nclistnew();
OCCHECK(oc_dds_octype(conn,dasnode,&ocsubtype));
if(ocsubtype == OC_Attribute) return 1; /* this is an attribute */
ASSERT((ocsubtype == OC_Attributeset));
OCCHECK(collect_subnodes(conn,dasnode,subnodes));
for(i=0;i<nclistlength(subnodes);i++) {
OCdasnode subnode = (OCdasnode)nclistget(subnodes,i);
OCCHECK(oc_dds_class(conn,subnode,&ocsubtype));
if(ocsubtype == OC_Attribute) {tf=1; break;}
}
done:
nclistfree(subnodes);
return tf;
}
int
dapmerge(NCDAPCOMMON* nccomm, CDFnode* ddsroot, OCddsnode dasroot)
{
unsigned int i,j;
NCerror ncerr = NC_NOERR;
OCerror ocstat = OC_NOERR;
OClink conn = nccomm->oc.conn;
size_t nsubnodes;
NClist* dasglobals = nclistnew();
NClist* dasnodes = nclistnew();
NClist* dodsextra = nclistnew();
NClist* varnodes = nclistnew();
NClist* alldasnodes = nclistnew();
if(ddsroot == NULL || dasroot == NULL) return NC_NOERR;
ocstat = collect_alldasnodes(conn,dasroot,alldasnodes);
/* 1. collect all the relevant DAS nodes;
namely those that contain at least one
attribute value.
Simultaneously look for potential ambiguities
if found; complain but continue: result are indeterminate.
also collect globals and DODS_EXTRA separately.
*/
for(i=0;i<nclistlength(alldasnodes);i++) {
OCddsnode das = (OCddsnode)nclistget(alldasnodes,i);
OCtype octype;
char* ocname = NULL;
int isglobal = 0;
int hasattributes = 0;
OCCHECK(oc_dds_class(conn,das,&octype));
if(octype == OC_Attribute) continue; /* ignore these for now*/
OCCHECK(oc_dds_name(conn,das,&ocname));
OCCHECK(oc_dds_nsubnodes(conn,das,&nsubnodes));
isglobal = (ocname == NULL ? 0 : isglobalname(ocname));
/* catch DODS_EXTRA */
if(isglobal && ocname != NULL && strcmp(ocname,"DODS_EXTRA")==0) {
nclistpush(dodsextra,(void*)das);
nullfree(ocname);
continue;
}
if(ocname == NULL || isglobal) {
nclistpush(dasglobals,(void*)das);
nullfree(ocname);
continue;
}
hasattributes = hasattribute(conn,das);
if(hasattributes) {
/* Look for previously collected nodes with same name*/
for(j=0;j<nclistlength(dasnodes);j++) {
OCddsnode das2 = (OCddsnode)nclistget(dasnodes,j);
char* ocname2;
OCCHECK(oc_dds_name(conn,das2,&ocname2));
if(ocname2 == NULL || ocname == NULL) goto loop;
if(strcmp(ocname2,"DODS")==0) goto loop;
if(strcmp(ocname,ocname2)==0)
nclog(NCLOGWARN,"nc_mergedas: potentially ambiguous DAS name: %s",ocname2);
loop:
nullfree(ocname2);
}
nclistpush(dasnodes,(void*)das);
}
nullfree(ocname);
}
/* 2. collect all the leaf DDS nodes (of type NC_Atomic)*/
ocstat = collect_leaves(link,ddsroot,varnodes);
/* 3. For each das node, locate matching DDS node(s) and attach
attributes to the DDS node(s).
Match means:
1. DAS->fullname :: DDS->fullname
2. DAS->name :: DDS->fullname (support DAS names with embedded '.'
3. DAS->name :: DDS->name
4. special case for DODS. Apply 1-3 on DODS parent.
*/
for(i=0;i<nclistlength(dasnodes);i++) {
OCddsnode das = (OCddsnode)nclistget(dasnodes,i);
char* ocfullname = NULL;
char* ocbasename = NULL;
if(das == NULL) continue;
OCCHECK(oc_dds_name(conn,das,&ocbasename));
if(strcmp(ocbasename,"DODS")==0) {
OCddsnode container;
OCCHECK(oc_dds_container(conn,das,&container));
ASSERT(container != NULL);
ocfullname = makeocpathstring(conn,container,".");
} else {
ocfullname = makeocpathstring(conn,das,".");
}
for(j=0;j<nclistlength(varnodes);j++) {
CDFnode* dds = (CDFnode*)nclistget(varnodes,j);
char* ddsfullname = makecdfpathstring(dds,".");
if(strcmp(ocfullname,ddsfullname)==0
|| strcmp(ocbasename,ddsfullname)==0
|| strcmp(ocbasename,dds->ocname)==0) {
mergedas1(nccomm,conn,dds,das);
/* remove from dasnodes list*/
nclistset(dasnodes,i,(void*)NULL);
}
nullfree(ddsfullname);
}
nullfree(ocfullname);
nullfree(ocbasename);
}
/* 4. Assign globals */
for(i=0;i<nclistlength(dasglobals);i++) {
OCddsnode das = (OCddsnode)nclistget(dasglobals,i);
mergedas1(nccomm,conn,ddsroot,das);
}
/* 5. Assign DOD_EXTRA */
for(i=0;i<nclistlength(dodsextra);i++) {
OCddsnode das = (OCddsnode)nclistget(dodsextra,i);
mergedas1(nccomm,conn,ddsroot,das);
}
done: /* cleanup*/
nclistfree(dasglobals);
nclistfree(dasnodes);
nclistfree(alldasnodes);
nclistfree(dodsextra);
nclistfree(varnodes);
if(ocstat != OC_NOERR)
ncerr = ocerrtoncerr(ocstat);
return THROW(ncerr);
}
static int
mergedas1(NCDAPCOMMON* nccomm, OClink conn, CDFnode* dds, OCddsnode das)
{
NCerror ncstat = NC_NOERR;
OCerror ocstat = OC_NOERR;
unsigned int i,j,k;
unsigned int nsubnodes;
OCobject* subnodes = NULL;
OCobject* dodsnodes = NULL;
unsigned int ndodsnodes;
if(dds == NULL || das == OCNULL) return NC_NOERR; /* nothing to do */
if(dds->attributes == NULL) dds->attributes = nclistnew();
/* assign the simple attributes in the das set to this dds node*/
OCCHECK(oc_inq_nsubnodes(conn,das,&nsubnodes));
OCCHECK(oc_inq_subnodes(conn,das,&subnodes));
for(i=0;i<nsubnodes;i++) {
OCobject attnode = subnodes[i];
OCtype octype, ocetype;
char* ocname = NULL;
unsigned int ocnvalues;
OCCHECK(oc_inq_name(conn,attnode,&ocname));
OCCHECK(oc_inq_class(conn,attnode,&octype));
if(octype == OC_Attribute) {
NCattribute* att = NULL;
NClist* stringvalues;
OCCHECK(oc_inq_primtype(conn,attnode,&ocetype));
OCCHECK(oc_inq_dasattr_nvalues(conn,attnode,&ocnvalues));
stringvalues = nclistnew();
for(j=0;j<ocnvalues;j++) {
char* stringval;
OCCHECK(oc_inq_dasattr(conn,attnode,j,&ocetype,&stringval));
nclistpush(stringvalues,(void*)stringval);
}
ncstat = buildattribute(ocname,
octypetonc(ocetype),
stringvalues,
&att);
if(ncstat) goto done;
nclistpush(dds->attributes,(void*)att);
} else if(octype == OC_Attributeset
&& (strcmp(ocname,"DODS")==0
|| strcmp(ocname,"DODS_EXTRA")==0)) {
/* Turn the DODS special attributes into into
special attributes for dds node */
OCCHECK(oc_inq_nsubnodes(conn,attnode,&ndodsnodes));
OCCHECK(oc_inq_subnodes(conn,attnode,&dodsnodes));
for(j=0;j<ndodsnodes;j++) {
char* dodsname = NULL;
char newname[4096];
OCobject attnode = dodsnodes[j];
NCattribute* att = NULL;
NClist* stringvalues;
OCCHECK(oc_inq_class(conn,attnode,&octype));
if(octype != OC_Attribute) continue;
OCCHECK(oc_inq_primtype(conn,attnode,&ocetype));
OCCHECK(oc_inq_dasattr_nvalues(conn,attnode,&ocnvalues));
stringvalues = nclistnew();
for(k=0;k<ocnvalues;k++) {
char* stringval;
OCCHECK(oc_inq_dasattr(conn,attnode,k,&ocetype,&stringval));
nclistpush(stringvalues,(void*)stringval);
}
OCCHECK(oc_inq_name(conn,attnode,&dodsname));
/* Compute new special name */
strcpy(newname,"_DODS_");
strcat(newname,dodsname);
ncstat = buildattribute(newname,
octypetonc(ocetype),
stringvalues,
&att);
if(ncstat) goto done;
att->invisible = 1;
nclistpush(dds->attributes,(void*)att);
/* Define extra semantics associated with DODS and DODS_EXTRA attribute */
if(strcmp(dodsname,"strlen")==0) {
unsigned int maxstrlen = 0;
if(nclistlength(stringvalues) > 0) {
char* stringval = (char*)nclistget(stringvalues,0);
if(0==sscanf(stringval,"%u",&maxstrlen)) maxstrlen = 0;
}
dds->dodsspecial.maxstrlen = maxstrlen;
#ifdef DEBUG
fprintf(stderr,"%s.maxstrlen=%d\n",dds->ocname,(int)dds->dodsspecial.maxstrlen);
#endif
} else if(strcmp(dodsname,"dimName")==0) {
if(nclistlength(stringvalues) > 0) {
char* stringval = (char*)nclistget(stringvalues,0);
dds->dodsspecial.dimname = nulldup(stringval);
#ifdef DEBUG
fprintf(stderr,"%s.dimname=%s\n",dds->ocname,dds->dodsspecial.dimname);
#endif
} else dds->dodsspecial.dimname = NULL;
} else if(strcmp(dodsname,"Unlimited_Dimension")==0) {
char* stringval = NULL;
if(nclistlength(stringvalues) > 0)
stringval = (char*)nclistget(stringvalues,0);
if(stringval != NULL) {
if(nccomm->cdf.recorddimname != NULL) {
if(strcmp(stringval,nccomm->cdf.recorddimname) != 0)
nclog(NCLOGWARN,"Duplicate DODS_EXTRA:Unlimited_Dimension specifications");
} else {
nccomm->cdf.recorddimname = nulldup(stringval);
#ifdef DEBUG
fprintf(stderr,"%s.Unlimited_Dimension=%s\n",dds->ocname,nccomm->cdf.recorddimname);
#endif
}
}
} /* else ignore */
nullfree(dodsname);
}
nullfree(dodsnodes);
}
nullfree(ocname);
}
done:
nullfree(subnodes);
if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat);
return THROW(ncstat);
}
static int
isglobalname(char* name)
{
int len = strlen(name);
int glen = strlen("global");
char* p;
if(len < glen) return 0;
p = name + (len - glen);
if(strcasecmp(p,"global") != 0)
return 0;
return 1;
}
static OCerror
collect_alldasnodes(OClink link, OCddsnode dasnode, NClist* alldasnodes)
{
size_t nsubnodes,i;
OCerror ocstat = OC_NOERR;
nclistpush(alldasnodes,(void*)dasnode);
ocstat = oc_dds_nsubnodes(link,dasnode,&nsubnodes);
if(ocstat != OC_NOERR) goto done;
for(i=0;i<nsubnodes;i++) {
OCddsnode subnode;
ocstat = oc_dds_ithsubnode(link,dasnode,i,&subnode);
if(ocstat != OC_NOERR) goto done;
ocstat = collect_alldasnodes(link,subnode,alldasnodes);
if(ocstat != OC_NOERR) goto done;
}
done:
return ocstat;
}
static OCerror
collect_leaves(OClink link, OCddsnode ddsnode, NClist* leaves)
{
size_t nsubnodes,i;
OCerror ocstat = OC_NOERR;
OCtype octype;
ocstat = oc_dds_octype(link,ddsnode,&octype);
if(ocstat != OC_NOERR) goto done;
if(octype == OC_Atomic) {
nclistpush(leaves,(void*)ddsnode);
} else {
ocstat = oc_dds_nsubnodes(link,ddsnode,&nsubnodes);
if(ocstat != OC_NOERR) goto done;
for(i=0;i<nsubnodes;i++) {
OCddsnode subnode;
ocstat = oc_dds_ithsubnode(link,ddsnode,i,&subnode);
if(ocstat != OC_NOERR) goto done;
ocstat = collect_leaves(link,subnode,leaves);
if(ocstat != OC_NOERR) goto done;
}
}
done:
return ocstat;
}
static OCerror
collect_subnodes(OClink link, OCddsnode ddsnode, NClist* subnodes)
{
size_t nsubnodes,i;
OCerror ocstat = OC_NOERR;
ocstat = oc_dds_nsubnodes(link,ddsnode,&nsubnodes);
if(ocstat != OC_NOERR) goto done;
for(i=0;i<nsubnodes;i++) {
OCddsnode subnode;
ocstat = oc_dds_ithsubnode(link,ddsnode,i,&subnode);
if(ocstat != OC_NOERR) goto done;
nclistpush(subnodes,(void*)subnode);
}
done:
return ocstat;
}
#endif /*0*/