summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/sexpr-wasm.c305
-rw-r--r--src/wasm-config.h.in3
-rw-r--r--src/wasm-option-parser.c233
-rw-r--r--src/wasm-option-parser.h65
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_ */