Various mostly safety fixes
This commit is contained in:
parent
5963b99c3e
commit
365d6e69e1
|
@ -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.
|
||||
|
||||
|
|
58
ell.c
58
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue