/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc. See the COPYRIGHT file for more information. */ #include "config.h" #include #include #include #include "oc.h" #include "ocinternal.h" #include "occontent.h" #include "ocdebug.h" #include "ocdump.h" #include "oclog.h" #include "occlientparams.h" #include "ochttp.h" #undef TRACK /**************************************************/ static int ocinitialized = 0; /**************************************************/ /* Track legal ids */ #ifdef OC_FASTCONSISTENCY #define ocverify(object) ((object) != NULL && (*(object) == OCMAGIC)?1:0) #define ocassign(object) ((OCobject)(object)) #define ocassignall(list) #else /*!OC_FASTCONSISTENCY*/ static OClist* ocmap = NULL; static int ocverify(unsigned long object) { unsigned int i; void** map = (void**)oclistcontents(ocmap); unsigned int len = oclistlength(ocmap); #ifdef TRACK fprintf(stderr,"verify: %lu\n",object); fflush(stderr); #endif if(object > 0) { for(i=0;iroot; if(root == NULL) return 0; nodes = root->tree->nodes; nobjects = oclistlength(nodes); return nobjects; } /* Return all the OCobjects associated with a tree with specified root */ OCobject* oc_inq_objects(OCconnection conn, OCobject root0) { unsigned int i; OCnode* root; OClist* nodes; OCobject* objects = NULL; unsigned int nobjects; OCVERIFYX(OCnode*,root,root0,OCNULL); OCDEREF(OCnode*,root,root0); if(root == NULL) return NULL; root = root->root; if(root == NULL) return NULL; nodes = root->tree->nodes; nobjects = oclistlength(nodes); if(nodes != NULL && nobjects > 0) { size_t len = sizeof(OCobject)*(1+nobjects); objects = (OCobject*)ocmalloc(len); for(i=0;iroot; if(root == NULL) return NULL; return root->tree->text; } OCerror oc_inq_object(OCconnection conn, OCobject node0, char** namep, OCtype* objecttypep, OCtype* primitivetypep, /* if objecttype == OC_Primitive */ OCobject* containerp, unsigned int* rankp, unsigned int* subnodesp, unsigned int* nattrp) { OCnode* node; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(namep) *namep = nulldup(node->name); if(objecttypep) *objecttypep = node->octype; if(primitivetypep) *primitivetypep = node->etype; if(rankp) *rankp = node->array.rank; if(containerp) *containerp = (OCobject)node->container; if(subnodesp) *subnodesp = oclistlength(node->subnodes); if(nattrp) { if(node->octype == OC_Attribute) { *nattrp = oclistlength(node->att.values); } else { *nattrp = oclistlength(node->attributes); } } return OC_NOERR; } /* Useful accessor functions */ OCerror oc_inq_name(OCconnection conn, OCobject node0, char** namep) { OCstate* state; OCnode* node; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(state == NULL || node == NULL) return OC_EINVAL; if(namep) *namep = nulldup(node->name); return OC_NOERR; } OCerror oc_inq_nsubnodes(OCconnection conn, OCobject node0, unsigned int* nsubnodesp) { OCnode* node; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(nsubnodesp) *nsubnodesp = oclistlength(node->subnodes); return OC_NOERR; } OCerror oc_inq_primtype(OCconnection conn, OCobject node0, OCtype* typep) { OCnode* node; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(typep) *typep = node->etype; return OC_NOERR; } /* Alias for oc_inq_class */ OCerror oc_inq_type(OCconnection conn, OCobject node0, OCtype* typep) { return oc_inq_class(conn,node0,typep); } OCerror oc_inq_class(OCconnection conn, OCobject node0, OCtype* typep) { OCnode* node; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(typep) *typep = node->octype; return OC_NOERR; } OCerror oc_inq_rank(OCconnection conn, OCobject node0, unsigned int* rankp) { OCnode* node; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(rankp) *rankp = node->array.rank; return OC_NOERR; } OCerror oc_inq_nattr(OCconnection conn, OCobject node0, unsigned int* nattrp) { OCnode* node; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(nattrp) { if(node->octype == OC_Attribute) { *nattrp = oclistlength(node->att.values); } else { *nattrp = oclistlength(node->attributes); } } return OC_NOERR; } OCerror oc_inq_root(OCconnection conn, OCobject node0, OCobject* rootp) { OCnode* node; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(rootp) *rootp = (OCobject)node->root; return OC_NOERR; } OCerror oc_inq_container(OCconnection conn, OCobject node0, OCobject* containerp) { OCnode* node; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(containerp) *containerp = (OCobject)node->container; return OC_NOERR; } /* Return the subnode objects, if any, for a given object */ /* Caller must free returned list */ OCerror oc_inq_subnodes(OCconnection conn, OCobject node0, OCobject** subnodesp) { OCnode* node; OCobject* subnodes = NULL; unsigned int len; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); len = oclistlength(node->subnodes); if(len > 0) { unsigned int i; subnodes = (OCobject*)occalloc(sizeof(OCobject),len+1); for(i=0;isubnodes,i); subnodes[i] = (OCobject)ocnode; } subnodes[len] = OCNULL; /* NULL terminate */ } if(subnodesp) *subnodesp = subnodes; return OC_NOERR; } OCerror oc_inq_ith(OCconnection conn, OCobject node0, unsigned int index, OCobject* subnodeidp) { OCnode* node; OCobject subnodeid = OCNULL; unsigned int nsubnodes; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); nsubnodes = oclistlength(node->subnodes); if(nsubnodes > 0 && index < nsubnodes) { subnodeid = (OCobject)oclistget(node->subnodes,index); } else return OC_EINVAL; if(subnodeidp) *subnodeidp = subnodeid; return OC_NOERR; } /* Return the dimension objects, if any, for a given object */ /* Caller must free returned dimids */ OCerror oc_inq_dimset(OCconnection conn, OCobject node0, OCobject** dimids) { OCnode* node; OCobject* dims = NULL; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(node->array.rank > 0) { unsigned int i; dims = (OCobject*)occalloc(sizeof(OCobject),node->array.rank+1); for(i=0;iarray.rank;i++) { OCnode* dim = (OCnode*)oclistget(node->array.dimensions,i); dims[i] = (OCobject)dim; } dims[node->array.rank] = OCNULL; } if(dimids) *dimids = dims; return OC_NOERR; } OCerror oc_inq_ithdim(OCconnection conn, OCobject node0, unsigned int index, OCobject* dimidp) { OCnode* node; OCobject dimid = OCNULL; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); if(node->array.rank > 0 && index < node->array.rank) { dimid = (OCobject)oclistget(node->array.dimensions,index); } else return OC_EINVAL; if(dimidp) *dimidp = dimid; return OC_NOERR; } OCerror oc_inq_dim(OCconnection conn, OCobject node0, size_t* sizep, char** namep) { OCnode* dim; OCVERIFY(OCnode*,dim,node0); OCDEREF(OCnode*,dim,node0); if(dim->octype != OC_Dimension) return OC_EINVAL; if(sizep) *sizep = dim->dim.declsize; if(namep) *namep = nulldup(dim->name); return OC_NOERR; } /* Obtain info about the ith attribute attached to a given DDS node*/ /* This procedure returns the value as the original DAS string */ OCerror oc_inq_attrstrings(OCconnection conn, OCobject node0, unsigned int i, char** namep, OCtype* octypep, unsigned int* nvaluesp, char*** stringsp) { OCnode* node; OCattribute* attr; unsigned int nattrs; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); nattrs = oclistlength(node->attributes); if(i >= nattrs) return OC_EINVAL; attr = (OCattribute*)oclistget(node->attributes,i); if(namep) *namep = strdup(attr->name); if(octypep) *octypep = attr->etype; if(nvaluesp) *nvaluesp = attr->nvalues; if(stringsp) { if(attr->nvalues > 0) { size_t space = attr->nvalues * sizeof(char*); char** strings = ocmalloc(space); for(i=0;invalues;i++) strings[i] = nulldup(attr->values[i]); *stringsp = strings; } else *stringsp = NULL; } return OC_NOERR; } /* This procedure returns the value as the default binary value corresponding to the string value */ OCerror oc_inq_attr(OCconnection conn, OCobject node0, unsigned int i, char** namep, OCtype* octypep, unsigned int* nvaluesp, void** valuesp) { OCnode* node; OCattribute* attr; unsigned int nattrs; OCVERIFY(OCnode*,node,node0); OCDEREF(OCnode*,node,node0); nattrs = oclistlength(node->attributes); if(i >= nattrs) return OC_EINVAL; attr = (OCattribute*)oclistget(node->attributes,i); if(namep) *namep = strdup(attr->name); if(octypep) *octypep = attr->etype; if(nvaluesp) *nvaluesp = attr->nvalues; if(valuesp && attr->nvalues > 0) { void* memory = NULL; memory = oclinearize(attr->etype,attr->nvalues,attr->values); *valuesp = memory; } return OC_NOERR; } /* Convenience function */ void oc_attr_reclaim(OCtype etype, unsigned int nvalues, void* values) { if(nvalues == 0 || values == NULL) return; if(etype == OC_String || etype == OC_URL) { unsigned int i; char** strings = (char**)values; for(i=0;ioctype != OC_Attribute) return OC_EINVAL; if(nvaluesp) *nvaluesp = oclistlength(attr->att.values); return OC_NOERR; } OCerror oc_inq_dasattr(OCconnection conn, OCobject node0, unsigned int i, OCtype* primtypep, char** valuep) { OCnode* attr; unsigned int nvalues; OCVERIFY(OCnode*,attr,node0); OCDEREF(OCnode*,attr,node0); if(attr->octype != OC_Attribute) return OC_EINVAL; nvalues = oclistlength(attr->att.values); if(i >= nvalues) return OC_EINVAL; if(valuep) *valuep = nulldup((char*)oclistget(attr->att.values,i)); if(primtypep) *primtypep = attr->etype; return OC_NOERR; } /**************************************************/ /* Fetch and parse a given class of DXD the server specified at open time, and using a specified set of constraints. Return the root node of the parsed tree of objects. */ OCerror oc_fetch(OCconnection conn, const char* constraint, OCdxd dxdkind, OCobject* rootp) { return oc_fetchf(conn,constraint,dxdkind,0,rootp); } OCerror oc_fetchf(OCconnection conn, const char* constraint, OCdxd dxdkind, OCflags flags, OCobject* rootp) { OCstate* state; OCerror ocerr = OC_NOERR; OCnode* root; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); ocerr = ocfetchf(state,constraint,dxdkind,flags,&root); if(ocerr) return ocerr; ocassignall(root->tree->nodes); if(rootp) *rootp = (OCobject)ocassign(root); return ocerr; } OCerror oc_data_root(OCconnection conn, OCobject root0, OCdata content0) { OCstate* state; OCnode* root; OCcontent* content; OCerror ocerr = OC_NOERR; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCnode*,root,root0); OCDEREF(OCnode*,root,root0); OCVERIFY(OCcontent*,content,content0); OCDEREF(OCcontent*,content,content0); if(root->tree == NULL) {OCTHROWCHK((ocerr=OC_EINVAL)); goto fail;} ocerr = ocrootdata(state,root,content); fail: return ocerr; } OCdata oc_data_new(OCconnection conn) { OCstate* state; OCVERIFYX(OCstate*,state,conn,OCNULL); OCDEREF(OCstate*,state,conn); return (OCdata)ocassign(ocnewcontent(state)); } OCerror oc_data_free(OCconnection conn, OCdata content0) { OCstate* state; OCcontent* content; if(content0 == OCNULL) return OC_NOERR; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCcontent*,content,content0); OCDEREF(OCcontent*,content,content0); ocfreecontent(state,content); return OC_NOERR; } OCerror oc_data_ith(OCconnection conn, OCdata parentdata, size_t index, OCdata subdata) { OCstate* state; OCcontent* parent; OCcontent* child; OCerror ocerr = OC_NOERR; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCcontent*,parent,parentdata); OCDEREF(OCcontent*,parent,parentdata); OCVERIFY(OCcontent*,child,subdata); OCDEREF(OCcontent*,child,subdata); ocerr = ocdataith(state,parent,index,child); return ocerr; } OCerror oc_data_get(OCconnection conn, OCdata currentcontent, void* memory, size_t memsize, size_t start, size_t count) { OCstate* state; OCcontent* current; OCerror ocerr = OC_NOERR; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCcontent*,current,currentcontent); OCDEREF(OCcontent*,current,currentcontent); ocerr = ocgetcontent(state,current,memory,memsize,start,count); if(ocerr == OC_EDATADDS) ocdataddsmsg(state,current->tree); return ocerr; } OCerror oc_data_count(OCconnection conn, OCdata content0, size_t* sizep) { OCstate* state; OCcontent* current; OCerror ocerr = OC_NOERR; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCcontent*,current,content0); OCDEREF(OCcontent*,current,content0); ocerr = ocdatacount(state, current, sizep); return ocerr; } OCerror oc_data_index(OCconnection conn, OCdata content0, size_t* sizep) { OCcontent* current; OCerror ocerr = OC_NOERR; OCVERIFY(OCcontent*,current,content0); OCDEREF(OCcontent*,current,content0); if(sizep) *sizep = (current->cache.valid ? current->cache.index : 0); return ocerr; } OCerror oc_data_object(OCconnection conn, OCdata content0, OCobject* op) { OCcontent* current; OCerror ocerr = OC_NOERR; OCVERIFY(OCcontent*,current,content0); OCDEREF(OCcontent*,current,content0); if(op) *op = (OCobject)current->node; return ocerr; } OCerror oc_data_mode(OCconnection conn, OCdata content0, OCmode* modep) { OCcontent* current; OCerror ocerr = OC_NOERR; OCVERIFY(OCcontent*,current,content0); OCDEREF(OCcontent*,current,content0); if(modep) *modep = current->mode; return ocerr; } /**************************************************/ /* OCtype management */ size_t oc_typesize(OCtype etype) { return octypesize(etype); } const char* oc_typetostring(OCtype octype) { return octypetoddsstring(octype); } OCerror oc_typeprint(OCtype etype, char* buf, size_t bufsize, void* value) { return octypeprint(etype,buf,bufsize,value); } /**************************************************/ /* The oc_logXXX procedures are define in oclog.c */ /**************************************************/ /* Miscellaneous */ const char* oc_errstring(int err) { return ocerrstring(err); } /* Get clientparameters from the URL */ const char* oc_clientparam_get(OCconnection conn, const char* param) { OCstate* state; OCVERIFYX(OCstate*,state,conn,NULL); OCDEREF(OCstate*,state,conn); return ocparamlookup(state,param); } #ifdef OCIGNORE /* Delete client parameter return value: OC_NOERR => defined; deletion performed OC_EINVAL => not already defined */ OCerror oc_clientparam_delete(OCconnection conn, const char* param) { OCstate* state; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); return ocparamdelete(state->clientparams,param); } /* Insert client parameter return value: OC_NOERR => not already define; insertion performed OC_EINVAL => already defined */ OCerror oc_clientparam_insert(OCconnection conn, const char* param, const char* value) { OCstate* state; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); state->clientparams = dapparaminsert(state->clientparams,param,value); return OC_NOERR; } /* Replace client parameter return value: OC_NOERR => already define; replacement performed OC_EINVAL => not already defined */ OCerror oc_clientparam_replace(OCconnection conn, const char* param, const char* value) { OCstate* state; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); return dapparamreplace(state->clientparams,param,value); } #endif OCerror oc_dd(OCconnection conn, OCobject root0, int level) { OCstate* state; OCnode* root; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCnode*,root,root0); OCDEREF(OCnode*,root,root0); ocdd(state,root,1,level); return OC_NOERR; } OCerror oc_ddnode(OCconnection conn, OCobject root0) { OCnode* root; OCVERIFY(OCnode*,root,root0); OCDEREF(OCnode*,root,root0); ocdumpnode(root); return OC_NOERR; } /* Merge a specified DAS into a specified DDS or DATADDS */ OCerror oc_attach_das(OCconnection conn, OCobject dasroot, OCobject ddsroot) { OCstate* state; OCnode* das; OCnode* dds; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCnode*,das,dasroot); OCDEREF(OCnode*,das,dasroot); OCVERIFY(OCnode*,dds,ddsroot); OCDEREF(OCnode*,dds,ddsroot); return ocddsdasmerge(state,das,dds); } #if 0 /* I suppressed this operation because I realized that it appears to be impossible to implement correctly in general. It also exposes a flaw in the protocol. If I have a dds with two identical Grids, G1 and G2, and I ask for the projection ?G1.temp,G2.temp, then I get back a DATADDS with duplicated temp fields, which means the DATADDS is illegal. The problem is that the protocol throws away important scoping information about the fact that each temp field is part of a different grid. */ /* Connect a specified DATADDS tree to a specified DDS tree */ OCerror oc_attach_datadds(OCconnection conn, OCobject dataddsroot, OCobject ddsroot) { OCstate* state; OCnode* dxd; OCnode* dds; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCnode*,dxd,dataddsroot); OCDEREF(OCnode*,dxd,dataddsroot); OCVERIFY(OCnode*,dds,ddsroot); OCDEREF(OCnode*,dds,ddsroot); /* get the true roots */ dds = dds->root; dxd = dxd->root; if(dds == NULL || dxd == NULL) return OC_EBADID; /* correlate the DATADDS to the DDS */ return occorrelate(dxd,dds); } /* Return the attached DATADDS object for a given DDS object */ OCerror oc_inq_datadds(OCconnection conn, OCobject dds0, OCobject* dataddsp) { OCstate* state; OCnode* dds; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); OCVERIFY(OCnode*,dds,dds0); OCDEREF(OCnode*,dds,dds0); if(dataddsp) *dataddsp = (OCobject)dds->datadds; return OC_NOERR; } #endif /**************************************************/ OCerror oc_svcerrordata(OCconnection conn, char** codep, char** msgp, long* httpp) { OCstate* state; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); return ocsvcerrordata(state,codep,msgp,httpp); } /**************************************************/ /* Experimental: this is useful for the netcdf DRNO project. */ /* New 10/31/2009: return the size (in bytes) of the fetched datadds. */ OCerror oc_raw_xdrsize(OCconnection conn, OCobject root0, size_t* sizep) { OCnode* root; OCerror ocerr = OC_NOERR; OCVERIFY(OCnode*,root,root0); OCDEREF(OCnode*,root,root0); if(sizep == NULL) goto done; if(root->tree == NULL || root->tree->dxdclass != OCDATADDS) {OCTHROWCHK((ocerr=OC_EINVAL)); goto done;} if(sizep) *sizep = root->tree->data.datasize; done: return ocerr; } /* Resend a url as a head request to check the Last-Modified time */ OCerror oc_update_lastmodified_data(OCconnection conn) { OCstate* state; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); return ocupdatelastmodifieddata(state); } long oc_get_lastmodified_data(OCconnection conn) { OCstate* state; OCVERIFY(OCstate*,state,conn); OCDEREF(OCstate*,state,conn); return state->datalastmodified; } int oc_dumpnode(OCconnection conn, OCobject root0) { OCnode* root; OCerror ocerr = OC_NOERR; OCVERIFY(OCnode*,root,root0); OCDEREF(OCnode*,root,root0); ocdumpnode(root); return ocerr; } OCerror oc_ping(const char* url) { return ocping(url); }