|  | /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ | 
|  | /* config-loader-expat.c  expat XML loader | 
|  | * | 
|  | * Copyright (C) 2003 Red Hat, Inc. | 
|  | * | 
|  | * Licensed under the Academic Free License version 2.1 | 
|  | * | 
|  | * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <config.h> | 
|  | #include "config-parser.h" | 
|  | #include <dbus/dbus-internals.h> | 
|  | #include <expat.h> | 
|  |  | 
|  | static XML_Memory_Handling_Suite memsuite; | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | BusConfigParser *parser; | 
|  | const char *filename; | 
|  | DBusString content; | 
|  | DBusError *error; | 
|  | dbus_bool_t failed; | 
|  | } ExpatParseContext; | 
|  |  | 
|  | static dbus_bool_t | 
|  | process_content (ExpatParseContext *context) | 
|  | { | 
|  | if (context->failed) | 
|  | return FALSE; | 
|  |  | 
|  | if (_dbus_string_get_length (&context->content) > 0) | 
|  | { | 
|  | if (!bus_config_parser_content (context->parser, | 
|  | &context->content, | 
|  | context->error)) | 
|  | { | 
|  | context->failed = TRUE; | 
|  | return FALSE; | 
|  | } | 
|  | _dbus_string_set_length (&context->content, 0); | 
|  | } | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static void | 
|  | expat_StartElementHandler (void            *userData, | 
|  | const XML_Char  *name, | 
|  | const XML_Char **atts) | 
|  | { | 
|  | ExpatParseContext *context = userData; | 
|  | int i; | 
|  | char **names; | 
|  | char **values; | 
|  |  | 
|  | /* Expat seems to suck and can't abort the parse if we | 
|  | * throw an error. Expat 2.0 is supposed to fix this. | 
|  | */ | 
|  | if (context->failed) | 
|  | return; | 
|  |  | 
|  | if (!process_content (context)) | 
|  | return; | 
|  |  | 
|  | /* "atts" is key, value, key, value, NULL */ | 
|  | for (i = 0; atts[i] != NULL; ++i) | 
|  | ; /* nothing */ | 
|  |  | 
|  | _dbus_assert (i % 2 == 0); | 
|  | names = dbus_new0 (char *, i / 2 + 1); | 
|  | values = dbus_new0 (char *, i / 2 + 1); | 
|  |  | 
|  | if (names == NULL || values == NULL) | 
|  | { | 
|  | dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL); | 
|  | context->failed = TRUE; | 
|  | dbus_free (names); | 
|  | dbus_free (values); | 
|  | return; | 
|  | } | 
|  |  | 
|  | i = 0; | 
|  | while (atts[i] != NULL) | 
|  | { | 
|  | _dbus_assert (i % 2 == 0); | 
|  | names [i / 2] = (char*) atts[i]; | 
|  | values[i / 2] = (char*) atts[i+1]; | 
|  |  | 
|  | i += 2; | 
|  | } | 
|  |  | 
|  | if (!bus_config_parser_start_element (context->parser, | 
|  | name, | 
|  | (const char **) names, | 
|  | (const char **) values, | 
|  | context->error)) | 
|  | { | 
|  | dbus_free (names); | 
|  | dbus_free (values); | 
|  | context->failed = TRUE; | 
|  | return; | 
|  | } | 
|  |  | 
|  | dbus_free (names); | 
|  | dbus_free (values); | 
|  | } | 
|  |  | 
|  | static void | 
|  | expat_EndElementHandler (void           *userData, | 
|  | const XML_Char *name) | 
|  | { | 
|  | ExpatParseContext *context = userData; | 
|  |  | 
|  | if (!process_content (context)) | 
|  | return; | 
|  |  | 
|  | if (!bus_config_parser_end_element (context->parser, | 
|  | name, | 
|  | context->error)) | 
|  | { | 
|  | context->failed = TRUE; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* s is not 0 terminated. */ | 
|  | static void | 
|  | expat_CharacterDataHandler (void           *userData, | 
|  | const XML_Char *s, | 
|  | int             len) | 
|  | { | 
|  | ExpatParseContext *context = userData; | 
|  | if (context->failed) | 
|  | return; | 
|  |  | 
|  | if (!_dbus_string_append_len (&context->content, | 
|  | s, len)) | 
|  | { | 
|  | dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL); | 
|  | context->failed = TRUE; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | BusConfigParser* | 
|  | bus_config_load (const DBusString      *file, | 
|  | dbus_bool_t            is_toplevel, | 
|  | const BusConfigParser *parent, | 
|  | DBusError             *error) | 
|  | { | 
|  | XML_Parser expat; | 
|  | const char *filename; | 
|  | BusConfigParser *parser; | 
|  | ExpatParseContext context; | 
|  | DBusString dirname; | 
|  |  | 
|  | _DBUS_ASSERT_ERROR_IS_CLEAR (error); | 
|  |  | 
|  | parser = NULL; | 
|  | expat = NULL; | 
|  | context.error = error; | 
|  | context.failed = FALSE; | 
|  |  | 
|  | filename = _dbus_string_get_const_data (file); | 
|  |  | 
|  | if (!_dbus_string_init (&context.content)) | 
|  | { | 
|  | dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | if (!_dbus_string_init (&dirname)) | 
|  | { | 
|  | dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); | 
|  | _dbus_string_free (&context.content); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | memsuite.malloc_fcn = dbus_malloc; | 
|  | memsuite.realloc_fcn = dbus_realloc; | 
|  | memsuite.free_fcn = dbus_free; | 
|  |  | 
|  | expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL); | 
|  | if (expat == NULL) | 
|  | { | 
|  | dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | if (!_dbus_string_get_dirname (file, &dirname)) | 
|  | { | 
|  | dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | parser = bus_config_parser_new (&dirname, is_toplevel, parent); | 
|  | if (parser == NULL) | 
|  | { | 
|  | dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); | 
|  | goto failed; | 
|  | } | 
|  | context.parser = parser; | 
|  |  | 
|  | XML_SetUserData (expat, &context); | 
|  | XML_SetElementHandler (expat, | 
|  | expat_StartElementHandler, | 
|  | expat_EndElementHandler); | 
|  | XML_SetCharacterDataHandler (expat, | 
|  | expat_CharacterDataHandler); | 
|  |  | 
|  | { | 
|  | DBusString data; | 
|  | const char *data_str; | 
|  |  | 
|  | if (!_dbus_string_init (&data)) | 
|  | { | 
|  | dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | if (!_dbus_file_get_contents (&data, file, error)) | 
|  | { | 
|  | _dbus_string_free (&data); | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | data_str = _dbus_string_get_const_data (&data); | 
|  |  | 
|  | if (!XML_Parse (expat, data_str, _dbus_string_get_length (&data), TRUE)) | 
|  | { | 
|  | if (context.error != NULL && | 
|  | !dbus_error_is_set (context.error)) | 
|  | { | 
|  | enum XML_Error e; | 
|  |  | 
|  | e = XML_GetErrorCode (expat); | 
|  | if (e == XML_ERROR_NO_MEMORY) | 
|  | dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); | 
|  | else | 
|  | dbus_set_error (error, DBUS_ERROR_FAILED, | 
|  | "Error in file %s, line %d, column %d: %s\n", | 
|  | filename, | 
|  | XML_GetCurrentLineNumber (expat), | 
|  | XML_GetCurrentColumnNumber (expat), | 
|  | XML_ErrorString (e)); | 
|  | } | 
|  |  | 
|  | _dbus_string_free (&data); | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | _dbus_string_free (&data); | 
|  |  | 
|  | if (context.failed) | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | if (!bus_config_parser_finished (parser, error)) | 
|  | goto failed; | 
|  |  | 
|  | _dbus_string_free (&dirname); | 
|  | _dbus_string_free (&context.content); | 
|  | XML_ParserFree (expat); | 
|  |  | 
|  | _DBUS_ASSERT_ERROR_IS_CLEAR (error); | 
|  | return parser; | 
|  |  | 
|  | failed: | 
|  | _DBUS_ASSERT_ERROR_IS_SET (error); | 
|  |  | 
|  | _dbus_string_free (&dirname); | 
|  | _dbus_string_free (&context.content); | 
|  | if (expat) | 
|  | XML_ParserFree (expat); | 
|  | if (parser) | 
|  | bus_config_parser_unref (parser); | 
|  | return NULL; | 
|  | } |