From 365d6e69e17e44f972934fd9582a01cc03754fe1 Mon Sep 17 00:00:00 2001 From: Přemysl Janouch Date: Thu, 18 May 2017 20:45:46 +0200 Subject: Various mostly safety fixes --- README.adoc | 2 +- ell.c | 58 ++++++++++++++++++++++++++++++++++------------------------ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/README.adoc b/README.adoc index 3ac6f15..376edfc 100644 --- a/README.adoc +++ b/README.adoc @@ -47,7 +47,7 @@ Runtime ------- All variables are put in a single global namespace with no further scoping. When calling a command (which is a list of lists), all arguments are -automatically stored in variables named 1, 2, 3, ... n. They are however +automatically stored in variables named 0, 1, 2, 3, ... n. They are however effectively inaccessible and you must rename them first using the `arg` special form. diff --git a/ell.c b/ell.c index 9572711..7fd4c53 100755 --- a/ell.c +++ b/ell.c @@ -270,8 +270,10 @@ lexer_free (struct lexer *self) { free (self->string.s); } -// FIXME: other isspace() stuff is missing -static bool lexer_is_word_char (int c) { return !strchr ("()[]{}\n@#' ", c); } +static bool lexer_is_ignored (int c) { return c == ' ' || c == '\t'; } +static bool lexer_is_word_char (int c) { + return !lexer_is_ignored (c) && !strchr ("()[]{}\n@#' ", c); +} static int lexer_advance (struct lexer *self) { @@ -286,11 +288,10 @@ lexer_advance (struct lexer *self) { return c; } -static void lexer_error (struct lexer *self, char **e, const char *fmt, ...) +static bool lexer_error (struct lexer *self, char **e, const char *fmt, ...) ATTRIBUTE_PRINTF (3, 4); -// TODO: see "script", we can just use error constants to avoid allocation -static void +static bool lexer_error (struct lexer *self, char **e, const char *fmt, ...) { va_list ap; va_start (ap, fmt); @@ -300,11 +301,12 @@ lexer_error (struct lexer *self, char **e, const char *fmt, ...) { *e = format ("near line %u, column %u: %s", self->line + 1, self->column + 1, description); - // TODO: see above, we should be able to indicate error without allocation + // TODO: see "script", we can just use error constants to avoid allocation if (!*e) abort (); free (description); + return false; } static bool @@ -333,10 +335,8 @@ lexer_hexa_escape (struct lexer *self, struct buffer *output) { static bool lexer_escape_sequence (struct lexer *self, struct buffer *output, char **e) { - if (!self->len) { - lexer_error (self, e, "premature end of escape sequence"); - return false; - } + if (!self->len) + return lexer_error (self, e, "premature end of escape sequence"); unsigned char c = *self->p; switch (c) { @@ -356,12 +356,10 @@ lexer_escape_sequence (struct lexer *self, struct buffer *output, char **e) { if (lexer_hexa_escape (self, output)) return true; - lexer_error (self, e, "invalid hexadecimal escape"); - return false; + return lexer_error (self, e, "invalid hexadecimal escape"); default: - lexer_error (self, e, "unknown escape sequence"); - return false; + return lexer_error (self, e, "unknown escape sequence"); } buffer_append_c (output, c); @@ -380,14 +378,13 @@ lexer_string (struct lexer *self, struct buffer *output, char **e) { else if (!lexer_escape_sequence (self, output, e)) return false; } - lexer_error (self, e, "premature end of string"); - return false; + return lexer_error (self, e, "premature end of string"); } static enum token lexer_next (struct lexer *self, char **e) { // Skip over any whitespace between tokens - while (self->len && isspace (*self->p) && *self->p != '\n') + while (self->len && lexer_is_ignored (*self->p)) lexer_advance (self); if (!self->len) return T_ABORT; @@ -428,6 +425,8 @@ lexer_next (struct lexer *self, char **e) { // --- Parsing ----------------------------------------------------------------- +// FIXME: the parser generally ignores memory allocation errors + static void print_string (const char *s) { putc ('\'', stdout); @@ -675,6 +674,9 @@ register_native (const char *name, handler_fn handler) { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// TODO: fill in "error_is_fatal" +// TODO: probably add new_*() methods that set "memory_failure" + struct context { struct item *variables; ///< List of variables @@ -704,7 +706,6 @@ get (struct context *ctx, const char *name) { return NULL; } -// FIXME: cloning and removing static bool set (struct context *ctx, const char *name, struct item *value) { struct item *iter, *key = NULL, *pair = NULL; @@ -713,12 +714,19 @@ set (struct context *ctx, const char *name, struct item *value) { break; if (iter) { item_free (iter->head->next); - iter->head->next = value; + if (!(iter->head->next = new_clone (value))) { + ctx->memory_failure = true; + return false; + } return true; } if ((key = new_string (name, strlen (name))) && (pair = new_list (NULL))) { - (pair->head = key)->next = value; + if (!((pair->head = key)->next = new_clone (value))) { + item_free (pair); + ctx->memory_failure = true; + return false; + } pair->next = ctx->variables; ctx->variables = pair; return true; @@ -756,10 +764,12 @@ rename_arguments (struct context *ctx, struct item *names) { if (names->type != ITEM_STRING) continue; - if (value) - set (ctx, names->value, new_clone (value)); - else - set (ctx, names->value, NULL); + if (value && !(value = new_clone (value))) { + ctx->memory_failure = true; + return false; + } + if (!set (ctx, names->value, value)) + return false; } return true; } -- cgit v1.2.3-70-g09d2