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

device_info.c

00001 /***************************************************************************
00002  * CVSID: $Id: device_info.c,v 1.4 2003/12/23 22:29:59 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
00063 {
00065     CURELEM_UNKNOWN  = -1,
00066 
00068     CURELEM_DEVICE_INFO  = 0,
00069 
00071     CURELEM_DEVICE  = 1,
00072 
00074     CURELEM_MATCH  = 2,
00075 
00077     CURELEM_MERGE  = 3,
00078 };
00079 
00082 typedef struct
00083 {
00085     char* file;
00086 
00088     XML_Parser parser;
00089 
00091     HalDevice* device;
00092 
00094     char cdata_buf[CDATA_BUF_SIZE];
00095 
00097     int cdata_buf_len;
00098     
00100     int depth;
00101 
00103     int curelem;
00104 
00106     int curelem_stack[MAX_DEPTH];
00107 
00109     dbus_bool_t aborted;
00110 
00111 
00113     int match_depth_first_fail;
00114 
00116     dbus_bool_t match_ok;
00117 
00118 
00119 
00121     char merge_key[MAX_KEY_SIZE];
00122 
00124     int merge_type;
00125 
00127     dbus_bool_t device_matched;
00128 
00129 } ParsingContext;
00130 
00138 static dbus_bool_t handle_match(ParsingContext* pc, const char** attr)
00139 {
00140     const char* key;
00141     int num_attrib;
00142 
00143     for(num_attrib=0; attr[num_attrib]!=NULL; num_attrib++)
00144         ;
00145 
00146     if( num_attrib!=4 )
00147         return FALSE;
00148 
00149     if( strcmp(attr[0], "key")!=0 )
00150         return FALSE;
00151     key = attr[1];
00152 
00153     if( strcmp(attr[2], "string")==0 )
00154     {
00155         const char* value;
00156 
00157         /* match string property */
00158 
00159         value = attr[3];
00160 
00161         /*HAL_INFO(("Checking that key='%s' is a string that equals '%s'",
00162           key, value));*/
00163 
00164         if( ds_property_get_type(pc->device, key)!=DBUS_TYPE_STRING )
00165             return FALSE;
00166 
00167         if( strcmp(ds_property_get_string(pc->device, key), value)!=0 )
00168             return FALSE;
00169 
00170         HAL_INFO(("*** string match for key %s", key));
00171         return TRUE;
00172     }
00173     else if( strcmp(attr[2], "int")==0 )
00174     {
00175         dbus_int32_t value;
00176 
00177         /* match integer property */
00178         value = strtol(attr[3], NULL, 0);
00179 
00182         HAL_INFO(("Checking that key='%s' is a int that equals %d",
00183                   key, value));
00184 
00185         if( ds_property_get_type(pc->device, key)!=DBUS_TYPE_INT32 )
00186             return FALSE;
00187 
00188         if( ds_property_get_int(pc->device, key)!=value )
00189         {
00190             return FALSE;
00191         }
00192 
00193         return TRUE;
00194     }
00195     else if( strcmp(attr[2], "bool")==0 )
00196     {
00197         dbus_bool_t value;
00198 
00199         /* match string property */
00200 
00201         if( strcmp(attr[3], "false")==0 )
00202             value=FALSE;
00203         else if( strcmp(attr[3], "true")==0 )
00204             value=TRUE;
00205         else
00206             return FALSE;
00207 
00208         HAL_INFO(("Checking that key='%s' is a bool that equals %s",
00209                   key, value ? "TRUE" : "FALSE"));
00210 
00211         if( ds_property_get_type(pc->device, key)!=DBUS_TYPE_BOOLEAN )
00212             return FALSE;
00213 
00214         if( ds_property_get_bool(pc->device, key)!=value )
00215             return FALSE;
00216 
00217         HAL_INFO(("*** bool match for key %s", key));
00218         return TRUE;
00219     }
00220 
00221     return FALSE;
00222 }
00223 
00224 
00230 static void handle_merge(ParsingContext* pc, const char** attr)
00231 {
00232     int num_attrib;
00233 
00234     for(num_attrib=0; attr[num_attrib]!=NULL; num_attrib++)
00235         ;
00236 
00237     if( num_attrib!=4 )
00238         return;
00239 
00240     if( strcmp(attr[0], "key")!=0 )
00241         return;
00242     strncpy(pc->merge_key, attr[1], MAX_KEY_SIZE);
00243 
00244     if( strcmp(attr[2], "type")!=0 )
00245         return;
00246 
00247     if( strcmp(attr[3], "string")==0 )
00248     {
00249         /* match string property */
00250         pc->merge_type = DBUS_TYPE_STRING;
00251         return;
00252     }
00253     else if( strcmp(attr[3], "bool")==0 )
00254     {
00255         /* match string property */
00256         pc->merge_type = DBUS_TYPE_BOOLEAN;
00257         return;
00258     }
00259     else if( strcmp(attr[3], "int")==0 )
00260     {
00261         /* match string property */
00262         pc->merge_type = DBUS_TYPE_INT32;
00263         return;
00264     }
00265     else if( strcmp(attr[3], "double")==0 )
00266     {
00267         /* match string property */
00268         pc->merge_type = DBUS_TYPE_DOUBLE;
00269         return;
00270     }
00271     
00272     return;
00273 }
00274 
00279 static void parsing_abort(ParsingContext* pc)
00280 {
00281     /* Grr, expat can't abort parsing */
00282     HAL_ERROR(("Aborting parsing of document"));
00283     pc->aborted = TRUE;
00284 }
00285 
00292 static void start(ParsingContext *pc, const char *el, const char **attr)
00293 {
00294     int i;
00295 
00296     if( pc->aborted )
00297         return;
00298     
00299     pc->cdata_buf_len = 0;
00300     
00301 /*
00302     for (i = 0; i < pc->depth; i++)
00303         printf("  ");
00304     
00305     printf("%s", el);
00306     
00307     for (i = 0; attr[i]; i += 2) {
00308         printf(" %s='%s'", attr[i], attr[i + 1]);
00309     }
00310 
00311     printf("   curelem=%d\n", pc->curelem);
00312 */
00313     
00314     if( strcmp(el, "match")==0 )
00315     {
00316         if( pc->curelem!=CURELEM_DEVICE && pc->curelem!=CURELEM_MATCH )
00317         {
00318             HAL_ERROR(("%s:%d:%d: Element <match> can only be inside "
00319                        "<device> and <match>", 
00320                        pc->file, 
00321                        XML_GetCurrentLineNumber(pc->parser), 
00322                        XML_GetCurrentColumnNumber(pc->parser)));
00323             parsing_abort(pc);
00324         }
00325 
00326         pc->curelem = CURELEM_MATCH;
00327         /* don't bother checking if matching at lower depths failed */
00328         if( pc->match_ok )
00329         {
00330             if( !handle_match(pc, attr) )
00331             {
00332                 /* No match */
00333                 pc->match_depth_first_fail = pc->depth;
00334                 pc->match_ok = FALSE;
00335             }
00336         }
00337     }
00338     else if( strcmp(el, "merge")==0 )
00339     {
00340         if( pc->curelem!=CURELEM_DEVICE && pc->curelem!=CURELEM_MATCH )
00341         {
00342             HAL_ERROR(("%s:%d:%d: Element <merge> can only be inside "
00343                        "<device> and <match>", 
00344                        pc->file, 
00345                        XML_GetCurrentLineNumber(pc->parser), 
00346                        XML_GetCurrentColumnNumber(pc->parser)));
00347             parsing_abort(pc);
00348         }
00349 
00350         pc->curelem = CURELEM_MERGE;
00351         if( pc->match_ok )
00352         {
00353             handle_merge(pc, attr);
00354         }
00355         else
00356         {
00357             /*HAL_INFO(("No merge!"));*/
00358         }
00359     }
00360     else if( strcmp(el, "device")==0 )
00361     {
00362         if( pc->curelem!=CURELEM_DEVICE_INFO )
00363         {
00364             HAL_ERROR(("%s:%d:%d: Element <device> can only be inside "
00365                        "<deviceinfo>", 
00366                        pc->file, 
00367                        XML_GetCurrentLineNumber(pc->parser), 
00368                        XML_GetCurrentColumnNumber(pc->parser)));
00369             parsing_abort(pc);
00370         }
00371         pc->curelem = CURELEM_DEVICE;
00372     }
00373     else if( strcmp(el, "deviceinfo")==0 )
00374     {
00375         if( pc->curelem!=CURELEM_UNKNOWN )
00376         {
00377             HAL_ERROR(("%s:%d:%d: Element <deviceinfo> must be a top-level "
00378                        "element", 
00379                        pc->file, 
00380                        XML_GetCurrentLineNumber(pc->parser), 
00381                        XML_GetCurrentColumnNumber(pc->parser)));
00382             parsing_abort(pc);
00383         }
00384         pc->curelem = CURELEM_DEVICE_INFO;
00385     }
00386     else
00387     {
00388         HAL_ERROR(("%s:%d:%d: Unknown element <%s>",
00389                    pc->file, 
00390                    XML_GetCurrentLineNumber(pc->parser), 
00391                    XML_GetCurrentColumnNumber(pc->parser), el));
00392         parsing_abort(pc);
00393     }
00394     
00395     /* Nasty hack */
00396     assert( pc->depth<MAX_DEPTH );
00397 
00398     pc->depth++;        
00399 
00400     /* store depth */
00401     pc->curelem_stack[pc->depth] = pc->curelem;
00402 
00403 }
00404 
00410 static void end(ParsingContext* pc, const char* el)
00411 {
00412     if( pc->aborted )
00413         return;
00414 
00415     pc->cdata_buf[pc->cdata_buf_len]='\0';
00416 
00417 /*    printf("   curelem=%d\n", pc->curelem);*/
00418 
00419     if( pc->curelem==CURELEM_MERGE && pc->match_ok )
00420     {
00421         /* As soon as we are merging, we have matched the device... */
00422         pc->device_matched = TRUE;
00423 
00424         switch( pc->merge_type )
00425         {
00426         case DBUS_TYPE_STRING:
00427             ds_property_set_string(pc->device, pc->merge_key, pc->cdata_buf);
00428             break;
00429 
00430             
00431         case DBUS_TYPE_INT32:
00432         {
00433             dbus_int32_t value;
00434             
00435             /* match integer property */
00436             value = strtol(pc->cdata_buf, NULL, 0);
00437             
00440             ds_property_set_int(pc->device, pc->merge_key, value);
00441             break;
00442         }
00443 
00444         case DBUS_TYPE_BOOLEAN:
00445             ds_property_set_bool(
00446                 pc->device, pc->merge_key, 
00447                 (strcmp(pc->cdata_buf, "true")==0) ? TRUE : FALSE);
00448             break;
00449 
00450         case DBUS_TYPE_DOUBLE:
00451             ds_property_set_double(pc->device, pc->merge_key, 
00452                                    atof(pc->cdata_buf));
00453             break;
00454 
00455         default:
00456             HAL_ERROR(("Unknown merge_type=%d='%c'", 
00457                        pc->merge_type, pc->merge_type));
00458             break;
00459         }
00460     }
00461 
00462 
00463     pc->cdata_buf_len = 0;
00464     pc->depth--;
00465 
00466     /* maintain curelem */
00467     pc->curelem = pc->curelem_stack[pc->depth];
00468 
00469     /* maintain pc->match_ok */
00470     if( pc->depth < pc->match_depth_first_fail )
00471         pc->match_ok=TRUE;
00472 }
00473 
00480 static void cdata(ParsingContext *pc, const char* s, int len)
00481 {
00482     int bytes_left;
00483     int bytes_to_copy;
00484 
00485     if( pc->aborted )
00486         return;
00487 
00488     bytes_left = CDATA_BUF_SIZE - pc->cdata_buf_len;
00489     if( len>bytes_left )
00490     {
00491         HAL_ERROR(("CDATA in element larger than %d", CDATA_BUF_SIZE));
00492     }
00493 
00494     bytes_to_copy = len;
00495     if( bytes_to_copy>bytes_left )
00496         bytes_to_copy = bytes_left;
00497 
00498     if( bytes_to_copy>0 )
00499         memcpy(pc->cdata_buf + pc->cdata_buf_len, s, bytes_to_copy);
00500 
00501     pc->cdata_buf_len += bytes_to_copy;
00502 }
00503 
00504 
00513 static dbus_bool_t process_fdi_file(const char* dir, const char* filename, 
00514                                     HalDevice* device)
00515 {
00516     int rc;
00517     char buf[512];
00518     FILE* file;
00519     int filesize;
00520     char* filebuf;
00521     dbus_bool_t device_matched;
00522     XML_Parser parser;
00523     ParsingContext* parsing_context;
00524 
00525     snprintf(buf, 511, "%s/%s", dir, filename);
00526 
00527     /*HAL_INFO(("analysing file %s", buf));*/
00528 
00529     // open file and read it into a buffer; it's a small file...
00530     file = fopen(buf, "r");
00531     if( file==NULL )
00532     {
00533         perror("fopen");
00534         return FALSE;
00535     }
00536 
00537     fseek(file, 0L, SEEK_END);
00538     filesize = (int) ftell(file);
00539     rewind(file);
00540     filebuf = (char*) malloc(filesize);
00541     if( filebuf==NULL )
00542     {
00543         perror("malloc");
00544         fclose(file);
00545         return FALSE;
00546     }
00547     fread(filebuf, sizeof(char), filesize, file);
00548 
00549 
00550     // ok, now parse the file (should probably reuse parser and say we are
00551     // not thread safe)
00552     parser = XML_ParserCreate(NULL);
00553 
00554     // initialize parsing context
00555     parsing_context = (ParsingContext*) malloc(sizeof(ParsingContext));
00556     if( parsing_context==NULL )
00557     {
00558         perror("malloc");
00559         return FALSE;
00560     }
00561     parsing_context->depth = 0;
00562     parsing_context->device_matched = FALSE;
00563     parsing_context->match_ok = TRUE;
00564     parsing_context->curelem = CURELEM_UNKNOWN;
00565     parsing_context->aborted = FALSE;
00566     parsing_context->file = buf;
00567     parsing_context->parser = parser;
00568     parsing_context->device = device;
00569 
00570 
00571     XML_SetElementHandler(parser, 
00572                           (XML_StartElementHandler) start, 
00573                           (XML_EndElementHandler) end);
00574     XML_SetCharacterDataHandler(parser, 
00575                                 (XML_CharacterDataHandler) cdata);
00576     XML_SetUserData(parser, parsing_context);
00577 
00578     rc = XML_Parse(parser, filebuf, filesize, 1);
00579     /*printf("XML_Parse rc=%d\r\n", rc);*/
00580 
00581     if( rc==0 )
00582     {
00583         // error parsing document
00584         HAL_ERROR(("Error parsing XML document %s at line %d, column %d : %s",
00585                    buf,
00586                    XML_GetCurrentLineNumber(parser),
00587                    XML_GetCurrentColumnNumber(parser),
00588                    XML_ErrorString(XML_GetErrorCode(parser)) ));
00589         device_matched = FALSE;
00590     }
00591     else
00592     {
00593         // document parsed ok
00594         device_matched = parsing_context->device_matched;
00595     }
00596 
00597     free(filebuf);
00598     fclose(file);
00599     free(parsing_context);
00600 
00601     return device_matched;
00602 }
00603 
00604 
00605 
00612 static int scan_fdi_files(const char* dir, HalDevice* d)
00613 {
00614     int i;
00615     int num_entries;
00616     dbus_bool_t found_fdi_file;
00617     struct dirent **name_list;
00618 
00619     found_fdi_file = 0;
00620 
00621     /*HAL_INFO(("scan_fdi_files: Processing dir '%s'", dir));*/
00622 
00623     num_entries = scandir(dir, &name_list, 0, alphasort);
00624     if( num_entries==-1 )
00625     {
00626         perror("scandir");
00627         return FALSE;
00628     }
00629 
00630     for(i=num_entries-1; i>=0; i--)
00631     {
00632         int len;
00633         char* filename;
00634 
00635         filename = name_list[i]->d_name;
00636         len = strlen(filename);
00637 
00638         if( name_list[i]->d_type==DT_REG )
00639         {
00640             // regular file
00641 
00642             if( len>=5 && 
00643                 filename[len-4]=='.' &&
00644                 filename[len-3]=='f' &&
00645                 filename[len-2]=='d' &&
00646                 filename[len-1]=='i' )
00647             {        
00648                 HAL_INFO(("scan_fdi_files: Processing file '%s'", filename));
00649                 found_fdi_file = process_fdi_file(dir, filename, d);
00650                 if( found_fdi_file )
00651                 {
00652                     HAL_INFO(("*** Matched file %s/%s", dir, filename));
00653                     break;
00654                 }
00655             }
00656 
00657         }
00658         else if( name_list[i]->d_type==DT_DIR &&
00659                  strcmp(filename, ".")!=0 && strcmp(filename, "..")!=0 )
00660         {
00661             int num_bytes;
00662             char* dirname;
00663 
00664             // Directory; do the recursion thingy but not for . and ..
00665 
00666             num_bytes = len + strlen(dir) + 1 + 1;
00667             dirname = (char*) malloc(num_bytes);
00668             if( dirname==NULL )
00669             {
00670                 HAL_ERROR(("couldn't allocated %d bytes", num_bytes));
00671                 break;
00672             }
00673 
00674             snprintf(dirname, num_bytes, "%s/%s", dir, filename);
00675             found_fdi_file = scan_fdi_files(dirname, d);
00676             free(dirname);
00677             if( found_fdi_file )
00678                 break;
00679         }
00680 
00681         free(name_list[i]);
00682     }
00683 
00684     for( ;i>=0; i--)
00685     {
00686         free(name_list[i]);
00687     }
00688 
00689     free(name_list);
00690 
00691     return found_fdi_file;
00692 }
00693 
00694 
00701 dbus_bool_t di_search_and_merge(HalDevice* d)
00702 {
00703     return scan_fdi_files(PACKAGE_DATA_DIR "/hal/fdi", d);
00704 }
00705 

Generated on Sat Feb 7 22:11:46 2004 for HAL by doxygen 1.3.5