start to improve "lot of devices" handling

Kay Sievers kay.sievers at vrfy.org
Wed Feb 8 07:55:01 PST 2006


Attached 3 patches, that:
  hal-10-hash-store-01.patch:
  Chanes the hal device store device lists and attribute lists to hashes
  instead of lists.

  hal-20-fdi-in-memory-01.patch:
  Reads all fdi files once at startup and keeps a linearized match/action
  list in memory. All devices are matched against this linear rules list
  instead of parsing the xml files again.

  hal-30-longer-timeout-01.patch
  Starting with a few thoudsend of devices the current 250 second
  timeout is to small for hald to initialize and it kills itself.

All highly experimental and contains probably bugs, but works for me.

We have a box with ~45.000 block devices (several hundred real disks)
where hal takes 80 hours to finish handling the event queue. lshal takes more
than 10 minutes before the first line is printed. These are numbers from
my laptop for hald initializing and scsi_debug loaded:

  modprobe scsi_debug max_luns=10 num_parts=5 num_tgts=5 add_host=10
  2646 devices
  ------------------------------------------------
  CVS:       5:29 min
  patched:   4:02 min


  modprobe scsi_debug max_luns=10 num_parts=5 num_tgts=5 add_host=30
  10668 devices
  ------------------------------------------------
  CVS:      68:28 min
  patched:  28:32 min

Next is the "is mounted" handling with the parsing of /proc/mounts which is
pretty expensive for thousends of devices.

Thanks,
Kay
-------------- next part --------------
292328a67a82e72facf4165abe2de49348b28158
diff --git a/hald/device.c b/hald/device.c
index 23f744c..24598d3 100644
--- a/hald/device.c
+++ b/hald/device.c
@@ -62,9 +62,9 @@ hal_device_finalize (GObject *obj)
 	printf ("************* in finalize for udi=%s\n", device->udi);
 #endif
 
-	g_slist_foreach (device->properties, (GFunc) hal_property_free, NULL);
-
+	g_hash_table_destroy (device->properties);
 	g_free (device->udi);
+	g_free (device->udi_hashed);
 
 	if (parent_class->finalize)
 		parent_class->finalize (obj);
@@ -77,7 +77,6 @@ hal_device_class_init (HalDeviceClass *k
 	GObjectClass *obj_class = (GObjectClass *) klass;
 
 	parent_class = g_type_class_peek_parent (klass);
-
 	obj_class->finalize = hal_device_finalize;
 
 	signals[PROPERTY_CHANGED] =
@@ -126,19 +125,25 @@ hal_device_class_init (HalDeviceClass *k
 }
 
 static void
+_property_destroy (gpointer data)
+{
+	hal_property_free ((HalProperty *)data);
+}
+
+static void
 hal_device_init (HalDevice *device)
 {
 	static int temp_device_counter = 0;
 
-	device->udi = g_strdup_printf ("/org/freedesktop/Hal/devices/temp/%d",
-				       temp_device_counter++);
+	device->udi = g_strdup_printf ("/org/freedesktop/Hal/devices/temp/%d", temp_device_counter++);
+	device->properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, _property_destroy);
 }
 
 GType
 hal_device_get_type (void)
 {
 	static GType type = 0;
-	
+
 	if (!type) {
 		static GTypeInfo type_info = {
 			sizeof (HalDeviceClass),
@@ -160,7 +165,6 @@ hal_device_get_type (void)
 	return type;
 }
 
-
 HalDevice *
 hal_device_new (void)
 {
@@ -174,6 +178,65 @@ hal_device_new (void)
 	return device;
 }
 
+struct hal_device_merge_with_rewrite_data {
+	HalDevice *target;
+	const char *target_namespace;
+	const char *source_namespace;
+};
+
+static void
+hal_device_merge_with_rewrite_func (gpointer k, gpointer value,
+				    gpointer user_data)
+{
+	HalProperty *p = value;
+	int type;
+	int target_type;
+	const char *key = k;
+	int source_ns_len;
+	struct hal_device_merge_with_rewrite_data *data = user_data;
+	gchar *target_key;
+
+	source_ns_len = strlen(data->source_namespace);
+
+	/* only care about properties that match source namespace */
+	if (strncmp(key, data->source_namespace, source_ns_len) != 0)
+		return;
+
+	target_key = g_strdup_printf("%s%s", data->target_namespace,
+				     key+source_ns_len);
+
+	type = hal_property_get_type (p);
+
+	/* only remove target if it exists with a different type */
+	target_type = hal_device_property_get_type (data->target, key);
+	if (target_type != HAL_PROPERTY_TYPE_INVALID && target_type != type)
+		hal_device_property_remove (data->target, key);
+
+	switch (type) {
+	case HAL_PROPERTY_TYPE_STRING:
+		hal_device_property_set_string (data->target, target_key, hal_property_get_string (p));
+		break;
+	case HAL_PROPERTY_TYPE_INT32:
+		hal_device_property_set_int (data->target, target_key, hal_property_get_int (p));
+		break;
+	case HAL_PROPERTY_TYPE_UINT64:
+		hal_device_property_set_uint64 (data->target, target_key, hal_property_get_uint64 (p));
+		break;
+	case HAL_PROPERTY_TYPE_BOOLEAN:
+		hal_device_property_set_bool (data->target, target_key, hal_property_get_bool (p));
+		break;
+	case HAL_PROPERTY_TYPE_DOUBLE:
+		hal_device_property_set_double (data->target, target_key, hal_property_get_double (p));
+		break;
+	default:
+		HAL_WARNING (("Unknown property type %d", type));
+		break;
+	}
+
+	g_free (target_key);
+
+}
+
 /** Merge all properties from source where the key starts with 
  *  source_namespace and put them onto target replacing source_namespace
  *  with target_namespace
@@ -189,80 +252,72 @@ hal_device_merge_with_rewrite  (HalDevic
 				const char   *target_namespace,
 				const char   *source_namespace)
 {
-	GSList *iter;
-	size_t source_ns_len;
+	struct hal_device_merge_with_rewrite_data data;
 
-	source_ns_len = strlen (source_namespace);
+	data.target = target;
+	data.target_namespace = target_namespace;
+	data.source_namespace = source_namespace;
 
 	/* doesn't handle info.capabilities */
 
 	/* device_property_atomic_update_begin (); */
 
-	for (iter = source->properties; iter != NULL; iter = iter->next) {
-		HalProperty *p = iter->data;
-		int type;
-		const char *key;
-		int target_type;
-		gchar *target_key;
-
-		key = hal_property_get_key (p);
-
-		/* only care about properties that match source namespace */
-		if (strncmp(key, source_namespace, source_ns_len) != 0)
-			continue;
-
-		target_key = g_strdup_printf("%s%s", target_namespace,
-					     key+source_ns_len);
-
-		type = hal_property_get_type (p);
-
-		/* only remove target if it exists with a different type */
-		target_type = hal_device_property_get_type (target, key);
-		if (target_type != HAL_PROPERTY_TYPE_INVALID && target_type != type)
-			hal_device_property_remove (target, key);
-
-		switch (type) {
-
-		case HAL_PROPERTY_TYPE_STRING:
-			hal_device_property_set_string (
-				target, target_key,
-				hal_property_get_string (p));
-			break;
-
-		case HAL_PROPERTY_TYPE_INT32:
-			hal_device_property_set_int (
-				target, target_key,
-				hal_property_get_int (p));
-			break;
+	g_hash_table_foreach(source->properties, hal_device_merge_with_rewrite_func, &data);
 
-		case HAL_PROPERTY_TYPE_UINT64:
-			hal_device_property_set_uint64 (
-				target, target_key,
-				hal_property_get_uint64 (p));
-			break;
+	/* device_property_atomic_update_end (); */
+}
 
-		case HAL_PROPERTY_TYPE_BOOLEAN:
-			hal_device_property_set_bool (
-				target, target_key,
-				hal_property_get_bool (p));
-			break;
+static void
+hal_device_merge_func (gpointer k, gpointer value, gpointer user_data)
+{
+	HalProperty *p = value;
+	int type;
+	const char *key = k;
+	int target_type;
+	HalDevice *target = user_data;
 
-		case HAL_PROPERTY_TYPE_DOUBLE:
-			hal_device_property_set_double (
-				target, target_key,
-				hal_property_get_double (p));
-			break;
+	type = hal_property_get_type (p);
 
-		default:
-			HAL_WARNING (("Unknown property type %d", type));
-			break;
-		}
+	/* handle info.capabilities in a special way */
+	if (strcmp (key, "info.capabilities") == 0)
+		return;
 
-		g_free (target_key);
+	/* only remove target if it exists with a different type */
+	target_type = hal_device_property_get_type (target, key);
+	if (target_type != HAL_PROPERTY_TYPE_INVALID && target_type != type)
+		hal_device_property_remove (target, key);
+
+	switch (type) {
+
+	case HAL_PROPERTY_TYPE_STRING:
+		hal_device_property_set_string (
+			target, key, hal_property_get_string (p));
+		break;
+
+	case HAL_PROPERTY_TYPE_INT32:
+		hal_device_property_set_int (
+			target, key, hal_property_get_int (p));
+		break;
+
+	case HAL_PROPERTY_TYPE_UINT64:
+		hal_device_property_set_uint64 (
+			target, key, hal_property_get_uint64 (p));
+		break;
+
+	case HAL_PROPERTY_TYPE_BOOLEAN:
+		hal_device_property_set_bool (
+			target, key, hal_property_get_bool (p));
+		break;
+
+	case HAL_PROPERTY_TYPE_DOUBLE:
+		hal_device_property_set_double (
+			target, key, hal_property_get_double (p));
+		break;
+
+	default:
+		HAL_WARNING (("Unknown property type %d", type));
+		break;
 	}
-
-	/* device_property_atomic_update_end (); */
-
 }
 
 void
@@ -273,61 +328,7 @@ hal_device_merge (HalDevice *target, Hal
 
 	/* device_property_atomic_update_begin (); */
 
-	for (iter = source->properties; iter != NULL; iter = iter->next) {
-		HalProperty *p = iter->data;
-		int type;
-		const char *key;
-		int target_type;
-
-		key = hal_property_get_key (p);
-		type = hal_property_get_type (p);
-
-		/* handle info.capabilities in a special way */
-		if (strcmp (key, "info.capabilities") == 0)
-			continue;
-
-		/* only remove target if it exists with a different type */
-		target_type = hal_device_property_get_type (target, key);
-		if (target_type != HAL_PROPERTY_TYPE_INVALID && target_type != type)
-			hal_device_property_remove (target, key);
-
-		switch (type) {
-
-		case HAL_PROPERTY_TYPE_STRING:
-			hal_device_property_set_string (
-				target, key,
-				hal_property_get_string (p));
-			break;
-
-		case HAL_PROPERTY_TYPE_INT32:
-			hal_device_property_set_int (
-				target, key,
-				hal_property_get_int (p));
-			break;
-
-		case HAL_PROPERTY_TYPE_UINT64:
-			hal_device_property_set_uint64 (
-				target, key,
-				hal_property_get_uint64 (p));
-			break;
-
-		case HAL_PROPERTY_TYPE_BOOLEAN:
-			hal_device_property_set_bool (
-				target, key,
-				hal_property_get_bool (p));
-			break;
-
-		case HAL_PROPERTY_TYPE_DOUBLE:
-			hal_device_property_set_double (
-				target, key,
-				hal_property_get_double (p));
-			break;
-
-		default:
-			HAL_WARNING (("Unknown property type %d", type));
-			break;
-		}
-	}
+	g_hash_table_foreach (source->properties, hal_device_merge_func, target);
 
 	/* device_property_atomic_update_end (); */
 
@@ -338,72 +339,71 @@ hal_device_merge (HalDevice *target, Hal
 	}
 }
 
-gboolean
-hal_device_matches (HalDevice *device1, HalDevice *device2,
-		    const char *namespace)
-{
-	int len;
-	GSList *iter;
-
-	len = strlen (namespace);
-
-	for (iter = device1->properties; iter != NULL; iter = iter->next) {
-		HalProperty *p;
-		const char *key;
-		int type;
-
-		p = (HalProperty *) iter->data;
-		key = hal_property_get_key (p);
-		type = hal_property_get_type (p);
 
-		if (strncmp (key, namespace, len) != 0)
-			continue;
+struct hal_device_matches_data {
+	HalDevice *device;
+	const char *namespace;
+};
 
-		if (!hal_device_has_property (device2, key))
-			return FALSE;
+static gboolean
+hal_device_matches_func (gpointer k, gpointer value, gpointer user_data)
+{
+	HalProperty *p = value;
+	const char *key = k;
+	int type;
+	struct hal_device_matches_data *data = user_data;
 
-		switch (type) {
+	type = hal_property_get_type (p);
 
-		case HAL_PROPERTY_TYPE_STRING:
-			if (strcmp (hal_property_get_string (p),
-				    hal_device_property_get_string (device2,
-								    key)) != 0)
-				return FALSE;
-			break;
+	if (strncmp (key, data->namespace, strlen(data->namespace)) != 0)
+		return FALSE;
 
-		case HAL_PROPERTY_TYPE_INT32:
-			if (hal_property_get_int (p) !=
-			    hal_device_property_get_int (device2, key))
-				return FALSE;
-			break;
+	if (!hal_device_has_property (data->device, key))
+		return TRUE;
 
-		case HAL_PROPERTY_TYPE_UINT64:
-			if (hal_property_get_uint64 (p) !=
-				hal_device_property_get_uint64 (device2, key))
-				return FALSE;
-			break;
+	switch (type) {
+	case HAL_PROPERTY_TYPE_STRING:
+		if (strcmp (hal_property_get_string (p),
+			    hal_device_property_get_string (data->device, key)) != 0)
+			return TRUE;
+		break;
+	case HAL_PROPERTY_TYPE_INT32:
+		if (hal_property_get_int (p) != hal_device_property_get_int (data->device, key))
+			return TRUE;
+		break;
+	case HAL_PROPERTY_TYPE_UINT64:
+		if (hal_property_get_uint64 (p) != hal_device_property_get_uint64 (data->device, key))
+			return TRUE;
+		break;
+	case HAL_PROPERTY_TYPE_BOOLEAN:
+		if (hal_property_get_bool (p) != hal_device_property_get_bool (data->device, key))
+			return TRUE;
+		break;
+	case HAL_PROPERTY_TYPE_DOUBLE:
+		if (hal_property_get_double (p) != hal_device_property_get_double (data->device, key))
+			return TRUE;
+		break;
+	default:
+		HAL_WARNING (("Unknown property type %d", type));
+		break;
+	}
 
-		case HAL_PROPERTY_TYPE_BOOLEAN:
-			if (hal_property_get_bool (p) !=
-			    hal_device_property_get_bool (device2, key))
-				return FALSE;
-			break;
+	return FALSE;
+}
 
-		case HAL_PROPERTY_TYPE_DOUBLE:
-			if (hal_property_get_double (p) !=
-			    hal_device_property_get_double (device2, key))
-				return FALSE;
-			break;
+gboolean
+hal_device_matches (HalDevice *device1, HalDevice *device2,
+                    const char *namespace)
+{
+	struct hal_device_matches_data data;
 
-		default:
-			HAL_WARNING (("Unknown property type %d", type));
-			break;
-		}
-	}
+	data.device = device2;
+	data.namespace = namespace;
 
-	return TRUE;
+	return !g_hash_table_find (device1->properties, hal_device_matches_func, &data);
 }
 
+
 const char *
 hal_device_get_udi (HalDevice *device)
 {
@@ -461,25 +461,16 @@ hal_device_num_properties (HalDevice *de
 {
 	g_return_val_if_fail (device != NULL, -1);
 
-	return g_slist_length (device->properties);
+	return g_hash_table_size (device->properties);
 }
 
 HalProperty *
 hal_device_property_find (HalDevice *device, const char *key)
 {
-	GSList *iter;
-
 	g_return_val_if_fail (device != NULL, NULL);
 	g_return_val_if_fail (key != NULL, NULL);
 
-	for (iter = device->properties; iter != NULL; iter = iter->next) {
-		HalProperty *p = iter->data;
-
-		if (strcmp (hal_property_get_key (p), key) == 0)
-			return p;
-	}
-
-	return NULL;
+	return (HalProperty *)g_hash_table_lookup (device->properties, key);
 }
 
 char *
@@ -494,25 +485,36 @@ hal_device_property_to_string (HalDevice
 	return hal_property_to_string (prop);
 }
 
+struct hal_property_callback_data {
+	HalDevice *device;
+	HalDevicePropertyForeachFn callback;
+	gpointer user_data;
+};
+
+static gboolean
+hal_device_property_callback (gpointer key, gpointer value, gpointer user_data)
+{
+	struct hal_property_callback_data *data = user_data;
+
+	return (!data->callback (data->device, (HalProperty *)value,
+				 data->user_data));
+}
+
 void
 hal_device_property_foreach (HalDevice *device,
 			     HalDevicePropertyForeachFn callback,
 			     gpointer user_data)
 {
-	GSList *iter;
+	struct hal_property_callback_data data;
 
 	g_return_if_fail (device != NULL);
 	g_return_if_fail (callback != NULL);
 
-	for (iter = device->properties; iter != NULL; iter = iter->next) {
-		HalProperty *p = iter->data;
-		gboolean cont;
-
-		cont = callback (device, p, user_data);
+	data.device = device;
+	data.callback = callback;
+	data.user_data = user_data;
 
-		if (cont == FALSE)
-			return;
-	}
+	g_hash_table_find (device->properties, hal_device_property_callback, &data);
 }
 
 int
@@ -578,14 +580,14 @@ hal_device_property_get_as_string (HalDe
 
 		case HAL_PROPERTY_TYPE_STRLIST:
 			/* print out as "\tval1\tval2\val3\t" */
-		        {
+			{
 				GSList *iter;
 				guint i;
 
 				if (bufsize > 0)
 					buf[0] = '\t';
 				i = 1;
-				for (iter = hal_property_get_strlist (prop); 
+				for (iter = hal_property_get_strlist (prop);
 				     iter != NULL && i < bufsize; 
 				     iter = g_slist_next (iter)) {
 					guint len;
@@ -676,8 +678,7 @@ hal_device_property_get_double (HalDevic
 }
 
 gboolean
-hal_device_property_set_string (HalDevice *device, const char *key,
-				const char *value)
+hal_device_property_set_string (HalDevice *device, const char *key, const char *value)
 {
 	HalProperty *prop;
 
@@ -689,31 +690,23 @@ hal_device_property_set_string (HalDevic
 			return FALSE;
 
 		/* don't bother setting the same value */
-		if (value != NULL &&
-		    strcmp (hal_property_get_string (prop), value) == 0)
+		if (value != NULL && strcmp (hal_property_get_string (prop), value) == 0)
 			return TRUE;
 
 		hal_property_set_string (prop, value);
 
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, FALSE);
-
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, FALSE);
 	} else {
-
 		prop = hal_property_new_string (key, value);
-
-		device->properties = g_slist_prepend (device->properties, prop);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, TRUE);
+		g_hash_table_insert (device->properties, (char *)key, prop);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, TRUE);
 	}
 
 	return TRUE;
 }
 
 gboolean
-hal_device_property_set_int (HalDevice *device, const char *key,
-			     dbus_int32_t value)
+hal_device_property_set_int (HalDevice *device, const char *key, dbus_int32_t value)
 {
 	HalProperty *prop;
 
@@ -729,25 +722,19 @@ hal_device_property_set_int (HalDevice *
 			return TRUE;
 
 		hal_property_set_int (prop, value);
-
 		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
 			       key, FALSE, FALSE);
-
 	} else {
 		prop = hal_property_new_int (key, value);
-
-		device->properties = g_slist_prepend (device->properties, prop);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, TRUE);
+		g_hash_table_insert (device->properties, (char *)key, prop);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, TRUE);
 	}
 
 	return TRUE;
 }
 
 gboolean
-hal_device_property_set_uint64 (HalDevice *device, const char *key,
-			     dbus_uint64_t value)
+hal_device_property_set_uint64 (HalDevice *device, const char *key, dbus_uint64_t value)
 {
 	HalProperty *prop;
 
@@ -763,25 +750,18 @@ hal_device_property_set_uint64 (HalDevic
 			return TRUE;
 
 		hal_property_set_uint64 (prop, value);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, FALSE);
-
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, FALSE);
 	} else {
 		prop = hal_property_new_uint64 (key, value);
-
-		device->properties = g_slist_prepend (device->properties, prop);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, TRUE);
+		g_hash_table_insert (device->properties, (char *)key, prop);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, TRUE);
 	}
 
 	return TRUE;
 }
 
 gboolean
-hal_device_property_set_bool (HalDevice *device, const char *key,
-			     dbus_bool_t value)
+hal_device_property_set_bool (HalDevice *device, const char *key, dbus_bool_t value)
 {
 	HalProperty *prop;
 
@@ -797,15 +777,11 @@ hal_device_property_set_bool (HalDevice 
 			return TRUE;
 
 		hal_property_set_bool (prop, value);
-
 		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
 			       key, FALSE, FALSE);
-
 	} else {
 		prop = hal_property_new_bool (key, value);
-
-		device->properties = g_slist_prepend (device->properties, prop);
-
+		g_hash_table_insert (device->properties, (char *)key, prop);
 		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
 			       key, FALSE, TRUE);
 	}
@@ -814,8 +790,7 @@ hal_device_property_set_bool (HalDevice 
 }
 
 gboolean
-hal_device_property_set_double (HalDevice *device, const char *key,
-				double value)
+hal_device_property_set_double (HalDevice *device, const char *key, double value)
 {
 	HalProperty *prop;
 
@@ -831,17 +806,11 @@ hal_device_property_set_double (HalDevic
 			return TRUE;
 
 		hal_property_set_double (prop, value);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, FALSE);
-
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, FALSE);
 	} else {
 		prop = hal_property_new_double (key, value);
-
-		device->properties = g_slist_prepend (device->properties, prop);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, TRUE);
+		g_hash_table_insert (device->properties, (char *)key, prop);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, TRUE);
 	}
 
 	return TRUE;
@@ -885,19 +854,8 @@ hal_device_copy_property (HalDevice *fro
 gboolean
 hal_device_property_remove (HalDevice *device, const char *key)
 {
-	HalProperty *prop;
-
-	prop = hal_device_property_find (device, key);
-
-	if (prop == NULL)
-		return FALSE;
-
-	device->properties = g_slist_remove (device->properties, prop);
-
-	hal_property_free (prop);
-
-	g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-		       key, TRUE, FALSE);
+	g_hash_table_remove (device->properties, key);
+	g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, TRUE, FALSE);
 
 	return TRUE;
 }
@@ -911,63 +869,59 @@ hal_device_property_set_attribute (HalDe
 	HalProperty *prop;
 
 	prop = hal_device_property_find (device, key);
-
 	if (prop == NULL)
 		return FALSE;
 
 	return TRUE;
 }
 
-void
-hal_device_print (HalDevice *device)
+
+static void
+hal_device_print_func (gpointer k, gpointer value, gpointer user_data)
 {
-	GSList *iter;
+	HalProperty *p = value;
+	int type;
+	char *key = k;
+
+	type = hal_property_get_type (p);
+
+	switch (type) {
+	case HAL_PROPERTY_TYPE_STRING:
+		fprintf (stderr, "  %s = '%s'  (string)\n", key,
+			 hal_property_get_string (p));
+		break;
+	case HAL_PROPERTY_TYPE_INT32:
+		fprintf (stderr, "  %s = %d  0x%x  (int)\n", key,
+			 hal_property_get_int (p),
+			 hal_property_get_int (p));
+		break;
+	case HAL_PROPERTY_TYPE_UINT64:
+		fprintf (stderr, "  %s = %lld  0x%llx  (uint64)\n", key,
+			 hal_property_get_uint64 (p),
+			 hal_property_get_uint64 (p));
+		break;
+	case HAL_PROPERTY_TYPE_DOUBLE:
+		fprintf (stderr, "  %s = %g  (double)\n", key,
+			 hal_property_get_double (p));
+		break;
+	case HAL_PROPERTY_TYPE_BOOLEAN:
+		fprintf (stderr, "  %s = %s  (bool)\n", key,
+			 (hal_property_get_bool (p) ? "true" :
+			  "false"));
+		break;
+	default:
+		HAL_WARNING (("Unknown property type %d", type));
+		break;
+	}
+}
 
-        fprintf (stderr, "device udi = %s\n", hal_device_get_udi (device));
 
-	for (iter = device->properties; iter != NULL; iter = iter->next) {
-		HalProperty *p = iter->data;
-                int type;
-                const char *key;
-
-                key = hal_property_get_key (p);
-                type = hal_property_get_type (p);
-
-                switch (type) {
-                case HAL_PROPERTY_TYPE_STRING:
-                        fprintf (stderr, "  %s = '%s'  (string)\n", key,
-                                hal_property_get_string (p));
-                        break;
- 
-                case HAL_PROPERTY_TYPE_INT32:
-                        fprintf (stderr, "  %s = %d  0x%x  (int)\n", key,
-                                hal_property_get_int (p),
-                                hal_property_get_int (p));
-                        break;
- 
-                case HAL_PROPERTY_TYPE_UINT64:
-                        fprintf (stderr, "  %s = %lld  0x%llx  (uint64)\n", key,
-                                hal_property_get_uint64 (p),
-                                hal_property_get_uint64 (p));
-                        break;
- 
-                case HAL_PROPERTY_TYPE_DOUBLE:
-                        fprintf (stderr, "  %s = %g  (double)\n", key,
-                                hal_property_get_double (p));
-                        break;
- 
-                case HAL_PROPERTY_TYPE_BOOLEAN:
-                        fprintf (stderr, "  %s = %s  (bool)\n", key,
-                                (hal_property_get_bool (p) ? "true" :
-                                 "false"));
-                        break;
- 
-                default:
-                        HAL_WARNING (("Unknown property type %d", type));
-                        break;
-                }
-        }
-        fprintf (stderr, "\n");
+void
+hal_device_print (HalDevice *device)
+{
+	fprintf (stderr, "device udi = %s\n", hal_device_get_udi (device));
+	g_hash_table_foreach (device->properties, hal_device_print_func, NULL);
+	fprintf (stderr, "\n");
 }
 
 
@@ -1004,7 +958,6 @@ prop_changed_cb (HalDevice *device, cons
 	if (removed)
 		goto cleanup;
 
-
 	ai->callback (ai->device, ai->user_data, TRUE);
 
 cleanup:
@@ -1018,7 +971,6 @@ async_wait_timeout (gpointer user_data)
 	AsyncMatchInfo *ai = (AsyncMatchInfo *) user_data;
 
 	ai->callback (ai->device, ai->user_data, FALSE);
-
 	destroy_async_match_info (ai);
 
 	return FALSE;
@@ -1043,16 +995,13 @@ hal_device_async_wait_property (HalDevic
 	}
 
 	ai = g_new0 (AsyncMatchInfo, 1);
-
 	ai->device = g_object_ref (device);
 	ai->key = g_strdup (key);
 	ai->callback = callback;
 	ai->user_data = user_data;
-
 	ai->prop_signal_id = g_signal_connect (device, "property_changed",
 					       G_CALLBACK (prop_changed_cb),
 					       ai);
-
 	ai->timeout_id = g_timeout_add (timeout, async_wait_timeout, ai);
 }
 
@@ -1071,9 +1020,6 @@ hal_device_cancel (HalDevice *device)
 	g_signal_emit (device, signals[CANCELLED], 0);
 }
 
-
-
-
 GSList *
 hal_device_property_get_strlist (HalDevice    *device, 
 				 const char   *key)
@@ -1125,18 +1071,13 @@ hal_device_property_strlist_append (HalD
 			return FALSE;
 
 		hal_property_strlist_append (prop, value);
-
 		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
 			       key, FALSE, FALSE);
-
 	} else {
 		prop = hal_property_new_strlist (key);
 		hal_property_strlist_append (prop, value);
-
-		device->properties = g_slist_prepend (device->properties, prop);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, TRUE);
+		g_hash_table_insert (device->properties, (char *)key, prop);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, TRUE);
 	}
 
 	return TRUE;
@@ -1157,18 +1098,12 @@ hal_device_property_strlist_prepend (Hal
 			return FALSE;
 
 		hal_property_strlist_prepend (prop, value);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, FALSE);
-
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, FALSE);
 	} else {
 		prop = hal_property_new_strlist (key);
 		hal_property_strlist_prepend (prop, value);
-
-		device->properties = g_slist_prepend (device->properties, prop);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, TRUE);
+		g_hash_table_insert (device->properties, (char *)key, prop);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, TRUE);
 	}
 
 	return TRUE;
@@ -1189,13 +1124,12 @@ hal_device_property_strlist_remove_elem 
 
 	if (hal_property_get_type (prop) != HAL_PROPERTY_TYPE_STRLIST)
 		return FALSE;
-	
+
 	if (hal_property_strlist_remove_elem (prop, index)) {
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, FALSE);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, FALSE);
 		return TRUE;
 	}
-	
+
 	return FALSE;
 }
 
@@ -1210,12 +1144,8 @@ hal_device_property_strlist_clear (HalDe
 
 	if (prop == NULL) {
 		prop = hal_property_new_strlist (key);
-
-		device->properties = g_slist_prepend (device->properties, prop);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, TRUE);
-
+		g_hash_table_insert (device->properties, (char *)key, prop);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, TRUE);
 		return TRUE;
 	}
 
@@ -1223,11 +1153,10 @@ hal_device_property_strlist_clear (HalDe
 		return FALSE;
 	
 	if (hal_property_strlist_clear (prop)) {
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, FALSE);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, FALSE);
 		return TRUE;
 	}
-	
+
 	return FALSE;
 }
 
@@ -1250,20 +1179,13 @@ hal_device_property_strlist_add (HalDevi
 			goto out;
 
 		res = hal_property_strlist_add (prop, value);
-		if (res) {
-			g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-				       key, FALSE, FALSE);
-		}
-
+		if (res)
+			g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, FALSE);
 	} else {
 		prop = hal_property_new_strlist (key);
 		hal_property_strlist_prepend (prop, value);
-
-		device->properties = g_slist_prepend (device->properties, prop);
-
-		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
-			       key, FALSE, TRUE);
-
+		g_hash_table_insert (device->properties, (char *)key, prop);
+		g_signal_emit (device, signals[PROPERTY_CHANGED], 0, key, FALSE, TRUE);
 		res = TRUE;
 	}
 
@@ -1286,11 +1208,11 @@ hal_device_property_strlist_remove (HalD
 
 	if (hal_property_get_type (prop) != HAL_PROPERTY_TYPE_STRLIST)
 		return FALSE;
-	
+
 	if (hal_property_strlist_remove (prop, value)) {
 		g_signal_emit (device, signals[PROPERTY_CHANGED], 0,
 			       key, FALSE, FALSE);
 	}
-	
+
 	return TRUE;
 }
diff --git a/hald/device.h b/hald/device.h
index 28bc677..ad60645 100644
--- a/hald/device.h
+++ b/hald/device.h
@@ -37,26 +37,20 @@ typedef struct _HalDeviceClass HalDevice
 
 struct _HalDevice {
 	GObject parent;
-
 	char *udi;
-	
-	GSList *properties;
+	char *udi_hashed;
+	GHashTable *properties;
 };
 
 struct _HalDeviceClass {
 	GObjectClass parent_class;
-
-	/* signals */
 	void (*property_changed) (HalDevice *device,
 				  const char *key,
 				  gboolean removed,
 				  gboolean added);
-
 	void (*capability_added) (HalDevice *device,
 				  const char *capability);
-
 	void (*callouts_finished) (HalDevice *device);
-
 	void (*cancelled) (HalDevice *device);
 };
 
diff --git a/hald/device_info.c b/hald/device_info.c
index a140c6e..b2535b3 100644
--- a/hald/device_info.c
+++ b/hald/device_info.c
@@ -347,20 +347,17 @@ handle_match (ParsingContext * pc, const
 	}
 
 	d = hal_device_store_find (hald_get_gdl (), udi_to_check);
-	if (d == NULL) {
+	if (d == NULL)
 		d = hal_device_store_find (hald_get_tdl (), udi_to_check);
-	}
 	if (d == NULL) {
 		HAL_ERROR (("Could not find device with udi '%s'", udi_to_check));
 		return FALSE;
 	}
-	
 
 	if (strcmp (attr[2], "string") == 0) {
 		const char *value;
 
 		/* match string property */
-
 		value = attr[3];
 
 		/*HAL_INFO(("Checking that key='%s' is a string that "
@@ -369,8 +366,7 @@ handle_match (ParsingContext * pc, const
 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
 			return FALSE;
 
-		if (strcmp (hal_device_property_get_string (d, prop_to_check),
-			    value) != 0)
+		if (strcmp (hal_device_property_get_string (d, prop_to_check), value) != 0)
 			return FALSE;
 
 		/*HAL_INFO (("*** string match for key %s", key));*/
@@ -380,7 +376,7 @@ handle_match (ParsingContext * pc, const
 
 		/* match integer property */
 		value = strtol (attr[3], NULL, 0);
-		
+
 		/** @todo Check error condition */
 
 		/*HAL_INFO (("Checking that key='%s' is a int that equals %d", 
diff --git a/hald/device_store.c b/hald/device_store.c
index 7981417..769caa0 100644
--- a/hald/device_store.c
+++ b/hald/device_store.c
@@ -47,11 +47,21 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 static void
+_finalize_func (gpointer key,
+		gpointer value,
+		gpointer user_data)
+{
+	g_object_unref (value);
+}
+
+
+static void
 hal_device_store_finalize (GObject *obj)
 {
 	HalDeviceStore *store = HAL_DEVICE_STORE (obj);
 
-	g_slist_foreach (store->devices, (GFunc) g_object_unref, NULL);
+	g_hash_table_foreach (store->devices, _finalize_func, NULL);
+	g_hash_table_destroy (store->devices);
 
 	if (parent_class->finalize)
 		parent_class->finalize (obj);
@@ -61,17 +71,14 @@ static void
 hal_device_store_class_init (HalDeviceStoreClass *klass)
 {
 	GObjectClass *obj_class = (GObjectClass *) klass;
-
 	parent_class = g_type_class_peek_parent (klass);
-
 	obj_class->finalize = hal_device_store_finalize;
 
 	signals[STORE_CHANGED] =
 		g_signal_new ("store_changed",
 			      G_TYPE_FROM_CLASS (klass),
 			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (HalDeviceStoreClass,
-					       store_changed),
+			      G_STRUCT_OFFSET (HalDeviceStoreClass, store_changed),
 			      NULL, NULL,
 			      hald_marshal_VOID__OBJECT_BOOL,
 			      G_TYPE_NONE, 2,
@@ -82,8 +89,7 @@ hal_device_store_class_init (HalDeviceSt
 		g_signal_new ("device_property_changed",
 			      G_TYPE_FROM_CLASS (klass),
 			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (HalDeviceStoreClass,
-					       device_property_changed),
+			      G_STRUCT_OFFSET (HalDeviceStoreClass, device_property_changed),
 			      NULL, NULL,
 			      hald_marshal_VOID__OBJECT_STRING_BOOL_BOOL,
 			      G_TYPE_NONE, 4,
@@ -96,8 +102,7 @@ hal_device_store_class_init (HalDeviceSt
 		g_signal_new ("device_capability_added",
 			      G_TYPE_FROM_CLASS (klass),
 			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (HalDeviceStoreClass,
-					       device_capability_added),
+			      G_STRUCT_OFFSET (HalDeviceStoreClass, device_capability_added),
 			      NULL, NULL,
 			      hald_marshal_VOID__OBJECT_STRING,
 			      G_TYPE_NONE, 2,
@@ -106,15 +111,16 @@ hal_device_store_class_init (HalDeviceSt
 }
 
 static void
-hal_device_store_init (HalDeviceStore *device)
+hal_device_store_init (HalDeviceStore *store)
 {
+	store->devices = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
 GType
 hal_device_store_get_type (void)
 {
 	static GType type = 0;
-	
+
 	if (!type) {
 		static GTypeInfo type_info = {
 			sizeof (HalDeviceStoreClass),
@@ -172,35 +178,37 @@ emit_device_capability_added (HalDevice 
 void
 hal_device_store_add (HalDeviceStore *store, HalDevice *device)
 {
-	const char buf[] = "/org/freedesktop/Hal/devices/";
+	const char udi_prefix[] = "/org/freedesktop/Hal/devices/";
 
-	if (strncmp(device->udi, buf, sizeof (buf) - 1) != 0) {
-		
+	if (strncmp(device->udi, udi_prefix, sizeof (udi_prefix)-1) != 0) {
 		HAL_ERROR(("Can't add HalDevice with incorrect UDI. Valid "
 			   "UDI must start with '/org/freedesktop/Hal/devices/'"));
-		goto out;
+		return;
 	}
-	store->devices = g_slist_prepend (store->devices,
-					  g_object_ref (device));
+
+	/* remember hash key, for "remove" lookup */
+	g_object_ref (device);
+	g_free (device->udi_hashed);
+	device->udi_hashed = g_strdup (device->udi);
+	g_hash_table_insert (store->devices, device->udi_hashed, device);
 
 	g_signal_connect (device, "property_changed",
 			  G_CALLBACK (emit_device_property_changed), store);
 	g_signal_connect (device, "capability_added",
 			  G_CALLBACK (emit_device_capability_added), store);
-
 	g_signal_emit (store, signals[STORE_CHANGED], 0, device, TRUE);
-
-out:
-	;
 }
 
 gboolean
 hal_device_store_remove (HalDeviceStore *store, HalDevice *device)
 {
-	if (!g_slist_find (store->devices, device))
+	if (device->udi_hashed == NULL)
 		return FALSE;
 
-	store->devices = g_slist_remove (store->devices, device);
+	if (!g_hash_table_remove (store->devices, device->udi_hashed))
+		return FALSE;
+	g_free (device->udi_hashed);
+	device->udi_hashed = NULL;
 
 	g_signal_handlers_disconnect_by_func (device,
 					      emit_device_property_changed,
@@ -208,102 +216,137 @@ hal_device_store_remove (HalDeviceStore 
 	g_signal_handlers_disconnect_by_func (device,
 					      emit_device_capability_added,
 					      store);
-
 	g_signal_emit (store, signals[STORE_CHANGED], 0, device, FALSE);
 
 	g_object_unref (device);
-
 	return TRUE;
 }
 
+
+static gboolean
+_find_func (gpointer key,
+	    gpointer value,
+	    gpointer user_data)
+{
+	HalDevice *device = value;
+	const char *udi = user_data;
+
+	if (strcmp(device->udi, udi) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
 HalDevice *
 hal_device_store_find (HalDeviceStore *store, const char *udi)
 {
-	GSList *iter;
+	HalDevice *device;
 
-	for (iter = store->devices; iter != NULL; iter = iter->next) {
-		HalDevice *d = iter->data;
+	/* lookup by udi */
+	device = g_hash_table_lookup(store->devices, udi);
 
-		if (strcmp (hal_device_get_udi (d), udi) == 0)
-			return d;
-	}
+	/* in the TDL we change the udi, so lookup by the udi not the hash key */
+	if (device == NULL)
+		device = g_hash_table_find (store->devices, _find_func, (gpointer) udi);
 
-	return NULL;
+	return device;
 }
 
 void
 hal_device_store_foreach (HalDeviceStore *store,
-			  HalDeviceStoreForeachFn callback,
+			  GHFunc callback,
 			  gpointer user_data)
 {
-	GSList *iter;
-
 	g_return_if_fail (store != NULL);
 	g_return_if_fail (callback != NULL);
 
-	for (iter = store->devices; iter != NULL; iter = iter->next) {
-		HalDevice *d = HAL_DEVICE (iter->data);
-		gboolean cont;
-
-		cont = callback (store, d, user_data);
-
-		if (cont == FALSE)
-			return;
-	}
+	g_hash_table_foreach (store->devices, callback, user_data);
 }
 
-static gboolean
-hal_device_store_print_foreach_fn (HalDeviceStore *store,
-				   HalDevice *device,
+static void
+hal_device_store_print_foreach_fn (gpointer key,
+				   gpointer value,
 				   gpointer user_data)
 {
+	HalDevice *device = value;
+
 	fprintf (stderr, "----\n");
 	hal_device_print (device);
 	fprintf (stderr, "----\n");
-	return TRUE;
 }
 
 void 
 hal_device_store_print (HalDeviceStore *store)
 {
 	fprintf (stderr, "===============================================\n");
-        fprintf (stderr, "Dumping %d devices\n", 
-		 g_slist_length (store->devices));
+	fprintf (stderr, "Dumping %d devices\n",
+		 g_hash_table_size (store->devices));
 	fprintf (stderr, "===============================================\n");
-	hal_device_store_foreach (store, 
-				  hal_device_store_print_foreach_fn, 
+	hal_device_store_foreach (store,
+				  hal_device_store_print_foreach_fn,
 				  NULL);
 	fprintf (stderr, "===============================================\n");
 }
 
+struct _key_value {
+	const char *key;
+	const char *value_string;
+	int value_int;
+	GSList *matches;
+};
+
+static gboolean
+_match_key_value_string_func (gpointer key,
+			      gpointer value,
+			      gpointer user_data)
+{
+	HalDevice *device = value;
+	struct _key_value *key_val = user_data;
+	const char *prop = key_val->key;
+
+	if (!hal_device_has_property (device, prop))
+		return FALSE;
+	if (hal_device_property_get_type (device, prop) != HAL_PROPERTY_TYPE_STRING)
+		return FALSE;
+	if (strcmp (hal_device_property_get_string (device, prop), key_val->value_string) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
 HalDevice *
 hal_device_store_match_key_value_string (HalDeviceStore *store,
 					 const char *key,
 					 const char *value)
 {
-	GSList *iter;
+	struct _key_value key_val;
 
 	g_return_val_if_fail (store != NULL, NULL);
 	g_return_val_if_fail (key != NULL, NULL);
 	g_return_val_if_fail (value != NULL, NULL);
 
-	for (iter = store->devices; iter != NULL; iter = iter->next) {
-		HalDevice *d = HAL_DEVICE (iter->data);
-		int type;
-
-		if (!hal_device_has_property (d, key))
-			continue;
-
-		type = hal_device_property_get_type (d, key);
-		if (type != HAL_PROPERTY_TYPE_STRING)
-			continue;
-
-		if (strcmp (hal_device_property_get_string (d, key),
-			    value) == 0)
-			return d;
-	}
+	key_val.key = key;
+	key_val.value_string = value;
+	return g_hash_table_find (store->devices, _match_key_value_string_func, &key_val);
+}
+
+static gboolean
+_match_key_value_int_func (gpointer key,
+			   gpointer value,
+			   gpointer user_data)
+{
+	HalDevice *device = value;
+	struct _key_value *key_val = user_data;
+	const char *prop = key_val->key;
 
-	return NULL;
+	if (!hal_device_has_property (device, prop))
+		return FALSE;
+	if (hal_device_property_get_type (device, prop) != HAL_PROPERTY_TYPE_INT32)
+		return FALSE;
+	if (hal_device_property_get_int (device, prop) == key_val->value_int)
+		return TRUE;
+
+	return FALSE;
 }
 
 HalDevice *
@@ -311,27 +354,33 @@ hal_device_store_match_key_value_int (Ha
 				      const char *key,
 				      int value)
 {
-	GSList *iter;
+	struct _key_value key_val;
 
 	g_return_val_if_fail (store != NULL, NULL);
 	g_return_val_if_fail (key != NULL, NULL);
 
-	for (iter = store->devices; iter != NULL; iter = iter->next) {
-		HalDevice *d = HAL_DEVICE (iter->data);
-		int type;
-
-		if (!hal_device_has_property (d, key))
-			continue;
-
-		type = hal_device_property_get_type (d, key);
-		if (type != HAL_PROPERTY_TYPE_INT32)
-			continue;
+	key_val.key = key;
+	key_val.value_int = value;
+	return g_hash_table_find (store->devices, _match_key_value_int_func, &key_val);
+}
+
+static void
+_match_multiple_key_value_string_func (gpointer key,
+				       gpointer value,
+				       gpointer user_data)
+{
+	HalDevice *device = value;
+	struct _key_value *key_val = user_data;
+	const char *prop = key_val->key;
 
-		if (hal_device_property_get_int (d, key) == value)
-			return d;
-	}
+	if (!hal_device_has_property (device, prop))
+		return;
 
-	return NULL;
+	if (hal_device_property_get_type (device, prop) != HAL_PROPERTY_TYPE_STRING)
+		return;
+
+	if (strcmp (hal_device_property_get_string (device, prop), key_val->value_string) == 0)
+		key_val->matches = g_slist_prepend (key_val->matches, device);
 }
 
 GSList *
@@ -339,30 +388,18 @@ hal_device_store_match_multiple_key_valu
 						  const char *key,
 						  const char *value)
 {
-	GSList *iter;
-	GSList *matches = NULL;
+	struct _key_value key_val;
 
 	g_return_val_if_fail (store != NULL, NULL);
 	g_return_val_if_fail (key != NULL, NULL);
 	g_return_val_if_fail (value != NULL, NULL);
 
-	for (iter = store->devices; iter != NULL; iter = iter->next) {
-		HalDevice *d = HAL_DEVICE (iter->data);
-		int type;
-
-		if (!hal_device_has_property (d, key))
-			continue;
-
-		type = hal_device_property_get_type (d, key);
-		if (type != HAL_PROPERTY_TYPE_STRING)
-			continue;
-
-		if (strcmp (hal_device_property_get_string (d, key),
-			    value) == 0)
-			matches = g_slist_prepend (matches, d);
-	}
+	key_val.key = key;
+	key_val.value_string = value;
+	key_val.matches = NULL;
 
-	return matches;
+	g_hash_table_foreach (store->devices, _match_multiple_key_value_string_func, &key_val);
+	return key_val.matches;
 }
 
 typedef struct {
@@ -371,7 +408,6 @@ typedef struct {
 	char *value;
 	HalDeviceStoreAsyncCallback callback;
 	gpointer user_data;
-
 	guint prop_signal_id;
 	guint store_signal_id;
 	guint timeout_id;
@@ -381,14 +417,11 @@ static void
 destroy_async_match_info (AsyncMatchInfo *info)
 {
 	g_object_unref (info->store);
-
 	g_free (info->key);
 	g_free (info->value);
-
 	g_signal_handler_disconnect (info->store, info->prop_signal_id);
 	g_signal_handler_disconnect (info->store, info->store_signal_id);
 	g_source_remove (info->timeout_id);
-
 	g_free (info);
 }
 
diff --git a/hald/device_store.h b/hald/device_store.h
index 4abfdfc..360b460 100644
--- a/hald/device_store.h
+++ b/hald/device_store.h
@@ -37,7 +37,7 @@ typedef struct _HalDeviceStoreClass HalD
 struct _HalDeviceStore {
 	GObject parent;
 
-	GSList *devices;
+	GHashTable *devices;
 };
 
 struct _HalDeviceStoreClass {
@@ -76,11 +76,6 @@ typedef void     (*HalDeviceStoreAsyncCa
 						 HalDevice      *device,
 						 gpointer        user_data);
 
-/* Return value of FALSE means that the foreach should be short-circuited */
-typedef gboolean (*HalDeviceStoreForeachFn) (HalDeviceStore *store,
-					     HalDevice      *device,
-					     gpointer        user_data);
-					     
 GType           hal_device_store_get_type   (void);
 
 HalDeviceStore *hal_device_store_new        (void);
@@ -94,7 +89,7 @@ HalDevice      *hal_device_store_find   
 					     const char     *udi);
 
 void            hal_device_store_foreach    (HalDeviceStore *store,
-					     HalDeviceStoreForeachFn callback,
+					     GHFunc callback,
 					     gpointer user_data);
 
 HalDevice      *hal_device_store_match_key_value_string (HalDeviceStore *store,
diff --git a/hald/hald.c b/hald/hald.c
index 69a88ad..0ce85a6 100644
--- a/hald/hald.c
+++ b/hald/hald.c
@@ -97,7 +97,7 @@ gdl_store_changed (HalDeviceStore *store
 		}
 	} else {
 		HAL_INFO (("Removed device from GDL; udi=%s", hal_device_get_udi(device)));
-    hald_runner_kill_device(device);
+		hald_runner_kill_device(device);
 	}
 
 	/*hal_device_print (device);*/
@@ -590,23 +590,28 @@ main (int argc, char *argv[])
 extern int dbg_hal_device_object_delta;
 
 /* useful for valgrinding; see below */
+static void
+_my_shutdown_remove_func (gpointer key,
+			  gpointer value,
+			  gpointer user_data)
+{
+	HalDevice *device = value;
+	HalDeviceStore *gdl = user_data;
+
+	hal_device_store_remove (gdl, device);
+	g_object_unref (device);
+}
+
 static gboolean
 my_shutdown (gpointer data)
 {
-	HalDeviceStore *gdl;
-	
-	printf ("Num devices in TDL: %d\n", g_slist_length ((hald_get_tdl ())->devices));
-	printf ("Num devices in GDL: %d\n", g_slist_length ((hald_get_gdl ())->devices));
-	
-	gdl = hald_get_gdl ();
-next:
-	if (g_slist_length (gdl->devices) > 0) {
-		HalDevice *d = HAL_DEVICE(gdl->devices->data);
-		hal_device_store_remove (gdl, d);
-		g_object_unref (d);
-		goto next;
-	}
-	
+	HalDeviceStore *gdl = hald_get_gdl ();
+
+	printf ("Num devices in TDL: %d\n", g_hash_table_size ((hald_get_tdl ())->devices));
+	printf ("Num devices in GDL: %d\n", g_hash_table_size ((hald_get_gdl ())->devices));
+
+	g_hash_table_foreach (gdl->devices, _my_shutdown_remove_func, gdl);
+
 	printf ("hal_device_object_delta = %d (should be zero)\n", dbg_hal_device_object_delta);
 	exit (1);
 }
diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index f875f6e..8d710dd 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -247,17 +247,15 @@ raise_permission_denied (DBusConnection 
  * @{
  */
 
-static gboolean
-foreach_device_get_udi (HalDeviceStore *store, HalDevice *device,
+static void
+foreach_device_get_udi (gpointer key, gpointer value,
 			gpointer user_data)
 {
+	HalDevice *d = value;
+	const char *udi = hal_device_get_udi (d);
 	DBusMessageIter *iter = user_data;
-	const char *udi;
 
-	udi = hal_device_get_udi (device);
 	dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &udi);
-
-	return TRUE;
 }
 
 /** Get all devices.
@@ -308,58 +306,53 @@ typedef struct {
 	DBusMessageIter *iter;
 } DeviceMatchInfo;
 
-static gboolean
-foreach_device_match_get_udi (HalDeviceStore *store, HalDevice *device,
+static void
+foreach_device_match_get_udi (gpointer key, gpointer value,
 			      gpointer user_data)
 {
+	HalDevice *d = value;
 	DeviceMatchInfo *info = user_data;
 	const char *dev_value;
 
-	if (hal_device_property_get_type (device,
-					  info->key) != DBUS_TYPE_STRING)
-		return TRUE;
+	if (hal_device_property_get_type (d, info->key) != DBUS_TYPE_STRING)
+		return;
 
-	dev_value = hal_device_property_get_string (device, info->key);
+	dev_value = hal_device_property_get_string (d, info->key);
 
 	if (dev_value != NULL && strcmp (dev_value, info->value) == 0) {
 		const char *udi;
-		udi =  hal_device_get_udi (device);
+		udi =  hal_device_get_udi (d);
 		dbus_message_iter_append_basic  (info->iter,
 						 DBUS_TYPE_STRING,
 						 &udi);
 	}
-
-	return TRUE;
 }
 
-static gboolean
-foreach_device_match_get_udi_tdl (HalDeviceStore *store, HalDevice *device,
+static void
+foreach_device_match_get_udi_tdl (gpointer key, gpointer value,
 				  gpointer user_data)
 {
+	HalDevice *d = value;
 	DeviceMatchInfo *info = user_data;
 	const char *dev_value;
+	const char temp_udi[] = "/org/freedesktop/Hal/devices/temp";
 
-	/* skip devices in the TDL that hasn't got a real UDI yet */
-	if (strncmp (device->udi, "/org/freedesktop/Hal/devices/temp",
-		     sizeof ("/org/freedesktop/Hal/devices/temp")) == 0)
-		return TRUE;
+	/* skip devices in the TDL that haven't got a real UDI yet */
+	if (strncmp (d->udi, temp_udi, sizeof (temp_udi)-1) == 0)
+		return;
 
-	if (hal_device_property_get_type (device,
-					  info->key) != DBUS_TYPE_STRING)
-		return TRUE;
+	if (hal_device_property_get_type (d, info->key) != DBUS_TYPE_STRING)
+		return;
 
-	dev_value = hal_device_property_get_string (device, info->key);
+	dev_value = hal_device_property_get_string (d, info->key);
 
 	if (dev_value != NULL && strcmp (dev_value, info->value) == 0) {
-		const char *udi;
-		udi = hal_device_get_udi (device);
+		const char *udi = hal_device_get_udi (d);
 
 		dbus_message_iter_append_basic (info->iter,
 						DBUS_TYPE_STRING,
 						&udi);
 	}
-
-	return TRUE;
 }
 
 /** Find devices in the GDL where a single string property matches a given
@@ -436,18 +429,17 @@ typedef struct {
 	DBusMessageIter *iter;
 } DeviceCapabilityInfo;
 
-static gboolean
-foreach_device_by_capability (HalDeviceStore *store, HalDevice *device, gpointer user_data)
+static void
+foreach_device_by_capability (gpointer key, gpointer value, gpointer user_data)
 {
+	HalDevice *d = value;
 	DeviceCapabilityInfo *info = (DeviceCapabilityInfo *) user_data;
 
-	if (hal_device_has_capability (device, info->capability)) {
+	if (hal_device_has_capability (d, info->capability)) {
 		dbus_message_iter_append_basic (info->iter,
 						DBUS_TYPE_STRING,
-						&(device->udi));
+						&(d->udi));
 	}
-
-	return TRUE;
 }
 
 /** Find devices in the GDL with a given capability.
@@ -2251,7 +2243,8 @@ manager_new_device (DBusConnection * con
 	gettimeofday(&tv, NULL);
 	for (i = 0; i < 1000000 ; i++) {
 		udi = g_strdup_printf ("/org/freedesktop/Hal/devices/tmp%05x", ((unsigned) tv.tv_usec & 0xfffff)) + i;
-		if (!hal_device_store_find (hald_get_tdl (), udi)) break;
+		if (!hal_device_store_find (hald_get_tdl (), udi))
+			break;
 		g_free (udi);
 		udi = NULL;
 	}
diff --git a/hald/linux2/blockdev.c b/hald/linux2/blockdev.c
index 6f309a5..d8e52bc 100644
--- a/hald/linux2/blockdev.c
+++ b/hald/linux2/blockdev.c
@@ -358,7 +358,7 @@ blockdev_callouts_preprobing_storage_don
 		/* Move from temporary to global device store */
 		hal_device_store_remove (hald_get_tdl (), d);
 		hal_device_store_add (hald_get_gdl (), d);
-		
+
 		hotplug_event_end (end_token);
 		goto out;
 	}
diff --git a/hald/linux2/classdev.c b/hald/linux2/classdev.c
index 87ce430..63bf17d 100644
--- a/hald/linux2/classdev.c
+++ b/hald/linux2/classdev.c
@@ -1429,17 +1429,16 @@ hotplug_event_begin_remove_classdev (con
 	guint i;
 	HalDevice *d;
 
-
 	HAL_INFO (("class_rem: subsys=%s sysfs_path=%s", subsystem, sysfs_path));
 
 	d = hal_device_store_match_key_value_string (hald_get_gdl (), "linux.sysfs_path", sysfs_path);
 	if (d == NULL) {
-		HAL_WARNING (("Error removing device"));
+		HAL_WARNING (("Error removing device %s", sysfs_path));
 	} else {
 
 		for (i = 0; classdev_handlers [i] != NULL; i++) {
 			ClassDevHandler *handler;
-			
+
 			handler = classdev_handlers[i];
 			if (strcmp (handler->subsystem, subsystem) == 0) {
 				handler->remove (d);
diff --git a/hald/property.c b/hald/property.c
index 0579c6c..8a063fe 100644
--- a/hald/property.c
+++ b/hald/property.c
@@ -54,7 +54,6 @@ struct _HalProperty {
 void
 hal_property_free (HalProperty *prop)
 {
-
 	g_free (prop->key);
 
 	if (prop->type == HAL_PROPERTY_TYPE_STRING) {
-------------- next part --------------
efceaf220166430bd1b35e21586fb573b05de630
diff --git a/hald/device_info.c b/hald/device_info.c
index b2535b3..a11f9b1 100644
--- a/hald/device_info.c
+++ b/hald/device_info.c
@@ -1,9 +1,10 @@
 /***************************************************************************
  * CVSID: $Id: device_info.c,v 1.32 2006/01/21 06:36:51 david Exp $
  *
- * device_store.c : Search for .fdi files and merge on match
+ * device_store.c : Parse .fdi files and match/merge device properties.
  *
  * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2006 Kay Sievers, <kay.sievers at vrfy.org>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -31,10 +32,12 @@
 #include <stdlib.h>
 #include <string.h>
 #include <dirent.h>
+#include <unistd.h>
 #include <expat.h>
 #include <assert.h>
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib.h>
+#include <sys/stat.h>
 #include <math.h>
 
 #include "hald.h"
@@ -42,116 +45,226 @@
 #include "device_info.h"
 #include "device_store.h"
 
-/**
- * @defgroup DeviceInfo Device Info File Parsing
- * @ingroup HalDaemon
- * @brief Parsing of device info files
- * @{
- */
-
-
-/** Maximum nesting depth */
-#define MAX_DEPTH 32
-
-/** Maximum amount of CDATA */
-#define CDATA_BUF_SIZE  1024
-
-/** Max length of property key */
-#define MAX_KEY_SIZE 128
-
-/** Possible elements the parser can process */
-enum {
-	/** Not processing a known tag */
-	CURELEM_UNKNOWN = -1,
-
-	/** Processing a deviceinfo element */
-	CURELEM_DEVICE_INFO = 0,
 
-	/** Processing a device element */
-	CURELEM_DEVICE = 1,
+#define MAX_INDENT_DEPTH		64
 
-	/** Processing a match element */
-	CURELEM_MATCH = 2,
-
-	/** Processing a merge element */
-	CURELEM_MERGE = 3,
-
-	/** Processing an append element */
-	CURELEM_APPEND = 4,
-
-	/** Processing a prepend element */
-	CURELEM_PREPEND = 5,
-
-	/** Processing a remove element */
-	CURELEM_REMOVE = 6,
-
-	/** Processing a clear element */
-	CURELEM_CLEAR = 7
+/* pre-parsed rules to keep in memory */
+static GSList *fdi_rules_preprobe;
+static GSList *fdi_rules_information;
+static GSList *fdi_rules_policy;
+
+enum rule_type {
+	RULE_UNKNOWN,
+	RULE_MATCH,
+	RULE_MERGE,
+	RULE_APPEND,
+	RULE_PREPEND,
+	RULE_REMOVE,
+	RULE_EOF,
 };
 
-/** What and how to merge */
-enum {
-	MERGE_TYPE_UNKNOWN       = 0,
-	MERGE_TYPE_STRING        = 1,
-	MERGE_TYPE_BOOLEAN       = 2,
-	MERGE_TYPE_INT32         = 3,
-	MERGE_TYPE_UINT64        = 4,
-	MERGE_TYPE_DOUBLE        = 5,
-	MERGE_TYPE_COPY_PROPERTY = 6,
-	MERGE_TYPE_STRLIST       = 7,
-	MERGE_TYPE_REMOVE        = 8,
-	MERGE_TYPE_CLEAR         = 9
-};
-
-/** Parsing Context
- */
-typedef struct {
-	/** Name of file being parsed */
-	char *file;
-
-	/** Parser object */
-	XML_Parser parser;
-
-	/** Device we are trying to match*/
-	HalDevice *device;
-
-	/** Buffer to put CDATA in */
-	char cdata_buf[CDATA_BUF_SIZE];
-
-	/** Current length of CDATA buffer */
-	int cdata_buf_len;
-	
-	/** Current depth we are parsing at */
-	int depth;
-
-	/** Element currently being processed */
-	int curelem;
+static enum
+rule_type get_rule_type(const char *str)
+{
+	if (strcmp (str, "match") == 0)
+		return RULE_MATCH;
+	if (strcmp (str, "merge") == 0)
+		return RULE_MERGE;
+	if (strcmp (str, "append") == 0)
+		return RULE_APPEND;
+	if (strcmp (str, "prepend") == 0)
+		return RULE_PREPEND;
+	if (strcmp (str, "remove") == 0)
+		return RULE_REMOVE;
+	return RULE_UNKNOWN;
+}
 
-	/** Stack of elements being processed */
-	int curelem_stack[MAX_DEPTH];
+static char
+*get_rule_type_str(enum rule_type type)
+{
+	switch (type) {
+	case RULE_MATCH:
+		return "match";
+	case RULE_MERGE:
+		return "merge";
+	case RULE_APPEND:
+		return "append";
+	case RULE_PREPEND:
+		return "prepend";
+	case RULE_REMOVE:
+		return "remove";
+	case RULE_EOF:
+		return "eof";
+	case RULE_UNKNOWN:
+		return "unknown rule type";
+	}
+	return "invalid rule type";
+}
 
-	/** #TRUE if parsing of document have been aborted */
-	dbus_bool_t aborted;
+enum merge_type {
+	MERGE_UNKNOWN,
+	MERGE_STRING,
+	MERGE_BOOLEAN,
+	MERGE_INT32,
+	MERGE_UINT64,
+	MERGE_DOUBLE,
+	MERGE_COPY_PROPERTY,
+	MERGE_STRLIST,
+};
 
+static enum
+merge_type get_merge_type(const char *str)
+{
+	if (strcmp (str, "string") == 0)
+		return MERGE_STRING;
+	if (strcmp (str, "bool") == 0)
+		return MERGE_BOOLEAN;
+	if (strcmp (str, "int") == 0)
+		return MERGE_INT32;
+	if (strcmp (str, "unint64") == 0)
+		return MERGE_UINT64;
+	if (strcmp (str, "double") == 0)
+		return MERGE_DOUBLE;
+	if (strcmp (str, "strlist") == 0)
+		return MERGE_STRLIST;
+	if (strcmp (str, "copy_property") == 0)
+		return MERGE_COPY_PROPERTY;
+	return MERGE_UNKNOWN;
+}
 
-	/** Depth of match-fail */
-	int match_depth_first_fail;
+static char
+*get_merge_type_str(enum merge_type type)
+{
+	switch (type) {
+	case MERGE_STRING:
+		return "string";
+	case MERGE_BOOLEAN:
+		return "bool";
+	case MERGE_INT32:
+		return "int";
+	case MERGE_UINT64:
+		return "unint64";
+	case MERGE_DOUBLE:
+		return "double";
+	case MERGE_STRLIST:
+		return "strlist";
+	case MERGE_COPY_PROPERTY:
+		return "copy_property";
+	case MERGE_UNKNOWN:
+		return "unknown merge type";
+	}
+	return "invalid merge type";
+}
 
-	/** #TRUE if all matches on prior depths have been OK */
-	dbus_bool_t match_ok;
 
+enum
+match_type {
+	MATCH_UNKNOWN,
+	MATCH_STRING,
+	MATCH_INT,
+	MATCH_UINT64,
+	MATCH_BOOL,
+	MATCH_EXISTS,
+	MATCH_EMPTY,
+	MATCH_ISASCII,
+	MATCH_IS_ABS_PATH,
+	MATCH_CONTAINS,
+	MATCH_CONTAINS_NCASE,
+	MATCH_COMPARE_LT,
+	MATCH_COMPARE_LE,
+	MATCH_COMPARE_GT,
+	MATCH_COMPARE_GE,
+};
 
+static enum
+match_type get_match_type(const char *str)
+{
+	if (strcmp (str, "string") == 0)
+		return MATCH_STRING;
+	if (strcmp (str, "int") == 0)
+		return MATCH_INT;
+	if (strcmp (str, "uint64") == 0)
+		return MATCH_UINT64;
+	if (strcmp (str, "bool") == 0)
+		return MATCH_BOOL;
+	if (strcmp (str, "exists") == 0)
+		return MATCH_EXISTS;
+	if (strcmp (str, "empty") == 0)
+		return MATCH_EMPTY;
+	if (strcmp (str, "is_ascii") == 0)
+		return MATCH_ISASCII;
+	if (strcmp (str, "is_absolute_path") == 0)
+		return MATCH_IS_ABS_PATH;
+	if (strcmp (str, "contains") == 0)
+		return MATCH_CONTAINS;
+	if (strcmp (str, "contains_ncase") == 0)
+		return MATCH_CONTAINS_NCASE;
+	if (strcmp (str, "compare_lt") == 0)
+		return MATCH_COMPARE_LT;
+	if (strcmp (str, "compare_le") == 0)
+		return MATCH_COMPARE_LE;
+	if (strcmp (str, "compare_gt") == 0)
+		return MATCH_COMPARE_GT;
+	if (strcmp (str, "compare_ge") == 0)
+		return MATCH_COMPARE_GE;
+	return MATCH_UNKNOWN;
+}
 
-	/** When merging, the key to store the value in */
-	char merge_key[MAX_KEY_SIZE];
+static char
+*get_match_type_str(enum match_type type)
+{
+	switch (type) {
+	case MATCH_STRING:
+		return "string";
+	case MATCH_INT:
+		return "int";
+	case MATCH_UINT64:
+		return "uint64";
+	case MATCH_BOOL:
+		return "bool";
+	case MATCH_EXISTS:
+		return "exists";
+	case MATCH_EMPTY:
+		return "empty";
+	case MATCH_ISASCII:
+		return "is_ascii";
+	case MATCH_IS_ABS_PATH:
+		return "is_absolute_path";
+	case MATCH_CONTAINS:
+		return "contains";
+	case MATCH_CONTAINS_NCASE:
+		return "contains_ncase";
+	case MATCH_COMPARE_LT:
+		return "compare_lt";
+	case MATCH_COMPARE_LE:
+		return "compare_le";
+	case MATCH_COMPARE_GT:
+		return "compare_gt";
+	case MATCH_COMPARE_GE:
+		return "compare_ge";
+	case MATCH_UNKNOWN:
+		return "unknown match type";
+	}
+	return "invalid match type";
+}
 
-	/** Type to merge*/
-	int merge_type;
+struct rule {
+	/* typ of tule in the list */
+	enum rule_type rtype;
+
+	/* all rules have a key */
+	char *key;
+
+	/* "match" or "merge" rule */
+	enum match_type type_match;
+	enum merge_type type_merge;
 
-	/** Set to #TRUE if a device is matched */
-	dbus_bool_t device_matched;
+	char *value;
+	int value_len;
 
-} ParsingContext;
+	/* if rule does not match, skip to this rule */
+	struct rule *next_rule;
+};
 
 /** Resolve a udi-property path as used in .fdi files. 
  *
@@ -177,11 +290,7 @@ resolve_udiprop_path (const char *path, 
 {
 	int i;
 	gchar **tokens = NULL;
-	gboolean rc;
-
-	rc = FALSE;
-
-	/*HAL_INFO (("Looking at '%s' for udi='%s'", path, source_udi));*/
+	gboolean rc = FALSE;
 
 	/* Split up path into ':' tokens */
 	tokens = g_strsplit (path, ":", 64);
@@ -230,26 +339,15 @@ resolve_udiprop_path (const char *path, 
 			if (newudi == NULL)
 				goto out;
 
-			/*HAL_INFO (("new_udi = '%s' (from indirection)", newudi));*/
-
 			strncpy (udi_result, newudi, udi_result_size);
 		} else {
-			/*HAL_INFO (("new_udi = '%s'", curtoken));*/
 			strncpy (udi_result, curtoken, udi_result_size);
 		}
 
 	}
 
 out:
-
-/*
-	HAL_INFO (("success     = '%s'", rc ? "yes" : "no"));
-	HAL_INFO (("udi_result  = '%s'", udi_result));
-	HAL_INFO (("prop_result = '%s'", prop_result));
-*/
-
 	g_strfreev (tokens);
-
 	return rc;
 }
 
@@ -312,37 +410,20 @@ out:
 	return rc;
 }
 
-/** Called when the match element begins.
- *
- *  @param  pc                  Parsing context
- *  @param  attr                Attribute key/value pairs
- *  @return                     #FALSE if the device in question didn't
- *                              match the data in the attributes
- */
-static dbus_bool_t
-handle_match (ParsingContext * pc, const char **attr)
+static gboolean
+handle_match (struct rule *rule, HalDevice *d)
 {
 	char udi_to_check[256];
 	char prop_to_check[256];
-	const char *key;
-	int num_attrib;
-	HalDevice *d;
-
-	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++);
-
-	if (num_attrib != 4)
-		return FALSE;
-
-	if (strcmp (attr[0], "key") != 0)
-		return FALSE;
-	key = attr[1];
+	const char *key = rule->key;
+	const char *value = rule->value;
 
 	/* Resolve key paths like 'someudi/foo/bar/baz:prop.name' '@prop.here.is.an.udi:with.prop.name' */
 	if (!resolve_udiprop_path (key,
-				   pc->device->udi,
+				   hal_device_get_udi (d),
 				   udi_to_check, sizeof (udi_to_check),
 				   prop_to_check, sizeof (prop_to_check))) {
-		HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", key, pc->device->udi));
+		HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", key, value));
 		return FALSE;
 	}
 
@@ -354,89 +435,61 @@ handle_match (ParsingContext * pc, const
 		return FALSE;
 	}
 
-	if (strcmp (attr[2], "string") == 0) {
-		const char *value;
-
-		/* match string property */
-		value = attr[3];
-
-		/*HAL_INFO(("Checking that key='%s' is a string that "
-		  "equals '%s'", key, value)); */
-
+	switch (rule->type_match) {
+	case MATCH_STRING:
+	{
 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
 			return FALSE;
-
 		if (strcmp (hal_device_property_get_string (d, prop_to_check), value) != 0)
 			return FALSE;
-
-		/*HAL_INFO (("*** string match for key %s", key));*/
 		return TRUE;
-	} else if (strcmp (attr[2], "int") == 0) {
-		dbus_int32_t value;
-
-		/* match integer property */
-		value = strtol (attr[3], NULL, 0);
-
-		/** @todo Check error condition */
+	}
 
-		/*HAL_INFO (("Checking that key='%s' is a int that equals %d", 
-		  key, value));*/
+	case MATCH_INT:
+	{
+		int val = strtol (value, NULL, 0);
 
 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_INT32)
 			return FALSE;
-
-		if (hal_device_property_get_int (d, prop_to_check) != value) {
+		if (hal_device_property_get_int (d, prop_to_check) != val)
 			return FALSE;
-		}
-
 		return TRUE;
-	} else if (strcmp (attr[2], "uint64") == 0) {
-		dbus_uint64_t value;
-
-		/* match integer property */
-		value = strtoull (attr[3], NULL, 0);
-		
-		/** @todo Check error condition */
+	}
 
-		/*HAL_INFO (("Checking that key='%s' is a int that equals %d", 
-		  key, value));*/
+	case MATCH_UINT64:
+	{
+		dbus_uint64_t val = strtol (value, NULL, 0);
 
 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_UINT64)
 			return FALSE;
-
-		if (hal_device_property_get_uint64 (d, prop_to_check) != value) {
+		if (hal_device_property_get_uint64 (d, prop_to_check) != val)
 			return FALSE;
-		}
-
 		return TRUE;
-	} else if (strcmp (attr[2], "bool") == 0) {
-		dbus_bool_t value;
-
-		/* match string property */
+	}
 
-		if (strcmp (attr[3], "false") == 0)
-			value = FALSE;
-		else if (strcmp (attr[3], "true") == 0)
-			value = TRUE;
+	case MATCH_BOOL:
+	{
+		dbus_bool_t val;
+
+		if (strcmp (value, "false") == 0)
+			val = FALSE;
+		else if (strcmp (value, "true") == 0)
+			val = TRUE;
 		else
 			return FALSE;
 
-		/*HAL_INFO (("Checking that key='%s' is a bool that equals %s", 
-		  key, value ? "TRUE" : "FALSE"));*/
-
-		if (hal_device_property_get_type (d, prop_to_check) != 
-		    HAL_PROPERTY_TYPE_BOOLEAN)
+		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_BOOLEAN)
 			return FALSE;
-
-		if (hal_device_property_get_bool (d, prop_to_check) != value)
+		if (hal_device_property_get_bool (d, prop_to_check) != val)
 			return FALSE;
-
-		/*HAL_INFO (("*** bool match for key %s", key));*/
 		return TRUE;
-	} else if (strcmp (attr[2], "exists") == 0) {
+	}
+
+	case MATCH_EXISTS:
+	{
 		dbus_bool_t should_exist = TRUE;
 
-		if (strcmp (attr[3], "false") == 0)
+		if (strcmp (value, "false") == 0)
 			should_exist = FALSE;
 
 		if (should_exist) {
@@ -450,16 +503,17 @@ handle_match (ParsingContext * pc, const
 			else
 				return TRUE;
 		}
-	} else if (strcmp (attr[2], "empty") == 0) {
+	}
+
+	case MATCH_EMPTY:
+	{
 		dbus_bool_t is_empty = TRUE;
 		dbus_bool_t should_be_empty = TRUE;
 
-		if (strcmp (attr[3], "false") == 0)
+		if (strcmp (value, "false") == 0)
 			should_be_empty = FALSE;
-
 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
 			return FALSE;
-
 		if (hal_device_has_property (d, prop_to_check))
 			if (strlen (hal_device_property_get_string (d, prop_to_check)) > 0)
 				is_empty = FALSE;
@@ -475,13 +529,16 @@ handle_match (ParsingContext * pc, const
 			else
 				return TRUE;
 		}
-	} else if (strcmp (attr[2], "is_ascii") == 0) {
+	}
+
+	case MATCH_ISASCII:
+	{
 		dbus_bool_t is_ascii = TRUE;
 		dbus_bool_t should_be_ascii = TRUE;
 		unsigned int i;
 		const char *str;
 
-		if (strcmp (attr[3], "false") == 0)
+		if (strcmp (value, "false") == 0)
 			should_be_ascii = FALSE;
 
 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
@@ -506,16 +563,17 @@ handle_match (ParsingContext * pc, const
 			else
 				return TRUE;
 		}
-	} else if (strcmp (attr[2], "is_absolute_path") == 0) {
+	}
+
+	case MATCH_IS_ABS_PATH:
+	{
 		const char *path = NULL;
 		dbus_bool_t is_absolute_path = FALSE;
 		dbus_bool_t should_be_absolute_path = TRUE;
 
-		if (strcmp (attr[3], "false") == 0)
+		if (strcmp (value, "false") == 0)
 			should_be_absolute_path = FALSE;
 
-		/*HAL_INFO (("d->udi='%s', prop_to_check='%s'", d->udi, prop_to_check));*/
-
 		if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
 			return FALSE;
 
@@ -525,8 +583,6 @@ handle_match (ParsingContext * pc, const
 				is_absolute_path = TRUE;
 		}
 
-		/*HAL_INFO (("is_absolute=%d, should_be=%d, path='%s'", is_absolute_path, should_be_absolute_path, path));*/
-
 		if (should_be_absolute_path) {
 			if (is_absolute_path)
 				return TRUE;
@@ -538,31 +594,28 @@ handle_match (ParsingContext * pc, const
 			else
 				return TRUE;
 		}
-	} else if (strcmp (attr[2], "contains") == 0) {
-		const char *needle;
-		dbus_bool_t contains = FALSE;
+	}
 
-		needle = attr[3];
+	case MATCH_CONTAINS:
+	{
+		dbus_bool_t contains = FALSE;
 
 		if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRING) {
 			if (hal_device_has_property (d, prop_to_check)) {
 				const char *haystack;
-				
+
 				haystack = hal_device_property_get_string (d, prop_to_check);
-				if (needle != NULL && haystack != NULL && strstr (haystack, needle)) {
+				if (value != NULL && haystack != NULL && strstr (haystack, value))
 					contains = TRUE;
-				}
-				
 			}
-		} else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST && 
-			   needle != NULL) {
+		} else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST && value != NULL) {
+			GSList *list;
 			GSList *i;
-			GSList *value;
 
-			value = hal_device_property_get_strlist (d, prop_to_check);
-			for (i = value; i != NULL; i = g_slist_next (i)) {
+			list = hal_device_property_get_strlist (d, prop_to_check);
+			for (i = list; i != NULL; i = g_slist_next (i)) {
 				const char *str = i->data;
-				if (strcmp (str, needle) == 0) {
+				if (strcmp (str, value) == 0) {
 					contains = TRUE;
 					break;
 				}
@@ -572,511 +625,141 @@ handle_match (ParsingContext * pc, const
 		}
 
 		return contains;
-	} else if (strcmp (attr[2], "contains_ncase") == 0) {
-		const char *needle;
-		dbus_bool_t contains_ncase = FALSE;
+	}
 
-		needle = attr[3];
+	case MATCH_CONTAINS_NCASE:
+	{
+		dbus_bool_t contains_ncase = FALSE;
 
 		if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRING) {
 			if (hal_device_has_property (d, prop_to_check)) {
-				char *needle_lowercase;
+				char *value_lowercase;
 				char *haystack_lowercase;
-				
-				needle_lowercase   = g_utf8_strdown (needle, -1);
+
+				value_lowercase   = g_utf8_strdown (value, -1);
 				haystack_lowercase = g_utf8_strdown (hal_device_property_get_string (d, prop_to_check), -1);
-				if (needle_lowercase != NULL && haystack_lowercase != NULL && strstr (haystack_lowercase, needle_lowercase)) {
+				if (value_lowercase != NULL && haystack_lowercase != NULL &&
+				    strstr (haystack_lowercase, value_lowercase))
 					contains_ncase = TRUE;
-				}
-				
-				g_free (needle_lowercase);
+
+				g_free (value_lowercase);
 				g_free (haystack_lowercase);
 			}
-		} else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST && 
-			   needle != NULL) {
+		} else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST && value != NULL) {
+			GSList *list;
 			GSList *i;
-			GSList *value;
 
-			value = hal_device_property_get_strlist (d, prop_to_check);
-			for (i = value; i != NULL; i = g_slist_next (i)) {
+			list = hal_device_property_get_strlist (d, prop_to_check);
+			for (i = list; i != NULL; i = g_slist_next (i)) {
 				const char *str = i->data;
-				if (g_ascii_strcasecmp (str, needle) == 0) {
+				if (g_ascii_strcasecmp (str, value) == 0) {
 					contains_ncase = TRUE;
 					break;
 				}
 			}
-		} else {
+		} else
 			return FALSE;
-		}
-
 		return contains_ncase;
-	} else if (strcmp (attr[2], "compare_lt") == 0) {
+	}
+
+	case MATCH_COMPARE_LT:
+	{
 		dbus_int64_t result;
-		if (!match_compare_property (d, prop_to_check, attr[3], &result)) {
+
+		if (!match_compare_property (d, prop_to_check, value, &result))
 			return FALSE;
-		} else {
+		else
 			return result < 0;
-		}
-	} else if (strcmp (attr[2], "compare_le") == 0) {
+	}
+
+	case MATCH_COMPARE_LE:
+	{
 		dbus_int64_t result;
-		if (!match_compare_property (d, prop_to_check, attr[3], &result))
+
+		if (!match_compare_property (d, prop_to_check, value, &result))
 			return FALSE;
 		else
 			return result <= 0;
-	} else if (strcmp (attr[2], "compare_gt") == 0) {
+	}
+
+	case MATCH_COMPARE_GT:
+	{
 		dbus_int64_t result;
-		if (!match_compare_property (d, prop_to_check, attr[3], &result))
+
+		if (!match_compare_property (d, prop_to_check, value, &result))
 			return FALSE;
 		else
 			return result > 0;
-	} else if (strcmp (attr[2], "compare_ge") == 0) {
+	}
+
+	case MATCH_COMPARE_GE:
+	{
 		dbus_int64_t result;
-		if (!match_compare_property (d, prop_to_check, attr[3], &result))
+
+		if (!match_compare_property (d, prop_to_check, value, &result))
 			return FALSE;
 		else
 			return result >= 0;
 	}
-	
-	return FALSE;
-}
-
-
-/** Called when the merge element begins.
- *
- *  @param  pc                  Parsing context
- *  @param  attr                Attribute key/value pairs
- */
-static void
-handle_merge (ParsingContext * pc, const char **attr)
-{
-	int num_attrib;
-
-	pc->merge_type = MERGE_TYPE_UNKNOWN;
-
-
-	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
-		;
-	}
-
-	if (num_attrib != 4)
-		return;
-
-	if (strcmp (attr[0], "key") != 0)
-		return;
-	strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
-
-	if (strcmp (attr[2], "type") != 0)
-		return;
-
-	if (strcmp (attr[3], "string") == 0) {
-		/* match string property */
-		pc->merge_type = MERGE_TYPE_STRING;
-		return;
-	} else if (strcmp (attr[3], "bool") == 0) {
-		/* match string property */
-		pc->merge_type = MERGE_TYPE_BOOLEAN;
-		return;
-	} else if (strcmp (attr[3], "int") == 0) {
-		/* match string property */
-		pc->merge_type = MERGE_TYPE_INT32;
-		return;
-	} else if (strcmp (attr[3], "uint64") == 0) {
-		/* match string property */
-		pc->merge_type = MERGE_TYPE_UINT64;
-		return;
-	} else if (strcmp (attr[3], "double") == 0) {
-		/* match string property */
-		pc->merge_type = MERGE_TYPE_DOUBLE;
-		return;
-	} else if (strcmp (attr[3], "strlist") == 0) {
-		/* match string property */
-		pc->merge_type = MERGE_TYPE_STRLIST;
-		return;
-	} else if (strcmp (attr[3], "copy_property") == 0) {
-		/* copy another property */
-		pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
-		return;
-	}
-
-	return;
-}
-
-/** Called when the append or prepend element begins.
- *
- *  @param  pc                  Parsing context
- *  @param  attr                Attribute key/value pairs
- */
-static void
-handle_append_prepend (ParsingContext * pc, const char **attr)
-{
-	int num_attrib;
-
-	pc->merge_type = MERGE_TYPE_UNKNOWN;
-
-	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
-		;
-	}
-
-	if (num_attrib != 4)
-		return;
-
-	if (strcmp (attr[0], "key") != 0)
-		return;
-	strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
-
-	if (strcmp (attr[2], "type") != 0)
-		return;
-
-	if (strcmp (attr[3], "string") == 0) {
-		/* append to a string */
-		pc->merge_type = MERGE_TYPE_STRING;
-		return;
-	} else if (strcmp (attr[3], "strlist") == 0) {
-		/* append to a string list*/
-		pc->merge_type = MERGE_TYPE_STRLIST;
-		return;
-	} else if (strcmp (attr[3], "copy_property") == 0) {
-		/* copy another property */
-		pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
-		return;
-	}
-
-	return;
-}
-
-/** Called when the remove element begins.
- *
- *  @param  pc                  Parsing context
- *  @param  attr                Attribute key/value pairs
- */
-static void
-handle_remove (ParsingContext * pc, const char **attr)
-{
-	int num_attrib;
-
-	pc->merge_type = MERGE_TYPE_UNKNOWN;
-
-	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
-		;
-	}
-
-	if (num_attrib != 2 && num_attrib != 4)
-		return;
-
-	if (strcmp (attr[0], "key") != 0)
-		return;
-	strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
-
-	if (num_attrib == 4) {
-		if (strcmp (attr[2], "type") != 0)
-			return;
-
-		if (strcmp (attr[3], "strlist") == 0) {
-			/* remove from strlist */
-			pc->merge_type = MERGE_TYPE_STRLIST;
-			return;
-		} else {
-			pc->merge_type = MERGE_TYPE_UNKNOWN;
-			return;
-		}
-	} else {
-		pc->merge_type = MERGE_TYPE_REMOVE;
-	}
-
-	return;
-}
 
-/** Called when the clear element begins.
- *
- *  @param  pc                  Parsing context
- *  @param  attr                Attribute key/value pairs
- */
-static void
-handle_clear (ParsingContext * pc, const char **attr)
-{
-	int num_attrib;
-
-	pc->merge_type = MERGE_TYPE_UNKNOWN;
-
-	for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
-		;
+	default:
+		HAL_INFO(("match ERROR"));
+		return FALSE;
 	}
 
-	if (num_attrib != 4)
-		return;
-	
-	if (strcmp (attr[0], "key") != 0)
-		return;
-
-
-	if (strcmp (attr[3], "strlist") != 0)
-		return;
-	
-	strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
-	
-	pc->merge_type = MERGE_TYPE_CLEAR;
-
-	return;
-}
-
-/** Abort parsing of document
- *
- *  @param  pc                  Parsing context
- */
-static void
-parsing_abort (ParsingContext * pc)
-{
-	/* Grr, expat can't abort parsing */
-	HAL_ERROR (("Aborting parsing of document"));
-	pc->aborted = TRUE;
-}
-
-/** Called by expat when an element begins.
- *
- *  @param  pc                  Parsing context
- *  @param  el                  Element name
- *  @param  attr                Attribute key/value pairs
- */
-static void
-start (ParsingContext * pc, const char *el, const char **attr)
-{
-	if (pc->aborted)
-		return;
-
-	pc->cdata_buf_len = 0;
-
-/*
-    for (i = 0; i < pc->depth; i++)
-        printf("  ");
-    
-    printf("%s", el);
-    
-    for (i = 0; attr[i]; i += 2) {
-        printf(" %s='%s'", attr[i], attr[i + 1]);
-    }
-
-    printf("   curelem=%d\n", pc->curelem);
-*/
-
-	if (strcmp (el, "match") == 0) {
-		if (pc->curelem != CURELEM_DEVICE
-		    && pc->curelem != CURELEM_MATCH) {
-			HAL_ERROR (("%s:%d:%d: Element <match> can only be "
-				    "inside <device> and <match>", 
-				    pc->file, 
-				    XML_GetCurrentLineNumber (pc->parser), 
-				    XML_GetCurrentColumnNumber (pc->parser)));
-			parsing_abort (pc);
-		}
-
-		pc->curelem = CURELEM_MATCH;
-
-		/* don't bother checking if matching at lower depths failed */
-		if (pc->match_ok) {
-			if (!handle_match (pc, attr)) {
-				/* No match */
-				pc->match_depth_first_fail = pc->depth;
-				pc->match_ok = FALSE;
-			}
-		}
-	} else if (strcmp (el, "merge") == 0) {
-		if (pc->curelem != CURELEM_DEVICE
-		    && pc->curelem != CURELEM_MATCH) {
-			HAL_ERROR (("%s:%d:%d: Element <merge> can only be "
-				    "inside <device> and <match>", 
-				    pc->file, 
-				    XML_GetCurrentLineNumber (pc->parser), 
-				    XML_GetCurrentColumnNumber (pc->parser)));
-			parsing_abort (pc);
-		}
-
-		pc->curelem = CURELEM_MERGE;
-		if (pc->match_ok) {
-			handle_merge (pc, attr);
-		} else {
-			/*HAL_INFO(("No merge!")); */
-		}
-	} else if (strcmp (el, "append") == 0) {
-		if (pc->curelem != CURELEM_DEVICE
-		    && pc->curelem != CURELEM_MATCH) {
-			HAL_ERROR (("%s:%d:%d: Element <append> can only be "
-				    "inside <device> and <match>", 
-				    pc->file, 
-				    XML_GetCurrentLineNumber (pc->parser), 
-				    XML_GetCurrentColumnNumber (pc->parser)));
-			parsing_abort (pc);
-		}
-
-		pc->curelem = CURELEM_APPEND;
-		if (pc->match_ok) {
-			handle_append_prepend (pc, attr);
-		} else {
-			/*HAL_INFO(("No merge!")); */
-		}
-	} else if (strcmp (el, "prepend") == 0) {
-		if (pc->curelem != CURELEM_DEVICE
-		    && pc->curelem != CURELEM_MATCH) {
-			HAL_ERROR (("%s:%d:%d: Element <prepend> can only be "
-				    "inside <device> and <match>", 
-				    pc->file, 
-				    XML_GetCurrentLineNumber (pc->parser), 
-				    XML_GetCurrentColumnNumber (pc->parser)));
-			parsing_abort (pc);
-		}
-
-		pc->curelem = CURELEM_PREPEND;
-		if (pc->match_ok) {
-			handle_append_prepend (pc, attr);
-		} else {
-			/*HAL_INFO(("No merge!")); */
-		}
-	} else if (strcmp (el, "remove") == 0) {
-		if (pc->curelem != CURELEM_DEVICE
-		    && pc->curelem != CURELEM_MATCH) {
-			HAL_ERROR (("%s:%d:%d: Element <remove> can only be "
-				    "inside <device> and <match>", 
-				    pc->file, 
-				    XML_GetCurrentLineNumber (pc->parser), 
-				    XML_GetCurrentColumnNumber (pc->parser)));
-			parsing_abort (pc);
-		}
-
-		pc->curelem = CURELEM_REMOVE;
-		if (pc->match_ok) {
-			handle_remove (pc, attr);
-		} else {
-			/*HAL_INFO(("No merge!")); */
-		}
-	} else if (strcmp (el, "clear") == 0) {
-		if (pc->curelem != CURELEM_DEVICE
-		    && pc->curelem != CURELEM_MATCH) {
-			HAL_ERROR (("%s:%d:%d: Element <remove> can only be "
-				    "inside <device> and <match>", 
-				    pc->file, 
-				    XML_GetCurrentLineNumber (pc->parser), 
-				    XML_GetCurrentColumnNumber (pc->parser)));
-			parsing_abort (pc);
-		}
-
-		pc->curelem = CURELEM_CLEAR;
-		if (pc->match_ok) {
-			handle_clear (pc, attr);
-		} else {
-			/*HAL_INFO(("No merge!")); */
-		}
-	} else if (strcmp (el, "device") == 0) {
-		if (pc->curelem != CURELEM_DEVICE_INFO) {
-			HAL_ERROR (("%s:%d:%d: Element <device> can only be "
-				    "inside <deviceinfo>", 
-				    pc->file, 
-				    XML_GetCurrentLineNumber (pc->parser), 
-				    XML_GetCurrentColumnNumber (pc->parser)));
-			parsing_abort (pc);
-		}
-		pc->curelem = CURELEM_DEVICE;
-	} else if (strcmp (el, "deviceinfo") == 0) {
-		if (pc->curelem != CURELEM_UNKNOWN) {
-			HAL_ERROR (("%s:%d:%d: Element <deviceinfo> must be "
-				    "a top-level element", 
-				    pc->file, 
-				    XML_GetCurrentLineNumber (pc->parser), 
-				    XML_GetCurrentColumnNumber (pc->parser)));
-			parsing_abort (pc);
-		}
-		pc->curelem = CURELEM_DEVICE_INFO;
-	} else {
-		HAL_ERROR (("%s:%d:%d: Unknown element <%s>",
-			    pc->file,
-			    XML_GetCurrentLineNumber (pc->parser),
-			    XML_GetCurrentColumnNumber (pc->parser), el));
-		parsing_abort (pc);
-	}
-
-	/* Nasty hack */
-	assert (pc->depth < MAX_DEPTH);
-
-	pc->depth++;
-
-	/* store depth */
-	pc->curelem_stack[pc->depth] = pc->curelem;
-
+	return FALSE;
 }
 
-/** Called by expat when an element ends.
- *
- *  @param  pc                  Parsing context
- *  @param  el                  Element name
- */
-static void
-end (ParsingContext * pc, const char *el)
+static gboolean
+handle_merge (struct rule *rule, HalDevice *d)
 {
-	if (pc->aborted)
-		return;
-
-	pc->cdata_buf[pc->cdata_buf_len] = '\0';
+	const char *key = rule->key;
+	const char *value = rule->value;
 
-/*    printf("   curelem=%d\n", pc->curelem);*/
-
-	if (pc->curelem == CURELEM_MERGE && pc->match_ok) {
-		/* As soon as we are merging, we have matched the device... */
-		pc->device_matched = TRUE;
-
-		switch (pc->merge_type) {
-		case MERGE_TYPE_STRING:
-			hal_device_property_set_string (pc->device, pc->merge_key, pc->cdata_buf);
+	switch (rule->rtype) {
+	case RULE_MERGE:
+	{
+		switch (rule->type_merge) {
+		case MERGE_STRING:
+			hal_device_property_set_string (d, key, value);
 			break;
 
-		case MERGE_TYPE_STRLIST:
+		case MERGE_STRLIST:
 		{
-			int type = hal_device_property_get_type (pc->device, pc->merge_key);
+			int type = hal_device_property_get_type (d, key);
+
 			if (type == HAL_PROPERTY_TYPE_STRLIST || type == HAL_PROPERTY_TYPE_INVALID) {
-				hal_device_property_remove (pc->device, pc->merge_key);
-				hal_device_property_strlist_append (pc->device, pc->merge_key, pc->cdata_buf);
+				hal_device_property_remove (d, key);
+				hal_device_property_strlist_append (d, key, value);
 			}
 			break;
 		}
 
-		case MERGE_TYPE_INT32:
-			{
-				dbus_int32_t value;
-
-				/* match integer property */
-				value = strtol (pc->cdata_buf, NULL, 0);
-
-				/** @todo FIXME: Check error condition */
-
-				hal_device_property_set_int (pc->device,
-						     pc->merge_key, value);
-				break;
-			}
-
-		case MERGE_TYPE_UINT64:
-			{
-				dbus_uint64_t value;
+		case MERGE_INT32:
+		{
+			dbus_int32_t val = strtol (value, NULL, 0);
 
-				/* match integer property */
-				value = strtoull (pc->cdata_buf, NULL, 0);
+			hal_device_property_set_int (d, key, val);
+			break;
+		}
 
-				/** @todo FIXME: Check error condition */
+		case MERGE_UINT64:
+		{
+			dbus_uint64_t val = strtoull (value, NULL, 0);
 
-				hal_device_property_set_uint64 (pc->device,
-						     pc->merge_key, value);
-				break;
-			}
+			hal_device_property_set_uint64 (d, key, val);
+			break;
+		}
 
-		case MERGE_TYPE_BOOLEAN:
-			hal_device_property_set_bool (pc->device, pc->merge_key,
-					      (strcmp (pc->cdata_buf,
-						       "true") == 0) 
-					      ? TRUE : FALSE);
+		case MERGE_BOOLEAN:
+			hal_device_property_set_bool (d, key, (strcmp (value, "true") == 0) ? TRUE : FALSE);
 			break;
 
-		case MERGE_TYPE_DOUBLE:
-			hal_device_property_set_double (pc->device, pc->merge_key,
-						atof (pc->cdata_buf));
+		case MERGE_DOUBLE:
+			hal_device_property_set_double (d, key, atof (value));
 			break;
 
-		case MERGE_TYPE_COPY_PROPERTY:
+		case MERGE_COPY_PROPERTY:
 		{
 			char udi_to_merge_from[256];
 			char prop_to_merge[256];
@@ -1084,11 +767,11 @@ end (ParsingContext * pc, const char *el
 			/* Resolve key paths like 'someudi/foo/bar/baz:prop.name' 
 			 * '@prop.here.is.an.udi:with.prop.name'
 			 */
-			if (!resolve_udiprop_path (pc->cdata_buf,
-						   pc->device->udi,
+			if (!resolve_udiprop_path (value,
+						   hal_device_get_udi (d),
 						   udi_to_merge_from, sizeof (udi_to_merge_from),
 						   prop_to_merge, sizeof (prop_to_merge))) {
-				HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", pc->cdata_buf, pc->device->udi));
+				HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", value, hal_device_get_udi (d)));
 			} else {
 				HalDevice *d;
 
@@ -1099,362 +782,364 @@ end (ParsingContext * pc, const char *el
 				if (d == NULL) {
 					HAL_ERROR (("Could not find device with udi '%s'", udi_to_merge_from));
 				} else {
-					hal_device_copy_property (d, prop_to_merge, pc->device, pc->merge_key);
+					hal_device_copy_property (d, prop_to_merge, d, key);
 				}
 			}
 			break;
 		}
 
 		default:
-			HAL_ERROR (("Unknown merge_type=%d='%c'",
-				    pc->merge_type, pc->merge_type));
 			break;
 		}
-	} else if (pc->curelem == CURELEM_APPEND && pc->match_ok && 
-		   (hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRING ||
-		    hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRLIST ||
-		    hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_INVALID)) {
+		break;
+	}
+
+	case RULE_APPEND:
+	{
 		char buf[256];
 		char buf2[256];
 
-		/* As soon as we are appending, we have matched the device... */
-		pc->device_matched = TRUE;
+		if (hal_device_property_get_type (d, key) != HAL_PROPERTY_TYPE_STRING &&
+		    hal_device_property_get_type (d, key) != HAL_PROPERTY_TYPE_STRLIST &&
+		    hal_device_property_get_type (d, key) != HAL_PROPERTY_TYPE_INVALID)
+			break;
 
-		if (pc->merge_type == MERGE_TYPE_STRLIST) {
-			hal_device_property_strlist_append (pc->device, pc->merge_key, pc->cdata_buf);
+		if (rule->type_merge == MERGE_STRLIST) {
+			hal_device_property_strlist_append (d, key, value);
 		} else {
 			const char *existing_string;
-			
-			switch (pc->merge_type) {
-			case MERGE_TYPE_STRING:
-				strncpy (buf, pc->cdata_buf, sizeof (buf));
+
+			switch (rule->type_merge) {
+			case MERGE_STRING:
+				strncpy (buf, value, sizeof (buf));
 				break;
-				
-			case MERGE_TYPE_COPY_PROPERTY:
-				hal_device_property_get_as_string (pc->device, pc->cdata_buf, buf, sizeof (buf));
+
+			case MERGE_COPY_PROPERTY:
+				hal_device_property_get_as_string (d, value, buf, sizeof (buf));
 				break;
-				
+
 			default:
-				HAL_ERROR (("Unknown merge_type=%d='%c'", pc->merge_type, pc->merge_type));
 				break;
 			}
-			
-			existing_string = hal_device_property_get_string (pc->device, pc->merge_key);
+
+			existing_string = hal_device_property_get_string (d, key);
 			if (existing_string != NULL) {
 				strncpy (buf2, existing_string, sizeof (buf2));
 				strncat (buf2, buf, sizeof (buf2) - strlen(buf2));
-			} else {
+			} else
 				strncpy (buf2, buf, sizeof (buf2));
-			}
-			hal_device_property_set_string (pc->device, pc->merge_key, buf2);
+			hal_device_property_set_string (d, key, buf2);
 		}
-	} else if (pc->curelem == CURELEM_PREPEND && pc->match_ok && 
-		   (hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRING ||
-		    hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRLIST ||
-		    hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_INVALID)) {
+		break;
+	}
+
+	case RULE_PREPEND:
+	{
 		char buf[256];
 		char buf2[256];
 
-		/* As soon as we are prepending, we have matched the device... */
-		pc->device_matched = TRUE;
+		if (hal_device_property_get_type (d, key) != HAL_PROPERTY_TYPE_STRING &&
+		    hal_device_property_get_type (d, key) != HAL_PROPERTY_TYPE_STRLIST &&
+		    hal_device_property_get_type (d, key) != HAL_PROPERTY_TYPE_INVALID)
+			break;
 
-		if (pc->merge_type == MERGE_TYPE_STRLIST) {
-			hal_device_property_strlist_prepend (pc->device, pc->merge_key, pc->cdata_buf);
+		if (rule->type_merge == MERGE_STRLIST) {
+			hal_device_property_strlist_prepend (d, key, value);
 		} else {
 			const char *existing_string;
-			
-			switch (pc->merge_type) {
-			case MERGE_TYPE_STRING:
-				strncpy (buf, pc->cdata_buf, sizeof (buf));
+
+			switch (rule->type_merge) {
+			case MERGE_STRING:
+				strncpy (buf, value, sizeof (buf));
 				break;
-				
-			case MERGE_TYPE_COPY_PROPERTY:
-				hal_device_property_get_as_string (pc->device, pc->cdata_buf, buf, sizeof (buf));
+
+			case MERGE_COPY_PROPERTY:
+				hal_device_property_get_as_string (d, value, buf, sizeof (buf));
 				break;
-				
+
 			default:
-				HAL_ERROR (("Unknown merge_type=%d='%c'", pc->merge_type, pc->merge_type));
 				break;
 			}
-			
-			existing_string = hal_device_property_get_string (pc->device, pc->merge_key);
+
+			existing_string = hal_device_property_get_string (d, key);
 			if (existing_string != NULL) {
 				strncpy (buf2, buf, sizeof (buf2));
 				strncat (buf2, existing_string, sizeof (buf2) - strlen(buf2));
-			} else {
+			} else
 				strncpy (buf2, buf, sizeof (buf2));
-			}
-			hal_device_property_set_string (pc->device, pc->merge_key, buf2);
+			hal_device_property_set_string (d, key, buf2);
 		}
-	} else if (pc->curelem == CURELEM_REMOVE && pc->match_ok) {
+		break;
+	}
 
-		if (pc->merge_type == MERGE_TYPE_STRLIST) {
+	case RULE_REMOVE:
+	{
+		if (rule->type_merge == MERGE_STRLIST) {
 			/* covers <remove key="foobar" type="strlist">blah</remove> */
-			hal_device_property_strlist_remove (pc->device, pc->merge_key, pc->cdata_buf);
+			hal_device_property_strlist_remove (d, key, value);
 		} else {
 			/* only allow <remove key="foobar"/>, not <remove key="foobar">blah</remove> */
-			if (strlen (pc->cdata_buf) == 0) {
-				hal_device_property_remove (pc->device, pc->merge_key);
-			}
+			if (strlen (value) == 0)
+				hal_device_property_remove (d, key);
+		}
+		break;
+	}
+
+	default:
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+rules_cleanup_list (GSList *fdi_rules)
+{
+	GSList *elem;
+
+	for (elem = fdi_rules; elem != NULL; elem = g_slist_next (elem)) {
+		struct rule *rule = elem->data;
+
+		g_free (rule->key);
+		g_free (rule->value);
+		g_free (rule);
+	}
+	g_slist_free (fdi_rules);
+	fdi_rules = NULL;
+}
+
+struct fdi_context {
+	int depth;
+	struct rule *match_at_depth[MAX_INDENT_DEPTH];
+
+	/* current rule */
+	struct rule *rule;
+
+	/* all rules */
+	GSList* rules;
+};
+
+static void
+start (void *data, const char *el, const char **attr)
+{
+	struct fdi_context *fdi_ctx = data;
+	enum rule_type rtype = get_rule_type(el);
+	int i;
+
+	if (rtype == RULE_UNKNOWN)
+		return;
+
+	if (fdi_ctx->rule == NULL)
+		return;
+
+	/* get key and attribute for current rule */
+	for (i = 0; attr[i] != NULL; i+=2) {
+		if (strcmp (attr[i], "key") == 0) {
+			fdi_ctx->rule->key = g_strdup (attr[1]);
+			continue;
 		}
-	} else if (pc->curelem == CURELEM_CLEAR && pc->match_ok) {
-		if (pc->merge_type == MERGE_TYPE_CLEAR) {
-			hal_device_property_strlist_clear (pc->device, pc->merge_key);
+		if (rtype == RULE_MATCH) {
+			fdi_ctx->rule->type_match = get_match_type (attr[i]);
+			if (fdi_ctx->rule->type_match == MATCH_UNKNOWN)
+				continue;
+			fdi_ctx->rule->value = g_strdup (attr[i+1]);
+		} else {
+			if (strcmp (attr[i], "type") != 0)
+				continue;
+			fdi_ctx->rule->type_merge = get_merge_type (attr[i+1]);
+			if (fdi_ctx->rule->type_merge == MERGE_UNKNOWN)
+				return;
 		}
 	}
 
+	if (fdi_ctx->rule->key[0] == '\0')
+		return;
 
+	fdi_ctx->rule->rtype = rtype;
 
-	pc->cdata_buf_len = 0;
-	pc->depth--;
+	/* match rules remember the current nesting and the label to jump to if not matching */
+	if (rtype == RULE_MATCH) {
+		/* remember current nesting */
+		fdi_ctx->depth++;
 
-	/* maintain curelem */
-	pc->curelem = pc->curelem_stack[pc->depth];
+		/* remember rule at nesting level nesting */
+		fdi_ctx->match_at_depth[fdi_ctx->depth] = fdi_ctx->rule;
 
-	/* maintain pc->match_ok */
-	if (pc->depth <= pc->match_depth_first_fail)
-		pc->match_ok = TRUE;
+		/* insert match rule into list and get new rule */
+		fdi_ctx->rules = g_slist_append (fdi_ctx->rules, fdi_ctx->rule);
+		fdi_ctx->rule = g_new0 (struct rule ,1);
+	}
+}
+
+static void
+cdata (void *data, const char *s, int len)
+{
+	struct fdi_context *fdi_ctx = data;
+
+	if (fdi_ctx->rule == NULL)
+		return;
+
+	if (fdi_ctx->rule->rtype != RULE_MERGE &&
+	    fdi_ctx->rule->rtype != RULE_PREPEND &&
+	    fdi_ctx->rule->rtype != RULE_APPEND &&
+	    fdi_ctx->rule->rtype != RULE_REMOVE)
+		return;
+
+	if (len < 1)
+		return;
+
+	/* copy cdata in current context */
+	fdi_ctx->rule->value = g_realloc (fdi_ctx->rule->value, fdi_ctx->rule->value_len + len+1);
+	memcpy(&fdi_ctx->rule->value[fdi_ctx->rule->value_len], s, len);
+	fdi_ctx->rule->value_len += len;
+	fdi_ctx->rule->value[fdi_ctx->rule->value_len] = '\0';
 }
 
-/** Called when there is CDATA 
- *
- *  @param  pc                  Parsing context
- *  @param  s                   Pointer to data
- *  @param  len                 Length of data
- */
 static void
-cdata (ParsingContext * pc, const char *s, int len)
+end (void *data, const char *el)
 {
-	int bytes_left;
-	int bytes_to_copy;
+	struct fdi_context *fdi_ctx = data;
+	enum rule_type rtype = get_rule_type(el);
 
-	if (pc->aborted)
+	if (rtype == RULE_UNKNOWN)
 		return;
 
-	bytes_left = CDATA_BUF_SIZE - pc->cdata_buf_len;
-	if (len > bytes_left) {
-		HAL_ERROR (("CDATA in element larger than %d",
-			    CDATA_BUF_SIZE));
+	if (rtype == RULE_MATCH) {
+		if (fdi_ctx->depth <= 0)
+			return;
+
+		/* get corresponding match rule and set the rule to skip to */
+		fdi_ctx->match_at_depth[fdi_ctx->depth]->next_rule = fdi_ctx->rule;
+		fdi_ctx->depth--;
+		return;
 	}
 
-	bytes_to_copy = len;
-	if (bytes_to_copy > bytes_left)
-		bytes_to_copy = bytes_left;
+	/* only valid if element is in  the current context */
+	if (fdi_ctx->rule->rtype != rtype)
+		return;
 
-	if (bytes_to_copy > 0)
-		memcpy (pc->cdata_buf + pc->cdata_buf_len, s,
-			bytes_to_copy);
+	/* set empty value to empty string */
+	if (fdi_ctx->rule->value == NULL)
+		fdi_ctx->rule->value = "";
 
-	pc->cdata_buf_len += bytes_to_copy;
+	/* insert merge rule into list and get new rule */
+	fdi_ctx->rules = g_slist_append (fdi_ctx->rules, fdi_ctx->rule);
+	fdi_ctx->rule = g_new0 (struct rule, 1);
 }
 
-
-/** Process a device information info file.
- *
- *  @param  dir                 Directory file resides in
- *  @param  filename            File name
- *  @param  device              Device to match on
- *  @return                     #TRUE if file matched device and information
- *                              was merged
- */
-static dbus_bool_t
-process_fdi_file (const char *dir, const char *filename,
-		  HalDevice * device)
+static int
+rules_add_fdi_file (GSList **fdi_rules, const char *filename)
 {
+	struct fdi_context *fdi_ctx;
+	char *buf;
+	gsize buflen;
 	int rc;
-	char buf[512];
-	FILE *file;
-	int filesize;
-	size_t read;
-	char *filebuf;
-	dbus_bool_t device_matched;
-	XML_Parser parser;
-	ParsingContext *parsing_context;
-
-	file = NULL;
-	filebuf = NULL;
-	parser = NULL;
-	parsing_context = NULL;
-
-	device_matched = FALSE;
-
-	snprintf (buf, sizeof (buf), "%s/%s", dir, filename);
-
-	/*HAL_INFO(("analyzing file %s", buf));*/
-
-	/* open file and read it into a buffer; it's a small file... */
-	file = fopen (buf, "r");
-	if (file == NULL) {
-		HAL_ERROR (("Could not open file %s", buf));
-		goto out;
-	}
 
-	fseek (file, 0L, SEEK_END);
-	filesize = (int) ftell (file);
-	rewind (file);
-	filebuf = (char *) malloc (filesize);
-	if (filebuf == NULL) {
-		HAL_ERROR (("Could not allocate %d bytes for file %s", filesize, buf));
-		goto out;
-	}
-	read = fread (filebuf, sizeof (char), filesize, file);
+	if (!g_file_get_contents (filename, &buf, &buflen, NULL))
+		return -1;
 
-	/* initialize parsing context */
-	parsing_context =
-	    (ParsingContext *) malloc (sizeof (ParsingContext));
-	if (parsing_context == NULL) {
-		HAL_ERROR (("Could not allocate parsing context"));
-		goto out;
-	}
+	/* get context and first rule */
+	fdi_ctx = g_new0 (struct fdi_context ,1);
+	fdi_ctx->rule = g_new0 (struct rule ,1);
 
-	/* TODO: reuse parser
-	 */
-	parser = XML_ParserCreate (NULL);
+	XML_Parser parser = XML_ParserCreate (NULL);
 	if (parser == NULL) {
-		HAL_ERROR (("Could not allocate XML parser"));
-		goto out;
+		fprintf (stderr, "Couldn't allocate memory for parser\n");
+		return -1;
 	}
+	XML_SetUserData (parser, fdi_ctx);
+	XML_SetElementHandler (parser, start, end);
+	XML_SetCharacterDataHandler (parser, cdata);
+	rc = XML_Parse (parser, buf, buflen, 1);
+	if (rc == 0)
+		fprintf (stderr, "Parse error at line %i:\n%s\n",
+			(int) XML_GetCurrentLineNumber (parser),
+			XML_ErrorString (XML_GetErrorCode (parser)));
+	XML_ParserFree (parser);
+	g_free (buf);
+
+	/* insert last dummy rule into list */
+	fdi_ctx->rule->rtype = RULE_EOF;
+	fdi_ctx->rule->key = g_strdup (filename);
+	fdi_ctx->rules = g_slist_append (fdi_ctx->rules, fdi_ctx->rule);
+
+	/* add rules to external list */
+	if (rc == 0)
+		rules_cleanup_list (fdi_ctx->rules);
+	else
+		*fdi_rules = g_slist_concat (*fdi_rules, fdi_ctx->rules);
+
+	g_free (fdi_ctx);
+
+	if (rc == 0)
+		return -1;
+	return 0;
+}
 
-	parsing_context->depth = 0;
-	parsing_context->device_matched = FALSE;
-	parsing_context->match_ok = TRUE;
-	parsing_context->curelem = CURELEM_UNKNOWN;
-	parsing_context->aborted = FALSE;
-	parsing_context->file = buf;
-	parsing_context->parser = parser;
-	parsing_context->device = device;
-	parsing_context->match_depth_first_fail = -1;
-
-	XML_SetElementHandler (parser,
-			       (XML_StartElementHandler) start,
-			       (XML_EndElementHandler) end);
-	XML_SetCharacterDataHandler (parser,
-				     (XML_CharacterDataHandler) cdata);
-	XML_SetUserData (parser, parsing_context);
-
-	rc = XML_Parse (parser, filebuf, filesize, 1);
-	/*printf("XML_Parse rc=%d\r\n", rc); */
-
-	if (rc == 0) {
-		/* error parsing document */
-		HAL_ERROR (("Error parsing XML document %s at line %d, "
-			    "column %d : %s", 
-			    buf, 
-			    XML_GetCurrentLineNumber (parser), 
-			    XML_GetCurrentColumnNumber (parser), 
-			    XML_ErrorString (XML_GetErrorCode (parser))));
-		device_matched = FALSE;
-	} else {
-		/* document parsed ok */
-		device_matched = parsing_context->device_matched;
-	}
+static void
+rules_dump (GSList *fdi_rules)
+{
+	GSList *elem;
 
-out:
-	if (filebuf != NULL)
-		free (filebuf);
-	if (file != NULL)
-		fclose (file);
-	if (parser != NULL)
-		XML_ParserFree (parser);
-	if (parsing_context != NULL)
-		free (parsing_context);
+	for (elem = fdi_rules; elem != NULL; elem = g_slist_next (elem)) {
+		struct rule *rule = elem->data;
 
-	return device_matched;
+		if (rule->rtype == RULE_EOF) {
+			printf ("%p: eof %s\n", rule, rule->key);
+		} else if (rule->rtype == RULE_MATCH) {
+			printf ("\n");
+			printf ("%p: match '%s' (%s) '%s' (skip to %p)\n",
+				rule, rule->key, get_match_type_str (rule->type_match),
+				rule->value, rule->next_rule);
+		} else {
+			printf ("%p: %s '%s' (%s) '%s'\n",
+				rule, get_rule_type_str (rule->rtype), rule->key,
+				get_merge_type_str (rule->type_merge), rule->value);
+		}
+	}
 }
 
-
-
-static int 
-my_alphasort(const void *a, const void *b)
+static int
+_alphasort(const void *a, const void *b)
 {
 	return -alphasort (a, b);
 }
 
-
-/** Scan all directories and subdirectories in the given directory and
- *  process each *.fdi file
- *
- *  @param  d                   Device to merge information into
- *  @return                     #TRUE if information was merged
- */
-static dbus_bool_t
-scan_fdi_files (const char *dir, HalDevice * d)
+static int
+rules_search_and_add_fdi_files (GSList **fdi_rules, const char *dir)
 {
 	int i;
 	int num_entries;
-	dbus_bool_t found_fdi_file;
 	struct dirent **name_list;
 
-	found_fdi_file = 0;
-
-	/*HAL_INFO(("scan_fdi_files: Processing dir '%s'", dir));*/
-
-	num_entries = scandir (dir, &name_list, 0, my_alphasort);
-	if (num_entries == -1) {
-		perror ("scandir");
-		return FALSE;
-	}
+	num_entries = scandir (dir, &name_list, 0, _alphasort);
+	if (num_entries == -1)
+		return -1;
 
 	for (i = num_entries - 1; i >= 0; i--) {
 		int len;
 		char *filename;
-		gchar *full_path;					     
+		gchar *full_path;
 
 		filename = name_list[i]->d_name;
 		len = strlen (filename);
-
 		full_path = g_strdup_printf ("%s/%s", dir, filename);
-		/*HAL_INFO (("Full path = %s", full_path));*/
-
-		/* Mmm, d_type can be DT_UNKNOWN, use glib to determine
-		 * the type
-		 */
 		if (g_file_test (full_path, (G_FILE_TEST_IS_REGULAR))) {
-			/* regular file */
-
-			if (len >= 5 &&
-			    filename[len - 4] == '.' &&
-			    filename[len - 3] == 'f' &&
-			    filename[len - 2] == 'd' &&
-			    filename[len - 1] == 'i') {
-				/*HAL_INFO (("scan_fdi_files: Processing file '%s'", filename));*/
-				found_fdi_file = process_fdi_file (dir, filename, d);
-				if (found_fdi_file) {
-					HAL_INFO (("*** Matched file %s/%s", dir, filename));
-					/*break;*/
-				}
-			}
-
-		} else if (g_file_test (full_path, (G_FILE_TEST_IS_DIR)) 
-			   && strcmp (filename, ".") != 0
-			   && strcmp (filename, "..") != 0) {
+			if (len >= 5 && strcmp(&filename[len - 4], ".fdi") == 0)
+				rules_add_fdi_file (fdi_rules, full_path);
+		} else if (g_file_test (full_path, (G_FILE_TEST_IS_DIR)) && filename[0] != '.') {
 			int num_bytes;
 			char *dirname;
 
-			/* Directory; do the recursion thingy but not 
-			 * for . and ..
-			 */
-
 			num_bytes = len + strlen (dir) + 1 + 1;
 			dirname = (char *) malloc (num_bytes);
-			if (dirname == NULL) {
-				HAL_ERROR (("couldn't allocated %d bytes",
-					    num_bytes));
+			if (dirname == NULL)
 				break;
-			}
 
-			snprintf (dirname, num_bytes, "%s/%s", dir,
-				  filename);
-			found_fdi_file = scan_fdi_files (dirname, d);
+			snprintf (dirname, num_bytes, "%s/%s", dir, filename);
+			rules_search_and_add_fdi_files (fdi_rules, dirname);
 			free (dirname);
-			/*
-			if (found_fdi_file)
-				break;
-			*/
 		}
-
 		g_free (full_path);
-
 		free (name_list[i]);
 	}
 
@@ -1463,80 +1148,119 @@ scan_fdi_files (const char *dir, HalDevi
 	}
 
 	free (name_list);
+	return 0;
+}
 
-	return found_fdi_file;
+void
+di_rules_init (void)
+{
+	char *hal_fdi_source_preprobe = getenv ("HAL_FDI_SOURCE_PREPROBE");
+	char *hal_fdi_source_information = getenv ("HAL_FDI_SOURCE_INFORMATION");
+	char *hal_fdi_source_policy = getenv ("HAL_FDI_SOURCE_POLICY");
+
+	if (hal_fdi_source_preprobe != NULL)
+		rules_search_and_add_fdi_files (&fdi_rules_preprobe, hal_fdi_source_preprobe);
+	else {
+		rules_search_and_add_fdi_files (&fdi_rules_preprobe, PACKAGE_DATA_DIR "/hal/fdi/preprobe");
+		rules_search_and_add_fdi_files (&fdi_rules_preprobe, PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe");
+	}
+
+	if (hal_fdi_source_information != NULL)
+		rules_search_and_add_fdi_files (&fdi_rules_information, hal_fdi_source_information);
+	else {
+		rules_search_and_add_fdi_files (&fdi_rules_information, PACKAGE_DATA_DIR "/hal/fdi/information");
+		rules_search_and_add_fdi_files (&fdi_rules_information, PACKAGE_SYSCONF_DIR "/hal/fdi/information");
+	}
+
+	if (hal_fdi_source_policy != NULL)
+		rules_search_and_add_fdi_files (&fdi_rules_policy, hal_fdi_source_policy);
+	else {
+		rules_search_and_add_fdi_files (&fdi_rules_policy, PACKAGE_DATA_DIR "/hal/fdi/policy");
+		rules_search_and_add_fdi_files (&fdi_rules_policy, PACKAGE_SYSCONF_DIR "/hal/fdi/policy");
+	}
+
+	rules_dump (fdi_rules_preprobe);
+	rules_dump (fdi_rules_information);
+	rules_dump (fdi_rules_policy);
 }
 
-/** Search the device info file repository for a .fdi file to merge
- *  more information into the device object.
- *
- *  @param  d                   Device to merge information into
- *  @return                     #TRUE if information was merged
- */
-dbus_bool_t
-di_search_and_merge (HalDevice *d, DeviceInfoType type)
+void
+di_rules_cleanup (void)
+{
+	rules_cleanup_list (fdi_rules_preprobe);
+	rules_cleanup_list (fdi_rules_information);
+	rules_cleanup_list (fdi_rules_policy);
+	fdi_rules_preprobe = NULL;
+	fdi_rules_information = NULL;
+	fdi_rules_policy = NULL;
+}
+
+static void
+rules_match_and_merge_device (GSList *fdi_rules, HalDevice *d)
 {
-	static gboolean have_checked_hal_fdi_source = FALSE;
-	static char *hal_fdi_source_preprobe = NULL;
-	static char *hal_fdi_source_information = NULL;
-	static char *hal_fdi_source_policy = NULL;
-	dbus_bool_t ret;
-	char *s1;
-	char *s2;
-
-	ret = FALSE;
-
-	if (!have_checked_hal_fdi_source) {
-		hal_fdi_source_preprobe    = getenv ("HAL_FDI_SOURCE_PREPROBE");
-		hal_fdi_source_information = getenv ("HAL_FDI_SOURCE_INFORMATION");
-		hal_fdi_source_policy      = getenv ("HAL_FDI_SOURCE_POLICY");
-		have_checked_hal_fdi_source = TRUE;
+	GSList *elem;
+
+	if (fdi_rules == NULL) {
+		di_rules_cleanup ();
+		di_rules_init ();
+	}
+
+	elem = fdi_rules;
+	while (elem != NULL) {
+		struct rule *rule = elem->data;
+
+		switch (rule->rtype) {
+		case RULE_MATCH:
+			/* skip non-matching rules block */
+			HAL_INFO(("%p match '%s' at %s", rule, rule->key, hal_device_get_udi (d)));
+			if (!handle_match (rule, d)) {
+				HAL_INFO(("skip to rule %s (%p)", get_rule_type_str (rule->next_rule->rtype), rule->next_rule));
+				elem = g_slist_find (elem, rule->next_rule);
+				continue;
+			}
+			break;
+
+		case RULE_APPEND:
+		case RULE_PREPEND:
+		case RULE_REMOVE:
+		case RULE_MERGE:
+			HAL_INFO(("%p merge '%s' at %s", rule, rule->key, hal_device_get_udi (d)));
+			handle_merge (rule, d);
+			break;
+
+		case RULE_EOF:
+			HAL_INFO(("%p fdi file '%s' finished", rule, rule->key));
+			break;
+
+		default:
+			break;
+		}
+		elem = g_slist_next (elem);
 	}
+}
 
+gboolean
+di_search_and_merge (HalDevice *d, DeviceInfoType type)
+{
 	switch (type) {
 	case DEVICE_INFO_TYPE_PREPROBE:
-		if (hal_fdi_source_preprobe != NULL) {
-			s1 = hal_fdi_source_preprobe;
-			s2 = NULL;
-		} else {
-			s1 = PACKAGE_DATA_DIR "/hal/fdi/preprobe";
-			s2 = PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe";
-		}
+		HAL_INFO(("apply fdi preprobe to device %p", d));
+		rules_match_and_merge_device (fdi_rules_preprobe, d);
 		break;
 
 	case DEVICE_INFO_TYPE_INFORMATION:
-		if (hal_fdi_source_information != NULL) {
-			s1 = hal_fdi_source_information;
-			s2 = NULL;
-		} else {
-			s1 = PACKAGE_DATA_DIR "/hal/fdi/information";
-			s2 = PACKAGE_SYSCONF_DIR "/hal/fdi/information";
-		}
+		HAL_INFO(("apply fdi info to device %p", d));
+		rules_match_and_merge_device (fdi_rules_information, d);
 		break;
 
 	case DEVICE_INFO_TYPE_POLICY:
-		if (hal_fdi_source_policy != NULL) {
-			s1 = hal_fdi_source_policy;
-			s2 = NULL;
-		} else {
-			s1 = PACKAGE_DATA_DIR "/hal/fdi/policy";
-			s2 = PACKAGE_SYSCONF_DIR "/hal/fdi/policy";
-		}
+		HAL_INFO(("apply fdi policy to device %p", d));
+		rules_match_and_merge_device (fdi_rules_policy, d);
 		break;
 
 	default:
-		s1 = NULL;
-		s2 = NULL;
-		HAL_ERROR (("Bogus device information type %d", type));
 		break;
 	}
 
-	if (s1 != NULL)
-		ret = scan_fdi_files (s1, d) || ret;
-	if (s2 != NULL)
-		ret = scan_fdi_files (s2, d) || ret;
-
-	return ret;
+	return TRUE;
 }
-
-/** @} */
diff --git a/hald/device_info.h b/hald/device_info.h
index 76f2b76..70adc75 100644
--- a/hald/device_info.h
+++ b/hald/device_info.h
@@ -38,6 +38,8 @@ typedef enum {
 	DEVICE_INFO_TYPE_POLICY
 } DeviceInfoType;
 
-dbus_bool_t di_search_and_merge (HalDevice *d, DeviceInfoType type);
+extern void di_rules_init (void);
+extern void di_rules_cleanup (void);
+extern gboolean di_search_and_merge (HalDevice *d, DeviceInfoType type);
 
 #endif				/* DEVICE_INFO_H */
diff --git a/hald/property.c b/hald/property.c
index 8a063fe..c1bcb9e 100644
--- a/hald/property.c
+++ b/hald/property.c
@@ -226,20 +226,20 @@ hal_property_to_string (HalProperty *pro
 		GSList *iter;
 		guint i;
 		char buf[256];
-		
+
 		i = 0;
 		buf[0] = '\0';
-		for (iter = hal_property_get_strlist (prop); 
-		     iter != NULL && i < sizeof(buf); 
+		for (iter = hal_property_get_strlist (prop);
+		     iter != NULL && i < sizeof(buf);
 		     iter = g_slist_next (iter)) {
 			guint len;
 			const char *str;
-			
+
 			str = (const char *) iter->data;
 			len = strlen (str);
 			strncpy (buf + i, str, sizeof(buf) - i);
 			i += len;
-			
+
 			if (g_slist_next (iter) != NULL && i < sizeof(buf)) {
 				buf[i] = '\t';
 				i++;
-------------- next part --------------
diff --git a/hald/hald.c b/hald/hald.c
index 0ce85a6..9be295e 100644
--- a/hald/hald.c
+++ b/hald/hald.c
@@ -326,8 +326,8 @@ parent_wait_for_child (int child_fd, pid
 	FD_SET(child_fd, &rfds);
 	FD_ZERO(&efds);
 	FD_SET(child_fd, &efds);
-	/* Wait up to 250 seconds for device probing */
-	tv.tv_sec = 250;
+	/* Wait up to 3600 seconds for device probing */
+	tv.tv_sec = 3600;
 	tv.tv_usec = 0;
 
 	retval = select (child_fd + 1, &rfds, NULL, &efds, &tv);


More information about the hal mailing list