Commit | Line | Data |
---|---|---|
b64eb60f MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Types for the test-vector framework | |
4 | * | |
5 | * (c) 2023 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of the mLib utilities library. | |
11 | * | |
12 | * mLib is free software: you can redistribute it and/or modify it under | |
13 | * the terms of the GNU Library General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or (at | |
15 | * your option) any later version. | |
16 | * | |
17 | * mLib is distributed in the hope that it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | |
20 | * License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU Library General Public | |
23 | * License along with mLib. If not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |
25 | * USA. | |
26 | */ | |
27 | ||
28 | /*----- Header files ------------------------------------------------------*/ | |
29 | ||
30 | #include <assert.h> | |
31 | #include <ctype.h> | |
32 | #include <errno.h> | |
33 | #include <limits.h> | |
34 | #include <stdio.h> | |
35 | #include <string.h> | |
36 | ||
37 | #include "buf.h" | |
38 | #include "codec.h" | |
39 | # include "base32.h" | |
40 | # include "base64.h" | |
41 | # include "hex.h" | |
42 | #include "dstr.h" | |
43 | #include "tvec.h" | |
44 | ||
45 | /*----- Preliminary utilities ---------------------------------------------*/ | |
46 | ||
47 | static int signed_to_buf(buf *b, long i) | |
48 | { | |
49 | kludge64 k; | |
50 | unsigned long u; | |
51 | ||
52 | u = i; | |
53 | if (i >= 0) ASSIGN64(k, u); | |
54 | else { ASSIGN64(k, ~u); CPL64(k, k); } | |
55 | return (buf_putk64l(b, k)); | |
56 | } | |
57 | ||
58 | static int signed_from_buf(buf *b, long *i_out) | |
59 | { | |
60 | kludge64 k, lmax, not_lmin; | |
61 | ||
62 | ASSIGN64(lmax, LONG_MAX); ASSIGN64(not_lmin, ~(unsigned long)LONG_MIN); | |
63 | if (buf_getk64l(b, &k)) return (-1); | |
64 | if (CMP64(k, <=, lmax)) *i_out = (long)GET64(unsigned long, k); | |
65 | else { | |
66 | CPL64(k, k); | |
67 | if (CMP64(k, <=, not_lmin)) *i_out = -(long)GET64(unsigned long, k) - 1; | |
68 | else return (-1); | |
69 | } | |
70 | return (0); | |
71 | } | |
72 | ||
73 | static int unsigned_to_buf(buf *b, unsigned long u) | |
74 | { kludge64 k; ASSIGN64(k, u); return (buf_putk64l(b, k)); } | |
75 | ||
76 | static int unsigned_from_buf(buf *b, unsigned long *u_out) | |
77 | { | |
78 | kludge64 k, ulmax; | |
79 | ||
80 | ASSIGN64(ulmax, ULONG_MAX); | |
81 | if (buf_getk64l(b, &k)) return (-1); | |
82 | if (CMP64(k, >, ulmax)) return (-1); | |
83 | *u_out = GET64(unsigned long, k); return (0); | |
84 | } | |
85 | ||
86 | static int hex_width(unsigned long u) | |
87 | { | |
88 | int wd; | |
89 | unsigned long t; | |
90 | ||
91 | for (t = u >> 4, wd = 4; t >>= wd, wd *= 2, t; ); | |
92 | return (wd/4); | |
93 | } | |
94 | ||
882a39c1 MW |
95 | static int check_signed_range(long i, |
96 | const struct tvec_irange *ir, | |
97 | struct tvec_state *tv) | |
b64eb60f | 98 | { |
882a39c1 | 99 | if (ir && (ir->min > i || i > ir->max)) { |
b64eb60f MW |
100 | tvec_error(tv, "integer %ld out of range (must be in [%ld .. %ld])", |
101 | i, ir->min, ir->max); | |
882a39c1 MW |
102 | return (-1); |
103 | } | |
104 | return (0); | |
b64eb60f MW |
105 | } |
106 | ||
882a39c1 MW |
107 | static int check_unsigned_range(unsigned long u, |
108 | const struct tvec_urange *ur, | |
109 | struct tvec_state *tv) | |
b64eb60f | 110 | { |
882a39c1 | 111 | if (ur && (ur->min > u || u > ur->max)) { |
b64eb60f MW |
112 | tvec_error(tv, "integer %lu out of range (must be in [%lu .. %lu])", |
113 | u, ur->min, ur->max); | |
882a39c1 MW |
114 | return (-1); |
115 | } | |
116 | return (0); | |
b64eb60f MW |
117 | } |
118 | ||
882a39c1 MW |
119 | static int parse_signed(long *i_out, const char *p, |
120 | const struct tvec_irange *ir, | |
121 | struct tvec_state *tv) | |
b64eb60f MW |
122 | { |
123 | char *q; const char *pp; | |
124 | int olderr; | |
125 | long i; | |
126 | ||
127 | olderr = errno; errno = 0; | |
128 | pp = p; if (*pp == '-' || *pp == '+') pp++; | |
882a39c1 | 129 | if (!ISDIGIT(*pp)) return (tvec_syntax(tv, *pp, "signed integer")); |
b64eb60f | 130 | i = strtol(p, &q, 0); |
882a39c1 MW |
131 | if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line")); |
132 | if (errno) return (tvec_error(tv, "invalid integer `%s'", p)); | |
b64eb60f MW |
133 | check_signed_range(i, ir, tv); |
134 | errno = olderr; *i_out = i; | |
882a39c1 | 135 | return (0); |
b64eb60f MW |
136 | } |
137 | ||
882a39c1 MW |
138 | static int parse_unsigned(unsigned long *u_out, const char *p, |
139 | const struct tvec_urange *ur, | |
140 | struct tvec_state *tv) | |
b64eb60f MW |
141 | { |
142 | char *q; | |
143 | int olderr; | |
144 | unsigned long u; | |
145 | ||
146 | olderr = errno; errno = 0; | |
882a39c1 | 147 | if (!ISDIGIT(*p)) return (tvec_syntax(tv, *p, "unsigned integer")); |
b64eb60f | 148 | u = strtoul(p, &q, 0); |
882a39c1 MW |
149 | if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line")); |
150 | if (errno) return (tvec_error(tv, "invalid integer `%s'", p)); | |
b64eb60f MW |
151 | check_unsigned_range(u, ur, tv); |
152 | errno = olderr; *u_out = u; | |
882a39c1 | 153 | return (0); |
b64eb60f MW |
154 | } |
155 | ||
156 | static int convert_hex(char ch, int *v_out) | |
157 | { | |
158 | if ('0' <= ch && ch <= '9') { *v_out = ch - '0'; return (0); } | |
159 | else if ('a' <= ch && ch <= 'f') { *v_out = ch - 'a' + 10; return (0); } | |
160 | else if ('A' <= ch && ch <= 'F') { *v_out = ch - 'A' + 10; return (0); } | |
161 | else return (-1); | |
162 | } | |
163 | ||
882a39c1 | 164 | static int read_quoted_string(dstr *d, int quote, struct tvec_state *tv) |
b64eb60f MW |
165 | { |
166 | char expect[4]; | |
167 | int ch, i, esc; | |
168 | unsigned f = 0; | |
169 | #define f_brace 1u | |
170 | ||
171 | sprintf(expect, "`%c'", quote); | |
172 | ||
173 | for (;;) { | |
174 | ch = getc(tv->fp); | |
175 | reinsert: | |
176 | switch (ch) { | |
177 | case EOF: case '\n': | |
882a39c1 | 178 | return (tvec_syntax(tv, ch, expect)); |
b64eb60f MW |
179 | |
180 | case '\\': | |
181 | if (quote == '\'') goto ordinary; | |
182 | ch = getc(tv->fp); | |
183 | switch (ch) { | |
184 | case EOF: tvec_syntax(tv, ch, expect); | |
185 | case '\n': tv->lno++; break; | |
186 | case '\'': DPUTC(d, '\''); break; | |
187 | case '\\': DPUTC(d, '\\'); break; | |
188 | case '"': DPUTC(d, '"'); break; | |
189 | case 'a': DPUTC(d, '\a'); break; | |
190 | case 'b': DPUTC(d, '\b'); break; | |
191 | case 'e': DPUTC(d, '\x1b'); break; | |
192 | case 'f': DPUTC(d, '\f'); break; | |
193 | case 'n': DPUTC(d, '\n'); break; | |
194 | case 'r': DPUTC(d, '\r'); break; | |
195 | case 't': DPUTC(d, '\t'); break; | |
196 | case 'v': DPUTC(d, '\v'); break; | |
197 | ||
198 | case 'x': | |
199 | ch = getc(tv->fp); | |
200 | if (ch == '{') { f |= f_brace; ch = getc(tv->fp); } | |
201 | else f &= ~f_brace; | |
882a39c1 MW |
202 | if (convert_hex(ch, &esc)) |
203 | return (tvec_syntax(tv, ch, "hex digit")); | |
b64eb60f MW |
204 | for (;;) { |
205 | ch = getc(tv->fp); if (convert_hex(ch, &i)) break; | |
206 | esc = 8*esc + i; | |
207 | if (esc > UCHAR_MAX) | |
882a39c1 MW |
208 | return (tvec_error(tv, |
209 | "character code %d out of range", esc)); | |
b64eb60f MW |
210 | } |
211 | DPUTC(d, esc); | |
212 | if (!(f&f_brace)) goto reinsert; | |
882a39c1 | 213 | else if (ch != '}') return (tvec_syntax(tv, ch, "`}'")); |
b64eb60f MW |
214 | break; |
215 | ||
216 | default: | |
217 | if ('0' <= ch && ch < '8') { | |
218 | i = 1; esc = ch - '0'; | |
219 | for (;;) { | |
220 | ch = getc(tv->fp); | |
221 | if (i > 3 || '0' > ch || ch >= '8') break; | |
222 | esc = 8*esc + ch - '0'; i++; | |
223 | } | |
224 | if (esc > UCHAR_MAX) | |
882a39c1 MW |
225 | return (tvec_error(tv, |
226 | "character code %d out of range", esc)); | |
b64eb60f MW |
227 | DPUTC(d, esc); |
228 | goto reinsert; | |
229 | } | |
882a39c1 | 230 | return (tvec_syntax(tv, ch, "string escape")); |
b64eb60f MW |
231 | } |
232 | break; | |
233 | ||
234 | default: | |
235 | if (ch == quote) goto end; | |
236 | ordinary: | |
237 | DPUTC(d, ch); | |
238 | break; | |
239 | } | |
240 | } | |
241 | ||
242 | end: | |
243 | DPUTZ(d); | |
882a39c1 | 244 | return (0); |
b64eb60f MW |
245 | |
246 | #undef f_brace | |
247 | } | |
248 | ||
249 | enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 }; | |
250 | ||
251 | static int collect_bare(dstr *d, struct tvec_state *tv) | |
252 | { | |
253 | size_t pos = d->len; | |
254 | enum { WORD, SPACE, ESCAPE }; unsigned s = WORD; | |
255 | int ch, rc; | |
256 | ||
257 | for (;;) { | |
258 | ch = getc(tv->fp); | |
259 | switch (ch) { | |
260 | case EOF: | |
882a39c1 MW |
261 | tvec_syntax(tv, ch, "bareword"); |
262 | rc = -1; goto end; | |
b64eb60f MW |
263 | case '\n': |
264 | if (s == ESCAPE) { tv->lno++; goto addch; } | |
265 | if (s == WORD) pos = d->len; | |
882a39c1 | 266 | ungetc(ch, tv->fp); if (tvec_nexttoken(tv)) { rc = -1; goto end; } |
b64eb60f MW |
267 | DPUTC(d, ' '); s = SPACE; |
268 | break; | |
269 | case '"': case '\'': case '!': | |
882a39c1 | 270 | if (s == SPACE) { ungetc(ch, tv->fp); goto done; } |
b64eb60f MW |
271 | goto addch; |
272 | case '\\': | |
273 | s = ESCAPE; | |
274 | break; | |
275 | default: | |
276 | if (s != ESCAPE && isspace(ch)) { | |
277 | if (s == WORD) pos = d->len; | |
278 | DPUTC(d, ch); s = SPACE; | |
279 | break; | |
280 | } | |
281 | addch: | |
282 | DPUTC(d, ch); s = WORD; | |
283 | } | |
284 | } | |
285 | ||
286 | done: | |
287 | if (s == SPACE) d->len = pos; | |
882a39c1 MW |
288 | DPUTZ(d); rc = 0; |
289 | end: | |
290 | return (rc); | |
b64eb60f MW |
291 | } |
292 | ||
293 | static void set_up_encoding(const codec_class **ccl_out, unsigned *f_out, | |
294 | unsigned code) | |
295 | { | |
296 | switch (code) { | |
297 | case TVCODE_BARE: | |
298 | *ccl_out = 0; *f_out = 0; | |
299 | break; | |
300 | case TVCODE_HEX: | |
301 | *ccl_out = &hex_class; *f_out = CDCF_IGNCASE; | |
302 | break; | |
303 | case TVCODE_BASE32: | |
304 | *ccl_out = &base32_class; *f_out = CDCF_IGNCASE | CDCF_IGNEQPAD; | |
305 | break; | |
306 | case TVCODE_BASE64: | |
307 | *ccl_out = &base64_class; *f_out = CDCF_IGNEQPAD; | |
308 | break; | |
309 | default: | |
310 | abort(); | |
311 | } | |
312 | } | |
313 | ||
882a39c1 MW |
314 | static int read_compound_string(void **p_inout, size_t *sz_inout, |
315 | unsigned code, struct tvec_state *tv) | |
b64eb60f MW |
316 | { |
317 | const codec_class *ccl; unsigned f; | |
318 | codec *cdc; | |
319 | dstr d = DSTR_INIT, w = DSTR_INIT; | |
320 | char *p; | |
882a39c1 | 321 | int ch, err, rc; |
b64eb60f MW |
322 | |
323 | set_up_encoding(&ccl, &f, code); | |
324 | if (tvec_nexttoken(tv)) tvec_syntax(tv, fgetc(tv->fp), "string"); | |
325 | do { | |
326 | ch = getc(tv->fp); | |
327 | if (ch == '"' || ch == '\'') | |
882a39c1 | 328 | { if (read_quoted_string(&d, ch, tv)) { rc = -1; goto end; } } |
b64eb60f MW |
329 | else if (ch == '!') { |
330 | ungetc(ch, tv->fp); | |
331 | DRESET(&w); tvec_readword(tv, &w, ";", "`!'-keyword"); | |
332 | if (STRCMP(w.buf, ==, "!bare")) code = TVCODE_BARE; | |
333 | else if (STRCMP(w.buf, ==, "!hex")) code = TVCODE_HEX; | |
334 | else if (STRCMP(w.buf, ==, "!base32")) code = TVCODE_BASE32; | |
335 | else if (STRCMP(w.buf, ==, "!base64")) code = TVCODE_BASE64; | |
882a39c1 MW |
336 | else { |
337 | tvec_error(tv, "unknown string keyword `%s'", w.buf); | |
338 | rc = -1; goto end; | |
339 | } | |
b64eb60f MW |
340 | set_up_encoding(&ccl, &f, code); |
341 | } else if (ccl) { | |
342 | ungetc(ch, tv->fp); | |
343 | DRESET(&w); | |
882a39c1 MW |
344 | if (tvec_readword(tv, &w, ";", "%s-encoded fragment", ccl->name)) |
345 | { rc = -1; goto end; } | |
b64eb60f MW |
346 | cdc = ccl->decoder(f); |
347 | err = cdc->ops->code(cdc, w.buf, w.len, &d); | |
348 | if (!err) err = cdc->ops->code(cdc, 0, 0, &d); | |
882a39c1 | 349 | if (err) { |
b64eb60f MW |
350 | tvec_error(tv, "invalid %s fragment `%s': %s", |
351 | ccl->name, w.buf, codec_strerror(err)); | |
882a39c1 MW |
352 | rc = -1; goto end; |
353 | } | |
b64eb60f MW |
354 | cdc->ops->destroy(cdc); |
355 | } else switch (code) { | |
356 | case TVCODE_BARE: | |
357 | ungetc(ch, tv->fp); | |
358 | if (collect_bare(&d, tv)) goto done; | |
359 | break; | |
360 | default: | |
361 | abort(); | |
362 | } | |
363 | } while (!tvec_nexttoken(tv)); | |
364 | ||
365 | done: | |
366 | if (*sz_inout <= d.len) | |
367 | { xfree(*p_inout); *p_inout = xmalloc(d.len + 1); } | |
368 | p = *p_inout; memcpy(p, d.buf, d.len); p[d.len] = 0; *sz_inout = d.len; | |
882a39c1 MW |
369 | rc = 0; |
370 | end: | |
b64eb60f | 371 | dstr_destroy(&d); dstr_destroy(&w); |
882a39c1 | 372 | return (rc); |
b64eb60f MW |
373 | } |
374 | ||
375 | /*----- Skeleton ----------------------------------------------------------*/ | |
376 | /* | |
377 | static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd) | |
378 | static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd) | |
379 | static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1, | |
380 | const struct tvec_regdef *rd) | |
381 | static size_t measure_...(const union tvec_regval *rv, | |
382 | const struct tvec_regdef *rd) | |
383 | static int tobuf_...(buf *b, const union tvec_regval *rv, | |
384 | const struct tvec_regdef *rd) | |
385 | static int frombuf_...(buf *b, union tvec_regval *rv, | |
386 | const struct tvec_regdef *rd) | |
387 | static void parse_...(union tvec_regval *rv, const struct tvec_regdef *rd, | |
388 | struct tvec_state *tv) | |
389 | static void dump_...(const union tvec_regval *rv, | |
390 | const struct tvec_regdef *rd, | |
391 | struct tvec_state *tv, unsigned style) | |
392 | ||
393 | const struct tvec_regty tvty_... = { | |
394 | init_..., release_..., eq_..., measure_..., | |
395 | tobuf_..., frombuf_..., | |
396 | parse_..., dump_... | |
397 | }; | |
398 | */ | |
399 | /*----- Signed and unsigned integer types ---------------------------------*/ | |
400 | ||
401 | static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd) | |
402 | { rv->i = 0; } | |
403 | ||
404 | static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd) | |
405 | { rv->u = 0; } | |
406 | ||
407 | static void release_int(union tvec_regval *rv, const struct tvec_regdef *rd) | |
408 | { ; } | |
409 | ||
410 | static int eq_int(const union tvec_regval *rv0, const union tvec_regval *rv1, | |
411 | const struct tvec_regdef *rd) | |
412 | { return (rv0->i == rv1->i); } | |
413 | ||
414 | static int eq_uint(const union tvec_regval *rv0, | |
415 | const union tvec_regval *rv1, | |
416 | const struct tvec_regdef *rd) | |
417 | { return (rv0->u == rv1->u); } | |
418 | ||
419 | static size_t measure_int(const union tvec_regval *rv, | |
420 | const struct tvec_regdef *rd) | |
421 | { return (8); } | |
422 | ||
423 | static int tobuf_int(buf *b, const union tvec_regval *rv, | |
424 | const struct tvec_regdef *rd) | |
425 | { return (signed_to_buf(b, rv->i)); } | |
426 | ||
427 | static int tobuf_uint(buf *b, const union tvec_regval *rv, | |
428 | const struct tvec_regdef *rd) | |
429 | { return (unsigned_to_buf(b, rv->u)); } | |
430 | ||
431 | static int frombuf_int(buf *b, union tvec_regval *rv, | |
432 | const struct tvec_regdef *rd) | |
882a39c1 | 433 | { return (signed_from_buf(b, &rv->i)); } |
b64eb60f MW |
434 | |
435 | static int frombuf_uint(buf *b, union tvec_regval *rv, | |
436 | const struct tvec_regdef *rd) | |
437 | { return (unsigned_from_buf(b, &rv->u)); } | |
438 | ||
882a39c1 MW |
439 | static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd, |
440 | struct tvec_state *tv) | |
b64eb60f MW |
441 | { |
442 | dstr d = DSTR_INIT; | |
882a39c1 | 443 | int rc; |
b64eb60f | 444 | |
882a39c1 MW |
445 | if (tvec_readword(tv, &d, ";", "signed integer")) { rc = -1; goto end; } |
446 | if (parse_signed(&rv->i, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } | |
447 | if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } | |
448 | rc = 0; | |
449 | end: | |
b64eb60f | 450 | dstr_destroy(&d); |
882a39c1 | 451 | return (rc); |
b64eb60f MW |
452 | } |
453 | ||
882a39c1 MW |
454 | static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd, |
455 | struct tvec_state *tv) | |
b64eb60f MW |
456 | { |
457 | dstr d = DSTR_INIT; | |
882a39c1 | 458 | int rc; |
b64eb60f | 459 | |
882a39c1 MW |
460 | if (tvec_readword(tv, &d, ";", "unsigned integer")) { rc = -1; goto end; } |
461 | if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } | |
462 | if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } | |
463 | rc = 0; | |
464 | end: | |
b64eb60f | 465 | dstr_destroy(&d); |
882a39c1 | 466 | return (rc); |
b64eb60f MW |
467 | } |
468 | ||
469 | static void dump_int(const union tvec_regval *rv, | |
470 | const struct tvec_regdef *rd, | |
471 | struct tvec_state *tv, unsigned style) | |
472 | { | |
473 | unsigned long u; | |
474 | ||
475 | tvec_write(tv, "%ld", rv->i); | |
476 | if (!(style&TVSF_COMPACT)) { | |
477 | if (rv->i >= 0) u = rv->i; | |
478 | else u = -(unsigned long)rv->i; | |
479 | tvec_write(tv, " ; = %s0x%0*lx", rv->i < 0 ? "-" : "", hex_width(u), u); | |
480 | } | |
481 | } | |
482 | ||
483 | static void dump_uint(const union tvec_regval *rv, | |
484 | const struct tvec_regdef *rd, | |
485 | struct tvec_state *tv, unsigned style) | |
486 | { | |
487 | tvec_write(tv, "%lu", rv->u); | |
488 | if (!(style&TVSF_COMPACT)) | |
489 | tvec_write(tv, " ; = 0x%0*lx", hex_width(rv->u), rv->u); | |
490 | } | |
491 | ||
492 | const struct tvec_regty tvty_int = { | |
493 | init_int, release_int, eq_int, measure_int, | |
494 | tobuf_int, frombuf_int, | |
495 | parse_int, dump_int | |
496 | }; | |
497 | ||
498 | const struct tvec_irange | |
499 | tvrange_schar = { SCHAR_MIN, SCHAR_MAX }, | |
500 | tvrange_short = { SHRT_MIN, SHRT_MAX }, | |
501 | tvrange_int = { INT_MIN, INT_MAX }, | |
502 | tvrange_long = { LONG_MIN, LONG_MAX }, | |
503 | tvrange_sbyte = { -128, 127 }, | |
504 | tvrange_i16 = { -32768, +32767 }, | |
505 | tvrange_i32 = { -2147483648, 2147483647 }; | |
506 | ||
507 | const struct tvec_regty tvty_uint = { | |
508 | init_uint, release_int, eq_uint, measure_int, | |
509 | tobuf_uint, frombuf_uint, | |
510 | parse_uint, dump_uint | |
511 | }; | |
512 | ||
513 | const struct tvec_urange | |
514 | tvrange_uchar = { 0, UCHAR_MAX }, | |
515 | tvrange_ushort = { 0, USHRT_MAX }, | |
516 | tvrange_uint = { 0, UINT_MAX }, | |
517 | tvrange_ulong = { 0, ULONG_MAX }, | |
518 | tvrange_size = { 0, (size_t)-1 }, | |
519 | tvrange_byte = { 0, 255 }, | |
520 | tvrange_u16 = { 0, 65535 }, | |
521 | tvrange_u32 = { 0, 4294967296 }; | |
522 | ||
523 | int tvec_claimeq_int(struct tvec_state *tv, long i0, long i1, | |
524 | const char *file, unsigned lno, const char *expr) | |
525 | { | |
526 | tv->in[0].v.i = i0; tv->out[0].v.i = i1; | |
527 | return (tvec_claimeq(tv, &tvty_int, 0, file, lno, expr)); | |
528 | } | |
529 | ||
530 | int tvec_claimeq_uint(struct tvec_state *tv, | |
531 | unsigned long u0, unsigned long u1, | |
532 | const char *file, unsigned lno, const char *expr) | |
533 | { | |
534 | tv->in[0].v.u = u0; tv->out[0].v.u = u1; | |
535 | return (tvec_claimeq(tv, &tvty_uint, 0, file, lno, expr)); | |
536 | } | |
537 | ||
538 | /*----- Enumerations ------------------------------------------------------*/ | |
539 | ||
540 | static void init_enum(union tvec_regval *rv, const struct tvec_regdef *rd) | |
541 | { | |
542 | const struct tvec_enuminfo *ei = rd->arg.p; | |
543 | ||
544 | switch (ei->mv) { | |
545 | #define CASE(tag, ty, slot) \ | |
546 | case TVMISC_##tag: rv->slot = 0; break; | |
547 | TVEC_MISCSLOTS(CASE) | |
548 | #undef CASE | |
549 | default: abort(); | |
550 | } | |
551 | } | |
552 | ||
553 | static int eq_enum(const union tvec_regval *rv0, | |
554 | const union tvec_regval *rv1, | |
555 | const struct tvec_regdef *rd) | |
556 | { | |
557 | const struct tvec_enuminfo *ei = rd->arg.p; | |
558 | ||
559 | switch (ei->mv) { | |
560 | #define CASE(tag, ty, slot) \ | |
561 | case TVMISC_##tag: return (rv0->slot == rv1->slot); | |
562 | TVEC_MISCSLOTS(CASE) | |
563 | #undef CASE | |
564 | default: abort(); | |
565 | } | |
566 | } | |
567 | ||
568 | static int tobuf_enum(buf *b, const union tvec_regval *rv, | |
569 | const struct tvec_regdef *rd) | |
570 | { | |
571 | const struct tvec_enuminfo *ei = rd->arg.p; | |
572 | ||
573 | switch (ei->mv) { | |
574 | #define CASE(tag, ty, slot) \ | |
575 | case TVMISC_##tag: return (HANDLE_##tag); | |
576 | #define HANDLE_INT signed_to_buf(b, rv->i) | |
577 | #define HANDLE_UINT unsigned_to_buf(b, rv->u) | |
578 | #define HANDLE_PTR -1 | |
579 | TVEC_MISCSLOTS(CASE) | |
580 | #undef CASE | |
581 | #undef HANDLE_INT | |
582 | #undef HANDLE_UINT | |
583 | #undef HANDLE_PTR | |
584 | default: abort(); | |
585 | } | |
586 | return (0); | |
587 | } | |
588 | ||
589 | static int frombuf_enum(buf *b, union tvec_regval *rv, | |
590 | const struct tvec_regdef *rd) | |
591 | { | |
592 | const struct tvec_enuminfo *ei = rd->arg.p; | |
593 | ||
594 | switch (ei->mv) { | |
595 | #define CASE(tag, ty, slot) \ | |
596 | case TVMISC_##tag: return (HANDLE_##tag); | |
597 | #define HANDLE_INT signed_from_buf(b, &rv->i) | |
598 | #define HANDLE_UINT unsigned_from_buf(b, &rv->u) | |
599 | #define HANDLE_PTR -1 | |
600 | TVEC_MISCSLOTS(CASE) | |
601 | #undef CASE | |
602 | #undef HANDLE_INT | |
603 | #undef HANDLE_UINT | |
604 | #undef HANDLE_PTR | |
605 | default: abort(); | |
606 | } | |
607 | } | |
608 | ||
882a39c1 MW |
609 | static int parse_enum(union tvec_regval *rv, const struct tvec_regdef *rd, |
610 | struct tvec_state *tv) | |
b64eb60f MW |
611 | { |
612 | const struct tvec_enuminfo *ei = rd->arg.p; | |
613 | #define DECLS(tag, ty, slot) \ | |
614 | const struct tvec_##slot##assoc *slot##a; | |
615 | TVEC_MISCSLOTS(DECLS) | |
616 | #undef DECLS | |
617 | dstr d = DSTR_INIT; | |
882a39c1 | 618 | int rc; |
b64eb60f | 619 | |
882a39c1 MW |
620 | if (tvec_readword(tv, &d, ";", "enumeration tag or literal integer")) |
621 | { rc = -1; goto end; } | |
b64eb60f MW |
622 | switch (ei->mv) { |
623 | #define CASE(tag_, ty, slot) \ | |
624 | case TVMISC_##tag_: \ | |
625 | for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \ | |
626 | if (STRCMP(d.buf, ==, slot##a->tag)) \ | |
882a39c1 | 627 | { rv->slot = FETCH_##tag_; goto done; } |
b64eb60f MW |
628 | #define FETCH_INT (ia->i) |
629 | #define FETCH_UINT (ua->u) | |
630 | #define FETCH_PTR ((/*unconst*/ void *)(pa->p)) | |
631 | TVEC_MISCSLOTS(CASE) | |
632 | #undef CASE | |
633 | #undef FETCH_INT | |
634 | #undef FETCH_UINT | |
635 | #undef FETCH_PTR | |
636 | } | |
637 | ||
638 | switch (ei->mv) { | |
639 | #define CASE(tag, ty, slot) \ | |
882a39c1 MW |
640 | case TVMISC_##tag: HANDLE_##tag goto done; |
641 | #define HANDLE_INT \ | |
642 | if (parse_signed(&rv->i, d.buf, ei->u.i.ir, tv)) \ | |
643 | { rc = -1; goto end; } | |
644 | #define HANDLE_UINT \ | |
645 | if (parse_unsigned(&rv->u, d.buf, ei->u.u.ur, tv)) \ | |
646 | { rc = -1; goto end; } | |
647 | #define HANDLE_PTR \ | |
648 | if (STRCMP(d.buf, ==, "#nil")) rv->p = 0; \ | |
649 | else goto tagonly; | |
b64eb60f MW |
650 | TVEC_MISCSLOTS(CASE) |
651 | #undef CASE | |
652 | #undef HANDLE_INT | |
653 | #undef HANDLE_UINT | |
654 | #undef HANDLE_PTR | |
655 | default: tagonly: | |
656 | tvec_error(tv, "unknown `%s' value `%s'", ei->name, d.buf); | |
882a39c1 | 657 | rc = -1; goto end; |
b64eb60f MW |
658 | } |
659 | ||
882a39c1 MW |
660 | done: |
661 | if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } | |
662 | rc = 0; | |
b64eb60f | 663 | end: |
b64eb60f | 664 | dstr_destroy(&d); |
882a39c1 | 665 | return (rc); |
b64eb60f MW |
666 | } |
667 | ||
668 | static void dump_enum(const union tvec_regval *rv, | |
669 | const struct tvec_regdef *rd, | |
670 | struct tvec_state *tv, unsigned style) | |
671 | { | |
672 | const struct tvec_enuminfo *ei = rd->arg.p; | |
673 | #define DECLS(tag, ty, slot) \ | |
674 | const struct tvec_##slot##assoc *slot##a; | |
675 | TVEC_MISCSLOTS(DECLS) | |
676 | #undef DECLS | |
677 | const char *tag; | |
678 | unsigned long u; | |
679 | unsigned f = 0; | |
680 | #define f_known 1u | |
681 | ||
682 | switch (ei->mv) { | |
683 | #define CASE(tag_, ty, slot) \ | |
684 | case TVMISC_##tag_: \ | |
685 | for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \ | |
686 | if (rv->slot == slot##a->slot) \ | |
687 | { tag = slot##a->tag; goto found; } \ | |
688 | break; | |
689 | TVEC_MISCSLOTS(CASE) | |
690 | #undef CASE | |
691 | default: abort(); | |
692 | } | |
693 | goto print_int; | |
694 | ||
695 | found: | |
696 | f |= f_known; | |
697 | tvec_write(tv, "%s", tag); | |
698 | if (style&TVSF_COMPACT) return; | |
699 | tvec_write(tv, " ; = "); | |
700 | ||
701 | print_int: | |
702 | switch (ei->mv) { | |
703 | #define CASE(tag, ty, slot) \ | |
704 | case TVMISC_##tag: HANDLE_##tag break; | |
705 | #define HANDLE_INT tvec_write(tv, "%ld", rv->i); | |
706 | #define HANDLE_UINT tvec_write(tv, "%lu", rv->u); | |
707 | #define HANDLE_PTR if (!rv->p) tvec_write(tv, "#nil"); \ | |
708 | else tvec_write(tv, "#<%s %p>", ei->name, rv->p); | |
709 | TVEC_MISCSLOTS(CASE) | |
710 | #undef CASE | |
711 | #undef HANDLE_INT | |
712 | #undef HANDLE_UINT | |
713 | #undef HANDLE_PTR | |
714 | } | |
715 | ||
716 | switch (ei->mv) { | |
717 | case TVMISC_INT: | |
718 | if (!(f&f_known)) tvec_write(tv, " ;"); | |
719 | if (rv->i >= 0) u = rv->i; | |
720 | else u = -(unsigned long)rv->i; | |
721 | tvec_write(tv, " = %s0x%0*lx", rv->i < 0 ? "-" : "", hex_width(u), u); | |
722 | break; | |
723 | case TVMISC_UINT: | |
724 | if (!(f&f_known)) tvec_write(tv, " ;"); | |
725 | tvec_write(tv, " = 0x%0*lx", hex_width(rv->u), rv->u); | |
726 | break; | |
727 | } | |
728 | } | |
729 | ||
730 | const struct tvec_regty tvty_enum = { | |
731 | init_enum, release_int, eq_enum, measure_int, | |
732 | tobuf_enum, frombuf_enum, | |
733 | parse_enum, dump_enum | |
734 | }; | |
735 | ||
736 | #define DEFCLAIM(tag, ty, slot) \ | |
737 | int tvec_claimeq_##slot##enum(struct tvec_state *tv, \ | |
738 | const struct tvec_enuminfo *ei, \ | |
739 | ty e0, ty e1, \ | |
740 | const char *file, unsigned lno, \ | |
741 | const char *expr) \ | |
742 | { \ | |
743 | union tvec_misc arg; \ | |
744 | \ | |
745 | assert(ei->mv == TVMISC_##tag); \ | |
746 | arg.p = ei; \ | |
747 | tv->in[0].v.slot = GET_##tag(e0); \ | |
748 | tv->out[0].v.slot = GET_##tag(e1); \ | |
749 | return (tvec_claimeq(tv, &tvty_enum, &arg, file, lno, expr)); \ | |
750 | } | |
751 | #define GET_INT(e) (e) | |
752 | #define GET_UINT(e) (e) | |
753 | #define GET_PTR(e) ((/*unconst*/ void *)(e)) | |
754 | TVEC_MISCSLOTS(DEFCLAIM) | |
755 | #undef DEFCLAIM | |
756 | #undef GET_INT | |
757 | #undef GET_UINT | |
758 | #undef GET_PTR | |
759 | ||
760 | /*----- Flag types --------------------------------------------------------*/ | |
761 | ||
882a39c1 MW |
762 | static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd, |
763 | struct tvec_state *tv) | |
b64eb60f MW |
764 | { |
765 | const struct tvec_flaginfo *fi = rd->arg.p; | |
766 | const struct tvec_flag *f; | |
767 | unsigned long m = 0, v = 0, t; | |
768 | dstr d = DSTR_INIT; | |
882a39c1 | 769 | int ch, rc; |
b64eb60f MW |
770 | |
771 | for (;;) { | |
882a39c1 MW |
772 | DRESET(&d); |
773 | if (tvec_readword(tv, &d, "|;", "flag name or integer")) | |
774 | { rc = -1; goto end; } | |
b64eb60f MW |
775 | |
776 | for (f = fi->fv; f->tag; f++) | |
777 | if (STRCMP(f->tag, ==, d.buf)) { | |
882a39c1 MW |
778 | if (m&f->m) |
779 | { tvec_error(tv, "colliding flag setting"); rc = -1; goto end; } | |
780 | else | |
781 | { m |= f->m; v |= f->v; goto next; } | |
b64eb60f MW |
782 | } |
783 | ||
882a39c1 MW |
784 | if (parse_unsigned(&t, d.buf, fi->range, tv)) { rc = -1; goto end; } |
785 | v |= t; | |
b64eb60f MW |
786 | next: |
787 | if (tvec_nexttoken(tv)) break; | |
882a39c1 MW |
788 | ch = getc(tv->fp); |
789 | if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; } | |
790 | if (tvec_nexttoken(tv)) | |
791 | { tvec_syntax(tv, '\n', "flag name or integer"); rc = -1; goto end; } | |
b64eb60f MW |
792 | } |
793 | rv->u = v; | |
882a39c1 MW |
794 | rc = 0; |
795 | end: | |
796 | dstr_destroy(&d); | |
797 | return (rc); | |
b64eb60f MW |
798 | } |
799 | ||
800 | static void dump_flags(const union tvec_regval *rv, | |
801 | const struct tvec_regdef *rd, | |
802 | struct tvec_state *tv, unsigned style) | |
803 | { | |
804 | const struct tvec_flaginfo *fi = rd->arg.p; | |
805 | const struct tvec_flag *f; | |
806 | unsigned long m = ~(unsigned long)0, v = rv->u; | |
807 | const char *sep; | |
808 | ||
809 | for (f = fi->fv, sep = ""; f->tag; f++) | |
810 | if ((m&f->m) && (v&f->m) == f->v) { | |
811 | tvec_write(tv, "%s%s", sep, f->tag); m &= ~f->m; | |
812 | sep = style&TVSF_COMPACT ? "|" : " | "; | |
813 | } | |
814 | ||
815 | if (v&m) tvec_write(tv, "%s0x%0*lx", sep, hex_width(v), v&m); | |
816 | ||
817 | if (!(style&TVSF_COMPACT)) | |
818 | tvec_write(tv, " ; = 0x%0*lx", hex_width(rv->u), rv->u); | |
819 | } | |
820 | ||
821 | const struct tvec_regty tvty_flags = { | |
822 | init_uint, release_int, eq_uint, measure_int, | |
823 | tobuf_uint, frombuf_uint, | |
824 | parse_flags, dump_flags | |
825 | }; | |
826 | ||
827 | int tvec_claimeq_flags(struct tvec_state *tv, | |
828 | const struct tvec_flaginfo *fi, | |
829 | unsigned long f0, unsigned long f1, | |
830 | const char *file, unsigned lno, const char *expr) | |
831 | { | |
832 | union tvec_misc arg; | |
833 | ||
834 | arg.p = fi; tv->in[0].v.u = f0; tv->out[0].v.u = f1; | |
835 | return (tvec_claimeq(tv, &tvty_flags, &arg, file, lno, expr)); | |
836 | } | |
837 | ||
838 | /*----- Text and byte strings ---------------------------------------------*/ | |
839 | ||
840 | void tvec_allocstring(union tvec_regval *rv, size_t sz) | |
841 | { | |
842 | if (rv->str.sz < sz) { xfree(rv->str.p); rv->str.p = xmalloc(sz); } | |
843 | rv->str.sz = sz; | |
844 | } | |
845 | ||
846 | void tvec_allocbytes(union tvec_regval *rv, size_t sz) | |
847 | { | |
848 | if (rv->bytes.sz < sz) { xfree(rv->bytes.p); rv->bytes.p = xmalloc(sz); } | |
849 | rv->bytes.sz = sz; | |
850 | } | |
851 | ||
852 | static void init_string(union tvec_regval *rv, const struct tvec_regdef *rd) | |
853 | { rv->str.p = 0; rv->str.sz = 0; } | |
854 | ||
855 | static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd) | |
856 | { rv->bytes.p = 0; rv->bytes.sz = 0; } | |
857 | ||
858 | static void release_string(union tvec_regval *rv, | |
859 | const struct tvec_regdef *rd) | |
860 | { xfree(rv->str.p); } | |
861 | ||
862 | static void release_bytes(union tvec_regval *rv, | |
863 | const struct tvec_regdef *rd) | |
864 | { xfree(rv->bytes.p); } | |
865 | ||
866 | static int eq_string(const union tvec_regval *rv0, | |
867 | const union tvec_regval *rv1, | |
868 | const struct tvec_regdef *rd) | |
869 | { | |
870 | return (rv0->str.sz == rv1->str.sz && | |
871 | (!rv0->bytes.sz || | |
872 | MEMCMP(rv0->str.p, ==, rv1->str.p, rv1->str.sz))); | |
873 | } | |
874 | ||
875 | static int eq_bytes(const union tvec_regval *rv0, | |
876 | const union tvec_regval *rv1, | |
877 | const struct tvec_regdef *rd) | |
878 | { | |
879 | return (rv0->bytes.sz == rv1->bytes.sz && | |
880 | (!rv0->bytes.sz || | |
881 | MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz))); | |
882 | } | |
883 | ||
884 | static size_t measure_string(const union tvec_regval *rv, | |
885 | const struct tvec_regdef *rd) | |
886 | { return (rv->str.sz + 4); } | |
887 | ||
888 | static size_t measure_bytes(const union tvec_regval *rv, | |
889 | const struct tvec_regdef *rd) | |
890 | { return (rv->bytes.sz + 4); } | |
891 | ||
892 | static int tobuf_string(buf *b, const union tvec_regval *rv, | |
893 | const struct tvec_regdef *rd) | |
894 | { return (buf_putmem32l(b, rv->str.p, rv->str.sz)); } | |
895 | ||
896 | static int tobuf_bytes(buf *b, const union tvec_regval *rv, | |
897 | const struct tvec_regdef *rd) | |
898 | { return (buf_putmem32l(b, rv->bytes.p, rv->bytes.sz)); } | |
899 | ||
900 | static int frombuf_string(buf *b, union tvec_regval *rv, | |
901 | const struct tvec_regdef *rd) | |
902 | { | |
903 | const void *p; | |
904 | size_t sz; | |
905 | ||
906 | p = buf_getmem32l(b, &sz); if (!p) return (-1); | |
907 | tvec_allocstring(rv, sz); memcpy(rv->str.p, p, sz); | |
908 | return (0); | |
909 | } | |
910 | ||
911 | static int frombuf_bytes(buf *b, union tvec_regval *rv, | |
912 | const struct tvec_regdef *rd) | |
913 | { | |
914 | const void *p; | |
915 | size_t sz; | |
916 | ||
917 | p = buf_getmem32l(b, &sz); if (!p) return (-1); | |
918 | tvec_allocbytes(rv, sz); memcpy(rv->bytes.p, p, sz); | |
919 | return (0); | |
920 | } | |
921 | ||
882a39c1 MW |
922 | static int check_string_length(size_t sz, const struct tvec_urange *ur, |
923 | struct tvec_state *tv) | |
b64eb60f MW |
924 | { |
925 | if (ur && (ur->min > sz || sz > ur->max)) | |
882a39c1 MW |
926 | return (tvec_error(tv, |
927 | "invalid string length %lu; must be in [%lu..%lu]", | |
928 | (unsigned long)sz, ur->min, ur->max)); | |
929 | return (0); | |
b64eb60f MW |
930 | } |
931 | ||
882a39c1 MW |
932 | static int parse_string(union tvec_regval *rv, const struct tvec_regdef *rd, |
933 | struct tvec_state *tv) | |
b64eb60f MW |
934 | { |
935 | void *p = rv->str.p; | |
936 | ||
882a39c1 MW |
937 | if (read_compound_string(&p, &rv->str.sz, TVCODE_BARE, tv)) return (-1); |
938 | rv->str.p = p; | |
939 | if (check_string_length(rv->str.sz, rd->arg.p, tv)) return (-1); | |
940 | return (0); | |
b64eb60f MW |
941 | } |
942 | ||
882a39c1 MW |
943 | static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd, |
944 | struct tvec_state *tv) | |
b64eb60f MW |
945 | { |
946 | void *p = rv->bytes.p; | |
947 | ||
882a39c1 MW |
948 | if (read_compound_string(&p, &rv->bytes.sz, TVCODE_HEX, tv)) return (-1); |
949 | rv->bytes.p = p; | |
950 | if (check_string_length(rv->bytes.sz, rd->arg.p, tv)) return (-1); | |
951 | return (0); | |
b64eb60f MW |
952 | } |
953 | ||
954 | static void dump_string(const union tvec_regval *rv, | |
955 | const struct tvec_regdef *rd, | |
956 | struct tvec_state *tv, unsigned style) | |
957 | { | |
958 | const unsigned char *p, *q, *l; | |
959 | int ch; | |
960 | unsigned f = 0; | |
961 | #define f_nonword 1u | |
962 | #define f_newline 2u | |
963 | ||
964 | if (!rv->str.sz) { tvec_write(tv, "\"\""); return; } | |
965 | ||
966 | p = (const unsigned char *)rv->str.p; l = p + rv->str.sz; | |
967 | if (*p == '!' || *p == ';' || *p == '"' || *p == '\'') goto quote; | |
968 | for (q = p; q < l; q++) | |
969 | if (*q == '\n' && q != l - 1) f |= f_newline; | |
970 | else if (!*q || !isgraph(*q) || *q == '\\') f |= f_nonword; | |
971 | if (f&f_newline) { tvec_write(tv, "\n\t"); goto quote; } | |
972 | else if (f&f_nonword) goto quote; | |
973 | tv->output->ops->write(tv->output, (const char *)p, rv->str.sz); return; | |
974 | ||
975 | quote: | |
976 | tvec_write(tv, "\""); | |
977 | for (q = p; q < l; q++) | |
978 | switch (*q) { | |
979 | case '"': case '\\': ch = *q; goto escape; | |
980 | case '\a': ch = 'a'; goto escape; | |
981 | case '\b': ch = 'b'; goto escape; | |
982 | case '\x1b': ch = 'e'; goto escape; | |
983 | case '\f': ch = 'f'; goto escape; | |
984 | case '\r': ch = 'r'; goto escape; | |
985 | case '\t': ch = 't'; goto escape; | |
986 | case '\v': ch = 'v'; goto escape; | |
987 | escape: | |
988 | if (p < q) | |
989 | tv->output->ops->write(tv->output, (const char *)p, q - p); | |
990 | tvec_write(tv, "\\%c", ch); p = q + 1; | |
991 | break; | |
992 | ||
993 | case '\n': | |
994 | if (p < q) | |
995 | tv->output->ops->write(tv->output, (const char *)p, q - p); | |
996 | tvec_write(tv, "\\n"); p = q + 1; | |
997 | if (!(style&TVSF_COMPACT) && q < l) tvec_write(tv, "\"\t\""); | |
998 | break; | |
999 | ||
1000 | default: | |
1001 | if (isprint(*q)) break; | |
1002 | if (p < q) | |
1003 | tv->output->ops->write(tv->output, (const char *)p, q - p); | |
1004 | tvec_write(tv, "\\x{%0*x}", hex_width(UCHAR_MAX), *q); p = q + 1; | |
1005 | break; | |
1006 | } | |
1007 | if (p < q) tv->output->ops->write(tv->output, (const char *)p, q - p); | |
1008 | tvec_write(tv, "\""); | |
1009 | ||
1010 | #undef f_nonword | |
1011 | #undef f_newline | |
1012 | } | |
1013 | ||
1014 | static void dump_bytes(const union tvec_regval *rv, | |
1015 | const struct tvec_regdef *rd, | |
1016 | struct tvec_state *tv, unsigned style) | |
1017 | { | |
1018 | const unsigned char *p = rv->bytes.p, *l = p + rv->bytes.sz; | |
1019 | size_t off, sz = rv->bytes.sz; | |
1020 | unsigned i, n; | |
1021 | int wd; | |
1022 | ||
1023 | if (!sz) { | |
1024 | tvec_write(tv, style&TVSF_COMPACT ? "\"\"" : "\"\" ; empty"); | |
1025 | return; | |
1026 | } | |
1027 | ||
1028 | if (style&TVSF_COMPACT) { | |
1029 | while (p < l) tvec_write(tv, "%02x", *p++); | |
1030 | return; | |
1031 | } | |
1032 | ||
1033 | if (sz > 16) tvec_write(tv, "\n\t"); | |
1034 | ||
1035 | off = 0; wd = hex_width(sz); | |
1036 | while (p < l) { | |
1037 | if (l - p < 16) n = l - p; | |
1038 | else n = 16; | |
1039 | ||
1040 | for (i = 0; i < 16; i++) { | |
1041 | if (i < n) tvec_write(tv, "%02x", p[i]); | |
1042 | else tvec_write(tv, " "); | |
1043 | if (i%4 == 3) tvec_write(tv, " "); | |
1044 | } | |
1045 | tvec_write(tv, " ; "); | |
1046 | if (sz > 16) tvec_write(tv, "[%0*lx] ", wd, (unsigned long)off); | |
1047 | for (i = 0; i < n; i++) | |
1048 | tvec_write(tv, "%c", isprint(p[i]) ? p[i] : '.'); | |
1049 | p += n; off += n; | |
1050 | if (p < l) tvec_write(tv, "\n\t"); | |
1051 | } | |
1052 | } | |
1053 | ||
1054 | const struct tvec_regty tvty_string = { | |
1055 | init_string, release_string, eq_string, measure_string, | |
1056 | tobuf_string, frombuf_string, | |
1057 | parse_string, dump_string | |
1058 | }; | |
1059 | ||
1060 | const struct tvec_regty tvty_bytes = { | |
1061 | init_bytes, release_bytes, eq_bytes, measure_bytes, | |
1062 | tobuf_bytes, frombuf_bytes, | |
1063 | parse_bytes, dump_bytes | |
1064 | }; | |
1065 | ||
1066 | int tvec_claimeq_string(struct tvec_state *tv, | |
1067 | const char *p0, size_t sz0, | |
1068 | const char *p1, size_t sz1, | |
1069 | const char *file, unsigned lno, const char *expr) | |
1070 | { | |
1071 | tv->in[0].v.str.p = (/*unconst*/ char *)p0; tv->in[0].v.str.sz = sz0; | |
1072 | tv->out[0].v.str.p =(/*unconst*/ char *) p1; tv->out[0].v.str.sz = sz1; | |
1073 | return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr)); | |
1074 | } | |
1075 | ||
1076 | int tvec_claimeq_strz(struct tvec_state *tv, | |
1077 | const char *p0, const char *p1, | |
1078 | const char *file, unsigned lno, const char *expr) | |
1079 | { | |
1080 | tv->in[0].v.str.p = (/*unconst*/ char *)p0; | |
1081 | tv->in[0].v.str.sz = strlen(p0); | |
1082 | tv->out[0].v.str.p = (/*unconst*/ char *)p1; | |
1083 | tv->out[0].v.str.sz = strlen(p1); | |
1084 | return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr)); | |
1085 | } | |
1086 | ||
1087 | int tvec_claimeq_bytes(struct tvec_state *tv, | |
1088 | const void *p0, size_t sz0, | |
1089 | const void *p1, size_t sz1, | |
1090 | const char *file, unsigned lno, const char *expr) | |
1091 | { | |
1092 | tv->in[0].v.bytes.p = (/*unconst*/ void *)p0; | |
1093 | tv->in[0].v.bytes.sz = sz0; | |
1094 | tv->out[0].v.bytes.p = (/*unconst*/ void *)p1; | |
1095 | tv->out[0].v.bytes.sz = sz1; | |
1096 | return (tvec_claimeq(tv, &tvty_bytes, 0, file, lno, expr)); | |
1097 | } | |
1098 | ||
1099 | /*----- Buffer type -------------------------------------------------------*/ | |
1100 | ||
1101 | static int eq_buffer(const union tvec_regval *rv0, | |
1102 | const union tvec_regval *rv1, | |
1103 | const struct tvec_regdef *rd) | |
1104 | { return (rv0->bytes.sz == rv1->bytes.sz); } | |
1105 | ||
1106 | static int tobuf_buffer(buf *b, const union tvec_regval *rv, | |
1107 | const struct tvec_regdef *rd) | |
1108 | { return (unsigned_to_buf(b, rv->bytes.sz)); } | |
1109 | ||
1110 | static int frombuf_buffer(buf *b, union tvec_regval *rv, | |
1111 | const struct tvec_regdef *rd) | |
1112 | { | |
1113 | unsigned long u; | |
1114 | ||
1115 | if (unsigned_from_buf(b, &u)) return (-1); | |
1116 | if (u > (size_t)-1) return (-1); | |
1117 | tvec_allocbytes(rv, u); memset(rv->bytes.p, '!', u); | |
1118 | return (0); | |
1119 | } | |
1120 | ||
1121 | static const char units[] = "kMGTPEZY"; | |
1122 | ||
882a39c1 MW |
1123 | static int parse_buffer(union tvec_regval *rv, |
1124 | const struct tvec_regdef *rd, | |
1125 | struct tvec_state *tv) | |
b64eb60f MW |
1126 | { |
1127 | dstr d = DSTR_INIT; | |
1128 | char *q; const char *unit; | |
1129 | int olderr; | |
1130 | size_t pos; | |
1131 | unsigned long u, t; | |
882a39c1 | 1132 | int rc; |
b64eb60f MW |
1133 | unsigned f = 0; |
1134 | #define f_range 1u | |
1135 | ||
882a39c1 | 1136 | if (tvec_readword(tv, &d, ";", "buffer length")) { rc = -1; goto end; } |
b64eb60f MW |
1137 | olderr = errno; errno = 0; |
1138 | u = strtoul(d.buf, &q, 0); | |
1139 | if (errno) goto bad; | |
1140 | errno = olderr; | |
1141 | if (!*q) { | |
1142 | tvec_skipspc(tv); pos = d.len; | |
1143 | if (!tvec_readword(tv, &d, ";", 0)) pos++; | |
1144 | q = d.buf + pos; | |
1145 | } | |
1146 | ||
1147 | if (u > (size_t)-1) goto rangerr; | |
1148 | for (t = u, unit = units; *unit; unit++) { | |
1149 | if (t > (size_t)-1/1024) f |= f_range; | |
1150 | else t *= 1024; | |
1151 | if (*q == *unit && (!q[1] || q[1] == 'B')) { | |
1152 | if (f&f_range) goto rangerr; | |
1153 | u = t; q += 2; break; | |
1154 | } | |
1155 | } | |
1156 | if (*q && *q != ';') goto bad; | |
882a39c1 | 1157 | if (check_string_length(u, rd->arg.p, tv)) { rc = -1; goto end; } |
b64eb60f | 1158 | |
882a39c1 MW |
1159 | if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } |
1160 | tvec_allocbytes(rv, u); memset(rv->bytes.p, '?', u); | |
1161 | rc = 0; | |
1162 | end: | |
1163 | DDESTROY(&d); return (rc); | |
b64eb60f MW |
1164 | |
1165 | bad: | |
1166 | tvec_error(tv, "invalid buffer length `%s'", d.buf); | |
882a39c1 | 1167 | rc = -1; goto end; |
b64eb60f MW |
1168 | |
1169 | rangerr: | |
1170 | tvec_error(tv, "buffer length `%s' out of range", d.buf); | |
882a39c1 | 1171 | rc = -1; goto end; |
b64eb60f MW |
1172 | |
1173 | #undef f_range | |
1174 | } | |
1175 | ||
1176 | static void dump_buffer(const union tvec_regval *rv, | |
1177 | const struct tvec_regdef *rd, | |
1178 | struct tvec_state *tv, unsigned style) | |
1179 | { | |
1180 | const char *unit; | |
1181 | unsigned long u = rv->bytes.sz; | |
1182 | ||
1183 | if (!u || u%1024) | |
1184 | tvec_write(tv, "%lu B", u); | |
1185 | else { | |
1186 | for (unit = units, u /= 1024; !(u%1024) && unit[1]; u /= 1024, unit++); | |
1187 | tvec_write(tv, "%lu %cB", u, *unit); | |
1188 | } | |
1189 | } | |
1190 | ||
1191 | const struct tvec_regty tvty_buffer = { | |
1192 | init_bytes, release_bytes, eq_buffer, measure_int, | |
1193 | tobuf_buffer, frombuf_buffer, | |
1194 | parse_buffer, dump_buffer | |
1195 | }; | |
1196 | ||
1197 | /*----- That's all, folks -------------------------------------------------*/ |