aboutsummaryrefslogtreecommitdiffstats
path: root/src/opt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/opt.c')
-rw-r--r--src/opt.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/src/opt.c b/src/opt.c
index 2450b9f4..ae9bc1cc 100644
--- a/src/opt.c
+++ b/src/opt.c
@@ -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