minor cleanup
[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 while (**lasts && strchr(sep, **lasts)) {
130 **lasts = '\0';
131 (*lasts)++;
132 }
133
134 tok = *lasts;
135 if (*tok == '\0')
136 return NULL;
137
138 do {
139 retry = 0;
140 while (**lasts) {
141 /* the previous character was the escape, do not consider this character any further */
142 if (escaped) {
143 escaped = 0;
144 (*lasts)++;
145 continue;
146 }
147
148 /* this character is the escape, do not consider the next character */
149 if (**lasts == esc) {
150 escaped = 1;
151 (*lasts)++;
152 continue;
153 }
154
155 /* we have a quote open, only consider matching quote to close */
156 if (*lastq) {
157 if (**lasts == **lastq)
158 *lastq = NULL;
159 (*lasts)++;
160 continue;
161 }
162
163 /* this character is an opening quote, remember what it is */
164 if (strchr(quote, **lasts)) {
165 *lastq = *lasts;
166 (*lasts)++;
167 continue;
168 }
169
170 /* this character is a separator, separate and be done */
171 if (strchr(sep, **lasts)) {
172 **lasts = '\0';
173 (*lasts)++;
174 break;
175 }
176 (*lasts)++;
177 }
178
179 /* were we left with an unmatched quote?
180 remember where we had trouble
181 try everything following lonely quote again, but pretend quote is there */
182 if (*lastq) {
183 lastq_ret = *lastq;
184 *lasts = *lastq + 1;
185 *lastq = NULL;
186 retry = 1;
187 }
188 } while (retry);
189
190 /* now strip escape characters */
191 for (src = dst = tok; *src; src++, dst++) {
192 if (*src == esc) {
193 src++;
194 if (*src == '\0')
195 break;
196 }
197 *dst = *src;
198 }
199 *dst = *src;
200
201 /* remember where we had trouble */
202 if (lastq_ret)
203 *lastq = lastq_ret;
204
205 return tok;
206 }