began support for DAT assembler directive
[dcpu16] / common.c
1 /* common.c
2 * Utility functions shared between modules, but not exported.
3 */
4
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <errno.h>
9 #include <assert.h>
10
11 #include "common.h"
12 #include "dcpu16.h"
13
14 /* initialize a generic dynamic array struct */
15 int dynarray_init(struct dynamic_array *da, size_t entry_size, size_t grow_size) {
16 assert(da);
17 assert(entry_size);
18 assert(grow_size);
19
20 if (entry_size == 0 || grow_size == 0) {
21 fprintf(stderr, "%s: internal error: sizes cannot be zero\n", __func__);
22 errno = EINVAL;
23 return -1;
24 }
25
26 da->allocated = 0;
27 da->entries = 0;
28 da->entry_size = entry_size;
29 da->grow_size = grow_size;
30 da->a = malloc(da->entry_size * da->grow_size);
31 if (da->a == NULL) {
32 fprintf(stderr, "%s():%s\n", "malloc", strerror(errno));
33 return -1;
34 }
35 da->allocated = da->grow_size;
36 return 0;
37 }
38
39 /* allocate and initialize a new generic dynamic array */
40 struct dynamic_array *dynarray_new(size_t entry_size, size_t grow_size) {
41 struct dynamic_array *da;
42
43 assert(entry_size);
44 assert(grow_size);
45
46 da = calloc(1, sizeof *da);
47 if (da == NULL) {
48 fprintf(stderr, "%s():%s\n", "calloc", strerror(errno));
49 return NULL;
50 }
51
52 if (dynarray_init(da, entry_size, grow_size)) {
53 fprintf(stderr, "%s():%s\n", "dynarray_init", strerror(errno));
54 free(da);
55 return NULL;
56 }
57
58 return da;
59 }
60
61 /* copy item onto end of array */
62 void *dynarray_add(struct dynamic_array *da, void *item) {
63 void *dst;
64
65 assert(da);
66 assert(item);
67
68 /* make room, make room */
69 if (da->entries == da->allocated) {
70 size_t new_allocated = da->allocated + da->grow_size;
71 void *tmp_ptr = realloc(da->a, new_allocated * da->entry_size);
72 if (tmp_ptr == NULL) {
73 fprintf(stderr, "%s():%s\n", "realloc", strerror(errno));
74 return NULL;
75 }
76 da->a = tmp_ptr;
77 da->allocated = new_allocated;
78
79 }
80
81 dst = DYNARRAY_ITEM(*da, da->entries);
82 memcpy(dst, item, da->entry_size);
83
84 da->entries++;
85
86 return dst;
87 }
88
89 /* simplified strtoul with range checking */
90 int str_to_word(char *s) {
91 unsigned long l;
92 char *ep;
93
94 assert(s);
95
96 errno = 0;
97 l = strtoul(s, &ep, 0);
98
99 if (errno
100 || !(*s && *ep == '\0') ) {
101 /* out of range of conversion, or invalid character encountered */
102 return -1;
103 }
104
105 if (l >= DCPU16_RAM) {
106 /* out of range for our needs */
107 errno = ERANGE;
108 return -1;
109 }
110
111 return l;
112 }
113
114 /* just like strtok_r, but ignores separators within quotes */
115 char *strqtok_r(char *str, const char *sep, int esc, const char *quote, char **lastq, char **lasts) {
116 int escaped = 0;
117 int retry;
118 char *tok,
119 *lastq_ret = NULL,
120 *src,
121 *dst;
122
123 if (str) {
124 *lasts = str;
125 *lastq = NULL;
126 }
127
128 /* next token starts after any leading seps */
129 *lasts += strspn(*lasts, sep);
130 tok = *lasts;
131 if (*tok == '\0')
132 return NULL;
133
134 do {
135 retry = 0;
136 while (**lasts) {
137 /* the previous character was the escape, do not consider this character any further */
138 if (escaped) {
139 escaped = 0;
140 (*lasts)++;
141 continue;
142 }
143
144 /* this character is the escape, do not consider the next character */
145 if (**lasts == esc) {
146 escaped = 1;
147 (*lasts)++;
148 continue;
149 }
150
151 /* we have a quote open, only consider matching quote to close */
152 if (*lastq) {
153 if (**lasts == **lastq)
154 *lastq = NULL;
155 (*lasts)++;
156 continue;
157 }
158
159 /* this character is an opening quote, remember what it is */
160 if (strchr(quote, **lasts)) {
161 *lastq = *lasts;
162 (*lasts)++;
163 continue;
164 }
165
166 /* this character is a separator, separate and be done */
167 if (strchr(sep, **lasts)) {
168 **lasts = '\0';
169 (*lasts)++;
170 break;
171 }
172 (*lasts)++;
173 }
174
175 /* were we left with an unmatched quote?
176 remember where we had trouble
177 try everything following lonely quote again, but pretend quote is there */
178 if (*lastq) {
179 lastq_ret = *lastq;
180 *lasts = *lastq + 1;
181 *lastq = NULL;
182 retry = 1;
183 }
184 } while (retry);
185
186 /* now strip escape characters */
187 for (src = dst = tok; *src; src++, dst++) {
188 if (*src == esc) {
189 src++;
190 if (*src == '\0')
191 break;
192 }
193 *dst = *src;
194 }
195 *dst = *src;
196
197 /* remember where we had trouble */
198 if (lastq_ret)
199 *lastq = lastq_ret;
200
201 return tok;
202 }