diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/sexpr-wasm.c | 305 | ||||
-rw-r--r-- | src/wasm-config.h.in | 3 | ||||
-rw-r--r-- | src/wasm-option-parser.c | 233 | ||||
-rw-r--r-- | src/wasm-option-parser.h | 65 |
4 files changed, 381 insertions, 225 deletions
diff --git a/src/sexpr-wasm.c b/src/sexpr-wasm.c index 938466d4..9415e19f 100644 --- a/src/sexpr-wasm.c +++ b/src/sexpr-wasm.c @@ -20,20 +20,26 @@ #include <stdlib.h> #include "wasm-config.h" -#if HAVE_GETOPT_H -#include <getopt.h> -#else -#define USE_MIN_PARSER 1 -#endif - #include "wasm-ast.h" #include "wasm-binary-writer.h" #include "wasm-check.h" #include "wasm-common.h" +#include "wasm-option-parser.h" #include "wasm-parser.h" #include "wasm-stack-allocator.h" #include "wasm-writer.h" +static const char* s_infile; +static const char* s_outfile; +static int s_dump_module; +static int s_verbose; +static WasmWriteBinaryOptions s_write_binary_options = + WASM_WRITE_BINARY_OPTIONS_DEFAULT; +static int s_use_libc_allocator; + +#define NOPE WASM_OPTION_NO_ARGUMENT +#define YEP WASM_OPTION_HAS_ARGUMENT + enum { FLAG_VERBOSE, FLAG_HELP, @@ -48,248 +54,103 @@ enum { NUM_FLAGS }; -static const char* s_infile; -static const char* s_outfile; -static int s_dump_module; -static int s_verbose; -static WasmWriteBinaryOptions s_write_binary_options = - WASM_WRITE_BINARY_OPTIONS_DEFAULT; -static int s_use_libc_allocator; - -#ifdef USE_MIN_PARSER -typedef struct option { - const char* name; - int has_arg; - int* flag; - int val; -} option; -#define null_argument 0 /*Argument Null*/ -#define no_argument 0 /*Argument Switch Only*/ -#define required_argument 1 /*Argument Required*/ -#define optional_argument 2 /*Argument Optional*/ - -typedef struct OptionText { - char long_name[64]; - char short_name[64]; -} OptionText; -#endif /* HAVE_GETOPT_H */ - -static struct option s_long_options[] = { - {"verbose", no_argument, NULL, 'v'}, - {"help", no_argument, NULL, 'h'}, - {"dump-module", no_argument, NULL, 'd'}, - {"output", required_argument, NULL, 'o'}, - {"spec", no_argument, NULL, 0}, - {"spec-verbose", no_argument, NULL, 0}, - {"use-libc-allocator", no_argument, NULL, 0}, - {"no-canonicalize-leb128s", no_argument, NULL, 0}, - {"no-remap-locals", no_argument, NULL, 0}, - {"debug-names", no_argument, NULL, 0}, - {NULL, 0, NULL, 0}, -}; -#define OPTIONS_LENGTH (WASM_ARRAY_SIZE(s_long_options) - 1) -WASM_STATIC_ASSERT(NUM_FLAGS == OPTIONS_LENGTH); - -typedef struct OptionHelp { - int flag; - const char* metavar; - const char* help; -} OptionHelp; - -static OptionHelp s_option_help[] = { - {FLAG_VERBOSE, NULL, "use multiple times for more info"}, - {FLAG_HELP, NULL, "print this help message"}, - {FLAG_DUMP_MODULE, NULL, "print a hexdump of the module to stdout"}, - {FLAG_OUTPUT, "FILENAME", "output file for the generated binary format"}, - {FLAG_SPEC, NULL, +static WasmOption s_options[] = { + {FLAG_VERBOSE, 'v', "verbose", NULL, NOPE, + "use multiple times for more info"}, + {FLAG_HELP, 'h', "help", NULL, NOPE, "print this help message"}, + {FLAG_DUMP_MODULE, 'd', "dump-module", NULL, NOPE, + "print a hexdump of the module to stdout"}, + {FLAG_OUTPUT, 'o', "output", "FILE", YEP, + "output file for the generated binary format"}, + {FLAG_SPEC, 0, "spec", NULL, NOPE, "parse a file with multiple modules and assertions, like the spec tests"}, - {FLAG_SPEC_VERBOSE, NULL, "print logging messages when running spec files"}, - {FLAG_USE_LIBC_ALLOCATOR, NULL, + {FLAG_SPEC_VERBOSE, 0, "spec-verbose", NULL, NOPE, + "print logging messages when running spec files"}, + {FLAG_USE_LIBC_ALLOCATOR, 0, "use-libc-allocator", NULL, NOPE, "use malloc, free, etc. instead of stack allocator"}, - {FLAG_NO_CANONICALIZE_LEB128S, NULL, + {FLAG_NO_CANONICALIZE_LEB128S, 0, "no-canonicalize-leb128s", NULL, NOPE, "Write all LEB128 sizes as 5-bytes instead of their minimal size"}, - {FLAG_NO_REMAP_LOCALS, NULL, + {FLAG_NO_REMAP_LOCALS, 0, "no-remap-locals", NULL, NOPE, "If set, function locals are written in source order, instead of packing " "them to reduce size"}, - {FLAG_DEBUG_NAMES, NULL, "Write debug names to the generated binary file"}, - {NUM_FLAGS, NULL}, + {FLAG_DEBUG_NAMES, 0, "debug-names", NULL, NOPE, + "Write debug names to the generated binary file"}, }; -#define OPTIONS_HELP_LENGTH (WASM_ARRAY_SIZE(s_option_help) - 1) -WASM_STATIC_ASSERT(NUM_FLAGS == OPTIONS_HELP_LENGTH); - -static void usage(const char* prog) { - printf("usage: %s [option] filename\n", prog); - printf("options:\n"); - struct option* opt = &s_long_options[0]; - int i = 0; - for (; opt->name; ++i, ++opt) { - OptionHelp* help = NULL; - - int n = 0; - while (s_option_help[n].help) { - if (i == s_option_help[n].flag) { - help = &s_option_help[n]; - break; - } - n++; - } - - if (opt->val) { - printf(" -%c, ", opt->val); - } else { - printf(" "); - } - - if (help && help->metavar) { - char buf[100]; - wasm_snprintf(buf, 100, "%s=%s", opt->name, help->metavar); - printf("--%-30s", buf); - } else { - printf("--%-30s", opt->name); - } +WASM_STATIC_ASSERT(NUM_FLAGS == WASM_ARRAY_SIZE(s_options)); + +static void on_option(struct WasmOptionParser* parser, + struct WasmOption* option, + const char* argument) { + switch (option->id) { + case FLAG_VERBOSE: + s_verbose++; + s_write_binary_options.log_writes = 1; + break; - if (help) { - printf("%s", help->help); - } + case FLAG_HELP: + wasm_print_help(parser); + exit(0); + break; - printf("\n"); - } - exit(0); -} + case FLAG_DUMP_MODULE: + s_dump_module = 1; + break; -static void parse_options(int argc, char** argv) { - int c; - int option_index = 0; -#ifdef USE_MIN_PARSER - const char* optarg = NULL; - int optind = 1; - OptionText options_text[OPTIONS_LENGTH]; - int i = 0; - for (; i < OPTIONS_LENGTH; i++) { - struct option* opt = &s_long_options[i]; - sprintf(options_text[i].long_name, "--%s", opt->name); - if (opt->val) { - sprintf(options_text[i].short_name, "-%c", (char)opt->val); - } - } -#endif /* USE_MIN_PARSER */ - while (1) { -#ifndef USE_MIN_PARSER - c = getopt_long(argc, argv, "vhdo:", s_long_options, &option_index); - if (c == -1) { + case FLAG_OUTPUT: + s_outfile = argument; break; - } -#else - if (optind >= argc) { + case FLAG_SPEC: + s_write_binary_options.spec = 1; break; - } - optarg = NULL; - option_index = -1; - const char* in_opt = argv[optind++]; - i = 0; - for (; i < OPTIONS_LENGTH; i++) { - const struct OptionText* optText = &options_text[i]; - const struct option* opt = &s_long_options[i]; - if (strcmp(in_opt, optText->long_name) == 0 || - (opt->val && strcmp(in_opt, optText->short_name) == 0)) { - option_index = i; - c = opt->val; - if (opt->has_arg != no_argument) { - if (optind < argc) { - optarg = argv[optind++]; - } else if (opt->has_arg == required_argument) { - WASM_FATAL("Missing argument for option %s.\n", opt->name); - usage(argv[0]); - } - } - break; - } - } - // no argument found - if (option_index == -1) { - --optind; + case FLAG_SPEC_VERBOSE: + s_write_binary_options.spec_verbose = 1; break; - } -#endif /* USE_MIN_PARSER */ - - redo_switch: - switch (c) { - case 0: - c = s_long_options[option_index].val; - if (c) { - goto redo_switch; - } - switch (option_index) { - case FLAG_VERBOSE: - case FLAG_HELP: - case FLAG_DUMP_MODULE: - case FLAG_OUTPUT: - /* Handled above by goto */ - assert(0); - break; - - case FLAG_SPEC: - s_write_binary_options.spec = 1; - break; - - case FLAG_SPEC_VERBOSE: - s_write_binary_options.spec_verbose = 1; - break; - - case FLAG_USE_LIBC_ALLOCATOR: - s_use_libc_allocator = 1; - break; - - case FLAG_NO_CANONICALIZE_LEB128S: - s_write_binary_options.canonicalize_lebs = 0; - break; - - case FLAG_NO_REMAP_LOCALS: - s_write_binary_options.remap_locals = 0; - break; - - case FLAG_DEBUG_NAMES: - s_write_binary_options.write_debug_names = 1; - break; - } - break; + case FLAG_USE_LIBC_ALLOCATOR: + s_use_libc_allocator = 1; + break; - case 'v': - s_verbose++; - s_write_binary_options.log_writes = 1; - break; + case FLAG_NO_CANONICALIZE_LEB128S: + s_write_binary_options.canonicalize_lebs = 0; + break; - case 'h': - usage(argv[0]); + case FLAG_NO_REMAP_LOCALS: + s_write_binary_options.remap_locals = 0; + break; - case 'd': - s_dump_module = 1; - break; + case FLAG_DEBUG_NAMES: + s_write_binary_options.write_debug_names = 1; + break; + } +} - case 'o': - s_outfile = optarg; - break; +static void on_argument(struct WasmOptionParser* parser, const char* argument) { + s_infile = argument; +} - case '?': - break; +static void on_option_error(struct WasmOptionParser* parser, + const char* message) { + WASM_FATAL("%s\n", message); +} - default: - WASM_FATAL("getopt_long returned '%c' (%d)\n", c, c); - break; - } - } +static void parse_options(int argc, char** argv) { + WasmOptionParser parser; + WASM_ZERO_MEMORY(parser); + parser.options = s_options; + parser.num_options = WASM_ARRAY_SIZE(s_options); + parser.on_option = on_option; + parser.on_argument = on_argument; + parser.on_error = on_option_error; + wasm_parse_options(&parser, argc, argv); if (s_dump_module && s_write_binary_options.spec) WASM_FATAL("--dump-module flag incompatible with --spec flag\n"); - if (optind < argc) { - s_infile = argv[optind]; - } else { + if (!s_infile) { + wasm_print_help(&parser); WASM_FATAL("No filename given.\n"); - usage(argv[0]); } } diff --git a/src/wasm-config.h.in b/src/wasm-config.h.in index f816082e..0f128a60 100644 --- a/src/wasm-config.h.in +++ b/src/wasm-config.h.in @@ -22,9 +22,6 @@ /* Whether <alloca.h> is available */ #cmakedefine01 HAVE_ALLOCA_H -/* Whether <getopt.h> is available */ -#cmakedefine01 HAVE_GETOPT_H - /* Whether <unistd.h> is available */ #cmakedefine01 HAVE_UNISTD_H diff --git a/src/wasm-option-parser.c b/src/wasm-option-parser.c new file mode 100644 index 00000000..d6d46c5d --- /dev/null +++ b/src/wasm-option-parser.c @@ -0,0 +1,233 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm-option-parser.h" + +#include <alloca.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "wasm-config.h" + +static int option_match(const char* s, const char* full, int has_argument) { + int i; + for (i = 0; ; i++) { + if (full[i] == '\0') { + /* perfect match. return +1, so it will be preferred over a longer option + * with the same prefix */ + if (s[i] == '\0') + return i + 1; + + /* we want to fail if s is longer than full, e.g. --foobar vs. --foo. + * However, if s ends with an '=', it's OK. */ + if (!(has_argument && s[i] == '=')) + return -1; + break; + } + if (s[i] != full[i]) + break; + } + return i; +} + +static void error(WasmOptionParser* parser, const char* format, ...) { + va_list args; + va_list args_copy; + va_start(args, format); + va_copy(args_copy, args); + + char buffer[128]; + int len = wasm_vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + if (len + 1 > sizeof(buffer)) { + char* buffer2 = alloca(len + 1); + len = wasm_vsnprintf(buffer2, len + 1, format, args_copy); + va_end(args_copy); + } + + parser->on_error(parser, buffer); +} + +void wasm_parse_options(WasmOptionParser* parser, + int argc, + char** argv) { + parser->argv0 = argv[0]; + + int i; + int j; + int k; + for (i = 1; i < argc; ++i) { + char* arg = argv[i]; + if (arg[0] == '-') { + if (arg[1] == '-') { + /* long option */ + int best_index = -1; + int best_length = 0; + int best_count = 0; + for (j = 0; j < parser->num_options; ++j) { + WasmOption* option = &parser->options[j]; + if (option->long_name) { + int match_length = + option_match(&arg[2], option->long_name, option->has_argument); + if (match_length > best_length) { + best_index = j; + best_length = match_length; + best_count = 1; + } else if (match_length == best_length && best_length > 0) { + best_count++; + } + } + } + + if (best_count > 1) { + error(parser, "ambiguous option \"%s\"", arg); + continue; + } else if (best_count == 0) { + error(parser, "unknown option \"%s\"", arg); + continue; + } + + WasmOption* best_option = &parser->options[best_index]; + const char* option_argument = NULL; + if (best_option->has_argument) { + if (arg[best_length] == '=') { + option_argument = &arg[best_length + 1]; + } else { + if (i + 1 == argc || argv[i + 1][0] == '-') { + error(parser, "option \"--%s\" requires argument", + best_option->long_name); + continue; + } + ++i; + option_argument = argv[i]; + } + } + parser->on_option(parser, best_option, option_argument); + } else { + /* short option */ + if (arg[1] == '\0') { + /* just "-" */ + parser->on_argument(parser, arg); + continue; + } + + /* allow short names to be combined, e.g. "-d -v" => "-dv" */ + for (k = 1; arg[k]; ++k) { + int matched = 0; + for (j = 0; j < parser->num_options; ++j) { + WasmOption* option = &parser->options[j]; + if (option->short_name && arg[k] == option->short_name) { + const char* option_argument = NULL; + if (option->has_argument) { + /* a short option with a required argument cannot be followed + * by other short options */ + if (arg[k + 1] != '\0') { + error(parser, "option \"-%c\" requires argument", + option->short_name); + break; + } + + if (i + 1 == argc || argv[i + 1][0] == '-') { + error(parser, "option \"-%c\" requires argument", + option->short_name); + break; + } + ++i; + option_argument = argv[i]; + } + parser->on_option(parser, option, option_argument); + matched = 1; + break; + } + } + + if (!matched) { + error(parser, "unknown option \"-%c\"", arg[k]); + continue; + } + } + } + } else { + /* non-option argument */ + parser->on_argument(parser, arg); + } + } +} + +void wasm_print_help(WasmOptionParser* parser) { + int i; + /* TODO(binji): do something more generic for filename here */ + printf("usage: %s [options] filename\n", parser->argv0); + printf("options:\n"); + + const int extra_space = 8; + int longest_name_length = 0; + for (i = 0; i < parser->num_options; ++i) { + WasmOption* option = &parser->options[i]; + int length; + if (option->long_name) { + if (option->metavar) { + length = + wasm_snprintf(NULL, 0, "%s=%s", option->long_name, option->metavar); + } else { + length = wasm_snprintf(NULL, 0, "%s", option->long_name); + } + } else { + continue; + } + + if (length > longest_name_length) + longest_name_length = length; + } + + size_t buffer_size = longest_name_length + 1; + char* buffer = alloca(buffer_size); + + for (i = 0; i < parser->num_options; ++i) { + WasmOption* option = &parser->options[i]; + if (!option->short_name && !option->long_name) + continue; + + if (option->short_name) + printf(" -%c, ", option->short_name); + else + printf(" "); + + char format[20]; + if (option->long_name) { + wasm_snprintf(format, sizeof(format), "--%%-%ds", + longest_name_length + extra_space); + + if (option->metavar) { + wasm_snprintf(buffer, buffer_size, "%s=%s", option->long_name, + option->metavar); + printf(format, buffer); + } else { + printf(format, option->long_name); + } + } else { + /* +2 for the extra "--" above */ + wasm_snprintf(format, sizeof(format), "%%-%ds", + longest_name_length + extra_space + 2); + printf(format, ""); + } + + if (option->help) + printf("%s", option->help); + printf("\n"); + } +} diff --git a/src/wasm-option-parser.h b/src/wasm-option-parser.h new file mode 100644 index 00000000..20601914 --- /dev/null +++ b/src/wasm-option-parser.h @@ -0,0 +1,65 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WASM_OPTION_PARSER_H_ +#define WASM_OPTION_PARSER_H_ + +#include "wasm-common.h" + +enum { + WASM_OPTION_NO_ARGUMENT, + WASM_OPTION_HAS_ARGUMENT, +}; + +struct WasmOption; +struct WasmOptionParser; +typedef void (*WasmOptionCallback)(struct WasmOptionParser*, + struct WasmOption*, + const char* argument); +typedef void (*WasmArgumentCallback)(struct WasmOptionParser*, + const char* argument); +typedef void (*WasmOptionErrorCallback)(struct WasmOptionParser*, + const char* message); + +typedef struct WasmOption { + int id; + char short_name; + const char* long_name; + const char* metavar; + int has_argument; + const char* help; +} WasmOption; + +typedef struct WasmOptionParser { + WasmOption* options; + int num_options; + WasmOptionCallback on_option; + WasmArgumentCallback on_argument; + WasmOptionErrorCallback on_error; + void* user_data; + + /* cached after call to wasm_parse_options */ + char* argv0; +} WasmOptionParser; + +WASM_EXTERN_C_BEGIN +void wasm_parse_options(WasmOptionParser* parser, + int argc, + char** argv); +void wasm_print_help(WasmOptionParser* parser); +WASM_EXTERN_C_END + +#endif /* WASM_OPTION_PARSER_H_ */ |