Annotation of rpm2html/rdf_api.c, revision 1.19
1.1 veillard 1: /*
2: * rdf_api.h : implementation of methods to read and write RDF schemas
1.6 veillard 3: *
4: * See Copyright for the status of this software.
5: *
1.19 ! daniel 6: * $Id: rdf_api.c,v 1.18 1999/08/08 18:29:42 daniel Exp $
1.1 veillard 7: */
8:
1.17 daniel 9: #include "config.h"
1.1 veillard 10: #include <stdio.h>
11: #include <string.h>
12: #include <errno.h>
1.15 daniel 13: #include <memory.h>
1.1 veillard 14: #include "rpmdata.h"
15: #include "rdf_api.h"
1.16 daniel 16: #include "rpm2html.h"
1.1 veillard 17:
18: /*
19: * Reading an RDF schema stored in a file, this just build the
20: * document tree, and check that it's an RDF:RDf element.
21: */
22: rdfSchema rdfRead(const char *filename) {
23: xmlDocPtr res;
24:
25: res = xmlParseFile(filename);
26: if (res == NULL) return(NULL);
27: if (res->root == NULL) {
28: fprintf(stderr, "rdfRead %s: tree is empty\n", filename);
29: xmlFreeDoc(res);
30: return(NULL);
31: }
32: if (strcmp(res->root->name, "RDF")) {
33: fprintf(stderr, "rdfRead %s: not an RDF file\n", filename);
34: xmlFreeDoc(res);
35: return(NULL);
36: }
37: return((rdfSchema) res);
38: }
39:
40: /*
41: * Writing an RDF schema to a file, this just save the
42: * document tree as an XML document.
43: */
44: void rdfWrite(rdfSchema rdf, const char *filename) {
45: FILE *output;
46:
47: output = fopen(filename, "w");
48: if (output == NULL) {
49: fprintf(stderr, "rdfWrite : couldn't save to file %s: %s\n",
50: filename, strerror(errno));
51: return;
52: }
53: xmlDocDump(output, (xmlDocPtr) rdf);
54: fclose(output);
55: }
56:
57: /*
1.4 veillard 58: * Writing an RDF schema to a memory buffer, this just save the
59: * document tree as an XML document.
60: */
61: void rdfWriteMemory(rdfSchema rdf, char **buffer, int *size) {
62: xmlDocDumpMemory((xmlDocPtr) rdf, (CHAR **) buffer, size);
63: }
64:
65: /*
1.2 veillard 66: * Creating an RDF schema tree in memory.
1.1 veillard 67: */
68: rdfSchema rdfNewSchema() {
69: xmlDocPtr res;
1.8 daniel 70: xmlNsPtr rdf;
1.1 veillard 71:
72: res = xmlNewDoc("1.0");
1.19 ! daniel 73: res->root = xmlNewDocNode(res, NULL, "RDF", NULL);
! 74: rdf = xmlNewNs(res->root, "http://www.w3.org/TR/WD-rdf-syntax#", "RDF");
! 75: xmlSetNs(res->root, rdf);
1.1 veillard 76:
77: return((rdfSchema) res);
78: }
79:
80: /*
81: * Adding a new namespace to an RDF schema.
82: */
1.8 daniel 83: rdfNamespace rdfNewNamespace(rdfSchema rdf, const char *url,
84: const char *namespace) {
1.19 ! daniel 85: xmlNodePtr root;
! 86: xmlNsPtr ns = NULL;
1.1 veillard 87:
1.19 ! daniel 88: root = xmlDocGetRootElement(rdf);
! 89: if (root != NULL)
! 90: ns = xmlNewNs(root, url, namespace);
1.1 veillard 91:
1.8 daniel 92: return((rdfNamespace) ns);
1.1 veillard 93: }
94:
95: /*
96: * Get a namespace associated to an RDF schema.
1.9 daniel 97: * Should be stored either as an old NS on the document or as local
98: * namespace stored on the root element.
1.1 veillard 99: */
100: rdfNamespace rdfGetNamespace(rdfSchema rdf, const char *href) {
1.9 daniel 101: return((rdfNamespace) xmlSearchNsByHref(rdf, rdf->root, href));
1.1 veillard 102: }
103:
104: /*
105: * Get the RDF namespace associated to an RDF schema.
106: */
107: rdfNamespace rdfGetRdfNamespace(rdfSchema rdf) {
1.9 daniel 108: rdfNamespace ns;
1.1 veillard 109:
1.9 daniel 110: ns = rdfGetNamespace(rdf, "http://www.w3.org/TR/WD-rdf-syntax#");
111: if (ns != NULL)
112: return((rdfNamespace) ns);
1.1 veillard 113: /* create it ! */
114: return(rdfNewNamespace(rdf, "http://www.w3.org/TR/WD-rdf-syntax#", "RDF"));
115: }
116:
117:
118: /*
119: * Destroying an RDF schema in memory.
120: */
121: void rdfDestroySchema(rdfSchema rdf) {
122: xmlFreeDoc((xmlDocPtr) rdf);
123: }
124:
125: /**
126: ** An RDF schema is a collection of RDF descriptions.
127: ** The following are used to browse the list.
128: **/
129:
130: /* First item */
131: rdfDescription rdfFirstDescription(rdfSchema schema) {
132: xmlNodePtr tree;
133: rdfNamespace rdfNs;
134:
135: rdfNs = rdfGetRdfNamespace(schema);
136: tree = schema->root;
137: if (tree == NULL) return(NULL);
138: tree = tree->childs;
139: while (tree != NULL) {
1.8 daniel 140: if ((tree->ns == rdfNs) && (!strcmp(tree->name, "Description")))
1.1 veillard 141: return(tree);
142: tree = tree->next;
143: }
144: return(NULL);
145: }
146:
147: /* Iteration : next item */
148: rdfDescription rdfNextDescription(rdfDescription desc) {
149: rdfNamespace rdfNs;
150:
151: if (desc == NULL) return(NULL);
1.8 daniel 152: rdfNs = desc->ns;
1.1 veillard 153: desc = desc->next;
154: while (desc != NULL) {
1.8 daniel 155: if ((desc->ns == rdfNs) && (!strcmp(desc->name, "Description")))
1.1 veillard 156: return(desc);
157: desc = desc->next;
158: }
159: return(NULL);
160: }
161:
162: /*
163: * Add an RDF description to an RDF schema, it just link it
164: * the top level node of the description and requires further
165: * action to fill it with the pertinent informations.
166: */
167: rdfDescription rdfAddDescription(rdfSchema schema, const char *id,
1.7 veillard 168: const char *about) {
1.1 veillard 169: xmlNodePtr tree;
170:
1.10 daniel 171: tree = xmlNewChild(schema->root, NULL, "Description", NULL);
1.1 veillard 172: if (id != NULL) {
173: xmlNewProp(tree, "ID", id);
174: }
1.7 veillard 175: if (about != NULL) {
176: if (oldXMLWDcompatibility) {
1.8 daniel 177: rdfNamespace ns;
1.7 veillard 178: char *buf;
179:
1.8 daniel 180: ns = rdfGetRdfNamespace(schema);
1.15 daniel 181: buf = (char *) debugMalloc(strlen(ns->prefix) + 10);
1.8 daniel 182: sprintf(buf, "%s:HREF", ns->prefix);
1.7 veillard 183: xmlNewProp(tree, buf, about);
1.15 daniel 184: debugFree(buf);
1.7 veillard 185: } else {
186: xmlNewProp(tree, "about", about);
187: }
1.1 veillard 188: }
189: return((rdfDescription) tree);
190: }
191:
1.5 veillard 192: /*
193: * Read the attributes of a Description node.
194: */
195: char *rdfGetDescriptionId(rdfSchema schema, rdfDescription desc) {
196: char *res;
197:
198: res = (char *) xmlGetProp(desc, "ID");
199: return(res);
200: }
201:
1.7 veillard 202: char *rdfGetDescriptionAbout(rdfSchema schema, rdfDescription desc) {
1.5 veillard 203: char *res;
1.8 daniel 204: rdfNamespace ns;
1.5 veillard 205: char *buf;
206:
1.7 veillard 207: res = (char *) xmlGetProp(desc, "about");
208: if (res != NULL) return(res);
209:
210: /*
211: * Maintain compatibility with old "RDF:HREF"
212: */
1.8 daniel 213: res = (char *) xmlGetProp(desc, "HREF");
214: if (res != NULL) return(res);
215: ns = rdfGetRdfNamespace(schema);
1.15 daniel 216: buf = (char *) debugMalloc(strlen(ns->prefix) + 10);
1.8 daniel 217: sprintf(buf, "%s:HREF", ns->prefix);
1.5 veillard 218: res = (char *) xmlGetProp(desc, buf);
1.15 daniel 219: debugFree(buf);
1.5 veillard 220: return(res);
221: }
222:
1.7 veillard 223: /*
224: * Read any resource associated to an element.
225: */
226: char *rdfGetElementResource(rdfSchema schema, rdfElement elem) {
227: char *res;
228:
229: res = (char *) xmlGetProp(elem, "resource");
230: return(res);
231: }
232:
233: void rdfSetElementResource(rdfSchema schema, rdfElement elem, const char *URI) {
234: xmlSetProp(elem, "resource", URI);
235: }
236:
1.1 veillard 237: /**
238: ** Routines to read/write final values.
239: **/
240:
241: /*
242: * browse the various property pertaining to this description,
243: * and return the value which can be either a text (RDF_LEAF)
244: * or an rdfElement (RDF_BAG, RDF_SEQ, RDF_ALT, RDF_DESC).
245: * Having both reflect an invalid RDF document (while valid from
246: * XML point of view).
247: * This routine tries to ajust in case a request is done with or
248: * or without a ns present or not on the node, and if the names
249: * matches.
250: * e.g. a request for Name may return BIB:Name
251: * or a request for BIB:Name may return Name
252: * if the exact match is not found.
253: *
254: * Return -1 if not found
255: * 0 if a single value is found
256: * RDF_xxx if a node is found.
257: */
258: int rdfGetValue(rdfDescription desc, const char *property,
259: rdfNamespace ns, char **value, rdfElement *elem){
260: rdfElement list = desc->childs;
261: rdfElement best = NULL;
262: int approx = 0;
263:
264: if (value != NULL) *value = NULL;
265: if (elem != NULL) *elem = NULL;
266:
267: while (list != NULL) {
1.8 daniel 268: if ((list->ns == ns) && (!strcmp(list->name, property))) {
1.1 veillard 269: /*
270: * Found a perfect match, return it !
271: */
272: if ((list->content != NULL) && (list->childs != NULL)) {
273: fprintf(stderr,
274: "RDF Schema invalid, node %s has content and child(s)\n",
275: list->name);
276: }
1.11 daniel 277: if (value != NULL) {
278: if (list->content != NULL)
279: *value = xmlStrdup(list->content);
280: else
281: *value = xmlNodeListGetString(desc->doc, list->childs, 1);
282: }
1.1 veillard 283: if (elem != NULL) *elem = list->childs;
284: if (!strcmp(list->name, "Bag")) return(RDF_BAG);
285: if (!strcmp(list->name, "Seq")) return(RDF_SEQ);
286: if (!strcmp(list->name, "Alt")) return(RDF_ALT);
287: if (!strcmp(list->name, "Description")) return(RDF_DESC);
288: return(0);
289: }
1.8 daniel 290: else if (((list->ns == NULL) || (ns == NULL)) &&
1.1 veillard 291: (!strcmp(list->name, property))) {
292: /*
293: * If more than one "best" match exists, ignore them !
294: */
295: if (approx) {
296: best = NULL;
297: } else {
298: best = list;
299: approx = 1;
300: }
301: }
302: list = list->next;
303: }
304: if (best != NULL) {
305: if ((best->content != NULL) && (best->childs != NULL)) {
306: fprintf(stderr,
307: "RDF Schema invalid, node %s has content and child(s)\n",
308: best->name);
309: }
1.11 daniel 310: if (value != NULL) {
311: if (best->content != NULL)
312: *value = xmlStrdup(best->content);
313: else
314: *value = xmlNodeListGetString(desc->doc, best->childs, 1);
315: }
1.1 veillard 316: if (elem != NULL) *elem = best->childs;
317: if (!strcmp(best->name, "Bag")) return(RDF_BAG);
318: if (!strcmp(best->name, "Seq")) return(RDF_SEQ);
319: if (!strcmp(best->name, "Alt")) return(RDF_ALT);
320: if (!strcmp(best->name, "Description")) return(RDF_DESC);
321: return(0);
322: }
323: return(-1);
324: }
325:
326: /*
327: * browse the various property pertaining to this description,
328: * update the value if found on a text mode, otherwise append
329: * a new property at the end of the list.
330: * Note that contrary to GetValue, the check is absolute !
331: */
332: void rdfSetValue(rdfDescription desc, const char *property,
333: rdfNamespace ns, const char *value) {
334: rdfElement list = desc->childs;
1.18 daniel 335: char *str;
1.1 veillard 336:
337: /*
338: * Search the property.
339: */
340: while (list != NULL) {
1.8 daniel 341: if ((list->ns == ns) && (!strcmp(list->name, property))) {
1.1 veillard 342: /*
343: * Property was found, update it !
344: */
345: if ((list->content != NULL) && (list->childs != NULL)) {
346: fprintf(stderr,
347: "RDF Schema invalid, node %s has content and child(s)\n",
348: list->name);
349: }
1.15 daniel 350: if (list->content != NULL) debugFree(list->content);
1.11 daniel 351: if (list->childs != NULL) xmlFreeNodeList(list->childs);
1.18 daniel 352: str = xmlEncodeEntitiesReentrant(desc->doc, value);
353: list->childs = xmlStringGetNodeList(desc->doc, value);
354: if (str != NULL)
355: free(str);
1.1 veillard 356: return;
357: }
358: list = list->next;
359: }
360:
361: /*
362: * Create a new property.
363: */
1.18 daniel 364: str = xmlEncodeEntitiesReentrant(desc->doc, value);
365: xmlNewChild(desc, ns, property, str);
366: if (str != NULL)
367: free(str);
1.1 veillard 368: }
369:
370: /*
371: * browse the various property pertaining to this description,
372: * and if found remove and destroy the subtree.
373: */
374: void rdfRemoveProperty(rdfDescription desc, const char *property,
375: rdfNamespace ns) {
376: rdfElement list = desc->childs;
377: rdfElement prev = NULL;
378:
379: /*
380: * Search the property.
381: */
382: while (list != NULL) {
1.8 daniel 383: if ((list->ns == ns) && (!strcmp(list->name, property))) {
1.1 veillard 384: /*
385: * Property was found, delete it !
386: */
387: if (prev == NULL)
388: desc->childs = list->next;
389: else
390: prev->next = list->next;
391: list->next = NULL;
392:
393: xmlFreeNode(list);
394: return;
395: }
396: prev = list;
397: list = list->next;
398: }
399: }
400:
401: /**
402: ** Routines to read/write bags
403: **/
404:
405: /*
1.2 veillard 406: * Create a new Bag.
1.1 veillard 407: */
1.2 veillard 408: rdfBag rdfBagCreate(rdfSchema schema, rdfDescription desc,
409: const char *property, rdfNamespace ns) {
410: rdfElement cur;
411: rdfNamespace rdf;
412:
413: rdf = rdfGetRdfNamespace(schema);
1.10 daniel 414: cur = xmlNewChild(desc, ns, property, NULL);
415: cur = xmlNewChild(cur, rdf, "Bag", NULL);
1.2 veillard 416: return(cur);
1.1 veillard 417: }
418:
419: /*
1.2 veillard 420: * Add an element to a bag.
1.1 veillard 421: */
1.2 veillard 422: rdfElement rdfBagAddValue(rdfBag bag, const char *property,
1.4 veillard 423: rdfNamespace ns, const char *value, rdfElement elem) {
1.2 veillard 424: rdfElement cur;
425:
426: if (value != NULL)
1.10 daniel 427: cur = xmlNewChild(bag, ns, property, (char *) value);
1.2 veillard 428: else {
1.11 daniel 429: cur = xmlNewDocNode(bag->doc, ns, property, NULL);
1.7 veillard 430: if (elem != NULL)
431: xmlAddChild(cur, elem);
1.2 veillard 432: xmlAddChild(bag, cur);
433: }
434: return(cur);
1.1 veillard 435: }
436:
437: /**
438: ** Routines to walk bags and sequences.
439: **/
440: rdfElement rdfFirstChild(rdfElement bag) {
441: return(bag->childs);
442: }
443: rdfElement rdfNextElem(rdfElement desc) {
444: return(desc->next);
445: }
446:
447: /**
448: ** Direct access to Element values.
449: **/
450: int rdfElemGetType(rdfElement elem) {
451: if (!strcmp(elem->name, "Bag")) return(RDF_BAG);
452: if (!strcmp(elem->name, "Seq")) return(RDF_SEQ);
453: if (!strcmp(elem->name, "Alt")) return(RDF_ALT);
454: if (!strcmp(elem->name, "Description")) return(RDF_DESC);
455: return(0);
456: }
457:
458: char *rdfElemGetValue(rdfElement elem) {
1.12 daniel 459: if (elem->content != NULL)
1.15 daniel 460: return(debugStrdup(elem->content));
1.12 daniel 461: else
462: return(xmlNodeListGetString(elem->doc, elem->childs, 1));
1.1 veillard 463: }
1.14 daniel 464: char *rdfElemGetPropertyName(rdfElement elem) {
1.15 daniel 465: return(debugStrdup(elem->name));
1.3 veillard 466: }
467: rdfNamespace rdfElemGetNamespace(rdfElement elem) {
1.8 daniel 468: return(elem->ns);
1.1 veillard 469: }
470: void rdfElemSetValue(rdfElement elem, const char *value) {
1.18 daniel 471: char *str;
1.1 veillard 472: if (elem->childs != NULL) {
473: fprintf(stderr, "rdfElemSetValue : elem %s has childs\n", elem->name);
474: return;
475: }
1.15 daniel 476: if (elem->content != NULL) debugFree(elem->content);
1.12 daniel 477: if (elem->childs != NULL) xmlFreeNodeList(elem->childs);
1.18 daniel 478: str = xmlEncodeEntitiesReentrant(elem->doc, value);
479: elem->childs = xmlStringGetNodeList(elem->doc, str);
480: if (str != NULL)
481: free(str);
1.1 veillard 482: }
483:
484: /************************************************************************
485: * *
486: * Debug *
487: * *
488: ************************************************************************/
489:
490: #ifdef DEBUG_RDF
491: int main(void) {
492: rdfSchema rdf;
493: rdfNamespace rpm;
1.2 veillard 494: rdfDescription desc;
495: rdfBag bag;
1.1 veillard 496:
497: char *value;
498:
499: /*
500: * build a fake RDF document
501: */
502: rdf = rdfNewSchema();
503: rpm = rdfNewNamespace(rdf, "http://www.rpm.org/", "RPM");
504: desc = rdfAddDescription(rdf, NULL,
505: "ftp://ftp.redhat.com/pub/contrib/i386/rpm2html-0.90-1.i386.rpm");
1.12 daniel 506: rdfSetValue(desc, "Description", rpm, "<shade/stick & close/destroy>");
1.1 veillard 507: rdfSetValue(desc, "Name", rpm, "rpm2html");
508: rdfSetValue(desc, "Version", rpm, "0.90");
509: rdfSetValue(desc, "Release", rpm, "1");
1.2 veillard 510: bag = rdfBagCreate(rdf, desc, "Provides", rpm);
511: rdfBagAddValue(bag, "Resource", rpm, "rpm2html", NULL);
512: bag = rdfBagCreate(rdf, desc, "Requires", rpm);
513: rdfBagAddValue(bag, "Resource", rpm, "libz.so.1", NULL);
514: rdfBagAddValue(bag, "Resource", rpm, "libdb.so.2", NULL);
515: rdfBagAddValue(bag, "Resource", rpm, "libc.so.6", NULL);
516: rdfBagAddValue(bag, "Resource", rpm, "ld-linux.so.2", NULL);
1.1 veillard 517:
518: /*
519: * Save it.
520: */
521: rdfWrite(rdf, "test.rdf");
522: rdfDestroySchema(rdf);
523:
524: /*
525: * Read it.
526: */
527: rdf = rdfRead("test.rdf");
528:
529: /*
530: * print it.
531: */
532: rpm = rdfGetNamespace(rdf, "http://www.rpm.org/");
533: if (rpm == NULL) fprintf(stderr, "RPM namespace not found !\n");
534: desc = rdfFirstDescription(rdf);
535: rdfGetValue(desc, "Name", rpm, &value, NULL);
536: printf("Name : %s\n", value);
1.15 daniel 537: debugFree(value);
1.1 veillard 538: rdfGetValue(desc, "Name", NULL, &value, NULL);
539: printf("Name : %s\n", value);
1.15 daniel 540: debugFree(value);
1.1 veillard 541: printf("\n---\n");
542: xmlDocDump(stdout, rdf);
543:
544: /*
545: * free it.
546: */
547: rdfDestroySchema(rdf);
548: return(0);
549: }
550: #endif
Webmaster