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
process_content(ExpatParseContext * context)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
expat_StartElementHandler(void * userData,const XML_Char * name,const XML_Char ** atts)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
expat_EndElementHandler(void * userData,const XML_Char * name)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
expat_CharacterDataHandler(void * userData,const XML_Char * s,int len)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*
bus_config_load(const DBusString * file,dbus_bool_t is_toplevel,const BusConfigParser * parent,DBusError * error)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