1#include <expat.h>
2#include <stdbool.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7#include "parse.h"
8#include "util.h"
9
10enum parse_scope {
11 SCOPE_PROTOCOL,
12 SCOPE_INTERFACE,
13 SCOPE_MESSAGE,
14 SCOPE_ENUM,
15 SCOPE_DESCRIPTION,
16};
17
18struct parser {
19 const char *path;
20 XML_Parser xml;
21 struct protocol *proto;
22 struct interface *iface;
23 struct message *msg;
24 struct enumdef *en;
25 enum parse_scope stack[32];
26 size_t depth;
27 struct description *desc;
28 char *text;
29 size_t textlen;
30 size_t textcap;
31};
32
33static const char *attr(const char **, const char *);
34static int parseint(const char *, const char *);
35static void pushscope(struct parser *, enum parse_scope);
36static void popscope(struct parser *);
37static bool inscope(const struct parser *, enum parse_scope);
38static void cleartext(struct parser *);
39static void appendtext(struct parser *, const char *, int);
40static char *normalizedtext(const char *);
41static void onstart(void *, const char *, const char **);
42static void onend(void *, const char *);
43static void ontext(void *, const XML_Char *, int);
44
45static const char *
46attr(const char **attrs, const char *name)
47{
48 size_t i;
49
50 for (i = 0; attrs && attrs[i]; i += 2) {
51 if (strcmp(attrs[i], name) == 0)
52 return attrs[i + 1];
53 }
54 return NULL;
55}
56
57static int
58parseint(const char *what, const char *s)
59{
60 char *end;
61 long v;
62
63 if (!s)
64 die("missing %s attribute", what);
65 v = strtol(s, &end, 10);
66 if (*s == '\0' || *end != '\0' || v < 0)
67 die("invalid %s: %s", what, s);
68 return (int)v;
69}
70
71static void
72pushscope(struct parser *p, enum parse_scope scope)
73{
74 if (p->depth == countof(p->stack))
75 die("%s: XML nesting too deep", p->path);
76 p->stack[p->depth++] = scope;
77}
78
79static void
80popscope(struct parser *p)
81{
82 if (p->depth > 0)
83 --p->depth;
84}
85
86static bool
87inscope(const struct parser *p, enum parse_scope scope)
88{
89 return p->depth > 0 && p->stack[p->depth - 1] == scope;
90}
91
92static void
93cleartext(struct parser *p)
94{
95 free(p->text);
96 p->text = NULL;
97 p->textlen = 0;
98 p->textcap = 0;
99}
100
101static void
102appendtext(struct parser *p, const char *s, int n)
103{
104 size_t need;
105
106 if (n <= 0)
107 return;
108 need = p->textlen + (size_t)n + 1;
109 if (need > p->textcap) {
110 p->textcap = p->textcap ? p->textcap * 2 : 128;
111 while (p->textcap < need)
112 p->textcap *= 2;
113 p->text = xreallocarray(p->text, p->textcap, sizeof(p->text[0]));
114 }
115 memcpy(p->text + p->textlen, s, (size_t)n);
116 p->textlen += (size_t)n;
117 p->text[p->textlen] = '\0';
118}
119
120static char *
121normalizedtext(const char *s)
122{
123 const char *start, *end, *lineend;
124 char *out;
125 size_t cap, len, n;
126
127 if (!s)
128 return NULL;
129 start = s;
130 while (*start == '\n' || *start == '\r' || *start == '\t' || *start == ' ')
131 ++start;
132 end = s + strlen(s);
133 while (end > start && (end[-1] == '\n' || end[-1] == '\r' || end[-1] == '\t' || end[-1] == ' '))
134 --end;
135 if (start == end)
136 return NULL;
137
138 cap = (size_t)(end - start) + 1;
139 out = xmalloc(cap);
140 len = 0;
141 while (start < end) {
142 while (start < end && (*start == '\n' || *start == '\r'))
143 ++start;
144 lineend = start;
145 while (lineend < end && *lineend != '\n' && *lineend != '\r')
146 ++lineend;
147 while (start < lineend && (*start == ' ' || *start == '\t'))
148 ++start;
149 while (lineend > start && (lineend[-1] == ' ' || lineend[-1] == '\t'))
150 --lineend;
151 n = (size_t)(lineend - start);
152 if (n > 0) {
153 if (len > 0)
154 out[len++] = '\n';
155 memcpy(out + len, start, n);
156 len += n;
157 }
158 start = lineend;
159 while (start < end && *start != '\n' && *start != '\r')
160 ++start;
161 }
162 if (len == 0) {
163 free(out);
164 return NULL;
165 }
166 out[len] = '\0';
167 return out;
168}
169
170static void
171onstart(void *data, const char *name, const char **attrs)
172{
173 struct parser *p = data;
174 const char *aname, *atype, *iface, *allow_null, *version, *type, *summary;
175
176 if (strcmp(name, "protocol") == 0) {
177 if (p->proto->name)
178 die("%s:%lu: duplicate protocol element",
179 p->path, XML_GetCurrentLineNumber(p->xml));
180 aname = attr(attrs, "name");
181 if (!aname)
182 die("%s:%lu: protocol missing name",
183 p->path, XML_GetCurrentLineNumber(p->xml));
184 p->proto->name = xstrdup(aname);
185 pushscope(p, SCOPE_PROTOCOL);
186 return;
187 }
188
189 if (strcmp(name, "interface") == 0 && inscope(p, SCOPE_PROTOCOL)) {
190 aname = attr(attrs, "name");
191 version = attr(attrs, "version");
192 if (!aname || !version)
193 die("%s:%lu: interface missing name/version",
194 p->path, XML_GetCurrentLineNumber(p->xml));
195 p->iface = proto_add_interface(p->proto, aname, parseint("version", version));
196 pushscope(p, SCOPE_INTERFACE);
197 return;
198 }
199
200 if ((strcmp(name, "request") == 0 || strcmp(name, "event") == 0) && inscope(p, SCOPE_INTERFACE)) {
201 aname = attr(attrs, "name");
202 type = attr(attrs, "type");
203 if (!aname)
204 die("%s:%lu: message missing name",
205 p->path, XML_GetCurrentLineNumber(p->xml));
206 p->msg = iface_add_message(p->iface,
207 strcmp(name, "request") == 0 ? MSG_REQUEST : MSG_EVENT, aname);
208 p->msg->destructor = type && strcmp(type, "destructor") == 0;
209 pushscope(p, SCOPE_MESSAGE);
210 return;
211 }
212
213 if (strcmp(name, "arg") == 0 && inscope(p, SCOPE_MESSAGE)) {
214 aname = attr(attrs, "name");
215 atype = attr(attrs, "type");
216 iface = attr(attrs, "interface");
217 allow_null = attr(attrs, "allow-null");
218 if (!aname || !atype)
219 die("%s:%lu: arg missing name/type",
220 p->path, XML_GetCurrentLineNumber(p->xml));
221 message_add_arg(p->msg, aname, atype, iface,
222 allow_null && strcmp(allow_null, "true") == 0);
223 return;
224 }
225
226 if (strcmp(name, "enum") == 0 && inscope(p, SCOPE_INTERFACE)) {
227 aname = attr(attrs, "name");
228 if (!aname)
229 die("%s:%lu: enum missing name",
230 p->path, XML_GetCurrentLineNumber(p->xml));
231 iface_add_enum(p->iface, aname);
232 p->en = iface_last_enum(p->iface);
233 pushscope(p, SCOPE_ENUM);
234 return;
235 }
236
237 if (strcmp(name, "description") == 0) {
238 if (inscope(p, SCOPE_MESSAGE))
239 p->desc = &p->msg->desc;
240 else if (inscope(p, SCOPE_ENUM))
241 p->desc = &p->en->desc;
242 else if (inscope(p, SCOPE_INTERFACE))
243 p->desc = &p->iface->desc;
244 else if (inscope(p, SCOPE_PROTOCOL))
245 p->desc = &p->proto->desc;
246 else
247 return;
248 summary = attr(attrs, "summary");
249 free(p->desc->summary);
250 p->desc->summary = summary ? xstrdup(summary) : NULL;
251 cleartext(p);
252 pushscope(p, SCOPE_DESCRIPTION);
253 return;
254 }
255}
256
257static void
258onend(void *data, const char *name)
259{
260 struct parser *p = data;
261
262 if (strcmp(name, "request") == 0 || strcmp(name, "event") == 0) {
263 if (inscope(p, SCOPE_MESSAGE)) {
264 p->msg = NULL;
265 popscope(p);
266 }
267 return;
268 }
269 if (strcmp(name, "enum") == 0) {
270 if (inscope(p, SCOPE_ENUM))
271 {
272 p->en = NULL;
273 popscope(p);
274 }
275 return;
276 }
277 if (strcmp(name, "description") == 0) {
278 if (inscope(p, SCOPE_DESCRIPTION)) {
279 free(p->desc->text);
280 p->desc->text = normalizedtext(p->text);
281 p->desc = NULL;
282 cleartext(p);
283 popscope(p);
284 }
285 return;
286 }
287 if (strcmp(name, "interface") == 0) {
288 if (inscope(p, SCOPE_INTERFACE)) {
289 p->iface = NULL;
290 popscope(p);
291 }
292 return;
293 }
294 if (strcmp(name, "protocol") == 0) {
295 if (inscope(p, SCOPE_PROTOCOL))
296 popscope(p);
297 return;
298 }
299}
300
301static void
302ontext(void *data, const XML_Char *s, int len)
303{
304 struct parser *p = data;
305
306 if (inscope(p, SCOPE_DESCRIPTION))
307 appendtext(p, s, len);
308}
309
310void
311parsefile(const char *path, struct protocol *proto)
312{
313 struct parser p;
314 FILE *fp;
315 void *buf;
316 int done;
317
318 memset(&p, 0, sizeof(p));
319 p.path = path;
320 p.proto = proto;
321 p.xml = XML_ParserCreate(NULL);
322 if (!p.xml)
323 die("XML_ParserCreate:");
324 XML_SetUserData(p.xml, &p);
325 XML_SetElementHandler(p.xml, onstart, onend);
326 XML_SetCharacterDataHandler(p.xml, ontext);
327
328 fp = fopen(path, "rb");
329 if (!fp) {
330 XML_ParserFree(p.xml);
331 die("open %s:", path);
332 }
333
334 done = 0;
335 while (!done) {
336 size_t nread;
337
338 buf = XML_GetBuffer(p.xml, 4096);
339 if (!buf)
340 die("%s: XML_GetBuffer failed", path);
341 nread = fread(buf, 1, 4096, fp);
342 done = nread < 4096;
343 if (ferror(fp))
344 die("read %s:", path);
345 if (XML_ParseBuffer(p.xml, (int)nread, done) == XML_STATUS_ERROR) {
346 die("%s:%lu:%lu: %s", path,
347 XML_GetCurrentLineNumber(p.xml),
348 XML_GetCurrentColumnNumber(p.xml),
349 XML_ErrorString(XML_GetErrorCode(p.xml)));
350 }
351 }
352
353 fclose(fp);
354 XML_ParserFree(p.xml);
355 cleartext(&p);
356
357 if (!proto->name)
358 die("%s: missing protocol element", path);
359 proto_resolve(proto);
360}