Annotation of rpm2html/rdf_api.c, revision 1.8

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

Webmaster