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

device-xml.c

Go to the documentation of this file.
00001 
00010 /* $Progeny$
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/discover.h>
00069 #include <discover/discover-xml.h>
00070 
00071 #include <discover/load-url.h>
00072 #include <discover/device.h>
00073 #include <discover/utils.h>
00074 #include <discover/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(model_id != NULL);
00176     assert(model_name != NULL);
00177     assert(vendor_id != NULL);
00178 
00179     vendor_name = discover_xml_vendor_id2name(context->vendors, vendor_id);
00180     assert(vendor_name != NULL);
00181 
00182     context->stack = discover_xml_stack_new();
00183 
00184     new_device = discover_device_new();
00185     new_device->busclasses = context->busclasses;
00186     new_device->vendors = context->vendors;
00187     if (busclass) {
00188         new_device->busclass = _discover_xstrdup(busclass);
00189     } else {
00190         new_device->busclass = NULL;
00191     }
00192     new_device->model_id = _discover_xstrdup(model_id);
00193     new_device->model_name = _discover_xstrdup(model_name);
00194     new_device->vendor_id = _discover_xstrdup(vendor_id);
00195     new_device->vendor_name = _discover_xstrdup(vendor_name);
00196     new_device->data = NULL;
00197     new_device->next = NULL;
00198 
00199     if (*(context->dhead)) {
00200         context->dtail->next = new_device;
00201         context->dtail = new_device;
00202     } else {
00203         *(context->dhead) = new_device;
00204         context->dtail = new_device;
00205     }
00206 }
00207 
00208 static void
00209 create_data(struct context *context, const XML_Char *attrs[])
00210 {
00211     discover_data_t *new_data, *current_data;
00212     discover_xml_stack *stack;
00213     int i;
00214     char *discover_class, *version;
00215 
00216     assert(context != NULL);
00217     assert(attrs != NULL);
00218 
00219     new_data = discover_data_new();
00220     new_data->text = NULL;
00221     new_data->next = new_data->prev = new_data->child = NULL;
00222 
00223 
00224     discover_class = version = NULL;
00225     for (i = 0; attrs[i]; i += 2) {
00226         if (strcmp(attrs[i], "class") == 0) {
00227             discover_class = (char *)attrs[i + 1];
00228         } else if (strcmp(attrs[i], "version") == 0) {
00229             version = (char *)attrs[i + 1];
00230         }
00231     }
00232 
00233     assert(discover_class != NULL);
00234 
00235     new_data->discover_class = _discover_xstrdup(discover_class);
00236     if (version) {
00237         new_data->version = _discover_xstrdup(version);
00238     }
00239 
00240     stack = context->stack;
00241 
00242     assert(stack != NULL);
00243 
00244     current_data = discover_xml_stack_get(stack);
00245 
00246     if(current_data) { /* The first time through this, we have no data. */
00247         /* First element of the list */
00248         if(stack->depth > context->nesting) {
00249             discover_xml_stack_pop(&stack);
00250             //current_data = discover_xml_stack_pop(&stack);
00251             new_data->prev = current_data;
00252             new_data->prev->next = new_data;
00253             if(context->nesting) {
00254                 new_data->parent =
00255                     discover_xml_stack_getbynum(stack, context->nesting);
00256             }
00257         } else {
00258             /* Brand new child */
00259             new_data->parent = current_data;
00260             new_data->parent->child = new_data;
00261         }
00262     }
00263 
00264     discover_xml_stack_push(&stack, new_data);
00265     context->stack = stack;
00266 }
00267 
00268 static void
00269 start_element(void *ctx, const XML_Char *name, const XML_Char *attrs[])
00270 {
00271     struct context *context = ctx;
00272 
00273     assert(context != NULL);
00274     assert(name != NULL);
00275     assert(attrs != NULL);
00276 
00277 
00278     if (unknown_device_element(name)) {
00279         context->unknown_level++;
00280         return;
00281     }
00282 
00283     if (context->unknown_level > 0) {
00284         return;
00285     }
00286 
00287     switch (context->state) {
00288     case FINISH:
00289         return;
00290 
00291     case START:
00292         if (strcmp(name, "device") == 0) {
00293             context->state = DEVICE;
00294             create_device(context, attrs);
00295         }
00296         break;
00297 
00298     case DEVICE:
00299         if (strcmp(name, "data") == 0) {
00300             context->nesting = context->last_nesting = 0;
00301             context->state = DATA;
00302         }
00303         /* Falls through */
00304 
00305     case DATA:
00306         if (strcmp(name, "data") == 0) {
00307             create_data(context, attrs);
00308             context->last_nesting = context->nesting;
00309             context->nesting++;
00310         }
00311         break;
00312     }
00313 }
00314 
00315 static void
00316 end_element(void *ctx, const XML_Char *name)
00317 {
00318     struct context *context = ctx;
00319     discover_device_t *device;
00320     discover_data_t *current_data;
00321     discover_xml_stack *stack;
00322 
00323 
00324     assert(context != NULL);
00325     assert(name != NULL);
00326 
00327 
00328     if (unknown_device_element(name)) {
00329         context->unknown_level--;
00330         return;
00331     }
00332 
00333     if (context->unknown_level > 0) {
00334         return;
00335     }
00336 
00337     switch (context->state) {
00338     case FINISH:
00339     case START:
00340         break;
00341 
00342     case DEVICE:
00343         context->state = START;
00344         device = context->dtail;
00345         stack = context->stack;
00346         current_data = discover_xml_stack_get(stack);
00347         device->data = discover_data_get_first(current_data);
00348 
00349         while (discover_xml_stack_pop(&(context->stack))) ;
00350         discover_xml_stack_destroy(context->stack);
00351         context->stack = NULL;
00352 
00353         break;
00354 
00355     case DATA:
00356         context->nesting--;
00357         stack = context->stack;
00358         if((context->nesting + 2) <= stack->depth) {
00359             while((context->nesting + 1 < stack->depth) &&
00360                     stack->depth > 1) {
00361                 discover_xml_stack_pop(&stack);
00362             }
00363             context->stack = stack;
00364         }
00365 
00366         if (context->nesting == 0) {
00367             context->state = DEVICE;
00368         }
00369     }
00370 }
00371 
00372 static void
00373 cdata(void *ctx, const XML_Char *data, int len)
00374 {
00375     struct context *context = ctx;
00376     size_t old_len;
00377     discover_data_t *current_data;
00378 
00379     assert(context != NULL);
00380     assert(data != NULL);
00381 
00382     if (context->state == DATA
00383         && context->nesting > context->last_nesting) {
00384         assert(context->stack != NULL);
00385         current_data = context->stack->data;
00386         assert(current_data != NULL);
00387 
00388         if (!current_data->text) {
00389             old_len = 0;
00390             current_data->text = _discover_xmalloc(1);
00391             current_data->text[0] = '\0';
00392         } else {
00393             old_len = strlen(current_data->text);
00394         }
00395         current_data->text
00396             = _discover_xrealloc(current_data->text,
00397                                  old_len + len + 1);
00398         strncat(current_data->text,
00399                 (const char *)data,
00400                 (unsigned int)len);
00401     }
00402 }
00403 
00418 /* Sshh!  Don't tell, but this doesn't actually do any merging at all.
00419  * Instead, it simply inserts newer entries at the front of the list,
00420  * meaning that device info found later supersedes info found earlier.
00421  * This gives the illusion of merging, but potentially wastes memory
00422  * with duplicates.
00423  */
00424 void
00425 discover_xml_merge_device_url(discover_device_t **dlist, char *url,
00426                               discover_xml_busclass_t *busclasses,
00427                               discover_xml_vendor_t *vendors,
00428                               discover_error_t *status)
00429 {
00430     XML_Parser parser;
00431     struct context context;
00432 
00433     assert(url != NULL);
00434     assert(busclasses != NULL);
00435     assert(vendors != NULL);
00436     assert(status != NULL);
00437 
00438     context.state = START;
00439     context.dhead = dlist;
00440     if (*(context.dhead)) {
00441         discover_device_t *next = *(context.dhead);
00442         while(next->next != NULL) {
00443             next = next->next;
00444         }
00445         context.dtail = next;
00446     } else {
00447         context.dtail = NULL;
00448     }
00449 
00450     context.busclasses = busclasses;
00451     context.vendors = vendors;
00452     context.unknown_level = 0;
00453     context.stack = NULL;
00454 
00455     parser = XML_ParserCreate(NULL);
00456     XML_SetElementHandler(parser, start_element, end_element);
00457     XML_SetCharacterDataHandler(parser, cdata);
00458     XML_SetUserData(parser, &context);
00459 
00460     if (!_discover_load_url(url, parser)) {
00461         XML_ParserFree(parser);
00462         status->code = DISCOVER_EIO;
00463         return;
00464     }
00465 
00466     if (!XML_Parse(parser, "", 0, 1)) {
00467         XML_ParserFree(parser);
00468         status->code = DISCOVER_EXML;
00469         return;
00470     }
00471 
00472     XML_ParserFree(parser);
00473 
00474     return;
00475 }
00476 
00483 discover_device_t *
00484 discover_xml_get_devices(discover_bus_t bus, discover_error_t *status)
00485 {
00486     discover_xml_url_t *urls, *i;
00487     char *url;
00488     discover_xml_busclass_t *busclasses;
00489     discover_xml_vendor_t *vendors;
00490 
00491     assert(status != NULL);
00492 
00493     status->code = 0;
00494 
00495     if (!devices_xml[bus]) {
00496         urls = discover_xml_get_data_urls(bus, DEVICE, status);
00497         if (status->code != 0) {
00498             return NULL;
00499         }
00500 
00501         busclasses = discover_xml_get_busclasses(bus, status);
00502         if (status->code != 0) {
00503             return NULL;
00504         }
00505 
00506         vendors = discover_xml_get_vendors(bus, status);
00507         if (status->code != 0) {
00508             return NULL;
00509         }
00510 
00511         for (i = urls;
00512              i;
00513              i = discover_xml_url_get_next(i)) {
00514             url = discover_xml_url_get_url(i);
00515             discover_xml_merge_device_url(&(devices_xml[bus]), url,
00516                                           busclasses, vendors, status);
00517             if (status->code != 0) {
00518                 get_data_failure_handler(&status, url);
00519             }
00520         }
00521     }
00522 
00523 
00524     return devices_xml[bus];
00525 }
00526 
00530 void
00531 discover_xml_free_devices(void)
00532 {
00533     int i;
00534     for (i = 0; i < BUS_COUNT; i++) {
00535         discover_device_free(devices_xml[i], 1);
00536         devices_xml[i] = NULL;
00537     }
00538 }
00539 
00549 discover_device_t *
00550 discover_xml_find_device(discover_device_t *xml_devices,
00551                          char *target_vendor, char *target_model,
00552                          discover_error_t *status)
00553 {
00554     discover_device_t *device;
00555 
00556     assert(target_vendor || target_model);
00557 
00558     for (device = xml_devices;
00559          device;
00560          device = device->next) {
00561         if (target_vendor && target_model) {
00562             if (strcmp(device->model_id, target_model) == 0
00563                 && strcmp(device->vendor_id, target_vendor) == 0) {
00564                 break;
00565             }
00566         } else if (target_vendor) {
00567             if (strcmp(device->vendor_id, target_vendor) == 0) {
00568                 break;
00569             }
00570         } else {
00571             /* We only have target_model. */
00572             if (strcmp(device->model_id, target_model) == 0) {
00573                 break;
00574             }
00575         }
00576     }
00577 
00578     return device;
00579 }
00580 
00593 discover_device_t *
00594 discover_xml_find_next_device(discover_device_t *xml_devices,
00595                          char *target_vendor, char *target_model,
00596                          discover_error_t *status)
00597 {
00598     return discover_xml_find_device(xml_devices->next,
00599                                     target_vendor, target_model,
00600                                     status);
00601 }
00602 
00603 
00613 discover_device_t *
00614 discover_xml_get_matching_devices(discover_device_t *xml_devices,
00615                                   char *target_vendor, char *target_model,
00616                                   discover_error_t *status)
00617 {
00618     discover_device_t *device, *last, *copy;
00619     discover_device_t *head_device = NULL;
00620 
00621     device = discover_xml_find_device(xml_devices, target_vendor,
00622                                       target_model, status);
00623     last = NULL;
00624 
00625     while(device) {
00626         copy = discover_device_new();
00627         discover_device_copy(device, copy);
00628         copy->next = NULL;
00629         copy->extra = NULL;
00630 
00631         if (last) {
00632             last->extra = copy;
00633         } else {
00634             head_device = copy;
00635         }
00636 
00637         last = copy;
00638 
00639         device = discover_xml_find_next_device(device, target_vendor,
00640                                                target_model, status);
00641     }
00642 
00643     return head_device;
00644 }
00645 
00648 /*
00649  * Local variables:
00650  * c-file-style: "progeny"
00651  * indent-tabs-mode: nil
00652  * End:
00653  */
00654 /* vim: set cin fo=tcroq sw=4 et sts=4 tw=75: */

Generated on Thu Jul 28 03:38:00 2005 for discover by  doxygen 1.4.2