File:  [Public] / rpm2html / rdf_api.c
Revision 1.13: download - view: text, annotated - select for diffs
Thu Nov 12 02:35:36 1998 UTC (25 years, 6 months ago) by daniel
Branches: MAIN
CVS tags: HEAD
Upgraded to the new XML code and fixed some memory leaks added, Daniel

/*
 * rdf_api.h : implementation of methods to read and write RDF schemas
 *
 * See Copyright for the status of this software.
 *
 * $Id: rdf_api.c,v 1.13 1998/11/12 02:35:36 daniel Exp $
 */

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include "rpmdata.h"
#include "rdf_api.h"

/*
 * Reading an RDF schema stored in a file, this just build the
 * document tree, and check that it's an RDF:RDf element.
 */
rdfSchema rdfRead(const char *filename) {
    xmlDocPtr res;
   
    res = xmlParseFile(filename);
    if (res == NULL) return(NULL);
    if (res->root == NULL) {
	fprintf(stderr, "rdfRead %s: tree is empty\n", filename);
        xmlFreeDoc(res);
	return(NULL);
    }
    if (strcmp(res->root->name, "RDF")) {
	fprintf(stderr, "rdfRead %s: not an RDF file\n", filename);
        xmlFreeDoc(res);
	return(NULL);
    }
    return((rdfSchema) res);
}

/*
 * Writing an RDF schema to a file, this just save the
 * document tree as an XML document.
 */
void rdfWrite(rdfSchema rdf, const char *filename) {
    FILE *output;
    
    output = fopen(filename, "w");
    if (output == NULL) {
        fprintf(stderr, "rdfWrite : couldn't save to file %s: %s\n",
	        filename, strerror(errno));
        return;
    }
    xmlDocDump(output, (xmlDocPtr) rdf);
    fclose(output);
}

/*
 * Writing an RDF schema to a memory buffer, this just save the
 * document tree as an XML document.
 */
void rdfWriteMemory(rdfSchema rdf, char **buffer, int *size) {
    xmlDocDumpMemory((xmlDocPtr) rdf, (CHAR **) buffer, size);
}

/*
 * Creating an RDF schema tree in memory.
 */
rdfSchema rdfNewSchema() {
    xmlDocPtr res;
    xmlNsPtr rdf;
   
    res = xmlNewDoc("1.0");
    rdf = xmlNewGlobalNs(res, "http://www.w3.org/TR/WD-rdf-syntax#", "RDF");
    res->root = xmlNewDocNode(res, rdf, "RDF", NULL);

    return((rdfSchema) res);
}

/*
 * Adding a new namespace to an RDF schema.
 */
rdfNamespace rdfNewNamespace(rdfSchema rdf, const char *url,
                             const char *namespace) {
    xmlNsPtr ns;
   
    ns = xmlNewGlobalNs(rdf, url, namespace);

    return((rdfNamespace) ns);
}

/*
 * Get a namespace associated to an RDF schema.
 * Should be stored either as an old NS on the document or as local
 * namespace stored on the root element.
 */
rdfNamespace rdfGetNamespace(rdfSchema rdf, const char *href) {
    return((rdfNamespace) xmlSearchNsByHref(rdf, rdf->root, href));
}

/*
 * Get the RDF namespace associated to an RDF schema.
 */
rdfNamespace rdfGetRdfNamespace(rdfSchema rdf) {
    rdfNamespace ns;
   
    ns = rdfGetNamespace(rdf, "http://www.w3.org/TR/WD-rdf-syntax#");
    if (ns != NULL)
        return((rdfNamespace) ns);
    /* create it ! */
    return(rdfNewNamespace(rdf, "http://www.w3.org/TR/WD-rdf-syntax#", "RDF"));
}


/*
 * Destroying an RDF schema in memory.
 */
void rdfDestroySchema(rdfSchema rdf) {
    xmlFreeDoc((xmlDocPtr) rdf);
}

/**
 ** An RDF schema is a collection of RDF descriptions.
 ** The following are used to browse the list.
 **/

/* First item */
rdfDescription rdfFirstDescription(rdfSchema schema) {
    xmlNodePtr tree;
    rdfNamespace rdfNs;

    rdfNs = rdfGetRdfNamespace(schema);
    tree = schema->root;
    if (tree == NULL) return(NULL);
    tree = tree->childs;
    while (tree != NULL) {
        if ((tree->ns == rdfNs) && (!strcmp(tree->name, "Description")))
	    return(tree);
	tree = tree->next;
    }
    return(NULL);
}

/* Iteration : next item */
rdfDescription rdfNextDescription(rdfDescription desc) {
    rdfNamespace rdfNs;

    if (desc == NULL) return(NULL);
    rdfNs = desc->ns;
    desc = desc->next;
    while (desc != NULL) {
        if ((desc->ns == rdfNs) && (!strcmp(desc->name, "Description")))
	    return(desc);
	desc = desc->next;
    }
    return(NULL);
}

/*
 * Add an RDF description to an RDF schema, it just link it
 * the top level node of the description and requires further
 * action to fill it with the pertinent informations.
 */
rdfDescription rdfAddDescription(rdfSchema schema, const char *id,
                                 const char *about) {
    xmlNodePtr tree;

    tree = xmlNewChild(schema->root, NULL, "Description", NULL);
    if (id != NULL) {
        xmlNewProp(tree, "ID", id);
    }
    if (about != NULL) {
        if (oldXMLWDcompatibility) {
	    rdfNamespace ns;
	    char *buf;

	    ns = rdfGetRdfNamespace(schema);
	    buf = (char *) malloc(strlen(ns->prefix) + 10);
	    sprintf(buf, "%s:HREF", ns->prefix);
	    xmlNewProp(tree, buf, about); 
	    free(buf);
        } else {
	    xmlNewProp(tree, "about", about); 
	}
    }
    return((rdfDescription) tree);
}

/*
 * Read the attributes of a Description node.
 */
char *rdfGetDescriptionId(rdfSchema schema, rdfDescription desc) {
    char *res;

    res = (char *) xmlGetProp(desc, "ID");
    return(res);
}

char *rdfGetDescriptionAbout(rdfSchema schema, rdfDescription desc) {
    char *res;
    rdfNamespace ns;
    char *buf;

    res = (char *) xmlGetProp(desc, "about");
    if (res != NULL) return(res);

    /*
     * Maintain compatibility with old "RDF:HREF"
     */
    res = (char *) xmlGetProp(desc, "HREF");
    if (res != NULL) return(res);
    ns = rdfGetRdfNamespace(schema);
    buf = (char *) malloc(strlen(ns->prefix) + 10);
    sprintf(buf, "%s:HREF", ns->prefix);
    res = (char *) xmlGetProp(desc, buf);
    free(buf);
    return(res);
}

/*
 * Read any resource associated to an element.
 */
char *rdfGetElementResource(rdfSchema schema, rdfElement elem) {
    char *res;

    res = (char *) xmlGetProp(elem, "resource");
    return(res);
}

void rdfSetElementResource(rdfSchema schema, rdfElement elem, const char *URI) {
    xmlSetProp(elem, "resource", URI);
}

/**
 ** Routines to read/write final values.
 **/

/*
 * browse the various property pertaining to this description,
 * and return the value which can be either a text (RDF_LEAF)
 * or an rdfElement (RDF_BAG, RDF_SEQ, RDF_ALT, RDF_DESC).
 * Having both reflect an invalid RDF document (while valid from
 * XML point of view).
 * This routine tries to ajust in case a request is done with or
 * or without a ns present or not on the node, and if the names
 * matches.
 *  e.g. a request for Name may return BIB:Name
 *    or a request for BIB:Name may return Name
 * if the exact match is not found.
 * 
 * Return -1 if not found
 *         0 if a single value is found
 *   RDF_xxx if a node is found.
 */
int rdfGetValue(rdfDescription desc, const char *property, 
                rdfNamespace ns, char **value, rdfElement *elem){
    rdfElement list = desc->childs;
    rdfElement best = NULL;
    int approx = 0;

    if (value != NULL) *value = NULL;
    if (elem != NULL) *elem = NULL;

    while (list != NULL) {
        if ((list->ns == ns) && (!strcmp(list->name, property))) {
	    /*
	     * Found a perfect match, return it !
	     */
	    if ((list->content != NULL) && (list->childs != NULL)) {
	        fprintf(stderr, 
		    "RDF Schema invalid, node %s has content and child(s)\n",
			list->name);
	    }
	    if (value != NULL) {
		if (list->content != NULL)
	            *value = xmlStrdup(list->content);
                else
		    *value = xmlNodeListGetString(desc->doc, list->childs, 1);
            }
	    if (elem != NULL) *elem = list->childs;
	    if (!strcmp(list->name, "Bag")) return(RDF_BAG);
	    if (!strcmp(list->name, "Seq")) return(RDF_SEQ);
	    if (!strcmp(list->name, "Alt")) return(RDF_ALT);
	    if (!strcmp(list->name, "Description")) return(RDF_DESC);
	    return(0);
	}
	else if (((list->ns == NULL) || (ns == NULL)) &&
	         (!strcmp(list->name, property))) {
	    /*
	     * If more than one "best" match exists, ignore them !
	     */
	    if (approx) {
	        best = NULL;
	    } else {
                best = list;
		approx = 1;
            }
        }
	list = list->next;
    }
    if (best != NULL) {
	if ((best->content != NULL) && (best->childs != NULL)) {
	    fprintf(stderr, 
		    "RDF Schema invalid, node %s has content and child(s)\n",
		    best->name);
	}
	if (value != NULL) {
	    if (best->content != NULL)
		*value = xmlStrdup(best->content);
	    else
		*value = xmlNodeListGetString(desc->doc, best->childs, 1);
	}
	if (elem != NULL) *elem = best->childs;
	if (!strcmp(best->name, "Bag")) return(RDF_BAG);
	if (!strcmp(best->name, "Seq")) return(RDF_SEQ);
	if (!strcmp(best->name, "Alt")) return(RDF_ALT);
	if (!strcmp(best->name, "Description")) return(RDF_DESC);
	return(0);
    }
    return(-1);
}

/*
 * browse the various property pertaining to this description,
 * update the value if found on a text mode, otherwise append
 * a new property at the end of the list.
 * Note that contrary to GetValue, the check is absolute !
 */
void rdfSetValue(rdfDescription desc, const char *property,
                 rdfNamespace ns, const char *value) {
    rdfElement list = desc->childs;

    /*
     * Search the property.
     */
    while (list != NULL) {
        if ((list->ns == ns) && (!strcmp(list->name, property))) {
	    /*
	     * Property was found, update it !
	     */
	    if ((list->content != NULL) && (list->childs != NULL)) {
	        fprintf(stderr, 
		    "RDF Schema invalid, node %s has content and child(s)\n",
		        list->name);
	    }
	    if (list->content != NULL) free(list->content);
	    if (list->childs != NULL) xmlFreeNodeList(list->childs);
	    list->childs = xmlStringGetNodeList(desc->doc,
	                       xmlEncodeEntities(desc->doc, value));
	    return;
	}
	list = list->next;
    }

    /*
     * Create a new property.
     */
    xmlNewChild(desc, ns, property, xmlEncodeEntities(desc->doc, value));
}

/*
 * browse the various property pertaining to this description,
 * and if found remove and destroy the subtree.
 */
void rdfRemoveProperty(rdfDescription desc, const char *property,
                       rdfNamespace ns) {
    rdfElement list = desc->childs;
    rdfElement prev = NULL;

    /*
     * Search the property.
     */
    while (list != NULL) {
        if ((list->ns == ns) && (!strcmp(list->name, property))) {
	    /*
	     * Property was found, delete it !
	     */
	    if (prev == NULL)
	        desc->childs = list->next;
	    else
	        prev->next = list->next;
	    list->next = NULL;
	    
	    xmlFreeNode(list);
	    return;
	}
	prev = list;
	list = list->next;
    }
}

/**
 ** Routines to read/write bags
 **/

/*
 * Create a new Bag.
 */
rdfBag rdfBagCreate(rdfSchema schema, rdfDescription desc,
                    const char *property, rdfNamespace ns) {
    rdfElement cur;
    rdfNamespace rdf;

    rdf = rdfGetRdfNamespace(schema);
    cur = xmlNewChild(desc, ns, property, NULL);
    cur = xmlNewChild(cur, rdf, "Bag", NULL);
    return(cur);
}

/*
 * Add an element to a bag.
 */
rdfElement rdfBagAddValue(rdfBag bag, const char *property,
                    rdfNamespace ns, const char *value, rdfElement elem) {
    rdfElement cur;

    if (value != NULL)
        cur = xmlNewChild(bag, ns, property, (char *) value);
    else {
        cur = xmlNewDocNode(bag->doc, ns, property, NULL);
	if (elem != NULL)
	    xmlAddChild(cur, elem);
	xmlAddChild(bag, cur);
    }
    return(cur);
}

/**
 ** Routines to walk bags and sequences.
 **/
rdfElement rdfFirstChild(rdfElement bag) {
    return(bag->childs);
}
rdfElement rdfNextElem(rdfElement desc) {
    return(desc->next);
}

/**
 ** Direct access to Element values.
 **/
int rdfElemGetType(rdfElement elem) {
    if (!strcmp(elem->name, "Bag")) return(RDF_BAG);
    if (!strcmp(elem->name, "Seq")) return(RDF_SEQ);
    if (!strcmp(elem->name, "Alt")) return(RDF_ALT);
    if (!strcmp(elem->name, "Description")) return(RDF_DESC);
    return(0);
}

char *rdfElemGetValue(rdfElement elem) {
    if (elem->content != NULL)
	return(strdup(elem->content));
    else
        return(xmlNodeListGetString(elem->doc, elem->childs, 1));
}
const char *rdfElemGetPropertyName(rdfElement elem) {
    return(strdup(elem->name));
}
rdfNamespace rdfElemGetNamespace(rdfElement elem) {
    return(elem->ns);
}
void rdfElemSetValue(rdfElement elem, const char *value) {
    if (elem->childs != NULL) {
        fprintf(stderr, "rdfElemSetValue : elem %s has childs\n", elem->name);
	return;
    }
    if (elem->content != NULL) free(elem->content);
    if (elem->childs != NULL) xmlFreeNodeList(elem->childs);
    elem->childs = xmlStringGetNodeList(elem->doc, 
                                  xmlEncodeEntities(elem->doc, value));
}

/************************************************************************
 *									*
 *				Debug					*
 *									*
 ************************************************************************/

#ifdef DEBUG_RDF
int main(void) {
    rdfSchema rdf;
    rdfNamespace rpm;
    rdfDescription desc;
    rdfBag bag;

    char *value;

    /*
     * build a fake RDF document
     */
    rdf = rdfNewSchema();
    rpm = rdfNewNamespace(rdf, "http://www.rpm.org/", "RPM");
    desc = rdfAddDescription(rdf, NULL,
           "ftp://ftp.redhat.com/pub/contrib/i386/rpm2html-0.90-1.i386.rpm");
    rdfSetValue(desc, "Description", rpm, "<shade/stick & close/destroy>");
    rdfSetValue(desc, "Name", rpm, "rpm2html");
    rdfSetValue(desc, "Version", rpm, "0.90");
    rdfSetValue(desc, "Release", rpm, "1");
    bag = rdfBagCreate(rdf, desc, "Provides", rpm);
    rdfBagAddValue(bag, "Resource", rpm, "rpm2html", NULL);
    bag = rdfBagCreate(rdf, desc, "Requires", rpm);
    rdfBagAddValue(bag, "Resource", rpm, "libz.so.1", NULL);
    rdfBagAddValue(bag, "Resource", rpm, "libdb.so.2", NULL);
    rdfBagAddValue(bag, "Resource", rpm, "libc.so.6", NULL);
    rdfBagAddValue(bag, "Resource", rpm, "ld-linux.so.2", NULL);

    /*
     * Save it.
     */
    rdfWrite(rdf, "test.rdf");
    rdfDestroySchema(rdf);

    /*
     * Read it.
     */
    rdf = rdfRead("test.rdf");

    /*
     * print it.
     */
    rpm = rdfGetNamespace(rdf, "http://www.rpm.org/");
    if (rpm == NULL) fprintf(stderr, "RPM namespace not found !\n");
    desc = rdfFirstDescription(rdf);
    rdfGetValue(desc, "Name", rpm, &value, NULL);
    printf("Name : %s\n", value);
    free(value);
    rdfGetValue(desc, "Name", NULL, &value, NULL);
    printf("Name : %s\n", value);
    free(value);
    printf("\n---\n");
    xmlDocDump(stdout, rdf);

    /*
     * free it.
     */
    rdfDestroySchema(rdf);
    return(0);
}
#endif

Webmaster