1#include <expat.h>
2#include <alloc.h>
3#include <byte.h>
4#include <scan.h>
5#include <str.h>
6#include <stralloc.h>
7#include <buffer.h>
8#include <strerr.h>
9#include <stdbool.h>
10#include <stdio.h>
11#include <unistd.h>
12
13#include "parse.h"
14#include "util.h"
15
16enum parse_scope {
17 SCOPE_PROTOCOL,
18 SCOPE_INTERFACE,
19 SCOPE_MESSAGE,
20 SCOPE_ENUM,
21 SCOPE_ENTRY,
22 SCOPE_DESCRIPTION,
23};
24
25struct parser {
26 const char *path;
27 XML_Parser xml;
28 struct protocol *proto;
29 struct interface *iface;
30 struct message *msg;
31 struct enumdef *en;
32 struct enumentry *entry;
33 enum parse_scope stack[32];
34 size_t depth;
35 struct description *desc;
36 stralloc text;
37};
38
39static const char *attr(const char **, const char *);
40static int parseint(const char *, const char *);
41static void pushscope(struct parser *, enum parse_scope);
42static void popscope(struct parser *);
43static bool inscope(const struct parser *, enum parse_scope);
44static void cleartext(struct parser *);
45static void appendtext(struct parser *, const char *, int);
46static char *normalizedtext(const char *);
47static void onstart(void *, const char *, const char **);
48static void onend(void *, const char *);
49static void ontext(void *, const XML_Char *, int);
50extern const char *argv0;
51
52static void
53die_path_line(const char *path, unsigned long line, const char *msg)
54{
55 buffer_puts(buffer_2, argv0);
56 buffer_puts(buffer_2, ": ");
57 buffer_puts(buffer_2, path);
58 buffer_puts(buffer_2, ":");
59 put_ulong(buffer_2, line);
60 buffer_puts(buffer_2, ": ");
61 buffer_puts(buffer_2, msg);
62 buffer_puts(buffer_2, "\n");
63 buffer_flush(buffer_2);
64 _exit(1);
65}
66
67static void
68die_path_line_col(const char *path, unsigned long line, unsigned long col,
69 const char *msg)
70{
71 buffer_puts(buffer_2, argv0);
72 buffer_puts(buffer_2, ": ");
73 buffer_puts(buffer_2, path);
74 buffer_puts(buffer_2, ":");
75 put_ulong(buffer_2, line);
76 buffer_puts(buffer_2, ":");
77 put_ulong(buffer_2, col);
78 buffer_puts(buffer_2, ": ");
79 buffer_puts(buffer_2, msg);
80 buffer_puts(buffer_2, "\n");
81 buffer_flush(buffer_2);
82 _exit(1);
83}
84
85static const char *
86attr(const char **attrs, const char *name)
87{
88 size_t i;
89
90 for (i = 0; attrs && attrs[i]; i += 2) {
91 if (str_equal(attrs[i], name))
92 return attrs[i + 1];
93 }
94 return NULL;
95}
96
97static int
98parseint(const char *what, const char *s)
99{
100 char *msg;
101 unsigned long v;
102 unsigned int n;
103
104 if (!s)
105 strerr_die4x(111, argv0, ": missing ", what, " attribute");
106 n = scan_ulong(s, &v);
107 if (*s == '\0' || s[n] != '\0' || v > 2147483647UL) {
108 msg = copystr(s);
109 strerr_die6x(111, argv0, ": invalid ", what, ": ", msg, "");
110 }
111 return (int)v;
112}
113
114static void
115pushscope(struct parser *p, enum parse_scope scope)
116{
117 if (p->depth == countof(p->stack))
118 strerr_die4x(111, argv0, ": ", p->path, ": XML nesting too deep");
119 p->stack[p->depth++] = scope;
120}
121
122static void
123popscope(struct parser *p)
124{
125 if (p->depth > 0)
126 --p->depth;
127}
128
129static bool
130inscope(const struct parser *p, enum parse_scope scope)
131{
132 return p->depth > 0 && p->stack[p->depth - 1] == scope;
133}
134
135static void
136cleartext(struct parser *p)
137{
138 alloc_free(p->text.s);
139 p->text.s = NULL;
140 p->text.len = 0;
141 p->text.a = 0;
142}
143
144static void
145appendtext(struct parser *p, const char *s, int n)
146{
147 size_t need;
148
149 if (n <= 0)
150 return;
151 need = p->text.len + (size_t)n + 1;
152 if (!stralloc_ready(&p->text, need))
153 die_nomem();
154 byte_copy(p->text.s + p->text.len, (unsigned int)n, (char *)s);
155 p->text.len += (size_t)n;
156 p->text.s[p->text.len] = '\0';
157}
158
159static char *
160normalizedtext(const char *s)
161{
162 const char *start, *end, *lineend;
163 stralloc out = {0};
164 size_t n;
165
166 if (!s)
167 return NULL;
168 start = s;
169 while (*start == '\n' || *start == '\r' || *start == '\t' || *start == ' ')
170 ++start;
171 end = s + str_len(s);
172 while (end > start && (end[-1] == '\n' || end[-1] == '\r' || end[-1] == '\t' || end[-1] == ' '))
173 --end;
174 if (start == end)
175 return NULL;
176
177 while (start < end) {
178 while (start < end && (*start == '\n' || *start == '\r'))
179 ++start;
180 lineend = start;
181 while (lineend < end && *lineend != '\n' && *lineend != '\r')
182 ++lineend;
183 while (start < lineend && (*start == ' ' || *start == '\t'))
184 ++start;
185 while (lineend > start && (lineend[-1] == ' ' || lineend[-1] == '\t'))
186 --lineend;
187 n = (size_t)(lineend - start);
188 if (n > 0) {
189 if (out.len > 0 && !stralloc_catb(&out, "\n", 1))
190 goto nomem;
191 if (!stralloc_catb(&out, start, (unsigned int)n))
192 goto nomem;
193 }
194 start = lineend;
195 while (start < end && *start != '\n' && *start != '\r')
196 ++start;
197 }
198 if (out.len == 0) {
199 alloc_free(out.s);
200 return NULL;
201 }
202 if (!stralloc_0(&out))
203 goto nomem;
204 return out.s;
205
206nomem:
207 alloc_free(out.s);
208 die_nomem();
209 return NULL;
210}
211
212static void
213onstart(void *data, const char *name, const char **attrs)
214{
215 struct parser *p = data;
216 const char *aname, *atype, *iface, *allow_null, *version, *type, *summary;
217
218 if (str_equal(name, "protocol")) {
219 if (p->proto->name)
220 die_path_line(p->path, XML_GetCurrentLineNumber(p->xml),
221 "duplicate protocol element");
222 aname = attr(attrs, "name");
223 if (!aname)
224 die_path_line(p->path, XML_GetCurrentLineNumber(p->xml),
225 "protocol missing name");
226 p->proto->name = copystr(aname);
227 pushscope(p, SCOPE_PROTOCOL);
228 return;
229 }
230
231 if (str_equal(name, "interface") && inscope(p, SCOPE_PROTOCOL)) {
232 aname = attr(attrs, "name");
233 version = attr(attrs, "version");
234 if (!aname || !version)
235 die_path_line(p->path, XML_GetCurrentLineNumber(p->xml),
236 "interface missing name/version");
237 p->iface = proto_add_interface(p->proto, aname, parseint("version", version));
238 pushscope(p, SCOPE_INTERFACE);
239 return;
240 }
241
242 if ((str_equal(name, "request") || str_equal(name, "event")) && inscope(p, SCOPE_INTERFACE)) {
243 aname = attr(attrs, "name");
244 type = attr(attrs, "type");
245 if (!aname)
246 die_path_line(p->path, XML_GetCurrentLineNumber(p->xml),
247 "message missing name");
248 p->msg = iface_add_message(p->iface,
249 str_equal(name, "request") ? MSG_REQUEST : MSG_EVENT, aname);
250 p->msg->destructor = type && str_equal(type, "destructor");
251 pushscope(p, SCOPE_MESSAGE);
252 return;
253 }
254
255 if (str_equal(name, "arg") && inscope(p, SCOPE_MESSAGE)) {
256 aname = attr(attrs, "name");
257 atype = attr(attrs, "type");
258 iface = attr(attrs, "interface");
259 allow_null = attr(attrs, "allow-null");
260 summary = attr(attrs, "summary");
261 if (!aname || !atype)
262 die_path_line(p->path, XML_GetCurrentLineNumber(p->xml),
263 "arg missing name/type");
264 message_add_arg(p->msg, aname, atype, iface,
265 allow_null && str_equal(allow_null, "true"));
266 if (summary)
267 p->msg->args[p->msg->nargs - 1].summary = copystr(summary);
268 return;
269 }
270
271 if (str_equal(name, "enum") && inscope(p, SCOPE_INTERFACE)) {
272 aname = attr(attrs, "name");
273 if (!aname)
274 die_path_line(p->path, XML_GetCurrentLineNumber(p->xml),
275 "enum missing name");
276 iface_add_enum(p->iface, aname);
277 p->en = iface_last_enum(p->iface);
278 pushscope(p, SCOPE_ENUM);
279 return;
280 }
281
282 if (str_equal(name, "entry") && inscope(p, SCOPE_ENUM)) {
283 const char *value;
284
285 aname = attr(attrs, "name");
286 value = attr(attrs, "value");
287 summary = attr(attrs, "summary");
288 if (!aname || !value)
289 die_path_line(p->path, XML_GetCurrentLineNumber(p->xml),
290 "entry missing name/value");
291 enum_add_entry(p->en, aname, value);
292 p->entry = enum_last_entry(p->en);
293 if (summary)
294 p->entry->summary = copystr(summary);
295 pushscope(p, SCOPE_ENTRY);
296 return;
297 }
298
299 if (str_equal(name, "description")) {
300 if (inscope(p, SCOPE_MESSAGE))
301 p->desc = &p->msg->desc;
302 else if (inscope(p, SCOPE_ENTRY))
303 p->desc = &p->entry->desc;
304 else if (inscope(p, SCOPE_ENUM))
305 p->desc = &p->en->desc;
306 else if (inscope(p, SCOPE_INTERFACE))
307 p->desc = &p->iface->desc;
308 else if (inscope(p, SCOPE_PROTOCOL))
309 p->desc = &p->proto->desc;
310 else
311 return;
312 summary = attr(attrs, "summary");
313 alloc_free(p->desc->summary);
314 p->desc->summary = summary ? copystr(summary) : NULL;
315 cleartext(p);
316 pushscope(p, SCOPE_DESCRIPTION);
317 return;
318 }
319}
320
321static void
322onend(void *data, const char *name)
323{
324 struct parser *p = data;
325
326 if (str_equal(name, "request") || str_equal(name, "event")) {
327 if (inscope(p, SCOPE_MESSAGE)) {
328 p->msg = NULL;
329 popscope(p);
330 }
331 return;
332 }
333 if (str_equal(name, "enum")) {
334 if (inscope(p, SCOPE_ENUM))
335 {
336 p->en = NULL;
337 popscope(p);
338 }
339 return;
340 }
341 if (str_equal(name, "entry")) {
342 if (inscope(p, SCOPE_ENTRY)) {
343 p->entry = NULL;
344 popscope(p);
345 }
346 return;
347 }
348 if (str_equal(name, "description")) {
349 if (inscope(p, SCOPE_DESCRIPTION)) {
350 alloc_free(p->desc->text);
351 p->desc->text = normalizedtext(p->text.s);
352 p->desc = NULL;
353 cleartext(p);
354 popscope(p);
355 }
356 return;
357 }
358 if (str_equal(name, "interface")) {
359 if (inscope(p, SCOPE_INTERFACE)) {
360 p->iface = NULL;
361 popscope(p);
362 }
363 return;
364 }
365 if (str_equal(name, "protocol")) {
366 if (inscope(p, SCOPE_PROTOCOL))
367 popscope(p);
368 return;
369 }
370}
371
372static void
373ontext(void *data, const XML_Char *s, int len)
374{
375 struct parser *p = data;
376
377 if (inscope(p, SCOPE_DESCRIPTION))
378 appendtext(p, s, len);
379}
380
381void
382parsefile(const char *path, struct protocol *proto)
383{
384 struct parser p;
385 FILE *fp;
386 void *buf;
387 int done;
388
389 byte_zero(&p, sizeof(p));
390 p.path = path;
391 p.proto = proto;
392 p.xml = XML_ParserCreate(NULL);
393 if (!p.xml)
394 strerr_die2sys(111, argv0, ": XML_ParserCreate");
395 XML_SetUserData(p.xml, &p);
396 XML_SetElementHandler(p.xml, onstart, onend);
397 XML_SetCharacterDataHandler(p.xml, ontext);
398
399 fp = fopen(path, "rb");
400 if (!fp) {
401 XML_ParserFree(p.xml);
402 strerr_die4sys(111, argv0, ": open ", path, "");
403 }
404
405 done = 0;
406 while (!done) {
407 size_t nread;
408
409 buf = XML_GetBuffer(p.xml, 4096);
410 if (!buf)
411 strerr_die4x(111, argv0, ": ", path, ": XML_GetBuffer failed");
412 nread = fread(buf, 1, 4096, fp);
413 done = nread < 4096;
414 if (ferror(fp))
415 strerr_die4sys(111, argv0, ": read ", path, "");
416 if (XML_ParseBuffer(p.xml, (int)nread, done) == XML_STATUS_ERROR) {
417 die_path_line_col(path,
418 XML_GetCurrentLineNumber(p.xml),
419 XML_GetCurrentColumnNumber(p.xml),
420 XML_ErrorString(XML_GetErrorCode(p.xml)));
421 }
422 }
423
424 fclose(fp);
425 XML_ParserFree(p.xml);
426 cleartext(&p);
427
428 if (!proto->name)
429 strerr_die4x(111, argv0, ": ", path, ": missing protocol element");
430 proto_resolve(proto);
431}