All pastes #104439 Raw Edit

Stuff

public text v1 · immutable
#104439 ·published 2006-07-28 21:54 UTC
rendered paste body

#include <string.h>
#include <glib.h>
#include <stdlib.h>

#include "hald-fdi.h"
#include "hald-device.h"

static void     hald_fdi_class_init	(HaldFdiClass *klass);
static void     hald_fdi_init		(HaldFdi      *fdi);
static void     hald_fdi_finalize	(GObject      *object);

#define HALD_FDI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), HALD_TYPE_FDI, HaldFdiPrivate))
#define BYTES_PER_READ 4096

struct HaldFdiPrivate
{
	GHashTable	*hash;
	GNode		*root;
	GNode		*current;
	GNode		*parent;
	GMarkupParser	*m_parser;
	GMarkupParseContext *context;
	gboolean	 ignore_device;
	const char	*current_file;
};

typedef enum {
	HALD_FDI_LINK_MATCH,
	HALD_FDI_LINK_MERGE,
	HALD_FDI_LINK_APPEND,
	HALD_FDI_LINK_PREPEND,
	HALD_FDI_LINK_REMOVE,
	HALD_FDI_LINK_SPAWN,
	HALD_FDI_LINK_LAST
} HaldFdiLinkType;

typedef struct {
	HaldFdiLinkType	 type;
	char		*match_type;
	char		*match_value;
	char		*value_type;
	char		*value_data;
	char		*data;
} HaldFdiLink;

enum {
	CREATE_DEVICE,
	LAST_SIGNAL
};

static guint	     signals [LAST_SIGNAL] = { 0, };
static gpointer      hald_fdi_object = NULL;

G_DEFINE_TYPE (HaldFdi, hald_fdi, G_TYPE_OBJECT)

/**
 * parser_error_print:
 *
 * Error out on the invalid fdi entry
 **/
static void
parser_error_print (HaldFdi *fdi, const char *data)
{
	gint line_number = 0;
	gint char_number = 0;
	g_markup_parse_context_get_position (fdi->priv->context,
					     &line_number, &char_number);
	g_error ("invalid entry: %s on %s (ln:%i, ch:%i)",
		 data, fdi->priv->current_file,
		 line_number, char_number);
}

/**
 * parser_start_node_cb:
 *
 * Called for open tags <foo bar="baz">
 **/
static void 
parser_start_node_cb (GMarkupParseContext *context,
		      const gchar         *element_name,
		      const gchar        **attribute_names,
		      const gchar        **attribute_values,
		      gpointer             user_data,
		      GError             **error)
{
	HaldFdiLink *link;
	HaldFdi *fdi;

	fdi = HALD_FDI (user_data);

	/* stuff to ignore */
	if (strcmp (element_name, "deviceinfo") == 0) {
		return;
	}

	/* check to see if we should ignore this device */
	if (strcmp (element_name, "device") == 0) {
		if (attribute_values[0] == NULL) {
			fdi->priv->ignore_device = FALSE;
			return;
		}
		fdi->priv->ignore_device = TRUE;
		/* look this up in the hash to see if we should load this device */
		if (g_hash_table_lookup (fdi->priv->hash, attribute_values[0])) {
			fdi->priv->ignore_device = FALSE;
		}
		return;
	}

	/* are we ignoring this device? */
	if (fdi->priv->ignore_device) {
		return;
	}

	link = g_new0 (HaldFdiLink, 1);
	fdi->priv->current = g_node_new (link);
	g_node_append (fdi->priv->parent, fdi->priv->current);

	link->match_type = g_strdup (attribute_names[0]);
	link->match_value = g_strdup (attribute_values[0]);
	link->value_type = g_strdup (attribute_names[1]);
	link->value_data = g_strdup (attribute_values[1]);
	link->data = NULL;

	if (strcmp (element_name, "match") == 0) {
		link->type = HALD_FDI_LINK_MATCH;
		fdi->priv->parent = fdi->priv->current;
	} else if (strcmp (element_name, "merge") == 0) {
		link->type = HALD_FDI_LINK_MERGE;
	} else if (strcmp (element_name, "append") == 0) {
		link->type = HALD_FDI_LINK_APPEND;
	} else if (strcmp (element_name, "prepend") == 0) {
		link->type = HALD_FDI_LINK_PREPEND;
	} else if (strcmp (element_name, "remove") == 0) {
		link->type = HALD_FDI_LINK_REMOVE;
	} else if (strcmp (element_name, "spawn") == 0) {
		link->type = HALD_FDI_LINK_SPAWN;
	} else {
		parser_error_print (fdi, element_name);
	}
}

/**
 * parser_end_node_cb:
 *
 * Called for close tags </foo>
 **/
static void
parser_end_node_cb (GMarkupParseContext *context,
		    const gchar         *element_name,
		    gpointer             user_data,
		    GError             **error)
{
	HaldFdi *fdi;
	fdi = HALD_FDI (user_data);

	/* are we ignoring this device? */
	if (fdi->priv->ignore_device) {
		return;
	}
	/* stuff to ignore */
	if (strcmp (element_name, "deviceinfo") == 0) {
		return;
	}
	/* go down a level */
	if (strcmp (element_name, "match") == 0) {
		fdi->priv->parent = fdi->priv->parent->parent;
	}
}

/**
 * parser_text_cb:
 *
 * Called for character data
 **/
static void
parser_text_cb (GMarkupParseContext *context,
		const gchar         *text,
		gsize                text_len,  
		gpointer             user_data,
		GError             **error)
{
	HaldFdi *fdi;
	fdi = HALD_FDI (user_data);

	/* are we ignoring this device? */
	if (fdi->priv->ignore_device) {
		return;
	}
	/* we have not started known tags yet */
	if (fdi->priv->current == NULL) {
		return;
	}

	char *newtext = g_strdup (text);
	newtext = g_strstrip (newtext);
	HaldFdiLink *link;
	link = fdi->priv->current->data;
	if (strlen (newtext) != 0) {
		link->data = g_strdup (newtext);
	}
	/* valid? */
	g_free (newtext);
}

/**
 * parser_error_cb:
 **/
static void
parser_error_cb (GMarkupParseContext *context,
		 GError              *error,
		 gpointer             user_data)
{
	HaldFdi *fdi;
	fdi = HALD_FDI (user_data);

	g_markup_parse_context_free (fdi->priv->context);
 	fdi->priv->context = NULL;
}

/**
 * hald_fdi_parse_file:
 **/
gboolean
hald_fdi_parse_file (HaldFdi      *fdi, 
		     const gchar  *path,
	             GError      **error)
{
	GIOChannel *io;
	gchar       buf[BYTES_PER_READ];
	gboolean    result = TRUE;
	
	fdi->priv->m_parser = g_new0 (GMarkupParser, 1);
	fdi->priv->m_parser->start_element = parser_start_node_cb;
	fdi->priv->m_parser->end_element   = parser_end_node_cb;
	fdi->priv->m_parser->error         = parser_error_cb;
	fdi->priv->m_parser->text	   = parser_text_cb;
	fdi->priv->context = g_markup_parse_context_new (fdi->priv->m_parser, 0,
						         fdi, NULL);
	fdi->priv->parent = fdi->priv->root;
	fdi->priv->current_file = path;

	/* Parse the string */
	io = g_io_channel_new_file (path, "r", error);
	if (!io) {
		result = FALSE;
		goto exit;
	}

	while (TRUE) {
		GIOStatus io_status;
		gsize     bytes_read;

		io_status = g_io_channel_read_chars (io, buf, BYTES_PER_READ,
						     &bytes_read, error);
		if (io_status == G_IO_STATUS_ERROR) {
			result = FALSE;
			goto exit;
		}
		if (io_status != G_IO_STATUS_NORMAL) break;

		g_markup_parse_context_parse (fdi->priv->context, buf,
					      bytes_read, error);
		if (error != NULL && *error != NULL) {
			result = FALSE;
			goto exit;
		}

		if (bytes_read < BYTES_PER_READ) {
			break;
		}
	}

	g_io_channel_shutdown (io, FALSE, NULL);
exit:
	g_markup_parse_context_free (fdi->priv->context);
	g_free (fdi->priv->m_parser);
	
	return result;
}

/**
 * hald_fdi_scan_directory:
 **/
gboolean
hald_fdi_scan_directory (HaldFdi    *fdi,
			 const char *location)
{
	g_return_val_if_fail (HALD_IS_FDI (fdi), FALSE);
	g_return_val_if_fail (location != NULL, FALSE);

	GDir *dir;
	const char *name;
	char *full_path;

	dir = g_dir_open (location, 0, NULL);
	if (dir == NULL) {
		g_error ("directory open failed: %s", location);
		return FALSE;
	}
	while (TRUE) {
		name = g_dir_read_name (dir);
		/* end of list? */
		if (name == NULL) {
			break;
		}
		full_path = g_strdup_printf ("%s/%s", location, name);

		/* recurse into directory structure */
		if (g_file_test (full_path, (G_FILE_TEST_IS_REGULAR))) {
			/* only process files that are *.fdi */
			if (g_str_has_suffix (name, ".fdi")) {
				hald_fdi_parse_file (fdi, full_path, NULL);
			}
		} else if (g_file_test (full_path, (G_FILE_TEST_IS_DIR))) {
			hald_fdi_scan_directory (fdi, full_path);
		}
		g_free (full_path);
	}
	g_dir_close (dir);
	return TRUE;
}

/**
 * hald_fdi_print_tree_recurse:
 **/
static void
hald_fdi_print_tree_recurse (HaldFdi *fdi, GNode *node)
{
	int i;
	HaldFdiLink *link;
	if (node == NULL) {
		return;
	}
	for (i=0; i<g_node_depth(node)-2; i++) {
		g_print (" ");
	}
	g_print ("* ");
	link = node->data;
	if (link) {
		if (link->type == HALD_FDI_LINK_MATCH) {
			g_print ("<match> %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data);
		} else if (link->type == HALD_FDI_LINK_MERGE) {
			g_print ("<merge> %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data);
		} else if (link->type == HALD_FDI_LINK_APPEND) {
			g_print ("<append> %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data);
		} else if (link->type == HALD_FDI_LINK_PREPEND) {
			g_print ("<prepend> %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data);
		} else if (link->type == HALD_FDI_LINK_REMOVE) {
			g_print ("<remove> %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data);
		} else if (link->type == HALD_FDI_LINK_SPAWN) {
			g_print ("<spawn> %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data);
		} else {
			g_error ("unknown type, must be an error");
		}
	}
	g_print ("\n");
	hald_fdi_print_tree_recurse (fdi, node->children);
	hald_fdi_print_tree_recurse (fdi, node->next);
}

/**
 * hald_fdi_print_tree:
 **/
gboolean
hald_fdi_print_tree (HaldFdi *fdi)
{
	g_return_val_if_fail (HALD_IS_FDI (fdi), FALSE);
	hald_fdi_print_tree_recurse (fdi, fdi->priv->root->children);
	return TRUE;
}

/**
 * hald_fdi_add_allowed_device:
 **/
gboolean
hald_fdi_add_allowed_device (HaldFdi *fdi, const char *devicename)
{
	g_return_val_if_fail (HALD_IS_FDI (fdi), FALSE);
	g_return_val_if_fail (devicename != NULL, FALSE);
	g_hash_table_insert (fdi->priv->hash, g_strdup (devicename), GINT_TO_POINTER (TRUE));
	return TRUE;
}

/**
 * hald_get_gvalue_from_type:
 **/
static GValue *
hald_get_gvalue_from_type (const char *type, const char *value)
{
	GValue *unit;
	unit = g_new0 (GValue, 1);
	if (strcmp (type, "bool") == 0) {
		g_value_init (unit, G_TYPE_BOOLEAN);
		if (strcmp (value, "true") == 0) {
			g_value_set_boolean (unit, TRUE);
		} else {
			g_value_set_boolean (unit, TRUE);
		}
	} else if (strcmp (type, "string") == 0) {
		g_value_init (unit, G_TYPE_STRING);
		g_value_set_string (unit, value);
	} else if (strcmp (type, "int") == 0) {
		g_value_init (unit, G_TYPE_INT);
		g_value_set_int (unit, atoi (value));
	} else if (strcmp (type, "float") == 0) {
		g_value_init (unit, G_TYPE_FLOAT);
		g_value_set_float (unit, strtod (value, NULL));
	} else {
		g_debug ("unknown value type: %s", type);
		unit = NULL;
	}
	return unit;
}

/**
 * hald_g_value_equal:
 *
 * how is this not in glib?
 **/
static gboolean
hald_g_value_equal (GValue *one, GValue *two)
{
	if (G_VALUE_TYPE (one) != G_VALUE_TYPE (two)) {
		g_error ("cannot compare types");
	}
	GType type = G_VALUE_TYPE (one);
	if (type == G_TYPE_STRING) {
		const char *o = g_value_get_string (one);
		const char *t = g_value_get_string (two);
		if (o == NULL || t == NULL)
			return FALSE;
		return (strcmp (o, t) == 0);
	} else if (type == G_TYPE_INT) {
		int o = g_value_get_int (one);
		int t = g_value_get_int (two);
		return (o == t);
	} else if (type == G_TYPE_FLOAT) {
		float o = g_value_get_float (one);
		float t = g_value_get_float (two);
		return (o == t);
	}
	g_error ("type not supported");
	return FALSE;
}

/**
 * hald_fdi_merge_item_merge:
 *
 * Merges information from the fdi entry into the device
 **/
static gboolean
hald_fdi_merge_item_merge (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link)
{
	GValue *new;
	gboolean ret;
	new = hald_get_gvalue_from_type (link->value_data, link->data);
	if (new == NULL) {
		return FALSE;
	}
	ret = hald_device_set_property (device, link->match_value, new, NULL);
	g_free (new);
	return ret;
}

/**
 * hald_fdi_merge_item_match:
 *
 * Find out if a device matches a rule
 **/
static gboolean
hald_fdi_merge_item_match (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link)
{
	GValue *new1 = NULL;
	GValue *new2 = NULL;

	/* convert the fdi value to a GValue */
	/* TODO: do this in parsing */
	new1 = hald_get_gvalue_from_type (link->value_type, link->value_data);
	if (new1 == NULL) {
		return FALSE;
	}

	hald_device_get_property (device, link->match_value, &new2, NULL);
	if (new2 == NULL) {
		return FALSE;
	}
	return hald_g_value_equal (new1, new2);
}

/**
 * hald_fdi_merge_item_spawn:
 *
 * Creates a new device. We have to emit this back to the manager class as we
 * have no reference to manager here.
 **/
static gboolean
hald_fdi_merge_item_spawn (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link)
{
	const char *udi = link->match_value;
	g_signal_emit (fdi, signals [CREATE_DEVICE], 0, udi);
	return TRUE;
}

/**
 * hald_fdi_merge_item_append:
 *
 * Appends an item onto a string list
 **/
static gboolean
hald_fdi_merge_item_append (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link)
{
	const char *key = link->match_value;
	return hald_device_string_list_append (device, key, link->data, NULL);
}

/**
 * hald_fdi_merge_item_prepend:
 *
 * Prepends an item onto a string list
 **/
static gboolean
hald_fdi_merge_item_prepend (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link)
{
	const char *key = link->match_value;
	return hald_device_string_list_prepend (device, key, link->data, NULL);
}

/**
 * hald_fdi_merge_item_remove:
 *
 * Removes a property
 **/
static gboolean
hald_fdi_merge_item_remove (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link)
{
	const char *key = link->match_value;
	return hald_device_remove_property (device, key, NULL);
}

/**
 * hald_fdi_merge_recurse:
 **/
static void
hald_fdi_merge_recurse (HaldFdi *fdi, HaldDevice *device, GNode *node)
{
	HaldFdiLink *link;
	gboolean cont;
	if (node == NULL) {
		return;
	}

	link = node->data;
	cont = TRUE;
	/* if we pass in root directly, then the first link is NULL */
	if (link) {
		if (link->type == HALD_FDI_LINK_MATCH) {
			cont = hald_fdi_merge_item_match (fdi, device, link);
		} else if (link->type == HALD_FDI_LINK_MERGE) {
			hald_fdi_merge_item_merge (fdi, device, link);
		} else if (link->type == HALD_FDI_LINK_SPAWN) {
			hald_fdi_merge_item_spawn (fdi, device, link);
		} else if (link->type == HALD_FDI_LINK_APPEND) {
			hald_fdi_merge_item_append (fdi, device, link);
		} else if (link->type == HALD_FDI_LINK_PREPEND) {
			hald_fdi_merge_item_prepend (fdi, device, link);
		} else if (link->type == HALD_FDI_LINK_REMOVE) {
			hald_fdi_merge_item_remove (fdi, device, link);
		} else {
			g_warning ("no action!");
		}
	}
	/* only do children if the match was okay */
	if (cont) {
		hald_fdi_merge_recurse (fdi, device, node->children);
	}
	hald_fdi_merge_recurse (fdi, device, node->next);
}

/**
 * hald_fdi_merge_recurse:
 *
 * We have device, try and match the cached fdi stuff
 **/
gboolean
hald_fdi_merge (HaldFdi *fdi, HaldDevice *device)
{
	g_return_val_if_fail (HALD_IS_FDI (fdi), FALSE);
	g_return_val_if_fail (HALD_IS_DEVICE (device), FALSE);
	hald_fdi_merge_recurse (fdi, device, fdi->priv->root->children);
	return TRUE;
}

/**
 * hald_fdi_class_init:
 * @klass: The HaldFdiClass
 **/
static void
hald_fdi_class_init (HaldFdiClass *klass)
{
	GObjectClass   *object_class = G_OBJECT_CLASS (klass);

	object_class->finalize	   = hald_fdi_finalize;

	g_type_class_add_private (klass, sizeof (HaldFdiPrivate));

	signals [CREATE_DEVICE] =
		g_signal_new ("create-device",
			      G_TYPE_FROM_CLASS (object_class),
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (HaldFdiClass, create_device),
			      NULL,
			      NULL,
			      g_cclosure_marshal_VOID__STRING,
			      G_TYPE_NONE,
			      1, G_TYPE_STRING);
}

/**
 * hald_fdi_init:
 * @fdi: This fdi class instance
 **/
static void
hald_fdi_init (HaldFdi *fdi)
{
	fdi->priv = HALD_FDI_GET_PRIVATE (fdi);
	fdi->priv->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
	fdi->priv->root = g_node_new (NULL);
}

/**
 * hald_fdi_free_item_recurse:
 *
 * Free all the text data for each node
 **/
static void
hald_fdi_free_item_recurse (GNode *node)
{
	HaldFdiLink *link;
	if (node == NULL) {
		return;
	}

	link = node->data;
	/* if we pass in root directly, then the first link is NULL */
	if (link) {
		if (link->match_type) {
			g_free (link->match_type);
		}
		if (link->match_value) {
			g_free (link->match_value);
		}
		if (link->value_type) {
			g_free (link->value_type);
		}
		if (link->value_data) {
			g_free (link->value_data);
		}
		if (link->data) {
			g_free (link->data);
		}
	}
	hald_fdi_free_item_recurse (node->children);
	hald_fdi_free_item_recurse (node->next);
}

/**
 * hald_fdi_finalize:
 * @object: The object to finalize
 *
 * Finalise the fdi, by unref'ing all the depending modules.
 **/
static void
hald_fdi_finalize (GObject *object)
{
	HaldFdi *fdi;

	g_return_if_fail (object != NULL);
	g_return_if_fail (HALD_IS_FDI (object));

	fdi = HALD_FDI (object);
	g_return_if_fail (fdi->priv != NULL);

	/* free all the node data */
	hald_fdi_free_item_recurse (fdi->priv->root->children);

	g_hash_table_destroy (fdi->priv->hash);

	G_OBJECT_CLASS (hald_fdi_parent_class)->finalize (object);
}

/**
 * hald_fdi_new:
 *
 * Return value: a new HaldFdi object.
 **/
HaldFdi *
hald_fdi_new (void)
{
	if (hald_fdi_object) {
		g_object_ref (hald_fdi_object);
	} else {
		hald_fdi_object = g_object_new (HALD_TYPE_FDI, NULL);
		g_object_add_weak_pointer (hald_fdi_object,
					   (gpointer *) &hald_fdi_object);
	}
	return HALD_FDI (hald_fdi_object);
}