Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

device_info.c

00001 /***************************************************************************
00002  * CVSID: $Id: device_info.c,v 1.5 2004/03/03 17:56:56 david Exp $
00003  *
00004  * device_store.c : Search for .fdi files and merge on match
00005  *
00006  * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
00007  *
00008  * Licensed under the Academic Free License version 2.0
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023  *
00024  **************************************************************************/
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #  include <config.h>
00028 #endif
00029 
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <dirent.h>
00034 #include <expat.h>
00035 #include <assert.h>
00036 #include <dbus/dbus.h>
00037 #include <dbus/dbus-glib.h>
00038 
00039 #include <libhal/libhal.h>  /* for common defines etc. */
00040 
00041 #include "logger.h"
00042 #include "device_info.h"
00043 
00053 #define MAX_DEPTH 32
00054 
00056 #define CDATA_BUF_SIZE  1024
00057 
00059 #define MAX_KEY_SIZE 128
00060 
00062 enum {
00064     CURELEM_UNKNOWN = -1,
00065 
00067     CURELEM_DEVICE_INFO = 0,
00068 
00070     CURELEM_DEVICE = 1,
00071 
00073     CURELEM_MATCH = 2,
00074 
00076     CURELEM_MERGE = 3,
00077 };
00078 
00081 typedef struct {
00083     char *file;
00084 
00086     XML_Parser parser;
00087 
00089     HalDevice *device;
00090 
00092     char cdata_buf[CDATA_BUF_SIZE];
00093 
00095     int cdata_buf_len;
00096     
00098     int depth;
00099 
00101     int curelem;
00102 
00104     int curelem_stack[MAX_DEPTH];
00105 
00107     dbus_bool_t aborted;
00108 
00109 
00111     int match_depth_first_fail;
00112 
00114     dbus_bool_t match_ok;
00115 
00116 
00117 
00119     char merge_key[MAX_KEY_SIZE];
00120 
00122     int merge_type;
00123 
00125     dbus_bool_t device_matched;
00126 
00127 } ParsingContext;
00128 
00136 static dbus_bool_t
00137 handle_match (ParsingContext * pc, const char **attr)
00138 {
00139     const char *key;
00140     int num_attrib;
00141 
00142     for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++);
00143 
00144     if (num_attrib != 4)
00145         return FALSE;
00146 
00147     if (strcmp (attr[0], "key") != 0)
00148         return FALSE;
00149     key = attr[1];
00150 
00151     if (strcmp (attr[2], "string") == 0) {
00152         const char *value;
00153 
00154         /* match string property */
00155 
00156         value = attr[3];
00157 
00158         /*HAL_INFO(("Checking that key='%s' is a string that "
00159           "equals '%s'", key, value)); */
00160 
00161         if (ds_property_get_type (pc->device, key) != DBUS_TYPE_STRING)
00162             return FALSE;
00163 
00164         if (strcmp
00165             (ds_property_get_string (pc->device, key), value) != 0)
00166             return FALSE;
00167 
00168         HAL_INFO (("*** string match for key %s", key));
00169         return TRUE;
00170     } else if (strcmp (attr[2], "int") == 0) {
00171         dbus_int32_t value;
00172 
00173         /* match integer property */
00174         value = strtol (attr[3], NULL, 0);
00175         
00178         HAL_INFO (("Checking that key='%s' is a int that equals %d", 
00179                key, value));
00180 
00181         if (ds_property_get_type (pc->device, key) != DBUS_TYPE_INT32)
00182             return FALSE;
00183 
00184         if (ds_property_get_int (pc->device, key) != value) {
00185             return FALSE;
00186         }
00187 
00188         return TRUE;
00189     } else if (strcmp (attr[2], "bool") == 0) {
00190         dbus_bool_t value;
00191 
00192         /* match string property */
00193 
00194         if (strcmp (attr[3], "false") == 0)
00195             value = FALSE;
00196         else if (strcmp (attr[3], "true") == 0)
00197             value = TRUE;
00198         else
00199             return FALSE;
00200 
00201         HAL_INFO (("Checking that key='%s' is a bool that equals %s", 
00202                key, value ? "TRUE" : "FALSE"));
00203 
00204         if (ds_property_get_type (pc->device, key) != 
00205             DBUS_TYPE_BOOLEAN)
00206             return FALSE;
00207 
00208         if (ds_property_get_bool (pc->device, key) != value)
00209             return FALSE;
00210 
00211         HAL_INFO (("*** bool match for key %s", key));
00212         return TRUE;
00213     }
00214 
00215     return FALSE;
00216 }
00217 
00218 
00224 static void
00225 handle_merge (ParsingContext * pc, const char **attr)
00226 {
00227     int num_attrib;
00228 
00229     for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
00230         ;
00231     }
00232 
00233     if (num_attrib != 4)
00234         return;
00235 
00236     if (strcmp (attr[0], "key") != 0)
00237         return;
00238     strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
00239 
00240     if (strcmp (attr[2], "type") != 0)
00241         return;
00242 
00243     if (strcmp (attr[3], "string") == 0) {
00244         /* match string property */
00245         pc->merge_type = DBUS_TYPE_STRING;
00246         return;
00247     } else if (strcmp (attr[3], "bool") == 0) {
00248         /* match string property */
00249         pc->merge_type = DBUS_TYPE_BOOLEAN;
00250         return;
00251     } else if (strcmp (attr[3], "int") == 0) {
00252         /* match string property */
00253         pc->merge_type = DBUS_TYPE_INT32;
00254         return;
00255     } else if (strcmp (attr[3], "double") == 0) {
00256         /* match string property */
00257         pc->merge_type = DBUS_TYPE_DOUBLE;
00258         return;
00259     }
00260 
00261     return;
00262 }
00263 
00268 static void
00269 parsing_abort (ParsingContext * pc)
00270 {
00271     /* Grr, expat can't abort parsing */
00272     HAL_ERROR (("Aborting parsing of document"));
00273     pc->aborted = TRUE;
00274 }
00275 
00282 static void
00283 start (ParsingContext * pc, const char *el, const char **attr)
00284 {
00285     int i;
00286 
00287     if (pc->aborted)
00288         return;
00289 
00290     pc->cdata_buf_len = 0;
00291 
00292 /*
00293     for (i = 0; i < pc->depth; i++)
00294         printf("  ");
00295     
00296     printf("%s", el);
00297     
00298     for (i = 0; attr[i]; i += 2) {
00299         printf(" %s='%s'", attr[i], attr[i + 1]);
00300     }
00301 
00302     printf("   curelem=%d\n", pc->curelem);
00303 */
00304 
00305     if (strcmp (el, "match") == 0) {
00306         if (pc->curelem != CURELEM_DEVICE
00307             && pc->curelem != CURELEM_MATCH) {
00308             HAL_ERROR (("%s:%d:%d: Element <match> can only be "
00309                     "inside <device> and <match>", 
00310                     pc->file, 
00311                     XML_GetCurrentLineNumber (pc->parser), 
00312                     XML_GetCurrentColumnNumber (pc->parser)));
00313             parsing_abort (pc);
00314         }
00315 
00316         pc->curelem = CURELEM_MATCH;
00317         /* don't bother checking if matching at lower depths failed */
00318         if (pc->match_ok) {
00319             if (!handle_match (pc, attr)) {
00320                 /* No match */
00321                 pc->match_depth_first_fail = pc->depth;
00322                 pc->match_ok = FALSE;
00323             }
00324         }
00325     } else if (strcmp (el, "merge") == 0) {
00326         if (pc->curelem != CURELEM_DEVICE
00327             && pc->curelem != CURELEM_MATCH) {
00328             HAL_ERROR (("%s:%d:%d: Element <merge> can only be "
00329                     "inside <device> and <match>", 
00330                     pc->file, 
00331                     XML_GetCurrentLineNumber (pc->parser), 
00332                     XML_GetCurrentColumnNumber (pc->parser)));
00333             parsing_abort (pc);
00334         }
00335 
00336         pc->curelem = CURELEM_MERGE;
00337         if (pc->match_ok) {
00338             handle_merge (pc, attr);
00339         } else {
00340             /*HAL_INFO(("No merge!")); */
00341         }
00342     } else if (strcmp (el, "device") == 0) {
00343         if (pc->curelem != CURELEM_DEVICE_INFO) {
00344             HAL_ERROR (("%s:%d:%d: Element <device> can only be "
00345                     "inside <deviceinfo>", 
00346                     pc->file, 
00347                     XML_GetCurrentLineNumber (pc->parser), 
00348                     XML_GetCurrentColumnNumber (pc->parser)));
00349             parsing_abort (pc);
00350         }
00351         pc->curelem = CURELEM_DEVICE;
00352     } else if (strcmp (el, "deviceinfo") == 0) {
00353         if (pc->curelem != CURELEM_UNKNOWN) {
00354             HAL_ERROR (("%s:%d:%d: Element <deviceinfo> must be "
00355                     "a top-level element", 
00356                     pc->file, 
00357                     XML_GetCurrentLineNumber (pc->parser), 
00358                     XML_GetCurrentColumnNumber (pc->parser)));
00359             parsing_abort (pc);
00360         }
00361         pc->curelem = CURELEM_DEVICE_INFO;
00362     } else {
00363         HAL_ERROR (("%s:%d:%d: Unknown element <%s>",
00364                 pc->file,
00365                 XML_GetCurrentLineNumber (pc->parser),
00366                 XML_GetCurrentColumnNumber (pc->parser), el));
00367         parsing_abort (pc);
00368     }
00369 
00370     /* Nasty hack */
00371     assert (pc->depth < MAX_DEPTH);
00372 
00373     pc->depth++;
00374 
00375     /* store depth */
00376     pc->curelem_stack[pc->depth] = pc->curelem;
00377 
00378 }
00379 
00385 static void
00386 end (ParsingContext * pc, const char *el)
00387 {
00388     if (pc->aborted)
00389         return;
00390 
00391     pc->cdata_buf[pc->cdata_buf_len] = '\0';
00392 
00393 /*    printf("   curelem=%d\n", pc->curelem);*/
00394 
00395     if (pc->curelem == CURELEM_MERGE && pc->match_ok) {
00396         /* As soon as we are merging, we have matched the device... */
00397         pc->device_matched = TRUE;
00398 
00399         switch (pc->merge_type) {
00400         case DBUS_TYPE_STRING:
00401             ds_property_set_string (pc->device, pc->merge_key,
00402                         pc->cdata_buf);
00403             break;
00404 
00405 
00406         case DBUS_TYPE_INT32:
00407             {
00408                 dbus_int32_t value;
00409 
00410                 /* match integer property */
00411                 value = strtol (pc->cdata_buf, NULL, 0);
00412 
00415                 ds_property_set_int (pc->device,
00416                              pc->merge_key, value);
00417                 break;
00418             }
00419 
00420         case DBUS_TYPE_BOOLEAN:
00421             ds_property_set_bool (pc->device, pc->merge_key,
00422                           (strcmp (pc->cdata_buf,
00423                                "true") == 0) 
00424                           ? TRUE : FALSE);
00425             break;
00426 
00427         case DBUS_TYPE_DOUBLE:
00428             ds_property_set_double (pc->device, pc->merge_key,
00429                         atof (pc->cdata_buf));
00430             break;
00431 
00432         default:
00433             HAL_ERROR (("Unknown merge_type=%d='%c'",
00434                     pc->merge_type, pc->merge_type));
00435             break;
00436         }
00437     }
00438 
00439 
00440     pc->cdata_buf_len = 0;
00441     pc->depth--;
00442 
00443     /* maintain curelem */
00444     pc->curelem = pc->curelem_stack[pc->depth];
00445 
00446     /* maintain pc->match_ok */
00447     if (pc->depth < pc->match_depth_first_fail)
00448         pc->match_ok = TRUE;
00449 }
00450 
00457 static void
00458 cdata (ParsingContext * pc, const char *s, int len)
00459 {
00460     int bytes_left;
00461     int bytes_to_copy;
00462 
00463     if (pc->aborted)
00464         return;
00465 
00466     bytes_left = CDATA_BUF_SIZE - pc->cdata_buf_len;
00467     if (len > bytes_left) {
00468         HAL_ERROR (("CDATA in element larger than %d",
00469                 CDATA_BUF_SIZE));
00470     }
00471 
00472     bytes_to_copy = len;
00473     if (bytes_to_copy > bytes_left)
00474         bytes_to_copy = bytes_left;
00475 
00476     if (bytes_to_copy > 0)
00477         memcpy (pc->cdata_buf + pc->cdata_buf_len, s,
00478             bytes_to_copy);
00479 
00480     pc->cdata_buf_len += bytes_to_copy;
00481 }
00482 
00483 
00492 static dbus_bool_t
00493 process_fdi_file (const char *dir, const char *filename,
00494           HalDevice * device)
00495 {
00496     int rc;
00497     char buf[512];
00498     FILE *file;
00499     int filesize;
00500     char *filebuf;
00501     dbus_bool_t device_matched;
00502     XML_Parser parser;
00503     ParsingContext *parsing_context;
00504 
00505     snprintf (buf, 511, "%s/%s", dir, filename);
00506 
00507     /*HAL_INFO(("analysing file %s", buf)); */
00508 
00509     /* open file and read it into a buffer; it's a small file... */
00510     file = fopen (buf, "r");
00511     if (file == NULL) {
00512         perror ("fopen");
00513         return FALSE;
00514     }
00515 
00516     fseek (file, 0L, SEEK_END);
00517     filesize = (int) ftell (file);
00518     rewind (file);
00519     filebuf = (char *) malloc (filesize);
00520     if (filebuf == NULL) {
00521         perror ("malloc");
00522         fclose (file);
00523         return FALSE;
00524     }
00525     fread (filebuf, sizeof (char), filesize, file);
00526 
00527 
00528     /* ok, now parse the file (should probably reuse parser and say we are
00529      * not thread safe 
00530      */
00531     parser = XML_ParserCreate (NULL);
00532 
00533     /* initialize parsing context */
00534     parsing_context =
00535         (ParsingContext *) malloc (sizeof (ParsingContext));
00536     if (parsing_context == NULL) {
00537         perror ("malloc");
00538         return FALSE;
00539     }
00540     parsing_context->depth = 0;
00541     parsing_context->device_matched = FALSE;
00542     parsing_context->match_ok = TRUE;
00543     parsing_context->curelem = CURELEM_UNKNOWN;
00544     parsing_context->aborted = FALSE;
00545     parsing_context->file = buf;
00546     parsing_context->parser = parser;
00547     parsing_context->device = device;
00548 
00549 
00550     XML_SetElementHandler (parser,
00551                    (XML_StartElementHandler) start,
00552                    (XML_EndElementHandler) end);
00553     XML_SetCharacterDataHandler (parser,
00554                      (XML_CharacterDataHandler) cdata);
00555     XML_SetUserData (parser, parsing_context);
00556 
00557     rc = XML_Parse (parser, filebuf, filesize, 1);
00558     /*printf("XML_Parse rc=%d\r\n", rc); */
00559 
00560     if (rc == 0) {
00561         /* error parsing document */
00562         HAL_ERROR (("Error parsing XML document %s at line %d, "
00563                 "column %d : %s", 
00564                 buf, 
00565                 XML_GetCurrentLineNumber (parser), 
00566                 XML_GetCurrentColumnNumber (parser), 
00567                 XML_ErrorString (XML_GetErrorCode (parser))));
00568         device_matched = FALSE;
00569     } else {
00570         /* document parsed ok */
00571         device_matched = parsing_context->device_matched;
00572     }
00573 
00574     free (filebuf);
00575     fclose (file);
00576     free (parsing_context);
00577 
00578     return device_matched;
00579 }
00580 
00581 
00582 
00589 static int
00590 scan_fdi_files (const char *dir, HalDevice * d)
00591 {
00592     int i;
00593     int num_entries;
00594     dbus_bool_t found_fdi_file;
00595     struct dirent **name_list;
00596 
00597     found_fdi_file = 0;
00598 
00599     /*HAL_INFO(("scan_fdi_files: Processing dir '%s'", dir)); */
00600 
00601     num_entries = scandir (dir, &name_list, 0, alphasort);
00602     if (num_entries == -1) {
00603         perror ("scandir");
00604         return FALSE;
00605     }
00606 
00607     for (i = num_entries - 1; i >= 0; i--) {
00608         int len;
00609         char *filename;
00610 
00611         filename = name_list[i]->d_name;
00612         len = strlen (filename);
00613 
00614         if (name_list[i]->d_type == DT_REG) {
00615             /* regular file */
00616 
00617             if (len >= 5 &&
00618                 filename[len - 4] == '.' &&
00619                 filename[len - 3] == 'f' &&
00620                 filename[len - 2] == 'd' &&
00621                 filename[len - 1] == 'i') {
00622                 HAL_INFO (("scan_fdi_files: Processing "
00623                        "file '%s'", filename));
00624                 found_fdi_file =
00625                     process_fdi_file (dir, filename, d);
00626                 if (found_fdi_file) {
00627                     HAL_INFO (("*** Matched file %s/%s", 
00628                            dir, filename));
00629                     break;
00630                 }
00631             }
00632 
00633         } else if (name_list[i]->d_type == DT_DIR &&
00634                strcmp (filename, ".") != 0
00635                && strcmp (filename, "..") != 0) {
00636             int num_bytes;
00637             char *dirname;
00638 
00639             /* Directory; do the recursion thingy but not 
00640              * for . and ..
00641              */
00642 
00643             num_bytes = len + strlen (dir) + 1 + 1;
00644             dirname = (char *) malloc (num_bytes);
00645             if (dirname == NULL) {
00646                 HAL_ERROR (("couldn't allocated %d bytes",
00647                         num_bytes));
00648                 break;
00649             }
00650 
00651             snprintf (dirname, num_bytes, "%s/%s", dir,
00652                   filename);
00653             found_fdi_file = scan_fdi_files (dirname, d);
00654             free (dirname);
00655             if (found_fdi_file)
00656                 break;
00657         }
00658 
00659         free (name_list[i]);
00660     }
00661 
00662     for (; i >= 0; i--) {
00663         free (name_list[i]);
00664     }
00665 
00666     free (name_list);
00667 
00668     return found_fdi_file;
00669 }
00670 
00671 
00678 dbus_bool_t
00679 di_search_and_merge (HalDevice * d)
00680 {
00681     return scan_fdi_files (PACKAGE_DATA_DIR "/hal/fdi", d);
00682 }
00683 

Generated on Thu Mar 11 21:32:21 2004 for HAL by doxygen 1.3.6-20040222