diff options
Diffstat (limited to 'src/opt.c')
-rw-r--r-- | src/opt.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/src/opt.c b/src/opt.c new file mode 100644 index 000000000000..730b1cd51f19 --- /dev/null +++ b/src/opt.c @@ -0,0 +1,250 @@ +/* + * ***************************************************************************** + * + * Copyright (c) 2018-2020 Gavin D. Howard and contributors. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * ***************************************************************************** + * + * Adapted from https://github.com/skeeto/optparse + * + * ***************************************************************************** + * + * Code for getopt_long() replacement. It turns out that getopt_long() has + * different behavior on different platforms. + * + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include <status.h> +#include <opt.h> +#include <vm.h> + +static inline bool bc_opt_longoptsEnd(const BcOptLong *longopts, size_t i) { + return !longopts[i].name && !longopts[i].val; +} + +static const char* bc_opt_longopt(const BcOptLong *longopts, int c) { + + size_t i; + + for (i = 0; !bc_opt_longoptsEnd(longopts, i); ++i) { + if (longopts[i].val == c) return longopts[i].name; + } + + return "NULL"; +} + +static void bc_opt_error(BcError err, int c, const char *str) { + if (err == BC_ERROR_FATAL_OPTION) bc_vm_error(err, 0, str); + else bc_vm_error(err, 0, (int) c, str); +} + +static int bc_opt_type(const BcOptLong *longopts, char c) { + + size_t i; + + if (c == ':') return -1; + + for (i = 0; !bc_opt_longoptsEnd(longopts, i) && longopts[i].val != c; ++i); + + if (bc_opt_longoptsEnd(longopts, i)) return -1; + + return (int) longopts[i].type; +} + +static int bc_opt_parseShort(BcOpt *o, const BcOptLong *longopts) { + + int type; + char *next; + char *option = o->argv[o->optind]; + int ret = -1; + + o->optopt = 0; + o->optarg = NULL; + + option += o->subopt + 1; + o->optopt = option[0]; + + type = bc_opt_type(longopts, option[0]); + next = o->argv[o->optind + 1]; + + switch (type) { + + case -1: + case BC_OPT_BC_ONLY: + case BC_OPT_DC_ONLY: + { + if (type == -1 || (type == BC_OPT_BC_ONLY && !BC_IS_BC) || + (type == BC_OPT_DC_ONLY && BC_IS_BC)) + { + char str[2] = {0, 0}; + + str[0] = option[0]; + o->optind += 1; + + bc_opt_error(BC_ERROR_FATAL_OPTION, option[0], str); + } + } + // Fallthrough. + case BC_OPT_NONE: + { + if (option[1]) o->subopt += 1; + else { + o->subopt = 0; + o->optind += 1; + } + + ret = (int) option[0]; + break; + } + + case BC_OPT_REQUIRED: + { + o->subopt = 0; + o->optind += 1; + + if (option[1]) o->optarg = option + 1; + else if (next != NULL) { + o->optarg = next; + o->optind += 1; + } + else bc_opt_error(BC_ERROR_FATAL_OPTION_NO_ARG, option[0], + bc_opt_longopt(longopts, option[0])); + + + ret = (int) option[0]; + break; + } + } + + return ret; +} + +static bool bc_opt_longoptsMatch(const char *name, const char *option) { + + const char *a = option, *n = name; + + if (name == NULL) return false; + + for (; *a && *n && *a != '='; ++a, ++n) { + if (*a != *n) return false; + } + + return (*n == '\0' && (*a == '\0' || *a == '=')); +} + +static char* bc_opt_longoptsArg(char *option) { + + for (; *option && *option != '='; ++option); + + if (*option == '=') return option + 1; + else return NULL; +} + +int bc_opt_parse(BcOpt *o, const BcOptLong *longopts) { + + size_t i; + char *option; + bool empty; + + do { + + option = o->argv[o->optind]; + if (option == NULL) return -1; + + empty = !strcmp(option, ""); + o->optind += empty; + + } while (empty); + + if (BC_OPT_ISDASHDASH(option)) { + + // Consume "--". + o->optind += 1; + return -1; + } + else if (BC_OPT_ISSHORTOPT(option)) return bc_opt_parseShort(o, longopts); + else if (!BC_OPT_ISLONGOPT(option)) return -1; + + o->optopt = 0; + o->optarg = NULL; + + // Skip "--" at beginning of the option. + option += 2; + o->optind += 1; + + for (i = 0; !bc_opt_longoptsEnd(longopts, i); i++) { + + const char *name = longopts[i].name; + + if (bc_opt_longoptsMatch(name, option)) { + + char *arg; + + o->optopt = longopts[i].val; + arg = bc_opt_longoptsArg(option); + + if ((longopts[i].type == BC_OPT_BC_ONLY && !BC_IS_BC) || + (longopts[i].type == BC_OPT_DC_ONLY && BC_IS_BC)) + { + bc_opt_error(BC_ERROR_FATAL_OPTION, o->optopt, name); + } + + if (longopts[i].type == BC_OPT_NONE && arg != NULL) + { + bc_opt_error(BC_ERROR_FATAL_OPTION_ARG, o->optopt, name); + } + + if (arg != NULL) o->optarg = arg; + else if (longopts[i].type == BC_OPT_REQUIRED) { + + o->optarg = o->argv[o->optind]; + + if (o->optarg != NULL) o->optind += 1; + else bc_opt_error(BC_ERROR_FATAL_OPTION_NO_ARG, + o->optopt, name); + } + + return o->optopt; + } + } + + bc_opt_error(BC_ERROR_FATAL_OPTION, 0, option); + + return -1; +} + +void bc_opt_init(BcOpt *o, char *argv[]) { + o->argv = argv; + o->optind = 1; + o->subopt = 0; + o->optarg = NULL; +} |