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 4368 2004-05-13 19:57:37Z licquia $
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             context->stack = discover_xml_stack_new();
00296         }
00297         break;
00298 
00299     case DEVICE:
00300         if (strcmp(name, "data") == 0) {
00301             context->nesting = context->last_nesting = 0;
00302             context->state = DATA;
00303         }
00304         /* Falls through */
00305 
00306     case DATA:
00307         if (strcmp(name, "data") == 0) {
00308             create_data(context, attrs);
00309             context->last_nesting = context->nesting;
00310             context->nesting++;
00311         }
00312         break;
00313     }
00314 }
00315 
00316 static void
00317 end_element(void *ctx, const XML_Char *name)
00318 {
00319     struct context *context = ctx;
00320     discover_device_t *device;
00321     discover_data_t *current_data;
00322     discover_xml_stack *stack;
00323 
00324 
00325     assert(context != NULL);
00326     assert(name != NULL);
00327 
00328 
00329     if (unknown_device_element(name)) {
00330         context->unknown_level--;
00331         return;
00332     }
00333 
00334     if (context->unknown_level > 0) {
00335         return;
00336     }
00337 
00338     switch (context->state) {
00339     case FINISH:
00340     case START:
00341         break;
00342 
00343     case DEVICE:
00344         context->state = START;
00345         device = context->dtail;
00346         stack = context->stack;
00347         current_data = discover_xml_stack_get(stack);
00348         device->data = discover_data_get_first(current_data);
00349         break;
00350 
00351     case DATA:
00352         context->nesting--;
00353         stack = context->stack;
00354         if((context->nesting + 2) <= stack->depth) {
00355             while((context->nesting + 1 < stack->depth) &&
00356                     stack->depth > 1) {
00357                 discover_xml_stack_pop(&stack);
00358             }
00359             context->stack = stack;
00360         }
00361 
00362         if (context->nesting == 0) {
00363             context->state = DEVICE;
00364         }
00365     }
00366 }
00367 
00368 static void
00369 cdata(void *ctx, const XML_Char *data, int len)
00370 {
00371     struct context *context = ctx;
00372     size_t old_len;
00373     discover_data_t *current_data;
00374 
00375     assert(context != NULL);
00376     assert(data != NULL);
00377 
00378     if (context->state == DATA
00379         && context->nesting > context->last_nesting) {
00380         assert(context->stack != NULL);
00381         current_data = context->stack->data;
00382         assert(current_data != NULL);
00383 
00384         if (!current_data->text) {
00385             old_len = 0;
00386             current_data->text = _discover_xmalloc(1);
00387             current_data->text[0] = '\0';
00388         } else {
00389             old_len = strlen(current_data->text);
00390         }
00391         current_data->text
00392             = _discover_xrealloc(current_data->text,
00393                                  old_len + len + 1);
00394         strncat(current_data->text,
00395                 (const char *)data,
00396                 (unsigned int)len);
00397     }
00398 }
00399 
00414 /* Sshh!  Don't tell, but this doesn't actually do any merging at all.
00415  * Instead, it simply inserts newer entries at the front of the list,
00416  * meaning that device info found later supersedes info found earlier.
00417  * This gives the illusion of merging, but potentially wastes memory
00418  * with duplicates.
00419  */
00420 void
00421 discover_xml_merge_device_url(discover_device_t **dlist, char *url,
00422                               discover_xml_busclass_t *busclasses,
00423                               discover_xml_vendor_t *vendors,
00424                               discover_error_t *status)
00425 {
00426     XML_Parser parser;
00427     struct context context;
00428 
00429     assert(url != NULL);
00430     assert(busclasses != NULL);
00431     assert(vendors != NULL);
00432     assert(status != NULL);
00433 
00434     context.state = START;
00435     context.dhead = dlist;
00436     if (*(context.dhead)) {
00437         discover_device_t *next = *(context.dhead);
00438         while(next->next != NULL) {
00439             next = next->next;
00440         }
00441         context.dtail = next;
00442     } else {
00443         context.dtail = NULL;
00444     }
00445 
00446     context.busclasses = busclasses;
00447     context.vendors = vendors;
00448     context.unknown_level = 0;
00449 
00450     parser = XML_ParserCreate(NULL);
00451     XML_SetElementHandler(parser, start_element, end_element);
00452     XML_SetCharacterDataHandler(parser, cdata);
00453     XML_SetUserData(parser, &context);
00454 
00455     if (!_discover_load_url(url, parser)) {
00456         XML_ParserFree(parser);
00457         status->code = DISCOVER_EIO;
00458         return;
00459     }
00460 
00461     if (!XML_Parse(parser, "", 0, 1)) {
00462         XML_ParserFree(parser);
00463         status->code = DISCOVER_EXML;
00464         return;
00465     }
00466 
00467     XML_ParserFree(parser);
00468 
00469     return;
00470 }
00471 
00478 discover_device_t *
00479 discover_xml_get_devices(discover_bus_t bus, discover_error_t *status)
00480 {
00481     discover_xml_url_t *urls, *i;
00482     char *url;
00483     discover_xml_busclass_t *busclasses;
00484     discover_xml_vendor_t *vendors;
00485 
00486     assert(status != NULL);
00487 
00488     status->code = 0;
00489 
00490     if (!devices_xml[bus]) {
00491         urls = discover_xml_get_data_urls(bus, DEVICE, status);
00492         if (status->code != 0) {
00493             return NULL;
00494         }
00495 
00496         busclasses = discover_xml_get_busclasses(bus, status);
00497         if (status->code != 0) {
00498             return NULL;
00499         }
00500 
00501         vendors = discover_xml_get_vendors(bus, status);
00502         if (status->code != 0) {
00503             return NULL;
00504         }
00505 
00506         for (i = urls;
00507              i;
00508              i = discover_xml_url_get_next(i)) {
00509             url = discover_xml_url_get_url(i);
00510             discover_xml_merge_device_url(&(devices_xml[bus]), url,
00511                                           busclasses, vendors, status);
00512             if (status->code != 0) {
00513                 get_data_failure_handler(&status, url);
00514             }
00515         }
00516     }
00517 
00518 
00519     return devices_xml[bus];
00520 }
00521 
00525 void
00526 discover_xml_free_devices(void)
00527 {
00528     int i;
00529     for (i = 0; i < BUS_COUNT; i++) {
00530         discover_device_free(devices_xml[i], 1);
00531         devices_xml[i] = NULL;
00532     }
00533 }
00534 
00544 discover_device_t *
00545 discover_xml_find_device(discover_device_t *xml_devices,
00546                          char *target_vendor, char *target_model,
00547                          discover_error_t *status)
00548 {
00549     discover_device_t *device;
00550 
00551     assert(target_vendor || target_model);
00552 
00553     for (device = xml_devices;
00554          device;
00555          device = device->next) {
00556         if (target_vendor && target_model) {
00557             if (strcmp(device->model_id, target_model) == 0
00558                 && strcmp(device->vendor_id, target_vendor) == 0) {
00559                 break;
00560             }
00561         } else if (target_vendor) {
00562             if (strcmp(device->vendor_id, target_vendor) == 0) {
00563                 break;
00564             }
00565         } else {
00566             /* We only have target_model. */
00567             if (strcmp(device->model_id, target_model) == 0) {
00568                 break;
00569             }
00570         }
00571     }
00572 
00573     return device;
00574 }
00575 
00588 discover_device_t *
00589 discover_xml_find_next_device(discover_device_t *xml_devices,
00590                          char *target_vendor, char *target_model,
00591                          discover_error_t *status)
00592 {
00593     return discover_xml_find_device(xml_devices->next,
00594                                     target_vendor, target_model,
00595                                     status);
00596 }
00597 
00598 
00608 discover_device_t *
00609 discover_xml_get_matching_devices(discover_device_t *xml_devices,
00610                                   char *target_vendor, char *target_model,
00611                                   discover_error_t *status)
00612 {
00613     discover_device_t *device, *last, *copy;
00614     discover_device_t *head_device = NULL;
00615 
00616     device = discover_xml_find_device(xml_devices, target_vendor,
00617                                       target_model, status);
00618     last = NULL;
00619 
00620     while(device) {
00621         copy = discover_device_new();
00622         discover_device_copy(device, copy);
00623         copy->next = NULL;
00624 
00625         if (last) {
00626             last->extra = copy;
00627         } else {
00628             head_device = copy;
00629         }
00630 
00631         last = copy;
00632 
00633         device = discover_xml_find_next_device(device, target_vendor,
00634                                                target_model, status);
00635     }
00636 
00637     return head_device;
00638 }
00639 
00642 /*
00643  * Local variables:
00644  * c-file-style: "progeny"
00645  * indent-tabs-mode: nil
00646  * End:
00647  */
00648 /* vim: set cin fo=tcroq sw=4 et sts=4 tw=75: */

Generated on Fri Nov 19 23:57:43 2004 for discover by  doxygen 1.3.9.1