1 | /* |
2 | * Copyright © 2008-2011 Kristian Høgsberg |
3 | * Copyright © 2011 Intel Corporation |
4 | * Copyright © 2015 Red Hat, Inc. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining |
7 | * a copy of this software and associated documentation files (the |
8 | * "Software"), to deal in the Software without restriction, including |
9 | * without limitation the rights to use, copy, modify, merge, publish, |
10 | * distribute, sublicense, and/or sell copies of the Software, and to |
11 | * permit persons to whom the Software is furnished to do so, subject to |
12 | * the following conditions: |
13 | * |
14 | * The above copyright notice and this permission notice (including the |
15 | * next paragraph) shall be included in all copies or substantial |
16 | * portions of the Software. |
17 | * |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
22 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
23 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
24 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
25 | * SOFTWARE. |
26 | */ |
27 | |
28 | #include "wayland-version.h" |
29 | |
30 | #include <stdbool.h> |
31 | #include <stdio.h> |
32 | #include <stdarg.h> |
33 | #include <stdint.h> |
34 | #include <string.h> |
35 | #include <errno.h> |
36 | #include <ctype.h> |
37 | #include <getopt.h> |
38 | #include <limits.h> |
39 | #include <unistd.h> |
40 | |
41 | #if HAVE_LIBXML |
42 | #include <libxml/parser.h> |
43 | |
44 | /* Embedded wayland.dtd file */ |
45 | /* static const char wayland_dtd[]; wayland.dtd */ |
46 | #include "wayland.dtd.h" |
47 | #endif |
48 | |
49 | /* Expat must be included after libxml as both want to declare XMLCALL; see |
50 | * the Git commit that 'git blame' for this comment points to for more. */ |
51 | #include <expat.h> |
52 | |
53 | #include "wayland-util.h" |
54 | |
55 | #define PROGRAM_NAME "wayland-scanner" |
56 | |
57 | enum side { |
58 | CLIENT, |
59 | SERVER, |
60 | }; |
61 | |
62 | enum visibility { |
63 | PRIVATE, |
64 | PUBLIC, |
65 | }; |
66 | |
67 | static int |
68 | usage(int ret) |
69 | { |
70 | fprintf(stderr, format: "usage: %s [OPTION] [client-header|server-header|private-code|public-code]" |
71 | " [input_file output_file]\n" , PROGRAM_NAME); |
72 | fprintf(stderr, format: "\n" ); |
73 | fprintf(stderr, format: "Converts XML protocol descriptions supplied on " |
74 | "stdin or input file to client\n" |
75 | "headers, server headers, or protocol marshalling code.\n\n" |
76 | "Use \"public-code\" only if the marshalling code will be public - " |
77 | "aka DSO will export it while other components will be using it.\n" |
78 | "Using \"private-code\" is strongly recommended.\n\n" ); |
79 | fprintf(stderr, format: "options:\n" ); |
80 | fprintf(stderr, format: " -h, --help display this help and exit.\n" |
81 | " -v, --version print the wayland library version that\n" |
82 | " the scanner was built against.\n" |
83 | " -c, --include-core-only include the core version of the headers,\n" |
84 | " that is e.g. wayland-client-core.h instead\n" |
85 | " of wayland-client.h.\n" |
86 | " -s, --strict exit immediately with an error if DTD\n" |
87 | " verification fails.\n" ); |
88 | exit(status: ret); |
89 | } |
90 | |
91 | static int |
92 | scanner_version(int ret) |
93 | { |
94 | fprintf(stderr, format: "%s %s\n" , PROGRAM_NAME, WAYLAND_VERSION); |
95 | exit(status: ret); |
96 | } |
97 | |
98 | static bool |
99 | is_dtd_valid(FILE *input, const char *filename) |
100 | { |
101 | bool rc = true; |
102 | #if HAVE_LIBXML |
103 | xmlParserCtxtPtr ctx = NULL; |
104 | xmlDocPtr doc = NULL; |
105 | xmlDtdPtr dtd = NULL; |
106 | xmlValidCtxtPtr dtdctx; |
107 | xmlParserInputBufferPtr buffer; |
108 | int fd = fileno(stream: input); |
109 | |
110 | dtdctx = xmlNewValidCtxt(); |
111 | ctx = xmlNewParserCtxt(); |
112 | if (!ctx || !dtdctx) |
113 | abort(); |
114 | |
115 | buffer = xmlParserInputBufferCreateMem(wayland_dtd, |
116 | sizeof(wayland_dtd), |
117 | XML_CHAR_ENCODING_UTF8); |
118 | if (!buffer) { |
119 | fprintf(stderr, format: "Failed to init buffer for DTD.\n" ); |
120 | abort(); |
121 | } |
122 | |
123 | dtd = xmlIOParseDTD(NULL, input: buffer, enc: XML_CHAR_ENCODING_UTF8); |
124 | if (!dtd) { |
125 | fprintf(stderr, format: "Failed to parse DTD.\n" ); |
126 | abort(); |
127 | } |
128 | |
129 | doc = xmlCtxtReadFd(ctxt: ctx, fd, URL: filename, NULL, options: 0); |
130 | if (!doc) { |
131 | fprintf(stderr, format: "Failed to read XML\n" ); |
132 | abort(); |
133 | } |
134 | |
135 | rc = xmlValidateDtd(ctxt: dtdctx, doc, dtd); |
136 | xmlFreeDoc(cur: doc); |
137 | xmlFreeParserCtxt(ctxt: ctx); |
138 | xmlFreeDtd(cur: dtd); |
139 | xmlFreeValidCtxt(dtdctx); |
140 | /* xmlIOParseDTD consumes buffer */ |
141 | |
142 | if (lseek(fd: fd, offset: 0, SEEK_SET) != 0) { |
143 | fprintf(stderr, format: "Failed to reset fd, output would be garbage.\n" ); |
144 | abort(); |
145 | } |
146 | #endif |
147 | return rc; |
148 | } |
149 | |
150 | #define XML_BUFFER_SIZE 4096 |
151 | |
152 | struct location { |
153 | const char *filename; |
154 | int line_number; |
155 | }; |
156 | |
157 | struct description { |
158 | char *summary; |
159 | char *text; |
160 | }; |
161 | |
162 | struct protocol { |
163 | char *name; |
164 | char *uppercase_name; |
165 | struct wl_list interface_list; |
166 | int type_index; |
167 | int null_run_length; |
168 | char *copyright; |
169 | struct description *description; |
170 | bool ; |
171 | }; |
172 | |
173 | struct interface { |
174 | struct location loc; |
175 | char *name; |
176 | char *uppercase_name; |
177 | int version; |
178 | int since; |
179 | struct wl_list request_list; |
180 | struct wl_list event_list; |
181 | struct wl_list enumeration_list; |
182 | struct wl_list link; |
183 | struct description *description; |
184 | }; |
185 | |
186 | struct message { |
187 | struct location loc; |
188 | char *name; |
189 | char *uppercase_name; |
190 | struct wl_list arg_list; |
191 | struct wl_list link; |
192 | int arg_count; |
193 | int new_id_count; |
194 | int type_index; |
195 | int all_null; |
196 | int destructor; |
197 | int since; |
198 | struct description *description; |
199 | }; |
200 | |
201 | enum arg_type { |
202 | NEW_ID, |
203 | INT, |
204 | UNSIGNED, |
205 | FIXED, |
206 | STRING, |
207 | OBJECT, |
208 | ARRAY, |
209 | FD |
210 | }; |
211 | |
212 | struct arg { |
213 | char *name; |
214 | enum arg_type type; |
215 | int nullable; |
216 | char *interface_name; |
217 | struct wl_list link; |
218 | char *summary; |
219 | char *enumeration_name; |
220 | }; |
221 | |
222 | struct enumeration { |
223 | char *name; |
224 | char *uppercase_name; |
225 | struct wl_list entry_list; |
226 | struct wl_list link; |
227 | struct description *description; |
228 | bool bitfield; |
229 | int since; |
230 | }; |
231 | |
232 | struct entry { |
233 | char *name; |
234 | char *uppercase_name; |
235 | char *value; |
236 | char *summary; |
237 | int since; |
238 | struct wl_list link; |
239 | struct description *description; |
240 | }; |
241 | |
242 | struct parse_context { |
243 | struct location loc; |
244 | XML_Parser parser; |
245 | struct protocol *protocol; |
246 | struct interface *interface; |
247 | struct message *message; |
248 | struct enumeration *enumeration; |
249 | struct entry *entry; |
250 | struct description *description; |
251 | char character_data[8192]; |
252 | unsigned int character_data_length; |
253 | }; |
254 | |
255 | enum identifier_role { |
256 | STANDALONE_IDENT, |
257 | TRAILING_IDENT |
258 | }; |
259 | |
260 | static void * |
261 | fail_on_null(void *p) |
262 | { |
263 | if (p == NULL) { |
264 | fprintf(stderr, format: "%s: out of memory\n" , PROGRAM_NAME); |
265 | exit(EXIT_FAILURE); |
266 | } |
267 | |
268 | return p; |
269 | } |
270 | |
271 | static void * |
272 | zalloc(size_t s) |
273 | { |
274 | return calloc(nmemb: s, size: 1); |
275 | } |
276 | |
277 | static void * |
278 | xzalloc(size_t s) |
279 | { |
280 | return fail_on_null(p: zalloc(s)); |
281 | } |
282 | |
283 | static char * |
284 | xstrdup(const char *s) |
285 | { |
286 | return fail_on_null(p: strdup(s: s)); |
287 | } |
288 | |
289 | static char * |
290 | uppercase_dup(const char *src) |
291 | { |
292 | char *u; |
293 | int i; |
294 | |
295 | u = xstrdup(s: src); |
296 | for (i = 0; u[i]; i++) |
297 | u[i] = toupper(u[i]); |
298 | u[i] = '\0'; |
299 | |
300 | return u; |
301 | } |
302 | |
303 | static const char *indent(int n) |
304 | { |
305 | const char *whitespace[] = { |
306 | "\t\t\t\t\t\t\t\t\t\t\t\t" , |
307 | "\t\t\t\t\t\t\t\t\t\t\t\t " , |
308 | "\t\t\t\t\t\t\t\t\t\t\t\t " , |
309 | "\t\t\t\t\t\t\t\t\t\t\t\t " , |
310 | "\t\t\t\t\t\t\t\t\t\t\t\t " , |
311 | "\t\t\t\t\t\t\t\t\t\t\t\t " , |
312 | "\t\t\t\t\t\t\t\t\t\t\t\t " , |
313 | "\t\t\t\t\t\t\t\t\t\t\t\t " |
314 | }; |
315 | |
316 | return whitespace[n % 8] + 12 - n / 8; |
317 | } |
318 | |
319 | static void |
320 | desc_dump(char *desc, const char *fmt, ...) WL_PRINTF(2, 3); |
321 | |
322 | static void |
323 | desc_dump(char *desc, const char *fmt, ...) |
324 | { |
325 | va_list ap; |
326 | char buf[128], hang; |
327 | int col, i, j, k, startcol, newlines; |
328 | |
329 | va_start(ap, fmt); |
330 | vsnprintf(s: buf, maxlen: sizeof buf, format: fmt, arg: ap); |
331 | va_end(ap); |
332 | |
333 | for (i = 0, col = 0; buf[i] != '*'; i++) { |
334 | if (buf[i] == '\t') |
335 | col = (col + 8) & ~7; |
336 | else |
337 | col++; |
338 | } |
339 | |
340 | printf(format: "%s" , buf); |
341 | |
342 | if (!desc) { |
343 | printf(format: "(none)\n" ); |
344 | return; |
345 | } |
346 | |
347 | startcol = col; |
348 | col += strlen(s: &buf[i]); |
349 | if (col - startcol > 2) |
350 | hang = '\t'; |
351 | else |
352 | hang = ' '; |
353 | |
354 | for (i = 0; desc[i]; ) { |
355 | k = i; |
356 | newlines = 0; |
357 | while (desc[i] && isspace(desc[i])) { |
358 | if (desc[i] == '\n') |
359 | newlines++; |
360 | i++; |
361 | } |
362 | if (!desc[i]) |
363 | break; |
364 | |
365 | j = i; |
366 | while (desc[i] && !isspace(desc[i])) |
367 | i++; |
368 | |
369 | if (newlines > 1) |
370 | printf(format: "\n%s*" , indent(n: startcol)); |
371 | if (newlines > 1 || col + i - j > 72) { |
372 | printf(format: "\n%s*%c" , indent(n: startcol), hang); |
373 | col = startcol; |
374 | } |
375 | |
376 | if (col > startcol && k > 0) |
377 | col += printf(format: " " ); |
378 | col += printf(format: "%.*s" , i - j, &desc[j]); |
379 | } |
380 | putchar(c: '\n'); |
381 | } |
382 | |
383 | static void __attribute__ ((noreturn)) |
384 | fail(struct location *loc, const char *msg, ...) |
385 | { |
386 | va_list ap; |
387 | |
388 | va_start(ap, msg); |
389 | fprintf(stderr, format: "%s:%d: error: " , |
390 | loc->filename, loc->line_number); |
391 | vfprintf(stderr, format: msg, arg: ap); |
392 | fprintf(stderr, format: "\n" ); |
393 | va_end(ap); |
394 | exit(EXIT_FAILURE); |
395 | } |
396 | |
397 | static void |
398 | warn(struct location *loc, const char *msg, ...) |
399 | { |
400 | va_list ap; |
401 | |
402 | va_start(ap, msg); |
403 | fprintf(stderr, format: "%s:%d: warning: " , |
404 | loc->filename, loc->line_number); |
405 | vfprintf(stderr, format: msg, arg: ap); |
406 | fprintf(stderr, format: "\n" ); |
407 | va_end(ap); |
408 | } |
409 | |
410 | static bool |
411 | is_nullable_type(struct arg *arg) |
412 | { |
413 | switch (arg->type) { |
414 | /* Strings, objects, and arrays are possibly nullable */ |
415 | case STRING: |
416 | case OBJECT: |
417 | case NEW_ID: |
418 | case ARRAY: |
419 | return true; |
420 | default: |
421 | return false; |
422 | } |
423 | } |
424 | |
425 | static struct message * |
426 | create_message(struct location loc, const char *name) |
427 | { |
428 | struct message *message; |
429 | |
430 | message = xzalloc(s: sizeof *message); |
431 | message->loc = loc; |
432 | message->name = xstrdup(s: name); |
433 | message->uppercase_name = uppercase_dup(src: name); |
434 | wl_list_init(list: &message->arg_list); |
435 | |
436 | return message; |
437 | } |
438 | |
439 | static void |
440 | free_arg(struct arg *arg) |
441 | { |
442 | free(ptr: arg->name); |
443 | free(ptr: arg->interface_name); |
444 | free(ptr: arg->summary); |
445 | free(ptr: arg->enumeration_name); |
446 | free(ptr: arg); |
447 | } |
448 | |
449 | static struct arg * |
450 | create_arg(const char *name) |
451 | { |
452 | struct arg *arg; |
453 | |
454 | arg = xzalloc(s: sizeof *arg); |
455 | arg->name = xstrdup(s: name); |
456 | |
457 | return arg; |
458 | } |
459 | |
460 | static bool |
461 | set_arg_type(struct arg *arg, const char *type) |
462 | { |
463 | if (strcmp(s1: type, s2: "int" ) == 0) |
464 | arg->type = INT; |
465 | else if (strcmp(s1: type, s2: "uint" ) == 0) |
466 | arg->type = UNSIGNED; |
467 | else if (strcmp(s1: type, s2: "fixed" ) == 0) |
468 | arg->type = FIXED; |
469 | else if (strcmp(s1: type, s2: "string" ) == 0) |
470 | arg->type = STRING; |
471 | else if (strcmp(s1: type, s2: "array" ) == 0) |
472 | arg->type = ARRAY; |
473 | else if (strcmp(s1: type, s2: "fd" ) == 0) |
474 | arg->type = FD; |
475 | else if (strcmp(s1: type, s2: "new_id" ) == 0) |
476 | arg->type = NEW_ID; |
477 | else if (strcmp(s1: type, s2: "object" ) == 0) |
478 | arg->type = OBJECT; |
479 | else |
480 | return false; |
481 | |
482 | return true; |
483 | } |
484 | |
485 | static void |
486 | free_description(struct description *desc) |
487 | { |
488 | if (!desc) |
489 | return; |
490 | |
491 | free(ptr: desc->summary); |
492 | free(ptr: desc->text); |
493 | |
494 | free(ptr: desc); |
495 | } |
496 | |
497 | static void |
498 | free_message(struct message *message) |
499 | { |
500 | struct arg *a, *a_next; |
501 | |
502 | free(ptr: message->name); |
503 | free(ptr: message->uppercase_name); |
504 | free_description(desc: message->description); |
505 | |
506 | wl_list_for_each_safe(a, a_next, &message->arg_list, link) |
507 | free_arg(arg: a); |
508 | |
509 | free(ptr: message); |
510 | } |
511 | |
512 | static struct enumeration * |
513 | create_enumeration(const char *name) |
514 | { |
515 | struct enumeration *enumeration; |
516 | |
517 | enumeration = xzalloc(s: sizeof *enumeration); |
518 | enumeration->name = xstrdup(s: name); |
519 | enumeration->uppercase_name = uppercase_dup(src: name); |
520 | enumeration->since = 1; |
521 | |
522 | wl_list_init(list: &enumeration->entry_list); |
523 | |
524 | return enumeration; |
525 | } |
526 | |
527 | static struct entry * |
528 | create_entry(const char *name, const char *value) |
529 | { |
530 | struct entry *entry; |
531 | |
532 | entry = xzalloc(s: sizeof *entry); |
533 | entry->name = xstrdup(s: name); |
534 | entry->uppercase_name = uppercase_dup(src: name); |
535 | entry->value = xstrdup(s: value); |
536 | |
537 | return entry; |
538 | } |
539 | |
540 | static void |
541 | free_entry(struct entry *entry) |
542 | { |
543 | free(ptr: entry->name); |
544 | free(ptr: entry->uppercase_name); |
545 | free(ptr: entry->value); |
546 | free(ptr: entry->summary); |
547 | free_description(desc: entry->description); |
548 | |
549 | free(ptr: entry); |
550 | } |
551 | |
552 | static void |
553 | free_enumeration(struct enumeration *enumeration) |
554 | { |
555 | struct entry *e, *e_next; |
556 | |
557 | free(ptr: enumeration->name); |
558 | free(ptr: enumeration->uppercase_name); |
559 | free_description(desc: enumeration->description); |
560 | |
561 | wl_list_for_each_safe(e, e_next, &enumeration->entry_list, link) |
562 | free_entry(entry: e); |
563 | |
564 | free(ptr: enumeration); |
565 | } |
566 | |
567 | static struct interface * |
568 | create_interface(struct location loc, const char *name, int version) |
569 | { |
570 | struct interface *interface; |
571 | |
572 | interface = xzalloc(s: sizeof *interface); |
573 | interface->loc = loc; |
574 | interface->name = xstrdup(s: name); |
575 | interface->uppercase_name = uppercase_dup(src: name); |
576 | interface->version = version; |
577 | interface->since = 1; |
578 | wl_list_init(list: &interface->request_list); |
579 | wl_list_init(list: &interface->event_list); |
580 | wl_list_init(list: &interface->enumeration_list); |
581 | |
582 | return interface; |
583 | } |
584 | |
585 | static void |
586 | free_interface(struct interface *interface) |
587 | { |
588 | struct message *m, *next_m; |
589 | struct enumeration *e, *next_e; |
590 | |
591 | free(ptr: interface->name); |
592 | free(ptr: interface->uppercase_name); |
593 | free_description(desc: interface->description); |
594 | |
595 | wl_list_for_each_safe(m, next_m, &interface->request_list, link) |
596 | free_message(message: m); |
597 | wl_list_for_each_safe(m, next_m, &interface->event_list, link) |
598 | free_message(message: m); |
599 | wl_list_for_each_safe(e, next_e, &interface->enumeration_list, link) |
600 | free_enumeration(enumeration: e); |
601 | |
602 | free(ptr: interface); |
603 | } |
604 | |
605 | /* Convert string to unsigned integer |
606 | * |
607 | * Parses a non-negative base-10 number from the given string. If the |
608 | * specified string is blank, contains non-numerical characters, is out |
609 | * of range, or results in a negative number, -1 is returned to indicate |
610 | * an error. |
611 | * |
612 | * Upon error, this routine does not modify or set errno. |
613 | * |
614 | * Returns -1 on error, or a non-negative integer on success |
615 | */ |
616 | static int |
617 | strtouint(const char *str) |
618 | { |
619 | long int ret; |
620 | char *end; |
621 | int prev_errno = errno; |
622 | |
623 | errno = 0; |
624 | ret = strtol(nptr: str, endptr: &end, base: 10); |
625 | if (errno != 0 || end == str || *end != '\0') |
626 | return -1; |
627 | |
628 | /* check range */ |
629 | if (ret < 0 || ret > INT_MAX) { |
630 | return -1; |
631 | } |
632 | |
633 | errno = prev_errno; |
634 | return (int)ret; |
635 | } |
636 | |
637 | /* Check that the provided string will produce valid "C" identifiers. |
638 | * |
639 | * If the string will form the prefix of an identifier in the |
640 | * generated C code, then it must match [_a-zA-Z][_0-9a-zA-Z]*. |
641 | * |
642 | * If the string will form the suffix of an identifier, then |
643 | * it must match [_0-9a-zA-Z]+. |
644 | * |
645 | * Unicode characters or escape sequences are not permitted, |
646 | * since not all C compilers support them. |
647 | * |
648 | * If the above conditions are not met, then fail() |
649 | */ |
650 | static void |
651 | validate_identifier(struct location *loc, |
652 | const char *str, |
653 | enum identifier_role role) |
654 | { |
655 | const char *scan; |
656 | |
657 | if (!*str) { |
658 | fail(loc, msg: "element name is empty" ); |
659 | } |
660 | |
661 | for (scan = str; *scan; scan++) { |
662 | char c = *scan; |
663 | |
664 | /* we do not use the locale-dependent `isalpha` */ |
665 | bool is_alpha = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); |
666 | bool is_digit = c >= '0' && c <= '9'; |
667 | bool leading_char = (scan == str) && role == STANDALONE_IDENT; |
668 | |
669 | if (is_alpha || c == '_' || (!leading_char && is_digit)) |
670 | continue; |
671 | |
672 | if (role == TRAILING_IDENT) |
673 | fail(loc, |
674 | msg: "'%s' is not a valid trailing identifier part" , str); |
675 | else |
676 | fail(loc, |
677 | msg: "'%s' is not a valid standalone identifier" , str); |
678 | } |
679 | } |
680 | |
681 | static int |
682 | version_from_since(struct parse_context *ctx, const char *since) |
683 | { |
684 | int version; |
685 | |
686 | if (since != NULL) { |
687 | version = strtouint(str: since); |
688 | if (version == -1) { |
689 | fail(loc: &ctx->loc, msg: "invalid integer (%s)\n" , since); |
690 | } else if (version > ctx->interface->version) { |
691 | fail(loc: &ctx->loc, msg: "since (%u) larger than version (%u)\n" , |
692 | version, ctx->interface->version); |
693 | } |
694 | } else { |
695 | version = 1; |
696 | } |
697 | |
698 | |
699 | return version; |
700 | } |
701 | |
702 | static void |
703 | start_element(void *data, const char *element_name, const char **atts) |
704 | { |
705 | struct parse_context *ctx = data; |
706 | struct interface *interface; |
707 | struct message *message; |
708 | struct arg *arg; |
709 | struct enumeration *enumeration; |
710 | struct entry *entry; |
711 | struct description *description = NULL; |
712 | const char *name = NULL; |
713 | const char *type = NULL; |
714 | const char *interface_name = NULL; |
715 | const char *value = NULL; |
716 | const char *summary = NULL; |
717 | const char *since = NULL; |
718 | const char *allow_null = NULL; |
719 | const char *enumeration_name = NULL; |
720 | const char *bitfield = NULL; |
721 | int i, version = 0; |
722 | |
723 | ctx->loc.line_number = XML_GetCurrentLineNumber(parser: ctx->parser); |
724 | for (i = 0; atts[i]; i += 2) { |
725 | if (strcmp(s1: atts[i], s2: "name" ) == 0) |
726 | name = atts[i + 1]; |
727 | if (strcmp(s1: atts[i], s2: "version" ) == 0) { |
728 | version = strtouint(str: atts[i + 1]); |
729 | if (version == -1) |
730 | fail(loc: &ctx->loc, msg: "wrong version (%s)" , atts[i + 1]); |
731 | } |
732 | if (strcmp(s1: atts[i], s2: "type" ) == 0) |
733 | type = atts[i + 1]; |
734 | if (strcmp(s1: atts[i], s2: "value" ) == 0) |
735 | value = atts[i + 1]; |
736 | if (strcmp(s1: atts[i], s2: "interface" ) == 0) |
737 | interface_name = atts[i + 1]; |
738 | if (strcmp(s1: atts[i], s2: "summary" ) == 0) |
739 | summary = atts[i + 1]; |
740 | if (strcmp(s1: atts[i], s2: "since" ) == 0) |
741 | since = atts[i + 1]; |
742 | if (strcmp(s1: atts[i], s2: "allow-null" ) == 0) |
743 | allow_null = atts[i + 1]; |
744 | if (strcmp(s1: atts[i], s2: "enum" ) == 0) |
745 | enumeration_name = atts[i + 1]; |
746 | if (strcmp(s1: atts[i], s2: "bitfield" ) == 0) |
747 | bitfield = atts[i + 1]; |
748 | } |
749 | |
750 | ctx->character_data_length = 0; |
751 | if (strcmp(s1: element_name, s2: "protocol" ) == 0) { |
752 | if (name == NULL) |
753 | fail(loc: &ctx->loc, msg: "no protocol name given" ); |
754 | |
755 | validate_identifier(loc: &ctx->loc, str: name, role: STANDALONE_IDENT); |
756 | ctx->protocol->name = xstrdup(s: name); |
757 | ctx->protocol->uppercase_name = uppercase_dup(src: name); |
758 | } else if (strcmp(s1: element_name, s2: "copyright" ) == 0) { |
759 | |
760 | } else if (strcmp(s1: element_name, s2: "interface" ) == 0) { |
761 | if (name == NULL) |
762 | fail(loc: &ctx->loc, msg: "no interface name given" ); |
763 | |
764 | if (version == 0) |
765 | fail(loc: &ctx->loc, msg: "no interface version given" ); |
766 | |
767 | validate_identifier(loc: &ctx->loc, str: name, role: STANDALONE_IDENT); |
768 | interface = create_interface(loc: ctx->loc, name, version); |
769 | ctx->interface = interface; |
770 | wl_list_insert(list: ctx->protocol->interface_list.prev, |
771 | elm: &interface->link); |
772 | } else if (strcmp(s1: element_name, s2: "request" ) == 0 || |
773 | strcmp(s1: element_name, s2: "event" ) == 0) { |
774 | if (name == NULL) |
775 | fail(loc: &ctx->loc, msg: "no request name given" ); |
776 | |
777 | validate_identifier(loc: &ctx->loc, str: name, role: STANDALONE_IDENT); |
778 | message = create_message(loc: ctx->loc, name); |
779 | |
780 | if (strcmp(s1: element_name, s2: "request" ) == 0) |
781 | wl_list_insert(list: ctx->interface->request_list.prev, |
782 | elm: &message->link); |
783 | else |
784 | wl_list_insert(list: ctx->interface->event_list.prev, |
785 | elm: &message->link); |
786 | |
787 | if (type != NULL && strcmp(s1: type, s2: "destructor" ) == 0) |
788 | message->destructor = 1; |
789 | |
790 | version = version_from_since(ctx, since); |
791 | |
792 | if (version < ctx->interface->since) |
793 | warn(loc: &ctx->loc, msg: "since version not increasing\n" ); |
794 | ctx->interface->since = version; |
795 | message->since = version; |
796 | |
797 | if (strcmp(s1: name, s2: "destroy" ) == 0 && !message->destructor) |
798 | fail(loc: &ctx->loc, msg: "destroy request should be destructor type" ); |
799 | |
800 | ctx->message = message; |
801 | } else if (strcmp(s1: element_name, s2: "arg" ) == 0) { |
802 | if (name == NULL) |
803 | fail(loc: &ctx->loc, msg: "no argument name given" ); |
804 | |
805 | validate_identifier(loc: &ctx->loc, str: name, role: STANDALONE_IDENT); |
806 | arg = create_arg(name); |
807 | if (!set_arg_type(arg, type)) |
808 | fail(loc: &ctx->loc, msg: "unknown type (%s)" , type); |
809 | |
810 | switch (arg->type) { |
811 | case NEW_ID: |
812 | ctx->message->new_id_count++; |
813 | /* fallthrough */ |
814 | case OBJECT: |
815 | if (interface_name) { |
816 | validate_identifier(loc: &ctx->loc, |
817 | str: interface_name, |
818 | role: STANDALONE_IDENT); |
819 | arg->interface_name = xstrdup(s: interface_name); |
820 | } |
821 | break; |
822 | default: |
823 | if (interface_name != NULL) |
824 | fail(loc: &ctx->loc, msg: "interface attribute not allowed for type %s" , type); |
825 | break; |
826 | } |
827 | |
828 | if (allow_null) { |
829 | if (strcmp(s1: allow_null, s2: "true" ) == 0) |
830 | arg->nullable = 1; |
831 | else if (strcmp(s1: allow_null, s2: "false" ) != 0) |
832 | fail(loc: &ctx->loc, |
833 | msg: "invalid value for allow-null attribute (%s)" , |
834 | allow_null); |
835 | |
836 | if (!is_nullable_type(arg)) |
837 | fail(loc: &ctx->loc, |
838 | msg: "allow-null is only valid for objects, strings, and arrays" ); |
839 | } |
840 | |
841 | if (enumeration_name == NULL || strcmp(s1: enumeration_name, s2: "" ) == 0) |
842 | arg->enumeration_name = NULL; |
843 | else |
844 | arg->enumeration_name = xstrdup(s: enumeration_name); |
845 | |
846 | if (summary) |
847 | arg->summary = xstrdup(s: summary); |
848 | |
849 | wl_list_insert(list: ctx->message->arg_list.prev, elm: &arg->link); |
850 | ctx->message->arg_count++; |
851 | } else if (strcmp(s1: element_name, s2: "enum" ) == 0) { |
852 | if (name == NULL) |
853 | fail(loc: &ctx->loc, msg: "no enum name given" ); |
854 | |
855 | validate_identifier(loc: &ctx->loc, str: name, role: TRAILING_IDENT); |
856 | enumeration = create_enumeration(name); |
857 | |
858 | if (bitfield == NULL || strcmp(s1: bitfield, s2: "false" ) == 0) |
859 | enumeration->bitfield = false; |
860 | else if (strcmp(s1: bitfield, s2: "true" ) == 0) |
861 | enumeration->bitfield = true; |
862 | else |
863 | fail(loc: &ctx->loc, |
864 | msg: "invalid value (%s) for bitfield attribute (only true/false are accepted)" , |
865 | bitfield); |
866 | |
867 | wl_list_insert(list: ctx->interface->enumeration_list.prev, |
868 | elm: &enumeration->link); |
869 | |
870 | ctx->enumeration = enumeration; |
871 | } else if (strcmp(s1: element_name, s2: "entry" ) == 0) { |
872 | if (name == NULL) |
873 | fail(loc: &ctx->loc, msg: "no entry name given" ); |
874 | |
875 | validate_identifier(loc: &ctx->loc, str: name, role: TRAILING_IDENT); |
876 | entry = create_entry(name, value); |
877 | version = version_from_since(ctx, since); |
878 | |
879 | if (version < ctx->enumeration->since) |
880 | warn(loc: &ctx->loc, msg: "since version not increasing\n" ); |
881 | ctx->enumeration->since = version; |
882 | entry->since = version; |
883 | |
884 | if (summary) |
885 | entry->summary = xstrdup(s: summary); |
886 | else |
887 | entry->summary = NULL; |
888 | wl_list_insert(list: ctx->enumeration->entry_list.prev, |
889 | elm: &entry->link); |
890 | ctx->entry = entry; |
891 | } else if (strcmp(s1: element_name, s2: "description" ) == 0) { |
892 | if (summary == NULL) |
893 | fail(loc: &ctx->loc, msg: "description without summary" ); |
894 | |
895 | description = xzalloc(s: sizeof *description); |
896 | description->summary = xstrdup(s: summary); |
897 | |
898 | if (ctx->message) |
899 | ctx->message->description = description; |
900 | else if (ctx->entry) |
901 | ctx->entry->description = description; |
902 | else if (ctx->enumeration) |
903 | ctx->enumeration->description = description; |
904 | else if (ctx->interface) |
905 | ctx->interface->description = description; |
906 | else |
907 | ctx->protocol->description = description; |
908 | ctx->description = description; |
909 | } |
910 | } |
911 | |
912 | static struct enumeration * |
913 | find_enumeration(struct protocol *protocol, |
914 | struct interface *interface, |
915 | char *enum_attribute) |
916 | { |
917 | struct interface *i; |
918 | struct enumeration *e; |
919 | char *enum_name; |
920 | uint32_t idx = 0, j; |
921 | |
922 | for (j = 0; j + 1 < strlen(s: enum_attribute); j++) { |
923 | if (enum_attribute[j] == '.') { |
924 | idx = j; |
925 | } |
926 | } |
927 | |
928 | if (idx > 0) { |
929 | enum_name = enum_attribute + idx + 1; |
930 | |
931 | wl_list_for_each(i, &protocol->interface_list, link) |
932 | if (strncmp(s1: i->name, s2: enum_attribute, n: idx) == 0) |
933 | wl_list_for_each(e, &i->enumeration_list, link) |
934 | if (strcmp(s1: e->name, s2: enum_name) == 0) |
935 | return e; |
936 | } else if (interface) { |
937 | enum_name = enum_attribute; |
938 | |
939 | wl_list_for_each(e, &interface->enumeration_list, link) |
940 | if (strcmp(s1: e->name, s2: enum_name) == 0) |
941 | return e; |
942 | } |
943 | |
944 | return NULL; |
945 | } |
946 | |
947 | static void |
948 | verify_arguments(struct parse_context *ctx, |
949 | struct interface *interface, |
950 | struct wl_list *messages, |
951 | struct wl_list *enumerations) |
952 | { |
953 | struct message *m; |
954 | wl_list_for_each(m, messages, link) { |
955 | struct arg *a; |
956 | wl_list_for_each(a, &m->arg_list, link) { |
957 | struct enumeration *e; |
958 | |
959 | if (!a->enumeration_name) |
960 | continue; |
961 | |
962 | |
963 | e = find_enumeration(protocol: ctx->protocol, interface, |
964 | enum_attribute: a->enumeration_name); |
965 | |
966 | switch (a->type) { |
967 | case INT: |
968 | if (e && e->bitfield) |
969 | fail(loc: &ctx->loc, |
970 | msg: "bitfield-style enum must only be referenced by uint" ); |
971 | break; |
972 | case UNSIGNED: |
973 | break; |
974 | default: |
975 | fail(loc: &ctx->loc, |
976 | msg: "enumeration-style argument has wrong type" ); |
977 | } |
978 | } |
979 | } |
980 | |
981 | } |
982 | |
983 | #ifndef HAVE_STRNDUP |
984 | char * |
985 | strndup(const char *s, size_t size) |
986 | { |
987 | char *r = malloc(size: size + 1); |
988 | strncpy(dest: r, src: s, n: size); |
989 | r[size] = '\0'; |
990 | return r; |
991 | } |
992 | #endif |
993 | |
994 | static void |
995 | end_element(void *data, const XML_Char *name) |
996 | { |
997 | struct parse_context *ctx = data; |
998 | |
999 | if (strcmp(s1: name, s2: "copyright" ) == 0) { |
1000 | ctx->protocol->copyright = |
1001 | strndup(s: ctx->character_data, |
1002 | size: ctx->character_data_length); |
1003 | } else if (strcmp(s1: name, s2: "description" ) == 0) { |
1004 | ctx->description->text = |
1005 | strndup(s: ctx->character_data, |
1006 | size: ctx->character_data_length); |
1007 | ctx->description = NULL; |
1008 | } else if (strcmp(s1: name, s2: "request" ) == 0 || |
1009 | strcmp(s1: name, s2: "event" ) == 0) { |
1010 | ctx->message = NULL; |
1011 | } else if (strcmp(s1: name, s2: "enum" ) == 0) { |
1012 | if (wl_list_empty(list: &ctx->enumeration->entry_list)) { |
1013 | fail(loc: &ctx->loc, msg: "enumeration %s was empty" , |
1014 | ctx->enumeration->name); |
1015 | } |
1016 | ctx->enumeration = NULL; |
1017 | } else if (strcmp(s1: name, s2: "entry" ) == 0) { |
1018 | ctx->entry = NULL; |
1019 | } else if (strcmp(s1: name, s2: "protocol" ) == 0) { |
1020 | struct interface *i; |
1021 | |
1022 | wl_list_for_each(i, &ctx->protocol->interface_list, link) { |
1023 | verify_arguments(ctx, interface: i, messages: &i->request_list, enumerations: &i->enumeration_list); |
1024 | verify_arguments(ctx, interface: i, messages: &i->event_list, enumerations: &i->enumeration_list); |
1025 | } |
1026 | } |
1027 | } |
1028 | |
1029 | static void |
1030 | character_data(void *data, const XML_Char *s, int len) |
1031 | { |
1032 | struct parse_context *ctx = data; |
1033 | |
1034 | if (ctx->character_data_length + len > sizeof (ctx->character_data)) { |
1035 | fprintf(stderr, format: "too much character data" ); |
1036 | exit(EXIT_FAILURE); |
1037 | } |
1038 | |
1039 | memcpy(dest: ctx->character_data + ctx->character_data_length, src: s, n: len); |
1040 | ctx->character_data_length += len; |
1041 | } |
1042 | |
1043 | static void |
1044 | (const char *text, bool standalone_comment) |
1045 | { |
1046 | int bol = 1, start = 0, i, length; |
1047 | bool = !standalone_comment; |
1048 | |
1049 | length = strlen(s: text); |
1050 | for (i = 0; i <= length; i++) { |
1051 | if (bol && (text[i] == ' ' || text[i] == '\t')) { |
1052 | continue; |
1053 | } else if (bol) { |
1054 | bol = 0; |
1055 | start = i; |
1056 | } |
1057 | if (text[i] == '\n' || |
1058 | (text[i] == '\0' && !(start == i))) { |
1059 | printf(format: "%s%s%.*s\n" , |
1060 | comment_started ? " *" : "/*" , |
1061 | i > start ? " " : "" , |
1062 | i - start, text + start); |
1063 | bol = 1; |
1064 | comment_started = true; |
1065 | } |
1066 | } |
1067 | if (comment_started && standalone_comment) |
1068 | printf(format: " */\n\n" ); |
1069 | } |
1070 | |
1071 | static void |
1072 | emit_opcodes(struct wl_list *message_list, struct interface *interface) |
1073 | { |
1074 | struct message *m; |
1075 | int opcode; |
1076 | |
1077 | if (wl_list_empty(list: message_list)) |
1078 | return; |
1079 | |
1080 | opcode = 0; |
1081 | wl_list_for_each(m, message_list, link) |
1082 | printf(format: "#define %s_%s %d\n" , |
1083 | interface->uppercase_name, m->uppercase_name, opcode++); |
1084 | |
1085 | printf(format: "\n" ); |
1086 | } |
1087 | |
1088 | static void |
1089 | emit_opcode_versions(struct wl_list *message_list, struct interface *interface) |
1090 | { |
1091 | struct message *m; |
1092 | |
1093 | wl_list_for_each(m, message_list, link) { |
1094 | printf(format: "/**\n * @ingroup iface_%s\n */\n" , interface->name); |
1095 | printf(format: "#define %s_%s_SINCE_VERSION %d\n" , |
1096 | interface->uppercase_name, m->uppercase_name, m->since); |
1097 | } |
1098 | |
1099 | printf(format: "\n" ); |
1100 | } |
1101 | |
1102 | static void |
1103 | emit_type(struct arg *a) |
1104 | { |
1105 | switch (a->type) { |
1106 | default: |
1107 | case INT: |
1108 | case FD: |
1109 | printf(format: "int32_t " ); |
1110 | break; |
1111 | case NEW_ID: |
1112 | case UNSIGNED: |
1113 | printf(format: "uint32_t " ); |
1114 | break; |
1115 | case FIXED: |
1116 | printf(format: "wl_fixed_t " ); |
1117 | break; |
1118 | case STRING: |
1119 | printf(format: "const char *" ); |
1120 | break; |
1121 | case OBJECT: |
1122 | printf(format: "struct %s *" , a->interface_name); |
1123 | break; |
1124 | case ARRAY: |
1125 | printf(format: "struct wl_array *" ); |
1126 | break; |
1127 | } |
1128 | } |
1129 | |
1130 | static void |
1131 | emit_stubs(struct wl_list *message_list, struct interface *interface) |
1132 | { |
1133 | struct message *m; |
1134 | struct arg *a, *ret; |
1135 | int has_destructor, has_destroy; |
1136 | |
1137 | printf(format: "/** @ingroup iface_%s */\n" , interface->name); |
1138 | printf(format: "static inline void\n" |
1139 | "%s_set_user_data(struct %s *%s, void *user_data)\n" |
1140 | "{\n" |
1141 | "\twl_proxy_set_user_data((struct wl_proxy *) %s, user_data);\n" |
1142 | "}\n\n" , |
1143 | interface->name, interface->name, interface->name, |
1144 | interface->name); |
1145 | |
1146 | printf(format: "/** @ingroup iface_%s */\n" , interface->name); |
1147 | printf(format: "static inline void *\n" |
1148 | "%s_get_user_data(struct %s *%s)\n" |
1149 | "{\n" |
1150 | "\treturn wl_proxy_get_user_data((struct wl_proxy *) %s);\n" |
1151 | "}\n\n" , |
1152 | interface->name, interface->name, interface->name, |
1153 | interface->name); |
1154 | |
1155 | printf(format: "static inline uint32_t\n" |
1156 | "%s_get_version(struct %s *%s)\n" |
1157 | "{\n" |
1158 | "\treturn wl_proxy_get_version((struct wl_proxy *) %s);\n" |
1159 | "}\n\n" , |
1160 | interface->name, interface->name, interface->name, |
1161 | interface->name); |
1162 | |
1163 | has_destructor = 0; |
1164 | has_destroy = 0; |
1165 | wl_list_for_each(m, message_list, link) { |
1166 | if (m->destructor) |
1167 | has_destructor = 1; |
1168 | if (strcmp(s1: m->name, s2: "destroy" ) == 0) |
1169 | has_destroy = 1; |
1170 | } |
1171 | |
1172 | if (!has_destructor && has_destroy) { |
1173 | fail(loc: &interface->loc, |
1174 | msg: "interface '%s' has method named destroy " |
1175 | "but no destructor" , |
1176 | interface->name); |
1177 | exit(EXIT_FAILURE); |
1178 | } |
1179 | |
1180 | if (!has_destroy && strcmp(s1: interface->name, s2: "wl_display" ) != 0) { |
1181 | printf(format: "/** @ingroup iface_%s */\n" , interface->name); |
1182 | printf(format: "static inline void\n" |
1183 | "%s_destroy(struct %s *%s)\n" |
1184 | "{\n" |
1185 | "\twl_proxy_destroy(" |
1186 | "(struct wl_proxy *) %s);\n" |
1187 | "}\n\n" , |
1188 | interface->name, interface->name, interface->name, |
1189 | interface->name); |
1190 | } |
1191 | |
1192 | if (wl_list_empty(list: message_list)) |
1193 | return; |
1194 | |
1195 | wl_list_for_each(m, message_list, link) { |
1196 | if (m->new_id_count > 1) { |
1197 | warn(loc: &m->loc, |
1198 | msg: "request '%s::%s' has more than " |
1199 | "one new_id arg, not emitting stub\n" , |
1200 | interface->name, m->name); |
1201 | continue; |
1202 | } |
1203 | |
1204 | ret = NULL; |
1205 | wl_list_for_each(a, &m->arg_list, link) { |
1206 | if (a->type == NEW_ID) |
1207 | ret = a; |
1208 | } |
1209 | |
1210 | printf(format: "/**\n" |
1211 | " * @ingroup iface_%s\n" , interface->name); |
1212 | if (m->description && m->description->text) |
1213 | format_text_to_comment(text: m->description->text, false); |
1214 | printf(format: " */\n" ); |
1215 | if (ret && ret->interface_name == NULL) |
1216 | printf(format: "static inline void *\n" ); |
1217 | else if (ret) |
1218 | printf(format: "static inline struct %s *\n" , |
1219 | ret->interface_name); |
1220 | else |
1221 | printf(format: "static inline void\n" ); |
1222 | |
1223 | printf(format: "%s_%s(struct %s *%s" , |
1224 | interface->name, m->name, |
1225 | interface->name, interface->name); |
1226 | |
1227 | wl_list_for_each(a, &m->arg_list, link) { |
1228 | if (a->type == NEW_ID && a->interface_name == NULL) { |
1229 | printf(format: ", const struct wl_interface *interface" |
1230 | ", uint32_t version" ); |
1231 | continue; |
1232 | } else if (a->type == NEW_ID) |
1233 | continue; |
1234 | printf(format: ", " ); |
1235 | emit_type(a); |
1236 | printf(format: "%s" , a->name); |
1237 | } |
1238 | |
1239 | printf(format: ")\n" |
1240 | "{\n" ); |
1241 | printf(format: "\t" ); |
1242 | if (ret) { |
1243 | printf(format: "struct wl_proxy *%s;\n\n" |
1244 | "\t%s = " , ret->name, ret->name); |
1245 | } |
1246 | printf(format: "wl_proxy_marshal_flags(" |
1247 | "(struct wl_proxy *) %s,\n" |
1248 | "\t\t\t %s_%s" , |
1249 | interface->name, |
1250 | interface->uppercase_name, |
1251 | m->uppercase_name); |
1252 | |
1253 | if (ret) { |
1254 | if (ret->interface_name) { |
1255 | /* Normal factory case, an arg has type="new_id" and |
1256 | * an interface is provided */ |
1257 | printf(format: ", &%s_interface" , ret->interface_name); |
1258 | } else { |
1259 | /* an arg has type ="new_id" but interface is not |
1260 | * provided, such as in wl_registry.bind */ |
1261 | printf(format: ", interface" ); |
1262 | } |
1263 | } else { |
1264 | /* No args have type="new_id" */ |
1265 | printf(format: ", NULL" ); |
1266 | } |
1267 | |
1268 | if (ret && ret->interface_name == NULL) |
1269 | printf(format: ", version" ); |
1270 | else |
1271 | printf(format: ", wl_proxy_get_version((struct wl_proxy *) %s)" , |
1272 | interface->name); |
1273 | printf(format: ", %s" , m->destructor ? "WL_MARSHAL_FLAG_DESTROY" : "0" ); |
1274 | |
1275 | wl_list_for_each(a, &m->arg_list, link) { |
1276 | if (a->type == NEW_ID) { |
1277 | if (a->interface_name == NULL) |
1278 | printf(format: ", interface->name, version" ); |
1279 | printf(format: ", NULL" ); |
1280 | } else { |
1281 | printf(format: ", %s" , a->name); |
1282 | } |
1283 | } |
1284 | printf(format: ");\n" ); |
1285 | |
1286 | if (ret && ret->interface_name == NULL) |
1287 | printf(format: "\n\treturn (void *) %s;\n" , ret->name); |
1288 | else if (ret) |
1289 | printf(format: "\n\treturn (struct %s *) %s;\n" , |
1290 | ret->interface_name, ret->name); |
1291 | |
1292 | printf(format: "}\n\n" ); |
1293 | } |
1294 | } |
1295 | |
1296 | static void |
1297 | emit_event_wrappers(struct wl_list *message_list, struct interface *interface) |
1298 | { |
1299 | struct message *m; |
1300 | struct arg *a; |
1301 | |
1302 | /* We provide hand written functions for the display object */ |
1303 | if (strcmp(s1: interface->name, s2: "wl_display" ) == 0) |
1304 | return; |
1305 | |
1306 | wl_list_for_each(m, message_list, link) { |
1307 | printf(format: "/**\n" |
1308 | " * @ingroup iface_%s\n" |
1309 | " * Sends an %s event to the client owning the resource.\n" , |
1310 | interface->name, |
1311 | m->name); |
1312 | printf(format: " * @param resource_ The client's resource\n" ); |
1313 | wl_list_for_each(a, &m->arg_list, link) { |
1314 | if (a->summary) |
1315 | printf(format: " * @param %s %s\n" , a->name, a->summary); |
1316 | } |
1317 | printf(format: " */\n" ); |
1318 | printf(format: "static inline void\n" |
1319 | "%s_send_%s(struct wl_resource *resource_" , |
1320 | interface->name, m->name); |
1321 | |
1322 | wl_list_for_each(a, &m->arg_list, link) { |
1323 | printf(format: ", " ); |
1324 | switch (a->type) { |
1325 | case NEW_ID: |
1326 | case OBJECT: |
1327 | printf(format: "struct wl_resource *" ); |
1328 | break; |
1329 | default: |
1330 | emit_type(a); |
1331 | } |
1332 | printf(format: "%s" , a->name); |
1333 | } |
1334 | |
1335 | printf(format: ")\n" |
1336 | "{\n" |
1337 | "\twl_resource_post_event(resource_, %s_%s" , |
1338 | interface->uppercase_name, m->uppercase_name); |
1339 | |
1340 | wl_list_for_each(a, &m->arg_list, link) |
1341 | printf(format: ", %s" , a->name); |
1342 | |
1343 | printf(format: ");\n" ); |
1344 | printf(format: "}\n\n" ); |
1345 | } |
1346 | } |
1347 | |
1348 | static void |
1349 | emit_enumerations(struct interface *interface) |
1350 | { |
1351 | struct enumeration *e; |
1352 | struct entry *entry; |
1353 | |
1354 | wl_list_for_each(e, &interface->enumeration_list, link) { |
1355 | struct description *desc = e->description; |
1356 | |
1357 | printf(format: "#ifndef %s_%s_ENUM\n" , |
1358 | interface->uppercase_name, e->uppercase_name); |
1359 | printf(format: "#define %s_%s_ENUM\n" , |
1360 | interface->uppercase_name, e->uppercase_name); |
1361 | |
1362 | if (desc) { |
1363 | printf(format: "/**\n" ); |
1364 | printf(format: " * @ingroup iface_%s\n" , interface->name); |
1365 | format_text_to_comment(text: desc->summary, false); |
1366 | if (desc->text) |
1367 | format_text_to_comment(text: desc->text, false); |
1368 | printf(format: " */\n" ); |
1369 | } |
1370 | printf(format: "enum %s_%s {\n" , interface->name, e->name); |
1371 | wl_list_for_each(entry, &e->entry_list, link) { |
1372 | desc = entry->description; |
1373 | if (entry->summary || entry->since > 1 || desc) { |
1374 | printf(format: "\t/**\n" ); |
1375 | if (entry->summary) |
1376 | printf(format: "\t * %s\n" , entry->summary); |
1377 | if (desc) { |
1378 | printf(format: "\t * %s\n" , desc->summary); |
1379 | printf(format: "\t *\n" ); |
1380 | if (desc->text) |
1381 | desc_dump(desc: desc->text, fmt: "\t * " ); |
1382 | } |
1383 | if (entry->since > 1) |
1384 | printf(format: "\t * @since %d\n" , entry->since); |
1385 | printf(format: "\t */\n" ); |
1386 | } |
1387 | printf(format: "\t%s_%s_%s = %s,\n" , |
1388 | interface->uppercase_name, |
1389 | e->uppercase_name, |
1390 | entry->uppercase_name, entry->value); |
1391 | } |
1392 | printf(format: "};\n" ); |
1393 | |
1394 | wl_list_for_each(entry, &e->entry_list, link) { |
1395 | if (entry->since == 1) |
1396 | continue; |
1397 | |
1398 | printf(format: "/**\n * @ingroup iface_%s\n */\n" , interface->name); |
1399 | printf(format: "#define %s_%s_%s_SINCE_VERSION %d\n" , |
1400 | interface->uppercase_name, |
1401 | e->uppercase_name, entry->uppercase_name, |
1402 | entry->since); |
1403 | |
1404 | } |
1405 | |
1406 | printf(format: "#endif /* %s_%s_ENUM */\n\n" , |
1407 | interface->uppercase_name, e->uppercase_name); |
1408 | } |
1409 | } |
1410 | |
1411 | static void |
1412 | emit_structs(struct wl_list *message_list, struct interface *interface, enum side side) |
1413 | { |
1414 | struct message *m; |
1415 | struct arg *a; |
1416 | int n; |
1417 | |
1418 | if (wl_list_empty(list: message_list)) |
1419 | return; |
1420 | |
1421 | printf(format: "/**\n" ); |
1422 | printf(format: " * @ingroup iface_%s\n" , interface->name); |
1423 | printf(format: " * @struct %s_%s\n" , interface->name, |
1424 | (side == SERVER) ? "interface" : "listener" ); |
1425 | printf(format: " */\n" ); |
1426 | printf(format: "struct %s_%s {\n" , interface->name, |
1427 | (side == SERVER) ? "interface" : "listener" ); |
1428 | |
1429 | wl_list_for_each(m, message_list, link) { |
1430 | struct description *mdesc = m->description; |
1431 | |
1432 | printf(format: "\t/**\n" ); |
1433 | if (mdesc) { |
1434 | if (mdesc->summary) |
1435 | printf(format: "\t * %s\n" , mdesc->summary); |
1436 | printf(format: "\t *\n" ); |
1437 | desc_dump(desc: mdesc->text, fmt: "\t * " ); |
1438 | } |
1439 | wl_list_for_each(a, &m->arg_list, link) { |
1440 | if (side == SERVER && a->type == NEW_ID && |
1441 | a->interface_name == NULL) |
1442 | printf(format: "\t * @param interface name of the objects interface\n" |
1443 | "\t * @param version version of the objects interface\n" ); |
1444 | |
1445 | if (a->summary) |
1446 | printf(format: "\t * @param %s %s\n" , a->name, |
1447 | a->summary); |
1448 | } |
1449 | if (m->since > 1) { |
1450 | printf(format: "\t * @since %d\n" , m->since); |
1451 | } |
1452 | printf(format: "\t */\n" ); |
1453 | printf(format: "\tvoid (*%s)(" , m->name); |
1454 | |
1455 | n = strlen(s: m->name) + 17; |
1456 | if (side == SERVER) { |
1457 | printf(format: "struct wl_client *client,\n" |
1458 | "%sstruct wl_resource *resource" , |
1459 | indent(n)); |
1460 | } else { |
1461 | printf(format: "void *data,\n" ), |
1462 | printf(format: "%sstruct %s *%s" , |
1463 | indent(n), interface->name, interface->name); |
1464 | } |
1465 | |
1466 | wl_list_for_each(a, &m->arg_list, link) { |
1467 | printf(format: ",\n%s" , indent(n)); |
1468 | |
1469 | if (side == SERVER && a->type == OBJECT) |
1470 | printf(format: "struct wl_resource *" ); |
1471 | else if (side == SERVER && a->type == NEW_ID && a->interface_name == NULL) |
1472 | printf(format: "const char *interface, uint32_t version, uint32_t " ); |
1473 | else if (side == CLIENT && a->type == OBJECT && a->interface_name == NULL) |
1474 | printf(format: "void *" ); |
1475 | |
1476 | else if (side == CLIENT && a->type == NEW_ID) |
1477 | printf(format: "struct %s *" , a->interface_name); |
1478 | else |
1479 | emit_type(a); |
1480 | |
1481 | printf(format: "%s" , a->name); |
1482 | } |
1483 | |
1484 | printf(format: ");\n" ); |
1485 | } |
1486 | |
1487 | printf(format: "};\n\n" ); |
1488 | |
1489 | if (side == CLIENT) { |
1490 | printf(format: "/**\n" |
1491 | " * @ingroup iface_%s\n" |
1492 | " */\n" , interface->name); |
1493 | printf(format: "static inline int\n" |
1494 | "%s_add_listener(struct %s *%s,\n" |
1495 | "%sconst struct %s_listener *listener, void *data)\n" |
1496 | "{\n" |
1497 | "\treturn wl_proxy_add_listener((struct wl_proxy *) %s,\n" |
1498 | "%s(void (**)(void)) listener, data);\n" |
1499 | "}\n\n" , |
1500 | interface->name, interface->name, interface->name, |
1501 | indent(n: 14 + strlen(s: interface->name)), |
1502 | interface->name, |
1503 | interface->name, |
1504 | indent(n: 37)); |
1505 | } |
1506 | } |
1507 | |
1508 | static void |
1509 | emit_types_forward_declarations(struct protocol *protocol, |
1510 | struct wl_list *message_list, |
1511 | struct wl_array *types) |
1512 | { |
1513 | struct message *m; |
1514 | struct arg *a; |
1515 | int length; |
1516 | char **p; |
1517 | |
1518 | wl_list_for_each(m, message_list, link) { |
1519 | length = 0; |
1520 | m->all_null = 1; |
1521 | wl_list_for_each(a, &m->arg_list, link) { |
1522 | length++; |
1523 | switch (a->type) { |
1524 | case NEW_ID: |
1525 | case OBJECT: |
1526 | if (!a->interface_name) |
1527 | continue; |
1528 | |
1529 | m->all_null = 0; |
1530 | p = fail_on_null(p: wl_array_add(array: types, size: sizeof *p)); |
1531 | *p = a->interface_name; |
1532 | break; |
1533 | default: |
1534 | break; |
1535 | } |
1536 | } |
1537 | |
1538 | if (m->all_null && length > protocol->null_run_length) |
1539 | protocol->null_run_length = length; |
1540 | } |
1541 | } |
1542 | |
1543 | static int |
1544 | cmp_names(const void *p1, const void *p2) |
1545 | { |
1546 | const char * const *s1 = p1, * const *s2 = p2; |
1547 | |
1548 | return strcmp(s1: *s1, s2: *s2); |
1549 | } |
1550 | |
1551 | static const char * |
1552 | get_include_name(bool core, enum side side) |
1553 | { |
1554 | if (side == SERVER) |
1555 | return core ? "wayland-server-core.h" : "wayland-server.h" ; |
1556 | else |
1557 | return core ? "wayland-client-core.h" : "wayland-client.h" ; |
1558 | } |
1559 | |
1560 | static void |
1561 | emit_mainpage_blurb(const struct protocol *protocol, enum side side) |
1562 | { |
1563 | struct interface *i; |
1564 | |
1565 | printf(format: "/**\n" |
1566 | " * @page page_%s The %s protocol\n" , |
1567 | protocol->name, protocol->name); |
1568 | |
1569 | if (protocol->description) { |
1570 | if (protocol->description->summary) { |
1571 | printf(format: " * %s\n" |
1572 | " *\n" , protocol->description->summary); |
1573 | } |
1574 | |
1575 | if (protocol->description->text) { |
1576 | printf(format: " * @section page_desc_%s Description\n" , protocol->name); |
1577 | format_text_to_comment(text: protocol->description->text, false); |
1578 | printf(format: " *\n" ); |
1579 | } |
1580 | } |
1581 | |
1582 | printf(format: " * @section page_ifaces_%s Interfaces\n" , protocol->name); |
1583 | wl_list_for_each(i, &protocol->interface_list, link) { |
1584 | printf(format: " * - @subpage page_iface_%s - %s\n" , |
1585 | i->name, |
1586 | i->description && i->description->summary ? i->description->summary : "" ); |
1587 | } |
1588 | |
1589 | if (protocol->copyright) { |
1590 | printf(format: " * @section page_copyright_%s Copyright\n" , |
1591 | protocol->name); |
1592 | printf(format: " * <pre>\n" ); |
1593 | format_text_to_comment(text: protocol->copyright, false); |
1594 | printf(format: " * </pre>\n" ); |
1595 | } |
1596 | |
1597 | printf(format: " */\n" ); |
1598 | } |
1599 | |
1600 | static void |
1601 | (struct protocol *protocol, enum side side) |
1602 | { |
1603 | struct interface *i, *i_next; |
1604 | struct wl_array types; |
1605 | const char *s = (side == SERVER) ? "SERVER" : "CLIENT" ; |
1606 | char **p, *prev; |
1607 | |
1608 | printf(format: "/* Generated by %s %s */\n\n" , PROGRAM_NAME, WAYLAND_VERSION); |
1609 | |
1610 | printf(format: "#ifndef %s_%s_PROTOCOL_H\n" |
1611 | "#define %s_%s_PROTOCOL_H\n" |
1612 | "\n" |
1613 | "#include <stdint.h>\n" |
1614 | "#include <stddef.h>\n" |
1615 | "#include \"%s\"\n\n" |
1616 | "#ifdef __cplusplus\n" |
1617 | "extern \"C\" {\n" |
1618 | "#endif\n\n" , |
1619 | protocol->uppercase_name, s, |
1620 | protocol->uppercase_name, s, |
1621 | get_include_name(core: protocol->core_headers, side)); |
1622 | if (side == SERVER) |
1623 | printf(format: "struct wl_client;\n" |
1624 | "struct wl_resource;\n\n" ); |
1625 | |
1626 | emit_mainpage_blurb(protocol, side); |
1627 | |
1628 | wl_array_init(array: &types); |
1629 | wl_list_for_each(i, &protocol->interface_list, link) { |
1630 | emit_types_forward_declarations(protocol, message_list: &i->request_list, types: &types); |
1631 | emit_types_forward_declarations(protocol, message_list: &i->event_list, types: &types); |
1632 | } |
1633 | |
1634 | wl_list_for_each(i, &protocol->interface_list, link) { |
1635 | p = fail_on_null(p: wl_array_add(array: &types, size: sizeof *p)); |
1636 | *p = i->name; |
1637 | } |
1638 | |
1639 | qsort(base: types.data, nmemb: types.size / sizeof *p, size: sizeof *p, compar: cmp_names); |
1640 | prev = NULL; |
1641 | wl_array_for_each(p, &types) { |
1642 | if (prev && strcmp(s1: *p, s2: prev) == 0) |
1643 | continue; |
1644 | printf(format: "struct %s;\n" , *p); |
1645 | prev = *p; |
1646 | } |
1647 | wl_array_release(array: &types); |
1648 | printf(format: "\n" ); |
1649 | |
1650 | wl_list_for_each(i, &protocol->interface_list, link) { |
1651 | printf(format: "#ifndef %s_INTERFACE\n" , i->uppercase_name); |
1652 | printf(format: "#define %s_INTERFACE\n" , i->uppercase_name); |
1653 | printf(format: "/**\n" |
1654 | " * @page page_iface_%s %s\n" , |
1655 | i->name, i->name); |
1656 | if (i->description && i->description->text) { |
1657 | printf(format: " * @section page_iface_%s_desc Description\n" , |
1658 | i->name); |
1659 | format_text_to_comment(text: i->description->text, false); |
1660 | } |
1661 | printf(format: " * @section page_iface_%s_api API\n" |
1662 | " * See @ref iface_%s.\n" |
1663 | " */\n" , |
1664 | i->name, i->name); |
1665 | printf(format: "/**\n" |
1666 | " * @defgroup iface_%s The %s interface\n" , |
1667 | i->name, i->name); |
1668 | if (i->description && i->description->text) |
1669 | format_text_to_comment(text: i->description->text, false); |
1670 | printf(format: " */\n" ); |
1671 | printf(format: "extern const struct wl_interface " |
1672 | "%s_interface;\n" , i->name); |
1673 | printf(format: "#endif\n" ); |
1674 | } |
1675 | |
1676 | printf(format: "\n" ); |
1677 | |
1678 | wl_list_for_each_safe(i, i_next, &protocol->interface_list, link) { |
1679 | |
1680 | emit_enumerations(interface: i); |
1681 | |
1682 | if (side == SERVER) { |
1683 | emit_structs(message_list: &i->request_list, interface: i, side); |
1684 | emit_opcodes(message_list: &i->event_list, interface: i); |
1685 | emit_opcode_versions(message_list: &i->event_list, interface: i); |
1686 | emit_opcode_versions(message_list: &i->request_list, interface: i); |
1687 | emit_event_wrappers(message_list: &i->event_list, interface: i); |
1688 | } else { |
1689 | emit_structs(message_list: &i->event_list, interface: i, side); |
1690 | emit_opcodes(message_list: &i->request_list, interface: i); |
1691 | emit_opcode_versions(message_list: &i->event_list, interface: i); |
1692 | emit_opcode_versions(message_list: &i->request_list, interface: i); |
1693 | emit_stubs(message_list: &i->request_list, interface: i); |
1694 | } |
1695 | |
1696 | free_interface(interface: i); |
1697 | } |
1698 | |
1699 | printf(format: "#ifdef __cplusplus\n" |
1700 | "}\n" |
1701 | "#endif\n" |
1702 | "\n" |
1703 | "#endif\n" ); |
1704 | } |
1705 | |
1706 | static void |
1707 | emit_null_run(struct protocol *protocol) |
1708 | { |
1709 | int i; |
1710 | |
1711 | for (i = 0; i < protocol->null_run_length; i++) |
1712 | printf(format: "\tNULL,\n" ); |
1713 | } |
1714 | |
1715 | static void |
1716 | emit_types(struct protocol *protocol, struct wl_list *message_list) |
1717 | { |
1718 | struct message *m; |
1719 | struct arg *a; |
1720 | |
1721 | wl_list_for_each(m, message_list, link) { |
1722 | if (m->all_null) { |
1723 | m->type_index = 0; |
1724 | continue; |
1725 | } |
1726 | |
1727 | m->type_index = |
1728 | protocol->null_run_length + protocol->type_index; |
1729 | protocol->type_index += m->arg_count; |
1730 | |
1731 | wl_list_for_each(a, &m->arg_list, link) { |
1732 | switch (a->type) { |
1733 | case NEW_ID: |
1734 | case OBJECT: |
1735 | if (a->interface_name) |
1736 | printf(format: "\t&%s_interface,\n" , |
1737 | a->interface_name); |
1738 | else |
1739 | printf(format: "\tNULL,\n" ); |
1740 | break; |
1741 | default: |
1742 | printf(format: "\tNULL,\n" ); |
1743 | break; |
1744 | } |
1745 | } |
1746 | } |
1747 | } |
1748 | |
1749 | static void |
1750 | emit_messages(const char *name, struct wl_list *message_list, |
1751 | struct interface *interface, const char *suffix) |
1752 | { |
1753 | struct message *m; |
1754 | struct arg *a; |
1755 | |
1756 | if (wl_list_empty(list: message_list)) |
1757 | return; |
1758 | |
1759 | printf(format: "static const struct wl_message " |
1760 | "%s_%s[] = {\n" , |
1761 | interface->name, suffix); |
1762 | |
1763 | wl_list_for_each(m, message_list, link) { |
1764 | printf(format: "\t{ \"%s\", \"" , m->name); |
1765 | |
1766 | if (m->since > 1) |
1767 | printf(format: "%d" , m->since); |
1768 | |
1769 | wl_list_for_each(a, &m->arg_list, link) { |
1770 | if (is_nullable_type(arg: a) && a->nullable) |
1771 | printf(format: "?" ); |
1772 | |
1773 | switch (a->type) { |
1774 | default: |
1775 | case INT: |
1776 | printf(format: "i" ); |
1777 | break; |
1778 | case NEW_ID: |
1779 | if (a->interface_name == NULL) |
1780 | printf(format: "su" ); |
1781 | printf(format: "n" ); |
1782 | break; |
1783 | case UNSIGNED: |
1784 | printf(format: "u" ); |
1785 | break; |
1786 | case FIXED: |
1787 | printf(format: "f" ); |
1788 | break; |
1789 | case STRING: |
1790 | printf(format: "s" ); |
1791 | break; |
1792 | case OBJECT: |
1793 | printf(format: "o" ); |
1794 | break; |
1795 | case ARRAY: |
1796 | printf(format: "a" ); |
1797 | break; |
1798 | case FD: |
1799 | printf(format: "h" ); |
1800 | break; |
1801 | } |
1802 | } |
1803 | printf(format: "\", %s_types + %d },\n" , name, m->type_index); |
1804 | } |
1805 | |
1806 | printf(format: "};\n\n" ); |
1807 | } |
1808 | |
1809 | |
1810 | static void |
1811 | emit_code(struct protocol *protocol, enum visibility vis) |
1812 | { |
1813 | const char *symbol_visibility; |
1814 | struct interface *i, *next; |
1815 | struct wl_array types; |
1816 | char **p, *prev; |
1817 | |
1818 | printf(format: "/* Generated by %s %s */\n\n" , PROGRAM_NAME, WAYLAND_VERSION); |
1819 | |
1820 | if (protocol->copyright) |
1821 | format_text_to_comment(text: protocol->copyright, true); |
1822 | |
1823 | printf(format: "#include <stdlib.h>\n" |
1824 | "#include <stdint.h>\n" |
1825 | "#include \"wayland-util.h\"\n\n" ); |
1826 | |
1827 | /* When building a shared library symbols must be exported, otherwise |
1828 | * we want to have the symbols hidden. */ |
1829 | if (vis == PRIVATE) { |
1830 | symbol_visibility = "WL_PRIVATE" ; |
1831 | printf(format: "#ifndef __has_attribute\n" |
1832 | "# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */\n" |
1833 | "#endif\n\n" ); |
1834 | |
1835 | printf(format: "#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)\n" |
1836 | "#define WL_PRIVATE __attribute__ ((visibility(\"hidden\")))\n" |
1837 | "#else\n" |
1838 | "#define WL_PRIVATE\n" |
1839 | "#endif\n\n" ); |
1840 | } else { |
1841 | symbol_visibility = "WL_EXPORT" ; |
1842 | } |
1843 | |
1844 | wl_array_init(array: &types); |
1845 | wl_list_for_each(i, &protocol->interface_list, link) { |
1846 | emit_types_forward_declarations(protocol, message_list: &i->request_list, types: &types); |
1847 | emit_types_forward_declarations(protocol, message_list: &i->event_list, types: &types); |
1848 | } |
1849 | qsort(base: types.data, nmemb: types.size / sizeof *p, size: sizeof *p, compar: cmp_names); |
1850 | prev = NULL; |
1851 | wl_array_for_each(p, &types) { |
1852 | if (prev && strcmp(s1: *p, s2: prev) == 0) |
1853 | continue; |
1854 | printf(format: "extern const struct wl_interface %s_interface;\n" , *p); |
1855 | prev = *p; |
1856 | } |
1857 | wl_array_release(array: &types); |
1858 | printf(format: "\n" ); |
1859 | |
1860 | printf(format: "static const struct wl_interface *%s_types[] = {\n" , protocol->name); |
1861 | emit_null_run(protocol); |
1862 | wl_list_for_each(i, &protocol->interface_list, link) { |
1863 | emit_types(protocol, message_list: &i->request_list); |
1864 | emit_types(protocol, message_list: &i->event_list); |
1865 | } |
1866 | printf(format: "};\n\n" ); |
1867 | |
1868 | wl_list_for_each_safe(i, next, &protocol->interface_list, link) { |
1869 | |
1870 | emit_messages(name: protocol->name, message_list: &i->request_list, interface: i, suffix: "requests" ); |
1871 | emit_messages(name: protocol->name, message_list: &i->event_list, interface: i, suffix: "events" ); |
1872 | |
1873 | printf(format: "%s const struct wl_interface " |
1874 | "%s_interface = {\n" |
1875 | "\t\"%s\", %d,\n" , |
1876 | symbol_visibility, i->name, i->name, i->version); |
1877 | |
1878 | if (!wl_list_empty(list: &i->request_list)) |
1879 | printf(format: "\t%d, %s_requests,\n" , |
1880 | wl_list_length(list: &i->request_list), i->name); |
1881 | else |
1882 | printf(format: "\t0, NULL,\n" ); |
1883 | |
1884 | if (!wl_list_empty(list: &i->event_list)) |
1885 | printf(format: "\t%d, %s_events,\n" , |
1886 | wl_list_length(list: &i->event_list), i->name); |
1887 | else |
1888 | printf(format: "\t0, NULL,\n" ); |
1889 | |
1890 | printf(format: "};\n\n" ); |
1891 | |
1892 | /* we won't need it any further */ |
1893 | free_interface(interface: i); |
1894 | } |
1895 | } |
1896 | |
1897 | static void |
1898 | free_protocol(struct protocol *protocol) |
1899 | { |
1900 | free(ptr: protocol->name); |
1901 | free(ptr: protocol->uppercase_name); |
1902 | free(ptr: protocol->copyright); |
1903 | free_description(desc: protocol->description); |
1904 | } |
1905 | |
1906 | int main(int argc, char *argv[]) |
1907 | { |
1908 | struct parse_context ctx; |
1909 | struct protocol protocol; |
1910 | FILE *input = stdin; |
1911 | char *input_filename = NULL; |
1912 | int len; |
1913 | void *buf; |
1914 | bool help = false; |
1915 | bool = false; |
1916 | bool version = false; |
1917 | bool strict = false; |
1918 | bool fail = false; |
1919 | int opt; |
1920 | enum { |
1921 | , |
1922 | , |
1923 | PRIVATE_CODE, |
1924 | PUBLIC_CODE, |
1925 | CODE, |
1926 | } mode; |
1927 | |
1928 | static const struct option options[] = { |
1929 | { "help" , no_argument, NULL, 'h' }, |
1930 | { "version" , no_argument, NULL, 'v' }, |
1931 | { "include-core-only" , no_argument, NULL, 'c' }, |
1932 | { "strict" , no_argument, NULL, 's' }, |
1933 | { 0, 0, NULL, 0 } |
1934 | }; |
1935 | |
1936 | while (1) { |
1937 | opt = getopt_long(argc: argc, argv: argv, shortopts: "hvcs" , longopts: options, NULL); |
1938 | |
1939 | if (opt == -1) |
1940 | break; |
1941 | |
1942 | switch (opt) { |
1943 | case 'h': |
1944 | help = true; |
1945 | break; |
1946 | case 'v': |
1947 | version = true; |
1948 | break; |
1949 | case 'c': |
1950 | core_headers = true; |
1951 | break; |
1952 | case 's': |
1953 | strict = true; |
1954 | break; |
1955 | default: |
1956 | fail = true; |
1957 | break; |
1958 | } |
1959 | } |
1960 | |
1961 | argv += optind; |
1962 | argc -= optind; |
1963 | |
1964 | if (help) |
1965 | usage(EXIT_SUCCESS); |
1966 | else if (version) |
1967 | scanner_version(EXIT_SUCCESS); |
1968 | else if ((argc != 1 && argc != 3) || fail) |
1969 | usage(EXIT_FAILURE); |
1970 | else if (strcmp(s1: argv[0], s2: "help" ) == 0) |
1971 | usage(EXIT_SUCCESS); |
1972 | else if (strcmp(s1: argv[0], s2: "client-header" ) == 0) |
1973 | mode = CLIENT_HEADER; |
1974 | else if (strcmp(s1: argv[0], s2: "server-header" ) == 0) |
1975 | mode = SERVER_HEADER; |
1976 | else if (strcmp(s1: argv[0], s2: "private-code" ) == 0) |
1977 | mode = PRIVATE_CODE; |
1978 | else if (strcmp(s1: argv[0], s2: "public-code" ) == 0) |
1979 | mode = PUBLIC_CODE; |
1980 | else if (strcmp(s1: argv[0], s2: "code" ) == 0) |
1981 | mode = CODE; |
1982 | else |
1983 | usage(EXIT_FAILURE); |
1984 | |
1985 | if (argc == 3) { |
1986 | input_filename = argv[1]; |
1987 | input = fopen(filename: input_filename, modes: "r" ); |
1988 | if (input == NULL) { |
1989 | fprintf(stderr, format: "Could not open input file: %s\n" , |
1990 | strerror(errno)); |
1991 | exit(EXIT_FAILURE); |
1992 | } |
1993 | if (freopen(filename: argv[2], modes: "w" , stdout) == NULL) { |
1994 | fprintf(stderr, format: "Could not open output file: %s\n" , |
1995 | strerror(errno)); |
1996 | fclose(stream: input); |
1997 | exit(EXIT_FAILURE); |
1998 | } |
1999 | } |
2000 | |
2001 | /* initialize protocol structure */ |
2002 | memset(s: &protocol, c: 0, n: sizeof protocol); |
2003 | wl_list_init(list: &protocol.interface_list); |
2004 | protocol.core_headers = core_headers; |
2005 | |
2006 | /* initialize context */ |
2007 | memset(s: &ctx, c: 0, n: sizeof ctx); |
2008 | ctx.protocol = &protocol; |
2009 | if (input == stdin) |
2010 | ctx.loc.filename = "<stdin>" ; |
2011 | else |
2012 | ctx.loc.filename = input_filename; |
2013 | |
2014 | if (!is_dtd_valid(input, filename: ctx.loc.filename)) { |
2015 | fprintf(stderr, |
2016 | format: "*******************************************************\n" |
2017 | "* *\n" |
2018 | "* WARNING: XML failed validation against built-in DTD *\n" |
2019 | "* *\n" |
2020 | "*******************************************************\n" ); |
2021 | if (strict) { |
2022 | fclose(stream: input); |
2023 | exit(EXIT_FAILURE); |
2024 | } |
2025 | } |
2026 | |
2027 | /* create XML parser */ |
2028 | ctx.parser = XML_ParserCreate(NULL); |
2029 | XML_SetUserData(parser: ctx.parser, userData: &ctx); |
2030 | if (ctx.parser == NULL) { |
2031 | fprintf(stderr, format: "failed to create parser\n" ); |
2032 | fclose(stream: input); |
2033 | exit(EXIT_FAILURE); |
2034 | } |
2035 | |
2036 | XML_SetElementHandler(parser: ctx.parser, start: start_element, end: end_element); |
2037 | XML_SetCharacterDataHandler(parser: ctx.parser, handler: character_data); |
2038 | |
2039 | do { |
2040 | buf = XML_GetBuffer(parser: ctx.parser, XML_BUFFER_SIZE); |
2041 | len = fread(ptr: buf, size: 1, XML_BUFFER_SIZE, stream: input); |
2042 | if (len < 0) { |
2043 | fprintf(stderr, format: "fread: %s\n" , strerror(errno)); |
2044 | fclose(stream: input); |
2045 | exit(EXIT_FAILURE); |
2046 | } |
2047 | if (XML_ParseBuffer(parser: ctx.parser, len, isFinal: len == 0) == 0) { |
2048 | fprintf(stderr, |
2049 | format: "Error parsing XML at line %ld col %ld: %s\n" , |
2050 | XML_GetCurrentLineNumber(parser: ctx.parser), |
2051 | XML_GetCurrentColumnNumber(parser: ctx.parser), |
2052 | XML_ErrorString(code: XML_GetErrorCode(parser: ctx.parser))); |
2053 | fclose(stream: input); |
2054 | exit(EXIT_FAILURE); |
2055 | } |
2056 | } while (len > 0); |
2057 | |
2058 | XML_ParserFree(parser: ctx.parser); |
2059 | |
2060 | switch (mode) { |
2061 | case CLIENT_HEADER: |
2062 | emit_header(protocol: &protocol, side: CLIENT); |
2063 | break; |
2064 | case SERVER_HEADER: |
2065 | emit_header(protocol: &protocol, side: SERVER); |
2066 | break; |
2067 | case PRIVATE_CODE: |
2068 | emit_code(protocol: &protocol, vis: PRIVATE); |
2069 | break; |
2070 | case CODE: |
2071 | fprintf(stderr, |
2072 | format: "Using \"code\" is deprecated - use " |
2073 | "private-code or public-code.\n" |
2074 | "See the help page for details.\n" ); |
2075 | /* fallthrough */ |
2076 | case PUBLIC_CODE: |
2077 | emit_code(protocol: &protocol, vis: PUBLIC); |
2078 | break; |
2079 | } |
2080 | |
2081 | free_protocol(protocol: &protocol); |
2082 | fclose(stream: input); |
2083 | |
2084 | return 0; |
2085 | } |
2086 | |