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

device_info.c

00001 /***************************************************************************
00002  * CVSID: $Id: device_info.c,v 1.8 2004/04/03 07:46:33 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 "logger.h"
00040 #include "device_info.h"
00041 
00051 #define MAX_DEPTH 32
00052 
00054 #define CDATA_BUF_SIZE  1024
00055 
00057 #define MAX_KEY_SIZE 128
00058 
00060 enum {
00062     CURELEM_UNKNOWN = -1,
00063 
00065     CURELEM_DEVICE_INFO = 0,
00066 
00068     CURELEM_DEVICE = 1,
00069 
00071     CURELEM_MATCH = 2,
00072 
00074     CURELEM_MERGE = 3,
00075 };
00076 
00079 typedef struct {
00081     char *file;
00082 
00084     XML_Parser parser;
00085 
00087     HalDevice *device;
00088 
00090     char cdata_buf[CDATA_BUF_SIZE];
00091 
00093     int cdata_buf_len;
00094     
00096     int depth;
00097 
00099     int curelem;
00100 
00102     int curelem_stack[MAX_DEPTH];
00103 
00105     dbus_bool_t aborted;
00106 
00107 
00109     int match_depth_first_fail;
00110 
00112     dbus_bool_t match_ok;
00113 
00114 
00115 
00117     char merge_key[MAX_KEY_SIZE];
00118 
00120     int merge_type;
00121 
00123     dbus_bool_t device_matched;
00124 
00125 } ParsingContext;
00126 
00134 static dbus_bool_t
00135 handle_match (ParsingContext * pc, const char **attr)
00136 {
00137     const char *key;
00138     int num_attrib;
00139 
00140     for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++);
00141 
00142     if (num_attrib != 4)
00143         return FALSE;
00144 
00145     if (strcmp (attr[0], "key") != 0)
00146         return FALSE;
00147     key = attr[1];
00148 
00149     if (strcmp (attr[2], "string") == 0) {
00150         const char *value;
00151 
00152         /* match string property */
00153 
00154         value = attr[3];
00155 
00156         /*HAL_INFO(("Checking that key='%s' is a string that "
00157           "equals '%s'", key, value)); */
00158 
00159         if (hal_device_property_get_type (pc->device, key) != DBUS_TYPE_STRING)
00160             return FALSE;
00161 
00162         if (strcmp (hal_device_property_get_string (pc->device, key),
00163                 value) != 0)
00164             return FALSE;
00165 
00166         HAL_INFO (("*** string match for key %s", key));
00167         return TRUE;
00168     } else if (strcmp (attr[2], "int") == 0) {
00169         dbus_int32_t value;
00170 
00171         /* match integer property */
00172         value = strtol (attr[3], NULL, 0);
00173         
00176         HAL_INFO (("Checking that key='%s' is a int that equals %d", 
00177                key, value));
00178 
00179         if (hal_device_property_get_type (pc->device, key) != DBUS_TYPE_INT32)
00180             return FALSE;
00181 
00182         if (hal_device_property_get_int (pc->device, key) != value) {
00183             return FALSE;
00184         }
00185 
00186         return TRUE;
00187     } else if (strcmp (attr[2], "bool") == 0) {
00188         dbus_bool_t value;
00189 
00190         /* match string property */
00191 
00192         if (strcmp (attr[3], "false") == 0)
00193             value = FALSE;
00194         else if (strcmp (attr[3], "true") == 0)
00195             value = TRUE;
00196         else
00197             return FALSE;
00198 
00199         HAL_INFO (("Checking that key='%s' is a bool that equals %s", 
00200                key, value ? "TRUE" : "FALSE"));
00201 
00202         if (hal_device_property_get_type (pc->device, key) != 
00203             DBUS_TYPE_BOOLEAN)
00204             return FALSE;
00205 
00206         if (hal_device_property_get_bool (pc->device, key) != value)
00207             return FALSE;
00208 
00209         HAL_INFO (("*** bool match for key %s", key));
00210         return TRUE;
00211     }
00212 
00213     return FALSE;
00214 }
00215 
00216 
00222 static void
00223 handle_merge (ParsingContext * pc, const char **attr)
00224 {
00225     int num_attrib;
00226 
00227     for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
00228         ;
00229     }
00230 
00231     if (num_attrib != 4)
00232         return;
00233 
00234     if (strcmp (attr[0], "key") != 0)
00235         return;
00236     strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
00237 
00238     if (strcmp (attr[2], "type") != 0)
00239         return;
00240 
00241     if (strcmp (attr[3], "string") == 0) {
00242         /* match string property */
00243         pc->merge_type = DBUS_TYPE_STRING;
00244         return;
00245     } else if (strcmp (attr[3], "bool") == 0) {
00246         /* match string property */
00247         pc->merge_type = DBUS_TYPE_BOOLEAN;
00248         return;
00249     } else if (strcmp (attr[3], "int") == 0) {
00250         /* match string property */
00251         pc->merge_type = DBUS_TYPE_INT32;
00252         return;
00253     } else if (strcmp (attr[3], "double") == 0) {
00254         /* match string property */
00255         pc->merge_type = DBUS_TYPE_DOUBLE;
00256         return;
00257     }
00258 
00259     return;
00260 }
00261 
00266 static void
00267 parsing_abort (ParsingContext * pc)
00268 {
00269     /* Grr, expat can't abort parsing */
00270     HAL_ERROR (("Aborting parsing of document"));
00271     pc->aborted = TRUE;
00272 }
00273 
00280 static void
00281 start (ParsingContext * pc, const char *el, const char **attr)
00282 {
00283     if (pc->aborted)
00284         return;
00285 
00286     pc->cdata_buf_len = 0;
00287 
00288 /*
00289     for (i = 0; i < pc->depth; i++)
00290         printf("  ");
00291     
00292     printf("%s", el);
00293     
00294     for (i = 0; attr[i]; i += 2) {
00295         printf(" %s='%s'", attr[i], attr[i + 1]);
00296     }
00297 
00298     printf("   curelem=%d\n", pc->curelem);
00299 */
00300 
00301     if (strcmp (el, "match") == 0) {
00302         if (pc->curelem != CURELEM_DEVICE
00303             && pc->curelem != CURELEM_MATCH) {
00304             HAL_ERROR (("%s:%d:%d: Element <match> can only be "
00305                     "inside <device> and <match>", 
00306                     pc->file, 
00307                     XML_GetCurrentLineNumber (pc->parser), 
00308                     XML_GetCurrentColumnNumber (pc->parser)));
00309             parsing_abort (pc);
00310         }
00311 
00312         pc->curelem = CURELEM_MATCH;
00313         /* don't bother checking if matching at lower depths failed */
00314         if (pc->match_ok) {
00315             if (!handle_match (pc, attr)) {
00316                 /* No match */
00317                 pc->match_depth_first_fail = pc->depth;
00318                 pc->match_ok = FALSE;
00319             }
00320         }
00321     } else if (strcmp (el, "merge") == 0) {
00322         if (pc->curelem != CURELEM_DEVICE
00323             && pc->curelem != CURELEM_MATCH) {
00324             HAL_ERROR (("%s:%d:%d: Element <merge> can only be "
00325                     "inside <device> and <match>", 
00326                     pc->file, 
00327                     XML_GetCurrentLineNumber (pc->parser), 
00328                     XML_GetCurrentColumnNumber (pc->parser)));
00329             parsing_abort (pc);
00330         }
00331 
00332         pc->curelem = CURELEM_MERGE;
00333         if (pc->match_ok) {
00334             handle_merge (pc, attr);
00335         } else {
00336             /*HAL_INFO(("No merge!")); */
00337         }
00338     } else if (strcmp (el, "device") == 0) {
00339         if (pc->curelem != CURELEM_DEVICE_INFO) {
00340             HAL_ERROR (("%s:%d:%d: Element <device> can only be "
00341                     "inside <deviceinfo>", 
00342                     pc->file, 
00343                     XML_GetCurrentLineNumber (pc->parser), 
00344                     XML_GetCurrentColumnNumber (pc->parser)));
00345             parsing_abort (pc);
00346         }
00347         pc->curelem = CURELEM_DEVICE;
00348     } else if (strcmp (el, "deviceinfo") == 0) {
00349         if (pc->curelem != CURELEM_UNKNOWN) {
00350             HAL_ERROR (("%s:%d:%d: Element <deviceinfo> must be "
00351                     "a top-level element", 
00352                     pc->file, 
00353                     XML_GetCurrentLineNumber (pc->parser), 
00354                     XML_GetCurrentColumnNumber (pc->parser)));
00355             parsing_abort (pc);
00356         }
00357         pc->curelem = CURELEM_DEVICE_INFO;
00358     } else {
00359         HAL_ERROR (("%s:%d:%d: Unknown element <%s>",
00360                 pc->file,
00361                 XML_GetCurrentLineNumber (pc->parser),
00362                 XML_GetCurrentColumnNumber (pc->parser), el));
00363         parsing_abort (pc);
00364     }
00365 
00366     /* Nasty hack */
00367     assert (pc->depth < MAX_DEPTH);
00368 
00369     pc->depth++;
00370 
00371     /* store depth */
00372     pc->curelem_stack[pc->depth] = pc->curelem;
00373 
00374 }
00375 
00381 static void
00382 end (ParsingContext * pc, const char *el)
00383 {
00384     if (pc->aborted)
00385         return;
00386 
00387     pc->cdata_buf[pc->cdata_buf_len] = '\0';
00388 
00389 /*    printf("   curelem=%d\n", pc->curelem);*/
00390 
00391     if (pc->curelem == CURELEM_MERGE && pc->match_ok) {
00392         /* As soon as we are merging, we have matched the device... */
00393         pc->device_matched = TRUE;
00394 
00395         switch (pc->merge_type) {
00396         case DBUS_TYPE_STRING:
00397             hal_device_property_set_string (pc->device, pc->merge_key,
00398                         pc->cdata_buf);
00399             break;
00400 
00401 
00402         case DBUS_TYPE_INT32:
00403             {
00404                 dbus_int32_t value;
00405 
00406                 /* match integer property */
00407                 value = strtol (pc->cdata_buf, NULL, 0);
00408 
00411                 hal_device_property_set_int (pc->device,
00412                              pc->merge_key, value);
00413                 break;
00414             }
00415 
00416         case DBUS_TYPE_BOOLEAN:
00417             hal_device_property_set_bool (pc->device, pc->merge_key,
00418                           (strcmp (pc->cdata_buf,
00419                                "true") == 0) 
00420                           ? TRUE : FALSE);
00421             break;
00422 
00423         case DBUS_TYPE_DOUBLE:
00424             hal_device_property_set_double (pc->device, pc->merge_key,
00425                         atof (pc->cdata_buf));
00426             break;
00427 
00428         default:
00429             HAL_ERROR (("Unknown merge_type=%d='%c'",
00430                     pc->merge_type, pc->merge_type));
00431             break;
00432         }
00433     }
00434 
00435 
00436     pc->cdata_buf_len = 0;
00437     pc->depth--;
00438 
00439     /* maintain curelem */
00440     pc->curelem = pc->curelem_stack[pc->depth];
00441 
00442     /* maintain pc->match_ok */
00443     if (pc->depth < pc->match_depth_first_fail)
00444         pc->match_ok = TRUE;
00445 }
00446 
00453 static void
00454 cdata (ParsingContext * pc, const char *s, int len)
00455 {
00456     int bytes_left;
00457     int bytes_to_copy;
00458 
00459     if (pc->aborted)
00460         return;
00461 
00462     bytes_left = CDATA_BUF_SIZE - pc->cdata_buf_len;
00463     if (len > bytes_left) {
00464         HAL_ERROR (("CDATA in element larger than %d",
00465                 CDATA_BUF_SIZE));
00466     }
00467 
00468     bytes_to_copy = len;
00469     if (bytes_to_copy > bytes_left)
00470         bytes_to_copy = bytes_left;
00471 
00472     if (bytes_to_copy > 0)
00473         memcpy (pc->cdata_buf + pc->cdata_buf_len, s,
00474             bytes_to_copy);
00475 
00476     pc->cdata_buf_len += bytes_to_copy;
00477 }
00478 
00479 
00488 static dbus_bool_t
00489 process_fdi_file (const char *dir, const char *filename,
00490           HalDevice * device)
00491 {
00492     int rc;
00493     char buf[512];
00494     FILE *file;
00495     int filesize;
00496     char *filebuf;
00497     dbus_bool_t device_matched;
00498     XML_Parser parser;
00499     ParsingContext *parsing_context;
00500 
00501     snprintf (buf, 511, "%s/%s", dir, filename);
00502 
00503     /*HAL_INFO(("analysing file %s", buf)); */
00504 
00505     /* open file and read it into a buffer; it's a small file... */
00506     file = fopen (buf, "r");
00507     if (file == NULL) {
00508         perror ("fopen");
00509         return FALSE;
00510     }
00511 
00512     fseek (file, 0L, SEEK_END);
00513     filesize = (int) ftell (file);
00514     rewind (file);
00515     filebuf = (char *) malloc (filesize);
00516     if (filebuf == NULL) {
00517         perror ("malloc");
00518         fclose (file);
00519         return FALSE;
00520     }
00521     fread (filebuf, sizeof (char), filesize, file);
00522 
00523 
00524     /* ok, now parse the file (should probably reuse parser and say we are
00525      * not thread safe 
00526      */
00527     parser = XML_ParserCreate (NULL);
00528 
00529     /* initialize parsing context */
00530     parsing_context =
00531         (ParsingContext *) malloc (sizeof (ParsingContext));
00532     if (parsing_context == NULL) {
00533         perror ("malloc");
00534         return FALSE;
00535     }
00536     parsing_context->depth = 0;
00537     parsing_context->device_matched = FALSE;
00538     parsing_context->match_ok = TRUE;
00539     parsing_context->curelem = CURELEM_UNKNOWN;
00540     parsing_context->aborted = FALSE;
00541     parsing_context->file = buf;
00542     parsing_context->parser = parser;
00543     parsing_context->device = device;
00544 
00545 
00546     XML_SetElementHandler (parser,
00547                    (XML_StartElementHandler) start,
00548                    (XML_EndElementHandler) end);
00549     XML_SetCharacterDataHandler (parser,
00550                      (XML_CharacterDataHandler) cdata);
00551     XML_SetUserData (parser, parsing_context);
00552 
00553     rc = XML_Parse (parser, filebuf, filesize, 1);
00554     /*printf("XML_Parse rc=%d\r\n", rc); */
00555 
00556     if (rc == 0) {
00557         /* error parsing document */
00558         HAL_ERROR (("Error parsing XML document %s at line %d, "
00559                 "column %d : %s", 
00560                 buf, 
00561                 XML_GetCurrentLineNumber (parser), 
00562                 XML_GetCurrentColumnNumber (parser), 
00563                 XML_ErrorString (XML_GetErrorCode (parser))));
00564         device_matched = FALSE;
00565     } else {
00566         /* document parsed ok */
00567         device_matched = parsing_context->device_matched;
00568     }
00569 
00570     free (filebuf);
00571     fclose (file);
00572     free (parsing_context);
00573 
00574     return device_matched;
00575 }
00576 
00577 
00578 
00585 static int
00586 scan_fdi_files (const char *dir, HalDevice * d)
00587 {
00588     int i;
00589     int num_entries;
00590     dbus_bool_t found_fdi_file;
00591     struct dirent **name_list;
00592 
00593     found_fdi_file = 0;
00594 
00595     /*HAL_INFO(("scan_fdi_files: Processing dir '%s'", dir)); */
00596 
00597     num_entries = scandir (dir, &name_list, 0, alphasort);
00598     if (num_entries == -1) {
00599         perror ("scandir");
00600         return FALSE;
00601     }
00602 
00603     for (i = num_entries - 1; i >= 0; i--) {
00604         int len;
00605         char *filename;
00606 
00607         filename = name_list[i]->d_name;
00608         len = strlen (filename);
00609 
00610         if (name_list[i]->d_type == DT_REG) {
00611             /* regular file */
00612 
00613             if (len >= 5 &&
00614                 filename[len - 4] == '.' &&
00615                 filename[len - 3] == 'f' &&
00616                 filename[len - 2] == 'd' &&
00617                 filename[len - 1] == 'i') {
00618                 HAL_INFO (("scan_fdi_files: Processing "
00619                        "file '%s'", filename));
00620                 found_fdi_file =
00621                     process_fdi_file (dir, filename, d);
00622                 if (found_fdi_file) {
00623                     HAL_INFO (("*** Matched file %s/%s", 
00624                            dir, filename));
00625                     break;
00626                 }
00627             }
00628 
00629         } else if (name_list[i]->d_type == DT_DIR &&
00630                strcmp (filename, ".") != 0
00631                && strcmp (filename, "..") != 0) {
00632             int num_bytes;
00633             char *dirname;
00634 
00635             /* Directory; do the recursion thingy but not 
00636              * for . and ..
00637              */
00638 
00639             num_bytes = len + strlen (dir) + 1 + 1;
00640             dirname = (char *) malloc (num_bytes);
00641             if (dirname == NULL) {
00642                 HAL_ERROR (("couldn't allocated %d bytes",
00643                         num_bytes));
00644                 break;
00645             }
00646 
00647             snprintf (dirname, num_bytes, "%s/%s", dir,
00648                   filename);
00649             found_fdi_file = scan_fdi_files (dirname, d);
00650             free (dirname);
00651             if (found_fdi_file)
00652                 break;
00653         }
00654 
00655         free (name_list[i]);
00656     }
00657 
00658     for (; i >= 0; i--) {
00659         free (name_list[i]);
00660     }
00661 
00662     free (name_list);
00663 
00664     return found_fdi_file;
00665 }
00666 
00667 
00674 dbus_bool_t
00675 di_search_and_merge (HalDevice *d)
00676 {
00677     return scan_fdi_files (PACKAGE_DATA_DIR "/hal/fdi", d);
00678 }
00679 

Generated on Sat Apr 24 19:57:44 2004 for HAL by doxygen 1.3.6-20040222