00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
00153
00154 value = attr[3];
00155
00156
00157
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
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
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
00243 pc->merge_type = DBUS_TYPE_STRING;
00244 return;
00245 } else if (strcmp (attr[3], "bool") == 0) {
00246
00247 pc->merge_type = DBUS_TYPE_BOOLEAN;
00248 return;
00249 } else if (strcmp (attr[3], "int") == 0) {
00250
00251 pc->merge_type = DBUS_TYPE_INT32;
00252 return;
00253 } else if (strcmp (attr[3], "double") == 0) {
00254
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
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
00290
00291
00292
00293
00294
00295
00296
00297
00298
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
00314 if (pc->match_ok) {
00315 if (!handle_match (pc, attr)) {
00316
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
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
00367 assert (pc->depth < MAX_DEPTH);
00368
00369 pc->depth++;
00370
00371
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
00390
00391 if (pc->curelem == CURELEM_MERGE && pc->match_ok) {
00392
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
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
00440 pc->curelem = pc->curelem_stack[pc->depth];
00441
00442
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
00504
00505
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
00525
00526
00527 parser = XML_ParserCreate (NULL);
00528
00529
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
00555
00556 if (rc == 0) {
00557
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
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
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
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
00636
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