summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Janouch <p.janouch@gmail.com>2014-09-27 20:22:21 +0200
committerPřemysl Janouch <p.janouch@gmail.com>2014-09-27 21:26:35 +0200
commitf9dced26cae066aaa804f91d20796702cd255091 (patch)
tree499f6a737ce72169537ff72aefc8f74959fcdcfa
parent89e9f13e12039194d9a00a39e34329f5e446774e (diff)
downloadxK-f9dced26cae066aaa804f91d20796702cd255091.tar.gz
xK-f9dced26cae066aaa804f91d20796702cd255091.tar.xz
xK-f9dced26cae066aaa804f91d20796702cd255091.zip
script: watch all memory allocations
And some other miscellaneous changes. Now it should be ready for the real life.
-rwxr-xr-xplugins/script835
1 files changed, 475 insertions, 360 deletions
diff --git a/plugins/script b/plugins/script
index a9de176..42960c1 100755
--- a/plugins/script
+++ b/plugins/script
@@ -15,7 +15,7 @@
//
// If you don't like something, just change it; this is just an experiment.
//
-// NOTE: it is really easy to crash and abuse. Be careful.
+// NOTE: it is relatively easy to abuse. Be careful.
//
#define _XOPEN_SOURCE 500
@@ -48,25 +48,33 @@
static char *strdup_printf (const char *format, ...) ATTRIBUTE_PRINTF (1, 2);
static char *
-strdup_printf (const char *format, ...)
+strdup_vprintf (const char *format, va_list ap)
{
- va_list ap;
- va_start (ap, format);
- int size = vsnprintf (NULL, 0, format, ap);
- va_end (ap);
+ va_list aq;
+ va_copy (aq, ap);
+ int size = vsnprintf (NULL, 0, format, aq);
+ va_end (aq);
if (size < 0)
return NULL;
char buf[size + 1];
- va_start (ap, format);
size = vsnprintf (buf, sizeof buf, format, ap);
- va_end (ap);
if (size < 0)
return NULL;
return strdup (buf);
}
+static char *
+strdup_printf (const char *format, ...)
+{
+ va_list ap;
+ va_start (ap, format);
+ char *result = strdup_vprintf (format, ap);
+ va_end (ap);
+ return result;
+}
+
// --- Generic buffer ----------------------------------------------------------
struct buffer
@@ -74,26 +82,37 @@ struct buffer
char *s; ///< Buffer data
size_t alloc; ///< Number of bytes allocated
size_t len; ///< Number of bytes used
+ bool memory_failure; ///< Memory allocation failed
};
-#define BUFFER_INITIALIZER {NULL, 0, 0}
+#define BUFFER_INITIALIZER { NULL, 0, 0, false }
-static void
+static bool
buffer_append (struct buffer *self, const void *s, size_t n)
{
+ if (self->memory_failure)
+ return false;
+
if (!self->s)
self->s = malloc (self->alloc = 8);
while (self->len + n > self->alloc)
self->s = realloc (self->s, self->alloc <<= 1);
+ if (!self->s)
+ {
+ self->memory_failure = true;
+ return false;
+ }
+
memcpy (self->s + self->len, s, n);
self->len += n;
+ return true;
}
-inline static void
+inline static bool
buffer_append_c (struct buffer *self, char c)
{
- buffer_append (self, &c, 1);
+ return buffer_append (self, &c, 1);
}
// --- Data types --------------------------------------------------------------
@@ -225,11 +244,19 @@ new_clone (const struct item *item)
case ITEM_LIST: size = sizeof (struct item_list); break;
}
- struct item *clone = memcpy (malloc (size), item, size);
+ struct item *clone = malloc (size);
+ if (!clone)
+ return NULL;
+
+ memcpy (clone, item, size);
if (item->type == ITEM_LIST)
{
struct item_list *x = (struct item_list *) clone;
- x->head = new_clone_list (x->head);
+ if (x->head && !(x->head = new_clone_list (x->head)))
+ {
+ free (clone);
+ return NULL;
+ }
}
clone->next = NULL;
return clone;
@@ -238,10 +265,14 @@ new_clone (const struct item *item)
static struct item *
new_clone_list (const struct item *item)
{
- struct item *head = NULL;
+ struct item *head = NULL, *clone;
for (struct item **out = &head; item; item = item->next)
{
- struct item *clone = *out = new_clone (item);
+ if (!(clone = *out = new_clone (item)))
+ {
+ item_free_list (head);
+ return NULL;
+ }
clone->next = NULL;
out = &clone->next;
}
@@ -269,6 +300,9 @@ static struct item *
new_word (const char *s, ssize_t len)
{
struct item *item = new_string (s, len);
+ if (!item)
+ return NULL;
+
item->type = ITEM_WORD;
return item;
}
@@ -277,6 +311,9 @@ static struct item *
new_integer (long long value)
{
struct item_integer *item = calloc (1, sizeof *item);
+ if (!item)
+ return NULL;
+
item->type = ITEM_INTEGER;
item->value = value;
return (struct item *) item;
@@ -286,6 +323,9 @@ static struct item *
new_float (long double value)
{
struct item_float *item = calloc (1, sizeof *item);
+ if (!item)
+ return NULL;
+
item->type = ITEM_FLOAT;
item->value = value;
return (struct item *) item;
@@ -295,6 +335,9 @@ static struct item *
new_list (struct item *head)
{
struct item_list *item = calloc (1, sizeof *item);
+ if (!item)
+ return NULL;
+
item->type = ITEM_LIST;
item->head = head;
return (struct item *) item;
@@ -302,10 +345,29 @@ new_list (struct item *head)
// --- Parsing -----------------------------------------------------------------
+#define PARSE_ERROR_TABLE(XX) \
+ XX( OK, NULL ) \
+ XX( EOF, "unexpected end of input" ) \
+ XX( INVALID_HEXA_ESCAPE, "invalid hexadecimal escape sequence" ) \
+ XX( INVALID_ESCAPE, "unrecognized escape sequence" ) \
+ XX( MEMORY, "memory allocation failure" ) \
+ XX( FLOAT_RANGE, "floating point value out of range" ) \
+ XX( INTEGER_RANGE, "integer out of range" ) \
+ XX( INVALID_INPUT, "invalid input" ) \
+ XX( UNEXPECTED_INPUT, "unexpected input" )
+
+enum tokenizer_error
+{
+#define XX(x, y) PARSE_ERROR_ ## x,
+ PARSE_ERROR_TABLE (XX)
+#undef XX
+ PARSE_ERROR_COUNT
+};
+
struct tokenizer
{
const char *cursor;
- const char *error;
+ enum tokenizer_error error;
};
static bool
@@ -365,7 +427,7 @@ decode_escape_sequence (struct tokenizer *self, struct buffer *buf)
switch ((c = *self->cursor))
{
case '\0':
- self->error = "unexpected end of input";
+ self->error = PARSE_ERROR_EOF;
return false;
case 'x':
case 'X':
@@ -373,7 +435,7 @@ decode_escape_sequence (struct tokenizer *self, struct buffer *buf)
if (decode_hexa_escape (self, buf))
return true;
- self->error = "invalid hexadecimal escape sequence";
+ self->error = PARSE_ERROR_INVALID_HEXA_ESCAPE;
return false;
default:
if (decode_octal_escape (self, buf))
@@ -387,7 +449,7 @@ decode_escape_sequence (struct tokenizer *self, struct buffer *buf)
return true;
}
- self->error = "unrecognized escape sequence";
+ self->error = PARSE_ERROR_INVALID_ESCAPE;
return false;
}
}
@@ -396,6 +458,7 @@ static struct item *
parse_string (struct tokenizer *self)
{
struct buffer buf = BUFFER_INITIALIZER;
+ struct item *item = NULL;
char c;
while (true)
@@ -403,25 +466,24 @@ parse_string (struct tokenizer *self)
{
case '\0':
self->cursor--;
- self->error = "unexpected end of input";
- goto fail;
+ self->error = PARSE_ERROR_EOF;
+ goto end;
case '"':
- {
- struct item *item = new_string (buf.s, buf.len);
- free (buf.s);
- return item;
- }
+ if (buf.memory_failure
+ || !(item = new_string (buf.s, buf.len)))
+ self->error = PARSE_ERROR_MEMORY;
+ goto end;
case '\\':
- if (!decode_escape_sequence (self, &buf))
- goto fail;
- break;
+ if (decode_escape_sequence (self, &buf))
+ break;
+ goto end;
default:
buffer_append_c (&buf, c);
}
-fail:
+end:
free (buf.s);
- return NULL;
+ return item;
}
static struct item *
@@ -444,25 +506,30 @@ try_parse_number (struct tokenizer *self)
return NULL;
// Only use the floating point result if it parses more characters:
+ struct item *item;
if (float_end > int_end)
{
if (float_errno == ERANGE)
{
- self->error = "floating point value out of range";
+ self->error = PARSE_ERROR_FLOAT_RANGE;
return NULL;
}
self->cursor = float_end;
- return new_float (float_value);
+ if (!(item = new_float (float_value)))
+ self->error = PARSE_ERROR_MEMORY;
+ return item;
}
else
{
if (int_errno == ERANGE)
{
- self->error = "integer out of range";
+ self->error = PARSE_ERROR_INTEGER_RANGE;
return NULL;
}
self->cursor = int_end;
- return new_integer (int_value);
+ if (!(item = new_integer (int_value)))
+ self->error = PARSE_ERROR_MEMORY;
+ return item;
}
}
@@ -470,6 +537,7 @@ static struct item *
parse_word (struct tokenizer *self)
{
struct buffer buf = BUFFER_INITIALIZER;
+ struct item *item = NULL;
char c;
// Here we accept almost anything that doesn't break the grammar
@@ -477,13 +545,13 @@ parse_word (struct tokenizer *self)
buffer_append_c (&buf, c);
self->cursor--;
- if (!buf.len)
- {
- self->error = "invalid input";
- return NULL;
- }
+ if (buf.memory_failure)
+ self->error = PARSE_ERROR_MEMORY;
+ else if (!buf.len)
+ self->error = PARSE_ERROR_INVALID_INPUT;
+ else if (!(item = new_word (buf.s, buf.len)))
+ self->error = PARSE_ERROR_MEMORY;
- struct item *item = new_word (buf.s, buf.len);
free (buf.s);
return item;
}
@@ -501,7 +569,7 @@ parse_list (struct tokenizer *self)
}
if (!*self->cursor)
{
- self->error = "unexpected end of input";
+ self->error = PARSE_ERROR_EOF;
item_free_list (list);
return NULL;
}
@@ -546,7 +614,7 @@ parse_item_list (struct tokenizer *self)
}
else if (!expected)
{
- self->error = "unexpected input";
+ self->error = PARSE_ERROR_UNEXPECTED_INPUT;
goto fail;
}
@@ -563,22 +631,29 @@ fail:
}
static struct item *
-parse (const char *s, char **error)
+parse (const char *s, const char **error)
{
- struct tokenizer self;
- self.cursor = s;
- self.error = NULL;
-
+ struct tokenizer self = { .cursor = s, .error = PARSE_ERROR_OK };
struct item *list = parse_item_list (&self);
if (!self.error && *self.cursor != '\0')
{
- self.error = "unexpected input";
+ self.error = PARSE_ERROR_UNEXPECTED_INPUT;
item_free_list (list);
list = NULL;
}
+
+#define XX(x, y) y,
+ static const char *strings[PARSE_ERROR_COUNT] =
+ { PARSE_ERROR_TABLE (XX) };
+#undef XX
+
+ static char error_buf[128];
if (self.error && error)
- *error = strdup_printf ("at character %d: %s",
- (int) (self.cursor - s) + 1, self.error);
+ {
+ snprintf (error_buf, sizeof error_buf, "at character %d: %s",
+ (int) (self.cursor - s) + 1, strings[self.error]);
+ *error = error_buf;
+ }
return list;
}
@@ -596,6 +671,7 @@ struct context
char *error; ///< Error information
bool error_is_fatal; ///< Whether the error can be catched
+ bool memory_failure; ///< Memory allocation failure
void *user_data; ///< User data
};
@@ -625,6 +701,7 @@ context_init (struct context *ctx)
ctx->error = NULL;
ctx->error_is_fatal = false;
+ ctx->memory_failure = false;
ctx->user_data = NULL;
}
@@ -639,13 +716,37 @@ context_free (struct context *ctx)
ctx->error = NULL;
}
-static void
+static bool
+set_error (struct context *ctx, const char *format, ...)
+{
+ free (ctx->error);
+
+ va_list ap;
+ va_start (ap, format);
+ ctx->error = strdup_vprintf (format, ap);
+ va_end (ap);
+
+ if (!ctx->error)
+ ctx->memory_failure = true;
+ return false;
+}
+
+static bool
push (struct context *ctx, struct item *item)
{
+ // The `item' is typically a result from new_<type>(), thus when it is null,
+ // that function must have failed. This is a shortcut for convenience.
+ if (!item)
+ {
+ ctx->memory_failure = true;
+ return false;
+ }
+
assert (item->next == NULL);
item->next = ctx->stack;
ctx->stack = item;
ctx->stack_size++;
+ return true;
}
static bool
@@ -653,9 +754,8 @@ bump_reductions (struct context *ctx)
{
if (++ctx->reduction_count >= ctx->reduction_limit)
{
- ctx->error = strdup ("reduction limit reached");
ctx->error_is_fatal = true;
- return false;
+ return set_error (ctx, "reduction limit reached");
}
return true;
}
@@ -669,9 +769,7 @@ call_function (struct context *ctx, const char *name)
for (iter = g_functions; iter; iter = iter->next)
if (!strcmp (name, iter->name))
goto found;
-
- ctx->error = strdup_printf ("unknown function: %s", name);
- return false;
+ return set_error (ctx, "unknown function: %s", name);
found:
if (!bump_reductions (ctx))
@@ -682,14 +780,26 @@ found:
: execute (ctx, iter->script))
return true;
+ // In this case, `error' is NULL
+ if (ctx->memory_failure)
+ return false;
+
// This creates some form of a stack trace
- char *error = strdup_printf ("%s -> %s", name, ctx->error);
- free (ctx->error);
- ctx->error = error;
+ char *tmp = ctx->error;
+ ctx->error = NULL;
+ set_error (ctx, "%s -> %s", name, tmp);
+ free (tmp);
return false;
}
static void
+free_function (struct fn *fn)
+{
+ item_free_list (fn->script);
+ free (fn);
+}
+
+static void
unregister_function (const char *name)
{
for (struct fn **iter = &g_functions; *iter; iter = &(*iter)->next)
@@ -697,9 +807,7 @@ unregister_function (const char *name)
{
struct fn *tmp = *iter;
*iter = tmp->next;
- if (tmp->script)
- item_free_list (tmp->script);
- free (tmp);
+ free_function (tmp);
break;
}
}
@@ -708,23 +816,34 @@ static struct fn *
prepend_new_fn (const char *name)
{
struct fn *fn = calloc (1, sizeof *fn + strlen (name) + 1);
+ if (!fn)
+ return NULL;
+
strcpy (fn->name, name);
fn->next = g_functions;
return g_functions = fn;
}
-static void
+static bool
register_handler (const char *name, handler_fn handler)
{
unregister_function (name);
- prepend_new_fn (name)->handler = handler;
+ struct fn *fn = prepend_new_fn (name);
+ if (!fn)
+ return false;
+ fn->handler = handler;
+ return true;
}
-static void
+static bool
register_script (const char *name, struct item *script)
{
unregister_function (name);
- prepend_new_fn (name)->script = script;
+ struct fn *fn = prepend_new_fn (name);
+ if (!fn)
+ return false;
+ fn->script = script;
+ return true;
}
static bool
@@ -734,9 +853,9 @@ execute (struct context *ctx, struct item *script)
{
if (script->type != ITEM_WORD)
{
- if (!bump_reductions (ctx))
+ if (!bump_reductions (ctx)
+ || !push (ctx, new_clone (script)))
return false;
- push (ctx, new_clone (script));
}
else if (!call_function (ctx, get_word (script)))
return false;
@@ -750,7 +869,7 @@ execute (struct context *ctx, struct item *script)
#define check_stack(n) \
if (ctx->stack_size < n) { \
- ctx->error = strdup ("stack underflow"); \
+ set_error (ctx, "stack underflow"); \
return 0; \
}
@@ -768,9 +887,8 @@ check_type (struct context *ctx, const void *item_, enum item_type type)
if (item->type == type)
return true;
- ctx->error = strdup_printf ("invalid type: expected `%s', got `%s'",
+ return set_error (ctx, "invalid type: expected `%s', got `%s'",
item_type_to_str (type), item_type_to_str (item->type));
- return false;
}
static struct item *
@@ -812,8 +930,7 @@ defn (fn_to_string)
case ITEM_WORD:
item->type = ITEM_STRING;
case ITEM_STRING:
- push (ctx, item);
- return true;
+ return push (ctx, item);
case ITEM_FLOAT:
value = strdup_printf ("%Lf", get_float (item));
@@ -823,18 +940,22 @@ defn (fn_to_string)
break;
default:
- ctx->error = strdup_printf ("cannot convert `%s' to `%s'",
+ set_error (ctx, "cannot convert `%s' to `%s'",
item_type_to_str (item->type), item_type_to_str (ITEM_STRING));
item_free (item);
return false;
}
item_free (item);
+ if (!value)
+ {
+ ctx->memory_failure = true;
+ return false;
+ }
+
item = new_string (value, -1);
free (value);
-
- push (ctx, item);
- return true;
+ return push (ctx, item);
}
defn (fn_to_integer)
@@ -846,8 +967,7 @@ defn (fn_to_integer)
switch (item->type)
{
case ITEM_INTEGER:
- push (ctx, item);
- return true;
+ return push (ctx, item);
case ITEM_FLOAT:
value = get_float (item);
break;
@@ -860,21 +980,19 @@ defn (fn_to_integer)
if (end != s && *s == '\0')
break;
- ctx->error = strdup ("integer conversion error");
item_free (item);
- return false;
+ return set_error (ctx, "integer conversion error");
}
default:
- ctx->error = strdup_printf ("cannot convert `%s' to `%s'",
+ set_error (ctx, "cannot convert `%s' to `%s'",
item_type_to_str (item->type), item_type_to_str (ITEM_INTEGER));
item_free (item);
return false;
}
item_free (item);
- push (ctx, new_integer (value));
- return true;
+ return push (ctx, new_integer (value));
}
defn (fn_to_float)
@@ -886,8 +1004,7 @@ defn (fn_to_float)
switch (item->type)
{
case ITEM_FLOAT:
- push (ctx, item);
- return true;
+ return push (ctx, item);
case ITEM_INTEGER:
value = get_integer (item);
break;
@@ -900,21 +1017,19 @@ defn (fn_to_float)
if (end != s && *s == '\0')
break;
- ctx->error = strdup ("float conversion error");
item_free (item);
- return false;
+ return set_error (ctx, "float conversion error");
}
default:
- ctx->error = strdup_printf ("cannot convert `%s' to `%s'",
+ set_error (ctx, "cannot convert `%s' to `%s'",
item_type_to_str (item->type), item_type_to_str (ITEM_FLOAT));
item_free (item);
return false;
}
item_free (item);
- push (ctx, new_float (value));
- return true;
+ return push (ctx, new_float (value));
}
// - - Miscellaneous - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -927,7 +1042,7 @@ defn (fn_length)
switch (item->type)
{
case ITEM_STRING:
- push (ctx, new_integer (((struct item_string *) item)->len));
+ success = push (ctx, new_integer (((struct item_string *) item)->len));
break;
case ITEM_LIST:
{
@@ -935,12 +1050,11 @@ defn (fn_length)
struct item *iter;
for (iter = get_list (item); iter; iter = iter->next)
length++;
- push (ctx, new_integer (length));
+ success = push (ctx, new_integer (length));
break;
}
default:
- ctx->error = strdup ("invalid type");
- success = false;
+ success = set_error (ctx, "invalid type");
}
item_free (item);
return success;
@@ -951,8 +1065,7 @@ defn (fn_length)
defn (fn_dup)
{
check_stack (1);
- push (ctx, new_clone (ctx->stack));
- return true;
+ return push (ctx, new_clone (ctx->stack));
}
defn (fn_drop)
@@ -965,11 +1078,8 @@ defn (fn_drop)
defn (fn_swap)
{
check_stack (2);
- struct item *second = pop (ctx);
- struct item *first = pop (ctx);
- push (ctx, second);
- push (ctx, first);
- return true;
+ struct item *second = pop (ctx), *first = pop (ctx);
+ return push (ctx, second) && push (ctx, first);
}
defn (fn_call)
@@ -989,20 +1099,20 @@ defn (fn_dip)
struct item *item = pop (ctx);
bool success = check_type (ctx, script, ITEM_LIST)
&& execute (ctx, get_list (script));
- if (success)
- push (ctx, item);
- else
- item_free (item);
item_free (script);
- return success;
+ if (!success)
+ {
+ item_free (item);
+ return false;
+ }
+ return push (ctx, item);
}
defn (fn_unit)
{
check_stack (1);
struct item *item = pop (ctx);
- push (ctx, new_list (item));
- return true;
+ return push (ctx, new_list (item));
}
defn (fn_cons)
@@ -1018,8 +1128,7 @@ defn (fn_cons)
}
item->next = get_list (list);
((struct item_list *) list)->head = item;
- push (ctx, list);
- return true;
+ return push (ctx, list);
}
defn (fn_cat)
@@ -1040,11 +1149,10 @@ defn (fn_cat)
while (*tail)
tail = &(*tail)->next;
*tail = get_list (scnd);
- push (ctx, frst);
((struct item_list *) scnd)->head = NULL;
item_free (scnd);
- return true;
+ return push (ctx, frst);
}
defn (fn_uncons)
@@ -1056,14 +1164,12 @@ defn (fn_uncons)
struct item *first = get_list (list);
if (!first)
{
- ctx->error = strdup ("list is empty");
+ set_error (ctx, "list is empty");
goto fail;
}
((struct item_list *) list)->head = first->next;
first->next = NULL;
- push (ctx, first);
- push (ctx, list);
- return true;
+ return push (ctx, first) && push (ctx, list);
fail:
item_free (list);
return false;
@@ -1072,7 +1178,7 @@ fail:
// - - Logical - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static bool
-to_boolean (struct context *ctx, struct item *item)
+to_boolean (struct context *ctx, struct item *item, bool *ok)
{
switch (item->type)
{
@@ -1083,9 +1189,8 @@ to_boolean (struct context *ctx, struct item *item)
case ITEM_FLOAT:
return get_float (item) != 0.;
default:
- ctx->error = strdup_printf ("cannot convert `%s' to boolean",
- item_type_to_str (item->type));
- return false;
+ return (*ok = set_error (ctx, "cannot convert `%s' to boolean",
+ item_type_to_str (item->type)));
}
}
@@ -1093,12 +1198,10 @@ defn (fn_not)
{
check_stack (1);
struct item *item = pop (ctx);
- bool result = !to_boolean (ctx, item);
+ bool ok = true;
+ bool result = !to_boolean (ctx, item, &ok);
item_free (item);
- if (ctx->error)
- return false;
- push (ctx, new_integer (result));
- return true;
+ return ok && push (ctx, new_integer (result));
}
defn (fn_and)
@@ -1106,11 +1209,11 @@ defn (fn_and)
check_stack (2);
struct item *op1 = pop (ctx);
struct item *op2 = pop (ctx);
- bool result = to_boolean (ctx, op1) && to_boolean (ctx, op2);
+ bool ok = true;
+ bool result = to_boolean (ctx, op1, &ok) && to_boolean (ctx, op2, &ok);
item_free (op1);
item_free (op2);
- push (ctx, new_integer (result));
- return !ctx->error;
+ return ok && push (ctx, new_integer (result));
}
defn (fn_or)
@@ -1118,13 +1221,12 @@ defn (fn_or)
check_stack (2);
struct item *op1 = pop (ctx);
struct item *op2 = pop (ctx);
- bool result = to_boolean (ctx, op1) || ctx->error || to_boolean (ctx, op2);
+ bool ok = true;
+ bool result = to_boolean (ctx, op1, &ok)
+ || !ok || to_boolean (ctx, op2, &ok);
item_free (op1);
item_free (op2);
- if (ctx->error)
- return false;
- push (ctx, new_integer (result));
- return true;
+ return ok && push (ctx, new_integer (result));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1136,11 +1238,12 @@ defn (fn_if)
struct item *then_ = pop (ctx);
struct item *cond_ = pop (ctx);
- bool condition = to_boolean (ctx, cond_);
+ bool ok = true;
+ bool condition = to_boolean (ctx, cond_, &ok);
item_free (cond_);
bool success = false;
- if (!ctx->error
+ if (ok
&& check_type (ctx, then_, ITEM_LIST)
&& check_type (ctx, else_, ITEM_LIST))
success = execute (ctx, condition
@@ -1164,17 +1267,16 @@ defn (fn_try)
if (!execute (ctx, get_list (try)))
{
- if (ctx->error_is_fatal)
+ if (ctx->memory_failure || ctx->error_is_fatal)
goto fail;
- push (ctx, new_string (ctx->error, -1));
+ success = push (ctx, new_string (ctx->error, -1));
free (ctx->error);
ctx->error = NULL;
- if (!execute (ctx, get_list (catch)))
- goto fail;
+ if (success)
+ success = execute (ctx, get_list (catch));
}
- success = true;
fail:
item_free (try);
@@ -1199,8 +1301,8 @@ defn (fn_map)
struct item *result = NULL, **tail = &result;
for (struct item *iter = get_list (list); iter; iter = iter->next)
{
- push (ctx, new_clone (iter));
- if (!execute (ctx, get_list (fn))
+ if (!push (ctx, new_clone (iter))
+ || !execute (ctx, get_list (fn))
|| !check_stack_safe (ctx, 1))
goto fail;
@@ -1212,13 +1314,13 @@ defn (fn_map)
fail:
set_list (list, result);
- if (success)
- push (ctx, list);
- else
- item_free (list);
-
item_free (fn);
- return success;
+ if (!success)
+ {
+ item_free (list);
+ return false;
+ }
+ return push (ctx, list);
}
defn (fn_filter)
@@ -1235,24 +1337,25 @@ defn (fn_filter)
}
bool success = false;
+ bool ok = true;
struct item *result = NULL, **tail = &result;
for (struct item *iter = get_list (list); iter; iter = iter->next)
{
- push (ctx, new_clone (iter));
- if (!execute (ctx, get_list (fn))
+ if (!push (ctx, new_clone (iter))
+ || !execute (ctx, get_list (fn))
|| !check_stack_safe (ctx, 1))
goto fail;
struct item *item = pop (ctx);
- bool survived = to_boolean (ctx, item);
+ bool survived = to_boolean (ctx, item, &ok);
item_free (item);
- if (ctx->error)
+ if (!ok)
goto fail;
-
if (!survived)
continue;
- item = new_clone (iter);
+ if (!(item = new_clone (iter)))
+ goto fail;
*tail = item;
tail = &item->next;
}
@@ -1260,13 +1363,13 @@ defn (fn_filter)
fail:
set_list (list, result);
- if (success)
- push (ctx, list);
- else
- item_free (list);
-
item_free (fn);
- return success;
+ if (!success)
+ {
+ item_free (list);
+ return false;
+ }
+ return push (ctx, list);
}
defn (fn_fold)
@@ -1285,11 +1388,9 @@ defn (fn_fold)
push (ctx, null);
for (struct item *iter = get_list (list); iter; iter = iter->next)
- {
- push (ctx, new_clone (iter));
- if (!execute (ctx, get_list (op)))
+ if (!push (ctx, new_clone (iter))
+ || !execute (ctx, get_list (op)))
goto fail;
- }
success = true;
fail:
@@ -1309,11 +1410,9 @@ defn (fn_each)
goto fail;
for (struct item *iter = get_list (list); iter; iter = iter->next)
- {
- push (ctx, new_clone (iter));
- if (!execute (ctx, get_list (op)))
+ if (!push (ctx, new_clone (iter))
+ || !execute (ctx, get_list (op)))
goto fail;
- }
success = true;
fail:
@@ -1334,10 +1433,7 @@ push_repeated_string (struct context *ctx, struct item *op1, struct item *op2)
assert (repeat->type == ITEM_INTEGER);
if (repeat->value < 0)
- {
- ctx->error = strdup ("cannot multiply a string by a negative value");
- return false;
- }
+ return set_error (ctx, "cannot multiply a string by a negative value");
char *buf = NULL;
size_t len = string->len * repeat->value;
@@ -1352,15 +1448,10 @@ push_repeated_string (struct context *ctx, struct item *op1, struct item *op2)
memcpy (buf + i, string->value, string->len);
struct item *item = new_string (buf, len);
free (buf);
- if (!item)
- goto allocation_fail;
-
- push (ctx, item);
- return true;
+ return push (ctx, item);
allocation_fail:
- // TODO: resolve the memory issues correctly, watch _all_ allocations
- ctx->error = strdup ("memory allocation failed");
+ ctx->memory_failure = true;
return false;
}
@@ -1369,30 +1460,27 @@ defn (fn_times)
check_stack (2);
struct item *op2 = pop (ctx);
struct item *op1 = pop (ctx);
- bool success = true;
+ bool ok;
if (op1->type == ITEM_INTEGER && op2->type == ITEM_INTEGER)
- push (ctx, new_integer (get_integer (op1) * get_integer (op2)));
+ ok = push (ctx, new_integer (get_integer (op1) * get_integer (op2)));
else if (op1->type == ITEM_INTEGER && op2->type == ITEM_FLOAT)
- push (ctx, new_float (get_integer (op1) * get_float (op2)));
+ ok = push (ctx, new_float (get_integer (op1) * get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_FLOAT)
- push (ctx, new_float (get_float (op1) * get_float (op2)));
+ ok = push (ctx, new_float (get_float (op1) * get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_INTEGER)
- push (ctx, new_float (get_float (op1) * get_integer (op2)));
+ ok = push (ctx, new_float (get_float (op1) * get_integer (op2)));
else if (op1->type == ITEM_INTEGER && op2->type == ITEM_STRING)
- success = push_repeated_string (ctx, op2, op1);
+ ok = push_repeated_string (ctx, op2, op1);
else if (op1->type == ITEM_STRING && op2->type == ITEM_INTEGER)
- success = push_repeated_string (ctx, op1, op2);
+ ok = push_repeated_string (ctx, op1, op2);
else
- {
- ctx->error = strdup_printf ("cannot multiply `%s' and `%s'",
+ ok = set_error (ctx, "cannot multiply `%s' and `%s'",
item_type_to_str (op1->type), item_type_to_str (op2->type));
- success = false;
- }
item_free (op1);
item_free (op2);
- return success;
+ return ok;
}
defn (fn_pow)
@@ -1400,27 +1488,24 @@ defn (fn_pow)
check_stack (2);
struct item *op2 = pop (ctx);
struct item *op1 = pop (ctx);
- bool success = true;
+ bool ok;
if (op1->type == ITEM_INTEGER && op2->type == ITEM_INTEGER)
// TODO: implement this properly, outputting an integer
- push (ctx, new_float (powl (get_integer (op1), get_integer (op2))));
+ ok = push (ctx, new_float (powl (get_integer (op1), get_integer (op2))));
else if (op1->type == ITEM_INTEGER && op2->type == ITEM_FLOAT)
- push (ctx, new_float (powl (get_integer (op1), get_float (op2))));
+ ok = push (ctx, new_float (powl (get_integer (op1), get_float (op2))));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_FLOAT)
- push (ctx, new_float (powl (get_float (op1), get_float (op2))));
+ ok = push (ctx, new_float (powl (get_float (op1), get_float (op2))));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_INTEGER)
- push (ctx, new_float (powl (get_float (op1), get_integer (op2))));
+ ok = push (ctx, new_float (powl (get_float (op1), get_integer (op2))));
else
- {
- ctx->error = strdup_printf ("cannot exponentiate `%s' and `%s'",
+ ok = set_error (ctx, "cannot exponentiate `%s' and `%s'",
item_type_to_str (op1->type), item_type_to_str (op2->type));
- success = false;
- }
item_free (op1);
item_free (op2);
- return success;
+ return ok;
}
defn (fn_div)
@@ -1428,34 +1513,28 @@ defn (fn_div)
check_stack (2);
struct item *op2 = pop (ctx);
struct item *op1 = pop (ctx);
- bool success = true;
+ bool ok;
if (op1->type == ITEM_INTEGER && op2->type == ITEM_INTEGER)
{
if (get_integer (op2) == 0)
- {
- ctx->error = strdup ("division by zero");
- success = false;
- }
+ ok = set_error (ctx, "division by zero");
else
- push (ctx, new_integer (get_integer (op1) / get_integer (op2)));
+ ok = push (ctx, new_integer (get_integer (op1) / get_integer (op2)));
}
else if (op1->type == ITEM_INTEGER && op2->type == ITEM_FLOAT)
- push (ctx, new_float (get_integer (op1) / get_float (op2)));
+ ok = push (ctx, new_float (get_integer (op1) / get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_FLOAT)
- push (ctx, new_float (get_float (op1) / get_float (op2)));
+ ok = push (ctx, new_float (get_float (op1) / get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_INTEGER)
- push (ctx, new_float (get_float (op1) / get_integer (op2)));
+ ok = push (ctx, new_float (get_float (op1) / get_integer (op2)));
else
- {
- ctx->error = strdup_printf ("cannot divide `%s' and `%s'",
+ ok = set_error (ctx, "cannot divide `%s' and `%s'",
item_type_to_str (op1->type), item_type_to_str (op2->type));
- success = false;
- }
item_free (op1);
item_free (op2);
- return success;
+ return ok;
}
defn (fn_mod)
@@ -1463,34 +1542,28 @@ defn (fn_mod)
check_stack (2);
struct item *op2 = pop (ctx);
struct item *op1 = pop (ctx);
- bool success = true;
+ bool ok;
if (op1->type == ITEM_INTEGER && op2->type == ITEM_INTEGER)
{
if (get_integer (op2) == 0)
- {
- ctx->error = strdup ("division by zero");
- success = false;
- }
+ ok = set_error (ctx, "division by zero");
else
- push (ctx, new_integer (get_integer (op1) % get_integer (op2)));
+ ok = push (ctx, new_integer (get_integer (op1) % get_integer (op2)));
}
else if (op1->type == ITEM_INTEGER && op2->type == ITEM_FLOAT)
- push (ctx, new_float (fmodl (get_integer (op1), get_float (op2))));
+ ok = push (ctx, new_float (fmodl (get_integer (op1), get_float (op2))));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_FLOAT)
- push (ctx, new_float (fmodl (get_float (op1), get_float (op2))));
+ ok = push (ctx, new_float (fmodl (get_float (op1), get_float (op2))));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_INTEGER)
- push (ctx, new_float (fmodl (get_float (op1), get_integer (op2))));
+ ok = push (ctx, new_float (fmodl (get_float (op1), get_integer (op2))));
else
- {
- ctx->error = strdup_printf ("cannot divide `%s' and `%s'",
+ ok = set_error (ctx, "cannot divide `%s' and `%s'",
item_type_to_str (op1->type), item_type_to_str (op2->type));
- success = false;
- }
item_free (op1);
item_free (op2);
- return success;
+ return ok;
}
static bool
@@ -1515,15 +1588,10 @@ push_concatenated_string (struct context *ctx,
memcpy (buf + s1->len, s2->value, s2->len);
struct item *item = new_string (buf, len);
free (buf);
- if (!item)
- goto allocation_fail;
-
- push (ctx, item);
- return true;
+ return push (ctx, item);
allocation_fail:
- // TODO: resolve the memory issues correctly, watch _all_ allocations
- ctx->error = strdup ("memory allocation failed");
+ ctx->memory_failure = true;
return false;
}
@@ -1533,28 +1601,25 @@ defn (fn_plus)
check_stack (2);
struct item *op2 = pop (ctx);
struct item *op1 = pop (ctx);
- bool success = true;
+ bool ok;
if (op1->type == ITEM_INTEGER && op2->type == ITEM_INTEGER)
- push (ctx, new_integer (get_integer (op1) + get_integer (op2)));
+ ok = push (ctx, new_integer (get_integer (op1) + get_integer (op2)));
else if (op1->type == ITEM_INTEGER && op2->type == ITEM_FLOAT)
- push (ctx, new_float (get_integer (op1) + get_float (op2)));
+ ok = push (ctx, new_float (get_integer (op1) + get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_FLOAT)
- push (ctx, new_float (get_float (op1) + get_float (op2)));
+ ok = push (ctx, new_float (get_float (op1) + get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_INTEGER)
- push (ctx, new_float (get_float (op1) + get_integer (op2)));
+ ok = push (ctx, new_float (get_float (op1) + get_integer (op2)));
else if (op1->type == ITEM_STRING && op2->type == ITEM_STRING)
- success = push_concatenated_string (ctx, op1, op2);
+ ok = push_concatenated_string (ctx, op1, op2);
else
- {
- ctx->error = strdup_printf ("cannot add `%s' and `%s'",
+ ok = set_error (ctx, "cannot add `%s' and `%s'",
item_type_to_str (op1->type), item_type_to_str (op2->type));
- success = false;
- }
item_free (op1);
item_free (op2);
- return success;
+ return ok;
}
defn (fn_minus)
@@ -1562,26 +1627,23 @@ defn (fn_minus)
check_stack (2);
struct item *op2 = pop (ctx);
struct item *op1 = pop (ctx);
- bool success = true;
+ bool ok;
if (op1->type == ITEM_INTEGER && op2->type == ITEM_INTEGER)
- push (ctx, new_integer (get_integer (op1) - get_integer (op2)));
+ ok = push (ctx, new_integer (get_integer (op1) - get_integer (op2)));
else if (op1->type == ITEM_INTEGER && op2->type == ITEM_FLOAT)
- push (ctx, new_float (get_integer (op1) - get_float (op2)));
+ ok = push (ctx, new_float (get_integer (op1) - get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_FLOAT)
- push (ctx, new_float (get_float (op1) - get_float (op2)));
+ ok = push (ctx, new_float (get_float (op1) - get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_INTEGER)
- push (ctx, new_float (get_float (op1) - get_integer (op2)));
+ ok = push (ctx, new_float (get_float (op1) - get_integer (op2)));
else
- {
- ctx->error = strdup_printf ("cannot subtract `%s' and `%s'",
+ ok = set_error (ctx, "cannot subtract `%s' and `%s'",
item_type_to_str (op1->type), item_type_to_str (op2->type));
- success = false;
- }
item_free (op1);
item_free (op2);
- return success;
+ return ok;
}
// - - Comparison - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1637,32 +1699,29 @@ defn (fn_eq)
check_stack (2);
struct item *op2 = pop (ctx);
struct item *op1 = pop (ctx);
- bool success = true;
+ bool ok;
if (op1->type == ITEM_INTEGER && op2->type == ITEM_INTEGER)
- push (ctx, new_integer (get_integer (op1) == get_integer (op2)));
+ ok = push (ctx, new_integer (get_integer (op1) == get_integer (op2)));
else if (op1->type == ITEM_INTEGER && op2->type == ITEM_FLOAT)
- push (ctx, new_integer (get_integer (op1) == get_float (op2)));
+ ok = push (ctx, new_integer (get_integer (op1) == get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_FLOAT)
- push (ctx, new_integer (get_float (op1) == get_float (op2)));
+ ok = push (ctx, new_integer (get_float (op1) == get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_INTEGER)
- push (ctx, new_integer (get_float (op1) == get_integer (op2)));
+ ok = push (ctx, new_integer (get_float (op1) == get_integer (op2)));
else if (op1->type == ITEM_LIST && op2->type == ITEM_LIST)
- push (ctx, new_integer (compare_lists
+ ok = push (ctx, new_integer (compare_lists
(get_list (op1), get_list (op2))));
else if (op1->type == ITEM_STRING && op2->type == ITEM_STRING)
- push (ctx, new_integer (compare_strings
+ ok = push (ctx, new_integer (compare_strings
((struct item_string *)(op1), (struct item_string *)(op2)) == 0));
else
- {
- ctx->error = strdup_printf ("cannot compare `%s' and `%s'",
+ ok = set_error (ctx, "cannot compare `%s' and `%s'",
item_type_to_str (op1->type), item_type_to_str (op2->type));
- success = false;
- }
item_free (op1);
item_free (op2);
- return success;
+ return ok;
}
defn (fn_lt)
@@ -1670,44 +1729,39 @@ defn (fn_lt)
check_stack (2);
struct item *op2 = pop (ctx);
struct item *op1 = pop (ctx);
- bool success = true;
+ bool ok;
if (op1->type == ITEM_INTEGER && op2->type == ITEM_INTEGER)
- push (ctx, new_integer (get_integer (op1) < get_integer (op2)));
+ ok = push (ctx, new_integer (get_integer (op1) < get_integer (op2)));
else if (op1->type == ITEM_INTEGER && op2->type == ITEM_FLOAT)
- push (ctx, new_integer (get_integer (op1) < get_float (op2)));
+ ok = push (ctx, new_integer (get_integer (op1) < get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_FLOAT)
- push (ctx, new_integer (get_float (op1) < get_float (op2)));
+ ok = push (ctx, new_integer (get_float (op1) < get_float (op2)));
else if (op1->type == ITEM_FLOAT && op2->type == ITEM_INTEGER)
- push (ctx, new_integer (get_float (op1) < get_integer (op2)));
+ ok = push (ctx, new_integer (get_float (op1) < get_integer (op2)));
else if (op1->type == ITEM_STRING && op2->type == ITEM_STRING)
- push (ctx, new_integer (compare_strings
+ ok = push (ctx, new_integer (compare_strings
((struct item_string *)(op1), (struct item_string *)(op2)) < 0));
else
- {
- ctx->error = strdup_printf ("cannot compare `%s' and `%s'",
+ ok = set_error (ctx, "cannot compare `%s' and `%s'",
item_type_to_str (op1->type), item_type_to_str (op2->type));
- success = false;
- }
item_free (op1);
item_free (op2);
- return success;
+ return ok;
}
// - - Utilities - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
defn (fn_rand)
{
- push (ctx, new_float ((long double) rand ()
+ return push (ctx, new_float ((long double) rand ()
/ ((long double) RAND_MAX + 1)));
- return true;
}
defn (fn_time)
{
- push (ctx, new_integer (time (NULL)));
- return true;
+ return push (ctx, new_integer (time (NULL)));
}
// XXX: this is a bit too constrained; combines strftime() with gmtime()
@@ -1723,7 +1777,7 @@ defn (fn_strftime)
if (get_integer (time_) < 0)
{
- ctx->error = strdup ("invalid time value");
+ set_error (ctx, "invalid time value");
goto fail;
}
@@ -1732,8 +1786,7 @@ defn (fn_strftime)
struct tm tm;
gmtime_r (&time__, &tm);
buf[strftime (buf, sizeof buf, get_string (format), &tm)] = '\0';
- push (ctx, new_string (buf, -1));
- success = true;
+ success = push (ctx, new_string (buf, -1));
fail:
item_free (time_);
@@ -1784,12 +1837,14 @@ item_to_str (const struct item *item, struct buffer *buf)
break;
}
case ITEM_INTEGER:
- x = strdup_printf ("%lld", get_integer (item));
+ if (!(x = strdup_printf ("%lld", get_integer (item))))
+ goto alloc_failure;
buffer_append (buf, x, strlen (x));
free (x);
break;
case ITEM_FLOAT:
- x = strdup_printf ("%Lf", get_float (item));
+ if (!(x = strdup_printf ("%Lf", get_float (item))))
+ goto alloc_failure;
buffer_append (buf, x, strlen (x));
free (x);
break;
@@ -1799,18 +1854,26 @@ item_to_str (const struct item *item, struct buffer *buf)
buffer_append_c (buf, ']');
break;
}
+ return;
+
+alloc_failure:
+ // This is a bit hackish but it simplifies stuff
+ buf->memory_failure = true;
+ free (buf->s);
+ buf->s = NULL;
}
static void
item_list_to_str (const struct item *script, struct buffer *buf)
{
- bool first = true;
- for (; script; script = script->next)
+ if (!script)
+ return;
+
+ item_to_str (script, buf);
+ while ((script = script->next))
{
- if (!first)
- buffer_append_c (buf, ' ');
+ buffer_append_c (buf, ' ');
item_to_str (script, buf);
- first = false;
}
}
@@ -1880,7 +1943,7 @@ read_message (void)
do
{
if (!fgets (buf, sizeof buf, stdin))
- exit (EXIT_SUCCESS);
+ return NULL;
size_t len = strlen (buf);
// Just to be on the safe side, if the line overflows our buffer,
@@ -1908,7 +1971,7 @@ get_config (const char *key)
{
printf ("ZYKLONB get_config :%s\r\n", key);
struct message *msg = read_message ();
- if (msg->n_params <= 0)
+ if (!msg || msg->n_params <= 0)
exit (EXIT_FAILURE);
return msg->params[0];
}
@@ -1917,9 +1980,11 @@ get_config (const char *key)
// TODO: implement more functions; try to avoid writing them in C
-static void
+static bool
init_runtime_library_scripts (void)
{
+ bool ok = true;
+
// It's much cheaper (and more fun) to define functions in terms of other
// ones. The "unit tests" serve a secondary purpose of showing the usage.
struct script
@@ -1957,107 +2022,136 @@ init_runtime_library_scripts (void)
for (size_t i = 0; i < N_ELEMENTS (scripts); i++)
{
- char *error = NULL;
+ const char *error = NULL;
struct item *script = parse (scripts[i].definition, &error);
if (error)
{
printf (BOT_PRINT "error parsing internal script `%s': %s\r\n",
scripts[i].definition, error);
- free (error);
- continue;
+ ok = false;
}
- register_script (scripts[i].name, script);
+ else
+ ok &= register_script (scripts[i].name, script);
}
struct context ctx;
for (size_t i = 0; i < N_ELEMENTS (scripts); i++)
{
- char *error = NULL;
+ const char *error = NULL;
struct item *script = parse (scripts[i].unit_test, &error);
if (error)
{
printf (BOT_PRINT "error parsing unit test for `%s': %s\r\n",
scripts[i].name, error);
- free (error);
+ ok = false;
continue;
}
context_init (&ctx);
execute (&ctx, script);
item_free_list (script);
- if (ctx.error || ctx.stack_size != 1
- || ctx.stack->type != ITEM_INTEGER || get_integer (ctx.stack) != 1)
+
+ const char *failure = NULL;
+ if (ctx.memory_failure)
+ failure = "memory allocation failure";
+ else if (ctx.error)
+ failure = ctx.error;
+ else if (ctx.stack_size != 1)
+ failure = "too many results on the stack";
+ else if (ctx.stack->type != ITEM_INTEGER)
+ failure = "result is not an integer";
+ else if (get_integer (ctx.stack) != 1)
+ failure = "wrong test result";
+ if (failure)
+ {
printf (BOT_PRINT "error executing unit test for `%s': %s\r\n",
- scripts[i].name, ctx.error ? ctx.error : "wrong test result");
+ scripts[i].name, failure);
+ ok = false;
+ }
context_free (&ctx);
}
+ return ok;
}
-static void
+static bool
init_runtime_library (void)
{
+ bool ok = true;
+
// Type detection
- register_handler ("string?", fn_is_string);
- register_handler ("word?", fn_is_word);
- register_handler ("integer?", fn_is_integer);
- register_handler ("float?", fn_is_float);
- register_handler ("list?", fn_is_list);
+ ok &= register_handler ("string?", fn_is_string);
+ ok &= register_handler ("word?", fn_is_word);
+ ok &= register_handler ("integer?", fn_is_integer);
+ ok &= register_handler ("float?", fn_is_float);
+ ok &= register_handler ("list?", fn_is_list);
// Type conversion
- register_handler (">string", fn_to_string);
- register_handler (">integer", fn_to_integer);
- register_handler (">float", fn_to_float);
+ ok &= register_handler (">string", fn_to_string);
+ ok &= register_handler (">integer", fn_to_integer);
+ ok &= register_handler (">float", fn_to_float);
// Miscellaneous
- register_handler ("length", fn_length);
+ ok &= register_handler ("length", fn_length);
// Basic stack manipulation
- register_handler ("dup", fn_dup);
- register_handler ("drop", fn_drop);
- register_handler ("swap", fn_swap);
+ ok &= register_handler ("dup", fn_dup);
+ ok &= register_handler ("drop", fn_drop);
+ ok &= register_handler ("swap", fn_swap);
// Calling stuff
- register_handler ("call", fn_call);
- register_handler ("dip", fn_dip);
+ ok &= register_handler ("call", fn_call);
+ ok &= register_handler ("dip", fn_dip);
// Control flow
- register_handler ("if", fn_if);
- register_handler ("try", fn_try);
+ ok &= register_handler ("if", fn_if);
+ ok &= register_handler ("try", fn_try);
// List processing
- register_handler ("map", fn_map);
- register_handler ("filter", fn_filter);
- register_handler ("fold", fn_fold);
- register_handler ("each", fn_each);
+ ok &= register_handler ("map", fn_map);
+ ok &= register_handler ("filter", fn_filter);
+ ok &= register_handler ("fold", fn_fold);
+ ok &= register_handler ("each", fn_each);
// List manipulation
- register_handler ("unit", fn_unit);
- register_handler ("cons", fn_cons);
- register_handler ("cat", fn_cat);
- register_handler ("uncons", fn_uncons);
+ ok &= register_handler ("unit", fn_unit);
+ ok &= register_handler ("cons", fn_cons);
+ ok &= register_handler ("cat", fn_cat);
+ ok &= register_handler ("uncons", fn_uncons);
// Arithmetic operations
- register_handler ("+", fn_plus);
- register_handler ("-", fn_minus);
- register_handler ("*", fn_times);
- register_handler ("^", fn_pow);
- register_handler ("/", fn_div);
- register_handler ("%", fn_mod);
+ ok &= register_handler ("+", fn_plus);
+ ok &= register_handler ("-", fn_minus);
+ ok &= register_handler ("*", fn_times);
+ ok &= register_handler ("^", fn_pow);
+ ok &= register_handler ("/", fn_div);
+ ok &= register_handler ("%", fn_mod);
// Comparison
- register_handler ("=", fn_eq);
- register_handler ("<", fn_lt);
+ ok &= register_handler ("=", fn_eq);
+ ok &= register_handler ("<", fn_lt);
// Logical operations
- register_handler ("not", fn_not);
- register_handler ("and", fn_and);
- register_handler ("or", fn_or);
+ ok &= register_handler ("not", fn_not);
+ ok &= register_handler ("and", fn_and);
+ ok &= register_handler ("or", fn_or);
// Utilities
- register_handler ("rand", fn_rand);
- register_handler ("time", fn_time);
- register_handler ("strftime", fn_strftime);
+ ok &= register_handler ("rand", fn_rand);
+ ok &= register_handler ("time", fn_time);
+ ok &= register_handler ("strftime", fn_strftime);
- init_runtime_library_scripts ();
+ ok &= init_runtime_library_scripts ();
+ return ok;
+}
+
+static void
+free_runtime_library (void)
+{
+ struct fn *next, *iter;
+ for (iter = g_functions; iter; iter = next)
+ {
+ next = iter->next;
+ free_function (iter);
+ }
}
// --- Function database -------------------------------------------------------
@@ -2097,6 +2191,12 @@ defn (fn_dot)
item_to_str (item, &buf);
item_free (item);
buffer_append_c (&buf, '\0');
+ if (buf.memory_failure)
+ {
+ ctx->memory_failure = true;
+ return false;
+ }
+
printf ("PRIVMSG %s :%s%s\r\n", info->ctx, info->ctx_quote, buf.s);
free (buf.s);
return true;
@@ -2105,7 +2205,9 @@ defn (fn_dot)
static void
process_message (struct message *msg)
{
- if (strcasecmp (msg->command, "PRIVMSG") || msg->n_params < 2)
+ if (!msg->prefix
+ || strcasecmp (msg->command, "PRIVMSG")
+ || msg->n_params < 2)
return;
char *line = msg->params[1];
@@ -2133,18 +2235,23 @@ process_message (struct message *msg)
else
msg_ctx_quote = strdup ("");
+ if (!msg_ctx_quote)
+ {
+ printf (BOT_PRINT "%s\r\n", "memory allocation failure");
+ return;
+ }
+
struct user_info info;
info.ctx = msg_ctx;
info.ctx_quote = msg_ctx_quote;
// Finally parse and execute the macro
- char *error = NULL;
+ const char *error = NULL;
struct item *script = parse (line, &error);
if (error)
{
printf ("PRIVMSG %s :%s%s: %s\r\n",
msg_ctx, msg_ctx_quote, "parse error", error);
- free (error);
goto end;
}
@@ -2153,9 +2260,15 @@ process_message (struct message *msg)
ctx.user_data = &info;
execute (&ctx, script);
item_free_list (script);
- if (ctx.error)
+
+ const char *failure = NULL;
+ if (ctx.memory_failure)
+ failure = "memory allocation failure";
+ else if (ctx.error)
+ failure = ctx.error;
+ if (failure)
printf ("PRIVMSG %s :%s%s: %s\r\n",
- msg_ctx, msg_ctx_quote, "runtime error", ctx.error);
+ msg_ctx, msg_ctx_quote, "runtime error", failure);
context_free (&ctx);
end:
free (msg_ctx_quote);
@@ -2177,16 +2290,18 @@ main (int argc, char *argv[])
(void) setrlimit (RLIMIT_AS, &limit);
read_db ();
- init_runtime_library ();
- register_handler (".", fn_dot);
+ if (!init_runtime_library ()
+ || !register_handler (".", fn_dot))
+ printf (BOT_PRINT "%s\r\n", "runtime library initialization failed");
g_prefix = strdup (get_config ("prefix"));
printf ("ZYKLONB register\r\n");
- while (true)
- {
- struct message *msg = read_message ();
+ struct message *msg;
+ while ((msg = read_message ()))
process_message (msg);
- }
+
+ free_runtime_library ();
+ free (g_prefix);
return 0;
}