diff options
Diffstat (limited to 'src/opt.c')
-rw-r--r-- | src/opt.c | 76 |
1 files changed, 76 insertions, 0 deletions
@@ -47,10 +47,22 @@ #include <opt.h> #include <vm.h> +/** + * Returns true if index @a i is the end of the longopts array. + * @param longopts The long options array. + * @param i The index to test. + * @return True if @a i is the last index, false otherwise. + */ static inline bool bc_opt_longoptsEnd(const BcOptLong *longopts, size_t i) { return !longopts[i].name && !longopts[i].val; } +/** + * Returns the name of the long option that matches the character @a c. + * @param longopts The long options array. + * @param c The character to match against. + * @return The name of the long option that matches @a c, or "NULL". + */ static const char* bc_opt_longopt(const BcOptLong *longopts, int c) { size_t i; @@ -64,11 +76,23 @@ static const char* bc_opt_longopt(const BcOptLong *longopts, int c) { return "NULL"; } +/** + * Issues a fatal error for an option parsing failure. + * @param err The error. + * @param c The character for the failing option. + * @param str Either the string for the failing option, or the invalid option. + */ static void bc_opt_error(BcErr err, int c, const char *str) { if (err == BC_ERR_FATAL_OPTION) bc_error(err, 0, str); else bc_error(err, 0, (int) c, str); } +/** + * Returns the type of the long option that matches @a c. + * @param longopts The long options array. + * @param c The character to match against. + * @return The type of the long option as an integer, or -1 if none. + */ static int bc_opt_type(const BcOptLong *longopts, char c) { size_t i; @@ -82,6 +106,12 @@ static int bc_opt_type(const BcOptLong *longopts, char c) { return (int) longopts[i].type; } +/** + * Parses a short option. + * @param o The option parser. + * @param longopts The long options array. + * @return The character for the short option, or -1 if none left. + */ static int bc_opt_parseShort(BcOpt *o, const BcOptLong *longopts) { int type; @@ -89,12 +119,15 @@ static int bc_opt_parseShort(BcOpt *o, const BcOptLong *longopts) { char *option = o->argv[o->optind]; int ret = -1; + // Make sure to clear these. o->optopt = 0; o->optarg = NULL; + // Get the next option. option += o->subopt + 1; o->optopt = option[0]; + // Get the type and the next data. type = bc_opt_type(longopts, option[0]); next = o->argv[o->optind + 1]; @@ -104,6 +137,7 @@ static int bc_opt_parseShort(BcOpt *o, const BcOptLong *longopts) { case BC_OPT_BC_ONLY: case BC_OPT_DC_ONLY: { + // Check for invalid option and barf if so. if (type == -1 || (type == BC_OPT_BC_ONLY && BC_IS_DC) || (type == BC_OPT_DC_ONLY && BC_IS_BC)) { @@ -120,31 +154,41 @@ static int bc_opt_parseShort(BcOpt *o, const BcOptLong *longopts) { case BC_OPT_NONE: { + // If there is something else, update the suboption. if (option[1]) o->subopt += 1; else { + + // Go to the next argument. o->subopt = 0; o->optind += 1; } ret = (int) option[0]; + break; } case BC_OPT_REQUIRED: { + // Always go to the next argument. o->subopt = 0; o->optind += 1; + // Use the next characters, if they exist. if (option[1]) o->optarg = option + 1; else if (next != NULL) { + + // USe the next. o->optarg = next; o->optind += 1; } + // No argument, barf. else bc_opt_error(BC_ERR_FATAL_OPTION_NO_ARG, option[0], bc_opt_longopt(longopts, option[0])); ret = (int) option[0]; + break; } } @@ -152,21 +196,38 @@ static int bc_opt_parseShort(BcOpt *o, const BcOptLong *longopts) { return ret; } +/** + * Ensures that a long option argument matches a long option name, regardless of + * "=<data>" at the end. + * @param name The name to match. + * @param option The command-line argument. + * @return True if @a option matches @a name, false otherwise. + */ static bool bc_opt_longoptsMatch(const char *name, const char *option) { const char *a = option, *n = name; + // Can never match a NULL name. if (name == NULL) return false; + // Loop through for (; *a && *n && *a != '='; ++a, ++n) { if (*a != *n) return false; } + // Ensure they both end at the same place. return (*n == '\0' && (*a == '\0' || *a == '=')); } +/** + * Returns a pointer to the argument of a long option, or NULL if it not in the + * same argument. + * @param option The option to find the argument of. + * @return A pointer to the argument of the option, or NULL if none. + */ static char* bc_opt_longoptsArg(char *option) { + // Find the end or equals sign. for (; *option && *option != '='; ++option); if (*option == '=') return option + 1; @@ -179,6 +240,7 @@ int bc_opt_parse(BcOpt *o, const BcOptLong *longopts) { char *option; bool empty; + // This just eats empty options. do { option = o->argv[o->optind]; @@ -189,15 +251,19 @@ int bc_opt_parse(BcOpt *o, const BcOptLong *longopts) { } while (empty); + // If the option is just a "--". if (BC_OPT_ISDASHDASH(option)) { // Consume "--". o->optind += 1; return -1; } + // Parse a short option. else if (BC_OPT_ISSHORTOPT(option)) return bc_opt_parseShort(o, longopts); + // If the option is not long at this point, we are done. else if (!BC_OPT_ISLONGOPT(option)) return -1; + // Clear these. o->optopt = 0; o->optarg = NULL; @@ -205,33 +271,42 @@ int bc_opt_parse(BcOpt *o, const BcOptLong *longopts) { option += 2; o->optind += 1; + // Loop through the valid long options. for (i = 0; !bc_opt_longoptsEnd(longopts, i); i++) { const char *name = longopts[i].name; + // If we have a match... if (bc_opt_longoptsMatch(name, option)) { char *arg; + // Get the option char and the argument. o->optopt = longopts[i].val; arg = bc_opt_longoptsArg(option); + // Error if the option is invalid.. if ((longopts[i].type == BC_OPT_BC_ONLY && BC_IS_DC) || (longopts[i].type == BC_OPT_DC_ONLY && BC_IS_BC)) { bc_opt_error(BC_ERR_FATAL_OPTION, o->optopt, name); } + // Error if we have an argument and should not. if (longopts[i].type == BC_OPT_NONE && arg != NULL) { bc_opt_error(BC_ERR_FATAL_OPTION_ARG, o->optopt, name); } + // Set the argument, or check the next argument if we don't have + // one. if (arg != NULL) o->optarg = arg; else if (longopts[i].type == BC_OPT_REQUIRED) { + // Get the next argument. o->optarg = o->argv[o->optind]; + // All's good if it exists; otherwise, barf. if (o->optarg != NULL) o->optind += 1; else bc_opt_error(BC_ERR_FATAL_OPTION_NO_ARG, o->optopt, name); @@ -241,6 +316,7 @@ int bc_opt_parse(BcOpt *o, const BcOptLong *longopts) { } } + // If we reach this point, the option is invalid. bc_opt_error(BC_ERR_FATAL_OPTION, 0, option); BC_UNREACHABLE |