On Tue, 28 Apr 2015 22:57:17 +0300
Giulio Camuffo <giuliocamu...@gmail.com> wrote:

> When using this new option the generated code will include the new
> core headers instead of the old ones. The default needs to remain
> unchanged for backward compatibility with old code.
> The option handling logic code in comes directly from weston's
> shared/option-parser.c.

Hi,

Weston had a good reason to not use getopt() I believe: each module
parses its own arguments in turn, so command line options are parsed in
several passes, and an unknown option is an error only when all modules
have had a go.

getopt_long() is a GNU extension, but libwayland is already littered
with _GNU_SOURCE.

Would it make sense to use getopt_long() instead of copying Weston's
pecualiarities?

> ---
> 
> v2: - rename --include-core-headers to --include-core-only
>     - show the new option in the help
>     - use a function to get the headers name instead of the nested ternaries
> 
>  src/scanner.c | 259 
> ++++++++++++++++++++++++++++++++++++++++++++++------------
>  1 file changed, 207 insertions(+), 52 deletions(-)
> 
> diff --git a/src/scanner.c b/src/scanner.c
> index adc9aa3..a1a1361 100644
> --- a/src/scanner.c
> +++ b/src/scanner.c
> @@ -39,12 +39,16 @@ enum side {
>  static int
>  usage(int ret)
>  {
> -     fprintf(stderr, "usage: ./scanner [client-header|server-header|code]"
> +     fprintf(stderr, "usage: ./scanner [OPTION] 
> [client-header|server-header|code]"
>               " [input_file output_file]\n");
>       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");
> +                     "headers, server headers, or protocol marshalling 
> code.\n\n");
> +     fprintf(stderr, "options:\n");
> +     fprintf(stderr, "    --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);
>  }
>  
> @@ -68,6 +72,7 @@ struct protocol {
>       int null_run_length;
>       char *copyright;
>       struct description *description;
> +     int include_core_headers;

bool core_headers;?

>  };
>  
>  struct interface {
> @@ -985,10 +990,64 @@ format_copyright(const char *copyright)
>  }
>  
>  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(int 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_header(struct protocol *protocol, enum side side)
>  {
>       struct interface *i;
> +     struct wl_array types;
>       const char *s = (side == SERVER) ? "SERVER" : "CLIENT";
> +     char **p, *prev;
>  
>       if (protocol->copyright)
>               format_copyright(protocol->copyright);
> @@ -1007,17 +1066,39 @@ emit_header(struct protocol *protocol, enum side side)
>              "struct wl_resource;\n\n",
>              protocol->uppercase_name, s,
>              protocol->uppercase_name, s,
> -            (side == SERVER) ? "wayland-server.h" : "wayland-client.h");
> +            get_include_name(protocol->include_core_headers, side));
>  
> -     wl_list_for_each(i, &protocol->interface_list, link)
> -             printf("struct %s;\n", i->name);
> -     printf("\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);
> +     }

This collects all the object types from arguments...

>  
>       wl_list_for_each(i, &protocol->interface_list, link) {
> +             p = fail_on_null(wl_array_add(&types, sizeof *p));
> +             *p = i->name;
> +     }

...and this collects all object types from new interfaces themselves.
Ok.

> +
> +     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;
> +     }
> +     printf("\n");

And then you emit a sorted list of them with duplicates removed. A
useful change.

You do not mention this in the commit message, though.

After a bit of pondering, I understood why this is necessary: this is
how you get the forward declarations for all message arguments. Would
have been worth to note that. It is a change visible outside.

> +
> +     prev = NULL;
> +     wl_array_for_each(p, &types) {
> +             if (prev && strcmp(*p, prev) == 0)
> +                     continue;
>               printf("extern const struct wl_interface "
> -                    "%s_interface;\n",
> -                    i->name);
> +                    "%s_interface;\n", *p);
> +             prev = *p;
>       }
> +
> +     wl_array_release(&types);
>       printf("\n");
>  
>       wl_list_for_each(i, &protocol->interface_list, link) {
> @@ -1044,41 +1125,6 @@ emit_header(struct protocol *protocol, enum side side)
>  }
>  
>  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 void
>  emit_null_run(struct protocol *protocol)
>  {
>       int i;
> @@ -1181,14 +1227,6 @@ emit_messages(struct wl_list *message_list,
>       printf("};\n\n");
>  }
>  
> -static int
> -cmp_names(const void *p1, const void *p2)
> -{
> -     const char * const *s1 = p1, * const *s2 = p2;
> -
> -     return strcmp(*s1, *s2);
> -}
> -
>  static void
>  emit_code(struct protocol *protocol)
>  {
> @@ -1253,6 +1291,115 @@ emit_code(struct protocol *protocol)
>       }
>  }
>  
> +enum option_type {
> +     OPTION_INTEGER,
> +     OPTION_UNSIGNED_INTEGER,
> +     OPTION_STRING,
> +     OPTION_BOOLEAN
> +};
> +
> +struct option {
> +     enum option_type type;
> +     const char *name;
> +     int short_name;
> +     void *data;
> +};
> +
> +static int
> +handle_option(const struct option *option, char *value)
> +{
> +     char* p;
> +
> +     switch (option->type) {
> +     case OPTION_INTEGER:
> +             * (int32_t *) option->data = strtol(value, &p, 0);
> +             return *value && !*p;
> +     case OPTION_UNSIGNED_INTEGER:
> +             * (uint32_t *) option->data = strtoul(value, &p, 0);
> +             return *value && !*p;
> +     case OPTION_STRING:
> +             * (char **) option->data = strdup(value);
> +             return 1;
> +     default:
> +             return 0;
> +     }
> +}
> +
> +static int
> +long_option(const struct option *options, int count, char *arg)
> +{
> +     int k, len;
> +
> +     for (k = 0; k < count; k++) {
> +             if (!options[k].name)
> +                     continue;
> +
> +             len = strlen(options[k].name);
> +             if (strncmp(options[k].name, arg + 2, len) != 0)
> +                     continue;
> +
> +             if (options[k].type == OPTION_BOOLEAN) {
> +                     if (!arg[len + 2]) {
> +                             * (int32_t *) options[k].data = 1;
> +
> +                             return 1;
> +                     }
> +             } else if (arg[len+2] == '=') {
> +                     return handle_option(options + k, arg + len + 3);
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +static int
> +short_option(const struct option *options, int count, char *arg)
> +{
> +     int k;
> +
> +     if (!arg[1])
> +             return 0;
> +
> +     for (k = 0; k < count; k++) {
> +             if (options[k].short_name != arg[1])
> +                     continue;
> +
> +             if (options[k].type == OPTION_BOOLEAN) {
> +                     if (!arg[2]) {
> +                             * (int32_t *) options[k].data = 1;
> +
> +                             return 1;
> +                     }
> +             } else {
> +                     return handle_option(options + k, arg + 2);
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +static int
> +parse_options(const struct option *options,
> +           int count, int *argc, char *argv[])
> +{
> +     int i, j;
> +
> +     for (i = 1, j = 1; i < *argc; i++) {
> +             if (argv[i][0] == '-') {
> +                     if (argv[i][1] == '-') {
> +                             if (long_option(options, count, argv[i]))
> +                                     continue;
> +                     } else if (short_option(options, count, argv[i]))
> +                             continue;
> +             }
> +             argv[j++] = argv[i];
> +     }
> +     argv[j] = NULL;
> +     *argc = j;
> +
> +     return j;
> +}

Since all we need is another boolean option, using getopt_long should
make all this much less code.

> +
>  int main(int argc, char *argv[])
>  {
>       struct parse_context ctx;
> @@ -1260,15 +1407,22 @@ int main(int argc, char *argv[])
>       FILE *input = stdin;
>       int len;
>       void *buf;
> +     int help = 0, include_core = 0;
>       enum {
>               CLIENT_HEADER,
>               SERVER_HEADER,
>               CODE,
>       } mode;
>  
> +     const struct option options[] = {
> +             { OPTION_BOOLEAN, "help", 'h', &help },
> +             { OPTION_BOOLEAN, "include-core-only", 0, &include_core },
> +     };
> +     parse_options(options, sizeof(options) / sizeof(options[0]), &argc, 
> argv);
> +
>       if (argc != 2 && argc != 4)
>               usage(EXIT_FAILURE);
> -     else if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0)
> +     else if (strcmp(argv[1], "help") == 0 || help)
>               usage(EXIT_SUCCESS);
>       else if (strcmp(argv[1], "client-header") == 0)
>               mode = CLIENT_HEADER;
> @@ -1297,6 +1451,7 @@ int main(int argc, char *argv[])
>       protocol.type_index = 0;
>       protocol.null_run_length = 0;
>       protocol.copyright = NULL;
> +     protocol.include_core_headers = include_core;
>       memset(&ctx, 0, sizeof ctx);
>       ctx.protocol = &protocol;
>  

I have checked that the generated headers and code from wayland.xml
do not change without the new option, excluding the the order of some
declarations which is insignificant.

Also I checked that the difference between without and with
--include-core-only indeed is just the header to be included.

Techincal comments aside, this patch is:
Reviewed-by: Pekka Paalanen <pekka.paala...@collabora.co.uk>


Thanks,
pq
_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to