3
* parse.c - parse XML introspection data and tool-specific annotations
5
* Copyright Ā© 2009 Scott James Remnant <scott@netsplit.com>.
6
* Copyright Ā© 2009 Canonical Ltd.
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License version 2, as
10
* published by the Free Software Foundation.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License along
18
* with this program; if not, write to the Free Software Foundation, Inc.,
19
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
#endif /* HAVE_CONFIG_H */
33
#include <nih/macros.h>
34
#include <nih/alloc.h>
36
#include <nih/error.h>
37
#include <nih/logging.h>
40
#include "interface.h"
45
#include "annotation.h"
53
* Size of buffer we use when parsing.
60
* @parent: parent object of new stack entry,
61
* @stack: stack to push onto,
62
* @type: type of object to push,
63
* @data: pointer to object data.
65
* Allocates a new Stack object with the @type and @data specified
66
* and pushes it onto @stack.
68
* The entry can be removed from the stack by freeing it, though this will
69
* not free the associated @data unless you arrange that by references.
71
* If @parent is not NULL, it should be a pointer to another object which
72
* will be used as a parent for the returned entry. When all parents
73
* of the returned node are freed, the returned entry will also be
76
* Returns: new entry or NULL if the allocation failed.
79
parse_stack_push (const void * parent,
86
nih_assert (stack != NULL);
88
entry = nih_new (parent, ParseStack);
92
nih_list_init (&entry->entry);
94
nih_alloc_set_destructor (entry, nih_list_destroy);
99
if ((entry->type != PARSE_IGNORED)
100
&& (entry->type != PARSE_ANNOTATION)) {
101
nih_assert (entry->data != NULL);
102
nih_ref (entry->data, entry);
104
nih_assert (entry->data == NULL);
107
nih_list_add_after (stack, &entry->entry);
114
* @stack: stack to return from.
116
* Returns: the top entry in @stack or NULL if the stack is empty.
119
parse_stack_top (NihList *stack)
121
nih_assert (stack != NULL);
123
if (! NIH_LIST_EMPTY (stack)) {
124
return (ParseStack *)stack->next;
134
* @tag: name of XML tag being parsed,
135
* @attr: NULL-terminated array of attribute name and value pairs.
137
* This function is intended to be used as the start element handler for
138
* the XML parser @xmlp and called by that parser. It looks at the tag
139
* name @tag and calls one of the other *_start_tag() functions to
142
* Unknown tags result in a warning and are otherwise ignored, the stack
143
* contains an ignore element and the content of those tags will also be
144
* ignored with no warnings generated.
146
* Errors are raised and reported by stopping the parser, other element
147
* handlers should check that the parsing status is not finished as they
148
* may be called as part of the unwinding process. XML_ParseBuffer will
149
* return to indicate an error, the XML error code will be XML_ERROR_ABORTED
150
* and the actual error can be retrieved with nih_error_get().
153
parse_start_tag (XML_Parser xmlp,
157
XML_ParsingStatus status;
158
ParseContext * context;
162
nih_assert (xmlp != NULL);
163
nih_assert (tag != NULL);
164
nih_assert (attr != NULL);
166
XML_GetParsingStatus (xmlp, &status);
167
if (status.parsing == XML_FINISHED)
170
nih_debug ("Parsed '%s' tag", tag);
172
context = XML_GetUserData (xmlp);
173
nih_assert (context != NULL);
175
/* Ignore any tag inside an ignored tag */
176
parent = parse_stack_top (&context->stack);
177
if (parent && (parent->type == PARSE_IGNORED)) {
178
if (! parse_stack_push (NULL, &context->stack,
179
PARSE_IGNORED, NULL)) {
180
nih_error_raise_system ();
187
/* Otherwise call out to handle the tag */
188
if (! strcmp (tag, "node")) {
189
ret = node_start_tag (xmlp, tag, attr);
190
} else if (! strcmp (tag, "interface")) {
191
ret = interface_start_tag (xmlp, tag, attr);
192
} else if (! strcmp (tag, "method")) {
193
ret = method_start_tag (xmlp, tag, attr);
194
} else if (! strcmp (tag, "signal")) {
195
ret = signal_start_tag (xmlp, tag, attr);
196
} else if (! strcmp (tag, "property")) {
197
ret = property_start_tag (xmlp, tag, attr);
198
} else if (! strcmp (tag, "arg")) {
199
ret = argument_start_tag (xmlp, tag, attr);
200
} else if (! strcmp (tag, "annotation")) {
201
ret = annotation_start_tag (xmlp, tag, attr);
203
nih_warn ("%s:%zu:%zu: %s: %s", context->filename,
204
(size_t)XML_GetCurrentLineNumber (xmlp),
205
(size_t)XML_GetCurrentColumnNumber (xmlp),
206
_("Ignored unknown tag"),
209
if (! parse_stack_push (NULL, &context->stack,
210
PARSE_IGNORED, NULL)) {
211
nih_error_raise_system ();
218
nih_assert (XML_StopParser (xmlp, FALSE) == XML_STATUS_OK);
224
* @tag: name of XML tag being parsed.
226
* This function is intended to be used as the end element handler for
227
* the XML parser @xmlp and called by that parser. It looks at the tag
228
* name @tag and calls one of the other *_end_tag() functions to
231
* The end tags whose start was ignored are ignored without any warning.
233
* Errors are raised and reported by stopping the parser, other element
234
* handlers should check that the parsing status is not finished as they
235
* may be called as part of the unwinding process. XML_ParseBuffer will
236
* return to indicate an error, the XML error code will be XML_ERROR_ABORTED
237
* and the actual error can be retrieved with nih_error_get().
240
parse_end_tag (XML_Parser xmlp,
243
XML_ParsingStatus status;
244
ParseContext * context;
248
nih_assert (xmlp != NULL);
249
nih_assert (tag != NULL);
251
XML_GetParsingStatus (xmlp, &status);
252
if (status.parsing == XML_FINISHED)
255
nih_debug ("Parsed '%s' end tag", tag);
257
context = XML_GetUserData (xmlp);
258
nih_assert (context != NULL);
260
/* Ignore the end tag of any ignored tag */
261
entry = parse_stack_top (&context->stack);
262
nih_assert (entry != NULL);
263
if (entry->type == PARSE_IGNORED) {
268
/* Otherwise call out to handle the tag */
269
if (! strcmp (tag, "node")) {
270
ret = node_end_tag (xmlp, tag);
271
} else if (! strcmp (tag, "interface")) {
272
ret = interface_end_tag (xmlp, tag);
273
} else if (! strcmp (tag, "method")) {
274
ret = method_end_tag (xmlp, tag);
275
} else if (! strcmp (tag, "signal")) {
276
ret = signal_end_tag (xmlp, tag);
277
} else if (! strcmp (tag, "property")) {
278
ret = property_end_tag (xmlp, tag);
279
} else if (! strcmp (tag, "arg")) {
280
ret = argument_end_tag (xmlp, tag);
281
} else if (! strcmp (tag, "annotation")) {
282
ret = annotation_end_tag (xmlp, tag);
284
nih_assert_not_reached ();
289
nih_assert (XML_StopParser (xmlp, FALSE) == XML_STATUS_OK);
295
* @parent: parent object of new node,
296
* @fd: file descriptor to parse from,
297
* @filename: filename for error reporting.
299
* Parse XML data from @fd according to the D-Bus Introspection
300
* specification, returning the top-level Node which contains the
301
* Interfaces defined by that object.
303
* Errors in parsing are output within this function, since it has the
304
* line and column number available to it. @filename is used when reporting
307
* In general, the parser is fairly liberal and will ignore unexpected tags,
308
* attributes and any character data. However it is strict about restrictions
309
* in the specification, for example it will not allow missing attributes or
310
* unknown values in them.
312
* If @parent is not NULL, it should be a pointer to another object which
313
* will be used as a parent for the returned node. When all parents
314
* of the returned node are freed, the returned node will also be
317
* Returns: newly allocated Node on success, NULL on error.
320
parse_xml (const void *parent,
322
const char *filename)
324
ParseContext context;
328
context.parent = parent;
329
nih_list_init (&context.stack);
330
context.filename = filename;
333
xmlp = XML_ParserCreate ("UTF-8");
335
nih_error ("%s: %s", _("Unable to create XML Parser"),
340
XML_SetUserData (xmlp, &context);
341
XML_UseParserAsHandlerArg (xmlp);
342
XML_SetElementHandler (xmlp,
343
(XML_StartElementHandler)parse_start_tag,
344
(XML_EndElementHandler)parse_end_tag);
347
enum XML_Status status;
348
enum XML_Error error;
351
buf = XML_GetBuffer (xmlp, BUF_SIZE);
353
error = XML_GetErrorCode (xmlp);
354
nih_error ("%s: %s", _("Unable to allocate parsing buffer"),
355
XML_ErrorString (error));
359
len = read (fd, buf, BUF_SIZE);
361
nih_error ("%s: %s: %s", context.filename,
362
_("Read error"), strerror (errno));
366
status = XML_ParseBuffer (xmlp, len, len == 0 ? TRUE : FALSE);
367
if (status != XML_STATUS_OK) {
368
error = XML_GetErrorCode (xmlp);
370
if (error == XML_ERROR_ABORTED) {
373
err = nih_error_get ();
374
nih_error ("%s:%zu:%zu: %s", context.filename,
375
(size_t)XML_GetCurrentLineNumber (xmlp),
376
(size_t)XML_GetCurrentColumnNumber (xmlp),
380
nih_error ("%s:%zu:%zu: %s: %s",
382
(size_t)XML_GetCurrentLineNumber (xmlp),
383
(size_t)XML_GetCurrentColumnNumber (xmlp),
384
_("XML parse error"),
385
XML_ErrorString (error));
392
nih_assert (NIH_LIST_EMPTY (&context.stack));
394
if (! context.node) {
395
nih_error ("%s: %s", context.filename,
396
_("No node present"));
400
XML_ParserFree (xmlp);
405
NIH_LIST_FOREACH_SAFE (&context.stack, iter) {
406
ParseStack *entry = (ParseStack *)iter;
412
nih_free (context.node);
414
XML_ParserFree (xmlp);