sl@0: /* -*- mode: C; c-file-style: "gnu" -*- */ sl@0: /* dbus-gloader-expat.c expat XML loader sl@0: * sl@0: * Copyright (C) 2003 Red Hat, Inc. sl@0: * sl@0: * Licensed under the Academic Free License version 2.1 sl@0: * sl@0: * This program is free software; you can redistribute it and/or modify sl@0: * it under the terms of the GNU General Public License as published by sl@0: * the Free Software Foundation; either version 2 of the License, or sl@0: * (at your option) any later version. sl@0: * sl@0: * This program is distributed in the hope that it will be useful, sl@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of sl@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the sl@0: * GNU General Public License for more details. sl@0: * sl@0: * You should have received a copy of the GNU General Public License sl@0: * along with this program; if not, write to the Free Software sl@0: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA sl@0: * sl@0: */ sl@0: sl@0: #include "dbus-gparser.h" sl@0: #include sl@0: #include sl@0: sl@0: static void* sl@0: expat_g_malloc (size_t sz) sl@0: { sl@0: return g_malloc (sz); sl@0: } sl@0: sl@0: static void* sl@0: expat_g_realloc (void *mem, size_t sz) sl@0: { sl@0: return g_realloc (mem, sz); sl@0: } sl@0: sl@0: static XML_Memory_Handling_Suite memsuite = sl@0: { sl@0: expat_g_malloc, sl@0: expat_g_realloc, sl@0: g_free sl@0: }; sl@0: sl@0: /** sl@0: * Context for Expat parser for introspection data. sl@0: */ sl@0: typedef struct sl@0: { sl@0: Parser *parser; /**< The parser for the introspection data */ sl@0: const char *filename; /**< The filename being loaded */ sl@0: GString *content; /**< The content of the current element */ sl@0: GError **error; /**< Error return location */ sl@0: gboolean failed; /**< True if parse has failed */ sl@0: } ExpatParseContext; sl@0: sl@0: static dbus_bool_t sl@0: process_content (ExpatParseContext *context) sl@0: { sl@0: if (context->failed) sl@0: return FALSE; sl@0: sl@0: if (context->content->len > 0) sl@0: { sl@0: if (!parser_content (context->parser, sl@0: context->content->str, sl@0: context->content->len, sl@0: context->error)) sl@0: { sl@0: context->failed = TRUE; sl@0: return FALSE; sl@0: } sl@0: g_string_set_size (context->content, 0); sl@0: } sl@0: sl@0: return TRUE; sl@0: } sl@0: sl@0: static void sl@0: expat_StartElementHandler (void *userData, sl@0: const XML_Char *name, sl@0: const XML_Char **atts) sl@0: { sl@0: ExpatParseContext *context = userData; sl@0: int i; sl@0: char **names; sl@0: char **values; sl@0: sl@0: /* Expat seems to suck and can't abort the parse if we sl@0: * throw an error. Expat 2.0 is supposed to fix this. sl@0: */ sl@0: if (context->failed) sl@0: return; sl@0: sl@0: if (!process_content (context)) sl@0: return; sl@0: sl@0: /* "atts" is key, value, key, value, NULL */ sl@0: for (i = 0; atts[i] != NULL; ++i) sl@0: ; /* nothing */ sl@0: sl@0: g_assert (i % 2 == 0); sl@0: names = g_new0 (char *, i / 2 + 1); sl@0: values = g_new0 (char *, i / 2 + 1); sl@0: sl@0: i = 0; sl@0: while (atts[i] != NULL) sl@0: { sl@0: g_assert (i % 2 == 0); sl@0: names [i / 2] = (char*) atts[i]; sl@0: values[i / 2] = (char*) atts[i+1]; sl@0: sl@0: i += 2; sl@0: } sl@0: sl@0: if (!parser_start_element (context->parser, sl@0: name, sl@0: (const char **) names, sl@0: (const char **) values, sl@0: context->error)) sl@0: { sl@0: g_free (names); sl@0: g_free (values); sl@0: context->failed = TRUE; sl@0: return; sl@0: } sl@0: sl@0: g_free (names); sl@0: g_free (values); sl@0: } sl@0: sl@0: static void sl@0: expat_EndElementHandler (void *userData, sl@0: const XML_Char *name) sl@0: { sl@0: ExpatParseContext *context = userData; sl@0: sl@0: if (!process_content (context)) sl@0: return; sl@0: sl@0: if (!parser_end_element (context->parser, sl@0: name, sl@0: context->error)) sl@0: { sl@0: context->failed = TRUE; sl@0: return; sl@0: } sl@0: } sl@0: sl@0: /* s is not 0 terminated. */ sl@0: static void sl@0: expat_CharacterDataHandler (void *userData, sl@0: const XML_Char *s, sl@0: int len) sl@0: { sl@0: ExpatParseContext *context = userData; sl@0: sl@0: if (context->failed) sl@0: return; sl@0: sl@0: g_string_append_len (context->content, sl@0: s, len); sl@0: } sl@0: sl@0: NodeInfo* sl@0: description_load_from_file (const char *filename, sl@0: GError **error) sl@0: { sl@0: char *contents; sl@0: gsize len; sl@0: NodeInfo *nodes; sl@0: sl@0: contents = NULL; sl@0: if (!g_file_get_contents (filename, &contents, &len, error)) sl@0: return NULL; sl@0: sl@0: nodes = description_load_from_string (contents, len, error); sl@0: g_free (contents); sl@0: sl@0: return nodes; sl@0: } sl@0: sl@0: NodeInfo* sl@0: description_load_from_string (const char *str, sl@0: int len, sl@0: GError **error) sl@0: { sl@0: XML_Parser expat; sl@0: ExpatParseContext context; sl@0: NodeInfo *nodes; sl@0: sl@0: g_return_val_if_fail (error == NULL || *error == NULL, NULL); sl@0: sl@0: if (len < 0) sl@0: len = strlen (str); sl@0: sl@0: expat = NULL; sl@0: context.parser = NULL; sl@0: context.error = error; sl@0: context.failed = FALSE; sl@0: sl@0: expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL); sl@0: if (expat == NULL) sl@0: g_error ("No memory to create XML parser\n"); sl@0: sl@0: context.parser = parser_new (); sl@0: context.content = g_string_new (NULL); sl@0: sl@0: XML_SetUserData (expat, &context); sl@0: XML_SetElementHandler (expat, sl@0: expat_StartElementHandler, sl@0: expat_EndElementHandler); sl@0: XML_SetCharacterDataHandler (expat, sl@0: expat_CharacterDataHandler); sl@0: sl@0: if (!XML_Parse (expat, str, len, TRUE)) sl@0: { sl@0: if (context.error != NULL && sl@0: *context.error == NULL) sl@0: { sl@0: enum XML_Error e; sl@0: sl@0: e = XML_GetErrorCode (expat); sl@0: if (e == XML_ERROR_NO_MEMORY) sl@0: g_error ("Not enough memory to parse XML document"); sl@0: else sl@0: g_set_error (error, sl@0: G_MARKUP_ERROR, sl@0: G_MARKUP_ERROR_PARSE, sl@0: "Error in D-BUS description XML, line %d, column %d: %s\n", sl@0: XML_GetCurrentLineNumber (expat), sl@0: XML_GetCurrentColumnNumber (expat), sl@0: XML_ErrorString (e)); sl@0: } sl@0: sl@0: goto failed; sl@0: } sl@0: sl@0: if (context.failed) sl@0: goto failed; sl@0: sl@0: if (!parser_finished (context.parser, error)) sl@0: goto failed; sl@0: sl@0: XML_ParserFree (expat); sl@0: g_string_free (context.content, TRUE); sl@0: sl@0: g_return_val_if_fail (error == NULL || *error == NULL, NULL); sl@0: nodes = parser_get_nodes (context.parser); sl@0: node_info_ref (nodes); sl@0: parser_unref (context.parser); sl@0: return nodes; sl@0: sl@0: failed: sl@0: g_return_val_if_fail (error == NULL || *error != NULL, NULL); sl@0: sl@0: g_string_free (context.content, TRUE); sl@0: if (expat) sl@0: XML_ParserFree (expat); sl@0: if (context.parser) sl@0: parser_unref (context.parser); sl@0: return NULL; sl@0: } sl@0: