| /* |
| * Copyright © 2008-2011 Kristian Høgsberg |
| * Copyright © 2011 Intel Corporation |
| * Copyright © 2015 Red Hat, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the |
| * next paragraph) shall be included in all copies or substantial |
| * portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include "wayland-version.h" |
| |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <ctype.h> |
| #include <getopt.h> |
| #include <limits.h> |
| #include <unistd.h> |
| |
| #if HAVE_LIBXML |
| #include <libxml/parser.h> |
| |
| /* Embedded wayland.dtd file, see dtddata.S */ |
| extern char DTD_DATA_begin; |
| extern int DTD_DATA_len; |
| #endif |
| |
| /* Expat must be included after libxml as both want to declare XMLCALL; see |
| * the Git commit that 'git blame' for this comment points to for more. */ |
| #include <expat.h> |
| |
| #include "wayland-util.h" |
| |
| #define PROGRAM_NAME "wayland-scanner" |
| |
| enum side { |
| CLIENT, |
| SERVER, |
| }; |
| |
| enum visibility { |
| PRIVATE, |
| PUBLIC, |
| }; |
| |
| static int |
| usage(int ret) |
| { |
| fprintf(stderr, "usage: %s [OPTION] [client-header|server-header|private-code|public-code]" |
| " [input_file output_file]\n", PROGRAM_NAME); |
| fprintf(stderr, "\n"); |
| fprintf(stderr, "Converts XML protocol descriptions supplied on " |
| "stdin or input file to client\n" |
| "headers, server headers, or protocol marshalling code.\n\n" |
| "Use \"public-code\" only if the marshalling code will be public - " |
| "aka DSO will export it while other components will be using it.\n" |
| "Using \"private-code\" is strongly recommended.\n\n"); |
| fprintf(stderr, "options:\n"); |
| fprintf(stderr, " -h, --help display this help and exit.\n" |
| " -v, --version print the wayland library version that\n" |
| " the scanner was built against.\n" |
| " -c, --include-core-only include the core version of the headers,\n" |
| " that is e.g. wayland-client-core.h instead\n" |
| " of wayland-client.h.\n" |
| " -s, --strict exit immediately with an error if DTD\n" |
| " verification fails.\n"); |
| exit(ret); |
| } |
| |
| static int |
| scanner_version(int ret) |
| { |
| fprintf(stderr, "%s %s\n", PROGRAM_NAME, WAYLAND_VERSION); |
| exit(ret); |
| } |
| |
| static bool |
| is_dtd_valid(FILE *input, const char *filename) |
| { |
| bool rc = true; |
| #if HAVE_LIBXML |
| xmlParserCtxtPtr ctx = NULL; |
| xmlDocPtr doc = NULL; |
| xmlDtdPtr dtd = NULL; |
| xmlValidCtxtPtr dtdctx; |
| xmlParserInputBufferPtr buffer; |
| int fd = fileno(input); |
| |
| dtdctx = xmlNewValidCtxt(); |
| ctx = xmlNewParserCtxt(); |
| if (!ctx || !dtdctx) |
| abort(); |
| |
| buffer = xmlParserInputBufferCreateMem(&DTD_DATA_begin, |
| DTD_DATA_len, |
| XML_CHAR_ENCODING_UTF8); |
| if (!buffer) { |
| fprintf(stderr, "Failed to init buffer for DTD.\n"); |
| abort(); |
| } |
| |
| dtd = xmlIOParseDTD(NULL, buffer, XML_CHAR_ENCODING_UTF8); |
| if (!dtd) { |
| fprintf(stderr, "Failed to parse DTD.\n"); |
| abort(); |
| } |
| |
| doc = xmlCtxtReadFd(ctx, fd, filename, NULL, 0); |
| if (!doc) { |
| fprintf(stderr, "Failed to read XML\n"); |
| abort(); |
| } |
| |
| rc = xmlValidateDtd(dtdctx, doc, dtd); |
| xmlFreeDoc(doc); |
| xmlFreeParserCtxt(ctx); |
| xmlFreeDtd(dtd); |
| xmlFreeValidCtxt(dtdctx); |
| /* xmlIOParseDTD consumes buffer */ |
| |
| if (lseek(fd, 0, SEEK_SET) != 0) { |
| fprintf(stderr, "Failed to reset fd, output would be garbage.\n"); |
| abort(); |
| } |
| #endif |
| return rc; |
| } |
| |
| #define XML_BUFFER_SIZE 4096 |
| |
| struct location { |
| const char *filename; |
| int line_number; |
| }; |
| |
| struct description { |
| char *summary; |
| char *text; |
| }; |
| |
| struct protocol { |
| char *name; |
| char *uppercase_name; |
| struct wl_list interface_list; |
| int type_index; |
| int null_run_length; |
| char *copyright; |
| struct description *description; |
| bool core_headers; |
| }; |
| |
| struct interface { |
| struct location loc; |
| char *name; |
| char *uppercase_name; |
| int version; |
| int since; |
| struct wl_list request_list; |
| struct wl_list event_list; |
| struct wl_list enumeration_list; |
| struct wl_list link; |
| struct description *description; |
| }; |
| |
| struct message { |
| struct location loc; |
| char *name; |
| char *uppercase_name; |
| struct wl_list arg_list; |
| struct wl_list link; |
| int arg_count; |
| int new_id_count; |
| int type_index; |
| int all_null; |
| int destructor; |
| int since; |
| struct description *description; |
| }; |
| |
| enum arg_type { |
| NEW_ID, |
| INT, |
| UNSIGNED, |
| FIXED, |
| STRING, |
| OBJECT, |
| ARRAY, |
| FD |
| }; |
| |
| struct arg { |
| char *name; |
| enum arg_type type; |
| int nullable; |
| char *interface_name; |
| struct wl_list link; |
| char *summary; |
| char *enumeration_name; |
| }; |
| |
| struct enumeration { |
| char *name; |
| char *uppercase_name; |
| struct wl_list entry_list; |
| struct wl_list link; |
| struct description *description; |
| bool bitfield; |
| int since; |
| }; |
| |
| struct entry { |
| char *name; |
| char *uppercase_name; |
| char *value; |
| char *summary; |
| int since; |
| struct wl_list link; |
| }; |
| |
| struct parse_context { |
| struct location loc; |
| XML_Parser parser; |
| struct protocol *protocol; |
| struct interface *interface; |
| struct message *message; |
| struct enumeration *enumeration; |
| struct description *description; |
| char character_data[8192]; |
| unsigned int character_data_length; |
| }; |
| |
| enum identifier_role { |
| STANDALONE_IDENT, |
| TRAILING_IDENT |
| }; |
| |
| static void * |
| fail_on_null(void *p) |
| { |
| if (p == NULL) { |
| fprintf(stderr, "%s: out of memory\n", PROGRAM_NAME); |
| exit(EXIT_FAILURE); |
| } |
| |
| return p; |
| } |
| |
| static void * |
| zalloc(size_t s) |
| { |
| return calloc(s, 1); |
| } |
| |
| static void * |
| xzalloc(size_t s) |
| { |
| return fail_on_null(zalloc(s)); |
| } |
| |
| static char * |
| xstrdup(const char *s) |
| { |
| return fail_on_null(strdup(s)); |
| } |
| |
| static char * |
| uppercase_dup(const char *src) |
| { |
| char *u; |
| int i; |
| |
| u = xstrdup(src); |
| for (i = 0; u[i]; i++) |
| u[i] = toupper(u[i]); |
| u[i] = '\0'; |
| |
| return u; |
| } |
| |
| static const char *indent(int n) |
| { |
| const char *whitespace[] = { |
| "\t\t\t\t\t\t\t\t\t\t\t\t", |
| "\t\t\t\t\t\t\t\t\t\t\t\t ", |
| "\t\t\t\t\t\t\t\t\t\t\t\t ", |
| "\t\t\t\t\t\t\t\t\t\t\t\t ", |
| "\t\t\t\t\t\t\t\t\t\t\t\t ", |
| "\t\t\t\t\t\t\t\t\t\t\t\t ", |
| "\t\t\t\t\t\t\t\t\t\t\t\t ", |
| "\t\t\t\t\t\t\t\t\t\t\t\t " |
| }; |
| |
| return whitespace[n % 8] + 12 - n / 8; |
| } |
| |
| static void |
| desc_dump(char *desc, const char *fmt, ...) WL_PRINTF(2, 3); |
| |
| static void |
| desc_dump(char *desc, const char *fmt, ...) |
| { |
| va_list ap; |
| char buf[128], hang; |
| int col, i, j, k, startcol, newlines; |
| |
| va_start(ap, fmt); |
| vsnprintf(buf, sizeof buf, fmt, ap); |
| va_end(ap); |
| |
| for (i = 0, col = 0; buf[i] != '*'; i++) { |
| if (buf[i] == '\t') |
| col = (col + 8) & ~7; |
| else |
| col++; |
| } |
| |
| printf("%s", buf); |
| |
| if (!desc) { |
| printf("(none)\n"); |
| return; |
| } |
| |
| startcol = col; |
| col += strlen(&buf[i]); |
| if (col - startcol > 2) |
| hang = '\t'; |
| else |
| hang = ' '; |
| |
| for (i = 0; desc[i]; ) { |
| k = i; |
| newlines = 0; |
| while (desc[i] && isspace(desc[i])) { |
| if (desc[i] == '\n') |
| newlines++; |
| i++; |
| } |
| if (!desc[i]) |
| break; |
| |
| j = i; |
| while (desc[i] && !isspace(desc[i])) |
| i++; |
| |
| if (newlines > 1) |
| printf("\n%s*", indent(startcol)); |
| if (newlines > 1 || col + i - j > 72) { |
| printf("\n%s*%c", indent(startcol), hang); |
| col = startcol; |
| } |
| |
| if (col > startcol && k > 0) |
| col += printf(" "); |
| col += printf("%.*s", i - j, &desc[j]); |
| } |
| putchar('\n'); |
| } |
| |
| static void __attribute__ ((noreturn)) |
| fail(struct location *loc, const char *msg, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, msg); |
| fprintf(stderr, "%s:%d: error: ", |
| loc->filename, loc->line_number); |
| vfprintf(stderr, msg, ap); |
| fprintf(stderr, "\n"); |
| va_end(ap); |
| exit(EXIT_FAILURE); |
| } |
| |
| static void |
| warn(struct location *loc, const char *msg, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, msg); |
| fprintf(stderr, "%s:%d: warning: ", |
| loc->filename, loc->line_number); |
| vfprintf(stderr, msg, ap); |
| fprintf(stderr, "\n"); |
| va_end(ap); |
| } |
| |
| static bool |
| is_nullable_type(struct arg *arg) |
| { |
| switch (arg->type) { |
| /* Strings, objects, and arrays are possibly nullable */ |
| case STRING: |
| case OBJECT: |
| case NEW_ID: |
| case ARRAY: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static struct message * |
| create_message(struct location loc, const char *name) |
| { |
| struct message *message; |
| |
| message = xzalloc(sizeof *message); |
| message->loc = loc; |
| message->name = xstrdup(name); |
| message->uppercase_name = uppercase_dup(name); |
| wl_list_init(&message->arg_list); |
| |
| return message; |
| } |
| |
| static void |
| free_arg(struct arg *arg) |
| { |
| free(arg->name); |
| free(arg->interface_name); |
| free(arg->summary); |
| free(arg->enumeration_name); |
| free(arg); |
| } |
| |
| static struct arg * |
| create_arg(const char *name) |
| { |
| struct arg *arg; |
| |
| arg = xzalloc(sizeof *arg); |
| arg->name = xstrdup(name); |
| |
| return arg; |
| } |
| |
| static bool |
| set_arg_type(struct arg *arg, const char *type) |
| { |
| if (strcmp(type, "int") == 0) |
| arg->type = INT; |
| else if (strcmp(type, "uint") == 0) |
| arg->type = UNSIGNED; |
| else if (strcmp(type, "fixed") == 0) |
| arg->type = FIXED; |
| else if (strcmp(type, "string") == 0) |
| arg->type = STRING; |
| else if (strcmp(type, "array") == 0) |
| arg->type = ARRAY; |
| else if (strcmp(type, "fd") == 0) |
| arg->type = FD; |
| else if (strcmp(type, "new_id") == 0) |
| arg->type = NEW_ID; |
| else if (strcmp(type, "object") == 0) |
| arg->type = OBJECT; |
| else |
| return false; |
| |
| return true; |
| } |
| |
| static void |
| free_description(struct description *desc) |
| { |
| if (!desc) |
| return; |
| |
| free(desc->summary); |
| free(desc->text); |
| |
| free(desc); |
| } |
| |
| static void |
| free_message(struct message *message) |
| { |
| struct arg *a, *a_next; |
| |
| free(message->name); |
| free(message->uppercase_name); |
| free_description(message->description); |
| |
| wl_list_for_each_safe(a, a_next, &message->arg_list, link) |
| free_arg(a); |
| |
| free(message); |
| } |
| |
| static struct enumeration * |
| create_enumeration(const char *name) |
| { |
| struct enumeration *enumeration; |
| |
| enumeration = xzalloc(sizeof *enumeration); |
| enumeration->name = xstrdup(name); |
| enumeration->uppercase_name = uppercase_dup(name); |
| enumeration->since = 1; |
| |
| wl_list_init(&enumeration->entry_list); |
| |
| return enumeration; |
| } |
| |
| static struct entry * |
| create_entry(const char *name, const char *value) |
| { |
| struct entry *entry; |
| |
| entry = xzalloc(sizeof *entry); |
| entry->name = xstrdup(name); |
| entry->uppercase_name = uppercase_dup(name); |
| entry->value = xstrdup(value); |
| |
| return entry; |
| } |
| |
| static void |
| free_entry(struct entry *entry) |
| { |
| free(entry->name); |
| free(entry->uppercase_name); |
| free(entry->value); |
| free(entry->summary); |
| |
| free(entry); |
| } |
| |
| static void |
| free_enumeration(struct enumeration *enumeration) |
| { |
| struct entry *e, *e_next; |
| |
| free(enumeration->name); |
| free(enumeration->uppercase_name); |
| free_description(enumeration->description); |
| |
| wl_list_for_each_safe(e, e_next, &enumeration->entry_list, link) |
| free_entry(e); |
| |
| free(enumeration); |
| } |
| |
| static struct interface * |
| create_interface(struct location loc, const char *name, int version) |
| { |
| struct interface *interface; |
| |
| interface = xzalloc(sizeof *interface); |
| interface->loc = loc; |
| interface->name = xstrdup(name); |
| interface->uppercase_name = uppercase_dup(name); |
| interface->version = version; |
| interface->since = 1; |
| wl_list_init(&interface->request_list); |
| wl_list_init(&interface->event_list); |
| wl_list_init(&interface->enumeration_list); |
| |
| return interface; |
| } |
| |
| static void |
| free_interface(struct interface *interface) |
| { |
| struct message *m, *next_m; |
| struct enumeration *e, *next_e; |
| |
| free(interface->name); |
| free(interface->uppercase_name); |
| free_description(interface->description); |
| |
| wl_list_for_each_safe(m, next_m, &interface->request_list, link) |
| free_message(m); |
| wl_list_for_each_safe(m, next_m, &interface->event_list, link) |
| free_message(m); |
| wl_list_for_each_safe(e, next_e, &interface->enumeration_list, link) |
| free_enumeration(e); |
| |
| free(interface); |
| } |
| |
| /* Convert string to unsigned integer |
| * |
| * Parses a non-negative base-10 number from the given string. If the |
| * specified string is blank, contains non-numerical characters, is out |
| * of range, or results in a negative number, -1 is returned to indicate |
| * an error. |
| * |
| * Upon error, this routine does not modify or set errno. |
| * |
| * Returns -1 on error, or a non-negative integer on success |
| */ |
| static int |
| strtouint(const char *str) |
| { |
| long int ret; |
| char *end; |
| int prev_errno = errno; |
| |
| errno = 0; |
| ret = strtol(str, &end, 10); |
| if (errno != 0 || end == str || *end != '\0') |
| return -1; |
| |
| /* check range */ |
| if (ret < 0 || ret > INT_MAX) { |
| return -1; |
| } |
| |
| errno = prev_errno; |
| return (int)ret; |
| } |
| |
| /* Check that the provided string will produce valid "C" identifiers. |
| * |
| * If the string will form the prefix of an identifier in the |
| * generated C code, then it must match [_a-zA-Z][_0-9a-zA-Z]*. |
| * |
| * If the string will form the suffix of an identifier, then |
| * it must match [_0-9a-zA-Z]+. |
| * |
| * Unicode characters or escape sequences are not permitted, |
| * since not all C compilers support them. |
| * |
| * If the above conditions are not met, then fail() |
| */ |
| static void |
| validate_identifier(struct location *loc, |
| const char *str, |
| enum identifier_role role) |
| { |
| const char *scan; |
| |
| if (!*str) { |
| fail(loc, "element name is empty"); |
| } |
| |
| for (scan = str; *scan; scan++) { |
| char c = *scan; |
| |
| /* we do not use the locale-dependent `isalpha` */ |
| bool is_alpha = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); |
| bool is_digit = c >= '0' && c <= '9'; |
| bool leading_char = (scan == str) && role == STANDALONE_IDENT; |
| |
| if (is_alpha || c == '_' || (!leading_char && is_digit)) |
| continue; |
| |
| if (role == TRAILING_IDENT) |
| fail(loc, |
| "'%s' is not a valid trailing identifier part", str); |
| else |
| fail(loc, |
| "'%s' is not a valid standalone identifier", str); |
| } |
| } |
| |
| static int |
| version_from_since(struct parse_context *ctx, const char *since) |
| { |
| int version; |
| |
| if (since != NULL) { |
| version = strtouint(since); |
| if (version == -1) { |
| fail(&ctx->loc, "invalid integer (%s)\n", since); |
| } else if (version > ctx->interface->version) { |
| fail(&ctx->loc, "since (%u) larger than version (%u)\n", |
| version, ctx->interface->version); |
| } |
| } else { |
| version = 1; |
| } |
| |
| |
| return version; |
| } |
| |
| static void |
| start_element(void *data, const char *element_name, const char **atts) |
| { |
| struct parse_context *ctx = data; |
| struct interface *interface; |
| struct message *message; |
| struct arg *arg; |
| struct enumeration *enumeration; |
| struct entry *entry; |
| struct description *description = NULL; |
| const char *name = NULL; |
| const char *type = NULL; |
| const char *interface_name = NULL; |
| const char *value = NULL; |
| const char *summary = NULL; |
| const char *since = NULL; |
| const char *allow_null = NULL; |
| const char *enumeration_name = NULL; |
| const char *bitfield = NULL; |
| int i, version = 0; |
| |
| ctx->loc.line_number = XML_GetCurrentLineNumber(ctx->parser); |
| for (i = 0; atts[i]; i += 2) { |
| if (strcmp(atts[i], "name") == 0) |
| name = atts[i + 1]; |
| if (strcmp(atts[i], "version") == 0) { |
| version = strtouint(atts[i + 1]); |
| if (version == -1) |
| fail(&ctx->loc, "wrong version (%s)", atts[i + 1]); |
| } |
| if (strcmp(atts[i], "type") == 0) |
| type = atts[i + 1]; |
| if (strcmp(atts[i], "value") == 0) |
| value = atts[i + 1]; |
| if (strcmp(atts[i], "interface") == 0) |
| interface_name = atts[i + 1]; |
| if (strcmp(atts[i], "summary") == 0) |
| summary = atts[i + 1]; |
| if (strcmp(atts[i], "since") == 0) |
| since = atts[i + 1]; |
| if (strcmp(atts[i], "allow-null") == 0) |
| allow_null = atts[i + 1]; |
| if (strcmp(atts[i], "enum") == 0) |
| enumeration_name = atts[i + 1]; |
| if (strcmp(atts[i], "bitfield") == 0) |
| bitfield = atts[i + 1]; |
| } |
| |
| ctx->character_data_length = 0; |
| if (strcmp(element_name, "protocol") == 0) { |
| if (name == NULL) |
| fail(&ctx->loc, "no protocol name given"); |
| |
| validate_identifier(&ctx->loc, name, STANDALONE_IDENT); |
| ctx->protocol->name = xstrdup(name); |
| ctx->protocol->uppercase_name = uppercase_dup(name); |
| } else if (strcmp(element_name, "copyright") == 0) { |
| |
| } else if (strcmp(element_name, "interface") == 0) { |
| if (name == NULL) |
| fail(&ctx->loc, "no interface name given"); |
| |
| if (version == 0) |
| fail(&ctx->loc, "no interface version given"); |
| |
| validate_identifier(&ctx->loc, name, STANDALONE_IDENT); |
| interface = create_interface(ctx->loc, name, version); |
| ctx->interface = interface; |
| wl_list_insert(ctx->protocol->interface_list.prev, |
| &interface->link); |
| } else if (strcmp(element_name, "request") == 0 || |
| strcmp(element_name, "event") == 0) { |
| if (name == NULL) |
| fail(&ctx->loc, "no request name given"); |
| |
| validate_identifier(&ctx->loc, name, STANDALONE_IDENT); |
| message = create_message(ctx->loc, name); |
| |
| if (strcmp(element_name, "request") == 0) |
| wl_list_insert(ctx->interface->request_list.prev, |
| &message->link); |
| else |
| wl_list_insert(ctx->interface->event_list.prev, |
| &message->link); |
| |
| if (type != NULL && strcmp(type, "destructor") == 0) |
| message->destructor = 1; |
| |
| version = version_from_since(ctx, since); |
| |
| if (version < ctx->interface->since) |
| warn(&ctx->loc, "since version not increasing\n"); |
| ctx->interface->since = version; |
| message->since = version; |
| |
| if (strcmp(name, "destroy") == 0 && !message->destructor) |
| fail(&ctx->loc, "destroy request should be destructor type"); |
| |
| ctx->message = message; |
| } else if (strcmp(element_name, "arg") == 0) { |
| if (name == NULL) |
| fail(&ctx->loc, "no argument name given"); |
| |
| validate_identifier(&ctx->loc, name, STANDALONE_IDENT); |
| arg = create_arg(name); |
| if (!set_arg_type(arg, type)) |
| fail(&ctx->loc, "unknown type (%s)", type); |
| |
| switch (arg->type) { |
| case NEW_ID: |
| ctx->message->new_id_count++; |
| /* fallthrough */ |
| case OBJECT: |
| if (interface_name) { |
| validate_identifier(&ctx->loc, |
| interface_name, |
| STANDALONE_IDENT); |
| arg->interface_name = xstrdup(interface_name); |
| } |
| break; |
| default: |
| if (interface_name != NULL) |
| fail(&ctx->loc, "interface attribute not allowed for type %s", type); |
| break; |
| } |
| |
| if (allow_null) { |
| if (strcmp(allow_null, "true") == 0) |
| arg->nullable = 1; |
| else if (strcmp(allow_null, "false") != 0) |
| fail(&ctx->loc, |
| "invalid value for allow-null attribute (%s)", |
| allow_null); |
| |
| if (!is_nullable_type(arg)) |
| fail(&ctx->loc, |
| "allow-null is only valid for objects, strings, and arrays"); |
| } |
| |
| if (enumeration_name == NULL || strcmp(enumeration_name, "") == 0) |
| arg->enumeration_name = NULL; |
| else |
| arg->enumeration_name = xstrdup(enumeration_name); |
| |
| if (summary) |
| arg->summary = xstrdup(summary); |
| |
| wl_list_insert(ctx->message->arg_list.prev, &arg->link); |
| ctx->message->arg_count++; |
| } else if (strcmp(element_name, "enum") == 0) { |
| if (name == NULL) |
| fail(&ctx->loc, "no enum name given"); |
| |
| validate_identifier(&ctx->loc, name, TRAILING_IDENT); |
| enumeration = create_enumeration(name); |
| |
| if (bitfield == NULL || strcmp(bitfield, "false") == 0) |
| enumeration->bitfield = false; |
| else if (strcmp(bitfield, "true") == 0) |
| enumeration->bitfield = true; |
| else |
| fail(&ctx->loc, |
| "invalid value (%s) for bitfield attribute (only true/false are accepted)", |
| bitfield); |
| |
| wl_list_insert(ctx->interface->enumeration_list.prev, |
| &enumeration->link); |
| |
| ctx->enumeration = enumeration; |
| } else if (strcmp(element_name, "entry") == 0) { |
| if (name == NULL) |
| fail(&ctx->loc, "no entry name given"); |
| |
| validate_identifier(&ctx->loc, name, TRAILING_IDENT); |
| entry = create_entry(name, value); |
| version = version_from_since(ctx, since); |
| |
| if (version < ctx->enumeration->since) |
| warn(&ctx->loc, "since version not increasing\n"); |
| ctx->enumeration->since = version; |
| entry->since = version; |
| |
| if (summary) |
| entry->summary = xstrdup(summary); |
| else |
| entry->summary = NULL; |
| wl_list_insert(ctx->enumeration->entry_list.prev, |
| &entry->link); |
| } else if (strcmp(element_name, "description") == 0) { |
| if (summary == NULL) |
| fail(&ctx->loc, "description without summary"); |
| |
| description = xzalloc(sizeof *description); |
| description->summary = xstrdup(summary); |
| |
| if (ctx->message) |
| ctx->message->description = description; |
| else if (ctx->enumeration) |
| ctx->enumeration->description = description; |
| else if (ctx->interface) |
| ctx->interface->description = description; |
| else |
| ctx->protocol->description = description; |
| ctx->description = description; |
| } |
| } |
| |
| static struct enumeration * |
| find_enumeration(struct protocol *protocol, |
| struct interface *interface, |
| char *enum_attribute) |
| { |
| struct interface *i; |
| struct enumeration *e; |
| char *enum_name; |
| uint32_t idx = 0, j; |
| |
| for (j = 0; j + 1 < strlen(enum_attribute); j++) { |
| if (enum_attribute[j] == '.') { |
| idx = j; |
| } |
| } |
| |
| if (idx > 0) { |
| enum_name = enum_attribute + idx + 1; |
| |
| wl_list_for_each(i, &protocol->interface_list, link) |
| if (strncmp(i->name, enum_attribute, idx) == 0) |
| wl_list_for_each(e, &i->enumeration_list, link) |
| if (strcmp(e->name, enum_name) == 0) |
| return e; |
| } else if (interface) { |
| enum_name = enum_attribute; |
| |
| wl_list_for_each(e, &interface->enumeration_list, link) |
| if (strcmp(e->name, enum_name) == 0) |
| return e; |
| } |
| |
| return NULL; |
| } |
| |
| static void |
| verify_arguments(struct parse_context *ctx, |
| struct interface *interface, |
| struct wl_list *messages, |
| struct wl_list *enumerations) |
| { |
| struct message *m; |
| wl_list_for_each(m, messages, link) { |
| struct arg *a; |
| wl_list_for_each(a, &m->arg_list, link) { |
| struct enumeration *e; |
| |
| if (!a->enumeration_name) |
| continue; |
| |
| |
| e = find_enumeration(ctx->protocol, interface, |
| a->enumeration_name); |
| |
| switch (a->type) { |
| case INT: |
| if (e && e->bitfield) |
| fail(&ctx->loc, |
| "bitfield-style enum must only be referenced by uint"); |
| break; |
| case UNSIGNED: |
| break; |
| default: |
| fail(&ctx->loc, |
| "enumeration-style argument has wrong type"); |
| } |
| } |
| } |
| |
| } |
| |
| #ifndef HAVE_STRNDUP |
| char * |
| strndup(const char *s, size_t size) |
| { |
| char *r = malloc(size + 1); |
| strncpy(r, s, size); |
| r[size] = '\0'; |
| return r; |
| } |
| #endif |
| |
| static void |
| end_element(void *data, const XML_Char *name) |
| { |
| struct parse_context *ctx = data; |
| |
| if (strcmp(name, "copyright") == 0) { |
| ctx->protocol->copyright = |
| strndup(ctx->character_data, |
| ctx->character_data_length); |
| } else if (strcmp(name, "description") == 0) { |
| ctx->description->text = |
| strndup(ctx->character_data, |
| ctx->character_data_length); |
| ctx->description = NULL; |
| } else if (strcmp(name, "request") == 0 || |
| strcmp(name, "event") == 0) { |
| ctx->message = NULL; |
| } else if (strcmp(name, "enum") == 0) { |
| if (wl_list_empty(&ctx->enumeration->entry_list)) { |
| fail(&ctx->loc, "enumeration %s was empty", |
| ctx->enumeration->name); |
| } |
| ctx->enumeration = NULL; |
| } else if (strcmp(name, "protocol") == 0) { |
| struct interface *i; |
| |
| wl_list_for_each(i, &ctx->protocol->interface_list, link) { |
| verify_arguments(ctx, i, &i->request_list, &i->enumeration_list); |
| verify_arguments(ctx, i, &i->event_list, &i->enumeration_list); |
| } |
| } |
| } |
| |
| static void |
| character_data(void *data, const XML_Char *s, int len) |
| { |
| struct parse_context *ctx = data; |
| |
| if (ctx->character_data_length + len > sizeof (ctx->character_data)) { |
| fprintf(stderr, "too much character data"); |
| exit(EXIT_FAILURE); |
| } |
| |
| memcpy(ctx->character_data + ctx->character_data_length, s, len); |
| ctx->character_data_length += len; |
| } |
| |
| static void |
| format_text_to_comment(const char *text, bool standalone_comment) |
| { |
| int bol = 1, start = 0, i, length; |
| bool comment_started = !standalone_comment; |
| |
| length = strlen(text); |
| for (i = 0; i <= length; i++) { |
| if (bol && (text[i] == ' ' || text[i] == '\t')) { |
| continue; |
| } else if (bol) { |
| bol = 0; |
| start = i; |
| } |
| if (text[i] == '\n' || |
| (text[i] == '\0' && !(start == i))) { |
| printf("%s%s%.*s\n", |
| comment_started ? " *" : "/*", |
| i > start ? " " : "", |
| i - start, text + start); |
| bol = 1; |
| comment_started = true; |
| } |
| } |
| if (comment_started && standalone_comment) |
| printf(" */\n\n"); |
| } |
| |
| static void |
| emit_opcodes(struct wl_list *message_list, struct interface *interface) |
| { |
| struct message *m; |
| int opcode; |
| |
| if (wl_list_empty(message_list)) |
| return; |
| |
| opcode = 0; |
| wl_list_for_each(m, message_list, link) |
| printf("#define %s_%s %d\n", |
| interface->uppercase_name, m->uppercase_name, opcode++); |
| |
| printf("\n"); |
| } |
| |
| static void |
| emit_opcode_versions(struct wl_list *message_list, struct interface *interface) |
| { |
| struct message *m; |
| |
| wl_list_for_each(m, message_list, link) { |
| printf("/**\n * @ingroup iface_%s\n */\n", interface->name); |
| printf("#define %s_%s_SINCE_VERSION %d\n", |
| interface->uppercase_name, m->uppercase_name, m->since); |
| } |
| |
| printf("\n"); |
| } |
| |
| static void |
| emit_type(struct arg *a) |
| { |
| switch (a->type) { |
| default: |
| case INT: |
| case FD: |
| printf("int32_t "); |
| break; |
| case NEW_ID: |
| case UNSIGNED: |
| printf("uint32_t "); |
| break; |
| case FIXED: |
| printf("wl_fixed_t "); |
| break; |
| case STRING: |
| printf("const char *"); |
| break; |
| case OBJECT: |
| printf("struct %s *", a->interface_name); |
| break; |
| case ARRAY: |
| printf("struct wl_array *"); |
| break; |
| } |
| } |
| |
| static void |
| emit_stubs(struct wl_list *message_list, struct interface *interface) |
| { |
| struct message *m; |
| struct arg *a, *ret; |
| int has_destructor, has_destroy; |
| |
| printf("/** @ingroup iface_%s */\n", interface->name); |
| printf("static inline void\n" |
| "%s_set_user_data(struct %s *%s, void *user_data)\n" |
| "{\n" |
| "\twl_proxy_set_user_data((struct wl_proxy *) %s, user_data);\n" |
| "}\n\n", |
| interface->name, interface->name, interface->name, |
| interface->name); |
| |
| printf("/** @ingroup iface_%s */\n", interface->name); |
| printf("static inline void *\n" |
| "%s_get_user_data(struct %s *%s)\n" |
| "{\n" |
| "\treturn wl_proxy_get_user_data((struct wl_proxy *) %s);\n" |
| "}\n\n", |
| interface->name, interface->name, interface->name, |
| interface->name); |
| |
| printf("static inline uint32_t\n" |
| "%s_get_version(struct %s *%s)\n" |
| "{\n" |
| "\treturn wl_proxy_get_version((struct wl_proxy *) %s);\n" |
| "}\n\n", |
| interface->name, interface->name, interface->name, |
| interface->name); |
| |
| has_destructor = 0; |
| has_destroy = 0; |
| wl_list_for_each(m, message_list, link) { |
| if (m->destructor) |
| has_destructor = 1; |
| if (strcmp(m->name, "destroy") == 0) |
| has_destroy = 1; |
| } |
| |
| if (!has_destructor && has_destroy) { |
| fail(&interface->loc, |
| "interface '%s' has method named destroy " |
| "but no destructor", |
| interface->name); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (!has_destroy && strcmp(interface->name, "wl_display") != 0) { |
| printf("/** @ingroup iface_%s */\n", interface->name); |
| printf("static inline void\n" |
| "%s_destroy(struct %s *%s)\n" |
| "{\n" |
| "\twl_proxy_destroy(" |
| "(struct wl_proxy *) %s);\n" |
| "}\n\n", |
| interface->name, interface->name, interface->name, |
| interface->name); |
| } |
| |
| if (wl_list_empty(message_list)) |
| return; |
| |
| wl_list_for_each(m, message_list, link) { |
| if (m->new_id_count > 1) { |
| warn(&m->loc, |
| "request '%s::%s' has more than " |
| "one new_id arg, not emitting stub\n", |
| interface->name, m->name); |
| continue; |
| } |
| |
| ret = NULL; |
| wl_list_for_each(a, &m->arg_list, link) { |
| if (a->type == NEW_ID) |
| ret = a; |
| } |
| |
| printf("/**\n" |
| " * @ingroup iface_%s\n", interface->name); |
| if (m->description && m->description->text) |
| format_text_to_comment(m->description->text, false); |
| printf(" */\n"); |
| if (ret && ret->interface_name == NULL) |
| printf("static inline void *\n"); |
| else if (ret) |
| printf("static inline struct %s *\n", |
| ret->interface_name); |
| else |
| printf("static inline void\n"); |
| |
| printf("%s_%s(struct %s *%s", |
| interface->name, m->name, |
| interface->name, interface->name); |
| |
| wl_list_for_each(a, &m->arg_list, link) { |
| if (a->type == NEW_ID && a->interface_name == NULL) { |
| printf(", const struct wl_interface *interface" |
| ", uint32_t version"); |
| continue; |
| } else if (a->type == NEW_ID) |
| continue; |
| printf(", "); |
| emit_type(a); |
| printf("%s", a->name); |
| } |
| |
| printf(")\n" |
| "{\n"); |
| if (ret && ret->interface_name == NULL) { |
| /* an arg has type ="new_id" but interface is not |
| * provided, such as in wl_registry.bind */ |
| printf("\tstruct wl_proxy *%s;\n\n" |
| "\t%s = wl_proxy_marshal_constructor_versioned(" |
| "(struct wl_proxy *) %s,\n" |
| "\t\t\t %s_%s, interface, version", |
| ret->name, ret->name, |
| interface->name, |
| interface->uppercase_name, |
| m->uppercase_name); |
| } else if (ret) { |
| /* Normal factory case, an arg has type="new_id" and |
| * an interface is provided */ |
| printf("\tstruct wl_proxy *%s;\n\n" |
| "\t%s = wl_proxy_marshal_constructor(" |
| "(struct wl_proxy *) %s,\n" |
| "\t\t\t %s_%s, &%s_interface", |
| ret->name, ret->name, |
| interface->name, |
| interface->uppercase_name, |
| m->uppercase_name, |
| ret->interface_name); |
| } else { |
| /* No args have type="new_id" */ |
| printf("\twl_proxy_marshal((struct wl_proxy *) %s,\n" |
| "\t\t\t %s_%s", |
| interface->name, |
| interface->uppercase_name, |
| m->uppercase_name); |
| } |
| |
| wl_list_for_each(a, &m->arg_list, link) { |
| if (a->type == NEW_ID) { |
| if (a->interface_name == NULL) |
| printf(", interface->name, version"); |
| printf(", NULL"); |
| } else { |
| printf(", %s", a->name); |
| } |
| } |
| printf(");\n"); |
| |
| if (m->destructor) |
| printf("\n\twl_proxy_destroy(" |
| "(struct wl_proxy *) %s);\n", |
| interface->name); |
| |
| if (ret && ret->interface_name == NULL) |
| printf("\n\treturn (void *) %s;\n", ret->name); |
| else if (ret) |
| printf("\n\treturn (struct %s *) %s;\n", |
| ret->interface_name, ret->name); |
| |
| printf("}\n\n"); |
| } |
| } |
| |
| static void |
| emit_event_wrappers(struct wl_list *message_list, struct interface *interface) |
| { |
| struct message *m; |
| struct arg *a; |
| |
| /* We provide hand written functions for the display object */ |
| if (strcmp(interface->name, "wl_display") == 0) |
| return; |
| |
| wl_list_for_each(m, message_list, link) { |
| printf("/**\n" |
| " * @ingroup iface_%s\n" |
| " * Sends an %s event to the client owning the resource.\n", |
| interface->name, |
| m->name); |
| printf(" * @param resource_ The client's resource\n"); |
| wl_list_for_each(a, &m->arg_list, link) { |
| if (a->summary) |
| printf(" * @param %s %s\n", a->name, a->summary); |
| } |
| printf(" */\n"); |
| printf("static inline void\n" |
| "%s_send_%s(struct wl_resource *resource_", |
| interface->name, m->name); |
| |
| wl_list_for_each(a, &m->arg_list, link) { |
| printf(", "); |
| switch (a->type) { |
| case NEW_ID: |
| case OBJECT: |
| printf("struct wl_resource *"); |
| break; |
| default: |
| emit_type(a); |
| } |
| printf("%s", a->name); |
| } |
| |
| printf(")\n" |
| "{\n" |
| "\twl_resource_post_event(resource_, %s_%s", |
| interface->uppercase_name, m->uppercase_name); |
| |
| wl_list_for_each(a, &m->arg_list, link) |
| printf(", %s", a->name); |
| |
| printf(");\n"); |
| printf("}\n\n"); |
| } |
| } |
| |
| static void |
| emit_enumerations(struct interface *interface) |
| { |
| struct enumeration *e; |
| struct entry *entry; |
| |
| wl_list_for_each(e, &interface->enumeration_list, link) { |
| struct description *desc = e->description; |
| |
| printf("#ifndef %s_%s_ENUM\n", |
| interface->uppercase_name, e->uppercase_name); |
| printf("#define %s_%s_ENUM\n", |
| interface->uppercase_name, e->uppercase_name); |
| |
| if (desc) { |
| printf("/**\n"); |
| printf(" * @ingroup iface_%s\n", interface->name); |
| format_text_to_comment(desc->summary, false); |
| if (desc->text) |
| format_text_to_comment(desc->text, false); |
| printf(" */\n"); |
| } |
| printf("enum %s_%s {\n", interface->name, e->name); |
| wl_list_for_each(entry, &e->entry_list, link) { |
| if (entry->summary || entry->since > 1) { |
| printf("\t/**\n"); |
| if (entry->summary) |
| printf("\t * %s\n", entry->summary); |
| if (entry->since > 1) |
| printf("\t * @since %d\n", entry->since); |
| printf("\t */\n"); |
| } |
| printf("\t%s_%s_%s = %s,\n", |
| interface->uppercase_name, |
| e->uppercase_name, |
| entry->uppercase_name, entry->value); |
| } |
| printf("};\n"); |
| |
| wl_list_for_each(entry, &e->entry_list, link) { |
| if (entry->since == 1) |
| continue; |
| |
| printf("/**\n * @ingroup iface_%s\n */\n", interface->name); |
| printf("#define %s_%s_%s_SINCE_VERSION %d\n", |
| interface->uppercase_name, |
| e->uppercase_name, entry->uppercase_name, |
| entry->since); |
| |
| } |
| |
| printf("#endif /* %s_%s_ENUM */\n\n", |
| interface->uppercase_name, e->uppercase_name); |
| } |
| } |
| |
| static void |
| emit_structs(struct wl_list *message_list, struct interface *interface, enum side side) |
| { |
| struct message *m; |
| struct arg *a; |
| int n; |
| |
| if (wl_list_empty(message_list)) |
| return; |
| |
| printf("/**\n"); |
| printf(" * @ingroup iface_%s\n", interface->name); |
| printf(" * @struct %s_%s\n", interface->name, |
| (side == SERVER) ? "interface" : "listener"); |
| printf(" */\n"); |
| printf("struct %s_%s {\n", interface->name, |
| (side == SERVER) ? "interface" : "listener"); |
| |
| wl_list_for_each(m, message_list, link) { |
| struct description *mdesc = m->description; |
| |
| printf("\t/**\n"); |
| if (mdesc) { |
| if (mdesc->summary) |
| printf("\t * %s\n", mdesc->summary); |
| printf("\t *\n"); |
| desc_dump(mdesc->text, "\t * "); |
| } |
| wl_list_for_each(a, &m->arg_list, link) { |
| if (side == SERVER && a->type == NEW_ID && |
| a->interface_name == NULL) |
| printf("\t * @param interface name of the objects interface\n" |
| "\t * @param version version of the objects interface\n"); |
| |
| if (a->summary) |
| printf("\t * @param %s %s\n", a->name, |
| a->summary); |
| } |
| if (m->since > 1) { |
| printf("\t * @since %d\n", m->since); |
| } |
| printf("\t */\n"); |
| printf("\tvoid (*%s)(", m->name); |
| |
| n = strlen(m->name) + 17; |
| if (side == SERVER) { |
| printf("struct wl_client *client,\n" |
| "%sstruct wl_resource *resource", |
| indent(n)); |
| } else { |
| printf("void *data,\n"), |
| printf("%sstruct %s *%s", |
| indent(n), interface->name, interface->name); |
| } |
| |
| wl_list_for_each(a, &m->arg_list, link) { |
| printf(",\n%s", indent(n)); |
| |
| if (side == SERVER && a->type == OBJECT) |
| printf("struct wl_resource *"); |
| else if (side == SERVER && a->type == NEW_ID && a->interface_name == NULL) |
| printf("const char *interface, uint32_t version, uint32_t "); |
| else if (side == CLIENT && a->type == OBJECT && a->interface_name == NULL) |
| printf("void *"); |
| |
| else if (side == CLIENT && a->type == NEW_ID) |
| printf("struct %s *", a->interface_name); |
| else |
| emit_type(a); |
| |
| printf("%s", a->name); |
| } |
| |
| printf(");\n"); |
| } |
| |
| printf("};\n\n"); |
| |
| if (side == CLIENT) { |
| printf("/**\n" |
| " * @ingroup iface_%s\n" |
| " */\n", interface->name); |
| printf("static inline int\n" |
| "%s_add_listener(struct %s *%s,\n" |
| "%sconst struct %s_listener *listener, void *data)\n" |
| "{\n" |
| "\treturn wl_proxy_add_listener((struct wl_proxy *) %s,\n" |
| "%s(void (**)(void)) listener, data);\n" |
| "}\n\n", |
| interface->name, interface->name, interface->name, |
| indent(14 + strlen(interface->name)), |
| interface->name, |
| interface->name, |
| indent(37)); |
| } |
| } |
| |
| static void |
| emit_types_forward_declarations(struct protocol *protocol, |
| struct wl_list *message_list, |
| struct wl_array *types) |
| { |
| struct message *m; |
| struct arg *a; |
| int length; |
| char **p; |
| |
| wl_list_for_each(m, message_list, link) { |
| length = 0; |
| m->all_null = 1; |
| wl_list_for_each(a, &m->arg_list, link) { |
| length++; |
| switch (a->type) { |
| case NEW_ID: |
| case OBJECT: |
| if (!a->interface_name) |
| continue; |
| |
| m->all_null = 0; |
| p = fail_on_null(wl_array_add(types, sizeof *p)); |
| *p = a->interface_name; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (m->all_null && length > protocol->null_run_length) |
| protocol->null_run_length = length; |
| } |
| } |
| |
| static int |
| cmp_names(const void *p1, const void *p2) |
| { |
| const char * const *s1 = p1, * const *s2 = p2; |
| |
| return strcmp(*s1, *s2); |
| } |
| |
| static const char * |
| get_include_name(bool core, enum side side) |
| { |
| if (side == SERVER) |
| return core ? "wayland-server-core.h" : "wayland-server.h"; |
| else |
| return core ? "wayland-client-core.h" : "wayland-client.h"; |
| } |
| |
| static void |
| emit_mainpage_blurb(const struct protocol *protocol, enum side side) |
| { |
| struct interface *i; |
| |
| printf("/**\n" |
| " * @page page_%s The %s protocol\n", |
| protocol->name, protocol->name); |
| |
| if (protocol->description) { |
| if (protocol->description->summary) { |
| printf(" * %s\n" |
| " *\n", protocol->description->summary); |
| } |
| |
| if (protocol->description->text) { |
| printf(" * @section page_desc_%s Description\n", protocol->name); |
| format_text_to_comment(protocol->description->text, false); |
| printf(" *\n"); |
| } |
| } |
| |
| printf(" * @section page_ifaces_%s Interfaces\n", protocol->name); |
| wl_list_for_each(i, &protocol->interface_list, link) { |
| printf(" * - @subpage page_iface_%s - %s\n", |
| i->name, |
| i->description && i->description->summary ? i->description->summary : ""); |
| } |
| |
| if (protocol->copyright) { |
| printf(" * @section page_copyright_%s Copyright\n", |
| protocol->name); |
| printf(" * <pre>\n"); |
| format_text_to_comment(protocol->copyright, false); |
| printf(" * </pre>\n"); |
| } |
| |
| printf(" */\n"); |
| } |
| |
| static void |
| emit_header(struct protocol *protocol, enum side side) |
| { |
| struct interface *i, *i_next; |
| struct wl_array types; |
| const char *s = (side == SERVER) ? "SERVER" : "CLIENT"; |
| char **p, *prev; |
| |
| printf("/* Generated by %s %s */\n\n", PROGRAM_NAME, WAYLAND_VERSION); |
| |
| printf("#ifndef %s_%s_PROTOCOL_H\n" |
| "#define %s_%s_PROTOCOL_H\n" |
| "\n" |
| "#include <stdint.h>\n" |
| "#include <stddef.h>\n" |
| "#include \"%s\"\n\n" |
| "#ifdef __cplusplus\n" |
| "extern \"C\" {\n" |
| "#endif\n\n", |
| protocol->uppercase_name, s, |
| protocol->uppercase_name, s, |
| get_include_name(protocol->core_headers, side)); |
| if (side == SERVER) |
| printf("struct wl_client;\n" |
| "struct wl_resource;\n\n"); |
| |
| emit_mainpage_blurb(protocol, side); |
| |
| wl_array_init(&types); |
| wl_list_for_each(i, &protocol->interface_list, link) { |
| emit_types_forward_declarations(protocol, &i->request_list, &types); |
| emit_types_forward_declarations(protocol, &i->event_list, &types); |
| } |
| |
| wl_list_for_each(i, &protocol->interface_list, link) { |
| p = fail_on_null(wl_array_add(&types, sizeof *p)); |
| *p = i->name; |
| } |
| |
| qsort(types.data, types.size / sizeof *p, sizeof *p, cmp_names); |
| prev = NULL; |
| wl_array_for_each(p, &types) { |
| if (prev && strcmp(*p, prev) == 0) |
| continue; |
| printf("struct %s;\n", *p); |
| prev = *p; |
| } |
| wl_array_release(&types); |
| printf("\n"); |
| |
| wl_list_for_each(i, &protocol->interface_list, link) { |
| printf("#ifndef %s_INTERFACE\n", i->uppercase_name); |
| printf("#define %s_INTERFACE\n", i->uppercase_name); |
| printf("/**\n" |
| " * @page page_iface_%s %s\n", |
| i->name, i->name); |
| if (i->description && i->description->text) { |
| printf(" * @section page_iface_%s_desc Description\n", |
| i->name); |
| format_text_to_comment(i->description->text, false); |
| } |
| printf(" * @section page_iface_%s_api API\n" |
| " * See @ref iface_%s.\n" |
| " */\n", |
| i->name, i->name); |
| printf("/**\n" |
| " * @defgroup iface_%s The %s interface\n", |
| i->name, i->name); |
| if (i->description && i->description->text) |
| format_text_to_comment(i->description->text, false); |
| printf(" */\n"); |
| printf("extern const struct wl_interface " |
| "%s_interface;\n", i->name); |
| printf("#endif\n"); |
| } |
| |
| printf("\n"); |
| |
| wl_list_for_each_safe(i, i_next, &protocol->interface_list, link) { |
| |
| emit_enumerations(i); |
| |
| if (side == SERVER) { |
| emit_structs(&i->request_list, i, side); |
| emit_opcodes(&i->event_list, i); |
| emit_opcode_versions(&i->event_list, i); |
| emit_opcode_versions(&i->request_list, i); |
| emit_event_wrappers(&i->event_list, i); |
| } else { |
| emit_structs(&i->event_list, i, side); |
| emit_opcodes(&i->request_list, i); |
| emit_opcode_versions(&i->event_list, i); |
| emit_opcode_versions(&i->request_list, i); |
| emit_stubs(&i->request_list, i); |
| } |
| |
| free_interface(i); |
| } |
| |
| printf("#ifdef __cplusplus\n" |
| "}\n" |
| "#endif\n" |
| "\n" |
| "#endif\n"); |
| } |
| |
| static void |
| emit_null_run(struct protocol *protocol) |
| { |
| int i; |
| |
| for (i = 0; i < protocol->null_run_length; i++) |
| printf("\tNULL,\n"); |
| } |
| |
| static void |
| emit_types(struct protocol *protocol, struct wl_list *message_list) |
| { |
| struct message *m; |
| struct arg *a; |
| |
| wl_list_for_each(m, message_list, link) { |
| if (m->all_null) { |
| m->type_index = 0; |
| continue; |
| } |
| |
| m->type_index = |
| protocol->null_run_length + protocol->type_index; |
| protocol->type_index += m->arg_count; |
| |
| wl_list_for_each(a, &m->arg_list, link) { |
| switch (a->type) { |
| case NEW_ID: |
| case OBJECT: |
| if (a->interface_name) |
| printf("\t&%s_interface,\n", |
| a->interface_name); |
| else |
| printf("\tNULL,\n"); |
| break; |
| default: |
| printf("\tNULL,\n"); |
| break; |
| } |
| } |
| } |
| } |
| |
| static void |
| emit_messages(const char *name, struct wl_list *message_list, |
| struct interface *interface, const char *suffix) |
| { |
| struct message *m; |
| struct arg *a; |
| |
| if (wl_list_empty(message_list)) |
| return; |
| |
| printf("static const struct wl_message " |
| "%s_%s[] = {\n", |
| interface->name, suffix); |
| |
| wl_list_for_each(m, message_list, link) { |
| printf("\t{ \"%s\", \"", m->name); |
| |
| if (m->since > 1) |
| printf("%d", m->since); |
| |
| wl_list_for_each(a, &m->arg_list, link) { |
| if (is_nullable_type(a) && a->nullable) |
| printf("?"); |
| |
| switch (a->type) { |
| default: |
| case INT: |
| printf("i"); |
| break; |
| case NEW_ID: |
| if (a->interface_name == NULL) |
| printf("su"); |
| printf("n"); |
| break; |
| case UNSIGNED: |
| printf("u"); |
| break; |
| case FIXED: |
| printf("f"); |
| break; |
| case STRING: |
| printf("s"); |
| break; |
| case OBJECT: |
| printf("o"); |
| break; |
| case ARRAY: |
| printf("a"); |
| break; |
| case FD: |
| printf("h"); |
| break; |
| } |
| } |
| printf("\", %s_types + %d },\n", name, m->type_index); |
| } |
| |
| printf("};\n\n"); |
| } |
| |
| |
| static void |
| emit_code(struct protocol *protocol, enum visibility vis) |
| { |
| const char *symbol_visibility; |
| struct interface *i, *next; |
| struct wl_array types; |
| char **p, *prev; |
| |
| printf("/* Generated by %s %s */\n\n", PROGRAM_NAME, WAYLAND_VERSION); |
| |
| if (protocol->copyright) |
| format_text_to_comment(protocol->copyright, true); |
| |
| printf("#include <stdlib.h>\n" |
| "#include <stdint.h>\n" |
| "#include \"wayland-util.h\"\n\n"); |
| |
| /* When building a shared library symbols must be exported, otherwise |
| * we want to have the symbols hidden. */ |
| if (vis == PRIVATE) { |
| symbol_visibility = "WL_PRIVATE"; |
| printf("#ifndef __has_attribute\n" |
| "# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */\n" |
| "#endif\n\n"); |
| |
| printf("#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)\n" |
| "#define WL_PRIVATE __attribute__ ((visibility(\"hidden\")))\n" |
| "#else\n" |
| "#define WL_PRIVATE\n" |
| "#endif\n\n"); |
| } else { |
| symbol_visibility = "WL_EXPORT"; |
| } |
| |
| wl_array_init(&types); |
| wl_list_for_each(i, &protocol->interface_list, link) { |
| emit_types_forward_declarations(protocol, &i->request_list, &types); |
| emit_types_forward_declarations(protocol, &i->event_list, &types); |
| } |
| qsort(types.data, types.size / sizeof *p, sizeof *p, cmp_names); |
| prev = NULL; |
| wl_array_for_each(p, &types) { |
| if (prev && strcmp(*p, prev) == 0) |
| continue; |
| printf("extern const struct wl_interface %s_interface;\n", *p); |
| prev = *p; |
| } |
| wl_array_release(&types); |
| printf("\n"); |
| |
| printf("static const struct wl_interface *%s_types[] = {\n", protocol->name); |
| emit_null_run(protocol); |
| wl_list_for_each(i, &protocol->interface_list, link) { |
| emit_types(protocol, &i->request_list); |
| emit_types(protocol, &i->event_list); |
| } |
| printf("};\n\n"); |
| |
| wl_list_for_each_safe(i, next, &protocol->interface_list, link) { |
| |
| emit_messages(protocol->name, &i->request_list, i, "requests"); |
| emit_messages(protocol->name, &i->event_list, i, "events"); |
| |
| printf("%s const struct wl_interface " |
| "%s_interface = {\n" |
| "\t\"%s\", %d,\n", |
| symbol_visibility, i->name, i->name, i->version); |
| |
| if (!wl_list_empty(&i->request_list)) |
| printf("\t%d, %s_requests,\n", |
| wl_list_length(&i->request_list), i->name); |
| else |
| printf("\t0, NULL,\n"); |
| |
| if (!wl_list_empty(&i->event_list)) |
| printf("\t%d, %s_events,\n", |
| wl_list_length(&i->event_list), i->name); |
| else |
| printf("\t0, NULL,\n"); |
| |
| printf("};\n\n"); |
| |
| /* we won't need it any further */ |
| free_interface(i); |
| } |
| } |
| |
| static void |
| free_protocol(struct protocol *protocol) |
| { |
| free(protocol->name); |
| free(protocol->uppercase_name); |
| free(protocol->copyright); |
| free_description(protocol->description); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| struct parse_context ctx; |
| struct protocol protocol; |
| FILE *input = stdin; |
| char *input_filename = NULL; |
| int len; |
| void *buf; |
| bool help = false; |
| bool core_headers = false; |
| bool version = false; |
| bool strict = false; |
| bool fail = false; |
| int opt; |
| enum { |
| CLIENT_HEADER, |
| SERVER_HEADER, |
| PRIVATE_CODE, |
| PUBLIC_CODE, |
| CODE, |
| } mode; |
| |
| static const struct option options[] = { |
| { "help", no_argument, NULL, 'h' }, |
| { "version", no_argument, NULL, 'v' }, |
| { "include-core-only", no_argument, NULL, 'c' }, |
| { "strict", no_argument, NULL, 's' }, |
| { 0, 0, NULL, 0 } |
| }; |
| |
| while (1) { |
| opt = getopt_long(argc, argv, "hvcs", options, NULL); |
| |
| if (opt == -1) |
| break; |
| |
| switch (opt) { |
| case 'h': |
| help = true; |
| break; |
| case 'v': |
| version = true; |
| break; |
| case 'c': |
| core_headers = true; |
| break; |
| case 's': |
| strict = true; |
| break; |
| default: |
| fail = true; |
| break; |
| } |
| } |
| |
| argv += optind; |
| argc -= optind; |
| |
| if (help) |
| usage(EXIT_SUCCESS); |
| else if (version) |
| scanner_version(EXIT_SUCCESS); |
| else if ((argc != 1 && argc != 3) || fail) |
| usage(EXIT_FAILURE); |
| else if (strcmp(argv[0], "help") == 0) |
| usage(EXIT_SUCCESS); |
| else if (strcmp(argv[0], "client-header") == 0) |
| mode = CLIENT_HEADER; |
| else if (strcmp(argv[0], "server-header") == 0) |
| mode = SERVER_HEADER; |
| else if (strcmp(argv[0], "private-code") == 0) |
| mode = PRIVATE_CODE; |
| else if (strcmp(argv[0], "public-code") == 0) |
| mode = PUBLIC_CODE; |
| else if (strcmp(argv[0], "code") == 0) |
| mode = CODE; |
| else |
| usage(EXIT_FAILURE); |
| |
| if (argc == 3) { |
| input_filename = argv[1]; |
| input = fopen(input_filename, "r"); |
| if (input == NULL) { |
| fprintf(stderr, "Could not open input file: %s\n", |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| if (freopen(argv[2], "w", stdout) == NULL) { |
| fprintf(stderr, "Could not open output file: %s\n", |
| strerror(errno)); |
| fclose(input); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| /* initialize protocol structure */ |
| memset(&protocol, 0, sizeof protocol); |
| wl_list_init(&protocol.interface_list); |
| protocol.core_headers = core_headers; |
| |
| /* initialize context */ |
| memset(&ctx, 0, sizeof ctx); |
| ctx.protocol = &protocol; |
| if (input == stdin) |
| ctx.loc.filename = "<stdin>"; |
| else |
| ctx.loc.filename = input_filename; |
| |
| if (!is_dtd_valid(input, ctx.loc.filename)) { |
| fprintf(stderr, |
| "*******************************************************\n" |
| "* *\n" |
| "* WARNING: XML failed validation against built-in DTD *\n" |
| "* *\n" |
| "*******************************************************\n"); |
| if (strict) { |
| fclose(input); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| /* create XML parser */ |
| ctx.parser = XML_ParserCreate(NULL); |
| XML_SetUserData(ctx.parser, &ctx); |
| if (ctx.parser == NULL) { |
| fprintf(stderr, "failed to create parser\n"); |
| fclose(input); |
| exit(EXIT_FAILURE); |
| } |
| |
| XML_SetElementHandler(ctx.parser, start_element, end_element); |
| XML_SetCharacterDataHandler(ctx.parser, character_data); |
| |
| do { |
| buf = XML_GetBuffer(ctx.parser, XML_BUFFER_SIZE); |
| len = fread(buf, 1, XML_BUFFER_SIZE, input); |
| if (len < 0) { |
| fprintf(stderr, "fread: %s\n", strerror(errno)); |
| fclose(input); |
| exit(EXIT_FAILURE); |
| } |
| if (XML_ParseBuffer(ctx.parser, len, len == 0) == 0) { |
| fprintf(stderr, |
| "Error parsing XML at line %ld col %ld: %s\n", |
| XML_GetCurrentLineNumber(ctx.parser), |
| XML_GetCurrentColumnNumber(ctx.parser), |
| XML_ErrorString(XML_GetErrorCode(ctx.parser))); |
| fclose(input); |
| exit(EXIT_FAILURE); |
| } |
| } while (len > 0); |
| |
| XML_ParserFree(ctx.parser); |
| |
| switch (mode) { |
| case CLIENT_HEADER: |
| emit_header(&protocol, CLIENT); |
| break; |
| case SERVER_HEADER: |
| emit_header(&protocol, SERVER); |
| break; |
| case PRIVATE_CODE: |
| emit_code(&protocol, PRIVATE); |
| break; |
| case CODE: |
| fprintf(stderr, |
| "Using \"code\" is deprecated - use " |
| "private-code or public-code.\n" |
| "See the help page for details.\n"); |
| /* fallthrough */ |
| case PUBLIC_CODE: |
| emit_code(&protocol, PUBLIC); |
| break; |
| } |
| |
| free_protocol(&protocol); |
| fclose(input); |
| |
| return 0; |
| } |