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);
}