diff options
author | Ben Smith <binji@chromium.org> | 2016-03-20 23:01:02 -0700 |
---|---|---|
committer | Ben Smith <binji@chromium.org> | 2016-03-20 23:19:04 -0700 |
commit | 02634375f056e8cae5176bc6ebb9bf4e160fe936 (patch) | |
tree | abb08223dd7a2ba184b3e633b74be79b34790990 /src/wasm-option-parser.c | |
parent | 4cdce96d43e421f714df37da03f802950bd811b1 (diff) | |
download | wabt-02634375f056e8cae5176bc6ebb9bf4e160fe936.tar.gz wabt-02634375f056e8cae5176bc6ebb9bf4e160fe936.tar.bz2 wabt-02634375f056e8cae5176bc6ebb9bf4e160fe936.zip |
remove getopt, use WasmOptionParser instead
Diffstat (limited to 'src/wasm-option-parser.c')
-rw-r--r-- | src/wasm-option-parser.c | 233 |
1 files changed, 233 insertions, 0 deletions
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"); + } +} |