diff --git a/src/neuroprovenance.c b/src/neuroprovenance.c index c126040..85515a4 100644 --- a/src/neuroprovenance.c +++ b/src/neuroprovenance.c @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include #include #include diff --git a/src/neuroprovenance.c~ b/src/neuroprovenance.c~ new file mode 100644 index 0000000..85515a4 --- /dev/null +++ b/src/neuroprovenance.c~ @@ -0,0 +1,310 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include "provenance.h" +#include "neuroprovenance.h" + +/*Initialization and Cleanup routines*/ +ProvObjectPtr newProvenanceObject(const char* id) +{ + ProvPtr p_prov = newProvenanceFactory(id); + // add new namespace + addNamespace(p_prov, "https://github.com/INCF/ProvenanceLibrary/wiki/terms", "ni"); + //xmlNewNs(root_node, "http://incf.org/incf-schema", "incf"); + //xmlNewNs(root_node, "http://this.namespace.needs.to/be.decided", "ni"); + return((ProvObjectPtr)p_prov); +} + +ProvObjectPtr newProvenanceObjectFromFile(const char* filename) +{ + ProvPtr p_prov = newProvenanceFactoryFromFile(filename); + return((ProvObjectPtr)p_prov); +} + +ProvObjectPtr newProvenanceObjectFromBuffer(const char* buffer, int bufferSize) +{ + ProvPtr p_prov = newProvenanceFactoryFromMemoryBuffer(buffer, bufferSize); + return((ProvObjectPtr)p_prov); +} + +int delProvenanceObject(ProvObjectPtr p_prov){ + return(delProvenanceFactory((ProvPtr)p_prov)); +} + +/*IO routines*/ +void printProvenance(ProvObjectPtr p_prov, const char* filename) +{ + print_provenance((ProvPtr)p_prov, filename); +} + +/* +return provenance as a string buffer +*/ +void toBuffer(ProvObjectPtr p_prov, char** buffer, int* buffer_size) +{ + dumpToMemoryBuffer((ProvPtr)p_prov, buffer, buffer_size); +} + +/* +free the memory buffer +*/ +void freeBuffer(char* buffer) +{ + freeMemoryBuffer(buffer); +} + +/* Record creation routines */ + +/* Create a new process */ +ProcessPtr newProcess(ProvObjectPtr p_prov, const char* startTime, const char* endTime, const char* type) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + IDREF act_id = newActivity(p_record, NULL, startTime, endTime); + if (type != NULL) + addAttribute(p_record, act_id, "prov", NULL, "label", type); + assert(act_id); + return((ProcessPtr)act_id); +} + +/* Associate an input with a process */ +REFID newProcessInput(ProvObjectPtr p_prov, ProcessPtr p_proc, const char* name, const char* value, const char* type) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + IDREF id = newEntity(p_record); + assert(id); + if (type == NULL) + addAttribute(p_record, id, "prov", "xsd:QName", "type", "ni:input"); + else + addAttribute(p_record, id, "prov", "xsd:string", "type", type); + addAttribute(p_record, id, "ni", "xsd:string", "name", name); + addAttribute(p_record, id, "ni", "xsd:string", "value", value); + IDREF used_id = newUsedRecord(p_record, (IDREF)p_proc, id, NULL); + assert(used_id); + freeID(used_id); + return((REFID)id); +} + +/* Associate an output with a process */ +REFID newProcessOutput(ProvObjectPtr p_prov, ProcessPtr p_proc, const char* name, const char* value, const char* type) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + IDREF id = newEntity(p_record); + assert(id); + if (type == NULL) + addAttribute(p_record, id, "prov", "xsd:QName", "type", "ni:output"); + else + addAttribute(p_record, id, "prov", "xsd:string", "type", type); + addAttribute(p_record, id, "ni", "xsd:string", "name", name); + addAttribute(p_record, id, "ni", "xsd:string", "value", value); + IDREF gen_id = newGeneratedByRecord(p_record, id, (IDREF)p_proc, NULL); + assert(gen_id); + freeID(gen_id); + return((REFID)id); +} + +/* Associated a input with a process */ +int addInput(ProvObjectPtr p_prov, ProcessPtr p_proc, REFID input) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + IDREF id = newUsedRecord(p_record, (IDREF)p_proc, input, NULL); + assert(id); + freeID(id); + return(0); +} + +/* Associated an output with a process */ +int addOutput(ProvObjectPtr p_prov, ProcessPtr p_proc, REFID output) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + IDREF id = newGeneratedByRecord(p_record, output, (IDREF)p_proc, NULL); + assert(id); + freeID(id); + return(0); +} + +/* + Does not use a lock. assumes file does not change while hash is + being computed. +*/ +static char* get_md5_hash(const char* path) +{ + FILE *file = fopen(path, "rb"); + if(!file) return NULL; + const int bufSize = 32768; + unsigned char *buffer = malloc(bufSize); + int bytesRead = 0; + if(!buffer) return NULL; + + EVP_MD_CTX mdctx; + unsigned char md_value[EVP_MAX_MD_SIZE]; + unsigned int md_len; + + EVP_DigestInit(&mdctx, EVP_md5()); + while((bytesRead = fread(buffer, 1, bufSize, file))) + { + EVP_DigestUpdate(&mdctx, buffer, (size_t) bytesRead); + } + EVP_DigestFinal_ex(&mdctx, md_value, &md_len); + EVP_MD_CTX_cleanup(&mdctx); + unsigned char* p_hash = (unsigned char*)malloc((2*md_len+1)*sizeof(unsigned char)); + unsigned char* p_idx = p_hash; + int i; + for(i = 0; i < md_len; i++, p_idx+=2) + sprintf(p_idx, "%02x", md_value[i]); + fclose(file); + free(buffer); + return p_hash; +} + +/* Create a new file record */ +REFID newFile(ProvObjectPtr p_prov, const char* filename, const char* type) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + IDREF id = newEntity(p_record); + unsigned char* p_hash; + if (type == NULL) + addAttribute(p_record, id, "prov", "xsd:QName", "type", "ni:file"); + else + addAttribute(p_record, id, "prov", "xsd:QName", "type", type); + //addAttribute(p_record, id, "ni", "xsd:string", "path", filename); + p_hash = get_md5_hash(filename); + if (p_hash != NULL){ + addAttribute(p_record, id, "ni", NULL, "md5sum", p_hash); + free(p_hash); + } + assert(id); + return((REFID)id); +} + +/* Create a new file collection record */ +REFID newFileCollection(ProvObjectPtr p_prov, const char** filenames, int n_files, const char* type) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + IDREF id = newEntity(p_record); + int i; + if (type == NULL) + addAttribute(p_record, id, "prov", "xsd:QName", "type", "ni:filelist"); + else + addAttribute(p_record, id, "prov", "xsd:string", "type", type); + for(i=0; ip_record; + IDREF id = newEntity(p_record); + addAttribute(p_record, id, "prov", "xsd:QName", "type", "ni:environ"); + addAttribute(p_record, id, "ni", NULL, name, getenv(name)); + assert(id); + return((REFID)id); +} + +/* Associated a environment variable with a process */ +REFID addAllEnvironVariables(ProvObjectPtr p_prov, ProcessPtr p_proc, char **envp) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + IDREF id = newEntity(p_record); + char buffer[255]; + int i; + addAttribute(p_record, id, "prov", "xsd:QName", "type", "ni:environ"); + //addAttribute(p_record, id, "ni", "foo", "foo"); + char** env; + for (env = envp; *env != 0; env++) + { + char* thisEnv = *env; + char *name; + char* p_index = thisEnv; + int pos = 0; + while (thisEnv[pos++] != '='); + name = strndup(thisEnv, pos-1); + if (name[0] != '_') + { + sprintf(buffer, "%s\0", &thisEnv[pos]); + //fprintf(stderr, "<%s>=%s\n", name, &buffer); + addAttribute(p_record, id, "ni", NULL, name, &buffer); + } + free(name); + } + assert(id); + return((REFID)id); +} + +/* Add a key-value informratin pair to a process */ +int addKeyValuePair(ProvObjectPtr p_prov, ProcessPtr p_proc, const char* key, const char* value) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + addAttribute(p_record, (IDREF)p_proc, "ni", NULL, key, value); + return(0); +} + +/* utility: convert inputs to a commandline */ +static char * get_cmdline(int argc, char **argv){ + int total_len = 0, i; + for(i = 0; i < argc; i++) + total_len += strlen(argv[i]) + 1; + char * cmdline = (char *)malloc(total_len*sizeof(char)); + char * p_index = cmdline; + for(i = 0; i < argc; i++){ + strcpy(p_index, argv[i]); + if ((i + 1) < argc) strcat(p_index, " "); + p_index += (strlen(argv[i]) + 1); + } + return cmdline; +} + +/* Add command line to a process */ +int addCommandLine(ProvObjectPtr p_prov, ProcessPtr p_proc, int argc, char** argv) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + IDREF id = (IDREF)p_proc; + char *cmdline = get_cmdline(argc, argv); + addAttribute(p_record, id, "ni", NULL, "cmdline", cmdline); + free(cmdline); + return(0); +} + +/* Link up two processes (not implemented) */ +int addDependency(ProvObjectPtr p_prov, ProcessPtr parent, ProcessPtr child) +{ + return(0); +} + +/* Add additional type information to a record object (process/input/output) */ +int addType(ProvObjectPtr p_prov, REFID id, const char* type, const char* xsdType) +{ + RecordPtr p_record = ((ProvPtr)p_prov)->p_record; + if (xsdType == NULL) + addAttribute(p_record, id, "prov", "xsd:string", "type", type); + else + addAttribute(p_record, id, "prov", xsdType, "type", type); + return(0); +} + +int changeREFID(ProvObjectPtr p_prov, REFID id, const char* new_id) +{ + return(changeID(((ProvPtr)p_prov)->p_record, (IDREF)id, new_id)); +} + +int freeREFID(REFID id) +{ + freeID((IDREF)id); + return(0); +} + +int freeProcess(ProcessPtr p_proc) +{ + return(freeREFID((REFID)p_proc)); +} + +int addProvenanceRecord(ProvObjectPtr p_curprov, const ProvObjectPtr p_otherprov, const char *prefix) +{ + return(addProvAsAccount(((ProvPtr)p_curprov)->p_record, (ProvPtr)p_otherprov, prefix)); +} diff --git a/src/provenance.c b/src/provenance.c index 050d06d..328e244 100644 --- a/src/provenance.c +++ b/src/provenance.c @@ -10,7 +10,7 @@ // Created by Satrajit Ghosh on 11/25/11. // Copyright (c) 2011 TankThink Labs LLC. All rights reserved. // - +#define _GNU_SOURCE #include #include #include diff --git a/src/provenance.c~ b/src/provenance.c~ new file mode 100644 index 0000000..050d06d --- /dev/null +++ b/src/provenance.c~ @@ -0,0 +1,671 @@ +// +// provenance.c +// libprov +// +// This library is being developed based on the draft model of the +// W3C provenance working group. +// http://dvcs.w3.org/hg/prov/raw-file/0f43d8bc798c/model/ProvenanceModel.html +// https://github.com/lucmoreau/ProvToolbox/blob/master/xml/src/main/resources +// +// Created by Satrajit Ghosh on 11/25/11. +// Copyright (c) 2011 TankThink Labs LLC. All rights reserved. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(LIBXML_XPATH_ENABLED) && defined(LIBXML_SAX1_ENABLED) + +#include "provenance.h" + +typedef struct { + xmlDocPtr doc; +} PrivateProv; + +typedef PrivateProv *PrivateProvPtr; + +typedef struct{ + xmlXPathContextPtr xpathCtx; + xmlXPathObjectPtr xpathObj; +} XPathQuery; + +typedef XPathQuery *XPathQueryPtr; + +/* Private U=utility routines */ + +/* +Query the document for elements using xpath +*/ +static XPathQueryPtr query_xpath(const xmlDocPtr doc, const xmlChar* xpathExpr) +{ + xmlXPathContextPtr xpathCtx; + xmlXPathObjectPtr xpathObj; + + /* Create xpath evaluation context */ + xpathCtx = xmlXPathNewContext(doc); + if(xpathCtx == NULL) { + fprintf(stderr,"Error: unable to create new XPath context\n"); + return(NULL); + } + + xpathCtx->namespaces = xmlGetNsList(doc, xmlDocGetRootElement(doc)); + xpathCtx->nsNr = 0; + if (xpathCtx->namespaces != NULL) { + while (xpathCtx->namespaces[xpathCtx->nsNr] != NULL) + xpathCtx->nsNr++; + } + + /* Evaluate xpath expression */ + xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); + if(xpathObj == NULL) { + fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr); + return(NULL); + } + XPathQueryPtr p_xpquery = (XPathQueryPtr)malloc(sizeof(XPathQuery)); + p_xpquery->xpathCtx = xpathCtx; + p_xpquery->xpathObj = xpathObj; + return p_xpquery; +} + +static int free_xpquery(XPathQueryPtr p_xpquery){ + if (p_xpquery->xpathCtx->namespaces != NULL) + xmlFree(p_xpquery->xpathCtx->namespaces); + xmlXPathFreeObject(p_xpquery->xpathObj); + xmlXPathFreeContext(p_xpquery->xpathCtx); + free(p_xpquery); + return(0); +} + +/* + Get the namespace prefix associated with the node +*/ +static xmlNsPtr getNs(xmlNodePtr p_node, const char *prefix) +{ + xmlNsPtr ns = NULL; + xmlNodePtr root_node = xmlDocGetRootElement(p_node->doc); + if (prefix == NULL) + ns = xmlSearchNs(p_node->doc, root_node, BAD_CAST "prov"); + else + ns = xmlSearchNs(p_node->doc, root_node, BAD_CAST prefix); + if (ns == NULL) { + if (prefix == NULL) + fprintf(stderr, "Prov namespace not defined.\n"); + else + fprintf(stderr, "Namespace %s not defined. Use AddNamespace().\n", prefix); + } + /* + else + fprintf(stderr, "Namespace: %s\n", ns->prefix); + */ + return(ns); +} + +/* +Generic element adding routine +*/ +static xmlNodePtr add_element(xmlNodePtr p_record, + const char* root, + const char* element_name, + const char* id_prefix) +{ + xmlNodePtr p_node, root_node; + xmlChar id[255]; + int size1, size2; + xmlChar xpathExpr[255]; + + if (root == NULL) + sprintf(xpathExpr, "//prov:%s", element_name); + else + sprintf(xpathExpr, "//prov:%s/prov:%s", root, element_name); + if (id_prefix != NULL){ + strcat(xpathExpr, "[@prov:id]"); + } + //fprintf(stderr, "Query: %s\n", xpathExpr); + XPathQueryPtr p_xpquery = query_xpath(p_record->doc, + xpathExpr); + + size1 = (p_xpquery->xpathObj->nodesetval) ? p_xpquery->xpathObj->nodesetval->nodeNr : 0; + free_xpquery(p_xpquery); + + if (root != NULL){ + sprintf(xpathExpr, "//prov:%s", root); + p_xpquery = query_xpath(p_record->doc, xpathExpr); + + size2 = (p_xpquery->xpathObj->nodesetval) ? p_xpquery->xpathObj->nodesetval->nodeNr : 0; + if (size2 == 0) + root_node = xmlNewChild(p_record, NULL, BAD_CAST root, NULL); + else + root_node = p_xpquery->xpathObj->nodesetval->nodeTab[0]; + free_xpquery(p_xpquery); + } + else + root_node = p_record; + + xmlNsPtr ns = getNs(root_node, NULL); + p_node = xmlNewChild(root_node, ns, BAD_CAST element_name, NULL); + if (id_prefix != NULL){ + sprintf(id, "%s_%d", id_prefix, size1); + //fprintf(stderr, "id: %s\n", id); + xmlNewNsProp(p_node, ns, BAD_CAST "id", BAD_CAST id); + } + return p_node; +} + +/* Add a records element to a node +*/ +static RecordPtr newRecord(ProvPtr p_prov) +{ + assert(p_prov); + PrivateProvPtr p_priv = (PrivateProvPtr)(p_prov->private); + XPathQueryPtr p_xpquery = query_xpath(p_priv->doc, "/prov:container"); + + int size = (p_xpquery->xpathObj->nodesetval) ? p_xpquery->xpathObj->nodesetval->nodeNr : 0; + if (size != 1){ + fprintf(stderr, "No container element found or multiple elements found\n"); + return NULL; + } + RecordPtr p_record = (RecordPtr)add_element(p_xpquery->xpathObj->nodesetval->nodeTab[0], NULL, BAD_CAST "records", NULL); + free_xpquery(p_xpquery); + return(p_record); +} + + +/*Initialization and Cleanup routines*/ +/* +Create a provenance object that can be used by a client to generate an +instance of a provenance schema +*/ +ProvPtr newProvenanceFactory(const char* id) +{ + //fprintf(stdout, "Creating provenance object [BEGIN]\n"); + LIBXML_TEST_VERSION; + ProvPtr p_prov = (ProvPtr)malloc(sizeof(Provenance)); + if(p_prov == NULL) { + fprintf(stderr, "Error: unable to create Provenance struct\n"); + return(NULL); + } + assert(p_prov); + assert(id); + p_prov->id = strdup(id); + p_prov->private = (void *)malloc(sizeof(PrivateProv)); + + PrivateProvPtr p_priv = (PrivateProvPtr)(p_prov->private); + p_priv->doc = xmlNewDoc(BAD_CAST "1.0"); + xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "container"); + xmlDocSetRootElement(p_priv->doc, root_node); + root_node->ns = xmlNewNs(root_node, "http://openprovenance.org/prov-xml#", "prov"); + xmlNewNs(root_node, "http://www.w3.org/2001/XMLSchema-instance", "xsi"); + xmlNewNs(root_node, "http://www.w3.org/2001/XMLSchema", "xsd"); + xmlNewNsProp(root_node, root_node->ns, BAD_CAST "id", BAD_CAST id); + + //fprintf(stdout, "Creating provenance object [END]\n"); + p_prov->p_record = (void *)newRecord(p_prov); + return(p_prov); +} + +/* Read a provenance document from a file */ +ProvPtr newProvenanceFactoryFromFile(const char* filename) +{ + LIBXML_TEST_VERSION; + ProvPtr p_prov = (ProvPtr)malloc(sizeof(Provenance)); + if(p_prov == NULL) { + fprintf(stderr, "Error: unable to create Provenance struct\n"); + return(NULL); + } + assert(p_prov); + p_prov->private = (void *)malloc(sizeof(PrivateProv)); + + PrivateProvPtr p_priv = (PrivateProvPtr)(p_prov->private); + p_priv->doc = xmlReadFile(filename, NULL, 0); + if (p_priv->doc == NULL) { + fprintf(stderr, "error: could not parse file %s\n", filename); + free(p_prov->private); + free(p_prov); + return(NULL); + } + xmlNodePtr root_node = xmlDocGetRootElement(p_priv->doc); + p_prov->id = (char*)xmlGetProp(root_node, BAD_CAST "id"); + XPathQueryPtr p_xpquery = query_xpath(p_priv->doc, "/prov:container/prov:records"); + int size = (p_xpquery->xpathObj->nodesetval) ? p_xpquery->xpathObj->nodesetval->nodeNr : 0; + if (size != 1){ + fprintf(stderr, "No records element found\n"); + free(p_prov->private); + xmlFree((xmlChar*)p_prov->id); + free(p_prov); + free_xpquery(p_xpquery); + return NULL; + } + p_prov->p_record = (void *)p_xpquery->xpathObj->nodesetval->nodeTab[0]; + free_xpquery(p_xpquery); + return(p_prov); +} + +/* Construct tree from a provenance model in memory */ +ProvPtr newProvenanceFactoryFromMemoryBuffer(const char* buffer, int bufferSize) +{ + LIBXML_TEST_VERSION; + ProvPtr p_prov = (ProvPtr)malloc(sizeof(Provenance)); + if(p_prov == NULL) { + fprintf(stderr, "Error: unable to create Provenance struct\n"); + return(NULL); + } + assert(p_prov); + p_prov->private = (void *)malloc(sizeof(PrivateProv)); + + PrivateProvPtr p_priv = (PrivateProvPtr)(p_prov->private); + p_priv->doc = xmlReadMemory((xmlChar*)buffer, bufferSize, "noname.xml", NULL, 0); + if (p_priv->doc == NULL) { + fprintf(stderr, "error: could not parse memory buffer\n"); + free(p_prov->private); + free(p_prov); + return(NULL); + } + //fprintf(stdout, "printing doc\n"); + //xmlSaveFormatFileEnc("-", p_priv->doc, "UTF-8", 1); + xmlNodePtr root_node = xmlDocGetRootElement(p_priv->doc); + assert(root_node); + xmlChar* p_id = xmlGetProp(root_node, BAD_CAST "id"); + assert(p_id); + p_prov->id = p_id; + XPathQueryPtr p_xpquery = query_xpath(p_priv->doc, "/prov:container/prov:records"); + int size = (p_xpquery->xpathObj->nodesetval) ? p_xpquery->xpathObj->nodesetval->nodeNr : 0; + if (size != 1){ + fprintf(stderr, "%d records element found in container id[%s] \n", size, p_prov->id); + xmlFree((xmlChar*)p_prov->id); + free(p_prov->private); + free(p_prov); + free_xpquery(p_xpquery); + return NULL; + } + p_prov->p_record = (void *)p_xpquery->xpathObj->nodesetval->nodeTab[0]; + free_xpquery(p_xpquery); + return(p_prov); +} + +/* +Clean up the memory used by the xml document +*/ +int delProvenanceFactory(ProvPtr p_prov) +{ + assert(p_prov); + //fprintf(stdout, "Destroying provenance object [BEGIN]\n"); + PrivateProvPtr p_priv = (PrivateProvPtr)(p_prov->private); + + /*free the document */ + xmlFreeDoc(p_priv->doc); + + /* + *Free the global variables that may + *have been allocated by the parser. + */ + xmlCleanupParser(); + + /* + * this is to debug memory for regression tests + */ + xmlFree((xmlChar*)p_prov->id); + xmlMemoryDump(); + free(p_priv); + free(p_prov); + //fprintf(stdout, "Destroying provenance object [END]\n"); + + return(0); +} + +int addNamespace(ProvPtr p_prov, const char* href, const char* prefix) +{ + assert(p_prov); + PrivateProvPtr p_priv = (PrivateProvPtr)(p_prov->private); + xmlNodePtr root_node = xmlDocGetRootElement(p_priv->doc); + xmlNewNs(root_node, href, prefix); + return(0); +} + +/* +print provenance info to stdout (filename==NULL) or to a file +*/ +void print_provenance(ProvPtr p_prov, const char *filename) +{ + assert(p_prov); + //fprintf(stdout, "Printing provenance\n"); + //fprintf(stdout, "ID: %d\n", p_prov->id); + + /*Get the root element node */ + PrivateProvPtr p_priv = (PrivateProvPtr)(p_prov->private); + if (filename == NULL) + xmlSaveFormatFileEnc("-", p_priv->doc, "UTF-8", 1); + else + xmlSaveFormatFileEnc(BAD_CAST filename, p_priv->doc, "UTF-8", 1); +} + +/* +return provenance as a string buffer +*/ +void dumpToMemoryBuffer(ProvPtr p_prov, char** buffer, int* p_buffer_size) +{ + assert(p_prov); + + /*Get the root element node */ + PrivateProvPtr p_priv = (PrivateProvPtr)(p_prov->private); + xmlDocDumpFormatMemory(p_priv->doc, (xmlChar**)buffer, p_buffer_size, 1); +} + +/* +free the memory buffer +*/ +void freeMemoryBuffer(char* buffer) +{ + assert(buffer); + xmlFree((xmlChar*)buffer); +} + +/* Record creation routines */ + +RecordPtr newAccount(RecordPtr p_record, const char* asserter) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, NULL, "account", "ac"); + if (asserter != NULL) + xmlNewChild(p_node, NULL, BAD_CAST "asserter", BAD_CAST asserter); + return (RecordPtr)add_element(p_node, NULL, BAD_CAST "records", NULL); +} + +IDREF newEntity(RecordPtr p_record) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, NULL, "entity", "e"); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newActivity(RecordPtr p_record, const char* recipeLink, + const char* startTime, const char* endTime) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, NULL, "activity", "a"); + if (startTime != NULL) + xmlNewChild(p_node, NULL, BAD_CAST "startTime", BAD_CAST startTime); + if (endTime != NULL) + xmlNewChild(p_node, NULL, BAD_CAST "endTime", BAD_CAST endTime); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newAgent(RecordPtr p_record) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, NULL, "agent", "ag"); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newNote(RecordPtr p_record) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, NULL, "note", "n"); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +/* add relations */ +IDREF newUsedRecord(RecordPtr p_record, IDREF activity, IDREF entity, const char* time) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, "dependencies", "used", "u"); + xmlNsPtr ns = getNs(p_node, NULL); + xmlNodePtr p_child = xmlNewChild(p_node, ns, BAD_CAST "activity", NULL); + xmlNewNsProp(p_child, ns, "ref", (xmlChar*) activity); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "entity", NULL), ns, BAD_CAST "ref", BAD_CAST entity); + if (time != NULL) + xmlNewChild(p_node, ns, BAD_CAST "time", BAD_CAST time); + return(xmlGetProp(p_node, "id")); +} + +IDREF newGeneratedByRecord(RecordPtr p_record, IDREF entity, IDREF activity, const char* time) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, "dependencies", "wasGeneratedBy", "wgb"); + xmlNsPtr ns = getNs(p_node, NULL); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "entity", NULL), ns, BAD_CAST "ref", BAD_CAST entity); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "activity", NULL), ns, BAD_CAST "ref", BAD_CAST activity); + if (time != NULL) + xmlNewChild(p_node, ns, BAD_CAST "time", BAD_CAST time); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newAssociatedWithRecord(RecordPtr p_record, IDREF activity, IDREF agent, const char* startTime, const char* endTime) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, "dependencies", "wasAssociatedWith", "waw"); + xmlNsPtr ns = getNs(p_node, NULL); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "activity", NULL), ns, BAD_CAST "ref", BAD_CAST activity); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "agent", NULL), ns, BAD_CAST "ref", BAD_CAST agent); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newControlledByRecord(RecordPtr p_record, IDREF activity, IDREF agent, const char* startTime, const char* endTime) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, "dependencies", "wasControlledBy", "wcb"); + xmlNsPtr ns = getNs(p_node, NULL); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "effect", NULL), ns, BAD_CAST "ref", BAD_CAST activity); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "cause", NULL), ns, BAD_CAST "ref", BAD_CAST agent); + if (startTime != NULL) + xmlNewChild(p_node, ns, BAD_CAST "startTime", BAD_CAST startTime); + if (endTime != NULL) + xmlNewChild(p_node, ns, BAD_CAST "endTime", BAD_CAST endTime); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newDerivedFromRecord(RecordPtr p_record, IDREF entity_effect, IDREF entity_cause) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, "dependencies", "wasDerivedFrom", "wdf"); + xmlNsPtr ns = getNs(p_node, NULL); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "effect", NULL), ns, BAD_CAST "ref", BAD_CAST entity_effect); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "cause", NULL), ns, BAD_CAST "ref", BAD_CAST entity_cause); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newInformedByRecord(RecordPtr p_record, IDREF activity_effect, IDREF activity_cause, const char* time) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, "dependencies", "wasInformedBy", "wib"); + xmlNsPtr ns = getNs(p_node, NULL); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "effect", NULL), ns, BAD_CAST "ref", BAD_CAST activity_effect); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "cause", NULL), ns, BAD_CAST "ref", BAD_CAST activity_cause); + if (time != NULL) + xmlNewChild(p_node, ns, BAD_CAST "time", BAD_CAST time); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newAlternateOfRecord(RecordPtr p_record, IDREF entity1, IDREF entity2) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, "dependencies", "alternateOf", "ao"); + xmlNsPtr ns = getNs(p_node, NULL); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "entity1", NULL), ns, BAD_CAST "ref", BAD_CAST entity1); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "entity2", NULL), ns, BAD_CAST "ref", BAD_CAST entity2); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newSpecializationOfRecord(RecordPtr p_record, IDREF entity1, IDREF entity2) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, "dependencies", "specializationOf", "so"); + xmlNsPtr ns = getNs(p_node, NULL); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "entity1", NULL), ns, BAD_CAST "ref", BAD_CAST entity1); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "entity2", NULL), ns, BAD_CAST "ref", BAD_CAST entity2); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +IDREF newHasAnnotationRecord(RecordPtr p_record, IDREF thing, IDREF note) +{ + xmlNodePtr p_node = add_element((xmlNodePtr)p_record, "dependencies", "hasAnnotation", "ha"); + xmlNsPtr ns = getNs(p_node, NULL); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "thing", NULL), ns, BAD_CAST "ref", BAD_CAST thing); + xmlNewNsProp(xmlNewChild(p_node, ns, BAD_CAST "note", NULL), ns, BAD_CAST "ref", BAD_CAST note); + IDREF id = (IDREF)xmlGetProp(p_node, BAD_CAST "id"); + assert(id); + return(id); +} + +/* Record manipulation routines */ + +/* +Add key value elements with given id +*/ +int addAttribute(RecordPtr p_record, IDREF id, const char * prefix, const char* type, const char* localName, const char* value) +{ + xmlChar xpathExpr[128]; + assert(id); + sprintf(xpathExpr, "//*[@prov:id='%s']", id); + XPathQueryPtr p_xpquery = query_xpath(((xmlNodePtr)p_record)->doc, + xpathExpr); + int size = (p_xpquery->xpathObj->nodesetval) ? p_xpquery->xpathObj->nodesetval->nodeNr : 0; + //fprintf(stdout, "xpath[%s]:numnodes[%d]\n", xpathExpr, size); + if (size==0){ + fprintf(stderr, "Element with id=%s not found.\n", id); + /* Cleanup */ + free_xpquery(p_xpquery); + return(1); + } + //fprintf(stdout, "=====%s=====\n", id); + if (size>1){ + fprintf(stderr, "addAttribute: Multiple elements found [n=%d]. Doc invalid. [id=%s]\n", size, id); + /* Cleanup */ + free_xpquery(p_xpquery); + return(1); + } + xmlNodePtr p_node = p_xpquery->xpathObj->nodesetval->nodeTab[0]; + assert(localName); + assert(value); + xmlNsPtr ns = getNs(p_node, prefix); + if (ns == NULL) { + free_xpquery(p_xpquery); + return(1); + } + xmlNodePtr p_child = xmlNewChild(p_node, ns, BAD_CAST localName, BAD_CAST value); + if (type != NULL) + xmlNewProp(p_child, "xsi:type", type); + //fprintf(stderr,"%s=%s\n", localName, value); + //xmlSaveFormatFileEnc("-", ((xmlNodePtr)p_record)->doc, "UTF-8", 1); + /* Cleanup */ + free_xpquery(p_xpquery); + return(0); +} + +int changeID(RecordPtr p_record, IDREF id, const char* new_id) +{ + xmlChar xpathExpr[128]; + sprintf(xpathExpr, "//*[@prov:id='%s']", id); + XPathQueryPtr p_xpquery = query_xpath(((xmlNodePtr)p_record)->doc, + xpathExpr); + int size = (p_xpquery->xpathObj->nodesetval) ? p_xpquery->xpathObj->nodesetval->nodeNr : 0; + //fprintf(stdout, "xpath[%s]:numnodes[%d]\n", xpathExpr, size); + if (size==0){ + fprintf(stderr, "Element with id=%s not found.\n", id); + /* Cleanup */ + free_xpquery(p_xpquery); + return(1); + } + if (size>1){ + fprintf(stderr, "changeID: Multiple elements found. Doc invalid.\n"); + /* Cleanup */ + free_xpquery(p_xpquery); + return(1); + } + xmlNodePtr p_node = p_xpquery->xpathObj->nodesetval->nodeTab[0]; + assert(new_id); + xmlSetProp(p_node, BAD_CAST "id", BAD_CAST new_id); + + /* Cleanup */ + free_xpquery(p_xpquery); + return(0); +} + +static void change_refs(xmlNodePtr p_node, char *prefix) +{ + xmlNodePtr p_curnode = NULL; + char newid[255]; + xmlChar* p_prop; + + for (p_curnode = p_node; p_curnode; p_curnode = p_curnode->next) { + if (p_curnode->type == XML_ELEMENT_NODE) { + if (xmlHasProp(p_curnode, BAD_CAST "id") != NULL){ + p_prop = xmlGetProp(p_curnode, BAD_CAST"id"); + sprintf(newid, "%s_%s", prefix, p_prop); + xmlSetProp(p_curnode, BAD_CAST "prov:id", BAD_CAST newid); + xmlFree(p_prop); + } + if (xmlHasProp(p_curnode, BAD_CAST "ref") != NULL){ + p_prop = xmlGetProp(p_curnode, BAD_CAST"ref"); + sprintf(newid, "%s_%s", prefix, p_prop); + xmlSetProp(p_curnode, BAD_CAST "prov:ref", BAD_CAST newid); + xmlFree(p_prop); + } + } + change_refs(p_curnode->children, prefix); + } +} + +int addProvAsAccount(RecordPtr p_record, const ProvPtr p_prov, const char *prefix) +{ + // create new account record + RecordPtr p_newrecord = newAccount(p_record, NULL); + xmlNodePtr p_account = ((xmlNodePtr)p_newrecord)->parent; + xmlUnlinkNode((xmlNodePtr)p_newrecord); + xmlFreeNode((xmlNodePtr)p_newrecord); + + char new_prefix[255]; + if (prefix != NULL) + sprintf(new_prefix, "%s", prefix); + else{ + xmlChar* id = xmlGetProp(p_account, "id"); + sprintf(new_prefix, "%s", id); + xmlFree(id); + } + //fprintf(stdout, "new prefix: %s\n", new_prefix); + + //copy contents of provenance record + PrivateProvPtr p_priv = (PrivateProvPtr)(p_prov->private); + XPathQueryPtr p_xpquery = query_xpath(p_priv->doc, "/prov:container/prov:records"); + int size = (p_xpquery->xpathObj->nodesetval) ? p_xpquery->xpathObj->nodesetval->nodeNr : 0; + //fprintf(stdout, "xpath[%s]:numnodes[%d]\n", xpathExpr, size); + if (size==0){ + fprintf(stderr, "Provenance contains no records element.\n"); + // Cleanup + xmlUnlinkNode(p_account); + xmlFreeNode(p_account); + free_xpquery(p_xpquery); + return(1); + } + p_newrecord = (RecordPtr)xmlCopyNode(p_xpquery->xpathObj->nodesetval->nodeTab[0], 1); + xmlAddChild(p_account, (xmlNodePtr)p_newrecord); + + change_refs((xmlNodePtr)p_newrecord, new_prefix); + + /* Cleanup */ + free_xpquery(p_xpquery); + return(0); + +} + +int freeID(IDREF id) +{ + xmlFree((xmlChar*)id); + id = NULL; + return(0); +} +#endif diff --git a/src/testneuroprov.c b/src/testneuroprov.c index d8fc71f..b3c3776 100644 --- a/src/testneuroprov.c +++ b/src/testneuroprov.c @@ -5,7 +5,7 @@ // Created by Satrajit Ghosh on 11/25/11. // Copyright (c) 2011 TankThink Labs LLC. All rights reserved. // - +#define _GNU_SOURCE #include #include #include diff --git a/src/testneuroprov.c~ b/src/testneuroprov.c~ new file mode 100644 index 0000000..d8fc71f --- /dev/null +++ b/src/testneuroprov.c~ @@ -0,0 +1,83 @@ +// Test routine for provenance library +// Requires: +// libprov +// +// Created by Satrajit Ghosh on 11/25/11. +// Copyright (c) 2011 TankThink Labs LLC. All rights reserved. +// + +#include +#include +#include + +#include "neuroprovenance.h" + +const char* version = "1.0.0"; + + +/* A simple example on using the provenance library +*/ +int +main(int argc, char **argv, char** envp) +{ + ProvObjectPtr p_prov = newProvenanceObject("BET"); + ProcessPtr p_proc; + REFID id, act_id, input_id; + char arg[50]; + int i; + + // Add program information + p_proc = newProcess(p_prov, + "11/30/11 00:13:20.650432 EST", + "11/30/11 00:13:20.650550 EST", + "brain_extraction"); + addCommandLine(p_prov, p_proc, argc, argv); + + addKeyValuePair(p_prov, p_proc, "program", argv[0]); + addKeyValuePair(p_prov, p_proc, "version", version); + + //Add all input parameters. if you use getopt this can be refined further + for(i=1;i #include #include diff --git a/src/testprov.c~ b/src/testprov.c~ new file mode 100644 index 0000000..45656fa --- /dev/null +++ b/src/testprov.c~ @@ -0,0 +1,121 @@ +// Test routine for provenance library +// Requires: +// libprov +// +// Created by Satrajit Ghosh on 11/25/11. +// Copyright (c) 2011 TankThink Labs LLC. All rights reserved. +// + +#include +#include +#include + +#include "provenance.h" + +const char* version = "1.0.0"; + +char * get_cmdline(int argc, char **argv){ + int total_len = 0, i; + for(i = 0; i < argc; i++) + total_len += strlen(argv[i]) + 1; + char * cmdline = (char *)malloc(total_len*sizeof(char)); + char * p_index = cmdline; + for(i = 0; i < argc; i++){ + strcpy(p_index, argv[i]); + if ((i + 1) < argc) strcat(p_index, " "); + p_index += (strlen(argv[i]) + 1); + } + return cmdline; +} + +/* A simple example on using the provenance library +*/ +int +main(int argc, char **argv, char** envp) +{ + ProvPtr p_prov = newProvenanceFactory("1"); + RecordPtr p_record = p_prov->p_record; + IDREF id, act_id, used_id, genby_id; + char arg[50]; + int i; + + addNamespace(p_prov, "https://github.com/INCF/ProvenanceLibrary/wiki/terms", "ni"); + + // Add program information + act_id = newActivity(p_record, NULL, "11/30/11 00:13:20.650432 EST", "11/30/11 00:13:20.650550 EST"); + addAttribute(p_record, act_id, "prov", "xsd:string", "type", "program"); + addAttribute(p_record, act_id, "ni", NULL, "name", argv[0]); + addAttribute(p_record, act_id, "ni", NULL, "version", version); + char * cmdline = get_cmdline(argc, argv); + addAttribute(p_record, act_id, "ni", NULL, "cmdline", cmdline); + free(cmdline); + + //Add all input parameters. if you use getopt this can be refined further + for(i=1;ip_record, p_prov, NULL); + print_provenance(p_prov2, NULL); + print_provenance(p_prov2, "testprov2.xml"); + delProvenanceFactory(p_prov); + delProvenanceFactory(p_prov2); + return(0); +}