1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2/* config-loader-expat.c expat XML loader 3 * 4 * Copyright (C) 2003 Red Hat, Inc. 5 * 6 * Licensed under the Academic Free License version 2.1 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24#include <config.h> 25#include "config-parser.h" 26#include <dbus/dbus-internals.h> 27#include <expat.h> 28 29static XML_Memory_Handling_Suite memsuite; 30 31typedef struct 32{ 33 BusConfigParser *parser; 34 const char *filename; 35 DBusString content; 36 DBusError *error; 37 dbus_bool_t failed; 38} ExpatParseContext; 39 40static dbus_bool_t 41process_content (ExpatParseContext *context) 42{ 43 if (context->failed) 44 return FALSE; 45 46 if (_dbus_string_get_length (&context->content) > 0) 47 { 48 if (!bus_config_parser_content (context->parser, 49 &context->content, 50 context->error)) 51 { 52 context->failed = TRUE; 53 return FALSE; 54 } 55 _dbus_string_set_length (&context->content, 0); 56 } 57 58 return TRUE; 59} 60 61static void 62expat_StartElementHandler (void *userData, 63 const XML_Char *name, 64 const XML_Char **atts) 65{ 66 ExpatParseContext *context = userData; 67 int i; 68 char **names; 69 char **values; 70 71 /* Expat seems to suck and can't abort the parse if we 72 * throw an error. Expat 2.0 is supposed to fix this. 73 */ 74 if (context->failed) 75 return; 76 77 if (!process_content (context)) 78 return; 79 80 /* "atts" is key, value, key, value, NULL */ 81 for (i = 0; atts[i] != NULL; ++i) 82 ; /* nothing */ 83 84 _dbus_assert (i % 2 == 0); 85 names = dbus_new0 (char *, i / 2 + 1); 86 values = dbus_new0 (char *, i / 2 + 1); 87 88 if (names == NULL || values == NULL) 89 { 90 dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL); 91 context->failed = TRUE; 92 dbus_free (names); 93 dbus_free (values); 94 return; 95 } 96 97 i = 0; 98 while (atts[i] != NULL) 99 { 100 _dbus_assert (i % 2 == 0); 101 names [i / 2] = (char*) atts[i]; 102 values[i / 2] = (char*) atts[i+1]; 103 104 i += 2; 105 } 106 107 if (!bus_config_parser_start_element (context->parser, 108 name, 109 (const char **) names, 110 (const char **) values, 111 context->error)) 112 { 113 dbus_free (names); 114 dbus_free (values); 115 context->failed = TRUE; 116 return; 117 } 118 119 dbus_free (names); 120 dbus_free (values); 121} 122 123static void 124expat_EndElementHandler (void *userData, 125 const XML_Char *name) 126{ 127 ExpatParseContext *context = userData; 128 129 if (!process_content (context)) 130 return; 131 132 if (!bus_config_parser_end_element (context->parser, 133 name, 134 context->error)) 135 { 136 context->failed = TRUE; 137 return; 138 } 139} 140 141/* s is not 0 terminated. */ 142static void 143expat_CharacterDataHandler (void *userData, 144 const XML_Char *s, 145 int len) 146{ 147 ExpatParseContext *context = userData; 148 if (context->failed) 149 return; 150 151 if (!_dbus_string_append_len (&context->content, 152 s, len)) 153 { 154 dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL); 155 context->failed = TRUE; 156 return; 157 } 158} 159 160 161BusConfigParser* 162bus_config_load (const DBusString *file, 163 dbus_bool_t is_toplevel, 164 const BusConfigParser *parent, 165 DBusError *error) 166{ 167 XML_Parser expat; 168 const char *filename; 169 BusConfigParser *parser; 170 ExpatParseContext context; 171 DBusString dirname; 172 173 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 174 175 parser = NULL; 176 expat = NULL; 177 context.error = error; 178 context.failed = FALSE; 179 filename = _dbus_string_get_const_data (file); 180 181 if (!_dbus_string_init (&context.content)) 182 { 183 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 184 return NULL; 185 } 186 187 if (!_dbus_string_init (&dirname)) 188 { 189 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 190 _dbus_string_free (&context.content); 191 return NULL; 192 } 193 194 memsuite.malloc_fcn = dbus_malloc; 195 memsuite.realloc_fcn = dbus_realloc; 196 memsuite.free_fcn = dbus_free; 197 198 expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL); 199 if (expat == NULL) 200 { 201 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 202 goto failed; 203 } 204 205 if (!_dbus_string_get_dirname (file, &dirname)) 206 { 207 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 208 goto failed; 209 } 210 211 parser = bus_config_parser_new (&dirname, is_toplevel, parent); 212 if (parser == NULL) 213 { 214 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 215 goto failed; 216 } 217 context.parser = parser; 218 219 XML_SetUserData (expat, &context); 220 XML_SetElementHandler (expat, 221 expat_StartElementHandler, 222 expat_EndElementHandler); 223 XML_SetCharacterDataHandler (expat, 224 expat_CharacterDataHandler); 225 226 { 227 DBusString data; 228 const char *data_str; 229 230 if (!_dbus_string_init (&data)) 231 { 232 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 233 goto failed; 234 } 235 236 if (!_dbus_file_get_contents (&data, file, error)) 237 { 238 _dbus_string_free (&data); 239 goto failed; 240 } 241 242 data_str = _dbus_string_get_const_data (&data); 243 244 if (!XML_Parse (expat, data_str, _dbus_string_get_length (&data), TRUE)) 245 { 246 if (context.error != NULL && 247 !dbus_error_is_set (context.error)) 248 { 249 enum XML_Error e; 250 251 e = XML_GetErrorCode (expat); 252 if (e == XML_ERROR_NO_MEMORY) 253 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 254 else 255 dbus_set_error (error, DBUS_ERROR_FAILED, 256 "Error in file %s, line %d, column %d: %s\n", 257 filename, 258 XML_GetCurrentLineNumber (expat), 259 XML_GetCurrentColumnNumber (expat), 260 XML_ErrorString (e)); 261 } 262 263 _dbus_string_free (&data); 264 goto failed; 265 } 266 267 _dbus_string_free (&data); 268 269 if (context.failed) 270 goto failed; 271 } 272 273 if (!bus_config_parser_finished (parser, error)) 274 goto failed; 275 276 _dbus_string_free (&dirname); 277 _dbus_string_free (&context.content); 278 XML_ParserFree (expat); 279 280 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 281 return parser; 282 283 failed: 284 _DBUS_ASSERT_ERROR_IS_SET (error); 285 286 _dbus_string_free (&dirname); 287 _dbus_string_free (&context.content); 288 if (expat) 289 XML_ParserFree (expat); 290 if (parser) 291 bus_config_parser_unref (parser); 292 return NULL; 293} 294