Annotation of rpm2html/rdf_api.c, revision 1.17

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

Webmaster