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