+/* 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;
+}