[RFC PATCH 09/28] cli: lil: Use error codes

Sean Anderson
2021-07-01T06:18:29Z

This adds numeric error codes to be used for errors. This makes it easier
for code to differentiate between different classes of errors. Some error
codes are not used yet, and will be added in future commits. The position
argument has been removed for a few reasons:

- It doesn't make sense for some errors, such as running out of memory
- It was often not very helpful, since it pointed at the end of the command
- Determining the location of an error is more difficult when the parsing
and evaluating steps are done separately.

In addition, lil.error is changed to lil.err for consistency.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

common/cli_lil.c | 92 ++++++++++++++++++-----------------------------
include/cli_lil.h | 36 +++++++++++++++++--
2 files changed, 68 insertions(+), 60 deletions(-)

diff --git a/common/cli_lil.c b/common/cli_lil.c
index dd020e8452..1bdf0e5265 100644
--- a/common/cli_lil.c
+++ b/common/cli_lil.c
@@ -92,12 +92,7 @@ struct lil {
struct lil_env *rootenv;
struct lil_env *downenv;
struct lil_value *empty;
- enum {
- ERROR_NOERROR = 0,
- ERROR_DEFAULT,
- ERROR_FIXHEAD,
- } error;
- size_t err_head;
+ enum lil_error err;
char *err_msg;
struct lil_callbacks callbacks;
size_t parse_depth;
@@ -117,6 +112,7 @@ struct expreval {

static struct lil_value *next_word(struct lil *lil);
static void register_stdcmds(struct lil *lil);
+static void lil_set_error(struct lil *lil, enum lil_error err, const char *msg);

#ifdef LIL_ENABLE_POOLS
static struct lil_value **pool;
@@ -974,7 +970,7 @@ static struct lil_list *substitute(struct lil *lil)
struct lil_list *words = lil_alloc_list();

lil_skip_spaces(lil);
- while (lil->head < lil->clen && !ateol(lil) && !lil->error) {
+ while (lil->head < lil->clen && !ateol(lil) && !lil->err) {
struct lil_value *w = alloc_value(NULL);

do {
@@ -993,7 +989,7 @@ static struct lil_list *substitute(struct lil *lil)
lil_free_value(wp);
} while (lil->head < lil->clen &&
!eolchar(lil->code[lil->head]) &&
- !isspace(lil->code[lil->head]) && !lil->error);
+ !isspace(lil->code[lil->head]) && !lil->err);
lil_skip_spaces(lil);

lil_list_append(words, w);
@@ -1042,13 +1038,7 @@ static struct lil_value *run_cmd(struct lil *lil, struct lil_func *cmd,
struct lil_value *r;

if (cmd->proc) {
- size_t shead = lil->head;
-
r = cmd->proc(lil, words->c - 1, words->v + 1);
- if (lil->error == ERROR_FIXHEAD) {
- lil->error = ERROR_DEFAULT;
- lil->err_head = shead;
- }
} else {
lil_push_env(lil);
lil->env->func = cmd;
@@ -1102,18 +1092,18 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen,
lil->parse_depth++;
#ifdef LIL_ENABLE_RECLIMIT
if (lil->parse_depth > LIL_ENABLE_RECLIMIT) {
- lil_set_error(lil, "Too many recursive calls");
+ lil_set_error(lil, LIL_ERR_DEPTH, "Too many recursive calls");
goto cleanup;
}
#endif

if (lil->parse_depth == 1)
- lil->error = 0;
+ lil->err = LIL_ERR_NONE;

if (funclevel)
lil->env->breakrun = 0;

- while (lil->head < lil->clen && !lil->error) {
+ while (lil->head < lil->clen && !lil->err) {
if (words)
lil_free_list(words);

@@ -1122,7 +1112,7 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen,
val = NULL;

words = substitute(lil);
- if (!words || lil->error)
+ if (!words || lil->err)
goto cleanup;

if (words->c) {
@@ -1136,7 +1126,7 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen,
snprintf(msg, sizeof(msg),
"unknown function %s",
words->v[0]->d);
- lil_set_error_at(lil, lil->head, msg);
+ lil_set_error(lil, LIL_ERR_NOCMD, msg);
goto cleanup;
}
} else {
@@ -1182,38 +1172,25 @@ struct lil_value *lil_parse_value(struct lil *lil, struct lil_value *val,
return lil_parse(lil, val->d, val->l, funclevel);
}

-void lil_set_error(struct lil *lil, const char *msg)
+static void lil_set_error(struct lil *lil, enum lil_error err, const char *msg)
{
- if (lil->error)
- return;
-
+ assert(!lil->err);
free(lil->err_msg);
- lil->error = ERROR_FIXHEAD;
- lil->err_head = 0;
- lil->err_msg = strdup(msg ? msg : "");
+ lil->err = err;
+ lil->err_msg = strdup(msg);
}

-void lil_set_error_at(struct lil *lil, size_t pos, const char *msg)
+enum lil_error lil_error(struct lil *lil, const char **msg)
{
- if (lil->error)
- return;
+ enum lil_error err = lil->err;

- free(lil->err_msg);
- lil->error = ERROR_DEFAULT;
- lil->err_head = pos;
- lil->err_msg = strdup(msg ? msg : "");
-}
-
-int lil_error(struct lil *lil, const char **msg, size_t *pos)
-{
- if (!lil->error)
- return 0;
+ if (!err)
+ return LIL_ERR_NONE;

*msg = lil->err_msg;
- *pos = lil->err_head;
- lil->error = ERROR_NOERROR;
+ lil->err = LIL_ERR_NONE;

- return 1;
+ return err;
}

static void ee_expr(struct expreval *ee);
@@ -1642,7 +1619,7 @@ struct lil_value *lil_eval_expr(struct lil *lil, struct lil_value *code)
struct expreval ee;

code = lil_subst_to_value(lil, code);
- if (lil->error)
+ if (lil->err)
return NULL;

ee.code = lil_to_string(code);
@@ -1664,13 +1641,13 @@ struct lil_value *lil_eval_expr(struct lil *lil, struct lil_value *code)
lil_free_value(code);
switch (ee.error) {
case EERR_DIVISION_BY_ZERO:
- lil_set_error(lil, "division by zero in expression");
+ lil_set_error(lil, LIL_ERR_DOM, "division by zero");
return NULL;
case EERR_SYNTAX_ERROR:
- lil_set_error(lil, "expression syntax error");
+ lil_set_error(lil, LIL_ERR_SYNTAX, "expression syntax");
return NULL;
case EERR_INVALID_EXPRESSION:
- lil_set_error(lil, "invalid expression");
+ lil_set_error(lil, LIL_ERR_SYNTAX, "invalid expression");
return NULL;
case EERR_NO_ERROR:
break;
@@ -1994,7 +1971,7 @@ static struct lil_value *fnc_rename(struct lil *lil, size_t argc,
char *msg = malloc(24 + strlen(oldname));

sprintf(msg, "unknown function '%s'", oldname);
- lil_set_error_at(lil, lil->head, msg);
+ lil_set_error(lil, LIL_ERR_NOCMD, msg);
free(msg);
return NULL;
}
@@ -2397,7 +2374,7 @@ static struct lil_value *fnc_foreach(struct lil *lil, size_t argc,
else
lil_free_value(rv);

- if (lil->env->breakrun || lil->error)
+ if (lil->env->breakrun || lil->err)
break;
}

@@ -2499,7 +2476,7 @@ static struct lil_value *fnc_if(struct lil *lil, size_t argc,
return NULL;

val = lil_eval_expr(lil, argv[base]);
- if (!val || lil->error)
+ if (!val || lil->err)
return NULL;

v = lil_to_boolean(val);
@@ -2530,9 +2507,9 @@ static struct lil_value *fnc_while(struct lil *lil, size_t argc,
if (argc < (size_t)base + 2)
return NULL;

- while (!lil->error && !lil->env->breakrun) {
+ while (!lil->err && !lil->env->breakrun) {
val = lil_eval_expr(lil, argv[base]);
- if (!val || lil->error)
+ if (!val || lil->err)
return NULL;

v = lil_to_boolean(val);
@@ -2562,9 +2539,9 @@ static struct lil_value *fnc_for(struct lil *lil, size_t argc,
return NULL;

lil_free_value(lil_parse_value(lil, argv[0], 0));
- while (!lil->error && !lil->env->breakrun) {
+ while (!lil->err && !lil->env->breakrun) {
val = lil_eval_expr(lil, argv[1]);
- if (!val || lil->error)
+ if (!val || lil->err)
return NULL;

if (!lil_to_boolean(val)) {
@@ -2889,12 +2866,12 @@ static struct lil_value *fnc_try(struct lil *lil, size_t argc,
if (argc < 1)
return NULL;

- if (lil->error)
+ if (lil->err)
return NULL;

r = lil_parse_value(lil, argv[0], 0);
- if (lil->error) {
- lil->error = ERROR_NOERROR;
+ if (lil->err) {
+ lil->err = LIL_ERR_NONE;
lil_free_value(r);

if (argc > 1)
@@ -2908,7 +2885,8 @@ static struct lil_value *fnc_try(struct lil *lil, size_t argc,
static struct lil_value *fnc_error(struct lil *lil, size_t argc,
struct lil_value **argv)
{
- lil_set_error(lil, argc > 0 ? lil_to_string(argv[0]) : NULL);
+ lil_set_error(lil, LIL_ERR_USER,
+ argc > 0 ? lil_to_string(argv[0]) : NULL);
return NULL;
}

diff --git a/include/cli_lil.h b/include/cli_lil.h
index b8df94a766..cdaa79fd15 100644
--- a/include/cli_lil.h
+++ b/include/cli_lil.h
@@ -39,6 +39,38 @@ struct lil_callbacks {
struct lil_value **value);
};

+/**
+ * enum lil_error - Error codes returned by @lil_error
+ * LIL_ERR_NONE: No error
+ * LIL_ERR_OOM: Out of memory
+ * LIL_ERR_CASE: Missing case statement; generally a bug in the program or the
+ * result of memory corruption.
+ * LIL_ERR_DEPTH: Too much recursion while parsing or evaluating
+ * LIL_ERR_SYNTAX: Unrecoverable syntax error
+ * LIL_ERR_EOF: Unexpected end of input; may be recoverable by prompting the
+ * user for more input.
+ * LIL_ERR_INTR: Interrupted by Ctrl-C
+ * LIL_ERR_NOCMD: No such command
+ * LIL_ERR_NOVAR: No such variable
+ * LIL_ERR_ARGC: Incorrect number of arguments
+ * LIL_ERR_DOM: Numerical argument out of range (typically division by zero)
+ * LIL_ERR_USER: Error set by user
+ */
+enum lil_error {
+ LIL_ERR_NONE = 0,
+ LIL_ERR_OOM,
+ LIL_ERR_CASE,
+ LIL_ERR_DEPTH,
+ LIL_ERR_SYNTAX,
+ LIL_ERR_EOF,
+ LIL_ERR_INTR,
+ LIL_ERR_NOCMD,
+ LIL_ERR_NOVAR,
+ LIL_ERR_ARGC,
+ LIL_ERR_DOM,
+ LIL_ERR_USER,
+};
+
struct lil *lil_new(const struct lil_callbacks *callbacks);
void lil_free(struct lil *lil);

@@ -49,9 +81,7 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen,
struct lil_value *lil_parse_value(struct lil *lil, struct lil_value *val,
int funclevel);

-void lil_set_error(struct lil *lil, const char *msg);
-void lil_set_error_at(struct lil *lil, size_t pos, const char *msg);
-int lil_error(struct lil *lil, const char **msg, size_t *pos);
+enum lil_error lil_error(struct lil *lil, const char **msg);

const char *lil_to_string(struct lil_value *val);
ssize_t lil_to_integer(struct lil_value *val);
--
2.32.0