decadb6ca050698435c3f838a1d2cc56115aff4d
[runtime.git] / lib / silcutil / silcxml.c
1 /*
2
3   silcxml.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2008 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
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 */
19
20 #include <silcruntime.h>
21
22 /* XML parser context */
23 struct SilcXMLParserStruct {
24   void *parser;                        /* Parser implementation */
25   SilcXMLParserHandlerStruct handler;  /* Handler */
26   void *context;                       /* User context */
27   SilcXMLParamsStruct params;          /* Parser parameters */
28 };
29
30 #ifdef HAVE_EXPAT_H
31
32 #include <expat.h>
33
34 /* Map expat error to silc_errno */
35
36 static SilcResult silc_xml_expat_error(XML_Parser parser)
37 {
38   enum XML_Error error = XML_GetErrorCode(parser);
39
40   switch (error) {
41   case XML_ERROR_NONE:
42     return SILC_OK;
43   case XML_ERROR_NO_MEMORY:
44     return SILC_ERR_OUT_OF_MEMORY;
45   case XML_ERROR_UNKNOWN_ENCODING:
46   case XML_ERROR_INCORRECT_ENCODING:
47     return SILC_ERR_BAD_ENCODING;
48   case XML_ERROR_ABORTED:
49     return SILC_ERR_ABORTED;
50   default:
51     return SILC_ERR_SYNTAX;
52   }
53 }
54
55 /* Return error string */
56
57 static const char *silc_xml_get_error(SilcXMLParser parser)
58 {
59   return XML_ErrorString(XML_GetErrorCode(parser->parser));
60 }
61
62 /* Start element */
63
64 static void silc_xml_expat_start_element(void *userData,
65                                          const XML_Char *name,
66                                          const XML_Char **atts)
67 {
68   SilcXMLParser parser = userData;
69   SilcHashTable t = NULL;
70   int i;
71
72   if (atts && atts[0]) {
73     t = silc_hash_table_alloc(NULL, 0, silc_hash_utf8_string, NULL,
74                               silc_hash_utf8_compare, NULL,
75                               NULL, NULL, TRUE);
76     if (!t) {
77       silc_set_errno(SILC_ERR_OUT_OF_MEMORY);
78       silc_set_errno_location(NULL,
79                               XML_GetCurrentLineNumber(parser->parser),
80                               XML_GetCurrentColumnNumber(parser->parser));
81       XML_StopParser(parser->parser, FALSE);
82       return;
83     }
84
85     for (i = 0; atts[i]; i += 2)
86       silc_hash_table_add(t, (void *)atts[i], (void *)atts[i + 1]);
87   }
88
89   if (parser->handler.start_element)
90     parser->handler.start_element(parser, name, t, parser->context);
91
92   if (t)
93     silc_hash_table_free(t);
94 }
95
96 /* End element */
97
98 static void silc_xml_expat_end_element(void *userData,
99                                        const XML_Char *name)
100 {
101   SilcXMLParser parser = userData;
102
103   if (parser->handler.end_element)
104     parser->handler.end_element(parser, name, parser->context);
105 }
106
107 /* Characters */
108
109 static void silc_xml_expat_data(void *userData,
110                                 const XML_Char *s,
111                                 int len)
112
113 {
114   SilcXMLParser parser = userData;
115
116   if (parser->handler.data)
117     parser->handler.data(parser, (const unsigned char *)s,
118                          (SilcUInt32)len, parser->context);
119 }
120
121 /* Processing instruction */
122
123 static void silc_xml_expat_pi(void *userData,
124                               const XML_Char *target,
125                               const XML_Char *data)
126 {
127   SilcXMLParser parser = userData;
128
129   if (parser->handler.pi)
130     parser->handler.pi(parser, target, data, parser->context);
131 }
132
133 /* Create parser */
134
135 SilcXMLParser silc_xml_parser_create(SilcXMLParams params,
136                                      SilcXMLParserHandler handler,
137                                      void *context)
138 {
139   SilcXMLParser parser;
140   XML_Parser ep;
141
142   parser = silc_calloc(1, sizeof(*parser));
143   if (!parser)
144     return NULL;
145
146   SILC_LOG_DEBUG(("Allcoated XML parser %p", parser));
147
148   if (params)
149     parser->params = *params;
150   if (handler)
151     parser->handler = *handler;
152   parser->context = context;
153
154   /* Allocate expat parser */
155   if (parser->params.no_namespace)
156     ep = XML_ParserCreate("UTF-8");
157   else
158     ep = XML_ParserCreateNS("UTF-8", '\0');
159
160   if (!ep) {
161     silc_set_errno(SILC_ERR_OUT_OF_MEMORY);
162     silc_free(ep);
163     return NULL;
164   }
165
166   parser->parser = ep;
167
168   /* Set callbacks */
169   XML_SetUserData(ep, parser);
170   XML_SetElementHandler(ep, silc_xml_expat_start_element,
171                         silc_xml_expat_end_element);
172   XML_SetCharacterDataHandler(ep, silc_xml_expat_data);
173   XML_SetProcessingInstructionHandler(ep, silc_xml_expat_pi);
174
175   return parser;
176 }
177
178 /* Free parser */
179
180 void silc_xml_parser_free(SilcXMLParser parser)
181 {
182   if (!parser)
183     return;
184
185   SILC_LOG_DEBUG(("Free XML parser %p", parser));
186
187   if (parser->parser)
188     XML_ParserFree(parser->parser);
189   silc_free(parser);
190 }
191
192 /* Parse */
193
194 SilcBool silc_xml_parse(SilcXMLParser parser,
195                         const unsigned char *data,
196                         SilcUInt32 data_len)
197 {
198   int ret;
199
200   SILC_LOG_DEBUG(("Parse XML data with parser %p", parser));
201
202   if (!parser || !data) {
203     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
204     return FALSE;
205   }
206
207   /* Parse */
208   ret = XML_Parse(parser->parser, (const char *)data, (int)data_len, 1);
209   if (!ret) {
210     silc_set_errno_reason(silc_xml_expat_error(parser->parser),
211                           silc_xml_get_error(parser));
212     return FALSE;
213   }
214
215   return TRUE;
216 }
217
218 /* Parse file */
219
220 SilcBool silc_xml_parse_file(SilcXMLParser parser,
221                              const char *filename)
222 {
223   unsigned char *data;
224   SilcUInt32 data_len;
225   SilcBool ret;
226
227   if (!filename) {
228     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
229     return FALSE;
230   }
231
232   SILC_LOG_DEBUG(("Parse XML file '%s' with parser %p", filename, parser));
233
234   data = silc_file_readfile(filename, &data_len, NULL);
235   if (!data)
236     return FALSE;
237
238   ret = silc_xml_parse(parser, data, data_len);
239   if (!ret) {
240     silc_set_errno_reason(silc_xml_expat_error(parser->parser),
241                           silc_xml_get_error(parser));
242     silc_set_errno_location(filename,
243                             XML_GetCurrentLineNumber(parser->parser),
244                             XML_GetCurrentColumnNumber(parser->parser));
245     return FALSE;
246   }
247
248   silc_free(data);
249
250   return ret;
251 }
252
253 /* Get attribute */
254
255 const char *silc_xml_get_attribute(SilcXMLParser parser,
256                                    SilcHashTable attributes,
257                                    const char *name)
258 {
259   char *val;
260
261   if (!attributes)
262     return NULL;
263
264   if (!silc_hash_table_find(attributes, (void *)name, NULL, (void *)&val))
265     return NULL;
266
267   return val;
268 }
269
270 /* Return current location */
271
272 void silc_xml_current_location(SilcXMLParser parser,
273                                SilcUInt32 *current_line,
274                                SilcUInt32 *current_column)
275 {
276   if (current_line)
277     *current_line = XML_GetCurrentLineNumber(parser->parser);
278   if (current_column)
279     *current_column = XML_GetCurrentColumnNumber(parser->parser);
280 }
281
282 #endif /* HAVE_EXPAT_H */