~jamesodhunt/ubuntu/raring/upstart/1.6

« back to all changes in this revision

Viewing changes to nih-dbus-tool/parse.c

  • Committer: Scott James Remnant
  • Date: 2010-02-04 23:39:59 UTC
  • mfrom: (1182.1.45 upstart)
  • mto: This revision was merged to the branch mainline in revision 1250.
  • Revision ID: scott@netsplit.com-20100204233959-7kajqjnaoh7208ob
Tags: upstream-0.6.5
ImportĀ upstreamĀ versionĀ 0.6.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* nih-dbus-tool
2
 
 *
3
 
 * parse.c - parse XML introspection data and tool-specific annotations
4
 
 *
5
 
 * Copyright Ā© 2009 Scott James Remnant <scott@netsplit.com>.
6
 
 * Copyright Ā© 2009 Canonical Ltd.
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 version 2, as
10
 
 * published by the Free Software Foundation.
11
 
 *
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.
16
 
 *
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.
20
 
 */
21
 
 
22
 
#ifdef HAVE_CONFIG_H
23
 
# include <config.h>
24
 
#endif /* HAVE_CONFIG_H */
25
 
 
26
 
 
27
 
#include <expat.h>
28
 
 
29
 
#include <errno.h>
30
 
#include <string.h>
31
 
#include <unistd.h>
32
 
 
33
 
#include <nih/macros.h>
34
 
#include <nih/alloc.h>
35
 
#include <nih/list.h>
36
 
#include <nih/error.h>
37
 
#include <nih/logging.h>
38
 
 
39
 
#include "node.h"
40
 
#include "interface.h"
41
 
#include "method.h"
42
 
#include "signal.h"
43
 
#include "property.h"
44
 
#include "argument.h"
45
 
#include "annotation.h"
46
 
#include "parse.h"
47
 
#include "errors.h"
48
 
 
49
 
 
50
 
/**
51
 
 * BUF_SIZE:
52
 
 *
53
 
 * Size of buffer we use when parsing.
54
 
 **/
55
 
#define BUF_SIZE 80
56
 
 
57
 
 
58
 
/**
59
 
 * parse_stack_push:
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.
64
 
 *
65
 
 * Allocates a new Stack object with the @type and @data specified
66
 
 * and pushes it onto @stack.
67
 
 *
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.
70
 
 *
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
74
 
 * freed.
75
 
 *
76
 
 * Returns: new entry or NULL if the allocation failed.
77
 
 **/
78
 
ParseStack *
79
 
parse_stack_push (const void *   parent,
80
 
                  NihList *      stack,
81
 
                  ParseStackType type,
82
 
                  void *         data)
83
 
{
84
 
        ParseStack *entry;
85
 
 
86
 
        nih_assert (stack != NULL);
87
 
 
88
 
        entry = nih_new (parent, ParseStack);
89
 
        if (! entry)
90
 
                return NULL;
91
 
 
92
 
        nih_list_init (&entry->entry);
93
 
 
94
 
        nih_alloc_set_destructor (entry, nih_list_destroy);
95
 
 
96
 
        entry->type = type;
97
 
        entry->data = data;
98
 
 
99
 
        if ((entry->type != PARSE_IGNORED)
100
 
            && (entry->type != PARSE_ANNOTATION)) {
101
 
                nih_assert (entry->data != NULL);
102
 
                nih_ref (entry->data, entry);
103
 
        } else {
104
 
                nih_assert (entry->data == NULL);
105
 
        }
106
 
 
107
 
        nih_list_add_after (stack, &entry->entry);
108
 
 
109
 
        return entry;
110
 
}
111
 
 
112
 
/**
113
 
 * parse_stack_top:
114
 
 * @stack: stack to return from.
115
 
 *
116
 
 * Returns: the top entry in @stack or NULL if the stack is empty.
117
 
 **/
118
 
ParseStack *
119
 
parse_stack_top (NihList *stack)
120
 
{
121
 
        nih_assert (stack != NULL);
122
 
 
123
 
        if (! NIH_LIST_EMPTY (stack)) {
124
 
                return (ParseStack *)stack->next;
125
 
        } else {
126
 
                return NULL;
127
 
        }
128
 
}
129
 
 
130
 
 
131
 
/**
132
 
 * parse_start_tag:
133
 
 * @xmlp: XML parser,
134
 
 * @tag: name of XML tag being parsed,
135
 
 * @attr: NULL-terminated array of attribute name and value pairs.
136
 
 *
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
140
 
 * handle the tag.
141
 
 *
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.
145
 
 *
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().
151
 
 **/
152
 
void
153
 
parse_start_tag (XML_Parser    xmlp,
154
 
                 const char *  tag,
155
 
                 char * const *attr)
156
 
{
157
 
        XML_ParsingStatus status;
158
 
        ParseContext *    context;
159
 
        ParseStack *      parent;
160
 
        int               ret = 0;
161
 
 
162
 
        nih_assert (xmlp != NULL);
163
 
        nih_assert (tag != NULL);
164
 
        nih_assert (attr != NULL);
165
 
 
166
 
        XML_GetParsingStatus (xmlp, &status);
167
 
        if (status.parsing == XML_FINISHED)
168
 
                return;
169
 
 
170
 
        nih_debug ("Parsed '%s' tag", tag);
171
 
 
172
 
        context = XML_GetUserData (xmlp);
173
 
        nih_assert (context != NULL);
174
 
 
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 ();
181
 
                        ret = -1;
182
 
                }
183
 
 
184
 
                goto exit;
185
 
        }
186
 
 
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);
202
 
        } else {
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"),
207
 
                          tag);
208
 
 
209
 
                if (! parse_stack_push (NULL, &context->stack,
210
 
                                        PARSE_IGNORED, NULL)) {
211
 
                        nih_error_raise_system ();
212
 
                        ret = -1;
213
 
                }
214
 
        }
215
 
 
216
 
exit:
217
 
        if (ret < 0)
218
 
                nih_assert (XML_StopParser (xmlp, FALSE) == XML_STATUS_OK);
219
 
}
220
 
 
221
 
/**
222
 
 * parse_end_tag:
223
 
 * @xmlp: XML parser,
224
 
 * @tag: name of XML tag being parsed.
225
 
 *
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
229
 
 * handle the tag.
230
 
 *
231
 
 * The end tags whose start was ignored are ignored without any warning.
232
 
 *
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().
238
 
 **/
239
 
void
240
 
parse_end_tag (XML_Parser  xmlp,
241
 
               const char *tag)
242
 
{
243
 
        XML_ParsingStatus status;
244
 
        ParseContext *    context;
245
 
        ParseStack *      entry;
246
 
        int               ret = 0;
247
 
 
248
 
        nih_assert (xmlp != NULL);
249
 
        nih_assert (tag != NULL);
250
 
 
251
 
        XML_GetParsingStatus (xmlp, &status);
252
 
        if (status.parsing == XML_FINISHED)
253
 
                return;
254
 
 
255
 
        nih_debug ("Parsed '%s' end tag", tag);
256
 
 
257
 
        context = XML_GetUserData (xmlp);
258
 
        nih_assert (context != NULL);
259
 
 
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) {
264
 
                nih_free (entry);
265
 
                goto exit;
266
 
        }
267
 
 
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);
283
 
        } else {
284
 
                nih_assert_not_reached ();
285
 
        }
286
 
 
287
 
exit:
288
 
        if (ret < 0)
289
 
                nih_assert (XML_StopParser (xmlp, FALSE) == XML_STATUS_OK);
290
 
}
291
 
 
292
 
 
293
 
/**
294
 
 * parse_xml:
295
 
 * @parent: parent object of new node,
296
 
 * @fd: file descriptor to parse from,
297
 
 * @filename: filename for error reporting.
298
 
 *
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.
302
 
 *
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
305
 
 * these errors.
306
 
 *
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.
311
 
 *
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
315
 
 * freed.
316
 
 *
317
 
 * Returns: newly allocated Node on success, NULL on error.
318
 
 **/
319
 
Node *
320
 
parse_xml (const void *parent,
321
 
           int         fd,
322
 
           const char *filename)
323
 
{
324
 
        ParseContext context;
325
 
        XML_Parser   xmlp;
326
 
        ssize_t      len;
327
 
 
328
 
        context.parent = parent;
329
 
        nih_list_init (&context.stack);
330
 
        context.filename = filename;
331
 
        context.node = NULL;
332
 
 
333
 
        xmlp = XML_ParserCreate ("UTF-8");
334
 
        if (! xmlp) {
335
 
                nih_error ("%s: %s", _("Unable to create XML Parser"),
336
 
                           strerror (ENOMEM));
337
 
                return NULL;
338
 
        }
339
 
 
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);
345
 
 
346
 
        do {
347
 
                enum XML_Status status;
348
 
                enum XML_Error  error;
349
 
                void *          buf;
350
 
 
351
 
                buf = XML_GetBuffer (xmlp, BUF_SIZE);
352
 
                if (! buf) {
353
 
                        error = XML_GetErrorCode (xmlp);
354
 
                        nih_error ("%s: %s", _("Unable to allocate parsing buffer"),
355
 
                                   XML_ErrorString (error));
356
 
                        goto error;
357
 
                }
358
 
 
359
 
                len = read (fd, buf, BUF_SIZE);
360
 
                if (len < 0) {
361
 
                        nih_error ("%s: %s: %s", context.filename,
362
 
                                   _("Read error"), strerror (errno));
363
 
                        goto error;
364
 
                }
365
 
 
366
 
                status = XML_ParseBuffer (xmlp, len, len == 0 ? TRUE : FALSE);
367
 
                if (status != XML_STATUS_OK) {
368
 
                        error = XML_GetErrorCode (xmlp);
369
 
 
370
 
                        if (error == XML_ERROR_ABORTED) {
371
 
                                NihError *err;
372
 
 
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),
377
 
                                           err->message);
378
 
                                nih_free (err);
379
 
                        } else {
380
 
                                nih_error ("%s:%zu:%zu: %s: %s",
381
 
                                           context.filename,
382
 
                                           (size_t)XML_GetCurrentLineNumber (xmlp),
383
 
                                           (size_t)XML_GetCurrentColumnNumber (xmlp),
384
 
                                           _("XML parse error"),
385
 
                                           XML_ErrorString (error));
386
 
                        }
387
 
 
388
 
                        goto error;
389
 
                }
390
 
        } while (len > 0);
391
 
 
392
 
        nih_assert (NIH_LIST_EMPTY (&context.stack));
393
 
 
394
 
        if (! context.node) {
395
 
                nih_error ("%s: %s", context.filename,
396
 
                           _("No node present"));
397
 
                goto error;
398
 
        }
399
 
 
400
 
        XML_ParserFree (xmlp);
401
 
 
402
 
        return context.node;
403
 
 
404
 
error:
405
 
        NIH_LIST_FOREACH_SAFE (&context.stack, iter) {
406
 
                ParseStack *entry = (ParseStack *)iter;
407
 
 
408
 
                nih_free (entry);
409
 
        }
410
 
 
411
 
        if (context.node)
412
 
                nih_free (context.node);
413
 
 
414
 
        XML_ParserFree (xmlp);
415
 
 
416
 
        return NULL;
417
 
}