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