Main Page | Modules | Data Structures | File List | Data Fields | Globals

device-xml.c

Go to the documentation of this file.
00001 
00010 /* $Progeny: device-xml.c 3839 2003-11-17 04:25:01Z dsp $
00011  *
00012  * AUTHOR: John R. Daily <jdaily@progeny.com>
00013  *
00014  * Copyright 2002 Progeny Linux Systems, Inc.
00015  * Copyright 2002 Hewlett-Packard Company
00016  *
00017  * Permission is hereby granted, free of charge, to any person obtaining a
00018  * copy of this software and associated documentation files (the "Software"),
00019  * to deal in the Software without restriction, including without limitation
00020  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00021  * and/or sell copies of the Software, and to permit persons to whom the
00022  * Software is furnished to do so, subject to the following conditions:
00023  *
00024  * The above copyright notice and this permission notice shall be included in
00025  * all copies or substantial portions of the Software.
00026  *
00027  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00028  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00029  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00030  * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00031  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00032  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00033  * DEALINGS IN THE SOFTWARE.
00034  */
00035 
00036 /*
00037  * This is slightly different than busclass-xml.c and vendor-xml.c.
00038  * With those, the data that interests us is actually in the
00039  * XML files, so the structures built by those modules are the final
00040  * structures.
00041 
00042  * With the devices, the list we build here is only an intermediate
00043  * place to store the discover_device structures.  When we probe the
00044  * bus, we will move the discover_device structures for which
00045  * we have found corresponding hardware from this list to the final
00046  * one. (That action happens in the modules for the individual buses,
00047  * such as pci.c.)
00048 
00049  * Whereas busclass-xml.c and vendor-xml.c create pci_busclasses and
00050  * pci_vendors lists, respectively, this module creates
00051  * pci_devices_xml.  Objects are moved from this list to the
00052  * pci_devices list in pci.c, and the rest are freed.
00053  */
00054 
00055 #include <config.h>
00056 
00057 #include <sys/types.h>
00058 
00059 #include <assert.h>
00060 #include <ctype.h>
00061 #include <limits.h>
00062 #include <stdbool.h>
00063 #include <string.h>
00064 #include <errno.h>
00065 
00066 #include <expat.h>
00067 
00068 #include <discover.h>
00069 #include <discover-xml.h>
00070 
00071 #include "load-url.h"
00072 #include "device.h"
00073 #include "utils.h"
00074 #include "stack.h"
00075 
00077 #define IDLEN 5
00078 
00079 static discover_device_t *devices_xml[BUS_COUNT];
00080 
00082 enum state { START, FINISH, DEVICE, DATA };
00083 struct context {
00084     enum state state;
00085 
00086     discover_xml_busclass_t *busclasses;
00087     discover_xml_vendor_t *vendors;
00088 
00089     /* Device stuff */
00090     discover_device_t **dhead;
00091     discover_device_t *dtail;
00092 
00093     /* data stuff */
00094     discover_xml_stack *stack;
00095 /*
00096     discover_data_t *data_current;
00097     discover_data_t *data_parent;
00098 */
00099     int nesting;
00100     int last_nesting;
00101 
00102     int unknown_level; /* How deep are we into unknown XML tags? */
00103 };
00104 
00105 static char *known_device_elements[] = {
00106     "data",
00107     "device",
00108     "device_list",
00109     NULL
00110 };
00111 
00112 static void
00113 get_data_failure_handler(discover_error_t **status, char *url)
00114 {
00115     char *errmsg;
00116     static int maxurlsize = 1024; /* If your URL is longer than this,
00117                                      you've got problems */
00118 
00119     if((*status)->code == DISCOVER_EIO) {
00120         errmsg = _discover_xmalloc(maxurlsize + 1);
00121         snprintf(errmsg, maxurlsize, "Resource not found: %s", url);
00122         (*status)->create_message(status, errmsg);
00123         free(errmsg);
00124     } else {
00125         if (errno) {
00126             errmsg = _discover_xmalloc(maxurlsize + 1);
00127             snprintf(errmsg, maxurlsize, "Problem loading resource: %s: %s",
00128                      strerror(errno), url);
00129             (*status)->create_message(status, errmsg);
00130             free(errmsg);
00131         } else {
00132             (*status)->create_message(status,
00133                     "Unknown failure from system-dependent libraries");
00134         }
00135     }
00136 }
00137 
00138 static bool
00139 unknown_device_element(const XML_Char * const tag)
00140 {
00141     int i;
00142     for (i = 0; known_device_elements[i] != NULL; i++) {
00143         if (strcmp(tag, known_device_elements[i]) == 0)
00144             return false;
00145     }
00146     return true;
00147 }
00148 
00149 static void
00150 create_device(struct context *context, const XML_Char *attrs[])
00151 {
00152     int i;
00153     char *busclass, *model_id, *model_name, *vendor_id, *vendor_name;
00154     discover_device_t *new_device;
00155 
00156     assert(context != NULL);
00157     assert(attrs != NULL);
00158 
00159     busclass = model_id = model_name = vendor_id = NULL;
00160     for (i = 0; attrs[i]; i += 2) {
00161         if (strcmp(attrs[i], "busclass") == 0) {
00162             busclass = (char *)attrs[i + 1];
00163 
00164         } else if (strcmp(attrs[i], "model") == 0) {
00165             model_id = (char *)attrs[i + 1];
00166 
00167         } else if (strcmp(attrs[i], "model_name") == 0) {
00168             model_name = (char *)attrs[i + 1];
00169 
00170         } else if (strcmp(attrs[i], "vendor") == 0) {
00171             vendor_id = (char *)attrs[i + 1];
00172         }
00173     }
00174 
00175     assert(busclass != NULL);
00176     assert(model_id != NULL);
00177     assert(model_name != NULL);
00178     assert(vendor_id != NULL);
00179 
00180     vendor_name = discover_xml_vendor_id2name(context->vendors, vendor_id);
00181     assert(vendor_name != NULL);
00182 
00183     context->stack = discover_xml_stack_new();
00184 
00185     new_device = discover_device_new();
00186     new_device->busclasses = context->busclasses;
00187     new_device->vendors = context->vendors;
00188     new_device->busclass = _discover_xstrdup(busclass);
00189     new_device->model_id = _discover_xstrdup(model_id);
00190     new_device->model_name = _discover_xstrdup(model_name);
00191     new_device->vendor_id = _discover_xstrdup(vendor_id);
00192     new_device->vendor_name = _discover_xstrdup(vendor_name);
00193     new_device->data = NULL;
00194     new_device->next = NULL;
00195 
00196     if (*(context->dhead)) {
00197         context->dtail->next = new_device;
00198         context->dtail = new_device;
00199     } else {
00200         *(context->dhead) = new_device;
00201         context->dtail = new_device;
00202     }
00203 }
00204 
00205 static void
00206 create_data(struct context *context, const XML_Char *attrs[])
00207 {
00208     discover_data_t *new_data, *current_data;
00209     discover_xml_stack *stack;
00210     int i;
00211     char *discover_class, *version;
00212 
00213     assert(context != NULL);
00214     assert(attrs != NULL);
00215 
00216     new_data = discover_data_new();
00217     new_data->text = NULL;
00218     new_data->next = new_data->prev = new_data->child = NULL;
00219 
00220 
00221     discover_class = version = NULL;
00222     for (i = 0; attrs[i]; i += 2) {
00223         if (strcmp(attrs[i], "class") == 0) {
00224             discover_class = (char *)attrs[i + 1];
00225         } else if (strcmp(attrs[i], "version") == 0) {
00226             version = (char *)attrs[i + 1];
00227         }
00228     }
00229 
00230     assert(discover_class != NULL);
00231 
00232     new_data->discover_class = _discover_xstrdup(discover_class);
00233     if (version) {
00234         new_data->version = _discover_xstrdup(version);
00235     }
00236 
00237     stack = context->stack;
00238 
00239     assert(stack != NULL);
00240 
00241     current_data = discover_xml_stack_get(stack);
00242 
00243     if(current_data) { /* The first time through this, we have no data. */
00244         /* First element of the list */
00245         if(stack->depth > context->nesting) {
00246             discover_xml_stack_pop(&stack);
00247             //current_data = discover_xml_stack_pop(&stack);
00248             new_data->prev = current_data;
00249             new_data->prev->next = new_data;
00250             if(context->nesting) {
00251                 new_data->parent =
00252                     discover_xml_stack_getbynum(stack, context->nesting);
00253             }
00254         } else {
00255             /* Brand new child */
00256             new_data->parent = current_data;
00257             new_data->parent->child = new_data;
00258         }
00259     }
00260 
00261     discover_xml_stack_push(&stack, new_data);
00262     context->stack = stack;
00263 }
00264 
00265 static void
00266 start_element(void *ctx, const XML_Char *name, const XML_Char *attrs[])
00267 {
00268     struct context *context = ctx;
00269 
00270     assert(context != NULL);
00271     assert(name != NULL);
00272     assert(attrs != NULL);
00273 
00274 
00275     if (unknown_device_element(name)) {
00276         context->unknown_level++;
00277         return;
00278     }
00279 
00280     if (context->unknown_level > 0) {
00281         return;
00282     }
00283 
00284     switch (context->state) {
00285     case FINISH:
00286         return;
00287 
00288     case START:
00289         if (strcmp(name, "device") == 0) {
00290             context->state = DEVICE;
00291             create_device(context, attrs);
00292             context->stack = discover_xml_stack_new();
00293         }
00294         break;
00295 
00296     case DEVICE:
00297         if (strcmp(name, "data") == 0) {
00298             context->nesting = context->last_nesting = 0;
00299             context->state = DATA;
00300         }
00301         /* Falls through */
00302 
00303     case DATA:
00304         if (strcmp(name, "data") == 0) {
00305             create_data(context, attrs);
00306             context->last_nesting = context->nesting;
00307             context->nesting++;
00308         }
00309         break;
00310     }
00311 }
00312 
00313 static void
00314 end_element(void *ctx, const XML_Char *name)
00315 {
00316     struct context *context = ctx;
00317     discover_device_t *device;
00318     discover_data_t *current_data;
00319     discover_xml_stack *stack;
00320 
00321 
00322     assert(context != NULL);
00323     assert(name != NULL);
00324 
00325 
00326     if (unknown_device_element(name)) {
00327         context->unknown_level--;
00328         return;
00329     }
00330 
00331     if (context->unknown_level > 0) {
00332         return;
00333     }
00334 
00335     switch (context->state) {
00336     case FINISH:
00337     case START:
00338         break;
00339 
00340     case DEVICE:
00341         context->state = START;
00342         device = context->dtail;
00343         stack = context->stack;
00344         current_data = discover_xml_stack_get(stack);
00345         device->data = discover_data_get_first(current_data);
00346         break;
00347 
00348     case DATA:
00349         context->nesting--;
00350         stack = context->stack;
00351         if((context->nesting + 2) <= stack->depth) {
00352             while((context->nesting + 1 < stack->depth) &&
00353                     stack->depth > 1) {
00354                 discover_xml_stack_pop(&stack);
00355             }
00356             context->stack = stack;
00357         }
00358 
00359         if (context->nesting == 0) {
00360             context->state = DEVICE;
00361         }
00362     }
00363 }
00364 
00365 static void
00366 cdata(void *ctx, const XML_Char *data, int len)
00367 {
00368     struct context *context = ctx;
00369     size_t old_len;
00370     discover_data_t *current_data;
00371 
00372     assert(context != NULL);
00373     assert(data != NULL);
00374 
00375     if (context->state == DATA
00376         && context->nesting > context->last_nesting) {
00377         assert(context->stack != NULL);
00378         current_data = context->stack->data;
00379         assert(current_data != NULL);
00380 
00381         if (!current_data->text) {
00382             old_len = 0;
00383             current_data->text = _discover_xmalloc(1);
00384             current_data->text[0] = '\0';
00385         } else {
00386             old_len = strlen(current_data->text);
00387         }
00388         current_data->text
00389             = _discover_xrealloc(current_data->text,
00390                                  old_len + len + 1);
00391         strncat(current_data->text,
00392                 (const char *)data,
00393                 (unsigned int)len);
00394     }
00395 }
00396 
00411 /* Sshh!  Don't tell, but this doesn't actually do any merging at all.
00412  * Instead, it simply inserts newer entries at the front of the list,
00413  * meaning that device info found later supersedes info found earlier.
00414  * This gives the illusion of merging, but potentially wastes memory
00415  * with duplicates.
00416  */
00417 void
00418 discover_xml_merge_device_url(discover_device_t **dlist, char *url,
00419                               discover_xml_busclass_t *busclasses,
00420                               discover_xml_vendor_t *vendors,
00421                               discover_error_t *status)
00422 {
00423     XML_Parser parser;
00424     struct context context;
00425 
00426     assert(url != NULL);
00427     assert(busclasses != NULL);
00428     assert(vendors != NULL);
00429     assert(status != NULL);
00430 
00431     context.state = START;
00432     context.dhead = dlist;
00433     if (*(context.dhead)) {
00434         discover_device_t *next = *(context.dhead);
00435         while(next->next != NULL) {
00436             next = next->next;
00437         }
00438         context.dtail = next;
00439     } else {
00440         context.dtail = NULL;
00441     }
00442 
00443     context.busclasses = busclasses;
00444     context.vendors = vendors;
00445     context.unknown_level = 0;
00446 
00447     parser = XML_ParserCreate(NULL);
00448     XML_SetElementHandler(parser, start_element, end_element);
00449     XML_SetCharacterDataHandler(parser, cdata);
00450     XML_SetUserData(parser, &context);
00451 
00452     if (!_discover_load_url(url, parser)) {
00453         XML_ParserFree(parser);
00454         status->code = DISCOVER_EIO;
00455         return;
00456     }
00457 
00458     if (!XML_Parse(parser, "", 0, 1)) {
00459         XML_ParserFree(parser);
00460         status->code = DISCOVER_EXML;
00461         return;
00462     }
00463 
00464     XML_ParserFree(parser);
00465 
00466     return;
00467 }
00468 
00475 discover_device_t *
00476 discover_xml_get_devices(discover_bus_t bus, discover_error_t *status)
00477 {
00478     discover_xml_url_t *urls, *i;
00479     char *url;
00480     discover_xml_busclass_t *busclasses;
00481     discover_xml_vendor_t *vendors;
00482 
00483     assert(status != NULL);
00484 
00485     status->code = 0;
00486 
00487     if (!devices_xml[bus]) {
00488         urls = discover_xml_get_data_urls(bus, DEVICE, status);
00489         if (status->code != 0) {
00490             return NULL;
00491         }
00492 
00493         busclasses = discover_xml_get_busclasses(bus, status);
00494         if (status->code != 0) {
00495             return NULL;
00496         }
00497 
00498         vendors = discover_xml_get_vendors(bus, status);
00499         if (status->code != 0) {
00500             return NULL;
00501         }
00502 
00503         for (i = urls;
00504              i;
00505              i = discover_xml_url_get_next(i)) {
00506             url = discover_xml_url_get_url(i);
00507             discover_xml_merge_device_url(&(devices_xml[bus]), url,
00508                                           busclasses, vendors, status);
00509             if (status->code != 0) {
00510                 get_data_failure_handler(&status, url);
00511             }
00512         }
00513     }
00514 
00515 
00516     return devices_xml[bus];
00517 }
00518 
00522 void
00523 discover_xml_free_devices(void)
00524 {
00525     int i;
00526     for (i = 0; i < BUS_COUNT; i++) {
00527         discover_device_free(devices_xml[i], 1);
00528         devices_xml[i] = NULL;
00529     }
00530 }
00531 
00541 discover_device_t *
00542 discover_xml_find_device(discover_device_t *xml_devices,
00543                          char *target_vendor, char *target_model,
00544                          discover_error_t *status)
00545 {
00546     discover_device_t *device;
00547 
00548     assert(target_vendor || target_model);
00549 
00550     for (device = xml_devices;
00551          device;
00552          device = device->next) {
00553         if (target_vendor && target_model) {
00554             if (strcmp(device->model_id, target_model) == 0
00555                 && strcmp(device->vendor_id, target_vendor) == 0) {
00556                 break;
00557             }
00558         } else if (target_vendor) {
00559             if (strcmp(device->vendor_id, target_vendor) == 0) {
00560                 break;
00561             }
00562         } else {
00563             /* We only have target_model. */
00564             if (strcmp(device->model_id, target_model) == 0) {
00565                 break;
00566             }
00567         }
00568     }
00569 
00570     return device;
00571 }
00572 
00585 discover_device_t *
00586 discover_xml_find_next_device(discover_device_t *xml_devices,
00587                          char *target_vendor, char *target_model,
00588                          discover_error_t *status)
00589 {
00590     return discover_xml_find_device(xml_devices->next,
00591                                     target_vendor, target_model,
00592                                     status);
00593 }
00594 
00595 
00605 discover_device_t *
00606 discover_xml_get_matching_devices(discover_device_t *xml_devices,
00607                                   char *target_vendor, char *target_model,
00608                                   discover_error_t *status)
00609 {
00610     discover_device_t *device, *last, *copy;
00611     discover_device_t *head_device = NULL;
00612 
00613     device = discover_xml_find_device(xml_devices, target_vendor,
00614                                       target_model, status);
00615     last = NULL;
00616 
00617     while(device) {
00618         copy = discover_device_new();
00619         discover_device_copy(device, copy);
00620         copy->next = NULL;
00621 
00622         if (last) {
00623             last->extra = copy;
00624         } else {
00625             head_device = copy;
00626         }
00627 
00628         last = copy;
00629 
00630         device = discover_xml_find_next_device(device, target_vendor,
00631                                                target_model, status);
00632     }
00633 
00634     return head_device;
00635 }
00636 
00639 /*
00640  * Local variables:
00641  * c-file-style: "progeny"
00642  * indent-tabs-mode: nil
00643  * End:
00644  */
00645 /* vim: set cin fo=tcroq sw=4 et sts=4 tw=75: */

Generated on Sat Jan 31 14:39:17 2004 for discover by doxygen 1.3.4