Annotation of rpm2html/rdf_api.c, revision 1.12

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

Webmaster