chiark / gitweb /
@@@ so much mess
[mLib] / test / tvec-types.c
CommitLineData
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
61static 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
72static 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
87static int unsigned_to_buf(buf *b, unsigned long u)
88 { kludge64 k; ASSIGN64(k, u); return (buf_putk64l(b, k)); }
89
90static 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
100static 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
109static 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
121static 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
133static 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
154static 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
173static 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
265static 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
287static 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;
365end:
366 dstr_destroy(&d);
367 return (rc);
368}
369
b64eb60f
MW
370static 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 378static 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
439static 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
461end:
462 DPUTZ(d);
882a39c1 463 return (0);
e63124bc 464}
b64eb60f 465
e63124bc
MW
466#define FCF_BRACE 1u
467static 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
490static 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
503enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 };
504
505static 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
540done:
541 if (s == SPACE) d->len = pos;
882a39c1
MW
542 DPUTZ(d); rc = 0;
543end:
544 return (rc);
b64eb60f
MW
545}
546
547static 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
568static 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
619done:
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;
624end:
b64eb60f 625 dstr_destroy(&d); dstr_destroy(&w);
882a39c1 626 return (rc);
b64eb60f
MW
627}
628
629/*----- Skeleton ----------------------------------------------------------*/
630/*
631static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd)
632static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd)
633static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1,
634 const struct tvec_regdef *rd)
b64eb60f
MW
635static int tobuf_...(buf *b, const union tvec_regval *rv,
636 const struct tvec_regdef *rd)
637static int frombuf_...(buf *b, union tvec_regval *rv,
638 const struct tvec_regdef *rd)
e63124bc
MW
639static int parse_...(union tvec_regval *rv, const struct tvec_regdef *rd,
640 struct tvec_state *tv)
b64eb60f
MW
641static void dump_...(const union tvec_regval *rv,
642 const struct tvec_regdef *rd,
643 struct tvec_state *tv, unsigned style)
644
645const 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
653static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd)
654 { rv->i = 0; }
655
656static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd)
657 { rv->u = 0; }
658
659static void release_int(union tvec_regval *rv, const struct tvec_regdef *rd)
660 { ; }
661
662static 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
666static 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
671static 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
675static 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
679static 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
683static 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
687static 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;
700end:
b64eb60f 701 dstr_destroy(&d);
882a39c1 702 return (rc);
b64eb60f
MW
703}
704
882a39c1
MW
705static 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;
718end:
b64eb60f 719 dstr_destroy(&d);
882a39c1 720 return (rc);
b64eb60f
MW
721}
722
723static 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
741static 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
754const 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
760const 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
769const 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
775const 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
785int 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
792int 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
802static void init_float(union tvec_regval *rv, const struct tvec_regdef *rd)
803 { rv->f = 0.0; }
804static void release_float(union tvec_regval *rv,
805 const struct tvec_regdef *rd)
806 { ; }
807
808static 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
813static int tobuf_float(buf *b, const union tvec_regval *rv,
814 const struct tvec_regdef *rd)
815 { return (buf_putf64l(b, rv->f)); }
816static int frombuf_float(buf *b, union tvec_regval *rv,
817 const struct tvec_regdef *rd)
818 { return (buf_getf64l(b, &rv->f)); }
819
820static 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;
833end:
834 dstr_destroy(&d);
835 return (rc);
836}
837
838static 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
844const struct tvec_regty tvty_float = {
845 init_float, release_float, eq_float,
846 tobuf_float, frombuf_float,
847 parse_float, dump_float
848};
849
850int 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}
862int 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
873static 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
886static 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
918static 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
951static 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
983static 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
1044done:
1045 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
1046 rc = 0;
b64eb60f 1047end:
b64eb60f 1048 dstr_destroy(&d);
882a39c1 1049 return (rc);
b64eb60f
MW
1050}
1051
1052static 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
1082found:
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 1088print_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
1125const 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
1131static 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
1148const 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))
1169TVEC_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
1178static 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;
1212end:
1213 dstr_destroy(&d);
1214 return (rc);
b64eb60f
MW
1215}
1216
1217static 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
1239const 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
1245int 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
1258static 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
1268static 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
1280static 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;
1318end:
1319 dstr_destroy(&d);
1320 return (rc);
1321
1322#undef f_quote
1323}
1324
1325static 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
1344const struct tvec_regty tvty_char = {
1345 init_int, release_int, eq_int,
1346 tobuf_char, frombuf_char,
1347 parse_char, dump_char
1348};
1349
1350int 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
1359void 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
1365void 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
1371static void init_string(union tvec_regval *rv, const struct tvec_regdef *rd)
1372 { rv->str.p = 0; rv->str.sz = 0; }
1373
1374static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd)
1375 { rv->bytes.p = 0; rv->bytes.sz = 0; }
1376
1377static void release_string(union tvec_regval *rv,
1378 const struct tvec_regdef *rd)
1379 { xfree(rv->str.p); }
1380
1381static void release_bytes(union tvec_regval *rv,
1382 const struct tvec_regdef *rd)
1383 { xfree(rv->bytes.p); }
1384
1385static 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
1394static 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
1403static 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
1407static 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
1411static 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
1422static 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
1433static 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
1443static 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
1454static 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
1465static 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
1486quote:
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
1501static 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
1542const 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
1548const 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
1554int 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
1564int 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
1575int 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
1589static 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
1594static 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
1598static 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
1609static const char units[] = "kMGTPEZY";
1610
882a39c1
MW
1611static 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;
1650end:
1651 DDESTROY(&d); return (rc);
b64eb60f
MW
1652
1653bad:
1654 tvec_error(tv, "invalid buffer length `%s'", d.buf);
882a39c1 1655 rc = -1; goto end;
b64eb60f
MW
1656
1657rangerr:
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
1664static 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
1680const 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 -------------------------------------------------*/