simplify error handling

This commit is contained in:
Lukas Wurzinger 2021-03-02 18:25:50 +01:00
parent 8aa1920213
commit 77394a61dc
3 changed files with 100 additions and 104 deletions

168
readopt.c
View file

@ -3,23 +3,23 @@
#include "readopt.h"
static enum readopt_status parse_arg(struct readopt_parser *rp, char *arg);
static void parse_arg(struct readopt_parser *rp, char *arg);
static enum readopt_status parse_opt(struct readopt_parser *rp, enum readopt_form form, char **pos);
static void parse_opt(struct readopt_parser *rp, enum readopt_form form, char **pos);
static struct readopt_opt *match_opt(struct readopt_parser *rp, enum readopt_form form, char **needle);
static struct readopt_opt *match_finish(struct readopt_parser *rp, char **needle, char *cmp, struct readopt_opt *opt);
static enum readopt_status update_opt(struct readopt_parser *rp, char *attach, struct readopt_opt *opt);
static enum readopt_status update_oper(struct readopt_parser *rp, struct readopt_view_strings val);
static void update_opt(struct readopt_parser *rp, char *attach, struct readopt_opt *opt);
static void update_oper(struct readopt_parser *rp, struct readopt_view_strings val);
static enum readopt_status assign_opers(struct readopt_parser *rp);
static void assign_opers(struct readopt_parser *rp);
static enum readopt_status add_val(struct readopt_parser *rp, struct readopt_oper *oper, char *str, int end);
static void add_val(struct readopt_parser *rp, struct readopt_oper *oper, char *str, int end);
static char *skip_incl(const char *inner, char *outer);
static enum readopt_status occ_opt(struct readopt_parser *rp, struct readopt_opt *opt);
static void occ_opt(struct readopt_parser *rp, struct readopt_opt *opt);
/* permutes the argument list to store a value for an option or operand */
static void permute_val(struct readopt_parser *rp, struct readopt_view_strings *target, char *val, int end);
@ -29,60 +29,54 @@ static void permute_rest(char **target, struct readopt_view_strings start);
static void format_usage_opts(struct readopt_format_context *ctx, struct readopt_opt *opts);
static void format_usage_opers(struct readopt_format_context *ctx, struct readopt_oper *opers);
struct readopt_answer
readopt_parse_all(struct readopt_parser *rp)
{
struct readopt_answer answer;
do
answer = readopt_parse(rp);
while (!answer.end && answer.status == READOPT_STATUS_SUCCESS);
return answer;
}
struct readopt_answer
int
readopt_parse(struct readopt_parser *rp)
{
/* check whether the current offset is at the end of argv */
size_t off = rp->state.curr.arg - rp->args.strings;
if (off >= rp->args.len) {
if (rp->state.pending)
if (rp->state.pending) {
/* the last specified option required an argument, but has been ignored */
return (struct readopt_answer){1, READOPT_STATUS_NOVAL};
rp->error = READOPT_ERROR_NOVAL;
return 0;
}
for (size_t i = 0; readopt_validate_opt(rp->opts + i); i++)
if (!readopt_validate_within(&rp->opts[i].cont.oper))
return (struct readopt_answer){1, READOPT_STATUS_RANGEOPT};
for (size_t i = 0; readopt_validate_opt(rp->opts + i); i++) {
if (!readopt_validate_within(&rp->opts[i].cont.oper)) {
rp->error = READOPT_ERROR_RANGEOPT;
return 0;
}
}
return (struct readopt_answer){1, assign_opers(rp)};
assign_opers(rp);
return 0;
}
if (rp->state.pending) {
enum readopt_status status = add_val(rp, &rp->state.curr.opt->cont.oper, *rp->state.curr.arg, 0);
add_val(rp, &rp->state.curr.opt->cont.oper, *rp->state.curr.arg, 0);
++rp->state.curr.arg;
return (struct readopt_answer){0, status};
return !rp->error;
}
enum readopt_status status = parse_arg(rp, *rp->state.curr.arg);
parse_arg(rp, *rp->state.curr.arg);
/* if grouped options are still being parsed, they should not be discarded */
if (!rp->state.grppos)
++rp->state.curr.arg;
return (struct readopt_answer){0, status};
return !rp->error;
}
static enum readopt_status
static void
parse_arg(struct readopt_parser *rp, char *arg)
{
/* parse the next option in the grouped option string, which automatically advances it */
if (rp->state.grppos) {
enum readopt_status s = parse_opt(rp, READOPT_FORM_SHORT, &rp->state.grppos);
parse_opt(rp, READOPT_FORM_SHORT, &rp->state.grppos);
if (!*rp->state.grppos) {
rp->state.grppos = NULL;
}
return s;
return;
}
char *pos = arg;
@ -101,30 +95,34 @@ parse_arg(struct readopt_parser *rp, char *arg)
assert(off);
if (off == 1) {
/* no operands after the "--" */
return READOPT_STATUS_SUCCESS;
} else {
enum readopt_status s = update_oper(rp, (struct readopt_view_strings){
.len = off - 1,
.strings = rp->state.curr.arg + 1
});
rp->state.curr.arg = rp->args.strings + rp->args.len - 1;
return s;
return;
}
update_oper(rp, (struct readopt_view_strings){
.len = off - 1,
.strings = rp->state.curr.arg + 1
});
rp->state.curr.arg = rp->args.strings + rp->args.len - 1;
return;
default:
return parse_opt(rp, READOPT_FORM_LONG, &pos);
parse_opt(rp, READOPT_FORM_LONG, &pos);
return;
}
case '\0':
return update_oper(rp, (struct readopt_view_strings){.len = 1, .strings = (char *[]){arg}});
update_oper(rp, (struct readopt_view_strings){.len = 1, .strings = (char *[]){arg}});
return;
default:
return parse_opt(rp, READOPT_FORM_SHORT, &pos);
parse_opt(rp, READOPT_FORM_SHORT, &pos);
return;
}
default:
return update_oper(rp, (struct readopt_view_strings){.len = 1, .strings = (char *[]){arg}});
update_oper(rp, (struct readopt_view_strings){.len = 1, .strings = (char *[]){arg}});
return;
}
}
static enum readopt_status
static void
parse_opt(struct readopt_parser *rp, enum readopt_form form, char **pos)
{
struct readopt_opt *match;
@ -137,12 +135,12 @@ parse_opt(struct readopt_parser *rp, enum readopt_form form, char **pos)
if (!match->cont.req && *strpos) {
rp->state.grppos = strpos;
return update_opt(rp, NULL, match);
update_opt(rp, NULL, match);
} else {
update_opt(rp, *strpos ? strpos : NULL, match);
}
return update_opt(rp, *strpos ? strpos : NULL, match);
} else {
return READOPT_STATUS_NOTOPT;
rp->error = READOPT_ERROR_NOTOPT;
}
} else {
/* match and advance pos to the end of the match */
@ -151,15 +149,18 @@ parse_opt(struct readopt_parser *rp, enum readopt_form form, char **pos)
if (match) {
switch (**pos) {
case '\0':
return update_opt(rp, NULL, match);
update_opt(rp, NULL, match);
break;
case '=':
++(*pos);
return update_opt(rp, *pos, match);
update_opt(rp, *pos, match);
break;
default:
return READOPT_STATUS_NOTOPT;
rp->error = READOPT_ERROR_NOTOPT;
break;
}
} else {
return READOPT_STATUS_NOTOPT;
rp->error = READOPT_ERROR_NOTOPT;
}
}
}
@ -217,26 +218,28 @@ match_finish(struct readopt_parser *rp, char **needle, char *adv, struct readopt
return opt;
}
static enum readopt_status
static void
update_opt(struct readopt_parser *rp, char *attach, struct readopt_opt *opt)
{
if (opt->cont.req) {
if (attach) {
/* --opt=value, --opt=, -ovalue */
struct readopt_oper *curr = &rp->state.curr.opt->cont.oper;
return occ_opt(rp, opt) == READOPT_STATUS_SUCCESS ? add_val(rp, curr, attach, 0) : READOPT_STATUS_SUCCESS;
occ_opt(rp, opt);
add_val(rp, curr, attach, 0);
} else {
/* --opt value, -o value */
rp->state.pending = 1;
return occ_opt(rp, opt);
occ_opt(rp, opt);
}
} else {
enum readopt_status s = occ_opt(rp, opt);
return attach ? READOPT_STATUS_NOTREQ : s;
occ_opt(rp, opt);
if (attach)
rp->error = READOPT_ERROR_NOTREQ;
}
}
static enum readopt_status
static void
update_oper(struct readopt_parser *rp, struct readopt_view_strings val)
{
assert(val.len && val.strings);
@ -248,11 +251,9 @@ update_oper(struct readopt_parser *rp, struct readopt_view_strings val)
permute_rest(rp->state.curr.eoval, val);
rp->state.curr.ioper.len += val.len;
}
return READOPT_STATUS_SUCCESS;
}
static enum readopt_status
static void
assign_opers(struct readopt_parser *rp)
{
size_t count = rp->state.curr.ioper.len;
@ -264,8 +265,10 @@ assign_opers(struct readopt_parser *rp)
nupper += readopt_select_upper(rp->opers[i].bounds);
}
if (count < nlower)
return READOPT_STATUS_RANGEOPER;
if (count < nlower) {
rp->error = READOPT_ERROR_RANGEOPER;
return;
}
struct {
size_t extra;
@ -296,19 +299,19 @@ assign_opers(struct readopt_parser *rp)
rp->opers[i].val.len += add, rest.extra -= add;
}
return rest.extra || rest.req ? READOPT_STATUS_RANGEOPER : READOPT_STATUS_SUCCESS;
if (rest.extra || rest.req)
rp->error = READOPT_ERROR_RANGEOPER;
}
static enum readopt_status
static void
add_val(struct readopt_parser *rp, struct readopt_oper *oper, char *string, int end)
{
rp->state.pending = 0;
if (!readopt_validate_within(oper))
return READOPT_STATUS_RANGEOPT;
permute_val(rp, &oper->val, string, end);
return READOPT_STATUS_SUCCESS;
rp->error = READOPT_ERROR_RANGEOPT;
else
permute_val(rp, &oper->val, string, end);
}
static char *
@ -322,13 +325,12 @@ skip_incl(const char *inner, char *outer)
return !*inner ? outer : NULL;
}
static enum readopt_status
static void
occ_opt(struct readopt_parser *rp, struct readopt_opt *opt)
{
assert(opt);
rp->state.curr.opt = opt;
++rp->state.curr.opt->cont.oper.val.len;
return READOPT_STATUS_SUCCESS;
}
static void
@ -349,7 +351,7 @@ permute_val(struct readopt_parser *rp, struct readopt_view_strings *target, char
char **start = pos, **stop = rp->state.curr.eoval;
/* increment all value pointers in the operands and options which are between start and stop, inclusive */
/* increment all value pointers in the options which are between start and stop, inclusive */
for (size_t i = 0; readopt_validate_opt(rp->opts + i); i++)
incr_between(start, stop, &rp->opts[i].cont.oper.val, target);
@ -435,16 +437,16 @@ readopt_keyval(char *s)
}
int
readopt_put_status(enum readopt_status status, struct readopt_write_context *ctx)
readopt_put_error(enum readopt_error e, struct readopt_write_context *ctx)
{
const char *s;
switch (status) {
case READOPT_STATUS_SUCCESS: s = "Success"; break;
case READOPT_STATUS_NOVAL: s = "Option did not receive its required value"; break;
case READOPT_STATUS_NOTREQ: s = "No value required for option"; break;
case READOPT_STATUS_NOTOPT: s = "Specified option does not exist"; break;
case READOPT_STATUS_RANGEOPT: s = "Option(s) are not within the defined limits"; break;
case READOPT_STATUS_RANGEOPER: s = "Operand(s) are not within the defined limits"; break;
switch (e) {
case READOPT_ERROR_SUCCESS: s = "Success"; break;
case READOPT_ERROR_NOVAL: s = "Option did not receive its required value"; break;
case READOPT_ERROR_NOTREQ: s = "No value required for option"; break;
case READOPT_ERROR_NOTOPT: s = "Specified option does not exist"; break;
case READOPT_ERROR_RANGEOPT: s = "Option(s) are not within the defined limits"; break;
case READOPT_ERROR_RANGEOPER: s = "Operand(s) are not within the defined limits"; break;
default: return 0;
}

View file

@ -4,13 +4,13 @@
#define READOPT_ALLOC_STRINGS(...) ((char *[]){__VA_ARGS__, NULL})
enum readopt_status {
READOPT_STATUS_SUCCESS,
READOPT_STATUS_NOVAL,
READOPT_STATUS_NOTREQ,
READOPT_STATUS_NOTOPT,
READOPT_STATUS_RANGEOPT,
READOPT_STATUS_RANGEOPER
enum readopt_error {
READOPT_ERROR_SUCCESS,
READOPT_ERROR_NOVAL,
READOPT_ERROR_NOTREQ,
READOPT_ERROR_NOTOPT,
READOPT_ERROR_RANGEOPT,
READOPT_ERROR_RANGEOPER
};
enum readopt_form {
@ -69,11 +69,7 @@ struct readopt_parser {
struct readopt_view_strings ioper;
} curr;
} state;
};
struct readopt_answer {
int end;
enum readopt_status status;
enum readopt_error error;
};
struct readopt_write_context {
@ -105,10 +101,8 @@ struct readopt_format_context {
struct readopt_write_context *wr;
};
/* parse everything at once until either an error occurs or the parsing process was successful */
struct readopt_answer readopt_parse_all(struct readopt_parser *rp);
/* iteratively parse the arguments */
struct readopt_answer readopt_parse(struct readopt_parser *rp);
int readopt_parse(struct readopt_parser *rp);
/* args should always exclude the first element, like this: {.strings = argv + 1, .len = argc - 1} */
void readopt_parser_init(struct readopt_parser *rp, struct readopt_opt *opts, struct readopt_oper *opers, struct readopt_view_strings args);
/* args should always exclude the first element, like this: {.strings = argv + 1, .len = argc - 1} */
@ -126,8 +120,8 @@ size_t readopt_select_lower(struct readopt_bounds bounds);
/* pass a string like "thing=value" and get "value" back */
char *readopt_keyval(char *s);
/* write the passed status as a string via ctx */
int readopt_put_status(enum readopt_status status, struct readopt_write_context *ctx);
/* write the passed error as a string via ctx */
int readopt_put_error(enum readopt_error error, struct readopt_write_context *ctx);
/* write the usage string, either as plaintext or mdoc format */
void readopt_put_usage(struct readopt_parser *rp, struct readopt_format_context *ctx);

View file

@ -116,14 +116,14 @@ main(int argc, char **argv)
}
);
struct readopt_answer answer = readopt_parse_all(&rp);
while (readopt_parse(&rp));
fputs("status: ", stderr);
readopt_put_status(answer.status, &(struct readopt_write_context){
fputs("error: ", stderr);
readopt_put_error(rp.error, &(struct readopt_write_context){
.dest.stream = stderr
});
fputc('\n', stderr);
if (answer.status != READOPT_STATUS_SUCCESS) {
if (rp.error != READOPT_ERROR_SUCCESS) {
return EXIT_FAILURE;
}