2003-09-07 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / glib / dbus-gloader-expat.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-gloader-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 "dbus-gparser.h"
25 #include <expat.h>
26
27 static void*
28 expat_g_malloc (size_t sz)
29 {
30   return g_malloc (sz);
31 }
32
33 static void*
34 expat_g_realloc (void *mem, size_t sz)
35 {
36   return g_realloc (mem, sz);
37 }
38
39 static XML_Memory_Handling_Suite memsuite =
40 {
41   expat_g_malloc,
42   expat_g_realloc,
43   g_free
44 };
45
46 /**
47  * Context for Expat parser for introspection data.
48  */
49 typedef struct
50 {
51   Parser *parser;       /**< The parser for the introspection data */
52   const char *filename; /**< The filename being loaded */
53   GString *content;     /**< The content of the current element */
54   GError **error;       /**< Error return location */
55   gboolean failed;      /**< True if parse has failed */
56 } ExpatParseContext;
57
58 static dbus_bool_t
59 process_content (ExpatParseContext *context)
60 {
61   if (context->failed)
62     return FALSE;
63
64   if (context->content->len > 0)
65     {
66       if (!parser_content (context->parser,
67                            context->content->str,
68                            context->content->len,
69                            context->error))
70         {
71           context->failed = TRUE;
72           return FALSE;
73         }
74       g_string_set_size (context->content, 0);
75     }
76
77   return TRUE;
78 }
79
80 static void
81 expat_StartElementHandler (void            *userData,
82                            const XML_Char  *name,
83                            const XML_Char **atts)
84 {
85   ExpatParseContext *context = userData;
86   int i;
87   char **names;
88   char **values;
89
90   /* Expat seems to suck and can't abort the parse if we
91    * throw an error. Expat 2.0 is supposed to fix this.
92    */
93   if (context->failed)
94     return;
95
96   if (!process_content (context))
97     return;
98
99   /* "atts" is key, value, key, value, NULL */
100   for (i = 0; atts[i] != NULL; ++i)
101     ; /* nothing */
102
103   g_assert (i % 2 == 0);
104   names = g_new0 (char *, i / 2 + 1);
105   values = g_new0 (char *, i / 2 + 1);
106
107   i = 0;
108   while (atts[i] != NULL)
109     {
110       g_assert (i % 2 == 0);
111       names [i / 2] = (char*) atts[i];
112       values[i / 2] = (char*) atts[i+1];
113
114       i += 2;
115     }
116
117   if (!parser_start_element (context->parser,
118                              name,
119                              (const char **) names,
120                              (const char **) values,
121                              context->error))
122     {
123       g_free (names);
124       g_free (values);
125       context->failed = TRUE;
126       return;
127     }
128
129   g_free (names);
130   g_free (values);
131 }
132
133 static void
134 expat_EndElementHandler (void           *userData,
135                          const XML_Char *name)
136 {
137   ExpatParseContext *context = userData;
138
139   if (!process_content (context))
140     return;
141
142   if (!parser_end_element (context->parser,
143                            name,
144                            context->error))
145     {
146       context->failed = TRUE;
147       return;
148     }
149 }
150
151 /* s is not 0 terminated. */
152 static void
153 expat_CharacterDataHandler (void           *userData,
154                             const XML_Char *s,
155                             int             len)
156 {
157   ExpatParseContext *context = userData;
158
159   if (context->failed)
160     return;
161
162   g_string_append_len (context->content,
163                        s, len);
164 }
165
166 Parser*
167 description_load_from_file (const char       *filename,
168                             GError          **error)
169 {
170   char *contents;
171   gsize len;
172   Parser *parser;
173   
174   contents = NULL;
175   if (!g_file_get_contents (filename, &contents, &len, error))
176     return NULL;
177
178   parser = description_load_from_string (contents, len, error);
179   g_free (contents);
180
181   return parser;
182 }
183
184 Parser*
185 description_load_from_string (const char  *str,
186                               int          len,
187                               GError     **error)
188 {
189   XML_Parser expat;
190   ExpatParseContext context;
191   
192   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
193
194   expat = NULL;
195   context.parser = NULL;
196   context.error = error;
197   context.failed = FALSE;
198   
199   expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL);
200   if (expat == NULL)
201     g_error ("No memory to create XML parser\n");
202
203   context.parser = parser_new ();
204   context.content = g_string_new (NULL);
205   
206   XML_SetUserData (expat, &context);
207   XML_SetElementHandler (expat,
208                          expat_StartElementHandler,
209                          expat_EndElementHandler);
210   XML_SetCharacterDataHandler (expat,
211                                expat_CharacterDataHandler);
212   
213   if (!XML_Parse (expat, str, len, TRUE))
214     {
215       if (context.error != NULL &&
216           *context.error == NULL)
217         {
218             enum XML_Error e;
219
220             e = XML_GetErrorCode (expat);
221             if (e == XML_ERROR_NO_MEMORY)
222               g_error ("Not enough memory to parse XML document");
223             else
224               g_set_error (error,
225                            G_MARKUP_ERROR,
226                            G_MARKUP_ERROR_PARSE,
227                            "Error in D-BUS description XML, line %d, column %d: %s\n",
228                            XML_GetCurrentLineNumber (expat),
229                            XML_GetCurrentColumnNumber (expat),
230                            XML_ErrorString (e));
231         }
232       
233         goto failed;
234     }
235   
236   if (context.failed)
237     goto failed;
238
239   if (!parser_finished (context.parser, error))
240     goto failed;
241
242   XML_ParserFree (expat);
243   g_string_free (context.content, TRUE);
244
245   g_return_val_if_fail (error == NULL || *error == NULL, NULL);  
246   return context.parser;
247
248  failed:
249   g_return_val_if_fail (error == NULL || *error != NULL, NULL);
250
251   g_string_free (context.content, TRUE);
252   if (expat)
253     XML_ParserFree (expat);
254   if (context.parser)
255     parser_unref (context.parser);
256   return NULL;
257 }