/*
|
* 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 "config.h"
|
#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 <expat.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
|
|
#include "wayland-util.h"
|
|
#define PROGRAM_NAME "wayland-scanner"
|
|
enum side {
|
CLIENT,
|
SERVER,
|
};
|
|
static int
|
usage(int ret)
|
{
|
fprintf(stderr, "usage: %s [OPTION] [client-header|server-header|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");
|
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");
|
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);
|
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;
|
};
|
|
struct entry {
|
char *name;
|
char *uppercase_name;
|
char *value;
|
char *summary;
|
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;
|
};
|
|
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
|
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);
|
}
|
|
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);
|
|
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;
|
}
|
|
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");
|
|
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");
|
|
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");
|
|
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;
|
|
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;
|
}
|
|
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");
|
|
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++;
|
|
/* Fall through to OBJECT case. */
|
|
case OBJECT:
|
if (interface_name)
|
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");
|
|
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");
|
|
entry = create_entry(name, value);
|
|
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);
|
|
if (e == NULL)
|
fail(&ctx->loc,
|
"could not find enumeration %s",
|
a->enumeration_name);
|
|
switch (a->type) {
|
case INT:
|
if (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");
|
}
|
}
|
}
|
|
}
|
|
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)
|
printf("\t/**\n"
|
"\t * %s\n"
|
"\t */\n", entry->summary);
|
printf("\t%s_%s_%s = %s,\n",
|
interface->uppercase_name,
|
e->uppercase_name,
|
entry->uppercase_name, entry->value);
|
}
|
printf("};\n");
|
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("/**\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("\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(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("\", types + %d },\n", m->type_index);
|
}
|
|
printf("};\n\n");
|
}
|
|
static void
|
emit_code(struct protocol *protocol)
|
{
|
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");
|
|
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 *types[] = {\n");
|
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(&i->request_list, i, "requests");
|
emit_messages(&i->event_list, i, "events");
|
|
printf("WL_EXPORT const struct wl_interface "
|
"%s_interface = {\n"
|
"\t\"%s\", %d,\n",
|
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 fail = false;
|
int opt;
|
enum {
|
CLIENT_HEADER,
|
SERVER_HEADER,
|
CODE,
|
} mode;
|
|
static const struct option options[] = {
|
{ "help", no_argument, NULL, 'h' },
|
{ "version", no_argument, NULL, 'v' },
|
{ "include-core-only", no_argument, NULL, 'c' },
|
{ 0, 0, NULL, 0 }
|
};
|
|
while (1) {
|
opt = getopt_long(argc, argv, "hvc", options, NULL);
|
|
if (opt == -1)
|
break;
|
|
switch (opt) {
|
case 'h':
|
help = true;
|
break;
|
case 'v':
|
version = true;
|
break;
|
case 'c':
|
core_headers = 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], "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");
|
}
|
|
/* 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: %m\n");
|
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 CODE:
|
emit_code(&protocol);
|
break;
|
}
|
|
free_protocol(&protocol);
|
fclose(input);
|
|
return 0;
|
}
|