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> | |
e63124bc | 33 | #include <float.h> |
b64eb60f | 34 | #include <limits.h> |
e63124bc | 35 | #include <math.h> |
b64eb60f MW |
36 | #include <stdio.h> |
37 | #include <string.h> | |
38 | ||
39 | #include "buf.h" | |
40 | #include "codec.h" | |
41 | # include "base32.h" | |
42 | # include "base64.h" | |
43 | # include "hex.h" | |
44 | #include "dstr.h" | |
45 | #include "tvec.h" | |
46 | ||
47 | /*----- Preliminary utilities ---------------------------------------------*/ | |
48 | ||
e63124bc MW |
49 | #ifdef isnan |
50 | # define NANP(x) isnan(x) | |
51 | #else | |
52 | # define NANP(x) (!((x) == (x))) | |
53 | #endif | |
54 | ||
55 | #ifdef isinf | |
56 | # define INFP(x) isinf(x) | |
57 | #else | |
58 | # define INFP(x) ((x) > DBL_MAX || (x) < DBL_MIN) | |
59 | #endif | |
60 | ||
b64eb60f MW |
61 | static int signed_to_buf(buf *b, long i) |
62 | { | |
63 | kludge64 k; | |
64 | unsigned long u; | |
65 | ||
66 | u = i; | |
67 | if (i >= 0) ASSIGN64(k, u); | |
68 | else { ASSIGN64(k, ~u); CPL64(k, k); } | |
69 | return (buf_putk64l(b, k)); | |
70 | } | |
71 | ||
72 | static int signed_from_buf(buf *b, long *i_out) | |
73 | { | |
74 | kludge64 k, lmax, not_lmin; | |
75 | ||
76 | ASSIGN64(lmax, LONG_MAX); ASSIGN64(not_lmin, ~(unsigned long)LONG_MIN); | |
77 | if (buf_getk64l(b, &k)) return (-1); | |
78 | if (CMP64(k, <=, lmax)) *i_out = (long)GET64(unsigned long, k); | |
79 | else { | |
80 | CPL64(k, k); | |
81 | if (CMP64(k, <=, not_lmin)) *i_out = -(long)GET64(unsigned long, k) - 1; | |
82 | else return (-1); | |
83 | } | |
84 | return (0); | |
85 | } | |
86 | ||
87 | static int unsigned_to_buf(buf *b, unsigned long u) | |
88 | { kludge64 k; ASSIGN64(k, u); return (buf_putk64l(b, k)); } | |
89 | ||
90 | static int unsigned_from_buf(buf *b, unsigned long *u_out) | |
91 | { | |
92 | kludge64 k, ulmax; | |
93 | ||
94 | ASSIGN64(ulmax, ULONG_MAX); | |
95 | if (buf_getk64l(b, &k)) return (-1); | |
96 | if (CMP64(k, >, ulmax)) return (-1); | |
97 | *u_out = GET64(unsigned long, k); return (0); | |
98 | } | |
99 | ||
100 | static int hex_width(unsigned long u) | |
101 | { | |
102 | int wd; | |
103 | unsigned long t; | |
104 | ||
105 | for (t = u >> 4, wd = 4; t >>= wd, wd *= 2, t; ); | |
106 | return (wd/4); | |
107 | } | |
108 | ||
882a39c1 MW |
109 | static int check_signed_range(long i, |
110 | const struct tvec_irange *ir, | |
111 | struct tvec_state *tv) | |
b64eb60f | 112 | { |
882a39c1 | 113 | if (ir && (ir->min > i || i > ir->max)) { |
b64eb60f MW |
114 | tvec_error(tv, "integer %ld out of range (must be in [%ld .. %ld])", |
115 | i, ir->min, ir->max); | |
882a39c1 MW |
116 | return (-1); |
117 | } | |
118 | return (0); | |
b64eb60f MW |
119 | } |
120 | ||
882a39c1 MW |
121 | static int check_unsigned_range(unsigned long u, |
122 | const struct tvec_urange *ur, | |
123 | struct tvec_state *tv) | |
b64eb60f | 124 | { |
882a39c1 | 125 | if (ur && (ur->min > u || u > ur->max)) { |
b64eb60f MW |
126 | tvec_error(tv, "integer %lu out of range (must be in [%lu .. %lu])", |
127 | u, ur->min, ur->max); | |
882a39c1 MW |
128 | return (-1); |
129 | } | |
130 | return (0); | |
b64eb60f MW |
131 | } |
132 | ||
882a39c1 MW |
133 | static int parse_signed(long *i_out, const char *p, |
134 | const struct tvec_irange *ir, | |
135 | struct tvec_state *tv) | |
b64eb60f MW |
136 | { |
137 | char *q; const char *pp; | |
138 | int olderr; | |
139 | long i; | |
140 | ||
141 | olderr = errno; errno = 0; | |
142 | pp = p; if (*pp == '-' || *pp == '+') pp++; | |
e63124bc MW |
143 | if (!ISDIGIT(*pp)) |
144 | return (tvec_syntax(tv, *pp ? *pp : fgetc(tv->fp), "signed integer")); | |
b64eb60f | 145 | i = strtol(p, &q, 0); |
882a39c1 | 146 | if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line")); |
e63124bc MW |
147 | if (errno) |
148 | return (tvec_error(tv, "invalid integer `%s': %s", p, strerror(errno))); | |
149 | if (check_signed_range(i, ir, tv)) return (-1); | |
b64eb60f | 150 | errno = olderr; *i_out = i; |
882a39c1 | 151 | return (0); |
b64eb60f MW |
152 | } |
153 | ||
882a39c1 MW |
154 | static int parse_unsigned(unsigned long *u_out, const char *p, |
155 | const struct tvec_urange *ur, | |
156 | struct tvec_state *tv) | |
b64eb60f MW |
157 | { |
158 | char *q; | |
159 | int olderr; | |
160 | unsigned long u; | |
161 | ||
162 | olderr = errno; errno = 0; | |
882a39c1 | 163 | if (!ISDIGIT(*p)) return (tvec_syntax(tv, *p, "unsigned integer")); |
b64eb60f | 164 | u = strtoul(p, &q, 0); |
882a39c1 | 165 | if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line")); |
e63124bc MW |
166 | if (errno) |
167 | return (tvec_error(tv, "invalid integer `%s': %s", p, strerror(errno))); | |
168 | if (check_unsigned_range(u, ur, tv)) return (-1); | |
b64eb60f | 169 | errno = olderr; *u_out = u; |
882a39c1 | 170 | return (0); |
b64eb60f MW |
171 | } |
172 | ||
e63124bc MW |
173 | static void format_floating(const struct gprintf_ops *gops, void *go, |
174 | double x) | |
175 | { | |
176 | int prec; | |
177 | ||
178 | if (NANP(x)) | |
179 | gprintf(gops, go, "#nan"); | |
180 | else if (INFP(x)) | |
181 | gprintf(gops, go, x > 0 ? "#+inf" : "#-inf"); | |
182 | else { | |
183 | /* Ugh. C doesn't provide any function for just printing a | |
184 | * floating-point number /correctly/, i.e., so that you can read the | |
185 | * result back and recover the number you first thought of. There are | |
186 | * complicated algorithms published for doing this, but I really don't | |
187 | * want to get into that here. So we have this. | |
188 | * | |
189 | * The sign doesn't cause significant difficulty so we're going to ignore | |
190 | * it for now. So suppose we're given a number %$x = f b^e$%, in | |
191 | * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with | |
192 | * %$0 \le f < 1$%. We're going to convert it into the nearest integer | |
193 | * of the form %$X = F B^E$%, with similar conditions, only with the | |
194 | * additional requirement that %$X$% is normalized, i.e., that %$X = 0$% | |
195 | * or %$F \ge B^{-N}$%. | |
196 | * | |
197 | * We're rounding to the nearest such %$X$%. If there is to be ambiguity | |
198 | * in the conversion, then some %$x = f b^e$% and the next smallest | |
199 | * representable number %$x' = x + b^{e-n}$% must both map to the same | |
200 | * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than | |
201 | * any other number representable in the target system. The nest larger | |
202 | * number is %$X' = X + B^{E-N}$%; the next smaller number will normally | |
203 | * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number | |
204 | * is actually %$X - B^{E-N-1}$%. We ignore this latter possibility in | |
205 | * the pursuit of a conservative estimate (though actually it doesn't | |
206 | * matter). | |
207 | * | |
208 | * If both %$x$% and %$x'$% map to %$X$% then we must have | |
209 | * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%; | |
210 | * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%, | |
211 | * and secondly %$b^{e-n} \le B^{E-N}$%. Since these inequalities are in | |
212 | * opposite senses, we can divide, giving | |
213 | * | |
214 | * %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% , | |
215 | * | |
216 | * whence | |
217 | * | |
218 | * %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% . | |
219 | * | |
220 | * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be | |
221 | * possible, it must be the case that | |
222 | * | |
223 | * %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% . | |
224 | * | |
225 | * Then rearrange and take logarithms, obtaining | |
226 | * | |
227 | * %$(N - 1) \log B < n \log b$% , | |
228 | * | |
229 | * and so | |
230 | * | |
231 | * %$N < n \log b/\log B + 1$% . | |
232 | * | |
233 | * Recall that this is a necessary condition for a collision to occur; we | |
234 | * are therefore safe whenever | |
235 | * | |
236 | * %$N \ge n \log b/\log B + 1$% ; | |
237 | * | |
238 | * so, taking ceilings, | |
239 | * | |
240 | * %$N \ge \lceil n \log b/\log B \rceil + 1$% . | |
241 | * | |
242 | * So that's why we have this. | |
243 | * | |
244 | * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that | |
245 | * we can calculate this without ending up on the wrong side of an | |
246 | * integer boundary. | |
247 | * | |
248 | * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only | |
249 | * as a constant. Except that modern compilers are more than clever | |
250 | * enough to work out that this is a constant anyway. | |
251 | * | |
252 | * This is sometimes an overestimate: we'll print out meaningless digits | |
253 | * that don't represent anything we actually know about the number in | |
254 | * question. To fix that, we'd need a complicated algorithm like Steele | |
255 | * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm | |
256 | * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to | |
257 | * something else in difficult situations). | |
258 | */ | |
259 | ||
260 | prec = ceil(DBL_MANT_DIG*log(FLT_RADIX)/log(10)) + 1; | |
261 | gprintf(gops, go, "%.*g", prec, x); | |
262 | } | |
263 | } | |
264 | ||
265 | static int eqish_floating_p(double x, double y, | |
266 | const struct tvec_floatinfo *fi) | |
267 | { | |
268 | double xx, yy, t; | |
269 | ||
270 | if (NANP(x)) return (NANP(y)); else if (NANP(y)) return (0); | |
271 | if (INFP(x)) return (x == y); else if (INFP(y)) return (0); | |
272 | ||
273 | switch (fi ? fi->f&TVFF_EQMASK : TVFF_EXACT) { | |
274 | case TVFF_EXACT: | |
275 | return (x == y); | |
276 | case TVFF_ABSDELTA: | |
277 | t = x - y; if (t < 0) t = -t; return (t < fi->delta); | |
278 | case TVFF_RELDELTA: | |
279 | xx = x >= 0 ? x : -x; yy = y >= 0 ? y : -y; | |
280 | if (xx < yy) { t = x; x = y; y = t; } | |
281 | return (1.0 - y/x < fi->delta); | |
282 | default: | |
283 | abort(); | |
284 | } | |
285 | } | |
286 | ||
287 | static int parse_floating(double *x_out, const char *p, | |
288 | const struct tvec_floatinfo *fi, | |
289 | struct tvec_state *tv) | |
290 | { | |
291 | const char *pp; char *q; | |
292 | dstr d = DSTR_INIT; | |
293 | double x; | |
294 | int olderr, rc; | |
295 | ||
296 | if (STRCMP(p, ==, "#nan")) { | |
297 | #ifdef NAN | |
298 | x = NAN; rc = 0; | |
299 | #else | |
300 | tvec_error(tv, "NaN not supported on this system"); | |
301 | rc = -1; goto end; | |
302 | #endif | |
303 | } else if (STRCMP(p, ==, "#inf") || | |
304 | STRCMP(p, ==, "#+inf") || | |
305 | STRCMP(p, ==, "+#inf")) { | |
306 | #ifdef NAN | |
307 | x = INFINITY; rc = 0; | |
308 | #else | |
309 | tvec_error(tv, "infinity not supported on this system"); | |
310 | rc = -1; goto end; | |
311 | #endif | |
312 | } else if (STRCMP(p, ==, "#-inf") || | |
313 | STRCMP(p, ==, "-#inf")) { | |
314 | #ifdef NAN | |
315 | x = -INFINITY; rc = 0; | |
316 | #else | |
317 | tvec_error(tv, "infinity not supported on this system"); | |
318 | rc = -1; goto end; | |
319 | #endif | |
320 | } else { | |
321 | pp = p; | |
322 | if (*pp == '+' || *pp == '-') pp++; | |
323 | if (*pp == '.') pp++; | |
324 | if (!ISDIGIT(*pp)) { | |
325 | tvec_syntax(tv, *pp ? *pp : fgetc(tv->fp), "floating-point number"); | |
326 | rc = -1; goto end; | |
327 | } | |
328 | olderr = errno; errno = 0; | |
329 | x = strtod(p, &q); | |
330 | if (*q) { | |
331 | tvec_syntax(tv, *q, "end-of-line"); | |
332 | rc = -1; goto end; | |
333 | } | |
334 | if (errno && (errno != ERANGE || (x > 0 ? -x : x) == HUGE_VAL)) { | |
335 | tvec_error(tv, "invalid floating-point number `%s': %s", | |
336 | p, strerror(errno)); | |
337 | rc = -1; goto end; | |
338 | } | |
339 | errno = olderr; | |
340 | } | |
341 | ||
342 | if (NANP(x) && fi && !(fi->f&TVFF_NANOK)) { | |
343 | tvec_error(tv, "#nan not allowed here"); | |
344 | rc = -1; goto end; | |
345 | } | |
346 | if (fi && ((!(fi->f&TVFF_NOMIN) && x < fi->min) || | |
347 | (!(fi->f&TVFF_NOMAX) && x > fi->max))) { | |
348 | dstr_puts(&d, "floating-point number "); | |
349 | format_floating(&dstr_printops, &d, x); | |
350 | dstr_puts(&d, " out of range (must be in "); | |
351 | if (fi->f&TVFF_NOMIN) | |
352 | dstr_puts(&d, "(#-inf"); | |
353 | else | |
354 | { dstr_putc(&d, '['); format_floating(&dstr_printops, &d, fi->min); } | |
355 | dstr_puts(&d, " .. "); | |
356 | if (fi->f&TVFF_NOMAX) | |
357 | dstr_puts(&d, "#+inf)"); | |
358 | else | |
359 | { format_floating(&dstr_printops, &d, fi->max); dstr_putc(&d, ']'); } | |
360 | dstr_putc(&d, ')'); dstr_putz(&d); | |
361 | tvec_error(tv, "%s", d.buf); rc = -1; goto end; | |
362 | } | |
363 | ||
364 | *x_out = x; rc = 0; | |
365 | end: | |
366 | dstr_destroy(&d); | |
367 | return (rc); | |
368 | } | |
369 | ||
b64eb60f MW |
370 | static int convert_hex(char ch, int *v_out) |
371 | { | |
372 | if ('0' <= ch && ch <= '9') { *v_out = ch - '0'; return (0); } | |
373 | else if ('a' <= ch && ch <= 'f') { *v_out = ch - 'a' + 10; return (0); } | |
374 | else if ('A' <= ch && ch <= 'F') { *v_out = ch - 'A' + 10; return (0); } | |
375 | else return (-1); | |
376 | } | |
377 | ||
e63124bc | 378 | static int read_escape(int *ch_out, struct tvec_state *tv) |
b64eb60f | 379 | { |
b64eb60f MW |
380 | int ch, i, esc; |
381 | unsigned f = 0; | |
382 | #define f_brace 1u | |
383 | ||
e63124bc MW |
384 | ch = getc(tv->fp); |
385 | switch (ch) { | |
386 | case EOF: case '\n': tvec_syntax(tv, ch, "string escape"); | |
387 | case '\'': *ch_out = '\''; break; | |
388 | case '\\': *ch_out = '\\'; break; | |
389 | case '"': *ch_out = '"'; break; | |
390 | case 'a': *ch_out = '\a'; break; | |
391 | case 'b': *ch_out = '\b'; break; | |
392 | case 'e': *ch_out = '\x1b'; break; | |
393 | case 'f': *ch_out = '\f'; break; | |
394 | case 'n': *ch_out = '\n'; break; | |
395 | case 'r': *ch_out = '\r'; break; | |
396 | case 't': *ch_out = '\t'; break; | |
397 | case 'v': *ch_out = '\v'; break; | |
398 | ||
399 | case 'x': | |
400 | ch = getc(tv->fp); | |
401 | if (ch == '{') { f |= f_brace; ch = getc(tv->fp); } | |
402 | else f &= ~f_brace; | |
403 | if (convert_hex(ch, &esc)) | |
404 | return (tvec_syntax(tv, ch, "hex digit")); | |
405 | for (;;) { | |
406 | ch = getc(tv->fp); if (convert_hex(ch, &i)) break; | |
407 | esc = 8*esc + i; | |
408 | if (esc > UCHAR_MAX) | |
409 | return (tvec_error(tv, | |
410 | "character code %d out of range", esc)); | |
411 | } | |
412 | if (!(f&f_brace)) ungetc(ch, tv->fp); | |
413 | else if (ch != '}') return (tvec_syntax(tv, ch, "`}'")); | |
414 | *ch_out = esc; | |
415 | break; | |
416 | ||
417 | default: | |
418 | if ('0' <= ch && ch < '8') { | |
419 | i = 1; esc = ch - '0'; | |
420 | for (;;) { | |
421 | ch = getc(tv->fp); | |
422 | if ('0' > ch || ch >= '8') { ungetc(ch, tv->fp); break; } | |
423 | esc = 8*esc + ch - '0'; | |
424 | i++; if (i >= 3) break; | |
425 | } | |
426 | if (esc > UCHAR_MAX) | |
427 | return (tvec_error(tv, | |
428 | "character code %d out of range", esc)); | |
429 | *ch_out = esc; | |
430 | } else | |
431 | return (tvec_syntax(tv, ch, "string escape")); | |
432 | } | |
433 | ||
434 | return (0); | |
435 | ||
436 | #undef f_brace | |
437 | } | |
438 | ||
439 | static int read_quoted_string(dstr *d, int quote, struct tvec_state *tv) | |
440 | { | |
441 | int ch; | |
b64eb60f MW |
442 | |
443 | for (;;) { | |
444 | ch = getc(tv->fp); | |
b64eb60f MW |
445 | switch (ch) { |
446 | case EOF: case '\n': | |
e63124bc | 447 | return (tvec_syntax(tv, ch, "`%c'", quote)); |
b64eb60f MW |
448 | case '\\': |
449 | if (quote == '\'') goto ordinary; | |
e63124bc MW |
450 | ch = getc(tv->fp); if (ch == '\n') { tv->lno++; break; } |
451 | ungetc(ch, tv->fp); if (read_escape(&ch, tv)) return (-1); | |
452 | goto ordinary; | |
b64eb60f MW |
453 | default: |
454 | if (ch == quote) goto end; | |
455 | ordinary: | |
456 | DPUTC(d, ch); | |
457 | break; | |
458 | } | |
459 | } | |
460 | ||
461 | end: | |
462 | DPUTZ(d); | |
882a39c1 | 463 | return (0); |
e63124bc | 464 | } |
b64eb60f | 465 | |
e63124bc MW |
466 | #define FCF_BRACE 1u |
467 | static void format_charesc(int ch, unsigned f, | |
468 | const struct gprintf_ops *gops, void *go) | |
469 | { | |
470 | switch (ch) { | |
471 | case '\a': gprintf(gops, go, "\\a"); break; | |
472 | case '\b': gprintf(gops, go, "\\b"); break; | |
473 | case '\x1b': gprintf(gops, go, "\\e"); break; | |
474 | case '\f': gprintf(gops, go, "\\f"); break; | |
475 | case '\r': gprintf(gops, go, "\\r"); break; | |
476 | case '\n': gprintf(gops, go, "\\n"); break; | |
477 | case '\t': gprintf(gops, go, "\\t"); break; | |
478 | case '\v': gprintf(gops, go, "\\v"); break; | |
479 | case '\'': gprintf(gops, go, "\\'"); break; | |
480 | case '"': gprintf(gops, go, "\\\""); break; | |
481 | default: | |
482 | if (f&FCF_BRACE) | |
483 | gprintf(gops, go, "\\x{%0*x}", hex_width(UCHAR_MAX), ch); | |
484 | else | |
485 | gprintf(gops, go, "\\x%0*x", hex_width(UCHAR_MAX), ch); | |
486 | break; | |
487 | } | |
488 | } | |
489 | ||
490 | static void format_char(int ch, const struct gprintf_ops *gops, void *go) | |
491 | { | |
492 | if (ch == EOF) | |
493 | gprintf(gops, go, "#eof"); | |
494 | else if (isprint(ch) && ch != '\'') | |
495 | gprintf(gops, go, "'%c'", ch); | |
496 | else { | |
497 | gprintf(gops, go, "'"); | |
498 | format_charesc(ch, 0, gops, go); | |
499 | gprintf(gops, go, "'"); | |
500 | } | |
b64eb60f MW |
501 | } |
502 | ||
503 | enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 }; | |
504 | ||
505 | static int collect_bare(dstr *d, struct tvec_state *tv) | |
506 | { | |
507 | size_t pos = d->len; | |
508 | enum { WORD, SPACE, ESCAPE }; unsigned s = WORD; | |
509 | int ch, rc; | |
510 | ||
511 | for (;;) { | |
512 | ch = getc(tv->fp); | |
513 | switch (ch) { | |
514 | case EOF: | |
882a39c1 MW |
515 | tvec_syntax(tv, ch, "bareword"); |
516 | rc = -1; goto end; | |
b64eb60f MW |
517 | case '\n': |
518 | if (s == ESCAPE) { tv->lno++; goto addch; } | |
519 | if (s == WORD) pos = d->len; | |
882a39c1 | 520 | ungetc(ch, tv->fp); if (tvec_nexttoken(tv)) { rc = -1; goto end; } |
b64eb60f MW |
521 | DPUTC(d, ' '); s = SPACE; |
522 | break; | |
523 | case '"': case '\'': case '!': | |
882a39c1 | 524 | if (s == SPACE) { ungetc(ch, tv->fp); goto done; } |
b64eb60f MW |
525 | goto addch; |
526 | case '\\': | |
527 | s = ESCAPE; | |
528 | break; | |
529 | default: | |
530 | if (s != ESCAPE && isspace(ch)) { | |
531 | if (s == WORD) pos = d->len; | |
532 | DPUTC(d, ch); s = SPACE; | |
533 | break; | |
534 | } | |
535 | addch: | |
536 | DPUTC(d, ch); s = WORD; | |
537 | } | |
538 | } | |
539 | ||
540 | done: | |
541 | if (s == SPACE) d->len = pos; | |
882a39c1 MW |
542 | DPUTZ(d); rc = 0; |
543 | end: | |
544 | return (rc); | |
b64eb60f MW |
545 | } |
546 | ||
547 | static void set_up_encoding(const codec_class **ccl_out, unsigned *f_out, | |
548 | unsigned code) | |
549 | { | |
550 | switch (code) { | |
551 | case TVCODE_BARE: | |
552 | *ccl_out = 0; *f_out = 0; | |
553 | break; | |
554 | case TVCODE_HEX: | |
555 | *ccl_out = &hex_class; *f_out = CDCF_IGNCASE; | |
556 | break; | |
557 | case TVCODE_BASE32: | |
558 | *ccl_out = &base32_class; *f_out = CDCF_IGNCASE | CDCF_IGNEQPAD; | |
559 | break; | |
560 | case TVCODE_BASE64: | |
561 | *ccl_out = &base64_class; *f_out = CDCF_IGNEQPAD; | |
562 | break; | |
563 | default: | |
564 | abort(); | |
565 | } | |
566 | } | |
567 | ||
882a39c1 MW |
568 | static int read_compound_string(void **p_inout, size_t *sz_inout, |
569 | unsigned code, struct tvec_state *tv) | |
b64eb60f MW |
570 | { |
571 | const codec_class *ccl; unsigned f; | |
572 | codec *cdc; | |
573 | dstr d = DSTR_INIT, w = DSTR_INIT; | |
574 | char *p; | |
882a39c1 | 575 | int ch, err, rc; |
b64eb60f MW |
576 | |
577 | set_up_encoding(&ccl, &f, code); | |
578 | if (tvec_nexttoken(tv)) tvec_syntax(tv, fgetc(tv->fp), "string"); | |
579 | do { | |
580 | ch = getc(tv->fp); | |
581 | if (ch == '"' || ch == '\'') | |
882a39c1 | 582 | { if (read_quoted_string(&d, ch, tv)) { rc = -1; goto end; } } |
b64eb60f MW |
583 | else if (ch == '!') { |
584 | ungetc(ch, tv->fp); | |
585 | DRESET(&w); tvec_readword(tv, &w, ";", "`!'-keyword"); | |
586 | if (STRCMP(w.buf, ==, "!bare")) code = TVCODE_BARE; | |
587 | else if (STRCMP(w.buf, ==, "!hex")) code = TVCODE_HEX; | |
588 | else if (STRCMP(w.buf, ==, "!base32")) code = TVCODE_BASE32; | |
589 | else if (STRCMP(w.buf, ==, "!base64")) code = TVCODE_BASE64; | |
882a39c1 MW |
590 | else { |
591 | tvec_error(tv, "unknown string keyword `%s'", w.buf); | |
592 | rc = -1; goto end; | |
593 | } | |
b64eb60f MW |
594 | set_up_encoding(&ccl, &f, code); |
595 | } else if (ccl) { | |
596 | ungetc(ch, tv->fp); | |
597 | DRESET(&w); | |
882a39c1 MW |
598 | if (tvec_readword(tv, &w, ";", "%s-encoded fragment", ccl->name)) |
599 | { rc = -1; goto end; } | |
b64eb60f MW |
600 | cdc = ccl->decoder(f); |
601 | err = cdc->ops->code(cdc, w.buf, w.len, &d); | |
602 | if (!err) err = cdc->ops->code(cdc, 0, 0, &d); | |
882a39c1 | 603 | if (err) { |
b64eb60f MW |
604 | tvec_error(tv, "invalid %s fragment `%s': %s", |
605 | ccl->name, w.buf, codec_strerror(err)); | |
882a39c1 MW |
606 | rc = -1; goto end; |
607 | } | |
b64eb60f MW |
608 | cdc->ops->destroy(cdc); |
609 | } else switch (code) { | |
610 | case TVCODE_BARE: | |
611 | ungetc(ch, tv->fp); | |
612 | if (collect_bare(&d, tv)) goto done; | |
613 | break; | |
614 | default: | |
615 | abort(); | |
616 | } | |
617 | } while (!tvec_nexttoken(tv)); | |
618 | ||
619 | done: | |
620 | if (*sz_inout <= d.len) | |
621 | { xfree(*p_inout); *p_inout = xmalloc(d.len + 1); } | |
622 | p = *p_inout; memcpy(p, d.buf, d.len); p[d.len] = 0; *sz_inout = d.len; | |
882a39c1 MW |
623 | rc = 0; |
624 | end: | |
b64eb60f | 625 | dstr_destroy(&d); dstr_destroy(&w); |
882a39c1 | 626 | return (rc); |
b64eb60f MW |
627 | } |
628 | ||
629 | /*----- Skeleton ----------------------------------------------------------*/ | |
630 | /* | |
631 | static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd) | |
632 | static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd) | |
633 | static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1, | |
634 | const struct tvec_regdef *rd) | |
b64eb60f MW |
635 | static int tobuf_...(buf *b, const union tvec_regval *rv, |
636 | const struct tvec_regdef *rd) | |
637 | static int frombuf_...(buf *b, union tvec_regval *rv, | |
638 | const struct tvec_regdef *rd) | |
e63124bc MW |
639 | static int parse_...(union tvec_regval *rv, const struct tvec_regdef *rd, |
640 | struct tvec_state *tv) | |
b64eb60f MW |
641 | static void dump_...(const union tvec_regval *rv, |
642 | const struct tvec_regdef *rd, | |
643 | struct tvec_state *tv, unsigned style) | |
644 | ||
645 | const struct tvec_regty tvty_... = { | |
e63124bc | 646 | init_..., release_..., eq_..., |
b64eb60f MW |
647 | tobuf_..., frombuf_..., |
648 | parse_..., dump_... | |
649 | }; | |
650 | */ | |
651 | /*----- Signed and unsigned integer types ---------------------------------*/ | |
652 | ||
653 | static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd) | |
654 | { rv->i = 0; } | |
655 | ||
656 | static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd) | |
657 | { rv->u = 0; } | |
658 | ||
659 | static void release_int(union tvec_regval *rv, const struct tvec_regdef *rd) | |
660 | { ; } | |
661 | ||
662 | static int eq_int(const union tvec_regval *rv0, const union tvec_regval *rv1, | |
663 | const struct tvec_regdef *rd) | |
664 | { return (rv0->i == rv1->i); } | |
665 | ||
666 | static int eq_uint(const union tvec_regval *rv0, | |
667 | const union tvec_regval *rv1, | |
668 | const struct tvec_regdef *rd) | |
669 | { return (rv0->u == rv1->u); } | |
670 | ||
b64eb60f MW |
671 | static int tobuf_int(buf *b, const union tvec_regval *rv, |
672 | const struct tvec_regdef *rd) | |
673 | { return (signed_to_buf(b, rv->i)); } | |
674 | ||
675 | static int tobuf_uint(buf *b, const union tvec_regval *rv, | |
676 | const struct tvec_regdef *rd) | |
677 | { return (unsigned_to_buf(b, rv->u)); } | |
678 | ||
679 | static int frombuf_int(buf *b, union tvec_regval *rv, | |
680 | const struct tvec_regdef *rd) | |
882a39c1 | 681 | { return (signed_from_buf(b, &rv->i)); } |
b64eb60f MW |
682 | |
683 | static int frombuf_uint(buf *b, union tvec_regval *rv, | |
684 | const struct tvec_regdef *rd) | |
685 | { return (unsigned_from_buf(b, &rv->u)); } | |
686 | ||
882a39c1 MW |
687 | static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd, |
688 | struct tvec_state *tv) | |
b64eb60f MW |
689 | { |
690 | dstr d = DSTR_INIT; | |
882a39c1 | 691 | int rc; |
b64eb60f | 692 | |
e63124bc MW |
693 | if (tvec_readword(tv, &d, ";", "signed integer")) |
694 | { rc = -1; goto end; } | |
695 | if (parse_signed(&rv->i, d.buf, rd->arg.p, tv)) | |
696 | { rc = -1; goto end; } | |
697 | if (tvec_flushtoeol(tv, 0)) | |
698 | { rc = -1; goto end; } | |
882a39c1 MW |
699 | rc = 0; |
700 | end: | |
b64eb60f | 701 | dstr_destroy(&d); |
882a39c1 | 702 | return (rc); |
b64eb60f MW |
703 | } |
704 | ||
882a39c1 MW |
705 | static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd, |
706 | struct tvec_state *tv) | |
b64eb60f MW |
707 | { |
708 | dstr d = DSTR_INIT; | |
882a39c1 | 709 | int rc; |
b64eb60f | 710 | |
e63124bc MW |
711 | if (tvec_readword(tv, &d, ";", "unsigned integer")) |
712 | { rc = -1; goto end; } | |
713 | if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv)) | |
714 | { rc = -1; goto end; } | |
715 | if (tvec_flushtoeol(tv, 0)) | |
716 | { rc = -1; goto end; } | |
882a39c1 MW |
717 | rc = 0; |
718 | end: | |
b64eb60f | 719 | dstr_destroy(&d); |
882a39c1 | 720 | return (rc); |
b64eb60f MW |
721 | } |
722 | ||
723 | static void dump_int(const union tvec_regval *rv, | |
724 | const struct tvec_regdef *rd, | |
e63124bc MW |
725 | unsigned style, |
726 | const struct gprintf_ops *gops, void *go) | |
b64eb60f MW |
727 | { |
728 | unsigned long u; | |
729 | ||
e63124bc | 730 | gprintf(gops, go, "%ld", rv->i); |
b64eb60f MW |
731 | if (!(style&TVSF_COMPACT)) { |
732 | if (rv->i >= 0) u = rv->i; | |
733 | else u = -(unsigned long)rv->i; | |
e63124bc MW |
734 | gprintf(gops, go, " ; = %s0x%0*lx", |
735 | rv->i < 0 ? "-" : "", hex_width(u), u); | |
736 | if (rv->i == EOF || (0 <= rv->i && rv->i < UCHAR_MAX)) | |
737 | { gprintf(gops, go, " = "); format_char(rv->i, gops, go); } | |
b64eb60f MW |
738 | } |
739 | } | |
740 | ||
741 | static void dump_uint(const union tvec_regval *rv, | |
742 | const struct tvec_regdef *rd, | |
e63124bc MW |
743 | unsigned style, |
744 | const struct gprintf_ops *gops, void *go) | |
b64eb60f | 745 | { |
e63124bc MW |
746 | gprintf(gops, go, "%lu", rv->u); |
747 | if (!(style&TVSF_COMPACT)) { | |
748 | gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u); | |
749 | if (rv->u < UCHAR_MAX) | |
750 | { gprintf(gops, go, " = "); format_char(rv->u, gops, go); } | |
751 | } | |
b64eb60f MW |
752 | } |
753 | ||
754 | const struct tvec_regty tvty_int = { | |
e63124bc | 755 | init_int, release_int, eq_int, |
b64eb60f MW |
756 | tobuf_int, frombuf_int, |
757 | parse_int, dump_int | |
758 | }; | |
759 | ||
760 | const struct tvec_irange | |
761 | tvrange_schar = { SCHAR_MIN, SCHAR_MAX }, | |
762 | tvrange_short = { SHRT_MIN, SHRT_MAX }, | |
763 | tvrange_int = { INT_MIN, INT_MAX }, | |
764 | tvrange_long = { LONG_MIN, LONG_MAX }, | |
765 | tvrange_sbyte = { -128, 127 }, | |
766 | tvrange_i16 = { -32768, +32767 }, | |
767 | tvrange_i32 = { -2147483648, 2147483647 }; | |
768 | ||
769 | const struct tvec_regty tvty_uint = { | |
e63124bc | 770 | init_uint, release_int, eq_uint, |
b64eb60f MW |
771 | tobuf_uint, frombuf_uint, |
772 | parse_uint, dump_uint | |
773 | }; | |
774 | ||
775 | const struct tvec_urange | |
776 | tvrange_uchar = { 0, UCHAR_MAX }, | |
777 | tvrange_ushort = { 0, USHRT_MAX }, | |
778 | tvrange_uint = { 0, UINT_MAX }, | |
779 | tvrange_ulong = { 0, ULONG_MAX }, | |
780 | tvrange_size = { 0, (size_t)-1 }, | |
781 | tvrange_byte = { 0, 255 }, | |
782 | tvrange_u16 = { 0, 65535 }, | |
783 | tvrange_u32 = { 0, 4294967296 }; | |
784 | ||
785 | int tvec_claimeq_int(struct tvec_state *tv, long i0, long i1, | |
786 | const char *file, unsigned lno, const char *expr) | |
787 | { | |
788 | tv->in[0].v.i = i0; tv->out[0].v.i = i1; | |
789 | return (tvec_claimeq(tv, &tvty_int, 0, file, lno, expr)); | |
790 | } | |
791 | ||
792 | int tvec_claimeq_uint(struct tvec_state *tv, | |
793 | unsigned long u0, unsigned long u1, | |
794 | const char *file, unsigned lno, const char *expr) | |
795 | { | |
796 | tv->in[0].v.u = u0; tv->out[0].v.u = u1; | |
797 | return (tvec_claimeq(tv, &tvty_uint, 0, file, lno, expr)); | |
798 | } | |
799 | ||
e63124bc MW |
800 | /*----- Main code ---------------------------------------------------------*/ |
801 | ||
802 | static void init_float(union tvec_regval *rv, const struct tvec_regdef *rd) | |
803 | { rv->f = 0.0; } | |
804 | static void release_float(union tvec_regval *rv, | |
805 | const struct tvec_regdef *rd) | |
806 | { ; } | |
807 | ||
808 | static int eq_float(const union tvec_regval *rv0, | |
809 | const union tvec_regval *rv1, | |
810 | const struct tvec_regdef *rd) | |
811 | { return (eqish_floating_p(rv0->f, rv1->f, rd->arg.p)); } | |
812 | ||
813 | static int tobuf_float(buf *b, const union tvec_regval *rv, | |
814 | const struct tvec_regdef *rd) | |
815 | { return (buf_putf64l(b, rv->f)); } | |
816 | static int frombuf_float(buf *b, union tvec_regval *rv, | |
817 | const struct tvec_regdef *rd) | |
818 | { return (buf_getf64l(b, &rv->f)); } | |
819 | ||
820 | static int parse_float(union tvec_regval *rv, const struct tvec_regdef *rd, | |
821 | struct tvec_state *tv) | |
822 | { | |
823 | dstr d = DSTR_INIT; | |
824 | int rc; | |
825 | ||
826 | if (tvec_readword(tv, &d, ";", "floating-point number")) | |
827 | { rc = -1; goto end; } | |
828 | if (parse_floating(&rv->f, d.buf, rd->arg.p, tv)) | |
829 | { rc = -1; goto end; } | |
830 | if (tvec_flushtoeol(tv, 0)) | |
831 | { rc = -1; goto end; } | |
832 | rc = 0; | |
833 | end: | |
834 | dstr_destroy(&d); | |
835 | return (rc); | |
836 | } | |
837 | ||
838 | static void dump_float(const union tvec_regval *rv, | |
839 | const struct tvec_regdef *rd, | |
840 | unsigned style, | |
841 | const struct gprintf_ops *gops, void *go) | |
842 | { format_floating(gops, go, rv->f); } | |
843 | ||
844 | const struct tvec_regty tvty_float = { | |
845 | init_float, release_float, eq_float, | |
846 | tobuf_float, frombuf_float, | |
847 | parse_float, dump_float | |
848 | }; | |
849 | ||
850 | int tvec_claimeqish_float(struct tvec_state *tv, | |
851 | double f0, double f1, unsigned f, double delta, | |
852 | const char *file, unsigned lno, | |
853 | const char *expr) | |
854 | { | |
855 | struct tvec_floatinfo fi; | |
856 | union tvec_misc arg; | |
857 | ||
858 | fi.f = f; fi.min = fi.max = 0.0; fi.delta = delta; arg.p = &fi; | |
859 | tv->in[0].v.f = f0; tv->in[1].v.f = f1; | |
860 | return (tvec_claimeq(tv, &tvty_float, &arg, file, lno, expr)); | |
861 | } | |
862 | int tvec_claimeq_float(struct tvec_state *tv, | |
863 | double f0, double f1, | |
864 | const char *file, unsigned lno, | |
865 | const char *expr) | |
866 | { | |
867 | return (tvec_claimeqish_float(tv, f0, f1, TVFF_EXACT, 0.0, | |
868 | file, lno, expr)); | |
869 | } | |
870 | ||
b64eb60f MW |
871 | /*----- Enumerations ------------------------------------------------------*/ |
872 | ||
873 | static void init_enum(union tvec_regval *rv, const struct tvec_regdef *rd) | |
874 | { | |
875 | const struct tvec_enuminfo *ei = rd->arg.p; | |
876 | ||
877 | switch (ei->mv) { | |
878 | #define CASE(tag, ty, slot) \ | |
879 | case TVMISC_##tag: rv->slot = 0; break; | |
880 | TVEC_MISCSLOTS(CASE) | |
881 | #undef CASE | |
882 | default: abort(); | |
883 | } | |
884 | } | |
885 | ||
886 | static int eq_enum(const union tvec_regval *rv0, | |
887 | const union tvec_regval *rv1, | |
888 | const struct tvec_regdef *rd) | |
889 | { | |
890 | const struct tvec_enuminfo *ei = rd->arg.p; | |
e63124bc | 891 | const struct tvec_fenuminfo *fei; |
b64eb60f MW |
892 | |
893 | switch (ei->mv) { | |
894 | #define CASE(tag, ty, slot) \ | |
e63124bc MW |
895 | case TVMISC_##tag: PREP_##tag return (HANDLE_##tag); |
896 | #define PREP_INT | |
897 | #define HANDLE_INT rv0->i == rv1->i | |
898 | #define PREP_UINT | |
899 | #define HANDLE_UINT rv0->u == rv1->u | |
900 | #define PREP_FLT fei = (const struct tvec_fenuminfo *)ei; | |
901 | #define HANDLE_FLT eqish_floating_p(rv0->f, rv1->f, fei->fi) | |
902 | #define PREP_PTR | |
903 | #define HANDLE_PTR rv0->p == rv1->p | |
b64eb60f MW |
904 | TVEC_MISCSLOTS(CASE) |
905 | #undef CASE | |
e63124bc MW |
906 | #undef PREP_INT |
907 | #undef HANDLE_INT | |
908 | #undef PREP_UINT | |
909 | #undef HANDLE_UINT | |
910 | #undef PREP_FLT | |
911 | #undef HANDLE_FLT | |
912 | #undef PREP_PTR | |
913 | #undef HANDLE_PTR | |
b64eb60f MW |
914 | default: abort(); |
915 | } | |
916 | } | |
917 | ||
918 | static int tobuf_enum(buf *b, const union tvec_regval *rv, | |
919 | const struct tvec_regdef *rd) | |
920 | { | |
921 | const struct tvec_enuminfo *ei = rd->arg.p; | |
e63124bc MW |
922 | const struct tvec_penuminfo *pei; |
923 | const struct tvec_passoc *pa; | |
924 | long i; | |
b64eb60f MW |
925 | |
926 | switch (ei->mv) { | |
927 | #define CASE(tag, ty, slot) \ | |
e63124bc MW |
928 | case TVMISC_##tag: HANDLE_##tag |
929 | #define HANDLE_INT return (signed_to_buf(b, rv->i)); | |
930 | #define HANDLE_UINT return (unsigned_to_buf(b, rv->u)); | |
931 | #define HANDLE_FLT return (buf_putf64l(b, rv->f)); | |
932 | #define HANDLE_PTR \ | |
933 | pei = (const struct tvec_penuminfo *)ei; \ | |
934 | for (pa = pei->av, i = 0; pa->tag; pa++, i++) \ | |
935 | if (pa->p == rv->p) goto found; \ | |
936 | if (!rv->p) i = -1; \ | |
937 | else return (-1); \ | |
938 | found: \ | |
939 | return (signed_to_buf(b, i)); | |
b64eb60f MW |
940 | TVEC_MISCSLOTS(CASE) |
941 | #undef CASE | |
942 | #undef HANDLE_INT | |
943 | #undef HANDLE_UINT | |
e63124bc | 944 | #undef HANDLE_FLT |
b64eb60f MW |
945 | #undef HANDLE_PTR |
946 | default: abort(); | |
947 | } | |
948 | return (0); | |
949 | } | |
950 | ||
951 | static int frombuf_enum(buf *b, union tvec_regval *rv, | |
952 | const struct tvec_regdef *rd) | |
953 | { | |
954 | const struct tvec_enuminfo *ei = rd->arg.p; | |
e63124bc MW |
955 | const struct tvec_penuminfo *pei; |
956 | const struct tvec_passoc *pa; | |
957 | long i, n; | |
b64eb60f MW |
958 | |
959 | switch (ei->mv) { | |
960 | #define CASE(tag, ty, slot) \ | |
e63124bc MW |
961 | case TVMISC_##tag: HANDLE_##tag |
962 | #define HANDLE_INT return (signed_from_buf(b, &rv->i)); | |
963 | #define HANDLE_UINT return (unsigned_from_buf(b, &rv->u)); | |
964 | #define HANDLE_FLT return (buf_getf64l(b, &rv->f)); | |
965 | #define HANDLE_PTR \ | |
966 | pei = (const struct tvec_penuminfo *)ei; \ | |
967 | for (pa = pei->av, n = 0; pa->tag; pa++, n++); \ | |
968 | if (signed_from_buf(b, &i)) return (-1); \ | |
969 | if (0 <= i && i < n) rv->p = (/*unconst*/ void *)pei->av[i].p; \ | |
970 | else if (i == -1) rv->p = 0; \ | |
971 | else return (-1); \ | |
972 | return (0); | |
b64eb60f MW |
973 | TVEC_MISCSLOTS(CASE) |
974 | #undef CASE | |
975 | #undef HANDLE_INT | |
976 | #undef HANDLE_UINT | |
e63124bc | 977 | #undef HANDLE_FLT |
b64eb60f MW |
978 | #undef HANDLE_PTR |
979 | default: abort(); | |
980 | } | |
981 | } | |
982 | ||
882a39c1 MW |
983 | static int parse_enum(union tvec_regval *rv, const struct tvec_regdef *rd, |
984 | struct tvec_state *tv) | |
b64eb60f MW |
985 | { |
986 | const struct tvec_enuminfo *ei = rd->arg.p; | |
987 | #define DECLS(tag, ty, slot) \ | |
e63124bc | 988 | const struct tvec_##slot##enuminfo *slot##ei; \ |
b64eb60f MW |
989 | const struct tvec_##slot##assoc *slot##a; |
990 | TVEC_MISCSLOTS(DECLS) | |
991 | #undef DECLS | |
992 | dstr d = DSTR_INIT; | |
882a39c1 | 993 | int rc; |
b64eb60f | 994 | |
882a39c1 MW |
995 | if (tvec_readword(tv, &d, ";", "enumeration tag or literal integer")) |
996 | { rc = -1; goto end; } | |
b64eb60f | 997 | switch (ei->mv) { |
e63124bc | 998 | |
b64eb60f MW |
999 | #define CASE(tag_, ty, slot) \ |
1000 | case TVMISC_##tag_: \ | |
e63124bc MW |
1001 | slot##ei = (const struct tvec_##slot##enuminfo *)ei; \ |
1002 | for (slot##a = slot##ei->av; slot##a->tag; slot##a++) \ | |
b64eb60f | 1003 | if (STRCMP(d.buf, ==, slot##a->tag)) \ |
e63124bc MW |
1004 | { rv->slot = FETCH_##tag_; goto done; } \ |
1005 | HANDLE_##tag_ goto done; | |
b64eb60f | 1006 | |
e63124bc | 1007 | #define FETCH_INT (ia->i) |
882a39c1 | 1008 | #define HANDLE_INT \ |
e63124bc | 1009 | if (parse_signed(&rv->i, d.buf, iei->ir, tv)) \ |
882a39c1 | 1010 | { rc = -1; goto end; } |
e63124bc MW |
1011 | |
1012 | #define FETCH_UINT (ua->u) | |
882a39c1 | 1013 | #define HANDLE_UINT \ |
e63124bc | 1014 | if (parse_unsigned(&rv->u, d.buf, uei->ur, tv)) \ |
882a39c1 | 1015 | { rc = -1; goto end; } |
e63124bc MW |
1016 | |
1017 | #define FETCH_FLT (fa->f) | |
1018 | #define HANDLE_FLT \ | |
1019 | if (parse_floating(&rv->f, d.buf, fei->fi, tv)) \ | |
1020 | { rc = -1; goto end; } | |
1021 | ||
1022 | #define FETCH_PTR ((/*unconst*/ void *)(pa->p)) | |
882a39c1 MW |
1023 | #define HANDLE_PTR \ |
1024 | if (STRCMP(d.buf, ==, "#nil")) rv->p = 0; \ | |
1025 | else goto tagonly; | |
e63124bc | 1026 | |
b64eb60f | 1027 | TVEC_MISCSLOTS(CASE) |
e63124bc | 1028 | |
b64eb60f | 1029 | #undef CASE |
e63124bc | 1030 | #undef FETCH_INT |
b64eb60f | 1031 | #undef HANDLE_INT |
e63124bc | 1032 | #undef FETCH_UINT |
b64eb60f | 1033 | #undef HANDLE_UINT |
e63124bc MW |
1034 | #undef FETCH_FLT |
1035 | #undef HANDLE_FLT | |
1036 | #undef FETCH_PTR | |
b64eb60f | 1037 | #undef HANDLE_PTR |
e63124bc MW |
1038 | |
1039 | tagonly: | |
b64eb60f | 1040 | tvec_error(tv, "unknown `%s' value `%s'", ei->name, d.buf); |
882a39c1 | 1041 | rc = -1; goto end; |
b64eb60f MW |
1042 | } |
1043 | ||
882a39c1 MW |
1044 | done: |
1045 | if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } | |
1046 | rc = 0; | |
b64eb60f | 1047 | end: |
b64eb60f | 1048 | dstr_destroy(&d); |
882a39c1 | 1049 | return (rc); |
b64eb60f MW |
1050 | } |
1051 | ||
1052 | static void dump_enum(const union tvec_regval *rv, | |
1053 | const struct tvec_regdef *rd, | |
e63124bc MW |
1054 | unsigned style, |
1055 | const struct gprintf_ops *gops, void *go) | |
b64eb60f MW |
1056 | { |
1057 | const struct tvec_enuminfo *ei = rd->arg.p; | |
1058 | #define DECLS(tag, ty, slot) \ | |
e63124bc | 1059 | const struct tvec_##slot##enuminfo *slot##ei; \ |
b64eb60f MW |
1060 | const struct tvec_##slot##assoc *slot##a; |
1061 | TVEC_MISCSLOTS(DECLS) | |
1062 | #undef DECLS | |
1063 | const char *tag; | |
1064 | unsigned long u; | |
1065 | unsigned f = 0; | |
1066 | #define f_known 1u | |
1067 | ||
1068 | switch (ei->mv) { | |
1069 | #define CASE(tag_, ty, slot) \ | |
1070 | case TVMISC_##tag_: \ | |
e63124bc MW |
1071 | slot##ei = (const struct tvec_##slot##enuminfo *)ei; \ |
1072 | for (slot##a = slot##ei->av; slot##a->tag; slot##a++) \ | |
b64eb60f MW |
1073 | if (rv->slot == slot##a->slot) \ |
1074 | { tag = slot##a->tag; goto found; } \ | |
1075 | break; | |
1076 | TVEC_MISCSLOTS(CASE) | |
1077 | #undef CASE | |
1078 | default: abort(); | |
1079 | } | |
e63124bc | 1080 | goto print_raw; |
b64eb60f MW |
1081 | |
1082 | found: | |
1083 | f |= f_known; | |
e63124bc | 1084 | gprintf(gops, go, "%s", tag); |
b64eb60f | 1085 | if (style&TVSF_COMPACT) return; |
e63124bc | 1086 | gprintf(gops, go, " ; = "); |
b64eb60f | 1087 | |
e63124bc | 1088 | print_raw: |
b64eb60f MW |
1089 | switch (ei->mv) { |
1090 | #define CASE(tag, ty, slot) \ | |
1091 | case TVMISC_##tag: HANDLE_##tag break; | |
e63124bc MW |
1092 | #define HANDLE_INT gprintf(gops, go, "%ld", rv->i); |
1093 | #define HANDLE_UINT gprintf(gops, go, "%lu", rv->u); | |
1094 | #define HANDLE_FLT format_floating(gops, go, rv->f); | |
1095 | #define HANDLE_PTR if (!rv->p) gprintf(gops, go, "#nil"); \ | |
1096 | else gprintf(gops, go, "#<%s %p>", ei->name, rv->p); | |
b64eb60f MW |
1097 | TVEC_MISCSLOTS(CASE) |
1098 | #undef CASE | |
1099 | #undef HANDLE_INT | |
1100 | #undef HANDLE_UINT | |
e63124bc | 1101 | #undef HANDLE_FLT |
b64eb60f MW |
1102 | #undef HANDLE_PTR |
1103 | } | |
1104 | ||
e63124bc | 1105 | if (style&TVSF_COMPACT) return; |
b64eb60f MW |
1106 | switch (ei->mv) { |
1107 | case TVMISC_INT: | |
e63124bc | 1108 | if (!(f&f_known)) gprintf(gops, go, " ;"); |
b64eb60f MW |
1109 | if (rv->i >= 0) u = rv->i; |
1110 | else u = -(unsigned long)rv->i; | |
e63124bc MW |
1111 | gprintf(gops, go, " = %s0x%0*lx", |
1112 | rv->i < 0 ? "-" : "", hex_width(u), u); | |
1113 | if (rv->i == EOF || (0 <= rv->i && rv->i < UCHAR_MAX)) | |
1114 | { gprintf(gops, go, " = "); format_char(rv->i, gops, go); } | |
b64eb60f MW |
1115 | break; |
1116 | case TVMISC_UINT: | |
e63124bc MW |
1117 | if (!(f&f_known)) gprintf(gops, go, " ;"); |
1118 | gprintf(gops, go, " = 0x%0*lx", hex_width(rv->u), rv->u); | |
1119 | if (rv->u < UCHAR_MAX) | |
1120 | { gprintf(gops, go, " = "); format_char(rv->u, gops, go); } | |
b64eb60f MW |
1121 | break; |
1122 | } | |
1123 | } | |
1124 | ||
1125 | const struct tvec_regty tvty_enum = { | |
e63124bc | 1126 | init_enum, release_int, eq_enum, |
b64eb60f MW |
1127 | tobuf_enum, frombuf_enum, |
1128 | parse_enum, dump_enum | |
1129 | }; | |
1130 | ||
e63124bc MW |
1131 | static const struct tvec_iassoc bool_assoc[] = { |
1132 | { "nil", 0 }, | |
1133 | { "false", 0 }, | |
1134 | { "f", 0 }, | |
1135 | { "no", 0 }, | |
1136 | { "n", 0 }, | |
1137 | { "off", 0 }, | |
1138 | ||
1139 | { "t", 1 }, | |
1140 | { "true", 1 }, | |
1141 | { "yes", 1 }, | |
1142 | { "y", 1 }, | |
1143 | { "on", 1 }, | |
1144 | ||
1145 | { 0, 0 } | |
1146 | }; | |
1147 | ||
1148 | const struct tvec_ienuminfo tvenum_bool = | |
1149 | { { "bool", TVMISC_INT }, bool_assoc, &tvrange_int }; | |
1150 | ||
b64eb60f | 1151 | #define DEFCLAIM(tag, ty, slot) \ |
e63124bc MW |
1152 | int tvec_claimeq_##slot##enum \ |
1153 | (struct tvec_state *tv, \ | |
1154 | const struct tvec_##slot##enuminfo *ei, ty e0, ty e1, \ | |
1155 | const char *file, unsigned lno, const char *expr) \ | |
b64eb60f MW |
1156 | { \ |
1157 | union tvec_misc arg; \ | |
1158 | \ | |
e63124bc | 1159 | assert(ei->_ei.mv == TVMISC_##tag); \ |
b64eb60f MW |
1160 | arg.p = ei; \ |
1161 | tv->in[0].v.slot = GET_##tag(e0); \ | |
1162 | tv->out[0].v.slot = GET_##tag(e1); \ | |
1163 | return (tvec_claimeq(tv, &tvty_enum, &arg, file, lno, expr)); \ | |
1164 | } | |
1165 | #define GET_INT(e) (e) | |
1166 | #define GET_UINT(e) (e) | |
e63124bc | 1167 | #define GET_FLT(e) (e) |
b64eb60f MW |
1168 | #define GET_PTR(e) ((/*unconst*/ void *)(e)) |
1169 | TVEC_MISCSLOTS(DEFCLAIM) | |
1170 | #undef DEFCLAIM | |
1171 | #undef GET_INT | |
1172 | #undef GET_UINT | |
e63124bc | 1173 | #undef GET_FLT |
b64eb60f MW |
1174 | #undef GET_PTR |
1175 | ||
1176 | /*----- Flag types --------------------------------------------------------*/ | |
1177 | ||
882a39c1 MW |
1178 | static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd, |
1179 | struct tvec_state *tv) | |
b64eb60f MW |
1180 | { |
1181 | const struct tvec_flaginfo *fi = rd->arg.p; | |
1182 | const struct tvec_flag *f; | |
1183 | unsigned long m = 0, v = 0, t; | |
1184 | dstr d = DSTR_INIT; | |
882a39c1 | 1185 | int ch, rc; |
b64eb60f MW |
1186 | |
1187 | for (;;) { | |
882a39c1 MW |
1188 | DRESET(&d); |
1189 | if (tvec_readword(tv, &d, "|;", "flag name or integer")) | |
1190 | { rc = -1; goto end; } | |
b64eb60f MW |
1191 | |
1192 | for (f = fi->fv; f->tag; f++) | |
1193 | if (STRCMP(f->tag, ==, d.buf)) { | |
882a39c1 MW |
1194 | if (m&f->m) |
1195 | { tvec_error(tv, "colliding flag setting"); rc = -1; goto end; } | |
1196 | else | |
1197 | { m |= f->m; v |= f->v; goto next; } | |
b64eb60f MW |
1198 | } |
1199 | ||
e63124bc MW |
1200 | if (parse_unsigned(&t, d.buf, fi->range, tv)) |
1201 | { rc = -1; goto end; } | |
882a39c1 | 1202 | v |= t; |
b64eb60f MW |
1203 | next: |
1204 | if (tvec_nexttoken(tv)) break; | |
882a39c1 MW |
1205 | ch = getc(tv->fp); |
1206 | if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; } | |
1207 | if (tvec_nexttoken(tv)) | |
1208 | { tvec_syntax(tv, '\n', "flag name or integer"); rc = -1; goto end; } | |
b64eb60f MW |
1209 | } |
1210 | rv->u = v; | |
882a39c1 MW |
1211 | rc = 0; |
1212 | end: | |
1213 | dstr_destroy(&d); | |
1214 | return (rc); | |
b64eb60f MW |
1215 | } |
1216 | ||
1217 | static void dump_flags(const union tvec_regval *rv, | |
1218 | const struct tvec_regdef *rd, | |
e63124bc MW |
1219 | unsigned style, |
1220 | const struct gprintf_ops *gops, void *go) | |
b64eb60f MW |
1221 | { |
1222 | const struct tvec_flaginfo *fi = rd->arg.p; | |
1223 | const struct tvec_flag *f; | |
1224 | unsigned long m = ~(unsigned long)0, v = rv->u; | |
1225 | const char *sep; | |
1226 | ||
1227 | for (f = fi->fv, sep = ""; f->tag; f++) | |
1228 | if ((m&f->m) && (v&f->m) == f->v) { | |
e63124bc | 1229 | gprintf(gops, go, "%s%s", sep, f->tag); m &= ~f->m; |
b64eb60f MW |
1230 | sep = style&TVSF_COMPACT ? "|" : " | "; |
1231 | } | |
1232 | ||
e63124bc | 1233 | if (v&m) gprintf(gops, go, "%s0x%0*lx", sep, hex_width(v), v&m); |
b64eb60f MW |
1234 | |
1235 | if (!(style&TVSF_COMPACT)) | |
e63124bc | 1236 | gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u); |
b64eb60f MW |
1237 | } |
1238 | ||
1239 | const struct tvec_regty tvty_flags = { | |
e63124bc | 1240 | init_uint, release_int, eq_uint, |
b64eb60f MW |
1241 | tobuf_uint, frombuf_uint, |
1242 | parse_flags, dump_flags | |
1243 | }; | |
1244 | ||
1245 | int tvec_claimeq_flags(struct tvec_state *tv, | |
1246 | const struct tvec_flaginfo *fi, | |
1247 | unsigned long f0, unsigned long f1, | |
1248 | const char *file, unsigned lno, const char *expr) | |
1249 | { | |
1250 | union tvec_misc arg; | |
1251 | ||
1252 | arg.p = fi; tv->in[0].v.u = f0; tv->out[0].v.u = f1; | |
1253 | return (tvec_claimeq(tv, &tvty_flags, &arg, file, lno, expr)); | |
1254 | } | |
1255 | ||
e63124bc MW |
1256 | /*----- Characters --------------------------------------------------------*/ |
1257 | ||
1258 | static int tobuf_char(buf *b, const union tvec_regval *rv, | |
1259 | const struct tvec_regdef *rd) | |
1260 | { | |
1261 | uint32 u; | |
1262 | if (0 <= rv->i && rv->i <= UCHAR_MAX) u = rv->i; | |
1263 | else if (rv->i == EOF) u = MASK32; | |
1264 | else return (-1); | |
1265 | return (buf_putu32l(b, u)); | |
1266 | } | |
1267 | ||
1268 | static int frombuf_char(buf *b, union tvec_regval *rv, | |
1269 | const struct tvec_regdef *rd) | |
1270 | { | |
1271 | uint32 u; | |
1272 | ||
1273 | if (buf_getu32l(b, &u)) return (-1); | |
1274 | if (0 <= u && u <= UCHAR_MAX) rv->i = u; | |
1275 | else if (u == MASK32) rv->i = EOF; | |
1276 | else return (-1); | |
1277 | return (0); | |
1278 | } | |
1279 | ||
1280 | static int parse_char(union tvec_regval *rv, const struct tvec_regdef *rd, | |
1281 | struct tvec_state *tv) | |
1282 | { | |
1283 | dstr d = DSTR_INIT; | |
1284 | int ch, rc; | |
1285 | unsigned f = 0; | |
1286 | #define f_quote 1u | |
1287 | ||
1288 | ch = getc(tv->fp); | |
1289 | if (ch == '#') { | |
1290 | ungetc(ch, tv->fp); | |
1291 | if (tvec_readword(tv, &d, ";", "character name")) { rc = -1; goto end; } | |
1292 | if (STRCMP(d.buf, ==, "#eof")) | |
1293 | rv->i = EOF; | |
1294 | else { | |
1295 | rc = tvec_error(tv, "unknown character name `%s'", d.buf); | |
1296 | goto end; | |
1297 | } | |
1298 | rc = 0; goto end; | |
1299 | } | |
1300 | ||
1301 | if (ch == '\'') { f |= f_quote; ch = getc(tv->fp); } | |
1302 | switch (ch) { | |
1303 | case '\'': | |
1304 | if (!(f&f_quote)) goto plain; | |
1305 | case EOF: case '\n': | |
1306 | rc = tvec_syntax(tv, ch, "character"); goto end; | |
1307 | case '\\': | |
1308 | if (read_escape(&ch, tv)) return (-1); | |
1309 | default: plain: | |
1310 | rv->i = ch; break; | |
1311 | } | |
1312 | if (f&f_quote) { | |
1313 | ch = getc(tv->fp); | |
1314 | if (ch != '\'') { rc = tvec_syntax(tv, ch, "`''"); goto end; } | |
1315 | } | |
1316 | if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } | |
1317 | rc = 0; | |
1318 | end: | |
1319 | dstr_destroy(&d); | |
1320 | return (rc); | |
1321 | ||
1322 | #undef f_quote | |
1323 | } | |
1324 | ||
1325 | static void dump_char(const union tvec_regval *rv, | |
1326 | const struct tvec_regdef *rd, | |
1327 | unsigned style, | |
1328 | const struct gprintf_ops *gops, void *go) | |
1329 | { | |
1330 | unsigned u; | |
1331 | ||
1332 | if ((style&TVSF_COMPACT) && isprint(rv->i) && rv->i != '\'') | |
1333 | gprintf(gops, go, "%c", (int)rv->i); | |
1334 | else | |
1335 | format_char(rv->i, gops, go); | |
1336 | ||
1337 | if (!(style&TVSF_COMPACT)) { | |
1338 | u = rv->i < 0 ? -rv->i : rv->i; | |
1339 | gprintf(gops, go, " ; = %d = %s0x%0*x", | |
1340 | (int)rv->i, rv->i < 0 ? "-" : "", hex_width(u), u); | |
1341 | } | |
1342 | } | |
1343 | ||
1344 | const struct tvec_regty tvty_char = { | |
1345 | init_int, release_int, eq_int, | |
1346 | tobuf_char, frombuf_char, | |
1347 | parse_char, dump_char | |
1348 | }; | |
1349 | ||
1350 | int tvec_claimeq_char(struct tvec_state *tv, int c0, int c1, | |
1351 | const char *file, unsigned lno, const char *expr) | |
1352 | { | |
1353 | tv->in[0].v.i = c0; tv->out[0].v.i = c1; | |
1354 | return (tvec_claimeq(tv, &tvty_char, 0, file, lno, expr)); | |
1355 | } | |
1356 | ||
b64eb60f MW |
1357 | /*----- Text and byte strings ---------------------------------------------*/ |
1358 | ||
1359 | void tvec_allocstring(union tvec_regval *rv, size_t sz) | |
1360 | { | |
1361 | if (rv->str.sz < sz) { xfree(rv->str.p); rv->str.p = xmalloc(sz); } | |
1362 | rv->str.sz = sz; | |
1363 | } | |
1364 | ||
1365 | void tvec_allocbytes(union tvec_regval *rv, size_t sz) | |
1366 | { | |
1367 | if (rv->bytes.sz < sz) { xfree(rv->bytes.p); rv->bytes.p = xmalloc(sz); } | |
1368 | rv->bytes.sz = sz; | |
1369 | } | |
1370 | ||
1371 | static void init_string(union tvec_regval *rv, const struct tvec_regdef *rd) | |
1372 | { rv->str.p = 0; rv->str.sz = 0; } | |
1373 | ||
1374 | static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd) | |
1375 | { rv->bytes.p = 0; rv->bytes.sz = 0; } | |
1376 | ||
1377 | static void release_string(union tvec_regval *rv, | |
1378 | const struct tvec_regdef *rd) | |
1379 | { xfree(rv->str.p); } | |
1380 | ||
1381 | static void release_bytes(union tvec_regval *rv, | |
1382 | const struct tvec_regdef *rd) | |
1383 | { xfree(rv->bytes.p); } | |
1384 | ||
1385 | static int eq_string(const union tvec_regval *rv0, | |
1386 | const union tvec_regval *rv1, | |
1387 | const struct tvec_regdef *rd) | |
1388 | { | |
1389 | return (rv0->str.sz == rv1->str.sz && | |
1390 | (!rv0->bytes.sz || | |
1391 | MEMCMP(rv0->str.p, ==, rv1->str.p, rv1->str.sz))); | |
1392 | } | |
1393 | ||
1394 | static int eq_bytes(const union tvec_regval *rv0, | |
1395 | const union tvec_regval *rv1, | |
1396 | const struct tvec_regdef *rd) | |
1397 | { | |
1398 | return (rv0->bytes.sz == rv1->bytes.sz && | |
1399 | (!rv0->bytes.sz || | |
1400 | MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz))); | |
1401 | } | |
1402 | ||
b64eb60f MW |
1403 | static int tobuf_string(buf *b, const union tvec_regval *rv, |
1404 | const struct tvec_regdef *rd) | |
1405 | { return (buf_putmem32l(b, rv->str.p, rv->str.sz)); } | |
1406 | ||
1407 | static int tobuf_bytes(buf *b, const union tvec_regval *rv, | |
1408 | const struct tvec_regdef *rd) | |
1409 | { return (buf_putmem32l(b, rv->bytes.p, rv->bytes.sz)); } | |
1410 | ||
1411 | static int frombuf_string(buf *b, union tvec_regval *rv, | |
1412 | const struct tvec_regdef *rd) | |
1413 | { | |
1414 | const void *p; | |
1415 | size_t sz; | |
1416 | ||
1417 | p = buf_getmem32l(b, &sz); if (!p) return (-1); | |
1418 | tvec_allocstring(rv, sz); memcpy(rv->str.p, p, sz); | |
1419 | return (0); | |
1420 | } | |
1421 | ||
1422 | static int frombuf_bytes(buf *b, union tvec_regval *rv, | |
1423 | const struct tvec_regdef *rd) | |
1424 | { | |
1425 | const void *p; | |
1426 | size_t sz; | |
1427 | ||
1428 | p = buf_getmem32l(b, &sz); if (!p) return (-1); | |
1429 | tvec_allocbytes(rv, sz); memcpy(rv->bytes.p, p, sz); | |
1430 | return (0); | |
1431 | } | |
1432 | ||
882a39c1 MW |
1433 | static int check_string_length(size_t sz, const struct tvec_urange *ur, |
1434 | struct tvec_state *tv) | |
b64eb60f MW |
1435 | { |
1436 | if (ur && (ur->min > sz || sz > ur->max)) | |
882a39c1 MW |
1437 | return (tvec_error(tv, |
1438 | "invalid string length %lu; must be in [%lu..%lu]", | |
1439 | (unsigned long)sz, ur->min, ur->max)); | |
1440 | return (0); | |
b64eb60f MW |
1441 | } |
1442 | ||
882a39c1 MW |
1443 | static int parse_string(union tvec_regval *rv, const struct tvec_regdef *rd, |
1444 | struct tvec_state *tv) | |
b64eb60f MW |
1445 | { |
1446 | void *p = rv->str.p; | |
1447 | ||
882a39c1 MW |
1448 | if (read_compound_string(&p, &rv->str.sz, TVCODE_BARE, tv)) return (-1); |
1449 | rv->str.p = p; | |
1450 | if (check_string_length(rv->str.sz, rd->arg.p, tv)) return (-1); | |
1451 | return (0); | |
b64eb60f MW |
1452 | } |
1453 | ||
882a39c1 MW |
1454 | static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd, |
1455 | struct tvec_state *tv) | |
b64eb60f MW |
1456 | { |
1457 | void *p = rv->bytes.p; | |
1458 | ||
882a39c1 MW |
1459 | if (read_compound_string(&p, &rv->bytes.sz, TVCODE_HEX, tv)) return (-1); |
1460 | rv->bytes.p = p; | |
1461 | if (check_string_length(rv->bytes.sz, rd->arg.p, tv)) return (-1); | |
1462 | return (0); | |
b64eb60f MW |
1463 | } |
1464 | ||
1465 | static void dump_string(const union tvec_regval *rv, | |
1466 | const struct tvec_regdef *rd, | |
e63124bc MW |
1467 | unsigned style, |
1468 | const struct gprintf_ops *gops, void *go) | |
b64eb60f MW |
1469 | { |
1470 | const unsigned char *p, *q, *l; | |
b64eb60f MW |
1471 | unsigned f = 0; |
1472 | #define f_nonword 1u | |
1473 | #define f_newline 2u | |
1474 | ||
e63124bc | 1475 | if (!rv->str.sz) { gprintf(gops, go, "\"\""); return; } |
b64eb60f MW |
1476 | |
1477 | p = (const unsigned char *)rv->str.p; l = p + rv->str.sz; | |
1478 | if (*p == '!' || *p == ';' || *p == '"' || *p == '\'') goto quote; | |
1479 | for (q = p; q < l; q++) | |
1480 | if (*q == '\n' && q != l - 1) f |= f_newline; | |
1481 | else if (!*q || !isgraph(*q) || *q == '\\') f |= f_nonword; | |
e63124bc | 1482 | if (f&f_newline) { gprintf(gops, go, "\n\t"); goto quote; } |
b64eb60f | 1483 | else if (f&f_nonword) goto quote; |
e63124bc | 1484 | gops->putm(go, (const char *)p, rv->str.sz); return; |
b64eb60f MW |
1485 | |
1486 | quote: | |
e63124bc | 1487 | gprintf(gops, go, "\""); |
b64eb60f | 1488 | for (q = p; q < l; q++) |
e63124bc MW |
1489 | if (!isprint(*q) || *q == '"') { |
1490 | if (p < q) gops->putm(go, (const char *)p, q - p); | |
1491 | if (*q == '\n' && !(style&TVSF_COMPACT))gprintf(gops, go, "\\n\"\t\""); | |
1492 | else format_charesc(*q, FCF_BRACE, gops, go); | |
b64eb60f | 1493 | } |
e63124bc MW |
1494 | if (p < q) gops->putm(go, (const char *)p, q - p); |
1495 | gprintf(gops, go, "\""); | |
b64eb60f MW |
1496 | |
1497 | #undef f_nonword | |
1498 | #undef f_newline | |
1499 | } | |
1500 | ||
1501 | static void dump_bytes(const union tvec_regval *rv, | |
1502 | const struct tvec_regdef *rd, | |
e63124bc MW |
1503 | unsigned style, |
1504 | const struct gprintf_ops *gops, void *go) | |
b64eb60f MW |
1505 | { |
1506 | const unsigned char *p = rv->bytes.p, *l = p + rv->bytes.sz; | |
1507 | size_t off, sz = rv->bytes.sz; | |
1508 | unsigned i, n; | |
1509 | int wd; | |
1510 | ||
1511 | if (!sz) { | |
e63124bc | 1512 | gprintf(gops, go, style&TVSF_COMPACT ? "\"\"" : "\"\" ; empty"); |
b64eb60f MW |
1513 | return; |
1514 | } | |
1515 | ||
1516 | if (style&TVSF_COMPACT) { | |
e63124bc | 1517 | while (p < l) gprintf(gops, go, "%02x", *p++); |
b64eb60f MW |
1518 | return; |
1519 | } | |
1520 | ||
e63124bc | 1521 | if (sz > 16) gprintf(gops, go, "\n\t"); |
b64eb60f MW |
1522 | |
1523 | off = 0; wd = hex_width(sz); | |
1524 | while (p < l) { | |
1525 | if (l - p < 16) n = l - p; | |
1526 | else n = 16; | |
1527 | ||
1528 | for (i = 0; i < 16; i++) { | |
e63124bc MW |
1529 | if (i < n) gprintf(gops, go, "%02x", p[i]); |
1530 | else gprintf(gops, go, " "); | |
1531 | if (i%4 == 3) gprintf(gops, go, " "); | |
b64eb60f | 1532 | } |
e63124bc MW |
1533 | gprintf(gops, go, " ; "); |
1534 | if (sz > 16) gprintf(gops, go, "[%0*lx] ", wd, (unsigned long)off); | |
b64eb60f | 1535 | for (i = 0; i < n; i++) |
e63124bc | 1536 | gprintf(gops, go, "%c", isprint(p[i]) ? p[i] : '.'); |
b64eb60f | 1537 | p += n; off += n; |
e63124bc | 1538 | if (p < l) gprintf(gops, go, "\n\t"); |
b64eb60f MW |
1539 | } |
1540 | } | |
1541 | ||
1542 | const struct tvec_regty tvty_string = { | |
e63124bc | 1543 | init_string, release_string, eq_string, |
b64eb60f MW |
1544 | tobuf_string, frombuf_string, |
1545 | parse_string, dump_string | |
1546 | }; | |
1547 | ||
1548 | const struct tvec_regty tvty_bytes = { | |
e63124bc | 1549 | init_bytes, release_bytes, eq_bytes, |
b64eb60f MW |
1550 | tobuf_bytes, frombuf_bytes, |
1551 | parse_bytes, dump_bytes | |
1552 | }; | |
1553 | ||
1554 | int tvec_claimeq_string(struct tvec_state *tv, | |
1555 | const char *p0, size_t sz0, | |
1556 | const char *p1, size_t sz1, | |
1557 | const char *file, unsigned lno, const char *expr) | |
1558 | { | |
1559 | tv->in[0].v.str.p = (/*unconst*/ char *)p0; tv->in[0].v.str.sz = sz0; | |
1560 | tv->out[0].v.str.p =(/*unconst*/ char *) p1; tv->out[0].v.str.sz = sz1; | |
1561 | return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr)); | |
1562 | } | |
1563 | ||
1564 | int tvec_claimeq_strz(struct tvec_state *tv, | |
1565 | const char *p0, const char *p1, | |
1566 | const char *file, unsigned lno, const char *expr) | |
1567 | { | |
1568 | tv->in[0].v.str.p = (/*unconst*/ char *)p0; | |
1569 | tv->in[0].v.str.sz = strlen(p0); | |
1570 | tv->out[0].v.str.p = (/*unconst*/ char *)p1; | |
1571 | tv->out[0].v.str.sz = strlen(p1); | |
1572 | return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr)); | |
1573 | } | |
1574 | ||
1575 | int tvec_claimeq_bytes(struct tvec_state *tv, | |
1576 | const void *p0, size_t sz0, | |
1577 | const void *p1, size_t sz1, | |
1578 | const char *file, unsigned lno, const char *expr) | |
1579 | { | |
1580 | tv->in[0].v.bytes.p = (/*unconst*/ void *)p0; | |
1581 | tv->in[0].v.bytes.sz = sz0; | |
1582 | tv->out[0].v.bytes.p = (/*unconst*/ void *)p1; | |
1583 | tv->out[0].v.bytes.sz = sz1; | |
1584 | return (tvec_claimeq(tv, &tvty_bytes, 0, file, lno, expr)); | |
1585 | } | |
1586 | ||
1587 | /*----- Buffer type -------------------------------------------------------*/ | |
1588 | ||
1589 | static int eq_buffer(const union tvec_regval *rv0, | |
1590 | const union tvec_regval *rv1, | |
1591 | const struct tvec_regdef *rd) | |
1592 | { return (rv0->bytes.sz == rv1->bytes.sz); } | |
1593 | ||
1594 | static int tobuf_buffer(buf *b, const union tvec_regval *rv, | |
1595 | const struct tvec_regdef *rd) | |
1596 | { return (unsigned_to_buf(b, rv->bytes.sz)); } | |
1597 | ||
1598 | static int frombuf_buffer(buf *b, union tvec_regval *rv, | |
1599 | const struct tvec_regdef *rd) | |
1600 | { | |
1601 | unsigned long u; | |
1602 | ||
1603 | if (unsigned_from_buf(b, &u)) return (-1); | |
1604 | if (u > (size_t)-1) return (-1); | |
1605 | tvec_allocbytes(rv, u); memset(rv->bytes.p, '!', u); | |
1606 | return (0); | |
1607 | } | |
1608 | ||
1609 | static const char units[] = "kMGTPEZY"; | |
1610 | ||
882a39c1 MW |
1611 | static int parse_buffer(union tvec_regval *rv, |
1612 | const struct tvec_regdef *rd, | |
1613 | struct tvec_state *tv) | |
b64eb60f MW |
1614 | { |
1615 | dstr d = DSTR_INIT; | |
1616 | char *q; const char *unit; | |
1617 | int olderr; | |
1618 | size_t pos; | |
1619 | unsigned long u, t; | |
882a39c1 | 1620 | int rc; |
b64eb60f MW |
1621 | unsigned f = 0; |
1622 | #define f_range 1u | |
1623 | ||
882a39c1 | 1624 | if (tvec_readword(tv, &d, ";", "buffer length")) { rc = -1; goto end; } |
b64eb60f MW |
1625 | olderr = errno; errno = 0; |
1626 | u = strtoul(d.buf, &q, 0); | |
1627 | if (errno) goto bad; | |
1628 | errno = olderr; | |
1629 | if (!*q) { | |
1630 | tvec_skipspc(tv); pos = d.len; | |
1631 | if (!tvec_readword(tv, &d, ";", 0)) pos++; | |
1632 | q = d.buf + pos; | |
1633 | } | |
1634 | ||
1635 | if (u > (size_t)-1) goto rangerr; | |
1636 | for (t = u, unit = units; *unit; unit++) { | |
1637 | if (t > (size_t)-1/1024) f |= f_range; | |
1638 | else t *= 1024; | |
1639 | if (*q == *unit && (!q[1] || q[1] == 'B')) { | |
1640 | if (f&f_range) goto rangerr; | |
1641 | u = t; q += 2; break; | |
1642 | } | |
1643 | } | |
1644 | if (*q && *q != ';') goto bad; | |
882a39c1 | 1645 | if (check_string_length(u, rd->arg.p, tv)) { rc = -1; goto end; } |
b64eb60f | 1646 | |
882a39c1 MW |
1647 | if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } |
1648 | tvec_allocbytes(rv, u); memset(rv->bytes.p, '?', u); | |
1649 | rc = 0; | |
1650 | end: | |
1651 | DDESTROY(&d); return (rc); | |
b64eb60f MW |
1652 | |
1653 | bad: | |
1654 | tvec_error(tv, "invalid buffer length `%s'", d.buf); | |
882a39c1 | 1655 | rc = -1; goto end; |
b64eb60f MW |
1656 | |
1657 | rangerr: | |
1658 | tvec_error(tv, "buffer length `%s' out of range", d.buf); | |
882a39c1 | 1659 | rc = -1; goto end; |
b64eb60f MW |
1660 | |
1661 | #undef f_range | |
1662 | } | |
1663 | ||
1664 | static void dump_buffer(const union tvec_regval *rv, | |
1665 | const struct tvec_regdef *rd, | |
e63124bc MW |
1666 | unsigned style, |
1667 | const struct gprintf_ops *gops, void *go) | |
b64eb60f MW |
1668 | { |
1669 | const char *unit; | |
1670 | unsigned long u = rv->bytes.sz; | |
1671 | ||
1672 | if (!u || u%1024) | |
e63124bc | 1673 | gprintf(gops, go, "%lu B", u); |
b64eb60f MW |
1674 | else { |
1675 | for (unit = units, u /= 1024; !(u%1024) && unit[1]; u /= 1024, unit++); | |
e63124bc | 1676 | gprintf(gops, go, "%lu %cB", u, *unit); |
b64eb60f MW |
1677 | } |
1678 | } | |
1679 | ||
1680 | const struct tvec_regty tvty_buffer = { | |
e63124bc | 1681 | init_bytes, release_bytes, eq_buffer, |
b64eb60f MW |
1682 | tobuf_buffer, frombuf_buffer, |
1683 | parse_buffer, dump_buffer | |
1684 | }; | |
1685 | ||
1686 | /*----- That's all, folks -------------------------------------------------*/ |