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