1N/A/***************************************************************************
1N/A * CVSID: $Id$
1N/A *
1N/A * device_store.c : Search for .fdi files and merge on match
1N/A *
1N/A * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
1N/A *
1N/A * Licensed under the Academic Free License version 2.1
1N/A *
1N/A * This program is free software; you can redistribute it and/or modify
1N/A * it under the terms of the GNU General Public License as published by
1N/A * the Free Software Foundation; either version 2 of the License, or
1N/A * (at your option) any later version.
1N/A *
1N/A * This program is distributed in the hope that it will be useful,
1N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
1N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A * GNU General Public License for more details.
1N/A *
1N/A * You should have received a copy of the GNU General Public License
1N/A * along with this program; if not, write to the Free Software
1N/A * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1N/A *
1N/A **************************************************************************/
1N/A
1N/A#ifdef HAVE_CONFIG_H
1N/A# include <config.h>
1N/A#endif
1N/A
1N/A#include <stdio.h>
1N/A#include <stdlib.h>
1N/A#include <string.h>
1N/A#include <dirent.h>
1N/A#include <expat.h>
1N/A#include <assert.h>
1N/A#include <dbus/dbus.h>
1N/A#include <dbus/dbus-glib.h>
1N/A#include <math.h>
1N/A
1N/A#include "hald.h"
1N/A#include "logger.h"
1N/A#include "device_info.h"
1N/A#include "device_store.h"
1N/A#include "util.h"
1N/A
1N/A/**
1N/A * @defgroup DeviceInfo Device Info File Parsing
1N/A * @ingroup HalDaemon
1N/A * @brief Parsing of device info files
1N/A * @{
1N/A */
1N/A
1N/A
1N/A/** Maximum nesting depth */
1N/A#define MAX_DEPTH 32
1N/A
1N/A/** Maximum amount of CDATA */
1N/A#define CDATA_BUF_SIZE 1024
1N/A
1N/A/** Max length of property key */
1N/A#define MAX_KEY_SIZE 128
1N/A
1N/A/** Possible elements the parser can process */
1N/Aenum {
1N/A /** Not processing a known tag */
1N/A CURELEM_UNKNOWN = -1,
1N/A
1N/A /** Processing a deviceinfo element */
1N/A CURELEM_DEVICE_INFO = 0,
1N/A
1N/A /** Processing a device element */
1N/A CURELEM_DEVICE = 1,
1N/A
1N/A /** Processing a match element */
1N/A CURELEM_MATCH = 2,
1N/A
1N/A /** Processing a merge element */
1N/A CURELEM_MERGE = 3,
1N/A
1N/A /** Processing an append element */
1N/A CURELEM_APPEND = 4,
1N/A
1N/A /** Processing a prepend element */
1N/A CURELEM_PREPEND = 5,
1N/A
1N/A /** Processing a remove element */
1N/A CURELEM_REMOVE = 6,
1N/A
1N/A /** Processing a clear element */
1N/A CURELEM_CLEAR = 7,
1N/A
1N/A /** Processing a spawn element */
1N/A CURELEM_SPAWN = 8
1N/A};
1N/A
1N/A/** What and how to merge */
1N/Aenum {
1N/A MERGE_TYPE_UNKNOWN = 0,
1N/A MERGE_TYPE_STRING = 1,
1N/A MERGE_TYPE_BOOLEAN = 2,
1N/A MERGE_TYPE_INT32 = 3,
1N/A MERGE_TYPE_UINT64 = 4,
1N/A MERGE_TYPE_DOUBLE = 5,
1N/A MERGE_TYPE_COPY_PROPERTY = 6,
1N/A MERGE_TYPE_STRLIST = 7,
1N/A MERGE_TYPE_REMOVE = 8,
1N/A MERGE_TYPE_CLEAR = 9,
1N/A MERGE_TYPE_SPAWN = 10
1N/A};
1N/A
1N/A/** Parsing Context
1N/A */
1N/Atypedef struct {
1N/A /** Name of file being parsed */
1N/A char *file;
1N/A
1N/A /** Parser object */
1N/A XML_Parser parser;
1N/A
1N/A /** Device we are trying to match*/
1N/A HalDevice *device;
1N/A
1N/A /** Buffer to put CDATA in */
1N/A char cdata_buf[CDATA_BUF_SIZE];
1N/A
1N/A /** Current length of CDATA buffer */
1N/A int cdata_buf_len;
1N/A
1N/A /** Current depth we are parsing at */
1N/A int depth;
1N/A
1N/A /** Element currently being processed */
1N/A int curelem;
1N/A
1N/A /** Stack of elements being processed */
1N/A int curelem_stack[MAX_DEPTH];
1N/A
1N/A /** #TRUE if parsing of document have been aborted */
1N/A dbus_bool_t aborted;
1N/A
1N/A
1N/A /** Depth of match-fail */
1N/A int match_depth_first_fail;
1N/A
1N/A /** #TRUE if all matches on prior depths have been OK */
1N/A dbus_bool_t match_ok;
1N/A
1N/A
1N/A
1N/A /** When merging, the key to store the value in */
1N/A char merge_key[MAX_KEY_SIZE];
1N/A
1N/A /** Type to merge*/
1N/A int merge_type;
1N/A
1N/A /** Set to #TRUE if a device is matched */
1N/A dbus_bool_t device_matched;
1N/A
1N/A} ParsingContext;
1N/A
1N/A/** Resolve a udi-property path as used in .fdi files.
1N/A *
1N/A * Examples of udi-property paths:
1N/A *
1N/A * info.udi
1N/A * /org/freedesktop/Hal/devices/computer:kernel.name
1N/A * @block.storage_device:storage.bus
1N/A * @block.storage_device:@storage.physical_device:ide.channel
1N/A *
1N/A * @param source_udi UDI of source device
1N/A * @param path The given path
1N/A * @param udi_result Where to store the resulting UDI
1N/A * @param udi_result_size Size of UDI string
1N/A * @param prop_result Where to store the resulting property name
1N/A * @param prop_result_size Size of property string
1N/A * @return TRUE if and only if the path resolved.
1N/A */
1N/Astatic gboolean
1N/Aresolve_udiprop_path (const char *path, const char *source_udi,
1N/A char *udi_result, size_t udi_result_size,
1N/A char *prop_result, size_t prop_result_size)
1N/A{
1N/A int i;
1N/A gchar **tokens = NULL;
1N/A gboolean rc;
1N/A
1N/A rc = FALSE;
1N/A
1N/A /*HAL_INFO (("Looking at '%s' for udi='%s'", path, source_udi));*/
1N/A
1N/A /* Split up path into ':' tokens */
1N/A tokens = g_strsplit (path, ":", 64);
1N/A
1N/A /* Detect trivial property access, e.g. path='foo.bar' */
1N/A if (tokens == NULL || tokens[0] == NULL || tokens[1] == NULL) {
1N/A strncpy (udi_result, source_udi, udi_result_size);
1N/A strncpy (prop_result, path, prop_result_size);
1N/A rc = TRUE;
1N/A goto out;
1N/A }
1N/A
1N/A /* Start with the source udi */
1N/A strncpy (udi_result, source_udi, udi_result_size);
1N/A
1N/A for (i = 0; tokens[i] != NULL; i++) {
1N/A HalDevice *d;
1N/A gchar *curtoken;
1N/A
1N/A /*HAL_INFO (("tokens[%d] = '%s'", i, tokens[i]));*/
1N/A
1N/A d = hal_device_store_find (hald_get_gdl (), udi_result);
1N/A if (d == NULL)
1N/A d = hal_device_store_find (hald_get_tdl (), udi_result);
1N/A if (d == NULL)
1N/A goto out;
1N/A
1N/A curtoken = tokens[i];
1N/A
1N/A /* process all but the last tokens as UDI paths */
1N/A if (tokens[i+1] == NULL) {
1N/A strncpy (prop_result, curtoken, prop_result_size);
1N/A rc = TRUE;
1N/A goto out;
1N/A }
1N/A
1N/A
1N/A /* Check for indirection */
1N/A if (curtoken[0] == '@') {
1N/A const char *udiprop;
1N/A const char *newudi;
1N/A
1N/A udiprop = curtoken + 1;
1N/A
1N/A newudi = hal_device_property_get_string (d, udiprop);
1N/A if (newudi == NULL)
1N/A goto out;
1N/A
1N/A /*HAL_INFO (("new_udi = '%s' (from indirection)", newudi));*/
1N/A
1N/A strncpy (udi_result, newudi, udi_result_size);
1N/A } else {
1N/A /*HAL_INFO (("new_udi = '%s'", curtoken));*/
1N/A strncpy (udi_result, curtoken, udi_result_size);
1N/A }
1N/A
1N/A }
1N/A
1N/Aout:
1N/A
1N/A/*
1N/A HAL_INFO (("success = '%s'", rc ? "yes" : "no"));
1N/A HAL_INFO (("udi_result = '%s'", udi_result));
1N/A HAL_INFO (("prop_result = '%s'", prop_result));
1N/A*/
1N/A
1N/A g_strfreev (tokens);
1N/A
1N/A return rc;
1N/A}
1N/A
1N/A/* Compare the value of a property on a hal device object against a string value
1N/A * and return the result. Note that this works for several types, e.g. both strings
1N/A * and integers - in the latter case the given right side string will be interpreted
1N/A * as a number.
1N/A *
1N/A * The comparison might not make sense if you are comparing a property which is an integer
1N/A * against a string in which case this function returns FALSE. Also, if the property doesn't
1N/A * exist this function will also return FALSE.
1N/A *
1N/A * @param d hal device object
1N/A * @param key Key of the property to compare
1N/A * @param right_side Value to compare against
1N/A * @param result Pointer to where to store result
1N/A * @return TRUE if, and only if, the comparison could take place
1N/A */
1N/Astatic gboolean
1N/Amatch_compare_property (HalDevice *d, const char *key, const char *right_side, dbus_int64_t *result)
1N/A{
1N/A gboolean rc;
1N/A int proptype;
1N/A
1N/A rc = FALSE;
1N/A
1N/A if (!hal_device_has_property (d, key))
1N/A goto out;
1N/A
1N/A proptype = hal_device_property_get_type (d, key);
1N/A switch (proptype) {
1N/A case HAL_PROPERTY_TYPE_STRING:
1N/A *result = (dbus_int64_t) strcmp (hal_device_property_get_string (d, key), right_side);
1N/A rc = TRUE;
1N/A break;
1N/A
1N/A case HAL_PROPERTY_TYPE_INT32:
1N/A *result = ((dbus_int64_t) hal_device_property_get_int (d, key)) - strtoll (right_side, NULL, 0);
1N/A rc = TRUE;
1N/A break;
1N/A
1N/A case HAL_PROPERTY_TYPE_UINT64:
1N/A *result = ((dbus_int64_t) hal_device_property_get_uint64 (d, key)) - ((dbus_int64_t) strtoll (right_side, NULL, 0));
1N/A rc = TRUE;
1N/A break;
1N/A
1N/A case HAL_PROPERTY_TYPE_DOUBLE:
1N/A *result = (dbus_int64_t) ceil (hal_device_property_get_double (d, key) - atof (right_side));
1N/A rc = TRUE;
1N/A break;
1N/A
1N/A default:
1N/A /* explicit fallthrough */
1N/A case HAL_PROPERTY_TYPE_BOOLEAN:
1N/A /* explicit blank since this doesn't make sense */
1N/A break;
1N/A }
1N/A
1N/Aout:
1N/A return rc;
1N/A}
1N/A
1N/A/** Called when the match element begins.
1N/A *
1N/A * @param pc Parsing context
1N/A * @param attr Attribute key/value pairs
1N/A * @return #FALSE if the device in question didn't
1N/A * match the data in the attributes
1N/A */
1N/Astatic dbus_bool_t
1N/Ahandle_match (ParsingContext * pc, const char **attr)
1N/A{
1N/A char udi_to_check[256];
1N/A char prop_to_check[256];
1N/A const char *key;
1N/A int num_attrib;
1N/A HalDevice *d;
1N/A
1N/A for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++);
1N/A
1N/A if (num_attrib != 4)
1N/A return FALSE;
1N/A
1N/A if (strcmp (attr[0], "key") != 0)
1N/A return FALSE;
1N/A key = attr[1];
1N/A
1N/A /* Resolve key paths like 'someudi/foo/bar/baz:prop.name' '@prop.here.is.an.udi:with.prop.name' */
1N/A if (!resolve_udiprop_path (key,
1N/A pc->device->udi,
1N/A udi_to_check, sizeof (udi_to_check),
1N/A prop_to_check, sizeof (prop_to_check))) {
1N/A HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", key, pc->device->udi));
1N/A return FALSE;
1N/A }
1N/A
1N/A d = hal_device_store_find (hald_get_gdl (), udi_to_check);
1N/A if (d == NULL) {
1N/A d = hal_device_store_find (hald_get_tdl (), udi_to_check);
1N/A }
1N/A if (d == NULL) {
1N/A HAL_ERROR (("Could not find device with udi '%s'", udi_to_check));
1N/A return FALSE;
1N/A }
1N/A
1N/A
1N/A if (strcmp (attr[2], "string") == 0) {
1N/A const char *value;
1N/A
1N/A /* match string property */
1N/A
1N/A value = attr[3];
1N/A
1N/A /*HAL_INFO(("Checking that key='%s' is a string that "
1N/A "equals '%s'", key, value)); */
1N/A
1N/A if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
1N/A return FALSE;
1N/A
1N/A if (strcmp (hal_device_property_get_string (d, prop_to_check),
1N/A value) != 0)
1N/A return FALSE;
1N/A
1N/A /*HAL_INFO (("*** string match for key %s", key));*/
1N/A return TRUE;
1N/A } else if (strcmp (attr[2], "int") == 0) {
1N/A dbus_int32_t value;
1N/A
1N/A /* match integer property */
1N/A value = strtol (attr[3], NULL, 0);
1N/A
1N/A /** @todo Check error condition */
1N/A
1N/A /*HAL_INFO (("Checking that key='%s' is a int that equals %d",
1N/A key, value));*/
1N/A
1N/A if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_INT32)
1N/A return FALSE;
1N/A
1N/A if (hal_device_property_get_int (d, prop_to_check) != value) {
1N/A return FALSE;
1N/A }
1N/A
1N/A return TRUE;
1N/A } else if (strcmp (attr[2], "uint64") == 0) {
1N/A dbus_uint64_t value;
1N/A
1N/A /* match integer property */
1N/A value = strtoull (attr[3], NULL, 0);
1N/A
1N/A /** @todo Check error condition */
1N/A
1N/A /*HAL_INFO (("Checking that key='%s' is a int that equals %d",
1N/A key, value));*/
1N/A
1N/A if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_UINT64)
1N/A return FALSE;
1N/A
1N/A if (hal_device_property_get_uint64 (d, prop_to_check) != value) {
1N/A return FALSE;
1N/A }
1N/A
1N/A return TRUE;
1N/A } else if (strcmp (attr[2], "bool") == 0) {
1N/A dbus_bool_t value;
1N/A
1N/A /* match string property */
1N/A
1N/A if (strcmp (attr[3], "false") == 0)
1N/A value = FALSE;
1N/A else if (strcmp (attr[3], "true") == 0)
1N/A value = TRUE;
1N/A else
1N/A return FALSE;
1N/A
1N/A /*HAL_INFO (("Checking that key='%s' is a bool that equals %s",
1N/A key, value ? "TRUE" : "FALSE"));*/
1N/A
1N/A if (hal_device_property_get_type (d, prop_to_check) !=
1N/A HAL_PROPERTY_TYPE_BOOLEAN)
1N/A return FALSE;
1N/A
1N/A if (hal_device_property_get_bool (d, prop_to_check) != value)
1N/A return FALSE;
1N/A
1N/A /*HAL_INFO (("*** bool match for key %s", key));*/
1N/A return TRUE;
1N/A } else if (strcmp (attr[2], "exists") == 0) {
1N/A dbus_bool_t should_exist = TRUE;
1N/A
1N/A if (strcmp (attr[3], "false") == 0)
1N/A should_exist = FALSE;
1N/A
1N/A if (should_exist) {
1N/A if (hal_device_has_property (d, prop_to_check))
1N/A return TRUE;
1N/A else
1N/A return FALSE;
1N/A } else {
1N/A if (hal_device_has_property (d, prop_to_check))
1N/A return FALSE;
1N/A else
1N/A return TRUE;
1N/A }
1N/A } else if (strcmp (attr[2], "empty") == 0) {
1N/A int type;
1N/A dbus_bool_t is_empty = TRUE;
1N/A dbus_bool_t should_be_empty = TRUE;
1N/A
1N/A
1N/A if (strcmp (attr[3], "false") == 0)
1N/A should_be_empty = FALSE;
1N/A
1N/A type = hal_device_property_get_type (d, prop_to_check);
1N/A switch (type) {
1N/A case HAL_PROPERTY_TYPE_STRING:
1N/A if (hal_device_has_property (d, prop_to_check))
1N/A if (strlen (hal_device_property_get_string (d, prop_to_check)) > 0)
1N/A is_empty = FALSE;
1N/A break;
1N/A case HAL_PROPERTY_TYPE_STRLIST:
1N/A if (hal_device_has_property (d, prop_to_check))
1N/A if (!hal_device_property_strlist_is_empty(d, prop_to_check))
1N/A is_empty = FALSE;
1N/A break;
1N/A default:
1N/A /* explicit fallthrough */
1N/A return FALSE;
1N/A break;
1N/A }
1N/A
1N/A if (should_be_empty) {
1N/A if (is_empty)
1N/A return TRUE;
1N/A else
1N/A return FALSE;
1N/A } else {
1N/A if (is_empty)
1N/A return FALSE;
1N/A else
1N/A return TRUE;
1N/A }
1N/A } else if (strcmp (attr[2], "is_ascii") == 0) {
1N/A dbus_bool_t is_ascii = TRUE;
1N/A dbus_bool_t should_be_ascii = TRUE;
1N/A unsigned int i;
1N/A const char *str;
1N/A
1N/A if (strcmp (attr[3], "false") == 0)
1N/A should_be_ascii = FALSE;
1N/A
1N/A if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
1N/A return FALSE;
1N/A
1N/A is_ascii = TRUE;
1N/A
1N/A str = hal_device_property_get_string (d, prop_to_check);
1N/A for (i = 0; str[i] != '\0'; i++) {
1N/A if (((unsigned char) str[i]) > 0x7f)
1N/A is_ascii = FALSE;
1N/A }
1N/A
1N/A if (should_be_ascii) {
1N/A if (is_ascii)
1N/A return TRUE;
1N/A else
1N/A return FALSE;
1N/A } else {
1N/A if (is_ascii)
1N/A return FALSE;
1N/A else
1N/A return TRUE;
1N/A }
1N/A } else if (strcmp (attr[2], "is_absolute_path") == 0) {
1N/A const char *path = NULL;
1N/A dbus_bool_t is_absolute_path = FALSE;
1N/A dbus_bool_t should_be_absolute_path = TRUE;
1N/A
1N/A if (strcmp (attr[3], "false") == 0)
1N/A should_be_absolute_path = FALSE;
1N/A
1N/A /*HAL_INFO (("d->udi='%s', prop_to_check='%s'", d->udi, prop_to_check));*/
1N/A
1N/A if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
1N/A return FALSE;
1N/A
1N/A if (hal_device_has_property (d, prop_to_check)) {
1N/A path = hal_device_property_get_string (d, prop_to_check);
1N/A if (g_path_is_absolute (path))
1N/A is_absolute_path = TRUE;
1N/A }
1N/A
1N/A /*HAL_INFO (("is_absolute=%d, should_be=%d, path='%s'", is_absolute_path, should_be_absolute_path, path));*/
1N/A
1N/A if (should_be_absolute_path) {
1N/A if (is_absolute_path)
1N/A return TRUE;
1N/A else
1N/A return FALSE;
1N/A } else {
1N/A if (is_absolute_path)
1N/A return FALSE;
1N/A else
1N/A return TRUE;
1N/A }
1N/A } else if (strcmp (attr[2], "contains") == 0) {
1N/A const char *needle;
1N/A dbus_bool_t contains = FALSE;
1N/A
1N/A needle = attr[3];
1N/A
1N/A if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRING) {
1N/A if (hal_device_has_property (d, prop_to_check)) {
1N/A const char *haystack;
1N/A
1N/A haystack = hal_device_property_get_string (d, prop_to_check);
1N/A if (needle != NULL && haystack != NULL && strstr (haystack, needle)) {
1N/A contains = TRUE;
1N/A }
1N/A
1N/A }
1N/A } else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST &&
1N/A needle != NULL) {
1N/A GSList *i;
1N/A GSList *value;
1N/A
1N/A value = hal_device_property_get_strlist (d, prop_to_check);
1N/A for (i = value; i != NULL; i = g_slist_next (i)) {
1N/A const char *str = i->data;
1N/A if (strcmp (str, needle) == 0) {
1N/A contains = TRUE;
1N/A break;
1N/A }
1N/A }
1N/A } else {
1N/A return FALSE;
1N/A }
1N/A
1N/A return contains;
1N/A } else if (strcmp (attr[2], "contains_ncase") == 0) {
1N/A const char *needle;
1N/A dbus_bool_t contains_ncase = FALSE;
1N/A
1N/A needle = attr[3];
1N/A
1N/A if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRING) {
1N/A if (hal_device_has_property (d, prop_to_check)) {
1N/A char *needle_lowercase;
1N/A char *haystack_lowercase;
1N/A
1N/A needle_lowercase = g_utf8_strdown (needle, -1);
1N/A haystack_lowercase = g_utf8_strdown (hal_device_property_get_string (d, prop_to_check), -1);
1N/A if (needle_lowercase != NULL && haystack_lowercase != NULL && strstr (haystack_lowercase, needle_lowercase)) {
1N/A contains_ncase = TRUE;
1N/A }
1N/A
1N/A g_free (needle_lowercase);
1N/A g_free (haystack_lowercase);
1N/A }
1N/A } else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST &&
1N/A needle != NULL) {
1N/A GSList *i;
1N/A GSList *value;
1N/A
1N/A value = hal_device_property_get_strlist (d, prop_to_check);
1N/A for (i = value; i != NULL; i = g_slist_next (i)) {
1N/A const char *str = i->data;
1N/A if (g_ascii_strcasecmp (str, needle) == 0) {
1N/A contains_ncase = TRUE;
1N/A break;
1N/A }
1N/A }
1N/A } else {
1N/A return FALSE;
1N/A }
1N/A
1N/A return contains_ncase;
1N/A } else if (strcmp (attr[2], "compare_lt") == 0) {
1N/A dbus_int64_t result;
1N/A if (!match_compare_property (d, prop_to_check, attr[3], &result)) {
1N/A return FALSE;
1N/A } else {
1N/A return result < 0;
1N/A }
1N/A } else if (strcmp (attr[2], "compare_le") == 0) {
1N/A dbus_int64_t result;
1N/A if (!match_compare_property (d, prop_to_check, attr[3], &result))
1N/A return FALSE;
1N/A else
1N/A return result <= 0;
1N/A } else if (strcmp (attr[2], "compare_gt") == 0) {
1N/A dbus_int64_t result;
1N/A if (!match_compare_property (d, prop_to_check, attr[3], &result))
1N/A return FALSE;
1N/A else
1N/A return result > 0;
1N/A } else if (strcmp (attr[2], "compare_ge") == 0) {
1N/A dbus_int64_t result;
1N/A if (!match_compare_property (d, prop_to_check, attr[3], &result))
1N/A return FALSE;
1N/A else
1N/A return result >= 0;
1N/A }
1N/A
1N/A return FALSE;
1N/A}
1N/A
1N/A
1N/A/** Called when the merge element begins.
1N/A *
1N/A * @param pc Parsing context
1N/A * @param attr Attribute key/value pairs
1N/A */
1N/Astatic void
1N/Ahandle_merge (ParsingContext * pc, const char **attr)
1N/A{
1N/A int num_attrib;
1N/A
1N/A pc->merge_type = MERGE_TYPE_UNKNOWN;
1N/A
1N/A
1N/A for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
1N/A ;
1N/A }
1N/A
1N/A if (num_attrib != 4)
1N/A return;
1N/A
1N/A if (strcmp (attr[0], "key") != 0)
1N/A return;
1N/A strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
1N/A
1N/A if (strcmp (attr[2], "type") != 0)
1N/A return;
1N/A
1N/A if (strcmp (attr[3], "string") == 0) {
1N/A /* match string property */
1N/A pc->merge_type = MERGE_TYPE_STRING;
1N/A return;
1N/A } else if (strcmp (attr[3], "bool") == 0) {
1N/A /* match string property */
1N/A pc->merge_type = MERGE_TYPE_BOOLEAN;
1N/A return;
1N/A } else if (strcmp (attr[3], "int") == 0) {
1N/A /* match string property */
1N/A pc->merge_type = MERGE_TYPE_INT32;
1N/A return;
1N/A } else if (strcmp (attr[3], "uint64") == 0) {
1N/A /* match string property */
1N/A pc->merge_type = MERGE_TYPE_UINT64;
1N/A return;
1N/A } else if (strcmp (attr[3], "double") == 0) {
1N/A /* match string property */
1N/A pc->merge_type = MERGE_TYPE_DOUBLE;
1N/A return;
1N/A } else if (strcmp (attr[3], "strlist") == 0) {
1N/A /* match string property */
1N/A pc->merge_type = MERGE_TYPE_STRLIST;
1N/A return;
1N/A } else if (strcmp (attr[3], "copy_property") == 0) {
1N/A /* copy another property */
1N/A pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
1N/A return;
1N/A }
1N/A
1N/A return;
1N/A}
1N/A
1N/A/** Called when the append or prepend element begins.
1N/A *
1N/A * @param pc Parsing context
1N/A * @param attr Attribute key/value pairs
1N/A */
1N/Astatic void
1N/Ahandle_append_prepend (ParsingContext * pc, const char **attr)
1N/A{
1N/A int num_attrib;
1N/A
1N/A pc->merge_type = MERGE_TYPE_UNKNOWN;
1N/A
1N/A for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
1N/A ;
1N/A }
1N/A
1N/A if (num_attrib != 4)
1N/A return;
1N/A
1N/A if (strcmp (attr[0], "key") != 0)
1N/A return;
1N/A strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
1N/A
1N/A if (strcmp (attr[2], "type") != 0)
1N/A return;
1N/A
1N/A if (strcmp (attr[3], "string") == 0) {
1N/A /* append to a string */
1N/A pc->merge_type = MERGE_TYPE_STRING;
1N/A return;
1N/A } else if (strcmp (attr[3], "strlist") == 0) {
1N/A /* append to a string list*/
1N/A pc->merge_type = MERGE_TYPE_STRLIST;
1N/A return;
1N/A } else if (strcmp (attr[3], "copy_property") == 0) {
1N/A /* copy another property */
1N/A pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
1N/A return;
1N/A }
1N/A
1N/A return;
1N/A}
1N/A
1N/A
1N/A/** Called when the spawn element begins.
1N/A *
1N/A * @param pc Parsing context
1N/A * @param attr Attribute key/value pairs
1N/A */
1N/Astatic void
1N/Ahandle_spawn (ParsingContext * pc, const char **attr)
1N/A{
1N/A int num_attrib;
1N/A
1N/A pc->merge_type = MERGE_TYPE_UNKNOWN;
1N/A
1N/A for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
1N/A ;
1N/A }
1N/A
1N/A if (num_attrib != 2)
1N/A return;
1N/A
1N/A if (strcmp (attr[0], "udi") != 0)
1N/A return;
1N/A
1N/A strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
1N/A
1N/A pc->merge_type = MERGE_TYPE_SPAWN;
1N/A return;
1N/A}
1N/A
1N/A/** Called when the remove element begins.
1N/A *
1N/A * @param pc Parsing context
1N/A * @param attr Attribute key/value pairs
1N/A */
1N/Astatic void
1N/Ahandle_remove (ParsingContext * pc, const char **attr)
1N/A{
1N/A int num_attrib;
1N/A
1N/A pc->merge_type = MERGE_TYPE_UNKNOWN;
1N/A
1N/A for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
1N/A ;
1N/A }
1N/A
1N/A if (num_attrib != 2 && num_attrib != 4)
1N/A return;
1N/A
1N/A if (strcmp (attr[0], "key") != 0)
1N/A return;
1N/A strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
1N/A
1N/A if (num_attrib == 4) {
1N/A if (strcmp (attr[2], "type") != 0)
1N/A return;
1N/A
1N/A if (strcmp (attr[3], "strlist") == 0) {
1N/A /* remove from strlist */
1N/A pc->merge_type = MERGE_TYPE_STRLIST;
1N/A return;
1N/A } else {
1N/A pc->merge_type = MERGE_TYPE_UNKNOWN;
1N/A return;
1N/A }
1N/A } else {
1N/A pc->merge_type = MERGE_TYPE_REMOVE;
1N/A }
1N/A
1N/A return;
1N/A}
1N/A
1N/A/** Called when the clear element begins.
1N/A *
1N/A * @param pc Parsing context
1N/A * @param attr Attribute key/value pairs
1N/A */
1N/Astatic void
1N/Ahandle_clear (ParsingContext * pc, const char **attr)
1N/A{
1N/A int num_attrib;
1N/A
1N/A pc->merge_type = MERGE_TYPE_UNKNOWN;
1N/A
1N/A for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
1N/A ;
1N/A }
1N/A
1N/A if (num_attrib != 4)
1N/A return;
1N/A
1N/A if (strcmp (attr[0], "key") != 0)
1N/A return;
1N/A
1N/A
1N/A if (strcmp (attr[3], "strlist") != 0)
1N/A return;
1N/A
1N/A strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
1N/A
1N/A pc->merge_type = MERGE_TYPE_CLEAR;
1N/A
1N/A return;
1N/A}
1N/A
1N/A/** Abort parsing of document
1N/A *
1N/A * @param pc Parsing context
1N/A */
1N/Astatic void
1N/Aparsing_abort (ParsingContext * pc)
1N/A{
1N/A /* Grr, expat can't abort parsing */
1N/A HAL_ERROR (("Aborting parsing of document"));
1N/A pc->aborted = TRUE;
1N/A}
1N/A
1N/A/** Called by expat when an element begins.
1N/A *
1N/A * @param pc Parsing context
1N/A * @param el Element name
1N/A * @param attr Attribute key/value pairs
1N/A */
1N/Astatic void
1N/Astart (ParsingContext * pc, const char *el, const char **attr)
1N/A{
1N/A if (pc->aborted)
1N/A return;
1N/A
1N/A pc->cdata_buf_len = 0;
1N/A
1N/A pc->merge_type = MERGE_TYPE_UNKNOWN;
1N/A
1N/A/*
1N/A for (i = 0; i < pc->depth; i++)
1N/A printf(" ");
1N/A
1N/A printf("%s", el);
1N/A
1N/A for (i = 0; attr[i]; i += 2) {
1N/A printf(" %s='%s'", attr[i], attr[i + 1]);
1N/A }
1N/A
1N/A printf(" curelem=%d\n", pc->curelem);
1N/A*/
1N/A
1N/A if (strcmp (el, "match") == 0) {
1N/A if (pc->curelem != CURELEM_DEVICE
1N/A && pc->curelem != CURELEM_MATCH) {
1N/A HAL_ERROR (("%s:%d:%d: Element <match> can only be "
1N/A "inside <device> and <match>",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser)));
1N/A parsing_abort (pc);
1N/A }
1N/A
1N/A pc->curelem = CURELEM_MATCH;
1N/A
1N/A /* don't bother checking if matching at lower depths failed */
1N/A if (pc->match_ok) {
1N/A if (!handle_match (pc, attr)) {
1N/A /* No match */
1N/A pc->match_depth_first_fail = pc->depth;
1N/A pc->match_ok = FALSE;
1N/A }
1N/A }
1N/A } else if (strcmp (el, "merge") == 0) {
1N/A if (pc->curelem != CURELEM_DEVICE
1N/A && pc->curelem != CURELEM_MATCH) {
1N/A HAL_ERROR (("%s:%d:%d: Element <merge> can only be "
1N/A "inside <device> and <match>",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser)));
1N/A parsing_abort (pc);
1N/A }
1N/A
1N/A pc->curelem = CURELEM_MERGE;
1N/A if (pc->match_ok) {
1N/A handle_merge (pc, attr);
1N/A } else {
1N/A /*HAL_INFO(("No merge!")); */
1N/A }
1N/A } else if (strcmp (el, "append") == 0) {
1N/A if (pc->curelem != CURELEM_DEVICE
1N/A && pc->curelem != CURELEM_MATCH) {
1N/A HAL_ERROR (("%s:%d:%d: Element <append> can only be "
1N/A "inside <device> and <match>",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser)));
1N/A parsing_abort (pc);
1N/A }
1N/A
1N/A pc->curelem = CURELEM_APPEND;
1N/A if (pc->match_ok) {
1N/A handle_append_prepend (pc, attr);
1N/A } else {
1N/A /*HAL_INFO(("No merge!")); */
1N/A }
1N/A } else if (strcmp (el, "prepend") == 0) {
1N/A if (pc->curelem != CURELEM_DEVICE
1N/A && pc->curelem != CURELEM_MATCH) {
1N/A HAL_ERROR (("%s:%d:%d: Element <prepend> can only be "
1N/A "inside <device> and <match>",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser)));
1N/A parsing_abort (pc);
1N/A }
1N/A
1N/A pc->curelem = CURELEM_PREPEND;
1N/A if (pc->match_ok) {
1N/A handle_append_prepend (pc, attr);
1N/A } else {
1N/A /*HAL_INFO(("No merge!")); */
1N/A }
1N/A } else if (strcmp (el, "remove") == 0) {
1N/A if (pc->curelem != CURELEM_DEVICE
1N/A && pc->curelem != CURELEM_MATCH) {
1N/A HAL_ERROR (("%s:%d:%d: Element <remove> can only be "
1N/A "inside <device> and <match>",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser)));
1N/A parsing_abort (pc);
1N/A }
1N/A
1N/A pc->curelem = CURELEM_REMOVE;
1N/A if (pc->match_ok) {
1N/A handle_remove (pc, attr);
1N/A } else {
1N/A /*HAL_INFO(("No merge!")); */
1N/A }
1N/A } else if (strcmp (el, "clear") == 0) {
1N/A if (pc->curelem != CURELEM_DEVICE
1N/A && pc->curelem != CURELEM_MATCH) {
1N/A HAL_ERROR (("%s:%d:%d: Element <remove> can only be "
1N/A "inside <device> and <match>",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser)));
1N/A parsing_abort (pc);
1N/A }
1N/A
1N/A pc->curelem = CURELEM_CLEAR;
1N/A if (pc->match_ok) {
1N/A handle_clear (pc, attr);
1N/A } else {
1N/A /*HAL_INFO(("No merge!")); */
1N/A }
1N/A } else if (strcmp (el, "device") == 0) {
1N/A if (pc->curelem != CURELEM_DEVICE_INFO) {
1N/A HAL_ERROR (("%s:%d:%d: Element <device> can only be "
1N/A "inside <deviceinfo>",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser)));
1N/A parsing_abort (pc);
1N/A }
1N/A pc->curelem = CURELEM_DEVICE;
1N/A } else if (strcmp (el, "deviceinfo") == 0) {
1N/A if (pc->curelem != CURELEM_UNKNOWN) {
1N/A HAL_ERROR (("%s:%d:%d: Element <deviceinfo> must be "
1N/A "a top-level element",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser)));
1N/A parsing_abort (pc);
1N/A }
1N/A pc->curelem = CURELEM_DEVICE_INFO;
1N/A } else if (strcmp (el, "spawn") == 0) {
1N/A if (pc->curelem != CURELEM_MATCH) {
1N/A HAL_ERROR (("%s:%d:%d: Element <spawn> can only be "
1N/A "inside <match>",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser)));
1N/A parsing_abort (pc);
1N/A }
1N/A
1N/A pc->curelem = CURELEM_SPAWN;
1N/A if (pc->match_ok) {
1N/A handle_spawn (pc, attr);
1N/A }
1N/A
1N/A } else {
1N/A HAL_ERROR (("%s:%d:%d: Unknown element <%s>",
1N/A pc->file,
1N/A XML_GetCurrentLineNumber (pc->parser),
1N/A XML_GetCurrentColumnNumber (pc->parser), el));
1N/A parsing_abort (pc);
1N/A }
1N/A
1N/A /* Nasty hack */
1N/A assert (pc->depth < MAX_DEPTH);
1N/A
1N/A pc->depth++;
1N/A
1N/A /* store depth */
1N/A pc->curelem_stack[pc->depth] = pc->curelem;
1N/A
1N/A}
1N/A
1N/Astatic void
1N/Aspawned_device_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
1N/A{
1N/A HAL_INFO (("Add callouts completed udi=%s", d->udi));
1N/A
1N/A /* Move from temporary to global device store */
1N/A hal_device_store_remove (hald_get_tdl (), d);
1N/A hal_device_store_add (hald_get_gdl (), d);
1N/A
1N/A}
1N/A
1N/A/** Called by expat when an element ends.
1N/A *
1N/A * @param pc Parsing context
1N/A * @param el Element name
1N/A */
1N/Astatic void
1N/Aend (ParsingContext * pc, const char *el)
1N/A{
1N/A if (pc->aborted)
1N/A return;
1N/A
1N/A pc->cdata_buf[pc->cdata_buf_len] = '\0';
1N/A
1N/A/* printf(" curelem=%d\n", pc->curelem);*/
1N/A
1N/A if (pc->curelem == CURELEM_MERGE && pc->match_ok) {
1N/A /* As soon as we are merging, we have matched the device... */
1N/A pc->device_matched = TRUE;
1N/A
1N/A switch (pc->merge_type) {
1N/A case MERGE_TYPE_STRING:
1N/A hal_device_property_set_string (pc->device, pc->merge_key, pc->cdata_buf);
1N/A break;
1N/A
1N/A case MERGE_TYPE_STRLIST:
1N/A {
1N/A int type = hal_device_property_get_type (pc->device, pc->merge_key);
1N/A if (type == HAL_PROPERTY_TYPE_STRLIST || type == HAL_PROPERTY_TYPE_INVALID) {
1N/A hal_device_property_remove (pc->device, pc->merge_key);
1N/A hal_device_property_strlist_append (pc->device, pc->merge_key, pc->cdata_buf);
1N/A }
1N/A break;
1N/A }
1N/A
1N/A case MERGE_TYPE_INT32:
1N/A {
1N/A dbus_int32_t value;
1N/A
1N/A /* match integer property */
1N/A value = strtol (pc->cdata_buf, NULL, 0);
1N/A
1N/A /** @todo FIXME: Check error condition */
1N/A
1N/A hal_device_property_set_int (pc->device,
1N/A pc->merge_key, value);
1N/A break;
1N/A }
1N/A
1N/A case MERGE_TYPE_UINT64:
1N/A {
1N/A dbus_uint64_t value;
1N/A
1N/A /* match integer property */
1N/A value = strtoull (pc->cdata_buf, NULL, 0);
1N/A
1N/A /** @todo FIXME: Check error condition */
1N/A
1N/A hal_device_property_set_uint64 (pc->device,
1N/A pc->merge_key, value);
1N/A break;
1N/A }
1N/A
1N/A case MERGE_TYPE_BOOLEAN:
1N/A hal_device_property_set_bool (pc->device, pc->merge_key,
1N/A (strcmp (pc->cdata_buf,
1N/A "true") == 0)
1N/A ? TRUE : FALSE);
1N/A break;
1N/A
1N/A case MERGE_TYPE_DOUBLE:
1N/A hal_device_property_set_double (pc->device, pc->merge_key,
1N/A atof (pc->cdata_buf));
1N/A break;
1N/A
1N/A case MERGE_TYPE_COPY_PROPERTY:
1N/A {
1N/A char udi_to_merge_from[256];
1N/A char prop_to_merge[256];
1N/A
1N/A /* Resolve key paths like 'someudi/foo/bar/baz:prop.name'
1N/A * '@prop.here.is.an.udi:with.prop.name'
1N/A */
1N/A if (!resolve_udiprop_path (pc->cdata_buf,
1N/A pc->device->udi,
1N/A udi_to_merge_from, sizeof (udi_to_merge_from),
1N/A prop_to_merge, sizeof (prop_to_merge))) {
1N/A HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", pc->cdata_buf, pc->device->udi));
1N/A } else {
1N/A HalDevice *d;
1N/A
1N/A d = hal_device_store_find (hald_get_gdl (), udi_to_merge_from);
1N/A if (d == NULL) {
1N/A d = hal_device_store_find (hald_get_tdl (), udi_to_merge_from);
1N/A }
1N/A if (d == NULL) {
1N/A HAL_ERROR (("Could not find device with udi '%s'", udi_to_merge_from));
1N/A } else {
1N/A hal_device_copy_property (d, prop_to_merge, pc->device, pc->merge_key);
1N/A }
1N/A }
1N/A break;
1N/A }
1N/A
1N/A default:
1N/A HAL_ERROR (("Unknown merge_type=%d='%c'",
1N/A pc->merge_type, pc->merge_type));
1N/A break;
1N/A }
1N/A } else if (pc->curelem == CURELEM_APPEND && pc->match_ok &&
1N/A (hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRING ||
1N/A hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRLIST ||
1N/A hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_INVALID)) {
1N/A char buf[256];
1N/A char buf2[256];
1N/A
1N/A /* As soon as we are appending, we have matched the device... */
1N/A pc->device_matched = TRUE;
1N/A
1N/A if (pc->merge_type == MERGE_TYPE_STRLIST) {
1N/A hal_device_property_strlist_append (pc->device, pc->merge_key, pc->cdata_buf);
1N/A } else {
1N/A const char *existing_string;
1N/A
1N/A switch (pc->merge_type) {
1N/A case MERGE_TYPE_STRING:
1N/A strncpy (buf, pc->cdata_buf, sizeof (buf));
1N/A break;
1N/A
1N/A case MERGE_TYPE_COPY_PROPERTY:
1N/A hal_device_property_get_as_string (pc->device, pc->cdata_buf, buf, sizeof (buf));
1N/A break;
1N/A
1N/A default:
1N/A HAL_ERROR (("Unknown merge_type=%d='%c'", pc->merge_type, pc->merge_type));
1N/A break;
1N/A }
1N/A
1N/A existing_string = hal_device_property_get_string (pc->device, pc->merge_key);
1N/A if (existing_string != NULL) {
1N/A strncpy (buf2, existing_string, sizeof (buf2));
1N/A strncat (buf2, buf, sizeof (buf2) - strlen(buf2));
1N/A } else {
1N/A strncpy (buf2, buf, sizeof (buf2));
1N/A }
1N/A hal_device_property_set_string (pc->device, pc->merge_key, buf2);
1N/A }
1N/A } else if (pc->curelem == CURELEM_PREPEND && pc->match_ok &&
1N/A (hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRING ||
1N/A hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_STRLIST ||
1N/A hal_device_property_get_type (pc->device, pc->merge_key) == HAL_PROPERTY_TYPE_INVALID)) {
1N/A char buf[256];
1N/A char buf2[256];
1N/A
1N/A /* As soon as we are prepending, we have matched the device... */
1N/A pc->device_matched = TRUE;
1N/A
1N/A if (pc->merge_type == MERGE_TYPE_STRLIST) {
1N/A hal_device_property_strlist_prepend (pc->device, pc->merge_key, pc->cdata_buf);
1N/A } else {
1N/A const char *existing_string;
1N/A
1N/A switch (pc->merge_type) {
1N/A case MERGE_TYPE_STRING:
1N/A strncpy (buf, pc->cdata_buf, sizeof (buf));
1N/A break;
1N/A
1N/A case MERGE_TYPE_COPY_PROPERTY:
1N/A hal_device_property_get_as_string (pc->device, pc->cdata_buf, buf, sizeof (buf));
1N/A break;
1N/A
1N/A default:
1N/A HAL_ERROR (("Unknown merge_type=%d='%c'", pc->merge_type, pc->merge_type));
1N/A break;
1N/A }
1N/A
1N/A existing_string = hal_device_property_get_string (pc->device, pc->merge_key);
1N/A if (existing_string != NULL) {
1N/A strncpy (buf2, buf, sizeof (buf2));
1N/A strncat (buf2, existing_string, sizeof (buf2) - strlen(buf2));
1N/A } else {
1N/A strncpy (buf2, buf, sizeof (buf2));
1N/A }
1N/A hal_device_property_set_string (pc->device, pc->merge_key, buf2);
1N/A }
1N/A } else if (pc->curelem == CURELEM_REMOVE && pc->match_ok) {
1N/A
1N/A if (pc->merge_type == MERGE_TYPE_STRLIST) {
1N/A /* covers <remove key="foobar" type="strlist">blah</remove> */
1N/A hal_device_property_strlist_remove (pc->device, pc->merge_key, pc->cdata_buf);
1N/A } else {
1N/A /* only allow <remove key="foobar"/>, not <remove key="foobar">blah</remove> */
1N/A if (strlen (pc->cdata_buf) == 0) {
1N/A hal_device_property_remove (pc->device, pc->merge_key);
1N/A }
1N/A }
1N/A } else if (pc->merge_type == MERGE_TYPE_SPAWN) {
1N/A HalDevice *spawned;
1N/A
1N/A spawned = hal_device_store_find (hald_get_gdl (), pc->merge_key);
1N/A if (spawned == NULL)
1N/A spawned = hal_device_store_find (hald_get_tdl (), pc->merge_key);
1N/A
1N/A if (spawned == NULL) {
1N/A HAL_INFO (("Spawning new device object '%s' caused by <spawn> on udi '%s'",
1N/A pc->merge_key, pc->device->udi));
1N/A
1N/A spawned = hal_device_new ();
1N/A hal_device_property_set_string (spawned, "info.bus", "unknown");
1N/A hal_device_property_set_string (spawned, "info.udi", pc->merge_key);
1N/A hal_device_property_set_string (spawned, "info.parent", pc->device->udi);
1N/A hal_device_set_udi (spawned, pc->merge_key);
1N/A
1N/A hal_device_store_add (hald_get_tdl (), spawned);
1N/A
1N/A di_search_and_merge (spawned, DEVICE_INFO_TYPE_INFORMATION);
1N/A di_search_and_merge (spawned, DEVICE_INFO_TYPE_POLICY);
1N/A
1N/A hal_util_callout_device_add (spawned, spawned_device_callouts_add_done, NULL, NULL);
1N/A }
1N/A
1N/A } else if (pc->curelem == CURELEM_CLEAR && pc->match_ok) {
1N/A if (pc->merge_type == MERGE_TYPE_CLEAR) {
1N/A hal_device_property_strlist_clear (pc->device, pc->merge_key);
1N/A }
1N/A }
1N/A
1N/A
1N/A pc->cdata_buf_len = 0;
1N/A pc->depth--;
1N/A
1N/A /* maintain curelem */
1N/A pc->curelem = pc->curelem_stack[pc->depth];
1N/A
1N/A /* maintain pc->match_ok */
1N/A if (pc->depth <= pc->match_depth_first_fail)
1N/A pc->match_ok = TRUE;
1N/A}
1N/A
1N/A/** Called when there is CDATA
1N/A *
1N/A * @param pc Parsing context
1N/A * @param s Pointer to data
1N/A * @param len Length of data
1N/A */
1N/Astatic void
1N/Acdata (ParsingContext * pc, const char *s, int len)
1N/A{
1N/A int bytes_left;
1N/A int bytes_to_copy;
1N/A
1N/A if (pc->aborted)
1N/A return;
1N/A
1N/A bytes_left = CDATA_BUF_SIZE - pc->cdata_buf_len;
1N/A if (len > bytes_left) {
1N/A HAL_ERROR (("CDATA in element larger than %d",
1N/A CDATA_BUF_SIZE));
1N/A }
1N/A
1N/A bytes_to_copy = len;
1N/A if (bytes_to_copy > bytes_left)
1N/A bytes_to_copy = bytes_left;
1N/A
1N/A if (bytes_to_copy > 0)
1N/A memcpy (pc->cdata_buf + pc->cdata_buf_len, s,
1N/A bytes_to_copy);
1N/A
1N/A pc->cdata_buf_len += bytes_to_copy;
1N/A}
1N/A
1N/A
1N/A/** Process a device information info file.
1N/A *
1N/A * @param dir Directory file resides in
1N/A * @param filename File name
1N/A * @param device Device to match on
1N/A * @return #TRUE if file matched device and information
1N/A * was merged
1N/A */
1N/Astatic dbus_bool_t
1N/Aprocess_fdi_file (const char *dir, const char *filename,
1N/A HalDevice * device)
1N/A{
1N/A int rc;
1N/A char buf[512];
1N/A FILE *file;
1N/A int filesize;
1N/A size_t read;
1N/A char *filebuf;
1N/A dbus_bool_t device_matched;
1N/A XML_Parser parser;
1N/A ParsingContext *parsing_context;
1N/A
1N/A file = NULL;
1N/A filebuf = NULL;
1N/A parser = NULL;
1N/A parsing_context = NULL;
1N/A
1N/A device_matched = FALSE;
1N/A
1N/A snprintf (buf, sizeof (buf), "%s/%s", dir, filename);
1N/A
1N/A /*HAL_INFO(("analyzing file %s", buf));*/
1N/A
1N/A /* open file and read it into a buffer; it's a small file... */
1N/A file = fopen (buf, "r");
1N/A if (file == NULL) {
1N/A HAL_ERROR (("Could not open file %s", buf));
1N/A goto out;
1N/A }
1N/A
1N/A fseek (file, 0L, SEEK_END);
1N/A filesize = (int) ftell (file);
1N/A rewind (file);
1N/A filebuf = (char *) malloc (filesize);
1N/A if (filebuf == NULL) {
1N/A HAL_ERROR (("Could not allocate %d bytes for file %s", filesize, buf));
1N/A goto out;
1N/A }
1N/A read = fread (filebuf, sizeof (char), filesize, file);
1N/A
1N/A /* initialize parsing context */
1N/A parsing_context =
1N/A (ParsingContext *) malloc (sizeof (ParsingContext));
1N/A if (parsing_context == NULL) {
1N/A HAL_ERROR (("Could not allocate parsing context"));
1N/A goto out;
1N/A }
1N/A
1N/A /* TODO: reuse parser
1N/A */
1N/A parser = XML_ParserCreate (NULL);
1N/A if (parser == NULL) {
1N/A HAL_ERROR (("Could not allocate XML parser"));
1N/A goto out;
1N/A }
1N/A
1N/A parsing_context->depth = 0;
1N/A parsing_context->device_matched = FALSE;
1N/A parsing_context->match_ok = TRUE;
1N/A parsing_context->curelem = CURELEM_UNKNOWN;
1N/A parsing_context->aborted = FALSE;
1N/A parsing_context->file = buf;
1N/A parsing_context->parser = parser;
1N/A parsing_context->device = device;
1N/A parsing_context->match_depth_first_fail = -1;
1N/A
1N/A XML_SetElementHandler (parser,
1N/A (XML_StartElementHandler) start,
1N/A (XML_EndElementHandler) end);
1N/A XML_SetCharacterDataHandler (parser,
1N/A (XML_CharacterDataHandler) cdata);
1N/A XML_SetUserData (parser, parsing_context);
1N/A
1N/A rc = XML_Parse (parser, filebuf, filesize, 1);
1N/A /*printf("XML_Parse rc=%d\r\n", rc); */
1N/A
1N/A if (rc == 0) {
1N/A /* error parsing document */
1N/A HAL_ERROR (("Error parsing XML document %s at line %d, "
1N/A "column %d : %s",
1N/A buf,
1N/A XML_GetCurrentLineNumber (parser),
1N/A XML_GetCurrentColumnNumber (parser),
1N/A XML_ErrorString (XML_GetErrorCode (parser))));
1N/A device_matched = FALSE;
1N/A } else {
1N/A /* document parsed ok */
1N/A device_matched = parsing_context->device_matched;
1N/A }
1N/A
1N/Aout:
1N/A if (filebuf != NULL)
1N/A free (filebuf);
1N/A if (file != NULL)
1N/A fclose (file);
1N/A if (parser != NULL)
1N/A XML_ParserFree (parser);
1N/A if (parsing_context != NULL)
1N/A free (parsing_context);
1N/A
1N/A return device_matched;
1N/A}
1N/A
1N/A
1N/A
1N/Astatic int
1N/A#ifdef __GLIBC__
1N/Amy_alphasort(const void *a, const void *b)
1N/A#else
1N/Amy_alphasort(const struct dirent **a, const struct dirent **b)
1N/A#endif
1N/A{
1N/A return -alphasort (a, b);
1N/A}
1N/A
1N/A
1N/A/** Scan all directories and subdirectories in the given directory and
1N/A * process each *.fdi file
1N/A *
1N/A * @param d Device to merge information into
1N/A * @return #TRUE if information was merged
1N/A */
1N/Astatic dbus_bool_t
1N/Ascan_fdi_files (const char *dir, HalDevice * d)
1N/A{
1N/A int i;
1N/A int num_entries;
1N/A dbus_bool_t found_fdi_file;
1N/A struct dirent **name_list;
1N/A
1N/A found_fdi_file = 0;
1N/A
1N/A /*HAL_INFO(("scan_fdi_files: Processing dir '%s'", dir));*/
1N/A
1N/A num_entries = scandir (dir, &name_list, 0, my_alphasort);
1N/A if (num_entries == -1) {
1N/A return FALSE;
1N/A }
1N/A
1N/A for (i = num_entries - 1; i >= 0; i--) {
1N/A int len;
1N/A char *filename;
1N/A gchar *full_path;
1N/A
1N/A filename = name_list[i]->d_name;
1N/A len = strlen (filename);
1N/A
1N/A full_path = g_strdup_printf ("%s/%s", dir, filename);
1N/A /*HAL_INFO (("Full path = %s", full_path));*/
1N/A
1N/A /* Mmm, d_type can be DT_UNKNOWN, use glib to determine
1N/A * the type
1N/A */
1N/A if (g_file_test (full_path, (G_FILE_TEST_IS_REGULAR))) {
1N/A /* regular file */
1N/A
1N/A if (len >= 5 &&
1N/A filename[len - 4] == '.' &&
1N/A filename[len - 3] == 'f' &&
1N/A filename[len - 2] == 'd' &&
1N/A filename[len - 1] == 'i') {
1N/A /*HAL_INFO (("scan_fdi_files: Processing file '%s'", filename));*/
1N/A found_fdi_file = process_fdi_file (dir, filename, d);
1N/A if (found_fdi_file) {
1N/A HAL_INFO (("*** Matched file %s/%s", dir, filename));
1N/A /*break;*/
1N/A }
1N/A }
1N/A
1N/A } else if (g_file_test (full_path, (G_FILE_TEST_IS_DIR))
1N/A && strcmp (filename, ".") != 0
1N/A && strcmp (filename, "..") != 0) {
1N/A int num_bytes;
1N/A char *dirname;
1N/A
1N/A /* Directory; do the recursion thingy but not
1N/A * for . and ..
1N/A */
1N/A
1N/A num_bytes = len + strlen (dir) + 1 + 1;
1N/A dirname = (char *) malloc (num_bytes);
1N/A if (dirname == NULL) {
1N/A HAL_ERROR (("couldn't allocated %d bytes",
1N/A num_bytes));
1N/A break;
1N/A }
1N/A
1N/A snprintf (dirname, num_bytes, "%s/%s", dir,
1N/A filename);
1N/A found_fdi_file = scan_fdi_files (dirname, d);
1N/A free (dirname);
1N/A /*
1N/A if (found_fdi_file)
1N/A break;
1N/A */
1N/A }
1N/A
1N/A g_free (full_path);
1N/A
1N/A free (name_list[i]);
1N/A }
1N/A
1N/A for (; i >= 0; i--) {
1N/A free (name_list[i]);
1N/A }
1N/A
1N/A free (name_list);
1N/A
1N/A return found_fdi_file;
1N/A}
1N/A
1N/A/** Search the device info file repository for a .fdi file to merge
1N/A * more information into the device object.
1N/A *
1N/A * @param d Device to merge information into
1N/A * @return #TRUE if information was merged
1N/A */
1N/Adbus_bool_t
1N/Adi_search_and_merge (HalDevice *d, DeviceInfoType type)
1N/A{
1N/A static gboolean have_checked_hal_fdi_source = FALSE;
1N/A static char *hal_fdi_source_preprobe = NULL;
1N/A static char *hal_fdi_source_information = NULL;
1N/A static char *hal_fdi_source_policy = NULL;
1N/A dbus_bool_t ret;
1N/A char *s1;
1N/A char *s2;
1N/A
1N/A ret = FALSE;
1N/A
1N/A if (!have_checked_hal_fdi_source) {
1N/A hal_fdi_source_preprobe = getenv ("HAL_FDI_SOURCE_PREPROBE");
1N/A hal_fdi_source_information = getenv ("HAL_FDI_SOURCE_INFORMATION");
1N/A hal_fdi_source_policy = getenv ("HAL_FDI_SOURCE_POLICY");
1N/A have_checked_hal_fdi_source = TRUE;
1N/A }
1N/A
1N/A switch (type) {
1N/A case DEVICE_INFO_TYPE_PREPROBE:
1N/A if (hal_fdi_source_preprobe != NULL) {
1N/A s1 = hal_fdi_source_preprobe;
1N/A s2 = NULL;
1N/A } else {
1N/A s1 = PACKAGE_DATA_DIR "/hal/fdi/preprobe";
1N/A s2 = PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe";
1N/A }
1N/A break;
1N/A
1N/A case DEVICE_INFO_TYPE_INFORMATION:
1N/A if (hal_fdi_source_information != NULL) {
1N/A s1 = hal_fdi_source_information;
1N/A s2 = NULL;
1N/A } else {
1N/A s1 = PACKAGE_DATA_DIR "/hal/fdi/information";
1N/A s2 = PACKAGE_SYSCONF_DIR "/hal/fdi/information";
1N/A }
1N/A break;
1N/A
1N/A case DEVICE_INFO_TYPE_POLICY:
1N/A if (hal_fdi_source_policy != NULL) {
1N/A s1 = hal_fdi_source_policy;
1N/A s2 = NULL;
1N/A } else {
1N/A s1 = PACKAGE_DATA_DIR "/hal/fdi/policy";
1N/A s2 = PACKAGE_SYSCONF_DIR "/hal/fdi/policy";
1N/A }
1N/A break;
1N/A
1N/A default:
1N/A s1 = NULL;
1N/A s2 = NULL;
1N/A HAL_ERROR (("Bogus device information type %d", type));
1N/A break;
1N/A }
1N/A
1N/A if (s1 != NULL)
1N/A ret = scan_fdi_files (s1, d) || ret;
1N/A if (s2 != NULL)
1N/A ret = scan_fdi_files (s2, d) || ret;
1N/A
1N/A return ret;
1N/A}
1N/A
1N/A/** @} */