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