netcdf-c/libdap2/ncdap3.c
Quincey Koziol 4bfd9b27a0 Merge remote-tracking branch 'upstream/master'
Conflicts:
	libdap2/common34.c
	oc2/ocutil.c
2014-02-11 17:26:09 -06:00

773 lines
22 KiB
C

/*********************************************************************
* Copyright 1993, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
* $Header: /upc/share/CVS/netcdf-3/libncdap3/ncdap3.c,v 1.94 2010/05/28 01:05:34 dmh 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 "oclog.h"
#include "nc3internal.h"
#include "nc3dispatch.h"
#include "ncd3dispatch.h"
#include "dapalign.h"
#include "dapdump.h"
static NCerror buildncstructures3(NCDAPCOMMON*);
static NCerror builddims(NCDAPCOMMON*);
static NCerror buildvars(NCDAPCOMMON*);
static NCerror buildglobalattrs3(NCDAPCOMMON*,CDFnode* root);
static NCerror buildattribute3a(NCDAPCOMMON*, NCattribute*, nc_type, int);
static char* getdefinename(CDFnode* node);
static int ncd3initialized = 0;
size_t dap_one[NC_MAX_VAR_DIMS];
size_t dap_zero[NC_MAX_VAR_DIMS];
/**************************************************/
/* Add an extra function whose sole purpose is to allow
configure(.ac) to test for the presence of thiscode.
*/
int nc__opendap(void) {return 0;}
/**************************************************/
/* Do local initialization */
static int
ncd3initialize(void)
{
int i;
compute_nccalignments();
for(i=0;i<NC_MAX_VAR_DIMS;i++) {
dap_one[i] = 1;
dap_zero[i] = 0;
}
ncd3initialized = 1;
#ifdef DEBUG
/* force logging to go to stderr */
nclogclose();
if(nclogopen(NULL))
ncsetlogging(1); /* turn it on */
#endif
return NC_NOERR;
}
/**************************************************/
/* See ncd3dispatch.c for other version */
int
NCD3_open(const char * path, int mode,
int basepe, size_t *chunksizehintp,
int useparallel, void* mpidata,
NC_Dispatch* dispatch, NC* drno)
{
NCerror ncstat = NC_NOERR;
OCerror ocstat = OC_NOERR;
NCDAPCOMMON* dapcomm = NULL;
const char* value;
if(!ncd3initialized) ncd3initialize();
if(path == NULL)
return NC_EDAPURL;
if(dispatch == NULL) PANIC("NC3D_open: no dispatch table");
/* Setup our NC and NCDAPCOMMON state*/
dapcomm = (NCDAPCOMMON*)calloc(1,sizeof(NCDAPCOMMON));
if(dapcomm == NULL) {ncstat = NC_ENOMEM; goto done;}
NCD3_DATA_SET(drno,dapcomm);
drno->int_ncid = nc__pseudofd(); /* create a unique id */
dapcomm->controller = (NC*)drno;
dapcomm->cdf.separator = ".";
dapcomm->cdf.smallsizelimit = DFALTSMALLLIMIT;
dapcomm->cdf.cache = createnccache();
#ifdef HAVE_GETRLIMIT
{ struct rlimit rl;
if(getrlimit(RLIMIT_NOFILE, &rl) >= 0) {
dapcomm->cdf.cache->cachecount = (size_t)(rl.rlim_cur / 2);
}
}
#endif
#ifdef OCCOMPILEBYDEFAULT
/* set the compile flag by default */
dapcomm->oc.rawurltext = (char*)emalloc(strlen(path)+strlen("[compile]")+1);
strcpy(dapcomm->oc.rawurltext,"[compile]");
strcat(dapcomm->oc.rawurltext, path);
#else
dapcomm->oc.rawurltext = strdup(path);
#endif
ncuriparse(dapcomm->oc.rawurltext,&dapcomm->oc.url);
/* parse the client parameters */
ncuridecodeparams(dapcomm->oc.url);
if(!constrainable34(dapcomm->oc.url))
SETFLAG(dapcomm->controls,NCF_UNCONSTRAINABLE);
#ifdef COLUMBIA_HACK
{
const char* p;
/* Does this url look like it is from columbia? */
if(dapcomm->oc.url->host != NULL) {
for(p=dapcomm->oc.url->host;*p;p++) {
if(strncmp(p,COLUMBIA_HACK,strlen(COLUMBIA_HACK))==0)
SETFLAG(dapcomm->controls,NCF_COLUMBIA);
}
}
}
#endif
/* fail if we are unconstrainable but have constraints */
if(FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE)) {
if(dapcomm->oc.url->constraint != NULL) {
nclog(NCLOGWARN,"Attempt to constrain an unconstrainable data source: %s",
dapcomm->oc.url->constraint);
ncstat = THROW(NC_EDAPCONSTRAINT);
goto done;
}
}
/* Use libsrc code for storing metadata */
{
char tmpname[32];
/* Create fake file name: exact name must be unique,
but is otherwise irrelevant because we are using NC_DISKLESS
*/
snprintf(tmpname,sizeof(tmpname),"%d",drno->int_ncid);
/* Now, use the file to create the netcdf file; force classic. */
ncstat = nc_create(tmpname,NC_DISKLESS|NC_CLASSIC_MODEL,&drno->substrate);
if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
}
/* Avoid fill */
nc_set_fill(drno->substrate,NC_NOFILL,NULL);
dapcomm->oc.dapconstraint = (DCEconstraint*)dcecreate(CES_CONSTRAINT);
dapcomm->oc.dapconstraint->projections = nclistnew();
dapcomm->oc.dapconstraint->selections = nclistnew();
/* Parse constraints to make sure they are syntactically correct */
ncstat = parsedapconstraints(dapcomm,dapcomm->oc.url->constraint,dapcomm->oc.dapconstraint);
if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
/* Construct a url for oc minus any constraint and params*/
dapcomm->oc.urltext = ncuribuild(dapcomm->oc.url,NULL,NULL,
(NCURISTD ^ NCURICONSTRAINTS));
/* Pass to OC */
ocstat = oc_open(dapcomm->oc.urltext,&dapcomm->oc.conn);
if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
#ifdef DEBUG1
(void)oc_trace_curl(dapcomm->oc.conn);
#endif
nullfree(dapcomm->oc.urltext); /* clean up */
dapcomm->oc.urltext = NULL;
/* process control client parameters */
applyclientparamcontrols3(dapcomm);
/* Turn on logging; only do this after oc_open*/
if((value = paramvalue34(dapcomm,"log")) != NULL) {
ncloginit();
if(nclogopen(value))
ncsetlogging(1);
ocloginit();
if(oclogopen(value))
ocsetlogging(1);
}
/* fetch and build the unconstrained DDS for use as
template */
ncstat = fetchtemplatemetadata3(dapcomm);
if(ncstat != NC_NOERR) goto done;
/* Operations on the template tree */
/* Accumulate useful nodes sets */
ncstat = computecdfnodesets3(dapcomm,dapcomm->cdf.fullddsroot->tree);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* Define the dimsettrans list */
ncstat = definedimsettrans3(dapcomm,dapcomm->cdf.fullddsroot->tree);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* Mark the nodes of the template that are eligible for prefetch */
ncstat = markprefetch3(dapcomm);
/* fetch and build the constrained DDS */
ncstat = fetchconstrainedmetadata3(dapcomm);
if(ncstat != NC_NOERR) goto done;
#ifdef DEBUG2
fprintf(stderr,"constrained dds: %s\n",dumptree(dapcomm->cdf.ddsroot));
#endif
/* Operations on the constrained tree */
/* Accumulate useful nodes sets */
ncstat = computecdfnodesets3(dapcomm,dapcomm->cdf.ddsroot->tree);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* Fix grids */
ncstat = fixgrids3(dapcomm);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* Locate and mark usable sequences */
ncstat = sequencecheck3(dapcomm);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* suppress variables not in usable sequences */
ncstat = suppressunusablevars3(dapcomm);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* apply client parameters */
ncstat = applyclientparams34(dapcomm);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* Add (as needed) string dimensions*/
ncstat = addstringdims(dapcomm);
if(ncstat) {THROWCHK(ncstat); goto done;}
if(nclistlength(dapcomm->cdf.ddsroot->tree->seqnodes) > 0) {
/* Build the sequence related dimensions */
ncstat = defseqdims(dapcomm);
if(ncstat) {THROWCHK(ncstat); goto done;}
}
/* Define the dimsetplus and dimsetall lists */
ncstat = definedimsets3(dapcomm,dapcomm->cdf.ddsroot->tree);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* Re-compute the dimension names*/
ncstat = computecdfdimnames34(dapcomm);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* Deal with zero size dimensions */
ncstat = fixzerodims3(dapcomm);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* Attempt to use the DODS_EXTRA info to turn
one of the dimensions into unlimited.
Assume computecdfdimnames34 has already been called.
*/
ncstat = defrecorddim3(dapcomm);
if(ncstat) {THROWCHK(ncstat); goto done;}
if(dapcomm->cdf.recorddimname != NULL
&& nclistlength(dapcomm->cdf.ddsroot->tree->seqnodes) > 0) {
/*nclog(NCLOGWARN,"unlimited dimension specified, but sequences exist in DDS");*/
PANIC("unlimited dimension specified, but sequences exist in DDS");
}
/* Re-compute the var names*/
ncstat = computecdfvarnames3(dapcomm,
dapcomm->cdf.ddsroot,
dapcomm->cdf.ddsroot->tree->varnodes);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* Transfer data from the unconstrained DDS data to the unconstrained DDS */
ncstat = dimimprint3(dapcomm);
if(ncstat) goto done;
/* Process the constraints to map to the constrained CDF tree */
/* (must follow fixgrids3 */
ncstat = mapconstraints3(dapcomm->oc.dapconstraint,dapcomm->cdf.ddsroot);
if(ncstat != NC_NOERR) goto done;
/* Canonicalize the constraint */
ncstat = fixprojections(dapcomm->oc.dapconstraint->projections);
if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
/* Fill in segment information */
ncstat = qualifyconstraints3(dapcomm->oc.dapconstraint);
if(ncstat != NC_NOERR) goto done;
/* Accumulate set of variables in the constraint's projections */
ncstat = computeprojectedvars(dapcomm,dapcomm->oc.dapconstraint);
if(ncstat) {THROWCHK(ncstat); goto done;}
/* using the modified constraint, rebuild the constraint string */
if(FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE)) {
/* ignore all constraints */
dapcomm->oc.urltext = ncuribuild(dapcomm->oc.url,NULL,NULL,0);
} else {
char* constraintstring = buildconstraintstring3(dapcomm->oc.dapconstraint);
ncurisetconstraints(dapcomm->oc.url,constraintstring);
nullfree(constraintstring);
dapcomm->oc.urltext = ncuribuild(dapcomm->oc.url,NULL,NULL,NCURICONSTRAINTS);
}
#ifdef DEBUG
fprintf(stderr,"ncdap3: final constraint: %s\n",dapcomm->oc.url->constraint);
#endif
/* Estimate the variable sizes */
estimatevarsizes3(dapcomm);
/* Build the meta data */
ncstat = buildncstructures3(dapcomm);
if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
/* Explicitly do not call enddef because it will complain
about variables that are too large.
*/
#if 0
ncstat = nc_endef(drno->substrate,NC_NOFILL,NULL);
if(ncstat != NC_NOERR && ncstat != NC_EVARSIZE)
{THROWCHK(ncstat); goto done;}
#endif
{
NC* ncsub;
NC* drno = dapcomm->controller;
CDFnode* unlimited = dapcomm->cdf.recorddim;
/* (for now) break abstractions*/
NC3_INFO* nc3i;
/* get the id for the substrate */
ncstat = NC_check_id(drno->substrate,&ncsub);
if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
nc3i = (NC3_INFO*)ncsub->dispatchdata;
if(unlimited != NULL) {
/* Set the effective size of UNLIMITED */
NC_set_numrecs(nc3i,unlimited->dim.declsize);
}
/* Pretend the substrate is read-only */
NC_set_readonly(nc3i);
}
/* Do any necessary data prefetch */
if(FLAGSET(dapcomm->controls,NCF_PREFETCH)
&& FLAGSET(dapcomm->controls,NCF_PREFETCH_EAGER)) {
ncstat = prefetchdata3(dapcomm);
if(ncstat != NC_NOERR) {
del_from_NCList((NC*)drno); /* undefine here */
{THROWCHK(ncstat); goto done;}
}
}
return ncstat;
done:
if(drno != NULL) NCD3_close(drno->ext_ncid);
if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat);
return THROW(ncstat);
}
int
NCD3_close(int ncid)
{
NC* drno;
NCDAPCOMMON* dapcomm;
int ncstatus = NC_NOERR;
ncstatus = NC_check_id(ncid, (NC**)&drno);
if(ncstatus != NC_NOERR) return THROW(ncstatus);
dapcomm = (NCDAPCOMMON*)drno->dispatchdata;
/* We call abort rather than close to avoid
trying to write anything or try to pad file length
*/
ncstatus = nc_abort(drno->substrate);
/* clean NC* */
freeNCDAPCOMMON(dapcomm);
return THROW(ncstatus);
}
/**************************************************/
static NCerror
buildncstructures3(NCDAPCOMMON* dapcomm)
{
NCerror ncstat = NC_NOERR;
CDFnode* dds = dapcomm->cdf.ddsroot;
NC* ncsub;
NC_check_id(dapcomm->controller->substrate,&ncsub);
ncstat = buildglobalattrs3(dapcomm,dds);
if(ncstat != NC_NOERR) goto done;
ncstat = builddims(dapcomm);
if(ncstat != NC_NOERR) goto done;
ncstat = buildvars(dapcomm);
if(ncstat != NC_NOERR) goto done;
done:
return THROW(ncstat);
}
static NCerror
builddims(NCDAPCOMMON* dapcomm)
{
int i;
NCerror ncstat = NC_NOERR;
int dimid;
NClist* dimset = NULL;
NC* drno = dapcomm->controller;
NC* ncsub;
char* definename;
/* collect all dimensions from variables */
dimset = dapcomm->cdf.ddsroot->tree->dimnodes;
/* Sort by fullname just for the fun of it */
for(;;) {
int last = nclistlength(dimset) - 1;
int swap = 0;
for(i=0;i<last;i++) {
CDFnode* dim1 = (CDFnode*)nclistget(dimset,i);
CDFnode* dim2 = (CDFnode*)nclistget(dimset,i+1);
if(strcmp(dim1->ncfullname,dim2->ncfullname) > 0) {
nclistset(dimset,i,(void*)dim2);
nclistset(dimset,i+1,(void*)dim1);
swap = 1;
break;
}
}
if(!swap) break;
}
/* Define unlimited only if needed */
if(dapcomm->cdf.recorddim != NULL) {
CDFnode* unlimited = dapcomm->cdf.recorddim;
definename = getdefinename(unlimited);
ncstat = nc_def_dim(drno->substrate,
definename,
NC_UNLIMITED,
&unlimited->ncid);
nullfree(definename);
if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
/* get the id for the substrate */
ncstat = NC_check_id(drno->substrate,&ncsub);
if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
#if 0
nc3sub = (NC3_INFO*)&ncsub->dispatchdata;
/* Set the effective size of UNLIMITED;
note that this cannot easily be done thru the normal API.*/
NC_set_numrecs(nc3sub,unlimited->dim.declsize);
#endif
}
for(i=0;i<nclistlength(dimset);i++) {
CDFnode* dim = (CDFnode*)nclistget(dimset,i);
if(dim->dim.basedim != NULL) continue; /* handle below */
if(DIMFLAG(dim,CDFDIMRECORD)) continue; /* defined above */
#ifdef DEBUG1
fprintf(stderr,"define: dim: %s=%ld\n",dim->ncfullname,(long)dim->dim.declsize);
#endif
definename = getdefinename(dim);
ncstat = nc_def_dim(drno->substrate,definename,dim->dim.declsize,&dimid);
if(ncstat != NC_NOERR) {
THROWCHK(ncstat); goto done;
}
nullfree(definename);
dim->ncid = dimid;
}
/* Make all duplicate dims have same dimid as basedim*/
/* (see computecdfdimnames)*/
for(i=0;i<nclistlength(dimset);i++) {
CDFnode* dim = (CDFnode*)nclistget(dimset,i);
if(dim->dim.basedim != NULL) {
dim->ncid = dim->dim.basedim->ncid;
}
}
done:
nclistfree(dimset);
return THROW(ncstat);
}
/* Simultaneously build any associated attributes*/
/* and any necessary pseudo-dimensions for string types*/
static NCerror
buildvars(NCDAPCOMMON* dapcomm)
{
int i,j;
NCerror ncstat = NC_NOERR;
int varid;
NClist* varnodes = dapcomm->cdf.ddsroot->tree->varnodes;
NC* drno = dapcomm->controller;
char* definename;
ASSERT((varnodes != NULL));
for(i=0;i<nclistlength(varnodes);i++) {
CDFnode* var = (CDFnode*)nclistget(varnodes,i);
int dimids[NC_MAX_VAR_DIMS];
unsigned int ncrank;
NClist* vardims = NULL;
if(var->invisible) continue;
if(var->array.basevar != NULL) continue;
#ifdef DEBUG1
fprintf(stderr,"buildvars.candidate=|%s|\n",var->ncfullname);
#endif
vardims = var->array.dimsetall;
ncrank = nclistlength(vardims);
if(ncrank > 0) {
for(j=0;j<ncrank;j++) {
CDFnode* dim = (CDFnode*)nclistget(vardims,j);
dimids[j] = dim->ncid;
}
}
definename = getdefinename(var);
#ifdef DEBUG1
fprintf(stderr,"define: var: %s/%s",
definename,var->ocname);
if(ncrank > 0) {
int k;
for(k=0;k<ncrank;k++) {
CDFnode* dim = (CDFnode*)nclistget(vardims,k);
fprintf(stderr,"[%ld]",dim->dim.declsize);
}
}
fprintf(stderr,"\n");
#endif
ncstat = nc_def_var(drno->substrate,
definename,
var->externaltype,
ncrank,
(ncrank==0?NULL:dimids),
&varid);
nullfree(definename);
if(ncstat != NC_NOERR) {
THROWCHK(ncstat);
goto done;
}
var->ncid = varid;
if(var->attributes != NULL) {
for(j=0;j<nclistlength(var->attributes);j++) {
NCattribute* att = (NCattribute*)nclistget(var->attributes,j);
ncstat = buildattribute3a(dapcomm,att,var->etype,varid);
if(ncstat != NC_NOERR) goto done;
}
}
/* Tag the variable with its DAP path */
if(paramcheck34(dapcomm,"show","projection"))
showprojection3(dapcomm,var);
}
done:
return THROW(ncstat);
}
static NCerror
buildglobalattrs3(NCDAPCOMMON* dapcomm, CDFnode* root)
{
int i;
NCerror ncstat = NC_NOERR;
const char* txt;
char *nltxt, *p;
NCbytes* buf = NULL;
NClist* cdfnodes;
NC* drno = dapcomm->controller;
if(root->attributes != NULL) {
for(i=0;i<nclistlength(root->attributes);i++) {
NCattribute* att = (NCattribute*)nclistget(root->attributes,i);
ncstat = buildattribute3a(dapcomm,att,NC_NAT,NC_GLOBAL);
if(ncstat != NC_NOERR) goto done;
}
}
/* Add global attribute identifying the sequence dimensions */
if(paramcheck34(dapcomm,"show","seqdims")) {
buf = ncbytesnew();
cdfnodes = dapcomm->cdf.ddsroot->tree->nodes;
for(i=0;i<nclistlength(cdfnodes);i++) {
CDFnode* dim = (CDFnode*)nclistget(cdfnodes,i);
if(dim->nctype != NC_Dimension) continue;
if(DIMFLAG(dim,CDFDIMSEQ)) {
char* cname = cdflegalname3(dim->ocname);
if(ncbyteslength(buf) > 0) ncbytescat(buf,", ");
ncbytescat(buf,cname);
nullfree(cname);
}
}
if(ncbyteslength(buf) > 0) {
ncstat = nc_put_att_text(drno->substrate,NC_GLOBAL,"_sequence_dimensions",
ncbyteslength(buf),ncbytescontents(buf));
}
}
/* Define some additional system global attributes
depending on show= clientparams*/
/* Ignore failures*/
if(paramcheck34(dapcomm,"show","translate")) {
/* Add a global attribute to show the translation */
ncstat = nc_put_att_text(drno->substrate,NC_GLOBAL,"_translate",
strlen("netcdf-3"),"netcdf-3");
}
if(paramcheck34(dapcomm,"show","url")) {
if(dapcomm->oc.rawurltext != NULL)
ncstat = nc_put_att_text(drno->substrate,NC_GLOBAL,"_url",
strlen(dapcomm->oc.rawurltext),dapcomm->oc.rawurltext);
}
if(paramcheck34(dapcomm,"show","dds")) {
txt = NULL;
if(dapcomm->cdf.ddsroot != NULL)
txt = oc_tree_text(dapcomm->oc.conn,dapcomm->cdf.ddsroot->ocnode);
if(txt != NULL) {
/* replace newlines with spaces*/
nltxt = nulldup(txt);
for(p=nltxt;*p;p++) {if(*p == '\n' || *p == '\r' || *p == '\t') {*p = ' ';}};
ncstat = nc_put_att_text(drno->substrate,NC_GLOBAL,"_dds",strlen(nltxt),nltxt);
nullfree(nltxt);
}
}
if(paramcheck34(dapcomm,"show","das")) {
txt = NULL;
if(dapcomm->oc.ocdasroot != NULL)
txt = oc_tree_text(dapcomm->oc.conn,dapcomm->oc.ocdasroot);
if(txt != NULL) {
nltxt = nulldup(txt);
for(p=nltxt;*p;p++) {if(*p == '\n' || *p == '\r' || *p == '\t') {*p = ' ';}};
ncstat = nc_put_att_text(drno->substrate,NC_GLOBAL,"_das",strlen(nltxt),nltxt);
nullfree(nltxt);
}
}
done:
ncbytesfree(buf);
return THROW(ncstat);
}
static NCerror
buildattribute3a(NCDAPCOMMON* dapcomm, NCattribute* att, nc_type vartype, int varid)
{
int i;
NCerror ncstat = NC_NOERR;
unsigned int nvalues = nclistlength(att->values);
NC* drno = dapcomm->controller;
/* If the type of the attribute is string, then we need*/
/* to convert to a single character string by concatenation.
modified: 10/23/09 to insert newlines.
modified: 10/28/09 to interpret escapes
*/
if(att->etype == NC_STRING || att->etype == NC_URL) {
char* newstring;
size_t newlen = 0;
for(i=0;i<nvalues;i++) {
char* s = (char*)nclistget(att->values,i);
newlen += (1+strlen(s));
}
newstring = (char*)malloc(newlen);
MEMCHECK(newstring,NC_ENOMEM);
newstring[0] = '\0';
for(i=0;i<nvalues;i++) {
char* s = (char*)nclistget(att->values,i);
if(i > 0) strcat(newstring,"\n");
strcat(newstring,s);
}
dapexpandescapes(newstring);
if(newstring[0]=='\0')
ncstat = nc_put_att_text(drno->substrate,varid,att->name,1,newstring);
else
ncstat = nc_put_att_text(drno->substrate,varid,att->name,strlen(newstring),newstring);
free(newstring);
} else {
nc_type atype;
unsigned int typesize;
void* mem;
/* It turns out that some servers upgrade the type
of _FillValue in order to correctly preserve the
original value. However, since the type of the
underlying variable is not changes, we get a type
mismatch. So, make sure the type of the fillvalue
is the same as that of the controlling variable.
*/
if(varid != NC_GLOBAL && strcmp(att->name,"_FillValue")==0)
atype = nctypeconvert(dapcomm,vartype);
else
atype = nctypeconvert(dapcomm,att->etype);
typesize = nctypesizeof(atype);
mem = malloc(typesize * nvalues);
ncstat = dapcvtattrval3(atype,mem,att->values);
ncstat = nc_put_att(drno->substrate,varid,att->name,atype,nvalues,mem);
nullfree(mem);
}
return THROW(ncstat);
}
static char*
getdefinename(CDFnode* node)
{
char* spath = NULL;
NClist* path = NULL;
switch (node->nctype) {
case NC_Atomic:
/* The define name is same as the fullname with elided nodes */
path = nclistnew();
collectnodepath3(node,path,!WITHDATASET);
spath = makepathstring3(path,".",PATHNC|PATHELIDE);
nclistfree(path);
break;
case NC_Dimension:
/* Return just the node's ncname */
spath = nulldup(node->ncbasename);
break;
default:
PANIC("unexpected nctype");
}
return spath;
}
int
NCDAP_ping(const char* url)
{
OCerror ocstat = OC_NOERR;
ocstat = oc_ping(url);
return ocerrtoncerr(ocstat);
}
int
NCD3_inq_format_extended(int ncid, int* formatp, int* modep)
{
NC* nc;
int ncstatus = NC_check_id(ncid, (NC**)&nc);
if(ncstatus != NC_NOERR) return THROW(ncstatus);
if(modep) *modep = nc->mode;
if(formatp) *formatp = NC_FORMAT_DAP2;
return NC_NOERR;
}