xref: /illumos-gate/usr/src/cmd/hal/hald/device_info.c (revision b14715fcd76b149c1fa0e1137cd516631ca5f295)
1 /***************************************************************************
2  * CVSID: $Id$
3  *
4  * device_store.c : Search for .fdi files and merge on match
5  *
6  * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
7  *
8  * Licensed under the Academic Free License version 2.1
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  *
24  **************************************************************************/
25 
26 #ifdef HAVE_CONFIG_H
27 #  include <config.h>
28 #endif
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <dirent.h>
34 #include <expat.h>
35 #include <assert.h>
36 #include <dbus/dbus.h>
37 #include <dbus/dbus-glib.h>
38 #include <math.h>
39 
40 #include "hald.h"
41 #include "logger.h"
42 #include "device_info.h"
43 #include "device_store.h"
44 #include "util.h"
45 
46 /**
47  * @defgroup DeviceInfo Device Info File Parsing
48  * @ingroup HalDaemon
49  * @brief Parsing of device info files
50  * @{
51  */
52 
53 
54 /** Maximum nesting depth */
55 #define MAX_DEPTH 32
56 
57 /** Maximum amount of CDATA */
58 #define CDATA_BUF_SIZE  1024
59 
60 /** Max length of property key */
61 #define MAX_KEY_SIZE 128
62 
63 /** Possible elements the parser can process */
64 enum {
65 	/** Not processing a known tag */
66 	CURELEM_UNKNOWN = -1,
67 
68 	/** Processing a deviceinfo element */
69 	CURELEM_DEVICE_INFO = 0,
70 
71 	/** Processing a device element */
72 	CURELEM_DEVICE = 1,
73 
74 	/** Processing a match element */
75 	CURELEM_MATCH = 2,
76 
77 	/** Processing a merge element */
78 	CURELEM_MERGE = 3,
79 
80 	/** Processing an append element */
81 	CURELEM_APPEND = 4,
82 
83 	/** Processing a prepend element */
84 	CURELEM_PREPEND = 5,
85 
86 	/** Processing a remove element */
87 	CURELEM_REMOVE = 6,
88 
89 	/** Processing a clear element */
90 	CURELEM_CLEAR = 7,
91 
92 	/** Processing a spawn element */
93 	CURELEM_SPAWN = 8
94 };
95 
96 /** What and how to merge */
97 enum {
98 	MERGE_TYPE_UNKNOWN       = 0,
99 	MERGE_TYPE_STRING        = 1,
100 	MERGE_TYPE_BOOLEAN       = 2,
101 	MERGE_TYPE_INT32         = 3,
102 	MERGE_TYPE_UINT64        = 4,
103 	MERGE_TYPE_DOUBLE        = 5,
104 	MERGE_TYPE_COPY_PROPERTY = 6,
105 	MERGE_TYPE_STRLIST       = 7,
106 	MERGE_TYPE_REMOVE        = 8,
107 	MERGE_TYPE_CLEAR         = 9,
108 	MERGE_TYPE_SPAWN         = 10
109 };
110 
111 /** Parsing Context
112  */
113 typedef struct {
114 	/** Name of file being parsed */
115 	char *file;
116 
117 	/** Parser object */
118 	XML_Parser parser;
119 
120 	/** Device we are trying to match*/
121 	HalDevice *device;
122 
123 	/** Buffer to put CDATA in */
124 	char cdata_buf[CDATA_BUF_SIZE];
125 
126 	/** Current length of CDATA buffer */
127 	int cdata_buf_len;
128 
129 	/** Current depth we are parsing at */
130 	int depth;
131 
132 	/** Element currently being processed */
133 	int curelem;
134 
135 	/** Stack of elements being processed */
136 	int curelem_stack[MAX_DEPTH];
137 
138 	/** #TRUE if parsing of document have been aborted */
139 	dbus_bool_t aborted;
140 
141 
142 	/** Depth of match-fail */
143 	int match_depth_first_fail;
144 
145 	/** #TRUE if all matches on prior depths have been OK */
146 	dbus_bool_t match_ok;
147 
148 
149 
150 	/** When merging, the key to store the value in */
151 	char merge_key[MAX_KEY_SIZE];
152 
153 	/** Type to merge*/
154 	int merge_type;
155 
156 	/** Set to #TRUE if a device is matched */
157 	dbus_bool_t device_matched;
158 
159 } ParsingContext;
160 
161 /** Resolve a udi-property path as used in .fdi files.
162  *
163  *  Examples of udi-property paths:
164  *
165  *   info.udi
166  *   /org/freedesktop/Hal/devices/computer:kernel.name
167  *   @block.storage_device:storage.bus
168  *   @block.storage_device:@storage.physical_device:ide.channel
169  *
170  *  @param  source_udi          UDI of source device
171  *  @param  path                The given path
172  *  @param  udi_result          Where to store the resulting UDI
173  *  @param  udi_result_size     Size of UDI string
174  *  @param  prop_result         Where to store the resulting property name
175  *  @param  prop_result_size    Size of property string
176  *  @return                     TRUE if and only if the path resolved.
177  */
178 static gboolean
179 resolve_udiprop_path (const char *path, const char *source_udi,
180 		      char *udi_result, size_t udi_result_size,
181 		      char *prop_result, size_t prop_result_size)
182 {
183 	int i;
184 	gchar **tokens = NULL;
185 	gboolean rc;
186 
187 	rc = FALSE;
188 
189 	/*HAL_INFO (("Looking at '%s' for udi='%s'", path, source_udi));*/
190 
191 	/* Split up path into ':' tokens */
192 	tokens = g_strsplit (path, ":", 64);
193 
194 	/* Detect trivial property access, e.g. path='foo.bar'   */
195 	if (tokens == NULL || tokens[0] == NULL || tokens[1] == NULL) {
196 		strncpy (udi_result, source_udi, udi_result_size);
197 		strncpy (prop_result, path, prop_result_size);
198 		rc = TRUE;
199 		goto out;
200 	}
201 
202 	/* Start with the source udi */
203 	strncpy (udi_result, source_udi, udi_result_size);
204 
205 	for (i = 0; tokens[i] != NULL; i++) {
206 		HalDevice *d;
207 		gchar *curtoken;
208 
209 		/*HAL_INFO (("tokens[%d] = '%s'", i, tokens[i]));*/
210 
211 		d = hal_device_store_find (hald_get_gdl (), udi_result);
212 		if (d == NULL)
213 			d = hal_device_store_find (hald_get_tdl (), udi_result);
214 		if (d == NULL)
215 			goto out;
216 
217 		curtoken = tokens[i];
218 
219 		/* process all but the last tokens as UDI paths */
220 		if (tokens[i+1] == NULL) {
221 			strncpy (prop_result, curtoken, prop_result_size);
222 			rc = TRUE;
223 			goto out;
224 		}
225 
226 
227 		/* Check for indirection */
228 		if (curtoken[0] == '@') {
229 			const char *udiprop;
230 			const char *newudi;
231 
232 			udiprop = curtoken + 1;
233 
234 			newudi = hal_device_property_get_string (d, udiprop);
235 			if (newudi == NULL)
236 				goto out;
237 
238 			/*HAL_INFO (("new_udi = '%s' (from indirection)", newudi));*/
239 
240 			strncpy (udi_result, newudi, udi_result_size);
241 		} else {
242 			/*HAL_INFO (("new_udi = '%s'", curtoken));*/
243 			strncpy (udi_result, curtoken, udi_result_size);
244 		}
245 
246 	}
247 
248 out:
249 
250 /*
251 	HAL_INFO (("success     = '%s'", rc ? "yes" : "no"));
252 	HAL_INFO (("udi_result  = '%s'", udi_result));
253 	HAL_INFO (("prop_result = '%s'", prop_result));
254 */
255 
256 	g_strfreev (tokens);
257 
258 	return rc;
259 }
260 
261 /* Compare the value of a property on a hal device object against a string value
262  * and return the result. Note that this works for several types, e.g. both strings
263  * and integers - in the latter case the given right side string will be interpreted
264  * as a number.
265  *
266  * The comparison might not make sense if you are comparing a property which is an integer
267  * against a string in which case this function returns FALSE. Also, if the property doesn't
268  * exist this function will also return FALSE.
269  *
270  * @param  d                    hal device object
271  * @param  key                  Key of the property to compare
272  * @param  right_side           Value to compare against
273  * @param  result               Pointer to where to store result
274  * @return                      TRUE if, and only if, the comparison could take place
275  */
276 static gboolean
277 match_compare_property (HalDevice *d, const char *key, const char *right_side, dbus_int64_t *result)
278 {
279 	gboolean rc;
280 	int proptype;
281 
282 	rc = FALSE;
283 
284 	if (!hal_device_has_property (d, key))
285 		goto out;
286 
287 	proptype = hal_device_property_get_type (d, key);
288 	switch (proptype) {
289 	case HAL_PROPERTY_TYPE_STRING:
290 		*result = (dbus_int64_t) strcmp (hal_device_property_get_string (d, key), right_side);
291 		rc = TRUE;
292 		break;
293 
294 	case HAL_PROPERTY_TYPE_INT32:
295 		*result = ((dbus_int64_t) hal_device_property_get_int (d, key)) - strtoll (right_side, NULL, 0);
296 		rc = TRUE;
297 		break;
298 
299 	case HAL_PROPERTY_TYPE_UINT64:
300 		*result = ((dbus_int64_t) hal_device_property_get_uint64 (d, key)) - ((dbus_int64_t) strtoll (right_side, NULL, 0));
301 		rc = TRUE;
302 		break;
303 
304 	case HAL_PROPERTY_TYPE_DOUBLE:
305 		*result = (dbus_int64_t) ceil (hal_device_property_get_double (d, key) - atof (right_side));
306 		rc = TRUE;
307 		break;
308 
309 	default:
310 		/* explicit fallthrough */
311 	case HAL_PROPERTY_TYPE_BOOLEAN:
312 		/* explicit blank since this doesn't make sense */
313 		break;
314 	}
315 
316 out:
317 	return rc;
318 }
319 
320 /** Called when the match element begins.
321  *
322  *  @param  pc                  Parsing context
323  *  @param  attr                Attribute key/value pairs
324  *  @return                     #FALSE if the device in question didn't
325  *                              match the data in the attributes
326  */
327 static dbus_bool_t
328 handle_match (ParsingContext * pc, const char **attr)
329 {
330 	char udi_to_check[256];
331 	char prop_to_check[256];
332 	const char *key;
333 	int num_attrib;
334 	HalDevice *d;
335 
336 	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++);
337 
338 	if (num_attrib != 4)
339 		return FALSE;
340 
341 	if (strcmp (attr[0], "key") != 0)
342 		return FALSE;
343 	key = attr[1];
344 
345 	/* Resolve key paths like 'someudi/foo/bar/baz:prop.name' '@prop.here.is.an.udi:with.prop.name' */
346 	if (!resolve_udiprop_path (key,
347 				   pc->device->udi,
348 				   udi_to_check, sizeof (udi_to_check),
349 				   prop_to_check, sizeof (prop_to_check))) {
350 		HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", key, pc->device->udi));
351 		return FALSE;
352 	}
353 
354 	d = hal_device_store_find (hald_get_gdl (), udi_to_check);
355 	if (d == NULL) {
356 		d = hal_device_store_find (hald_get_tdl (), udi_to_check);
357 	}
358 	if (d == NULL) {
359 		HAL_ERROR (("Could not find device with udi '%s'", udi_to_check));
360 		return FALSE;
361 	}
362 
363 
364 	if (strcmp (attr[2], "string") == 0) {
365 		const char *value;
366 
367 		/* match string property */
368 
369 		value = attr[3];
370 
371 		/*HAL_INFO(("Checking that key='%s' is a string that "
372 		  "equals '%s'", key, value)); */
373 
374 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
375 			return FALSE;
376 
377 		if (strcmp (hal_device_property_get_string (d, prop_to_check),
378 			    value) != 0)
379 			return FALSE;
380 
381 		/*HAL_INFO (("*** string match for key %s", key));*/
382 		return TRUE;
383 	} else if (strcmp (attr[2], "int") == 0) {
384 		dbus_int32_t value;
385 
386 		/* match integer property */
387 		value = strtol (attr[3], NULL, 0);
388 
389 		/** @todo Check error condition */
390 
391 		/*HAL_INFO (("Checking that key='%s' is a int that equals %d",
392 		  key, value));*/
393 
394 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_INT32)
395 			return FALSE;
396 
397 		if (hal_device_property_get_int (d, prop_to_check) != value) {
398 			return FALSE;
399 		}
400 
401 		return TRUE;
402 	} else if (strcmp (attr[2], "uint64") == 0) {
403 		dbus_uint64_t value;
404 
405 		/* match integer property */
406 		value = strtoull (attr[3], NULL, 0);
407 
408 		/** @todo Check error condition */
409 
410 		/*HAL_INFO (("Checking that key='%s' is a int that equals %d",
411 		  key, value));*/
412 
413 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_UINT64)
414 			return FALSE;
415 
416 		if (hal_device_property_get_uint64 (d, prop_to_check) != value) {
417 			return FALSE;
418 		}
419 
420 		return TRUE;
421 	} else if (strcmp (attr[2], "bool") == 0) {
422 		dbus_bool_t value;
423 
424 		/* match string property */
425 
426 		if (strcmp (attr[3], "false") == 0)
427 			value = FALSE;
428 		else if (strcmp (attr[3], "true") == 0)
429 			value = TRUE;
430 		else
431 			return FALSE;
432 
433 		/*HAL_INFO (("Checking that key='%s' is a bool that equals %s",
434 		  key, value ? "TRUE" : "FALSE"));*/
435 
436 		if (hal_device_property_get_type (d, prop_to_check) !=
437 		    HAL_PROPERTY_TYPE_BOOLEAN)
438 			return FALSE;
439 
440 		if (hal_device_property_get_bool (d, prop_to_check) != value)
441 			return FALSE;
442 
443 		/*HAL_INFO (("*** bool match for key %s", key));*/
444 		return TRUE;
445 	} else if (strcmp (attr[2], "exists") == 0) {
446 		dbus_bool_t should_exist = TRUE;
447 
448 		if (strcmp (attr[3], "false") == 0)
449 			should_exist = FALSE;
450 
451 		if (should_exist) {
452 			if (hal_device_has_property (d, prop_to_check))
453 				return TRUE;
454 			else
455 				return FALSE;
456 		} else {
457 			if (hal_device_has_property (d, prop_to_check))
458 				return FALSE;
459 			else
460 				return TRUE;
461 		}
462 	} else if (strcmp (attr[2], "empty") == 0) {
463 		int type;
464 		dbus_bool_t is_empty = TRUE;
465 		dbus_bool_t should_be_empty = TRUE;
466 
467 
468 		if (strcmp (attr[3], "false") == 0)
469 			should_be_empty = FALSE;
470 
471 		type = hal_device_property_get_type (d, prop_to_check);
472 		switch (type) {
473 		case HAL_PROPERTY_TYPE_STRING:
474 			if (hal_device_has_property (d, prop_to_check))
475 				if (strlen (hal_device_property_get_string (d, prop_to_check)) > 0)
476 					is_empty = FALSE;
477 			break;
478 		case HAL_PROPERTY_TYPE_STRLIST:
479 			if (hal_device_has_property (d, prop_to_check))
480 				if (!hal_device_property_strlist_is_empty(d, prop_to_check))
481 					is_empty = FALSE;
482 			break;
483 		default:
484 			/* explicit fallthrough */
485 			return FALSE;
486 			break;
487 		}
488 
489 		if (should_be_empty) {
490 			if (is_empty)
491 				return TRUE;
492 			else
493 				return FALSE;
494 		} else {
495 			if (is_empty)
496 				return FALSE;
497 			else
498 				return TRUE;
499 		}
500 	} else if (strcmp (attr[2], "is_ascii") == 0) {
501 		dbus_bool_t is_ascii = TRUE;
502 		dbus_bool_t should_be_ascii = TRUE;
503 		unsigned int i;
504 		const char *str;
505 
506 		if (strcmp (attr[3], "false") == 0)
507 			should_be_ascii = FALSE;
508 
509 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
510 			return FALSE;
511 
512 		is_ascii = TRUE;
513 
514 		str = hal_device_property_get_string (d, prop_to_check);
515 		for (i = 0; str[i] != '\0'; i++) {
516 			if (((unsigned char) str[i]) > 0x7f)
517 				is_ascii = FALSE;
518 		}
519 
520 		if (should_be_ascii) {
521 			if (is_ascii)
522 				return TRUE;
523 			else
524 				return FALSE;
525 		} else {
526 			if (is_ascii)
527 				return FALSE;
528 			else
529 				return TRUE;
530 		}
531 	} else if (strcmp (attr[2], "is_absolute_path") == 0) {
532 		const char *path = NULL;
533 		dbus_bool_t is_absolute_path = FALSE;
534 		dbus_bool_t should_be_absolute_path = TRUE;
535 
536 		if (strcmp (attr[3], "false") == 0)
537 			should_be_absolute_path = FALSE;
538 
539 		/*HAL_INFO (("d->udi='%s', prop_to_check='%s'", d->udi, prop_to_check));*/
540 
541 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
542 			return FALSE;
543 
544 		if (hal_device_has_property (d, prop_to_check)) {
545 			path = hal_device_property_get_string (d, prop_to_check);
546 			if (g_path_is_absolute (path))
547 				is_absolute_path = TRUE;
548 		}
549 
550 		/*HAL_INFO (("is_absolute=%d, should_be=%d, path='%s'", is_absolute_path, should_be_absolute_path, path));*/
551 
552 		if (should_be_absolute_path) {
553 			if (is_absolute_path)
554 				return TRUE;
555 			else
556 				return FALSE;
557 		} else {
558 			if (is_absolute_path)
559 				return FALSE;
560 			else
561 				return TRUE;
562 		}
563 	} else if (strcmp (attr[2], "contains") == 0) {
564 		const char *needle;
565 		dbus_bool_t contains = FALSE;
566 
567 		needle = attr[3];
568 
569 		if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRING) {
570 			if (hal_device_has_property (d, prop_to_check)) {
571 				const char *haystack;
572 
573 				haystack = hal_device_property_get_string (d, prop_to_check);
574 				if (needle != NULL && haystack != NULL && strstr (haystack, needle)) {
575 					contains = TRUE;
576 				}
577 
578 			}
579 		} else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST &&
580 			   needle != NULL) {
581 			GSList *i;
582 			GSList *value;
583 
584 			value = hal_device_property_get_strlist (d, prop_to_check);
585 			for (i = value; i != NULL; i = g_slist_next (i)) {
586 				const char *str = i->data;
587 				if (strcmp (str, needle) == 0) {
588 					contains = TRUE;
589 					break;
590 				}
591 			}
592 		} else {
593 			return FALSE;
594 		}
595 
596 		return contains;
597 	} else if (strcmp (attr[2], "contains_ncase") == 0) {
598 		const char *needle;
599 		dbus_bool_t contains_ncase = FALSE;
600 
601 		needle = attr[3];
602 
603 		if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRING) {
604 			if (hal_device_has_property (d, prop_to_check)) {
605 				char *needle_lowercase;
606 				char *haystack_lowercase;
607 
608 				needle_lowercase   = g_utf8_strdown (needle, -1);
609 				haystack_lowercase = g_utf8_strdown (hal_device_property_get_string (d, prop_to_check), -1);
610 				if (needle_lowercase != NULL && haystack_lowercase != NULL && strstr (haystack_lowercase, needle_lowercase)) {
611 					contains_ncase = TRUE;
612 				}
613 
614 				g_free (needle_lowercase);
615 				g_free (haystack_lowercase);
616 			}
617 		} else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST &&
618 			   needle != NULL) {
619 			GSList *i;
620 			GSList *value;
621 
622 			value = hal_device_property_get_strlist (d, prop_to_check);
623 			for (i = value; i != NULL; i = g_slist_next (i)) {
624 				const char *str = i->data;
625 				if (g_ascii_strcasecmp (str, needle) == 0) {
626 					contains_ncase = TRUE;
627 					break;
628 				}
629 			}
630 		} else {
631 			return FALSE;
632 		}
633 
634 		return contains_ncase;
635 	} else if (strcmp (attr[2], "compare_lt") == 0) {
636 		dbus_int64_t result;
637 		if (!match_compare_property (d, prop_to_check, attr[3], &result)) {
638 			return FALSE;
639 		} else {
640 			return result < 0;
641 		}
642 	} else if (strcmp (attr[2], "compare_le") == 0) {
643 		dbus_int64_t result;
644 		if (!match_compare_property (d, prop_to_check, attr[3], &result))
645 			return FALSE;
646 		else
647 			return result <= 0;
648 	} else if (strcmp (attr[2], "compare_gt") == 0) {
649 		dbus_int64_t result;
650 		if (!match_compare_property (d, prop_to_check, attr[3], &result))
651 			return FALSE;
652 		else
653 			return result > 0;
654 	} else if (strcmp (attr[2], "compare_ge") == 0) {
655 		dbus_int64_t result;
656 		if (!match_compare_property (d, prop_to_check, attr[3], &result))
657 			return FALSE;
658 		else
659 			return result >= 0;
660 	}
661 
662 	return FALSE;
663 }
664 
665 
666 /** Called when the merge element begins.
667  *
668  *  @param  pc                  Parsing context
669  *  @param  attr                Attribute key/value pairs
670  */
671 static void
672 handle_merge (ParsingContext * pc, const char **attr)
673 {
674 	int num_attrib;
675 
676 	pc->merge_type = MERGE_TYPE_UNKNOWN;
677 
678 
679 	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
680 		;
681 	}
682 
683 	if (num_attrib != 4)
684 		return;
685 
686 	if (strcmp (attr[0], "key") != 0)
687 		return;
688 	strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
689 
690 	if (strcmp (attr[2], "type") != 0)
691 		return;
692 
693 	if (strcmp (attr[3], "string") == 0) {
694 		/* match string property */
695 		pc->merge_type = MERGE_TYPE_STRING;
696 		return;
697 	} else if (strcmp (attr[3], "bool") == 0) {
698 		/* match string property */
699 		pc->merge_type = MERGE_TYPE_BOOLEAN;
700 		return;
701 	} else if (strcmp (attr[3], "int") == 0) {
702 		/* match string property */
703 		pc->merge_type = MERGE_TYPE_INT32;
704 		return;
705 	} else if (strcmp (attr[3], "uint64") == 0) {
706 		/* match string property */
707 		pc->merge_type = MERGE_TYPE_UINT64;
708 		return;
709 	} else if (strcmp (attr[3], "double") == 0) {
710 		/* match string property */
711 		pc->merge_type = MERGE_TYPE_DOUBLE;
712 		return;
713 	} else if (strcmp (attr[3], "strlist") == 0) {
714 		/* match string property */
715 		pc->merge_type = MERGE_TYPE_STRLIST;
716 		return;
717 	} else if (strcmp (attr[3], "copy_property") == 0) {
718 		/* copy another property */
719 		pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
720 		return;
721 	}
722 
723 	return;
724 }
725 
726 /** Called when the append or prepend element begins.
727  *
728  *  @param  pc                  Parsing context
729  *  @param  attr                Attribute key/value pairs
730  */
731 static void
732 handle_append_prepend (ParsingContext * pc, const char **attr)
733 {
734 	int num_attrib;
735 
736 	pc->merge_type = MERGE_TYPE_UNKNOWN;
737 
738 	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
739 		;
740 	}
741 
742 	if (num_attrib != 4)
743 		return;
744 
745 	if (strcmp (attr[0], "key") != 0)
746 		return;
747 	strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
748 
749 	if (strcmp (attr[2], "type") != 0)
750 		return;
751 
752 	if (strcmp (attr[3], "string") == 0) {
753 		/* append to a string */
754 		pc->merge_type = MERGE_TYPE_STRING;
755 		return;
756 	} else if (strcmp (attr[3], "strlist") == 0) {
757 		/* append to a string list*/
758 		pc->merge_type = MERGE_TYPE_STRLIST;
759 		return;
760 	} else if (strcmp (attr[3], "copy_property") == 0) {
761 		/* copy another property */
762 		pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
763 		return;
764 	}
765 
766 	return;
767 }
768 
769 
770 /** Called when the spawn element begins.
771  *
772  *  @param  pc                  Parsing context
773  *  @param  attr                Attribute key/value pairs
774  */
775 static void
776 handle_spawn (ParsingContext * pc, const char **attr)
777 {
778 	int num_attrib;
779 
780 	pc->merge_type = MERGE_TYPE_UNKNOWN;
781 
782 	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
783 		;
784 	}
785 
786 	if (num_attrib != 2)
787 		return;
788 
789 	if (strcmp (attr[0], "udi") != 0)
790 		return;
791 
792 	strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
793 
794 	pc->merge_type = MERGE_TYPE_SPAWN;
795 	return;
796 }
797 
798 /** Called when the remove element begins.
799  *
800  *  @param  pc                  Parsing context
801  *  @param  attr                Attribute key/value pairs
802  */
803 static void
804 handle_remove (ParsingContext * pc, const char **attr)
805 {
806 	int num_attrib;
807 
808 	pc->merge_type = MERGE_TYPE_UNKNOWN;
809 
810 	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
811 		;
812 	}
813 
814 	if (num_attrib != 2 && num_attrib != 4)
815 		return;
816 
817 	if (strcmp (attr[0], "key") != 0)
818 		return;
819 	strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
820 
821 	if (num_attrib == 4) {
822 		if (strcmp (attr[2], "type") != 0)
823 			return;
824 
825 		if (strcmp (attr[3], "strlist") == 0) {
826 			/* remove from strlist */
827 			pc->merge_type = MERGE_TYPE_STRLIST;
828 			return;
829 		} else {
830 			pc->merge_type = MERGE_TYPE_UNKNOWN;
831 			return;
832 		}
833 	} else {
834 		pc->merge_type = MERGE_TYPE_REMOVE;
835 	}
836 
837 	return;
838 }
839 
840 /** Called when the clear element begins.
841  *
842  *  @param  pc                  Parsing context
843  *  @param  attr                Attribute key/value pairs
844  */
845 static void
846 handle_clear (ParsingContext * pc, const char **attr)
847 {
848 	int num_attrib;
849 
850 	pc->merge_type = MERGE_TYPE_UNKNOWN;
851 
852 	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
853 		;
854 	}
855 
856 	if (num_attrib != 4)
857 		return;
858 
859 	if (strcmp (attr[0], "key") != 0)
860 		return;
861 
862 
863 	if (strcmp (attr[3], "strlist") != 0)
864 		return;
865 
866 	strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
867 
868 	pc->merge_type = MERGE_TYPE_CLEAR;
869 
870 	return;
871 }
872 
873 /** Abort parsing of document
874  *
875  *  @param  pc                  Parsing context
876  */
877 static void
878 parsing_abort (ParsingContext * pc)
879 {
880 	/* Grr, expat can't abort parsing */
881 	HAL_ERROR (("Aborting parsing of document"));
882 	pc->aborted = TRUE;
883 }
884 
885 /** Called by expat when an element begins.
886  *
887  *  @param  pc                  Parsing context
888  *  @param  el                  Element name
889  *  @param  attr                Attribute key/value pairs
890  */
891 static void
892 start (ParsingContext * pc, const char *el, const char **attr)
893 {
894 	if (pc->aborted)
895 		return;
896 
897 	pc->cdata_buf_len = 0;
898 
899 	pc->merge_type = MERGE_TYPE_UNKNOWN;
900 
901 /*
902     for (i = 0; i < pc->depth; i++)
903         printf("  ");
904 
905     printf("%s", el);
906 
907     for (i = 0; attr[i]; i += 2) {
908         printf(" %s='%s'", attr[i], attr[i + 1]);
909     }
910 
911     printf("   curelem=%d\n", pc->curelem);
912 */
913 
914 	if (strcmp (el, "match") == 0) {
915 		if (pc->curelem != CURELEM_DEVICE
916 		    && pc->curelem != CURELEM_MATCH) {
917 			HAL_ERROR (("%s:%d:%d: Element <match> can only be "
918 				    "inside <device> and <match>",
919 				    pc->file,
920 				    XML_GetCurrentLineNumber (pc->parser),
921 				    XML_GetCurrentColumnNumber (pc->parser)));
922 			parsing_abort (pc);
923 		}
924 
925 		pc->curelem = CURELEM_MATCH;
926 
927 		/* don't bother checking if matching at lower depths failed */
928 		if (pc->match_ok) {
929 			if (!handle_match (pc, attr)) {
930 				/* No match */
931 				pc->match_depth_first_fail = pc->depth;
932 				pc->match_ok = FALSE;
933 			}
934 		}
935 	} else if (strcmp (el, "merge") == 0) {
936 		if (pc->curelem != CURELEM_DEVICE
937 		    && pc->curelem != CURELEM_MATCH) {
938 			HAL_ERROR (("%s:%d:%d: Element <merge> can only be "
939 				    "inside <device> and <match>",
940 				    pc->file,
941 				    XML_GetCurrentLineNumber (pc->parser),
942 				    XML_GetCurrentColumnNumber (pc->parser)));
943 			parsing_abort (pc);
944 		}
945 
946 		pc->curelem = CURELEM_MERGE;
947 		if (pc->match_ok) {
948 			handle_merge (pc, attr);
949 		} else {
950 			/*HAL_INFO(("No merge!")); */
951 		}
952 	} else if (strcmp (el, "append") == 0) {
953 		if (pc->curelem != CURELEM_DEVICE
954 		    && pc->curelem != CURELEM_MATCH) {
955 			HAL_ERROR (("%s:%d:%d: Element <append> can only be "
956 				    "inside <device> and <match>",
957 				    pc->file,
958 				    XML_GetCurrentLineNumber (pc->parser),
959 				    XML_GetCurrentColumnNumber (pc->parser)));
960 			parsing_abort (pc);
961 		}
962 
963 		pc->curelem = CURELEM_APPEND;
964 		if (pc->match_ok) {
965 			handle_append_prepend (pc, attr);
966 		} else {
967 			/*HAL_INFO(("No merge!")); */
968 		}
969 	} else if (strcmp (el, "prepend") == 0) {
970 		if (pc->curelem != CURELEM_DEVICE
971 		    && pc->curelem != CURELEM_MATCH) {
972 			HAL_ERROR (("%s:%d:%d: Element <prepend> can only be "
973 				    "inside <device> and <match>",
974 				    pc->file,
975 				    XML_GetCurrentLineNumber (pc->parser),
976 				    XML_GetCurrentColumnNumber (pc->parser)));
977 			parsing_abort (pc);
978 		}
979 
980 		pc->curelem = CURELEM_PREPEND;
981 		if (pc->match_ok) {
982 			handle_append_prepend (pc, attr);
983 		} else {
984 			/*HAL_INFO(("No merge!")); */
985 		}
986 	} else if (strcmp (el, "remove") == 0) {
987 		if (pc->curelem != CURELEM_DEVICE
988 		    && pc->curelem != CURELEM_MATCH) {
989 			HAL_ERROR (("%s:%d:%d: Element <remove> can only be "
990 				    "inside <device> and <match>",
991 				    pc->file,
992 				    XML_GetCurrentLineNumber (pc->parser),
993 				    XML_GetCurrentColumnNumber (pc->parser)));
994 			parsing_abort (pc);
995 		}
996 
997 		pc->curelem = CURELEM_REMOVE;
998 		if (pc->match_ok) {
999 			handle_remove (pc, attr);
1000 		} else {
1001 			/*HAL_INFO(("No merge!")); */
1002 		}
1003 	} else if (strcmp (el, "clear") == 0) {
1004 		if (pc->curelem != CURELEM_DEVICE
1005 		    && pc->curelem != CURELEM_MATCH) {
1006 			HAL_ERROR (("%s:%d:%d: Element <remove> can only be "
1007 				    "inside <device> and <match>",
1008 				    pc->file,
1009 				    XML_GetCurrentLineNumber (pc->parser),
1010 				    XML_GetCurrentColumnNumber (pc->parser)));
1011 			parsing_abort (pc);
1012 		}
1013 
1014 		pc->curelem = CURELEM_CLEAR;
1015 		if (pc->match_ok) {
1016 			handle_clear (pc, attr);
1017 		} else {
1018 			/*HAL_INFO(("No merge!")); */
1019 		}
1020 	} else if (strcmp (el, "device") == 0) {
1021 		if (pc->curelem != CURELEM_DEVICE_INFO) {
1022 			HAL_ERROR (("%s:%d:%d: Element <device> can only be "
1023 				    "inside <deviceinfo>",
1024 				    pc->file,
1025 				    XML_GetCurrentLineNumber (pc->parser),
1026 				    XML_GetCurrentColumnNumber (pc->parser)));
1027 			parsing_abort (pc);
1028 		}
1029 		pc->curelem = CURELEM_DEVICE;
1030 	} else if (strcmp (el, "deviceinfo") == 0) {
1031 		if (pc->curelem != CURELEM_UNKNOWN) {
1032 			HAL_ERROR (("%s:%d:%d: Element <deviceinfo> must be "
1033 				    "a top-level element",
1034 				    pc->file,
1035 				    XML_GetCurrentLineNumber (pc->parser),
1036 				    XML_GetCurrentColumnNumber (pc->parser)));
1037 			parsing_abort (pc);
1038 		}
1039 		pc->curelem = CURELEM_DEVICE_INFO;
1040 	} else if (strcmp (el, "spawn") == 0) {
1041 		if (pc->curelem != CURELEM_MATCH) {
1042 			HAL_ERROR (("%s:%d:%d: Element <spawn> can only be "
1043 				    "inside <match>",
1044 				    pc->file,
1045 				    XML_GetCurrentLineNumber (pc->parser),
1046 				    XML_GetCurrentColumnNumber (pc->parser)));
1047 			parsing_abort (pc);
1048 		}
1049 
1050 		pc->curelem = CURELEM_SPAWN;
1051 		if (pc->match_ok) {
1052 			handle_spawn (pc, attr);
1053 		}
1054 
1055 	} else {
1056 		HAL_ERROR (("%s:%d:%d: Unknown element <%s>",
1057 			    pc->file,
1058 			    XML_GetCurrentLineNumber (pc->parser),
1059 			    XML_GetCurrentColumnNumber (pc->parser), el));
1060 		parsing_abort (pc);
1061 	}
1062 
1063 	/* Nasty hack */
1064 	assert (pc->depth < MAX_DEPTH);
1065 
1066 	pc->depth++;
1067 
1068 	/* store depth */
1069 	pc->curelem_stack[pc->depth] = pc->curelem;
1070 
1071 }
1072 
1073 static void
1074 spawned_device_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
1075 {
1076 	HAL_INFO (("Add callouts completed udi=%s", d->udi));
1077 
1078 	/* Move from temporary to global device store */
1079 	hal_device_store_remove (hald_get_tdl (), d);
1080 	hal_device_store_add (hald_get_gdl (), d);
1081 
1082 }
1083 
1084 /** Called by expat when an element ends.
1085  *
1086  *  @param  pc                  Parsing context
1087  *  @param  el                  Element name
1088  */
1089 static void
1090 end (ParsingContext * pc, const char *el)
1091 {
1092 	if (pc->aborted)
1093 		return;
1094 
1095 	pc->cdata_buf[pc->cdata_buf_len] = '\0';
1096 
1097 /*    printf("   curelem=%d\n", pc->curelem);*/
1098 
1099 	if (pc->curelem == CURELEM_MERGE && pc->match_ok) {
1100 		/* As soon as we are merging, we have matched the device... */
1101 		pc->device_matched = TRUE;
1102 
1103 		switch (pc->merge_type) {
1104 		case MERGE_TYPE_STRING:
1105 			hal_device_property_set_string (pc->device, pc->merge_key, pc->cdata_buf);
1106 			break;
1107 
1108 		case MERGE_TYPE_STRLIST:
1109 		{
1110 			int type = hal_device_property_get_type (pc->device, pc->merge_key);
1111 			if (type == HAL_PROPERTY_TYPE_STRLIST || type == HAL_PROPERTY_TYPE_INVALID) {
1112 				hal_device_property_remove (pc->device, pc->merge_key);
1113 				hal_device_property_strlist_append (pc->device, pc->merge_key, pc->cdata_buf);
1114 			}
1115 			break;
1116 		}
1117 
1118 		case MERGE_TYPE_INT32:
1119 			{
1120 				dbus_int32_t value;
1121 
1122 				/* match integer property */
1123 				value = strtol (pc->cdata_buf, NULL, 0);
1124 
1125 				/** @todo FIXME: Check error condition */
1126 
1127 				hal_device_property_set_int (pc->device,
1128 						     pc->merge_key, value);
1129 				break;
1130 			}
1131 
1132 		case MERGE_TYPE_UINT64:
1133 			{
1134 				dbus_uint64_t value;
1135 
1136 				/* match integer property */
1137 				value = strtoull (pc->cdata_buf, NULL, 0);
1138 
1139 				/** @todo FIXME: Check error condition */
1140 
1141 				hal_device_property_set_uint64 (pc->device,
1142 						     pc->merge_key, value);
1143 				break;
1144 			}
1145 
1146 		case MERGE_TYPE_BOOLEAN:
1147 			hal_device_property_set_bool (pc->device, pc->merge_key,
1148 					      (strcmp (pc->cdata_buf,
1149 						       "true") == 0)
1150 					      ? TRUE : FALSE);
1151 			break;
1152 
1153 		case MERGE_TYPE_DOUBLE:
1154 			hal_device_property_set_double (pc->device, pc->merge_key,
1155 						atof (pc->cdata_buf));
1156 			break;
1157 
1158 		case MERGE_TYPE_COPY_PROPERTY:
1159 		{
1160 			char udi_to_merge_from[256];
1161 			char prop_to_merge[256];
1162 
1163 			/* Resolve key paths like 'someudi/foo/bar/baz:prop.name'
1164 			 * '@prop.here.is.an.udi:with.prop.name'
1165 			 */
1166 			if (!resolve_udiprop_path (pc->cdata_buf,
1167 						   pc->device->udi,
1168 						   udi_to_merge_from, sizeof (udi_to_merge_from),
1169 						   prop_to_merge, sizeof (prop_to_merge))) {
1170 				HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", pc->cdata_buf, pc->device->udi));
1171 			} else {
1172 				HalDevice *d;
1173 
1174 				d = hal_device_store_find (hald_get_gdl (), udi_to_merge_from);
1175 				if (d == NULL) {
1176 					d = hal_device_store_find (hald_get_tdl (), udi_to_merge_from);
1177 				}
1178 				if (d == NULL) {
1179 					HAL_ERROR (("Could not find device with udi '%s'", udi_to_merge_from));
1180 				} else {
1181 					hal_device_copy_property (d, prop_to_merge, pc->device, pc->merge_key);
1182 				}
1183 			}
1184 			break;
1185 		}
1186 
1187 		default:
1188 			HAL_ERROR (("Unknown merge_type=%d='%c'",
1189 				    pc->merge_type, pc->merge_type));
1190 			break;
1191 		}
1192 	} else if (pc->curelem == CURELEM_APPEND && pc->match_ok &&
1193 		   (hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRING ||
1194 		    hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRLIST ||
1195 		    hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_INVALID)) {
1196 		char buf[256];
1197 		char buf2[256];
1198 
1199 		/* As soon as we are appending, we have matched the device... */
1200 		pc->device_matched = TRUE;
1201 
1202 		if (pc->merge_type == MERGE_TYPE_STRLIST) {
1203 			hal_device_property_strlist_append (pc->device, pc->merge_key, pc->cdata_buf);
1204 		} else {
1205 			const char *existing_string;
1206 
1207 			switch (pc->merge_type) {
1208 			case MERGE_TYPE_STRING:
1209 				strncpy (buf, pc->cdata_buf, sizeof (buf));
1210 				break;
1211 
1212 			case MERGE_TYPE_COPY_PROPERTY:
1213 				hal_device_property_get_as_string (pc->device, pc->cdata_buf, buf, sizeof (buf));
1214 				break;
1215 
1216 			default:
1217 				HAL_ERROR (("Unknown merge_type=%d='%c'", pc->merge_type, pc->merge_type));
1218 				break;
1219 			}
1220 
1221 			existing_string = hal_device_property_get_string (pc->device, pc->merge_key);
1222 			if (existing_string != NULL) {
1223 				strncpy (buf2, existing_string, sizeof (buf2));
1224 				strncat (buf2, buf, sizeof (buf2) - strlen(buf2));
1225 			} else {
1226 				strncpy (buf2, buf, sizeof (buf2));
1227 			}
1228 			hal_device_property_set_string (pc->device, pc->merge_key, buf2);
1229 		}
1230 	} else if (pc->curelem == CURELEM_PREPEND && pc->match_ok &&
1231 		   (hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRING ||
1232 		    hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRLIST ||
1233 		    hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_INVALID)) {
1234 		char buf[256];
1235 		char buf2[256];
1236 
1237 		/* As soon as we are prepending, we have matched the device... */
1238 		pc->device_matched = TRUE;
1239 
1240 		if (pc->merge_type == MERGE_TYPE_STRLIST) {
1241 			hal_device_property_strlist_prepend (pc->device, pc->merge_key, pc->cdata_buf);
1242 		} else {
1243 			const char *existing_string;
1244 
1245 			switch (pc->merge_type) {
1246 			case MERGE_TYPE_STRING:
1247 				strncpy (buf, pc->cdata_buf, sizeof (buf));
1248 				break;
1249 
1250 			case MERGE_TYPE_COPY_PROPERTY:
1251 				hal_device_property_get_as_string (pc->device, pc->cdata_buf, buf, sizeof (buf));
1252 				break;
1253 
1254 			default:
1255 				HAL_ERROR (("Unknown merge_type=%d='%c'", pc->merge_type, pc->merge_type));
1256 				break;
1257 			}
1258 
1259 			existing_string = hal_device_property_get_string (pc->device, pc->merge_key);
1260 			if (existing_string != NULL) {
1261 				strncpy (buf2, buf, sizeof (buf2));
1262 				strncat (buf2, existing_string, sizeof (buf2) - strlen(buf2));
1263 			} else {
1264 				strncpy (buf2, buf, sizeof (buf2));
1265 			}
1266 			hal_device_property_set_string (pc->device, pc->merge_key, buf2);
1267 		}
1268 	} else if (pc->curelem == CURELEM_REMOVE && pc->match_ok) {
1269 
1270 		if (pc->merge_type == MERGE_TYPE_STRLIST) {
1271 			/* covers <remove key="foobar" type="strlist">blah</remove> */
1272 			hal_device_property_strlist_remove (pc->device, pc->merge_key, pc->cdata_buf);
1273 		} else {
1274 			/* only allow <remove key="foobar"/>, not <remove key="foobar">blah</remove> */
1275 			if (strlen (pc->cdata_buf) == 0) {
1276 				hal_device_property_remove (pc->device, pc->merge_key);
1277 			}
1278 		}
1279 	} else if (pc->merge_type == MERGE_TYPE_SPAWN) {
1280 		HalDevice *spawned;
1281 
1282 		spawned = hal_device_store_find (hald_get_gdl (), pc->merge_key);
1283 		if (spawned == NULL)
1284 			spawned = hal_device_store_find (hald_get_tdl (), pc->merge_key);
1285 
1286 		if (spawned == NULL) {
1287 			HAL_INFO (("Spawning new device object '%s' caused by <spawn> on udi '%s'",
1288 				   pc->merge_key, pc->device->udi));
1289 
1290 			spawned = hal_device_new ();
1291 			hal_device_property_set_string (spawned, "info.bus", "unknown");
1292 			hal_device_property_set_string (spawned, "info.udi", pc->merge_key);
1293 			hal_device_property_set_string (spawned, "info.parent", pc->device->udi);
1294 			hal_device_set_udi (spawned, pc->merge_key);
1295 
1296 			hal_device_store_add (hald_get_tdl (), spawned);
1297 
1298 			di_search_and_merge (spawned, DEVICE_INFO_TYPE_INFORMATION);
1299 			di_search_and_merge (spawned, DEVICE_INFO_TYPE_POLICY);
1300 
1301 			hal_util_callout_device_add (spawned, spawned_device_callouts_add_done, NULL, NULL);
1302 		}
1303 
1304 	} else if (pc->curelem == CURELEM_CLEAR && pc->match_ok) {
1305 		if (pc->merge_type == MERGE_TYPE_CLEAR) {
1306 			hal_device_property_strlist_clear (pc->device, pc->merge_key);
1307 		}
1308 	}
1309 
1310 
1311 	pc->cdata_buf_len = 0;
1312 	pc->depth--;
1313 
1314 	/* maintain curelem */
1315 	pc->curelem = pc->curelem_stack[pc->depth];
1316 
1317 	/* maintain pc->match_ok */
1318 	if (pc->depth <= pc->match_depth_first_fail)
1319 		pc->match_ok = TRUE;
1320 }
1321 
1322 /** Called when there is CDATA
1323  *
1324  *  @param  pc                  Parsing context
1325  *  @param  s                   Pointer to data
1326  *  @param  len                 Length of data
1327  */
1328 static void
1329 cdata (ParsingContext * pc, const char *s, int len)
1330 {
1331 	int bytes_left;
1332 	int bytes_to_copy;
1333 
1334 	if (pc->aborted)
1335 		return;
1336 
1337 	bytes_left = CDATA_BUF_SIZE - pc->cdata_buf_len;
1338 	if (len > bytes_left) {
1339 		HAL_ERROR (("CDATA in element larger than %d",
1340 			    CDATA_BUF_SIZE));
1341 	}
1342 
1343 	bytes_to_copy = len;
1344 	if (bytes_to_copy > bytes_left)
1345 		bytes_to_copy = bytes_left;
1346 
1347 	if (bytes_to_copy > 0)
1348 		memcpy (pc->cdata_buf + pc->cdata_buf_len, s,
1349 			bytes_to_copy);
1350 
1351 	pc->cdata_buf_len += bytes_to_copy;
1352 }
1353 
1354 
1355 /** Process a device information info file.
1356  *
1357  *  @param  dir                 Directory file resides in
1358  *  @param  filename            File name
1359  *  @param  device              Device to match on
1360  *  @return                     #TRUE if file matched device and information
1361  *                              was merged
1362  */
1363 static dbus_bool_t
1364 process_fdi_file (const char *dir, const char *filename,
1365 		  HalDevice * device)
1366 {
1367 	int rc;
1368 	char buf[512];
1369 	FILE *file;
1370 	int filesize;
1371 	char *filebuf;
1372 	dbus_bool_t device_matched;
1373 	XML_Parser parser;
1374 	ParsingContext *parsing_context;
1375 
1376 	file = NULL;
1377 	filebuf = NULL;
1378 	parser = NULL;
1379 	parsing_context = NULL;
1380 
1381 	device_matched = FALSE;
1382 
1383 	snprintf (buf, sizeof (buf), "%s/%s", dir, filename);
1384 
1385 	/*HAL_INFO(("analyzing file %s", buf));*/
1386 
1387 	/* open file and read it into a buffer; it's a small file... */
1388 	file = fopen (buf, "r");
1389 	if (file == NULL) {
1390 		HAL_ERROR (("Could not open file %s", buf));
1391 		goto out;
1392 	}
1393 
1394 	fseek (file, 0L, SEEK_END);
1395 	filesize = (int) ftell (file);
1396 	rewind (file);
1397 	filebuf = (char *) malloc (filesize);
1398 	if (filebuf == NULL) {
1399 		HAL_ERROR (("Could not allocate %d bytes for file %s", filesize, buf));
1400 		goto out;
1401 	}
1402 	(void) fread (filebuf, sizeof (char), filesize, file);
1403 
1404 	/* initialize parsing context */
1405 	parsing_context =
1406 	    (ParsingContext *) malloc (sizeof (ParsingContext));
1407 	if (parsing_context == NULL) {
1408 		HAL_ERROR (("Could not allocate parsing context"));
1409 		goto out;
1410 	}
1411 
1412 	/* TODO: reuse parser
1413 	 */
1414 	parser = XML_ParserCreate (NULL);
1415 	if (parser == NULL) {
1416 		HAL_ERROR (("Could not allocate XML parser"));
1417 		goto out;
1418 	}
1419 
1420 	parsing_context->depth = 0;
1421 	parsing_context->device_matched = FALSE;
1422 	parsing_context->match_ok = TRUE;
1423 	parsing_context->curelem = CURELEM_UNKNOWN;
1424 	parsing_context->aborted = FALSE;
1425 	parsing_context->file = buf;
1426 	parsing_context->parser = parser;
1427 	parsing_context->device = device;
1428 	parsing_context->match_depth_first_fail = -1;
1429 
1430 	XML_SetElementHandler (parser,
1431 			       (XML_StartElementHandler) start,
1432 			       (XML_EndElementHandler) end);
1433 	XML_SetCharacterDataHandler (parser,
1434 				     (XML_CharacterDataHandler) cdata);
1435 	XML_SetUserData (parser, parsing_context);
1436 
1437 	rc = XML_Parse (parser, filebuf, filesize, 1);
1438 	/*printf("XML_Parse rc=%d\r\n", rc); */
1439 
1440 	if (rc == 0) {
1441 		/* error parsing document */
1442 		HAL_ERROR (("Error parsing XML document %s at line %d, "
1443 			    "column %d : %s",
1444 			    buf,
1445 			    XML_GetCurrentLineNumber (parser),
1446 			    XML_GetCurrentColumnNumber (parser),
1447 			    XML_ErrorString (XML_GetErrorCode (parser))));
1448 		device_matched = FALSE;
1449 	} else {
1450 		/* document parsed ok */
1451 		device_matched = parsing_context->device_matched;
1452 	}
1453 
1454 out:
1455 	if (filebuf != NULL)
1456 		free (filebuf);
1457 	if (file != NULL)
1458 		fclose (file);
1459 	if (parser != NULL)
1460 		XML_ParserFree (parser);
1461 	if (parsing_context != NULL)
1462 		free (parsing_context);
1463 
1464 	return device_matched;
1465 }
1466 
1467 
1468 
1469 static int
1470 #ifdef __GLIBC__
1471 my_alphasort(const void *a, const void *b)
1472 #else
1473 my_alphasort(const struct dirent **a, const struct dirent **b)
1474 #endif
1475 {
1476 	return -alphasort (a, b);
1477 }
1478 
1479 
1480 /** Scan all directories and subdirectories in the given directory and
1481  *  process each *.fdi file
1482  *
1483  *  @param  d                   Device to merge information into
1484  *  @return                     #TRUE if information was merged
1485  */
1486 static dbus_bool_t
1487 scan_fdi_files (const char *dir, HalDevice * d)
1488 {
1489 	int i;
1490 	int num_entries;
1491 	dbus_bool_t found_fdi_file;
1492 	struct dirent **name_list;
1493 
1494 	found_fdi_file = 0;
1495 
1496 	/*HAL_INFO(("scan_fdi_files: Processing dir '%s'", dir));*/
1497 
1498 	num_entries = scandir (dir, &name_list, 0, my_alphasort);
1499 	if (num_entries == -1) {
1500 		return FALSE;
1501 	}
1502 
1503 	for (i = num_entries - 1; i >= 0; i--) {
1504 		int len;
1505 		char *filename;
1506 		gchar *full_path;
1507 
1508 		filename = name_list[i]->d_name;
1509 		len = strlen (filename);
1510 
1511 		full_path = g_strdup_printf ("%s/%s", dir, filename);
1512 		/*HAL_INFO (("Full path = %s", full_path));*/
1513 
1514 		/* Mmm, d_type can be DT_UNKNOWN, use glib to determine
1515 		 * the type
1516 		 */
1517 		if (g_file_test (full_path, (G_FILE_TEST_IS_REGULAR))) {
1518 			/* regular file */
1519 
1520 			if (len >= 5 &&
1521 			    filename[len - 4] == '.' &&
1522 			    filename[len - 3] == 'f' &&
1523 			    filename[len - 2] == 'd' &&
1524 			    filename[len - 1] == 'i') {
1525 				/*HAL_INFO (("scan_fdi_files: Processing file '%s'", filename));*/
1526 				found_fdi_file = process_fdi_file (dir, filename, d);
1527 				if (found_fdi_file) {
1528 					HAL_INFO (("*** Matched file %s/%s", dir, filename));
1529 					/*break;*/
1530 				}
1531 			}
1532 
1533 		} else if (g_file_test (full_path, (G_FILE_TEST_IS_DIR))
1534 			   && strcmp (filename, ".") != 0
1535 			   && strcmp (filename, "..") != 0) {
1536 			int num_bytes;
1537 			char *dirname;
1538 
1539 			/* Directory; do the recursion thingy but not
1540 			 * for . and ..
1541 			 */
1542 
1543 			num_bytes = len + strlen (dir) + 1 + 1;
1544 			dirname = (char *) malloc (num_bytes);
1545 			if (dirname == NULL) {
1546 				HAL_ERROR (("couldn't allocated %d bytes",
1547 					    num_bytes));
1548 				break;
1549 			}
1550 
1551 			snprintf (dirname, num_bytes, "%s/%s", dir,
1552 				  filename);
1553 			found_fdi_file = scan_fdi_files (dirname, d);
1554 			free (dirname);
1555 			/*
1556 			if (found_fdi_file)
1557 				break;
1558 			*/
1559 		}
1560 
1561 		g_free (full_path);
1562 
1563 		free (name_list[i]);
1564 	}
1565 
1566 	for (; i >= 0; i--) {
1567 		free (name_list[i]);
1568 	}
1569 
1570 	free (name_list);
1571 
1572 	return found_fdi_file;
1573 }
1574 
1575 /** Search the device info file repository for a .fdi file to merge
1576  *  more information into the device object.
1577  *
1578  *  @param  d                   Device to merge information into
1579  *  @return                     #TRUE if information was merged
1580  */
1581 dbus_bool_t
1582 di_search_and_merge (HalDevice *d, DeviceInfoType type)
1583 {
1584 	static gboolean have_checked_hal_fdi_source = FALSE;
1585 	static char *hal_fdi_source_preprobe = NULL;
1586 	static char *hal_fdi_source_information = NULL;
1587 	static char *hal_fdi_source_policy = NULL;
1588 	dbus_bool_t ret;
1589 	char *s1;
1590 	char *s2;
1591 	char *s3;
1592 
1593 	ret = FALSE;
1594 
1595 	if (!have_checked_hal_fdi_source) {
1596 		hal_fdi_source_preprobe    = getenv ("HAL_FDI_SOURCE_PREPROBE");
1597 		hal_fdi_source_information = getenv ("HAL_FDI_SOURCE_INFORMATION");
1598 		hal_fdi_source_policy      = getenv ("HAL_FDI_SOURCE_POLICY");
1599 		have_checked_hal_fdi_source = TRUE;
1600 	}
1601 
1602 	switch (type) {
1603 	case DEVICE_INFO_TYPE_PREPROBE:
1604 		if (hal_fdi_source_preprobe != NULL) {
1605 			s1 = hal_fdi_source_preprobe;
1606 			s2 = NULL;
1607 			s3 = NULL;
1608 		} else {
1609 			s1 = PACKAGE_OLD_DATA_DIR "/hal/fdi/preprobe";
1610 			s2 = PACKAGE_DATA_DIR "/hal/fdi/preprobe";
1611 			s3 = PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe";
1612 		}
1613 		break;
1614 
1615 	case DEVICE_INFO_TYPE_INFORMATION:
1616 		if (hal_fdi_source_information != NULL) {
1617 			s1 = hal_fdi_source_information;
1618 			s2 = NULL;
1619 			s3 = NULL;
1620 		} else {
1621 			s1 = PACKAGE_OLD_DATA_DIR "/hal/fdi/information";
1622 			s2 = PACKAGE_DATA_DIR "/hal/fdi/information";
1623 			s3 = PACKAGE_SYSCONF_DIR "/hal/fdi/information";
1624 		}
1625 		break;
1626 
1627 	case DEVICE_INFO_TYPE_POLICY:
1628 		if (hal_fdi_source_policy != NULL) {
1629 			s1 = hal_fdi_source_policy;
1630 			s2 = NULL;
1631 			s3 = NULL;
1632 		} else {
1633 			s1 = PACKAGE_OLD_DATA_DIR "/hal/fdi/policy";
1634 			s2 = PACKAGE_DATA_DIR "/hal/fdi/policy";
1635 			s3 = PACKAGE_SYSCONF_DIR "/hal/fdi/policy";
1636 		}
1637 		break;
1638 
1639 	default:
1640 		s1 = NULL;
1641 		s2 = NULL;
1642 		s3 = NULL;
1643 		HAL_ERROR (("Bogus device information type %d", type));
1644 		break;
1645 	}
1646 
1647 	if (s1 != NULL)
1648 		ret = scan_fdi_files (s1, d) || ret;
1649 	if (s2 != NULL)
1650 		ret = scan_fdi_files (s2, d) || ret;
1651 	if (s3 != NULL)
1652 		ret = scan_fdi_files (s3, d) || ret;
1653 
1654 	return ret;
1655 }
1656 
1657 /** @} */
1658