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 */
64enum {
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 */
97enum {
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 */
113typedef 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 */
178static gboolean
179resolve_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
248out:
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 */
276static gboolean
277match_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
316out:
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 */
327static dbus_bool_t
328handle_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 */
671static void
672handle_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 */
731static void
732handle_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 */
775static void
776handle_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 */
803static void
804handle_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 */
845static void
846handle_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 */
877static void
878parsing_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 */
891static void
892start (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
1073static void
1074spawned_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 */
1089static void
1090end (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 */
1328static void
1329cdata (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 */
1363static dbus_bool_t
1364process_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
1454out:
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
1469static int
1470#ifdef __GLIBC__
1471my_alphasort(const void *a, const void *b)
1472#else
1473my_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 */
1486static dbus_bool_t
1487scan_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 */
1581dbus_bool_t
1582di_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