/* * Copyright © 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include "wayland-util.h" static const char copyright[] = "/*\n" " * Copyright © 2010 Kristian Høgsberg\n" " *\n" " * Permission to use, copy, modify, distribute, and sell this software and its\n" " * documentation for any purpose is hereby granted without fee, provided that\n" " * the above copyright notice appear in all copies and that both that copyright\n" " * notice and this permission notice appear in supporting documentation, and\n" " * that the name of the copyright holders not be used in advertising or\n" " * publicity pertaining to distribution of the software without specific,\n" " * written prior permission. The copyright holders make no representations\n" " * about the suitability of this software for any purpose. It is provided \"as\n" " * is\" without express or implied warranty.\n" " *\n" " * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n" " * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO\n" " * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n" " * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,\n" " * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n" " * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE\n" " * OF THIS SOFTWARE.\n" " */\n"; static int usage(int ret) { fprintf(stderr, "usage: ./scanner [header|code]\n"); exit(ret); } #define XML_BUFFER_SIZE 4096 struct protocol { struct wl_list interface_list; }; struct interface { char *name; char *uppercase_name; int version; struct wl_list request_list; struct wl_list event_list; struct wl_list link; }; struct message { char *name; char *uppercase_name; struct wl_list arg_list; struct wl_list link; }; enum arg_type { NEW_ID, INT, UNSIGNED, STRING, OBJECT, ARRAY }; struct arg { char *name; enum arg_type type; char *object_name; struct wl_list link; }; struct parse_context { struct protocol *protocol; struct interface *interface; struct message *message; }; static char * uppercase_dup(const char *src) { char *u; int i; u = strdup(src); for (i = 0; u[i]; i++) u[i] = toupper(u[i]); u[i] = '\0'; return u; } 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; const char *name, *type; int i, version; name = 0; type = 0; version = 0; for (i = 0; atts[i]; i += 2) { if (strcmp(atts[i], "name") == 0) name = atts[i + 1]; if (strcmp(atts[i], "version") == 0) version = atoi(atts[i + 1]); if (strcmp(atts[i], "type") == 0) type = atts[i + 1]; } if (strcmp(element_name, "interface") == 0) { if (name == NULL) { fprintf(stderr, "no interface name given\n"); exit(EXIT_FAILURE); } if (version == 0) { fprintf(stderr, "no interface version given\n"); exit(EXIT_FAILURE); } interface = malloc(sizeof *interface); interface->name = strdup(name); interface->uppercase_name = uppercase_dup(name); interface->version = version; wl_list_init(&interface->request_list); wl_list_init(&interface->event_list); wl_list_insert(ctx->protocol->interface_list.prev, &interface->link); ctx->interface = interface; } else if (strcmp(element_name, "request") == 0 || strcmp(element_name, "event") == 0) { if (name == NULL) { fprintf(stderr, "no request name given\n"); exit(EXIT_FAILURE); } message = malloc(sizeof *message); message->name = strdup(name); message->uppercase_name = uppercase_dup(name); wl_list_init(&message->arg_list); 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); ctx->message = message; } else if (strcmp(element_name, "arg") == 0) { arg = malloc(sizeof *arg); arg->name = strdup(name); if (strcmp(type, "new_id") == 0) arg->type = NEW_ID; else if (strcmp(type, "int") == 0) arg->type = INT; else if (strcmp(type, "uint") == 0) arg->type = UNSIGNED; else if (strcmp(type, "string") == 0) arg->type = STRING; else if (strcmp(type, "array") == 0) arg->type = ARRAY; else { arg->type = OBJECT; arg->object_name = strdup(type); } wl_list_insert(ctx->message->arg_list.prev, &arg->link); } } 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 WL_%s_%s\t%d\n", interface->uppercase_name, m->uppercase_name, opcode++); printf("\n"); } static void emit_structs(struct wl_list *message_list, struct interface *interface) { struct message *m; struct arg *a; int is_interface; is_interface = message_list == &interface->request_list; printf("struct wl_%s_%s {\n", interface->name, is_interface ? "interface" : "listener"); wl_list_for_each(m, message_list, link) { printf("\tvoid (*%s)(", m->name); if (is_interface) { printf("struct wl_client *client, struct wl_%s *%s", interface->name, interface->name); } else { printf("void *data, struct wl_%s *%s", interface->name, interface->name); } if (!wl_list_empty(&m->arg_list)) printf(", "); wl_list_for_each(a, &m->arg_list, link) { switch (a->type) { default: case INT: printf("int32_t "); break; case NEW_ID: case UNSIGNED: printf("uint32_t "); break; case STRING: printf("const char *"); break; case OBJECT: printf("struct wl_%s *", a->object_name); break; case ARRAY: printf("struct wl_array *"); break; } printf("%s%s", a->name, a->link.next == &m->arg_list ? "" : ", "); } printf(");\n"); } printf("};\n\n"); } static void emit_header(struct protocol *protocol, int server) { struct interface *i; printf("%s\n\n" "#ifndef WAYLAND_PROTOCOL_H\n" "#define WAYLAND_PROTOCOL_H\n" "\n" "#ifdef __cplusplus\n" "extern \"C\" {\n" "#endif\n" "\n" "#include \n" "#include \"wayland-util.h\"\n\n" "struct wl_client;\n\n", copyright); wl_list_for_each(i, &protocol->interface_list, link) printf("struct wl_%s;\n", i->name); printf("\n"); wl_list_for_each(i, &protocol->interface_list, link) { if (server) { emit_structs(&i->request_list, i); emit_opcodes(&i->event_list, i); } else { emit_structs(&i->event_list, i); emit_opcodes(&i->request_list, i); } printf("extern const struct wl_interface " "wl_%s_interface;\n\n", i->name); } printf("#ifdef __cplusplus\n" "}\n" "#endif\n" "\n" "#endif\n"); } static void emit_messages(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); wl_list_for_each(a, &m->arg_list, link) { switch (a->type) { default: case INT: printf("i"); break; case NEW_ID: printf("n"); break; case UNSIGNED: printf("u"); break; case STRING: printf("s"); break; case OBJECT: printf("o"); break; case ARRAY: printf("a"); break; } } printf("\" },\n"); } printf("};\n\n"); } static void emit_code(struct protocol *protocol) { struct interface *i; printf("%s\n\n" "#include \n" "#include \n" "#include \"wayland-util.h\"\n\n", copyright); wl_list_for_each(i, &protocol->interface_list, link) { emit_messages(&i->request_list, i, "requests"); emit_messages(&i->event_list, i, "events"); printf("WL_EXPORT const struct wl_interface " "wl_%s_interface = {\n" "\t\"%s\", %d,\n", i->name, i->name, i->version); if (!wl_list_empty(&i->request_list)) printf("\tARRAY_LENGTH(%s_requests), %s_requests,\n", i->name, i->name); else printf("\t0, NULL,\n"); if (!wl_list_empty(&i->event_list)) printf("\tARRAY_LENGTH(%s_events), %s_events,\n", i->name, i->name); else printf("\t0, NULL,\n"); printf("};\n\n"); } } int main(int argc, char *argv[]) { struct parse_context ctx; struct protocol protocol; XML_Parser parser; int len; void *buf; if (argc != 2) usage(EXIT_FAILURE); wl_list_init(&protocol.interface_list); ctx.protocol = &protocol; parser = XML_ParserCreate(NULL); XML_SetUserData(parser, &ctx); if (parser == NULL) { fprintf(stderr, "failed to create parser\n"); exit(EXIT_FAILURE); } XML_SetElementHandler(parser, start_element, NULL); do { buf = XML_GetBuffer(parser, XML_BUFFER_SIZE); len = fread(buf, 1, XML_BUFFER_SIZE, stdin); if (len < 0) { fprintf(stderr, "fread: %s\n", strerror(errno)); exit(EXIT_FAILURE); } XML_ParseBuffer(parser, len, len == 0); } while (len > 0); XML_ParserFree(parser); if (strcmp(argv[1], "client-header") == 0) { emit_header(&protocol, 0); } else if (strcmp(argv[1], "server-header") == 0) { emit_header(&protocol, 1); } else if (strcmp(argv[1], "code") == 0) { emit_code(&protocol); } return 0; }