Logo Search packages:      
Sourcecode: hal version File versions

device_info.c

/***************************************************************************
 * CVSID: $Id: device_info.c,v 1.16.2.1 2004/11/22 21:53:50 david Exp $
 *
 * device_store.c : Search for .fdi files and merge on match
 *
 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <expat.h>
#include <assert.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <math.h>

#include "hald.h"
#include "logger.h"
#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 */
00054 #define MAX_DEPTH 32

/** Maximum amount of CDATA */
00057 #define CDATA_BUF_SIZE  1024

/** Max length of property key */
00060 #define MAX_KEY_SIZE 128

/** Possible elements the parser can process */
enum {
      /** Not processing a known tag */
00065       CURELEM_UNKNOWN = -1,

      /** Processing a deviceinfo element */
00068       CURELEM_DEVICE_INFO = 0,

      /** Processing a device element */
00071       CURELEM_DEVICE = 1,

      /** Processing a match element */
00074       CURELEM_MATCH = 2,

      /** Processing a merge element */
00077       CURELEM_MERGE = 3,

      /** Processing an append element */
00080       CURELEM_APPEND = 4,
};

/** What and how to merge */
enum {
      MERGE_TYPE_UNKNOWN,
      MERGE_TYPE_STRING,
      MERGE_TYPE_BOOLEAN,
      MERGE_TYPE_INT32,
      MERGE_TYPE_UINT64,
      MERGE_TYPE_DOUBLE,
      MERGE_TYPE_COPY_PROPERTY
};

/** Parsing Context
 */
00096 typedef struct {
      /** Name of file being parsed */
00098       char *file;

      /** Parser object */
00101       XML_Parser parser;

      /** Device we are trying to match*/
00104       HalDevice *device;

      /** Buffer to put CDATA in */
00107       char cdata_buf[CDATA_BUF_SIZE];

      /** Current length of CDATA buffer */
00110       int cdata_buf_len;
      
      /** Current depth we are parsing at */
00113       int depth;

      /** Element currently being processed */
00116       int curelem;

      /** Stack of elements being processed */
00119       int curelem_stack[MAX_DEPTH];

      /** #TRUE if parsing of document have been aborted */
00122       dbus_bool_t aborted;


      /** Depth of match-fail */
00126       int match_depth_first_fail;

      /** #TRUE if all matches on prior depths have been OK */
00129       dbus_bool_t match_ok;



      /** When merging, the key to store the value in */
00134       char merge_key[MAX_KEY_SIZE];

      /** Type to merge*/
00137       int merge_type;

      /** Set to #TRUE if a device is matched */
00140       dbus_bool_t device_matched;

} ParsingContext;

/** Resolve a udi-property path as used in .fdi files. 
 *
 *  Examples of udi-property paths:
 *
 *   info.udi
 *   /org/freedesktop/Hal/devices/computer:kernel.name
 *   @block.storage_device:storage.bus
 *   @block.storage_device:@storage.physical_device:ide.channel
 *
 *  @param  source_udi          UDI of source device
 *  @param  path                The given path
 *  @param  udi_result          Where to store the resulting UDI
 *  @param  udi_result_size     Size of UDI string
 *  @param  prop_result         Where to store the resulting property name
 *  @param  prop_result_size    Size of property string
 *  @return                     TRUE if and only if the path resolved.
 */
gboolean
00162 resolve_udiprop_path (const char *path, const char *source_udi,
                  char *udi_result, size_t udi_result_size, 
                  char *prop_result, size_t prop_result_size)
{
      int i;
      gchar **tokens = NULL;
      gboolean rc;

      rc = FALSE;

      /*HAL_INFO (("Looking at '%s' for udi='%s'", path, source_udi));*/

      /* Split up path into ':' tokens */
      tokens = g_strsplit (path, ":", 64);

      /* Detect trivial property access, e.g. path='foo.bar'   */
      if (tokens == NULL || tokens[0] == NULL || tokens[1] == NULL) {
            strncpy (udi_result, source_udi, udi_result_size);
            strncpy (prop_result, path, prop_result_size);
            rc = TRUE;
            goto out;
      }

      
      /* Start with the source udi */
      strncpy (udi_result, source_udi, udi_result_size);

      for (i = 0; tokens[i] != NULL; i++) {
            HalDevice *d;
            gchar *curtoken;

            /*HAL_INFO (("tokens[%d] = '%s'", i, tokens[i]));*/

            d = hal_device_store_find (hald_get_gdl (), udi_result);
            if (d == NULL)
                  d = hal_device_store_find (hald_get_tdl (), udi_result);
            if (d == NULL)
                  goto out;

            curtoken = tokens[i];

            /* process all but the last tokens as UDI paths */
            if (tokens[i+1] == NULL) {
                  strncpy (prop_result, curtoken, prop_result_size);
                  rc = TRUE;
                  goto out;
            }


            /* Check for indirection */
            if (curtoken[0] == '@') {
                  const char *udiprop;
                  const char *newudi;

                  udiprop = curtoken + 1;

                  newudi = hal_device_property_get_string (d, udiprop);
                  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;
}

/* Compare the value of a property on a hal device object against a string value
 * and return the result. Note that this works for several types, e.g. both strings
 * and integers - in the latter case the given right side string will be interpreted
 * as a number.
 *
 * The comparison might not make sense if you are comparing a property which is an integer
 * against a string in which case this function returns FALSE. Also, if the property doesn't
 * exist this function will also return FALSE.
 *
 * @param  d                    hal device object
 * @param  key                  Key of the property to compare
 * @param  right_side           Value to compare against
 * @param  result               Pointer to where to store result
 * @return                      TRUE if, and only if, the comparison could take place
 */
static gboolean
match_compare_property (HalDevice *d, const char *key, const char *right_side, dbus_int64_t *result)
{
      gboolean rc;
      int proptype;

      rc = FALSE;

      if (!hal_device_has_property (d, key))
            goto out;

      proptype = hal_device_property_get_type (d, key);
      switch (proptype) {
      case DBUS_TYPE_STRING:
            *result = (dbus_int64_t) strcmp (hal_device_property_get_string (d, key), right_side);
            rc = TRUE;
            break;

      case DBUS_TYPE_INT32:
            *result = ((dbus_int64_t) hal_device_property_get_int (d, key)) - strtoll (right_side, NULL, 0);
            rc = TRUE;
            break;

      case DBUS_TYPE_UINT64:
            *result = ((dbus_int64_t) hal_device_property_get_uint64 (d, key)) - ((dbus_int64_t) strtoll (right_side, NULL, 0));
            rc = TRUE;
            break;

      case DBUS_TYPE_DOUBLE:
            *result = (dbus_int64_t) ceil (hal_device_property_get_double (d, key) - atof (right_side));
            rc = TRUE;
            break;

      default:
            /* explicit fallthrough */
      case DBUS_TYPE_BOOLEAN:
            /* explicit blank since this doesn't make sense */
            break;
      }

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
00312 handle_match (ParsingContext * pc, const char **attr)
{
      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];

      /* 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,
                           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));
            return FALSE;
      }

      d = hal_device_store_find (hald_get_gdl (), udi_to_check);
      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 "
              "equals '%s'", key, value)); */

            if (hal_device_property_get_type (d, prop_to_check) != DBUS_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));*/

            if (hal_device_property_get_type (d, prop_to_check) != DBUS_TYPE_INT32)
                  return FALSE;

            if (hal_device_property_get_int (d, prop_to_check) != value) {
                  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));*/

            if (hal_device_property_get_type (d, prop_to_check) != DBUS_TYPE_UINT64)
                  return FALSE;

            if (hal_device_property_get_uint64 (d, prop_to_check) != value) {
                  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;
            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) != 
                DBUS_TYPE_BOOLEAN)
                  return FALSE;

            if (hal_device_property_get_bool (d, prop_to_check) != value)
                  return FALSE;

            /*HAL_INFO (("*** bool match for key %s", key));*/
            return TRUE;
      } else if (strcmp (attr[2], "exists") == 0) {
            dbus_bool_t should_exist = TRUE;

            if (strcmp (attr[3], "false") == 0)
                  should_exist = FALSE;

            if (should_exist) {
                  if (hal_device_has_property (d, prop_to_check))
                        return TRUE;
                  else
                        return FALSE;
            } else {
                  if (hal_device_has_property (d, prop_to_check))
                        return FALSE;
                  else
                        return TRUE;
            }
      } else if (strcmp (attr[2], "empty") == 0) {
            dbus_bool_t is_empty = TRUE;
            dbus_bool_t should_be_empty = TRUE;

            if (strcmp (attr[3], "false") == 0)
                  should_be_empty = FALSE;

            if (hal_device_property_get_type (d, prop_to_check) != DBUS_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;

            if (should_be_empty) {
                  if (is_empty)
                        return TRUE;
                  else
                        return FALSE;
            } else {
                  if (is_empty)
                        return FALSE;
                  else
                        return TRUE;
            }
      } else if (strcmp (attr[2], "is_ascii") == 0) {
            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)
                  should_be_ascii = FALSE;

            if (hal_device_property_get_type (d, prop_to_check) != DBUS_TYPE_STRING)
                  return FALSE;

            is_ascii = TRUE;

            str = hal_device_property_get_string (d, prop_to_check);
            for (i = 0; str[i] != '\0'; i++) {
                  if (((unsigned char) str[i]) > 0x7f)
                        is_ascii = FALSE;
            }

            if (should_be_ascii) {
                  if (is_ascii)
                        return TRUE;
                  else
                        return FALSE;
            } else {
                  if (is_ascii)
                        return FALSE;
                  else
                        return TRUE;
            }
      } else if (strcmp (attr[2], "is_absolute_path") == 0) {
            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)
                  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) != DBUS_TYPE_STRING)
                  return FALSE;

            if (hal_device_has_property (d, prop_to_check)) {
                  path = hal_device_property_get_string (d, prop_to_check);
                  if (g_path_is_absolute (path))
                        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;
                  else
                        return FALSE;
            } else {
                  if (is_absolute_path)
                        return FALSE;
                  else
                        return TRUE;
            }
      } else if (strcmp (attr[2], "contains") == 0) {
            const char *needle;
            dbus_bool_t contains = FALSE;

            needle = attr[3];

            if (hal_device_property_get_type (d, prop_to_check) != DBUS_TYPE_STRING)
                  return FALSE;

            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)) {
                        contains = TRUE;
                  }

            }
            return contains;
      } else if (strcmp (attr[2], "contains_ncase") == 0) {
            const char *needle;
            dbus_bool_t contains_ncase = FALSE;

            needle = attr[3];

            if (hal_device_property_get_type (d, prop_to_check) != DBUS_TYPE_STRING)
                  return FALSE;

            if (hal_device_has_property (d, prop_to_check)) {
                  char *needle_lowercase;
                  char *haystack_lowercase;

                  needle_lowercase   = g_utf8_strdown (needle, -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)) {
                        contains_ncase = TRUE;
                  }

                  g_free (needle_lowercase);
                  g_free (haystack_lowercase);
            }
            return contains_ncase;
      } else if (strcmp (attr[2], "compare_lt") == 0) {
            dbus_int64_t result;
            if (!match_compare_property (d, prop_to_check, attr[3], &result)) {
                  return FALSE;
            } else {
                  return result < 0;
            }
      } else if (strcmp (attr[2], "compare_le") == 0) {
            dbus_int64_t result;
            if (!match_compare_property (d, prop_to_check, attr[3], &result))
                  return FALSE;
            else
                  return result <= 0;
      } else if (strcmp (attr[2], "compare_gt") == 0) {
            dbus_int64_t result;
            if (!match_compare_property (d, prop_to_check, attr[3], &result))
                  return FALSE;
            else
                  return result > 0;
      } else if (strcmp (attr[2], "compare_ge") == 0) {
            dbus_int64_t result;
            if (!match_compare_property (d, prop_to_check, attr[3], &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
00614 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], "copy_property") == 0) {
            /* copy another property */
            pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
            return;
      }

      return;
}

/** Called when the append element begins.
 *
 *  @param  pc                  Parsing context
 *  @param  attr                Attribute key/value pairs
 */
static void
00670 handle_append (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], "copy_property") == 0) {
            /* copy another property */
            pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
            return;
      }

      return;
}

/** Abort parsing of document
 *
 *  @param  pc                  Parsing context
 */
static void
00708 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
00722 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 (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;

}

/** Called by expat when an element ends.
 *
 *  @param  pc                  Parsing context
 *  @param  el                  Element name
 */
static void
00841 end (ParsingContext * pc, const char *el)
{
      if (pc->aborted)
            return;

      pc->cdata_buf[pc->cdata_buf_len] = '\0';

/*    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);
                  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;

                        /* match integer property */
                        value = strtoull (pc->cdata_buf, NULL, 0);

                        /** @todo FIXME: Check error condition */

                        hal_device_property_set_uint64 (pc->device,
                                         pc->merge_key, value);
                        break;
                  }

            case MERGE_TYPE_BOOLEAN:
                  hal_device_property_set_bool (pc->device, pc->merge_key,
                                    (strcmp (pc->cdata_buf,
                                           "true") == 0) 
                                    ? TRUE : FALSE);
                  break;

            case MERGE_TYPE_DOUBLE:
                  hal_device_property_set_double (pc->device, pc->merge_key,
                                    atof (pc->cdata_buf));
                  break;

            case MERGE_TYPE_COPY_PROPERTY:
            {
                  char udi_to_merge_from[256];
                  char prop_to_merge[256];

                  /* 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,
                                       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));
                  } else {
                        HalDevice *d;

                        d = hal_device_store_find (hald_get_gdl (), udi_to_merge_from);
                        if (d == NULL) {
                              d = hal_device_store_find (hald_get_tdl (), udi_to_merge_from);
                        }
                        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);
                        }
                  }
                  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) == DBUS_TYPE_STRING) {
            char buf[256];
            char buf2[256];

            /* As soon as we are appending, we have matched the device... */
            pc->device_matched = TRUE;

            switch (pc->merge_type) {
            case MERGE_TYPE_STRING:
                  strncpy (buf, pc->cdata_buf, sizeof (buf));
                  break;

            case MERGE_TYPE_COPY_PROPERTY:
                  hal_device_property_get_as_string (pc->device, pc->cdata_buf, buf, sizeof (buf));
                  break;

            default:
                  HAL_ERROR (("Unknown merge_type=%d='%c'",
                            pc->merge_type, pc->merge_type));
                  break;
            }

            strncpy (buf2, hal_device_property_get_string (pc->device, pc->merge_key), sizeof (buf2));
            strncat (buf2, buf, sizeof (buf2) - strlen(buf2));
            hal_device_property_set_string (pc->device, pc->merge_key, buf2);
      }


      pc->cdata_buf_len = 0;
      pc->depth--;

      /* maintain curelem */
      pc->curelem = pc->curelem_stack[pc->depth];

      /* maintain pc->match_ok */
      if (pc->depth <= pc->match_depth_first_fail)
            pc->match_ok = TRUE;
}

/** Called when there is CDATA 
 *
 *  @param  pc                  Parsing context
 *  @param  s                   Pointer to data
 *  @param  len                 Length of data
 */
static void
00982 cdata (ParsingContext * pc, const char *s, int len)
{
      int bytes_left;
      int bytes_to_copy;

      if (pc->aborted)
            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));
      }

      bytes_to_copy = len;
      if (bytes_to_copy > bytes_left)
            bytes_to_copy = bytes_left;

      if (bytes_to_copy > 0)
            memcpy (pc->cdata_buf + pc->cdata_buf_len, s,
                  bytes_to_copy);

      pc->cdata_buf_len += bytes_to_copy;
}


/** 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
01017 process_fdi_file (const char *dir, const char *filename,
              HalDevice * device)
{
      int rc;
      char buf[512];
      FILE *file;
      int filesize;
      char *filebuf;
      dbus_bool_t device_matched;
      XML_Parser parser;
      ParsingContext *parsing_context;

      snprintf (buf, 511, "%s/%s", dir, filename);

      /*HAL_INFO(("analysing file %s", buf)); */

      /* open file and read it into a buffer; it's a small file... */
      file = fopen (buf, "r");
      if (file == NULL) {
            perror ("fopen");
            return FALSE;
      }

      fseek (file, 0L, SEEK_END);
      filesize = (int) ftell (file);
      rewind (file);
      filebuf = (char *) malloc (filesize);
      if (filebuf == NULL) {
            perror ("malloc");
            fclose (file);
            return FALSE;
      }
      fread (filebuf, sizeof (char), filesize, file);


      /* ok, now parse the file (should probably reuse parser and say we are
       * not thread safe 
       */
      parser = XML_ParserCreate (NULL);

      /* initialize parsing context */
      parsing_context =
          (ParsingContext *) malloc (sizeof (ParsingContext));
      if (parsing_context == NULL) {
            perror ("malloc");
            return FALSE;
      }
      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;
      }

      free (filebuf);
      fclose (file);
      free (parsing_context);

      return device_matched;
}



static int 
my_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 int
01121 scan_fdi_files (const char *dir, HalDevice * d)
{
      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;
      }

      for (i = num_entries - 1; i >= 0; i--) {
            int len;
            char *filename;
            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) {
                  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));
                        break;
                  }

                  snprintf (dirname, num_bytes, "%s/%s", dir,
                          filename);
                  found_fdi_file = scan_fdi_files (dirname, d);
                  free (dirname);
                  /*
                  if (found_fdi_file)
                        break;
                  */
            }

            g_free (full_path);

            free (name_list[i]);
      }

      for (; i >= 0; i--) {
            free (name_list[i]);
      }

      free (name_list);

      return found_fdi_file;
}


/** 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
01218 di_search_and_merge (HalDevice *d)
{

      return scan_fdi_files (PACKAGE_DATA_DIR "/hal/fdi", d);
}

/** @} */

Generated by  Doxygen 1.6.0   Back to index