began support for DAT assembler directive
[dcpu16] / common.c
diff --git a/common.c b/common.c
new file mode 100644 (file)
index 0000000..9dbadf3
--- /dev/null
+++ b/common.c
@@ -0,0 +1,202 @@
+/*  common.c
+ *  Utility functions shared between modules, but not exported.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "common.h"
+#include "dcpu16.h"
+
+/* initialize a generic dynamic array struct */
+int dynarray_init(struct dynamic_array *da, size_t entry_size, size_t grow_size) {
+    assert(da);
+    assert(entry_size);
+    assert(grow_size);
+
+    if (entry_size == 0 || grow_size == 0) {
+        fprintf(stderr, "%s: internal error: sizes cannot be zero\n", __func__);
+        errno = EINVAL;
+        return -1;
+    }
+
+    da->allocated = 0;
+    da->entries = 0;
+    da->entry_size = entry_size;
+    da->grow_size = grow_size;
+    da->a = malloc(da->entry_size * da->grow_size);
+    if (da->a == NULL) {
+        fprintf(stderr, "%s():%s\n", "malloc", strerror(errno));
+        return -1;
+    }
+    da->allocated = da->grow_size;
+    return 0;
+}
+
+/* allocate and initialize a new generic dynamic array */
+struct dynamic_array *dynarray_new(size_t entry_size, size_t grow_size) {
+    struct dynamic_array *da;
+
+    assert(entry_size);
+    assert(grow_size);
+
+    da = calloc(1, sizeof *da);
+    if (da == NULL) {
+        fprintf(stderr, "%s():%s\n", "calloc", strerror(errno));
+        return NULL;
+    }
+
+    if (dynarray_init(da, entry_size, grow_size)) {
+        fprintf(stderr, "%s():%s\n", "dynarray_init", strerror(errno));
+        free(da);
+        return NULL;
+    }
+
+    return da;
+}
+
+/* copy item onto end of array */
+void *dynarray_add(struct dynamic_array *da, void *item) {
+    void *dst;
+
+    assert(da);
+    assert(item);
+
+    /* make room, make room */
+    if (da->entries == da->allocated) {
+        size_t new_allocated = da->allocated + da->grow_size;
+        void *tmp_ptr = realloc(da->a, new_allocated * da->entry_size);
+        if (tmp_ptr == NULL) {
+            fprintf(stderr, "%s():%s\n", "realloc", strerror(errno));
+            return NULL;
+        }
+        da->a = tmp_ptr;
+        da->allocated = new_allocated;
+
+    }
+
+    dst = DYNARRAY_ITEM(*da, da->entries);
+    memcpy(dst, item, da->entry_size);
+
+    da->entries++;
+
+    return dst;
+}
+
+/* simplified strtoul with range checking */
+int str_to_word(char *s) {
+    unsigned long l;
+    char *ep;
+
+    assert(s);
+
+    errno = 0;
+    l = strtoul(s, &ep, 0);
+
+    if (errno
+    ||  !(*s && *ep == '\0') ) {
+        /* out of range of conversion, or invalid character encountered */
+        return -1;
+    }
+
+    if (l >= DCPU16_RAM) {
+        /* out of range for our needs */
+        errno = ERANGE;
+        return -1;
+    }
+
+    return l;
+}
+
+/* just like strtok_r, but ignores separators within quotes */
+char *strqtok_r(char *str, const char *sep, int esc, const char *quote, char **lastq, char **lasts) {
+    int escaped = 0;
+    int retry;
+    char *tok,
+         *lastq_ret = NULL,
+         *src,
+         *dst;
+
+    if (str) {
+        *lasts = str;
+        *lastq = NULL;
+    }
+
+    /* next token starts after any leading seps */
+    *lasts += strspn(*lasts, sep);
+    tok = *lasts;
+    if (*tok == '\0')
+        return NULL;
+
+    do {
+        retry = 0;
+        while (**lasts) {
+            /* the previous character was the escape, do not consider this character any further */
+            if (escaped) {
+                escaped = 0;
+                (*lasts)++;
+                continue;
+            }
+
+            /* this character is the escape, do not consider the next character */
+            if (**lasts == esc) {
+                escaped = 1;
+                (*lasts)++;
+                continue;
+            }
+
+            /* we have a quote open, only consider matching quote to close */
+            if (*lastq) {
+                if (**lasts == **lastq)
+                    *lastq = NULL;
+                (*lasts)++;
+                continue;
+            }
+
+            /* this character is an opening quote, remember what it is */
+            if (strchr(quote, **lasts)) {
+                *lastq = *lasts;
+                (*lasts)++;
+                continue;
+            }
+
+            /* this character is a separator, separate and be done */
+            if (strchr(sep, **lasts)) {
+                **lasts = '\0';
+                (*lasts)++;
+                break;
+            }
+            (*lasts)++;
+        }
+
+        /* were we left with an unmatched quote?
+           remember where we had trouble
+           try everything following lonely quote again, but pretend quote is there */
+        if (*lastq) {
+            lastq_ret = *lastq;
+            *lasts = *lastq + 1;
+            *lastq = NULL;
+            retry = 1;
+        }
+    } while (retry);
+
+    /* now strip escape characters */
+    for (src = dst = tok; *src; src++, dst++) {
+        if (*src == esc) {
+            src++;
+            if (*src == '\0')
+                break;
+        }
+        *dst = *src;
+    }
+    *dst = *src;
+
+    /* remember where we had trouble */
+    if (lastq_ret)
+        *lastq = lastq_ret;
+
+    return tok;
+}