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