From f9dced26cae066aaa804f91d20796702cd255091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Sat, 27 Sep 2014 20:22:21 +0200 Subject: [PATCH] script: watch all memory allocations And some other miscellaneous changes. Now it should be ready for the real life. --- plugins/script | 883 ++++++++++++++++++++++++++++--------------------- 1 file changed, 499 insertions(+), 384 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_(), 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,13 +780,25 @@ 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) { @@ -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 bool +init_runtime_library (void) +{ + bool ok = true; + + // Type detection + 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 + ok &= register_handler (">string", fn_to_string); + ok &= register_handler (">integer", fn_to_integer); + ok &= register_handler (">float", fn_to_float); + + // Miscellaneous + ok &= register_handler ("length", fn_length); + + // Basic stack manipulation + ok &= register_handler ("dup", fn_dup); + ok &= register_handler ("drop", fn_drop); + ok &= register_handler ("swap", fn_swap); + + // Calling stuff + ok &= register_handler ("call", fn_call); + ok &= register_handler ("dip", fn_dip); + + // Control flow + ok &= register_handler ("if", fn_if); + ok &= register_handler ("try", fn_try); + + // List processing + 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 + 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 + 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 + ok &= register_handler ("=", fn_eq); + ok &= register_handler ("<", fn_lt); + + // Logical operations + ok &= register_handler ("not", fn_not); + ok &= register_handler ("and", fn_and); + ok &= register_handler ("or", fn_or); + + // Utilities + ok &= register_handler ("rand", fn_rand); + ok &= register_handler ("time", fn_time); + ok &= register_handler ("strftime", fn_strftime); + + ok &= init_runtime_library_scripts (); + return ok; } static void -init_runtime_library (void) +free_runtime_library (void) { - // 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); - - // Type conversion - register_handler (">string", fn_to_string); - register_handler (">integer", fn_to_integer); - register_handler (">float", fn_to_float); - - // Miscellaneous - register_handler ("length", fn_length); - - // Basic stack manipulation - register_handler ("dup", fn_dup); - register_handler ("drop", fn_drop); - register_handler ("swap", fn_swap); - - // Calling stuff - register_handler ("call", fn_call); - register_handler ("dip", fn_dip); - - // Control flow - register_handler ("if", fn_if); - 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); - - // List manipulation - register_handler ("unit", fn_unit); - register_handler ("cons", fn_cons); - register_handler ("cat", fn_cat); - 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); - - // Comparison - register_handler ("=", fn_eq); - register_handler ("<", fn_lt); - - // Logical operations - register_handler ("not", fn_not); - register_handler ("and", fn_and); - register_handler ("or", fn_or); - - // Utilities - register_handler ("rand", fn_rand); - register_handler ("time", fn_time); - register_handler ("strftime", fn_strftime); - - init_runtime_library_scripts (); + 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; }