chiark / gitweb /
@@@ misc 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 "maths.h"
46 #include "tvec.h"
47
48 /*----- Preliminary utilities ---------------------------------------------*/
49
50 /* --- @trivial_release@ --- *
51  *
52  * Arguments:   @union tvec_regval *rv@ = a register value
53  *              @const struct tvec_regdef@ = the register definition
54  *
55  * Returns:     ---
56  *
57  * Use:         Does nothing.  Used for register values which don't retain
58  *              resources.
59  */
60
61 static void trivial_release(union tvec_regval *rv,
62                             const struct tvec_regdef *rd)
63   { ; }
64
65 /*----- Integer utilities -------------------------------------------------*/
66
67 /* --- @unsigned_to_buf@, @signed_to_buf@ --- *
68  *
69  * Arguments:   @buf *b@ = buffer to write on
70  *              @unsigned long u@ or @long i@ = integer to write
71  *
72  * Returns:     Zero on success, @-1@ on failure.
73  *
74  * Use:         Write @i@ to the buffer, in big-endian (two's-complement, it
75  *              signed) format.
76  */
77
78 static int unsigned_to_buf(buf *b, unsigned long u)
79   { kludge64 k; ASSIGN64(k, u); return (buf_putk64l(b, k)); }
80
81 static int signed_to_buf(buf *b, long i)
82 {
83   kludge64 k;
84   unsigned long u;
85
86   u = i;
87   if (i >= 0) ASSIGN64(k, u);
88   else { ASSIGN64(k, ~u); CPL64(k, k); }
89   return (buf_putk64l(b, k));
90 }
91
92 /* --- @unsigned_from_buf@, @signed_from_buf@ --- *
93  *
94  * Arguments:   @buf *b@ = buffer to write on
95  *              @unsigned long *u_out@ or @long *i_out@ = where to put the
96  *                      result
97  *
98  * Returns:     Zero on success, @-1@ on failure.
99  *
100  * Use:         Read an integer, in big-endian (two's-complement, if signed)
101  *              format, from the buffer.
102  */
103
104 static int unsigned_from_buf(buf *b, unsigned long *u_out)
105 {
106   kludge64 k, ulmax;
107
108   ASSIGN64(ulmax, ULONG_MAX);
109   if (buf_getk64l(b, &k)) return (-1);
110   if (CMP64(k, >, ulmax)) { buf_break(b); return (-1); }
111   *u_out = GET64(unsigned long, k); return (0);
112 }
113
114 /* --- @hex_width@ --- *
115  *
116  * Arguments:   @unsigned long u@ = an integer
117  *
118  * Returns:     A suitable number of digits to use in order to display @u@ in
119  *              hex.  Currently, we select a power of two sufficient to show
120  *              the value, but at least 2.
121  */
122
123 static int hex_width(unsigned long u)
124 {
125   int wd;
126   unsigned long t;
127
128   for (t = u >> 4, wd = 4; t >>= wd, wd *= 2, t; );
129   return (wd/4);
130 }
131
132 /* --- @format_unsigned_hex@, @format_signed_hex@ --- *
133  *
134  * Arguments:   @const struct gprintf_ops *gops@ = print operations
135  *              @void *go@ = print destination
136  *              @unsigned long u@ or @long i@ = integer to print
137  *
138  * Returns:     ---
139  *
140  * Use:         Print an unsigned or signed integer in hexadecimal.
141  */
142
143 static void format_unsigned_hex(const struct gprintf_ops *gops, void *go,
144                                 unsigned long u)
145   { gprintf(gops, go, "0x%0*lx", hex_width(u), u); }
146
147 static void format_signed_hex(const struct gprintf_ops *gops, void *go,
148                               long i)
149 {
150   unsigned long u = i >= 0 ? i : -(unsigned long)i;
151   gprintf(gops, go, "%s0x%0*lx", i < 0 ? "-" : "", hex_width(u), u);
152 }
153
154 static int signed_from_buf(buf *b, long *i_out)
155 {
156   kludge64 k, lmax, not_lmin;
157
158   ASSIGN64(lmax, LONG_MAX); ASSIGN64(not_lmin, ~(unsigned long)LONG_MIN);
159   if (buf_getk64l(b, &k)) return (-1);
160   if (CMP64(k, <=, lmax)) *i_out = (long)GET64(unsigned long, k);
161   else {
162     CPL64(k, k);
163     if (CMP64(k, <=, not_lmin)) *i_out = -(long)GET64(unsigned long, k) - 1;
164     else { buf_break(b); return (-1); }
165   }
166   return (0);
167 }
168
169 /* --- @check_unsigned_range@, @check_signed_range@ --- *
170  *
171  * Arguments:   @unsigned long u@ or @long i@ = an integer
172  *              @const struct tvec_urange *ur@ or
173  *                      @const struct tvec_irange *ir@ = range specification,
174  *                      or null
175  *              @struct tvec_state *tv@ = test vector state
176  *              @const char *what@ = description of value
177  *
178  * Returns:     Zero on success, or @-1@ on error.
179  *
180  * Use:         Check that the integer is within bounds.  If not, report a
181  *              suitable error and return a failure indication.
182  */
183
184 static int check_signed_range(long i,
185                               const struct tvec_irange *ir,
186                               struct tvec_state *tv, const char *what)
187 {
188   if (ir && (ir->min > i || i > ir->max)) {
189     tvec_error(tv, "%s %ld out of range (must be in [%ld .. %ld])",
190                what, i, ir->min, ir->max);
191     return (-1);
192   }
193   return (0);
194 }
195
196 static int check_unsigned_range(unsigned long u,
197                                 const struct tvec_urange *ur,
198                                 struct tvec_state *tv, const char *what)
199 {
200   if (ur && (ur->min > u || u > ur->max)) {
201     tvec_error(tv, "%s %lu out of range (must be in [%lu .. %lu])",
202                what, u, ur->min, ur->max);
203     return (-1);
204   }
205   return (0);
206 }
207
208 /* --- @chtodig@ --- *
209  *
210  * Arguments:   @int ch@ = a character
211  *
212  * Returns:     The numeric value of the character as a digit, or @-1@ if
213  *              it's not a digit.  Letters count as extended digits starting
214  *              with value 10; case is not significant.
215  */
216
217 static int chtodig(int ch)
218 {
219   if ('0' <= ch && ch <= '9') return (ch - '0');
220   else if ('a' <= ch && ch <= 'z') return (ch - 'a' + 10);
221   else if ('A' <= ch && ch <= 'Z') return (ch - 'A' + 10);
222   else return (-1);
223 }
224
225 /* --- @parse_unsigned_integer@, @parse_signed_integer@ --- *
226  *
227  * Arguments:   @unsigned long *u_out@, @long *i_out@ = where to put the
228  *                      result
229  *              @const char **q_out@ = where to put the end position
230  *              @const char *p@ = pointer to the string to parse
231  *
232  * Returns:     Zero on success, @-1@ on error.
233  *
234  * Use:         Parse an integer from a string in the test-vector format.
235  *              This is mostly extension of the traditional C @strtoul@
236  *              format: supported inputs include:
237  *
238  *                * NNN -- a decimal number (even if it starts with `0');
239  *                * 0xNNN -- hexadecimal;
240  *                * 0oNNN -- octal;
241  *                * 0bNNN -- binary;
242  *                * NNrNNN -- base NN.
243  *
244  *              Furthermore, single underscores are permitted internally as
245  *              an insignificant digit separator.
246  */
247
248 static int parse_unsigned_integer(unsigned long *u_out, const char **q_out,
249                                   const char *p)
250 {
251   unsigned long u;
252   int ch, d, r;
253   const char *q;
254   unsigned f = 0;
255 #define f_implicit 1u                   /* implicitly reading base 10 */
256 #define f_digit 2u                      /* read a real digit */
257 #define f_uscore 4u                     /* found an underscore */
258
259   /* Initial setup
260    *
261    * This will deal with the traditional `0[box]...' prefixes.  We'll leave
262    * our new `NNr...' syntax for later.
263    */
264   if (p[0] != '0' || !p[1]) {
265     d = chtodig(*p); if (0 > d || d >= 10) return (-1);
266     r = 10; u = d; p++; f |= f_implicit | f_digit;
267   } else {
268     u = 0; d = chtodig(p[2]);
269     if (d < 0) { r = 10; f |= f_implicit | f_digit; p++; }
270     else if ((p[1] == 'x' || p[1] == 'X') && d < 16) { r = 16; p += 2; }
271     else if ((p[1] == 'o' || p[1] == 'O') && d < 8) { r = 8; p += 2; }
272     else if ((p[1] == 'b' || p[1] == 'B') && d < 2) { r = 2; p += 2; }
273     else { r = 10; f |= f_digit; p++; }
274   }
275
276   q = p;
277   for (;;) {
278     /* Work through the string a character at a time. */
279
280     ch = *p; switch (ch) {
281
282       case '_':
283         /* An underscore is OK if we haven't just seen one. */
284
285         if (f&f_uscore) goto done;
286         p++; f = (f&~f_implicit) | f_uscore;
287         break;
288
289       case 'r': case 'R':
290         /* An `r' is OK if the number so far is small enough to be a sensible
291          * base, and we're scanning decimal implicitly.
292          */
293
294         if (!(f&f_implicit) || !u || u >= 36) goto done;
295         d = chtodig(p[1]); if (0 > d || d >= u) goto done;
296         r = u; u = d; f = (f&~f_implicit) | f_digit; p += 2; q = p;
297         break;
298
299       default:
300         /* Otherwise we expect a valid digit and accumulate it. */
301         d = chtodig(ch); if (d < 0 || d >= r) goto done;
302         if (u > ULONG_MAX/r) return (-1);
303         u *= r; if (u > ULONG_MAX - d) return (-1);
304         u += d; f = (f&~f_uscore) | f_digit; p++; q = p;
305         break;
306     }
307   }
308
309 done:
310   if (!(f&f_digit)) return (-1);
311   *u_out = u; *q_out = q; return (0);
312
313 #undef f_implicit
314 #undef f_digit
315 #undef f_uscore
316 }
317
318 static int parse_signed_integer(long *i_out, const char **q_out,
319                                 const char *p)
320 {
321   unsigned long u;
322   unsigned f = 0;
323 #define f_neg 1u
324
325   /* Read an initial sign. */
326   if (*p == '+') p++;
327   else if (*p == '-') { f |= f_neg; p++; }
328
329   /* Scan an unsigned number. */
330   if (parse_unsigned_integer(&u, q_out, p)) return (-1);
331
332   /* Check for signed overflow and apply the sign. */
333   if (!(f&f_neg)) {
334     if (u > LONG_MAX) return (-1);
335     *i_out = u;
336   } else {
337     if (u && u - 1 > -(LONG_MIN + 1)) return (-1);
338     *i_out = u ? -(long)(u - 1) - 1 : 0;
339   }
340
341   return (0);
342
343 #undef f_neg
344 }
345
346 /* --- @parse_unsigned@, @parse_signed@ --- *
347  *
348  * Arguments:   @unsigned long *u_out@ or @long *i_out@ = where to put the
349  *                      result
350  *              @const char *p@ = string to parse
351  *              @const struct tvec_urange *ur@ or
352  *                      @const struct tvec_irange *ir@ = range specification,
353  *                      or null
354  *              @struct tvec_state *tv@ = test vector state
355  *
356  * Returns:     Zero on success, @-1@ on error.
357  *
358  * Use:         Parse and range-check an integer.  Unlike @parse_(un)signed_
359  *              integer@, these functions check that there's no cruft
360  *              following the final digit, and report errors as they find
361  *              them rather than leaving that to the caller.
362  */
363
364 static int parse_unsigned(unsigned long *u_out, const char *p,
365                           const struct tvec_urange *ur,
366                           struct tvec_state *tv)
367 {
368   unsigned long u;
369   const char *q;
370
371   if (parse_unsigned_integer(&u, &q, p))
372     return (tvec_error(tv, "invalid unsigned integer `%s'", p));
373   if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
374   if (check_unsigned_range(u, ur, tv, "integer")) return (-1);
375   *u_out = u; return (0);
376 }
377
378 static int parse_signed(long *i_out, const char *p,
379                         const struct tvec_irange *ir,
380                         struct tvec_state *tv)
381 {
382   long i;
383   const char *q;
384
385   if (parse_signed_integer(&i, &q, p))
386     return (tvec_error(tv, "invalid signed integer `%s'", p));
387   if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
388   if (check_signed_range(i, ir, tv, "integer")) return (-1);
389   *i_out = i; return (0);
390 }
391 static const char size_units[] = "kMGTPEZY";
392
393 /* --- @parse_szint@ --- *
394  *
395  * Arguments:   @struct tvec_state *tv@ = test-vector state
396  *              @unsigned long *u_out@ = where to put the answer
397  *              @const char *delims@ = delimiters
398  *              @const char *what@ = description of what we're parsing
399  *
400  * Returns:     Zero on success, %$-1$% on failure.
401  *
402  * Use:         Parse a memory size.
403  */
404
405 static int parse_szint(struct tvec_state *tv, unsigned long *u_out,
406                        const char *delims, const char *what)
407 {
408   dstr d = DSTR_INIT;
409   const char *p, *unit;
410   unsigned long u, t;
411   int rc;
412   unsigned f = 0;
413 #define f_range 1u
414
415   if (tvec_readword(tv, &d, 0, delims, what)) { rc = -1; goto end; }
416   p = d.buf;
417   if (parse_unsigned_integer(&u, &p, p)) goto bad;
418   if (!*p) tvec_readword(tv, &d, &p, delims, 0);
419
420   for (t = u, unit = size_units; *unit; unit++) {
421     if (t > ULONG_MAX/1024) f |= f_range;
422     else t *= 1024;
423     if (*p == *unit) {
424       if (f&f_range) goto rangerr;
425       u = t; p++; break;
426     }
427   }
428   if (*p == 'B') p++;
429   if (*p) goto bad;
430
431   *u_out = u; rc = 0;
432 end:
433   dstr_destroy(&d);
434   return (rc);
435
436 bad:
437   tvec_error(tv, "invalid %s `%s'", what, d.buf);
438   rc = -1; goto end;
439
440 rangerr:
441   tvec_error(tv, "%s `%s' out of range", what, d.buf);
442   rc = -1; goto end;
443
444 #undef f_range
445 }
446
447 /* --- @format_size@ --- *
448  *
449  * Arguments:   @const struct gprintf_ops *gops@ = print operations
450  *              @void *go@ = print destination
451  *              @unsigned long u@ = a size
452  *              @unsigned style@ = style (@TVSF_...@)
453  *
454  * Returns:     ---
455  *
456  * Use:         Format @u@ as a size in bytes to the destination, expressing
457  *              it with a unit prefix if this is possible exactly.
458  */
459
460 static void format_size(const struct gprintf_ops *gops, void *go,
461                         unsigned long u, unsigned style)
462 {
463   const char *unit;
464
465   if (style&TVSF_RAW)
466     gprintf(gops, go, "%lu", u);
467   else if (!u || u%1024)
468     gprintf(gops, go, "%lu%sB", u, style&TVSF_COMPACT ? "" : " ");
469   else {
470     for (unit = size_units, u /= 1024;
471          !(u%1024) && unit[1];
472          u /= 1024, unit++);
473     gprintf(gops, go, "%lu%s%cB", u, style&TVSF_COMPACT ? "" : " ", *unit);
474   }
475 }
476
477 /*----- Floating-point utilities ------------------------------------------*/
478
479 /* --- @eqish_floating_p@ --- *
480  *
481  * Arguments:   @double x, y@ = two numbers to compare
482  *              @const struct tvec_floatinfo *fi@ = floating-point info
483  *
484  * Returns:     Nonzero if  the comparand @x@ is sufficiently close to the
485  *              reference @y@, or zero if it's definitely different.
486  */
487
488 static int eqish_floating_p(double x, double y,
489                             const struct tvec_floatinfo *fi)
490 {
491   double t;
492
493   if (NANP(x)) return (NANP(y)); else if (NANP(y)) return (0);
494   if (INFP(x)) return (x == y); else if (INFP(y)) return (0);
495
496   switch (fi ? fi->f&TVFF_EQMASK : TVFF_EXACT) {
497     case TVFF_EXACT:
498       return (x == y && NEGP(x) == NEGP(y));
499     case TVFF_ABSDELTA:
500       t = x - y; if (t < 0) t = -t; return (t < fi->delta);
501     case TVFF_RELDELTA:
502       t = 1.0 - x/y; if (t < 0) t = -t; return (t < fi->delta);
503     default:
504       abort();
505   }
506 }
507
508 /* --- @format_floating@ --- *
509  *
510  * Arguments:   @const struct gprintf_ops *gops@ = print operations
511  *              @void *go@ = print destination
512  *              @double x@ = number to print
513  *
514  * Returns:     ---
515  *
516  * Use:         Print a floating-point number, accurately.
517  */
518
519 static void format_floating(const struct gprintf_ops *gops, void *go,
520                             double x)
521 {
522   int prec;
523
524   if (NANP(x))
525     gprintf(gops, go, "#nan");
526   else if (INFP(x))
527     gprintf(gops, go, x > 0 ? "#+inf" : "#-inf");
528   else {
529     /* Ugh.  C doesn't provide any function for just printing a
530      * floating-point number /correctly/, i.e., so that you can read the
531      * result back and recover the number you first thought of.  There are
532      * complicated algorithms published for doing this, but I really don't
533      * want to get into that here.  So we have this.
534      *
535      * The sign doesn't cause significant difficulty so we're going to ignore
536      * it for now.  So suppose we're given a number %$x = f b^e$%, in
537      * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with
538      * %$0 \le f < 1$%.  We're going to convert it into the nearest integer
539      * of the form %$X = F B^E$%, with similar conditions, only with the
540      * additional requirement that %$X$% is normalized, i.e., that %$X = 0$%
541      * or %$F \ge B^{-N}$%.
542      *
543      * We're rounding to the nearest such %$X$%.  If there is to be ambiguity
544      * in the conversion, then some %$x = f b^e$% and the next smallest
545      * representable number %$x' = x + b^{e-n}$% must both map to the same
546      * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than
547      * any other number representable in the target system.  The nest larger
548      * number is %$X' = X + B^{E-N}$%; the next smaller number will normally
549      * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number
550      * is actually %$X - B^{E-N-1}$%.  We ignore this latter possibility in
551      * the pursuit of a conservative estimate (though actually it doesn't
552      * matter).
553      *
554      * If both %$x$% and %$x'$% map to %$X$% then we must have
555      * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%;
556      * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%,
557      * and secondly %$b^{e-n} \le B^{E-N}$%.  Since these inequalities are in
558      * opposite senses, we can divide, giving
559      *
560      *         %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% ,
561      *
562      * whence
563      *
564      *         %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% .
565      *
566      * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be
567      * possible, it must be the case that
568      *
569      *         %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% .
570      *
571      * Then rearrange and take logarithms, obtaining
572      *
573      *         %$(N - 1) \log B < n \log b$% ,
574      *
575      * and so
576      *
577      *         %$N < n \log b/\log B + 1$% .
578      *
579      * Recall that this is a necessary condition for a collision to occur; we
580      * are therefore safe whenever
581      *
582      *         %$N \ge n \log b/\log B + 1$% ;
583      *
584      * so, taking ceilings,
585      *
586      *         %$N \ge \lceil n \log b/\log B \rceil + 1$% .
587      *
588      * So that's why we have this.
589      *
590      * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that
591      * we can calculate this without ending up on the wrong side of an
592      * integer boundary.
593      *
594      * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only
595      * as a constant.  Except that modern compilers are more than clever
596      * enough to work out that this is a constant anyway.
597      *
598      * This is sometimes an overestimate: we'll print out meaningless digits
599      * that don't represent anything we actually know about the number in
600      * question.  To fix that, we'd need a complicated algorithm like Steele
601      * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm
602      * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to
603      * something else in difficult situations).
604      */
605
606     prec = ceil(DBL_MANT_DIG*log(FLT_RADIX)/log(10)) + 1;
607     gprintf(gops, go, "%.*g", prec, x);
608   }
609 }
610
611 /* --- @parse_floating@ --- *
612  *
613  * Arguments:   @double *x_out@ = where to put the result
614  *              @const char *q_out@ = where to leave end pointer, or null
615  *              @const char *p@ = string to parse
616  *              @const struct tvec_floatinfo *fi@ = floating-point info
617  *              @struct tvec_state *tv@ = test vector state
618  *
619  * Returns:     Zero on success, @-1@ on error.
620  *
621  * Use:         Parse a floating-point number from a string.  Reports any
622  *              necessary errors.  If @q_out@ is not null then trailing
623  *              material is permitted and a pointer to it (or the end of the
624  *              string) is left in @*q_out@.
625  */
626
627 static int parse_floating(double *x_out, const char **q_out, const char *p,
628                           const struct tvec_floatinfo *fi,
629                           struct tvec_state *tv)
630 {
631   const char *pp; char *q;
632   dstr d = DSTR_INIT;
633   double x;
634   int olderr, rc;
635
636   /* Check for special tokens. */
637   if (STRCMP(p, ==, "#nan")) {
638 #ifdef NAN
639     if (q_out) *q_out = p + strlen(p);
640     x = NAN; rc = 0;
641 #else
642     tvec_error(tv, "NaN not supported on this system");
643     rc = -1; goto end;
644 #endif
645   }
646
647   else if (STRCMP(p, ==, "#inf") ||
648            STRCMP(p, ==, "#+inf") || STRCMP(p, ==, "+#inf")) {
649 #ifdef INFINITY
650     if (q_out) *q_out = p + strlen(p);
651     x = INFINITY; rc = 0;
652 #else
653     tvec_error(tv, "infinity not supported on this system");
654     rc = -1; goto end;
655 #endif
656   }
657
658   else if (STRCMP(p, ==, "#-inf") || STRCMP(p, ==, "-#inf")) {
659 #ifdef INFINITY
660     if (q_out) *q_out = p + strlen(p);
661     x = -INFINITY; rc = 0;
662 #else
663     tvec_error(tv, "infinity not supported on this system");
664     rc = -1; goto end;
665 #endif
666   }
667
668   /* Check that this looks like a number, so we can exclude `strtod'
669    * recognizing its own non-finite number tokens.
670    */
671   else {
672     pp = p;
673     if (*pp == '+' || *pp == '-') pp++;
674     if (*pp == '.') pp++;
675     if (!ISDIGIT(*pp)) {
676       tvec_syntax(tv, *p ? *p : fgetc(tv->fp), "floating-point number");
677       rc = -1; goto end;
678     }
679
680     /* Parse the number using the system parser. */
681     olderr = errno; errno = 0;
682     x = strtod(p, &q);
683     if (q_out) *q_out = q;
684     else if (*q) { tvec_syntax(tv, *q, "end-of-line"); rc = -1; goto end; }
685     if (errno && (errno != ERANGE || (x > 0 ? -x : x) == HUGE_VAL)) {
686       tvec_error(tv, "invalid floating-point number `%.*s': %s",
687                  (int)(q - p), p, strerror(errno));
688       rc = -1; goto end;
689     }
690     errno = olderr;
691   }
692
693   /* Check that the number is acceptable. */
694   if (NANP(x) && fi && !(fi->f&TVFF_NANOK)) {
695     tvec_error(tv, "#nan not allowed here");
696     rc = -1; goto end;
697   }
698
699   if (fi && ((!(fi->f&TVFF_NOMIN) && x < fi->min) ||
700              (!(fi->f&TVFF_NOMAX) && x > fi->max))) {
701     dstr_puts(&d, "floating-point number ");
702     format_floating(&dstr_printops, &d, x);
703     dstr_puts(&d, " out of range (must be in ");
704     if (fi->f&TVFF_NOMIN)
705       dstr_puts(&d, "(#-inf");
706     else
707       { dstr_putc(&d, '['); format_floating(&dstr_printops, &d, fi->min); }
708     dstr_puts(&d, " .. ");
709     if (fi->f&TVFF_NOMAX)
710       dstr_puts(&d, "#+inf)");
711     else
712       { format_floating(&dstr_printops, &d, fi->max); dstr_putc(&d, ']'); }
713     dstr_putc(&d, ')'); dstr_putz(&d);
714     tvec_error(tv, "%s", d.buf); rc = -1; goto end;
715   }
716
717   /* All done. */
718   *x_out = x; rc = 0;
719 end:
720   dstr_destroy(&d);
721   return (rc);
722 }
723
724 /*----- String utilities --------------------------------------------------*/
725
726 /* Special character name table. */
727 static const struct chartab {
728   const char *name;                     /* character name */
729   int ch;                               /* character value */
730   unsigned f;                           /* flags: */
731 #define CTF_PREFER 1u                   /*   preferred name */
732 #define CTF_SHORT 2u                    /*   short name (compact style) */
733 } chartab[] = {
734   { "#eof",             EOF,    CTF_PREFER | CTF_SHORT },
735   { "#nul",             '\0',   CTF_PREFER },
736   { "#bell",            '\a',   CTF_PREFER },
737   { "#ding",            '\a',   0 },
738   { "#bel",             '\a',   CTF_SHORT },
739   { "#backspace",       '\b',   CTF_PREFER },
740   { "#bs",              '\b',   CTF_SHORT },
741   { "#escape",          '\x1b', CTF_PREFER },
742   { "#esc",             '\x1b', CTF_SHORT },
743   { "#formfeed",        '\f',   CTF_PREFER },
744   { "#ff",              '\f',   CTF_SHORT },
745   { "#newline",         '\n',   CTF_PREFER },
746   { "#linefeed",        '\n',   0 },
747   { "#lf",              '\n',   CTF_SHORT },
748   { "#nl",              '\n',   0 },
749   { "#return",          '\r',   CTF_PREFER },
750   { "#carriage-return", '\r',   0 },
751   { "#cr",              '\r',   CTF_SHORT },
752   { "#tab",             '\t',   CTF_PREFER | CTF_SHORT },
753   { "#horizontal-tab",  '\t',   0 },
754   { "#ht",              '\t',   0 },
755   { "#vertical-tab",    '\v',   CTF_PREFER },
756   { "#vt",              '\v',   CTF_SHORT },
757   { "#space",           ' ',    0 },
758   { "#spc",             ' ',    CTF_SHORT },
759   { "#delete",          '\x7f', CTF_PREFER },
760   { "#del",             '\x7f', CTF_SHORT },
761   { 0,                  0,      0 }
762 };
763
764 /* --- @find_charname@ --- *
765  *
766  * Arguments:   @int ch@ = character to match
767  *              @unsigned f@ = flags (@CTF_...@) to match
768  *
769  * Returns:     The name of the character, or null if no match is found.
770  *
771  * Use:         Looks up a name for a character.  Specifically, it returns
772  *              the first entry in the @chartab@ table which matches @ch@ and
773  *              which has one of the flags @f@ set.
774  */
775
776 static const char *find_charname(int ch, unsigned f)
777 {
778   const struct chartab *ct;
779
780   for (ct = chartab; ct->name; ct++)
781     if (ct->ch == ch && (ct->f&f)) return (ct->name);
782   return (0);
783 }
784
785 /* --- @read_charname@ --- *
786  *
787  * Arguments:   @int *ch_out@ = where to put the character
788  *              @const char *p@ = character name
789  *              @unsigned f@ = flags (@TCF_...@)
790  *
791  * Returns:     Zero if a match was found, @-1@ if not.
792  *
793  * Use:         Looks up a character by name.  If @RCF_EOFOK@ is set in @f@,
794  *              then the @EOF@ marker can be matched; otherwise it can't.
795  */
796
797 #define RCF_EOFOK 1u
798 static int read_charname(int *ch_out, const char *p, unsigned f)
799 {
800   const struct chartab *ct;
801
802   for (ct = chartab; ct->name; ct++)
803     if (STRCMP(p, ==, ct->name) && ((f&RCF_EOFOK) || ct->ch >= 0))
804       { *ch_out = ct->ch; return (0); }
805   return (-1);
806 }
807
808 /* --- @format_charesc@ --- *
809  *
810  * Arguments:   @const struct gprintf_ops *gops@ = print operations
811  *              @void *go@ = print destination
812  *              @int ch@ = character to format
813  *              @unsigned f@ = flags (@FCF_...@)
814  *
815  * Returns:     ---
816  *
817  * Use:         Format a character as an escape sequence, possibly as part of
818  *              a larger string.  If @FCF_BRACE@ is set in @f@, then put
819  *              braces around a `\x...'  code, so that it's suitable for use
820  *              in a longer string.
821  */
822
823 #define FCF_BRACE 1u
824 static void format_charesc(const struct gprintf_ops *gops, void *go,
825                            int ch, unsigned f)
826 {
827   switch (ch) {
828     case '\a': gprintf(gops, go, "\\a"); break;
829     case '\b': gprintf(gops, go, "\\b"); break;
830     case '\x1b': gprintf(gops, go, "\\e"); break;
831     case '\f': gprintf(gops, go, "\\f"); break;
832     case '\r': gprintf(gops, go, "\\r"); break;
833     case '\n': gprintf(gops, go, "\\n"); break;
834     case '\t': gprintf(gops, go, "\\t"); break;
835     case '\v': gprintf(gops, go, "\\v"); break;
836     case '\\': gprintf(gops, go, "\\\\"); break;
837     case '\'': gprintf(gops, go, "\\'"); break;
838     case '\0':
839       if (f&FCF_BRACE) gprintf(gops, go, "\\{0}");
840       else gprintf(gops, go, "\\0");
841       break;
842     default:
843       if (f&FCF_BRACE)
844         gprintf(gops, go, "\\x{%0*x}", hex_width(UCHAR_MAX), ch);
845       else
846         gprintf(gops, go, "\\x%0*x", hex_width(UCHAR_MAX), ch);
847       break;
848   }
849 }
850
851 /* --- @format_char@ --- *
852  *
853  * Arguments:   @const struct gprintf_ops *gops@ = print operations
854  *              @void *go@ = print destination
855  *              @int ch@ = character to format
856  *
857  * Returns:     ---
858  *
859  * Use:         Format a single character.
860  */
861
862 static void format_char(const struct gprintf_ops *gops, void *go, int ch)
863 {
864   switch (ch) {
865     case '\\': case '\'': escape:
866       gprintf(gops, go, "'");
867       format_charesc(gops, go, ch, 0);
868       gprintf(gops, go, "'");
869       break;
870     default:
871       if (!isprint(ch)) goto escape;
872       gprintf(gops, go, "'%c'", ch);
873       break;
874   }
875 }
876
877 /* --- @maybe_format_unsigned_char@, @maybe_format_signed_char@ --- *
878  *
879  * Arguments:   @const struct gprintf_ops *gops@ = print operations
880  *              @void *go@ = print destination
881  *              @unsigned long u@ or @long i@ = an integer
882  *
883  * Returns:     ---
884  *
885  * Use:         Format a (signed or unsigned) integer as a character, if it's
886  *              in range, printing something like `= 'q''.  It's assumed that
887  *              a comment marker has already been output.
888  */
889
890 static void maybe_format_unsigned_char
891   (const struct gprintf_ops *gops, void *go, unsigned long u)
892 {
893   const char *p;
894
895   p = find_charname(u, CTF_PREFER);
896   if (p) gprintf(gops, go, " = %s", p);
897   if (u < UCHAR_MAX)
898     { gprintf(gops, go, " = "); format_char(gops, go, u); }
899 }
900
901 static void maybe_format_signed_char
902   (const struct gprintf_ops *gops, void *go, long i)
903 {
904   const char *p;
905
906   p = find_charname(i, CTF_PREFER);
907   if (p) gprintf(gops, go, " = %s", p);
908   if (0 <= i && i < UCHAR_MAX)
909     { gprintf(gops, go, " = "); format_char(gops, go, i); }
910 }
911
912 /* --- @read_charesc@ --- *
913  *
914  * Arguments:   @int *ch_out@ = where to put the result
915  *              @struct tvec_state *tv@ = test vector state
916  *
917  * Returns:     Zero on success, @-1@ on error.
918  *
919  * Use:         Parse and convert an escape sequence from @tv@'s input
920  *              stream, assuming that the initial `\' has already been read.
921  *              Reports errors as appropriate.
922  */
923
924 static int read_charesc(int *ch_out, struct tvec_state *tv)
925 {
926   int ch, i, esc;
927   unsigned f = 0;
928 #define f_brace 1u
929
930   ch = getc(tv->fp);
931   switch (ch) {
932
933     /* Things we shouldn't find. */
934     case EOF: case '\n': return (tvec_syntax(tv, ch, "string escape"));
935
936     /* Single-character escapes. */
937     case '\'': *ch_out = '\''; break;
938     case '\\': *ch_out = '\\'; break;
939     case '"': *ch_out = '"'; break;
940     case 'a': *ch_out = '\a'; break;
941     case 'b': *ch_out = '\b'; break;
942     case 'e': *ch_out = '\x1b'; break;
943     case 'f': *ch_out = '\f'; break;
944     case 'n': *ch_out = '\n'; break;
945     case 'r': *ch_out = '\r'; break;
946     case 't': *ch_out = '\t'; break;
947     case 'v': *ch_out = '\v'; break;
948
949     /* Hex escapes, with and without braces. */
950     case 'x':
951       ch = getc(tv->fp);
952       if (ch == '{') { f |= f_brace; ch = getc(tv->fp); }
953       else f &= ~f_brace;
954       esc = chtodig(ch);
955       if (esc < 0 || esc >= 16) return (tvec_syntax(tv, ch, "hex digit"));
956       for (;;) {
957         ch = getc(tv->fp); i = chtodig(ch); if (i < 0 || i >= 16) break;
958         esc = 16*esc + i;
959         if (esc > UCHAR_MAX)
960           return (tvec_error(tv,
961                              "character code %d out of range", esc));
962       }
963       if (!(f&f_brace)) ungetc(ch, tv->fp);
964       else if (ch != '}') return (tvec_syntax(tv, ch, "`}'"));
965       *ch_out = esc;
966       break;
967
968     /* Other things, primarily octal escapes. */
969     case '{':
970       f |= f_brace; ch = getc(tv->fp);
971       /* fall through */
972     default:
973       if ('0' <= ch && ch < '8') {
974         i = 1; esc = ch - '0';
975         for (;;) {
976           ch = getc(tv->fp);
977           if ('0' > ch || ch >= '8') { ungetc(ch, tv->fp); break; }
978           esc = 8*esc + ch - '0';
979           i++; if (i >= 3) break;
980         }
981         if (f&f_brace) {
982           ch = getc(tv->fp);
983           if (ch != '}') return (tvec_syntax(tv, ch, "`}'"));
984         }
985         if (esc > UCHAR_MAX)
986           return (tvec_error(tv,
987                              "character code %d out of range", esc));
988         *ch_out = esc; break;
989       } else
990         return (tvec_syntax(tv, ch, "string escape"));
991   }
992
993   /* Done. */
994   return (0);
995
996 #undef f_brace
997 }
998
999 /* --- @read_quoted_string@ --- *
1000  *
1001  * Arguments:   @dstr *d@ = string to write to
1002  *              @int quote@ = initial quote, `'' or `"'
1003  *              @struct tvec_state *tv@ = test vector state
1004  *
1005  * Returns:     Zero on success, @-1@ on error.
1006  *
1007  * Use:         Read the rest of a quoted string into @d@, reporting errors
1008  *              as appropriate.
1009  *
1010  *              A single-quoted string is entirely literal.  A double-quoted
1011  *              string may contain C-like escapes.
1012  */
1013
1014 static int read_quoted_string(dstr *d, int quote, struct tvec_state *tv)
1015 {
1016   int ch;
1017
1018   for (;;) {
1019     ch = getc(tv->fp);
1020     switch (ch) {
1021       case EOF: case '\n':
1022         return (tvec_syntax(tv, ch, "`%c'", quote));
1023       case '\\':
1024         if (quote == '\'') goto ordinary;
1025         ch = getc(tv->fp); if (ch == '\n') { tv->lno++; break; }
1026         ungetc(ch, tv->fp); if (read_charesc(&ch, tv)) return (-1);
1027         goto ordinary;
1028       default:
1029         if (ch == quote) goto end;
1030       ordinary:
1031         DPUTC(d, ch);
1032         break;
1033     }
1034   }
1035
1036 end:
1037   DPUTZ(d);
1038   return (0);
1039 }
1040
1041 /* --- @collect_bare@ --- *
1042  *
1043  * Arguments:   @dstr *d@ = string to write to
1044  *              @struct tvec_state *tv@ = test vector state
1045  *
1046  * Returns:     Zero on success, @-1@ on error.
1047  *
1048  * Use:         Read barewords and the whitespace between them.  Stop when we
1049  *              encounter something which can't start a bareword.
1050  */
1051
1052 static int collect_bare(dstr *d, struct tvec_state *tv)
1053 {
1054   size_t pos = d->len;
1055   enum { WORD, SPACE, ESCAPE }; unsigned s = WORD;
1056   int ch, rc;
1057
1058   for (;;) {
1059     ch = getc(tv->fp);
1060     switch (ch) {
1061       case EOF:
1062         tvec_syntax(tv, ch, "bareword");
1063         rc = -1; goto end;
1064       case '\n':
1065         if (s == ESCAPE) { tv->lno++; goto addch; }
1066         if (s == WORD) pos = d->len;
1067         ungetc(ch, tv->fp); if (tvec_nexttoken(tv)) { rc = -1; goto end; }
1068         DPUTC(d, ' '); s = SPACE;
1069         break;
1070       case '"': case '\'': case '!': case '#': case ')': case '}': case ']':
1071         if (s == SPACE) { ungetc(ch, tv->fp); goto done; }
1072         goto addch;
1073       case '\\':
1074         s = ESCAPE;
1075         break;
1076       default:
1077         if (s != ESCAPE && isspace(ch)) {
1078           if (s == WORD) pos = d->len;
1079           DPUTC(d, ch); s = SPACE;
1080           break;
1081         }
1082       addch:
1083         DPUTC(d, ch); s = WORD;
1084     }
1085   }
1086
1087 done:
1088   if (s == SPACE) d->len = pos;
1089   DPUTZ(d); rc = 0;
1090 end:
1091   return (rc);
1092 }
1093
1094 /* --- @set_up_encoding@ --- *
1095  *
1096  * Arguments:   @const codec_class **ccl_out@ = where to put the class
1097  *              @unsigned *f_out@ = where to put the flags
1098  *              @unsigned code@ = the coding scheme to use (@TVEC_...@)
1099  *
1100  * Returns:     ---
1101  *
1102  * Use:         Helper for @read_compound_string@ below.
1103  *
1104  *              Return the appropriate codec class and flags for @code@.
1105  *              Leaves @*ccl_out@ null if the coding scheme doesn't have a
1106  *              backing codec class (e.g., @TVCODE_BARE@).
1107  */
1108
1109 enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 };
1110 static void set_up_encoding(const codec_class **ccl_out, unsigned *f_out,
1111                             unsigned code)
1112 {
1113   switch (code) {
1114     case TVCODE_BARE:
1115       *ccl_out = 0; *f_out = 0;
1116       break;
1117     case TVCODE_HEX:
1118       *ccl_out = &hex_class; *f_out = CDCF_IGNCASE;
1119       break;
1120     case TVCODE_BASE32:
1121       *ccl_out = &base32_class; *f_out = CDCF_IGNCASE | CDCF_IGNEQPAD;
1122       break;
1123     case TVCODE_BASE64:
1124       *ccl_out = &base64_class; *f_out = CDCF_IGNEQPAD;
1125       break;
1126     default:
1127       abort();
1128   }
1129 }
1130
1131 /* --- @flush_codec@ --- *
1132  *
1133  * Arguments:   @codec *cdc@ = a codec, or null
1134  *              @dstr *d@ = output string
1135  *              @struct tvec_state *tv@ = test vector state
1136  *
1137  * Returns:     Zero on success, @-1@ on error.
1138  *
1139  * Use:         Helper for @read_compound_string@ below.
1140  *
1141  *              Flush out any final buffered material from @cdc@, and check
1142  *              that it's in a good state.  Frees the codec on success.  Does
1143  *              nothing if @cdc@ is null.
1144  */
1145
1146 static int flush_codec(codec *cdc, dstr *d, struct tvec_state *tv)
1147 {
1148   int err;
1149
1150   if (cdc) {
1151     err = cdc->ops->code(cdc, 0, 0, d);
1152     if (err)
1153       return (tvec_error(tv, "invalid %s sequence end: %s",
1154                          cdc->ops->c->name, codec_strerror(err)));
1155     cdc->ops->destroy(cdc);
1156   }
1157   return (0);
1158 }
1159
1160 /* --- @read_compound_string@ --- *
1161  *
1162  * Arguments:   @void **p_inout@ = address of output buffer pointer
1163  *              @size_t *sz_inout@ = address of buffer size
1164  *              @unsigned code@ = initial interpretation of barewords
1165  *              @unsigned f@ = other flags (@RCSF_...@)
1166  *              @struct tvec_state *tv@ = test vector state
1167  *
1168  * Returns:     Zero on success, @-1@ on error.
1169  *
1170  * Use:         Parse a compound string, i.e., a sequence of stringish pieces
1171  *              which might be quoted strings, character names, or barewords
1172  *              to be decoded accoding to @code@, interspersed with
1173  *              additional directives.
1174  *
1175  *              If the initial buffer pointer is non-null and sufficiently
1176  *              large, then it will be reused; otherwise, it is freed and a
1177  *              fresh, sufficiently large buffer is allocated and returned.
1178  */
1179
1180 #define RCSF_NESTED 1u
1181 static int read_compound_string(void **p_inout, size_t *sz_inout,
1182                                 unsigned code, unsigned f,
1183                                 struct tvec_state *tv)
1184 {
1185   const codec_class *ccl; unsigned cdf;
1186   codec *cdc;
1187   dstr d = DSTR_INIT, w = DSTR_INIT;
1188   char *p;
1189   const char *q;
1190   void *pp = 0; size_t sz;
1191   unsigned long n;
1192   int ch, err, rc;
1193
1194   set_up_encoding(&ccl, &cdf, code); cdc = 0;
1195
1196   if (tvec_nexttoken(tv)) return (tvec_syntax(tv, fgetc(tv->fp), "string"));
1197   do {
1198     ch = getc(tv->fp);
1199     switch (ch) {
1200
1201       case ')': case ']': case '}':
1202         /* Close brackets.  Leave these for recursive caller if there is one,
1203          * or just complain.
1204          */
1205
1206         if (!(f&RCSF_NESTED))
1207           { rc = tvec_syntax(tv, ch, "string"); goto end; }
1208         ungetc(ch, tv->fp); goto done;
1209
1210       case '"': case '\'':
1211         /* Quotes.  Read a quoted string. */
1212
1213         if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
1214         cdc = 0;
1215         if (read_quoted_string(&d, ch, tv)) { rc = -1; goto end; }
1216         break;
1217
1218       case '#':
1219         /* A named character. */
1220
1221         ungetc(ch, tv->fp);
1222         if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
1223         cdc = 0;
1224         DRESET(&w); tvec_readword(tv, &w, 0, ";", "character name");
1225         if (STRCMP(w.buf, ==, "#empty")) break;
1226         if (read_charname(&ch, w.buf, RCF_EOFOK)) {
1227           rc = tvec_error(tv, "unknown character name `%s'", d.buf);
1228           goto end;
1229         }
1230         DPUTC(&d, ch); break;
1231
1232       case '!':
1233         /* A magic keyword. */
1234
1235         if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
1236         cdc = 0;
1237         ungetc(ch, tv->fp);
1238         DRESET(&w); tvec_readword(tv, &w, 0, ";", "`!'-keyword");
1239
1240         /* Change bareword coding system. */
1241         if (STRCMP(w.buf, ==, "!bare"))
1242           { code = TVCODE_BARE; set_up_encoding(&ccl, &cdf, code); }
1243         else if (STRCMP(w.buf, ==, "!hex"))
1244           { code = TVCODE_HEX; set_up_encoding(&ccl, &cdf, code); }
1245         else if (STRCMP(w.buf, ==, "!base32"))
1246           { code = TVCODE_BASE32; set_up_encoding(&ccl, &cdf, code); }
1247         else if (STRCMP(w.buf, ==, "!base64"))
1248           { code = TVCODE_BASE64; set_up_encoding(&ccl, &cdf, code); }
1249
1250         /* Repeated substrings. */
1251         else if (STRCMP(w.buf, ==, "!repeat")) {
1252           if (tvec_nexttoken(tv)) {
1253             rc = tvec_syntax(tv, fgetc(tv->fp), "repeat count");
1254             goto end;
1255           }
1256           DRESET(&w);
1257           if (tvec_readword(tv, &w, 0, ";{", "repeat count"))
1258             { rc = -1; goto end;  }
1259           if (parse_unsigned_integer(&n, &q, w.buf)) {
1260             rc = tvec_error(tv, "invalid repeat count `%s'", w.buf);
1261             goto end;
1262           }
1263           if (*q) { rc = tvec_syntax(tv, *q, "`{'"); goto end; }
1264           if (tvec_nexttoken(tv))
1265             { rc = tvec_syntax(tv, fgetc(tv->fp), "`{'"); goto end; }
1266           ch = getc(tv->fp); if (ch != '{')
1267             { rc = tvec_syntax(tv, ch, "`{'"); goto end; }
1268           sz = 0;
1269           if (read_compound_string(&pp, &sz, code, f | RCSF_NESTED, tv))
1270             { rc = -1; goto end; }
1271           ch = getc(tv->fp); if (ch != '}')
1272             { rc = tvec_syntax(tv, ch, "`}'"); goto end; }
1273           if (sz) {
1274             if (n > (size_t)-1/sz)
1275               { rc = tvec_error(tv, "repeat size out of range"); goto end; }
1276             dstr_ensure(&d, n*sz);
1277             if (sz == 1)
1278               { memset(d.buf + d.len, *(unsigned char *)pp, n); d.len += n; }
1279             else
1280               for (; n--; d.len += sz) memcpy(d.buf + d.len, pp, sz);
1281           }
1282           xfree(pp); pp = 0;
1283         }
1284
1285         /* Anything else is an error. */
1286         else {
1287           tvec_error(tv, "unknown string keyword `%s'", w.buf);
1288           rc = -1; goto end;
1289         }
1290         break;
1291
1292       default:
1293         /* A bareword.  Process it according to the current coding system. */
1294
1295         switch (code) {
1296           case TVCODE_BARE:
1297             ungetc(ch, tv->fp);
1298             if (collect_bare(&d, tv)) goto done;
1299             break;
1300           default:
1301             assert(ccl);
1302             ungetc(ch, tv->fp); DRESET(&w);
1303             if (tvec_readword(tv, &w, 0, ";",
1304                               "%s-encoded fragment", ccl->name))
1305               { rc = -1; goto end; }
1306             if (!cdc) cdc = ccl->decoder(cdf);
1307             err = cdc->ops->code(cdc, w.buf, w.len, &d);
1308             if (err) {
1309               tvec_error(tv, "invalid %s fragment `%s': %s",
1310                          ccl->name, w.buf, codec_strerror(err));
1311               rc = -1; goto end;
1312             }
1313             break;
1314         }
1315         break;
1316     }
1317   } while (!tvec_nexttoken(tv));
1318
1319 done:
1320   /* Wrap things up. */
1321   if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
1322   cdc = 0;
1323   if (*sz_inout <= d.len)
1324     { xfree(*p_inout); *p_inout = xmalloc(d.len + 1); }
1325   p = *p_inout; memcpy(p, d.buf, d.len); p[d.len] = 0; *sz_inout = d.len;
1326   rc = 0;
1327
1328 end:
1329   /* Clean up any debris. */
1330   if (cdc) cdc->ops->destroy(cdc);
1331   if (pp) xfree(pp);
1332   dstr_destroy(&d); dstr_destroy(&w);
1333   return (rc);
1334 }
1335
1336 /*----- Signed and unsigned integer types ---------------------------------*/
1337
1338 /* --- @init_int@, @init_uint@ --- *
1339  *
1340  * Arguments:   @union tvec_regval *rv@ = register value
1341  *              @const struct tvec_regdef *rd@ = register definition
1342  *
1343  * Returns:     ---
1344  *
1345  * Use:         Initialize a register value.
1346  *
1347  *              Integer values are initialized to zero.
1348  */
1349
1350 static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd)
1351   { rv->i = 0; }
1352
1353 static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd)
1354   { rv->u = 0; }
1355
1356 /* --- @eq_int@, @eq_uint@ --- *
1357  *
1358  * Arguments:   @const union tvec_regval *rv0, *rv1@ = register values
1359  *              @const struct tvec_regdef *rd@ = register definition
1360  *
1361  * Returns:     Nonzero if the values are equal, zero if unequal
1362  *
1363  * Use:         Compare register values for equality.
1364  */
1365
1366 static int eq_int(const union tvec_regval *rv0, const union tvec_regval *rv1,
1367                   const struct tvec_regdef *rd)
1368   { return (rv0->i == rv1->i); }
1369
1370 static int eq_uint(const union tvec_regval *rv0,
1371                    const union tvec_regval *rv1,
1372                    const struct tvec_regdef *rd)
1373   { return (rv0->u == rv1->u); }
1374
1375 /* --- @tobuf_int@, @tobuf_uint@ --- *
1376  *
1377  * Arguments:   @buf *b@ = buffer
1378  *              @const union tvec_regval *rv@ = register value
1379  *              @const struct tvec_regdef *rd@ = register definition
1380  *
1381  * Returns:     Zero on success, %$-1$% on failure.
1382  *
1383  * Use:         Serialize a register value to a buffer.
1384  *
1385  *              Integer values are serialized as little-endian 64-bit signed
1386  *              or unsigned integers.
1387  */
1388
1389 static int tobuf_int(buf *b, const union tvec_regval *rv,
1390                      const struct tvec_regdef *rd)
1391   { return (signed_to_buf(b, rv->i)); }
1392
1393 static int tobuf_uint(buf *b, const union tvec_regval *rv,
1394                        const struct tvec_regdef *rd)
1395   { return (unsigned_to_buf(b, rv->u)); }
1396
1397 /* --- @frombuf_int@, @frombuf_uint@ --- *
1398  *
1399  * Arguments:   @buf *b@ = buffer
1400  *              @union tvec_regval *rv@ = register value
1401  *              @const struct tvec_regdef *rd@ = register definition
1402  *
1403  * Returns:     Zero on success, %$-1$% on failure.
1404  *
1405  * Use:         Deserialize a register value from a buffer.
1406  *
1407  *              Integer values are serialized as 64-bit signed or unsigned
1408  *              integers.
1409  */
1410
1411 static int frombuf_int(buf *b, union tvec_regval *rv,
1412                        const struct tvec_regdef *rd)
1413   { return (signed_from_buf(b, &rv->i)); }
1414
1415 static int frombuf_uint(buf *b, union tvec_regval *rv,
1416                         const struct tvec_regdef *rd)
1417   { return (unsigned_from_buf(b, &rv->u)); }
1418
1419 /* --- @parse_int@, @parse_uint@ --- *
1420  *
1421  * Arguments:   @union tvec_regval *rv@ = register value
1422  *              @const struct tvec_regdef *rd@ = register definition
1423  *              @struct tvec_state *tv@ = test-vector state
1424  *
1425  * Returns:     Zero on success, %$-1$% on error.
1426  *
1427  * Use:         Parse a register value from an input file.
1428  *
1429  *              Integers may be input in decimal, hex, binary, or octal,
1430  *              following approximately usual conventions.
1431  *
1432  *                * Signed integers may be preceded with a `+' or `-' sign.
1433  *
1434  *                * Decimal integers are just a sequence of decimal digits
1435  *                  `0' ... `9'.
1436  *
1437  *                * Octal integers are a sequence of digits `0' ... `7',
1438  *                  preceded by `0o' or `0O'.
1439  *
1440  *                * Hexadecimal integers are a sequence of digits `0'
1441  *                  ... `9', `a' ... `f', or `A' ... `F', preceded by `0x' or
1442  *                  `0X'.
1443  *
1444  *                * Radix-B integers are a sequence of digits `0' ... `9',
1445  *                  `a' ... `f', or `A' ... `F', each with value less than B,
1446  *                  preceded by `Br' or `BR', where 0 < B < 36 is expressed
1447  *                  in decimal without any leading `0' or internal
1448  *                  underscores `_'.
1449  *
1450  *                * A digit sequence may contain internal underscore `_'
1451  *                  separators, but not before or after all of the digits;
1452  *                  and two consecutive `_' characters are not permitted.
1453  */
1454
1455 static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd,
1456                      struct tvec_state *tv)
1457 {
1458   dstr d = DSTR_INIT;
1459   int rc;
1460
1461   if (tvec_readword(tv, &d, 0, ";", "signed integer"))
1462     { rc = -1; goto end; }
1463   if (parse_signed(&rv->i, d.buf, rd->arg.p, tv)) { rc = -1; goto end; }
1464   if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
1465   rc = 0;
1466 end:
1467   dstr_destroy(&d);
1468   return (rc);
1469 }
1470
1471 static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd,
1472                       struct tvec_state *tv)
1473 {
1474   dstr d = DSTR_INIT;
1475   int rc;
1476
1477   if (tvec_readword(tv, &d, 0, ";", "unsigned integer"))
1478     { rc = -1; goto end; }
1479   if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv)) { rc = -1; goto end; }
1480   if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
1481   rc = 0;
1482 end:
1483   dstr_destroy(&d);
1484   return (rc);
1485 }
1486
1487 /* --- @dump_int@, @dump_uint@ --- *
1488  *
1489  * Arguments:   @const union tvec_regval *rv@ = register value
1490  *              @const struct tvec_regdef *rd@ = register definition
1491  *              @unsigned style@ = output style (@TVSF_...@)
1492  *              @const struct gprintf_ops *gops@, @void *gp@ = format output
1493  *
1494  * Returns:     ---
1495  *
1496  * Use:         Dump a register value to the format output.
1497  *
1498  *              Integer values are dumped in decimal and, unless compact
1499  *              output is requested, hex, and maybe a character, as a
1500  *              comment.
1501  */
1502
1503 static void dump_int(const union tvec_regval *rv,
1504                      const struct tvec_regdef *rd,
1505                      unsigned style,
1506                      const struct gprintf_ops *gops, void *go)
1507 {
1508   if (style&TVSF_RAW) gprintf(gops, go, "int:");
1509   gprintf(gops, go, "%ld", rv->i);
1510   if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
1511     gprintf(gops, go, " ; = ");
1512     format_signed_hex(gops, go, rv->i);
1513     maybe_format_signed_char(gops, go, rv->i);
1514   }
1515 }
1516
1517 static void dump_uint(const union tvec_regval *rv,
1518                       const struct tvec_regdef *rd,
1519                       unsigned style,
1520                       const struct gprintf_ops *gops, void *go)
1521 {
1522   if (style&TVSF_RAW) gprintf(gops, go, "uint:");
1523   gprintf(gops, go, "%lu", rv->u);
1524   if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
1525     gprintf(gops, go, " ; = ");
1526     format_unsigned_hex(gops, go, rv->u);
1527     maybe_format_unsigned_char(gops, go, rv->u);
1528   }
1529 }
1530
1531 /* Integer type definitions. */
1532 const struct tvec_regty tvty_int = {
1533   init_int, trivial_release, eq_int,
1534   tobuf_int, frombuf_int,
1535   parse_int, dump_int
1536 };
1537 const struct tvec_regty tvty_uint = {
1538   init_uint, trivial_release, eq_uint,
1539   tobuf_uint, frombuf_uint,
1540   parse_uint, dump_uint
1541 };
1542
1543 /* Predefined integer ranges. */
1544 const struct tvec_irange
1545   tvrange_schar = { SCHAR_MIN, SCHAR_MAX },
1546   tvrange_short = { SHRT_MIN, SHRT_MAX },
1547   tvrange_int = { INT_MIN, INT_MAX },
1548   tvrange_long = { LONG_MIN, LONG_MAX },
1549   tvrange_sbyte = { -128, 127 },
1550   tvrange_i16 = { -32768, +32767 },
1551   tvrange_i32 = { -2147483648, 2147483647 };
1552 const struct tvec_urange
1553   tvrange_uchar = { 0, UCHAR_MAX },
1554   tvrange_ushort = { 0, USHRT_MAX },
1555   tvrange_uint = { 0, UINT_MAX },
1556   tvrange_ulong = { 0, ULONG_MAX },
1557   tvrange_size = { 0, (size_t)-1 },
1558   tvrange_byte = { 0, 255 },
1559   tvrange_u16 = { 0, 65535 },
1560   tvrange_u32 = { 0, 4294967295 };
1561
1562 /* --- @tvec_claimeq_int@ --- *
1563  *
1564  * Arguments:   @struct tvec_state *tv@ = test-vector state
1565  *              @long i0, i1@ = two signed integers
1566  *              @const char *file@, @unsigned @lno@ = calling file and line
1567  *              @const char *expr@ = the expression to quote on failure
1568  *
1569  * Returns:     Nonzero if @i0@ and @i1@ are equal, otherwise zero.
1570  *
1571  * Use:         Check that values of @i0@ and @i1@ are equal.  As for
1572  *              @tvec_claim@ above, a test case is automatically begun and
1573  *              ended if none is already underway.  If the values are
1574  *              unequal, then @tvec_fail@ is called, quoting @expr@, and the
1575  *              mismatched values are dumped: @i0@ is printed as the output
1576  *              value and @i1@ is printed as the input reference.
1577  */
1578
1579 int tvec_claimeq_int(struct tvec_state *tv, long i0, long i1,
1580                      const char *file, unsigned lno, const char *expr)
1581 {
1582   tv->out[0].v.i = i0; tv->in[0].v.i = i1;
1583   return (tvec_claimeq(tv, &tvty_int, 0, file, lno, expr));
1584 }
1585
1586 /* --- @tvec_claimeq_uint@ --- *
1587  *
1588  * Arguments:   @struct tvec_state *tv@ = test-vector state
1589  *              @unsigned long u0, u1@ = two unsigned integers
1590  *              @const char *file@, @unsigned @lno@ = calling file and line
1591  *              @const char *expr@ = the expression to quote on failure
1592  *
1593  * Returns:     Nonzero if @u0@ and @u1@ are equal, otherwise zero.
1594  *
1595  * Use:         Check that values of @u0@ and @u1@ are equal.  As for
1596  *              @tvec_claim@ above, a test case is automatically begun and
1597  *              ended if none is already underway.  If the values are
1598  *              unequal, then @tvec_fail@ is called, quoting @expr@, and the
1599  *              mismatched values are dumped: @u0@ is printed as the output
1600  *              value and @u1@ is printed as the input reference.
1601  */
1602
1603 int tvec_claimeq_uint(struct tvec_state *tv,
1604                       unsigned long u0, unsigned long u1,
1605                       const char *file, unsigned lno, const char *expr)
1606 {
1607   tv->out[0].v.u = u0; tv->in[0].v.u = u1;
1608   return (tvec_claimeq(tv, &tvty_uint, 0, file, lno, expr));
1609 }
1610
1611 /*----- Size type ---------------------------------------------------------*/
1612
1613 /* --- @parse_size@ --- *
1614  *
1615  * Arguments:   @union tvec_regval *rv@ = register value
1616  *              @const struct tvec_regdef *rd@ = register definition
1617  *              @struct tvec_state *tv@ = test-vector state
1618  *
1619  * Returns:     Zero on success, %$-1$% on error.
1620  *
1621  * Use:         Parse a register value from an input file.
1622  *
1623  *              The input format for a size value consists of an unsigned
1624  *              integer followed by an optional unit specifier consisting of
1625  *              an SI unit prefix and (optionally) the letter `B'. */
1626
1627 static int parse_size(union tvec_regval *rv, const struct tvec_regdef *rd,
1628                       struct tvec_state *tv)
1629 {
1630   unsigned long sz;
1631   int rc;
1632
1633   if (parse_szint(tv, &sz, ";", "size")) { rc = -1; goto end; }
1634   if (check_unsigned_range(sz, rd->arg.p, tv, "size")) { rc = -1; goto end; }
1635   if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
1636   rv->u = sz; rc = 0;
1637 end:
1638   return (rc);
1639 }
1640
1641 /* --- @dump_size@ --- *
1642  *
1643  * Arguments:   @const union tvec_regval *rv@ = register value
1644  *              @const struct tvec_regdef *rd@ = register definition
1645  *              @unsigned style@ = output style (@TVSF_...@)
1646  *              @const struct gprintf_ops *gops@, @void *gp@ = format output
1647  *
1648  * Returns:     ---
1649  *
1650  * Use:         Dump a register value to the format output.
1651  *
1652  *              Size values are dumped with a unit specifier, with a unit
1653  *              prefox only if the size is an exact multiple of the relevant
1654  *              power of two.  Unless compact style is requested, the plain
1655  *              decimal and hex representations of the value are also
1656  *              printed.
1657  */
1658
1659 static void dump_size(const union tvec_regval *rv,
1660                       const struct tvec_regdef *rd,
1661                       unsigned style,
1662                       const struct gprintf_ops *gops, void *go)
1663 {
1664   if (style&TVSF_RAW) gprintf(gops, go, "size:");
1665   format_size(gops, go, rv->u, style);
1666   if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
1667     gprintf(gops, go, " ; = %lu", (unsigned long)rv->u);
1668     gprintf(gops, go, " = "); format_unsigned_hex(gops, go, rv->u);
1669     maybe_format_unsigned_char(gops, go, rv->u);
1670   }
1671 }
1672
1673 /* Size type definitions. */
1674 const struct tvec_regty tvty_size = {
1675   init_uint, trivial_release, eq_uint,
1676   tobuf_uint, frombuf_uint,
1677   parse_size, dump_size
1678 };
1679
1680 /* --- @tvec_claimeq_size@ --- *
1681  *
1682  * Arguments:   @struct tvec_state *tv@ = test-vector state
1683  *              @unsigned long sz0, sz1@ = two sizes
1684  *              @const char *file@, @unsigned @lno@ = calling file and line
1685  *              @const char *expr@ = the expression to quote on failure
1686  *
1687  * Returns:     Nonzero if @sz0@ and @sz1@ are equal, otherwise zero.
1688  *
1689  * Use:         Check that values of @u0@ and @u1@ are equal.  As for
1690  *              @tvec_claim@ above, a test case is automatically begun and
1691  *              ended if none is already underway.  If the values are
1692  *              unequal, then @tvec_fail@ is called, quoting @expr@, and the
1693  *              mismatched values are dumped: @u0@ is printed as the output
1694  *              value and @u1@ is printed as the input reference.
1695  */
1696
1697 int tvec_claimeq_size(struct tvec_state *tv,
1698                       unsigned long sz0, unsigned long sz1,
1699                       const char *file, unsigned lno, const char *expr)
1700 {
1701   tv->out[0].v.u = sz0; tv->in[0].v.u = sz1;
1702   return (tvec_claimeq(tv, &tvty_size, 0, file, lno, expr));
1703 }
1704
1705 /*----- Floating-point type -----------------------------------------------*/
1706
1707 /* --- @int_float@ --- *
1708  *
1709  * Arguments:   @union tvec_regval *rv@ = register value
1710  *              @const struct tvec_regdef *rd@ = register definition
1711  *
1712  * Returns:     ---
1713  *
1714  * Use:         Initialize a register value.
1715  *
1716  *              Floating-point values are initialized to zero.
1717  */
1718
1719 static void init_float(union tvec_regval *rv, const struct tvec_regdef *rd)
1720   { rv->f = 0.0; }
1721
1722 /* --- @eq_float@ --- *
1723  *
1724  * Arguments:   @const union tvec_regval *rv0, *rv1@ = register values
1725  *              @const struct tvec_regdef *rd@ = register definition
1726  *
1727  * Returns:     Nonzero if the values are equal, zero if unequal
1728  *
1729  * Use:         Compare register values for equality.
1730  *
1731  *              Floating-point values may be considered equal if their
1732  *              absolute or relative difference is sufficiently small, as
1733  *              described in the register definition.
1734  */
1735
1736 static int eq_float(const union tvec_regval *rv0,
1737                     const union tvec_regval *rv1,
1738                     const struct tvec_regdef *rd)
1739   { return (eqish_floating_p(rv0->f, rv1->f, rd->arg.p)); }
1740
1741 /* --- @tobuf_float@ --- *
1742  *
1743  * Arguments:   @buf *b@ = buffer
1744  *              @const union tvec_regval *rv@ = register value
1745  *              @const struct tvec_regdef *rd@ = register definition
1746  *
1747  * Returns:     Zero on success, %$-1$% on failure.
1748  *
1749  * Use:         Serialize a register value to a buffer.
1750  *
1751  *              Floating-point values are serialized as little-endian
1752  *              IEEE 754 Binary64.
1753  */
1754
1755 static int tobuf_float(buf *b, const union tvec_regval *rv,
1756                      const struct tvec_regdef *rd)
1757   { return (buf_putf64l(b, rv->f)); }
1758
1759 /* --- @frombuf_float@ --- *
1760  *
1761  * Arguments:   @buf *b@ = buffer
1762  *              @union tvec_regval *rv@ = register value
1763  *              @const struct tvec_regdef *rd@ = register definition
1764  *
1765  * Returns:     Zero on success, %$-1$% on failure.
1766  *
1767  * Use:         Deserialize a register value from a buffer.
1768  *
1769  *              Floating-point values are serialized as little-endian
1770  *              IEEE 754 Binary64.
1771  */
1772
1773 static int frombuf_float(buf *b, union tvec_regval *rv,
1774                        const struct tvec_regdef *rd)
1775   { return (buf_getf64l(b, &rv->f)); }
1776
1777 /* --- @parse_float@ --- *
1778  *
1779  * Arguments:   @union tvec_regval *rv@ = register value
1780  *              @const struct tvec_regdef *rd@ = register definition
1781  *              @struct tvec_state *tv@ = test-vector state
1782  *
1783  * Returns:     Zero on success, %$-1$% on error.
1784  *
1785  * Use:         Parse a register value from an input file.
1786  *
1787  *              Floating-point values are either NaN (%|#nan|%, if supported
1788  *              by the platform); positive or negative infinity (%|#inf|%,
1789  *              %|+#inf|%, or %|#+inf|% (preferring the last), and %|-#inf|%
1790  *              or %|#-inf|% (preferring the latter), if supported by the
1791  *              platform); or a number in strtod(3) syntax.
1792  */
1793
1794 static int parse_float(union tvec_regval *rv, const struct tvec_regdef *rd,
1795                        struct tvec_state *tv)
1796 {
1797   dstr d = DSTR_INIT;
1798   int rc;
1799
1800   if (tvec_readword(tv, &d, 0, ";", "floating-point number"))
1801     { rc = -1; goto end; }
1802   if (parse_floating(&rv->f, 0, d.buf, rd->arg.p, tv))
1803     { rc = -1; goto end; }
1804   if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
1805   rc = 0;
1806 end:
1807   dstr_destroy(&d);
1808   return (rc);
1809 }
1810
1811 /* --- @dump_float@ --- *
1812  *
1813  * Arguments:   @const union tvec_regval *rv@ = register value
1814  *              @const struct tvec_regdef *rd@ = register definition
1815  *              @unsigned style@ = output style (@TVSF_...@)
1816  *              @const struct gprintf_ops *gops@, @void *gp@ = format output
1817  *
1818  * Returns:     ---
1819  *
1820  * Use:         Dump a register value to the format output.
1821  *
1822  *              Floating-point values are dumped in decimal or as a special
1823  *              token beginning with `%|#|%'.  Some effort is taken to ensure
1824  *              that the output is sufficient to uniquely identify the
1825  *              original value, but, honestly, C makes this really hard.
1826  */
1827
1828 static void dump_float(const union tvec_regval *rv,
1829                        const struct tvec_regdef *rd,
1830                        unsigned style,
1831                        const struct gprintf_ops *gops, void *go)
1832 {
1833   if (style&TVSF_RAW) gprintf(gops, go, "float:");
1834   format_floating(gops, go, rv->f);
1835 }
1836
1837 /* Floating-point type definition. */
1838 const struct tvec_regty tvty_float = {
1839   init_float, trivial_release, eq_float,
1840   tobuf_float, frombuf_float,
1841   parse_float, dump_float
1842 };
1843
1844 /* Predefined floating-point ranges. */
1845 const struct tvec_floatinfo
1846   tvflt_finite = { TVFF_EXACT, -DBL_MAX, DBL_MAX, 0.0 },
1847   tvflt_nonneg = { TVFF_EXACT, 0, DBL_MAX, 0.0 };
1848
1849 /* --- @tvec_claimeqish_float@ --- *
1850  *
1851  * Arguments:   @struct tvec_state *tv@ = test-vector state
1852  *              @double f0, f1@ = two floating-point numbers
1853  *              @unsigned f@ = flags (@TVFF_...@)
1854  *              @double delta@ = maximum tolerable difference
1855  *              @const char *file@, @unsigned @lno@ = calling file and line
1856  *              @const char *expr@ = the expression to quote on failure
1857  *
1858  * Returns:     Nonzero if @f0@ and @f1@ are sufficiently close, otherwise
1859  *              zero.
1860  *
1861  * Use:         Check that values of @f0@ and @f1@ are sufficiently close.
1862  *              As for @tvec_claim@ above, a test case is automatically begun
1863  *              and ended if none is already underway.  If the values are
1864  *              too far apart, then @tvec_fail@ is called, quoting @expr@,
1865  *              and the mismatched values are dumped: @f0@ is printed as the
1866  *              output value and @f1@ is printed as the input reference.
1867  *
1868  *              The details for the comparison are as follows.
1869  *
1870  *                * A NaN value matches any other NaN, and nothing else.
1871  *
1872  *                * An infinity matches another infinity of the same sign,
1873  *                  and nothing else.
1874  *
1875  *                * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any
1876  *                  representable number matches only itself: in particular,
1877  *                  positive and negative zero are considered distinct.
1878  *                  (This allows tests to check that they land on the correct
1879  *                  side of branch cuts, for example.)
1880  *
1881  *                * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches
1882  *                  %$y$% when %$|x - y| < \delta$%.
1883  *
1884  *                * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
1885  *                  %$y$% when %$|1 - x/y| < \delta$%.  (Note that this
1886  *                  criterion is asymmetric.  Write %$x \approx_\delta y$%
1887  *                  if and only if %$|1 - x/y < \delta$%.  Then, for example,
1888  *                  if %$y/(1 + \delta) < x < y (1 - \delta)$%, then
1889  *                  %$x \approx_\delta y$%, but %$y \not\approx_\delta x$%.)
1890  */
1891
1892 int tvec_claimeqish_float(struct tvec_state *tv,
1893                           double f0, double f1, unsigned f, double delta,
1894                           const char *file, unsigned lno,
1895                           const char *expr)
1896 {
1897   struct tvec_floatinfo fi;
1898   union tvec_misc arg;
1899
1900   fi.f = f; fi.min = fi.max = 0.0; fi.delta = delta; arg.p = &fi;
1901   tv->out[0].v.f = f0; tv->in[0].v.f = f1;
1902   return (tvec_claimeq(tv, &tvty_float, &arg, file, lno, expr));
1903 }
1904
1905 /* --- @tvec_claimeq_float@ --- *
1906  *
1907  * Arguments:   @struct tvec_state *tv@ = test-vector state
1908  *              @double f0, f1@ = two floating-point numbers
1909  *              @const char *file@, @unsigned @lno@ = calling file and line
1910  *              @const char *expr@ = the expression to quote on failure
1911  *
1912  * Returns:     Nonzero if @f0@ and @f1@ are identical, otherwise zero.
1913  *
1914  * Use:         Check that values of @f0@ and @f1@ are identical.  The
1915  *              function is exactly equivalent to @tvec_claimeqish_float@
1916  *              with @f == TVFF_EXACT@.
1917  */
1918
1919 int tvec_claimeq_float(struct tvec_state *tv,
1920                        double f0, double f1,
1921                        const char *file, unsigned lno,
1922                        const char *expr)
1923 {
1924   return (tvec_claimeqish_float(tv, f0, f1, TVFF_EXACT, 0.0,
1925                                 file, lno, expr));
1926 }
1927
1928 /*----- Durations ---------------------------------------------------------*/
1929
1930 /* A duration is a floating-point number of seconds.  Initialization and
1931  * teardown, equality comparison, and serialization are as for floating-point
1932  * values.
1933  */
1934
1935 static const struct duration_unit {
1936   const char *unit;
1937   double scale;
1938   unsigned f;
1939 #define DUF_PREFER 1u
1940 } duration_units[] = {
1941   { "Ys",       1e+24,          0 },
1942   { "Zs",       1e+21,          0 },
1943   { "Es",       1e+18,          0 },
1944   { "Ps",       1e+15,          0 },
1945   { "Ts",       1e+12,          0 },
1946   { "Gs",       1e+9,           0 },
1947   { "Ms",       1e+6,           0 },
1948   { "ks",       1e+3,           0 },
1949   { "hs",       1e+2,           0 },
1950   { "das",      1e+1,           0 },
1951
1952   { "yr",       31557600.0,     DUF_PREFER },
1953   { "y",        31557600.0,     0 },
1954   { "day",      86400.0,        DUF_PREFER },
1955   { "dy",       86400.0,        0 },
1956   { "d",        86400.0,        0 },
1957   { "hr",       3600.0,         DUF_PREFER },
1958   { "hour",     3600.0,         0 },
1959   { "h",        3600.0,         0 },
1960   { "min",      60.0,           DUF_PREFER },
1961   { "m",        60.0,           0 },
1962
1963   { "s",        1.0,            DUF_PREFER },
1964   { "sec",      1.0,            0 },
1965
1966   { "ds",       1e-1,           0 },
1967   { "cs",       1e-2,           0 },
1968   { "ms",       1e-3,           DUF_PREFER },
1969   { "µs",      1e-6,           DUF_PREFER },
1970   { "ns",       1e-9,           DUF_PREFER },
1971   { "ps",       1e-12,          DUF_PREFER },
1972   { "fs",       1e-15,          DUF_PREFER },
1973   { "as",       1e-18,          DUF_PREFER },
1974   { "zs",       1e-21,          DUF_PREFER },
1975   { "ys",       1e-24,          DUF_PREFER },
1976
1977   { 0 }
1978 };
1979
1980 /* --- @tvec_parsedurunit@ --- *
1981  *
1982  * Arguments:   @double *scale_out@ = where to leave the scale
1983  *              @const char **p_inout@ = input unit string, updated
1984  *
1985  * Returns:     Zero on success, %$-1$% on error.
1986  *
1987  * Use:         If @*p_inout@ begins with a unit string followed by the end
1988  *              of the string or some non-alphanumeric character, then store
1989  *              the corresponding scale factor in @*scale_out@, advance
1990  *              @*p_inout@ past the unit string, and return zero.  Otherwise,
1991  *              return %$-1$%.
1992  */
1993
1994 int tvec_parsedurunit(double *scale_out, const char **p_inout)
1995 {
1996   const char *p = *p_inout, *q;
1997   const struct duration_unit *u;
1998   size_t n;
1999
2000   while (ISSPACE(*p)) p++;
2001   for (q = p; *q && ISALNUM(*q); q++);
2002   n = q - p; if (!n) { *scale_out = 1.0; return (0); }
2003
2004   for (u = duration_units; u->unit; u++)
2005     if (STRNCMP(p, ==, u->unit, n) && !u->unit[n])
2006       { *scale_out = u->scale; *p_inout = q; return (0); }
2007   return (-1);
2008 }
2009
2010 /* --- @parse_duration@ --- *
2011  *
2012  * Arguments:   @union tvec_regval *rv@ = register value
2013  *              @const struct tvec_regdef *rd@ = register definition
2014  *              @struct tvec_state *tv@ = test-vector state
2015  *
2016  * Returns:     Zero on success, %$-1$% on error.
2017  *
2018  * Use:         Parse a register value from an input file.
2019  *
2020  *              Duration values are finite nonnegative floating-point
2021  *              numbers in @strtod@ syntax, optionally followed by a unit .
2022  */
2023
2024 static int parse_duration(union tvec_regval *rv,
2025                           const struct tvec_regdef *rd,
2026                           struct tvec_state *tv)
2027 {
2028   const struct duration_unit *u;
2029   const char *q;
2030   dstr d = DSTR_INIT;
2031   double t;
2032   int rc;
2033
2034   if (tvec_readword(tv, &d, 0, ";", "duration")) { rc = -1; goto end; }
2035   if (parse_floating(&t, &q, d.buf,
2036                      rd->arg.p ? rd->arg.p : &tvflt_nonneg, tv))
2037     { rc = -1; goto end; }
2038
2039   if (!*q) tvec_readword(tv, &d, &q, ";", 0);
2040   if (*q) {
2041     for (u = duration_units; u->unit; u++)
2042       if (STRCMP(q, ==, u->unit)) { t *= u->scale; goto found_unit; }
2043     rc = tvec_syntax(tv, *q, "end-of-line"); goto end;
2044   found_unit:;
2045   }
2046
2047   if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
2048   rv->f = t; rc = 0;
2049 end:
2050   dstr_destroy(&d);
2051   return (rc);
2052 }
2053
2054 /* --- @dump_duration@ --- *
2055  *
2056  * Arguments:   @const union tvec_regval *rv@ = register value
2057  *              @const struct tvec_regdef *rd@ = register definition
2058  *              @unsigned style@ = output style (@TVSF_...@)
2059  *              @const struct gprintf_ops *gops@, @void *gp@ = format output
2060  *
2061  * Returns:     ---
2062  *
2063  * Use:         Dump a register value to the format output.
2064  *
2065  *              Durations are dumped as a human-palatable scaled value with
2066  *              unit, and, if compact style is not requested, as a raw number
2067  *              of seconds at full precision as a comment.
2068  */
2069
2070 static void dump_duration(const union tvec_regval *rv,
2071                           const struct tvec_regdef *rd,
2072                           unsigned style,
2073                           const struct gprintf_ops *gops, void *go)
2074 {
2075   const struct duration_unit *u;
2076   double t = rv->f;
2077
2078   if (style&TVSF_RAW) {
2079     gprintf(gops, go, "duration:");
2080     format_floating(gops, go, rv->f);
2081     gprintf(gops, go, "s");
2082   } else {
2083     if (!t) u = 0;
2084     else {
2085       for (u = duration_units; u->scale > t && u[1].unit; u++);
2086       t /= u->scale;
2087     }
2088     gprintf(gops, go, "%.4g %s", t, u ? u->unit : "s");
2089
2090     if (!(style&TVSF_COMPACT)) {
2091       gprintf(gops, go, "; = ");
2092       format_floating(gops, go, rv->f);
2093       gprintf(gops, go, " s");
2094     }
2095   }
2096 }
2097
2098 /* Duration type definition. */
2099 const struct tvec_regty tvty_duration = {
2100   init_float, trivial_release, eq_float,
2101   tobuf_float, frombuf_float,
2102   parse_duration, dump_duration
2103 };
2104
2105 /* --- @tvec_claimeqish_duration@ --- *
2106  *
2107  * Arguments:   @struct tvec_state *tv@ = test-vector state
2108  *              @double to, t1@ = two durations
2109  *              @unsigned f@ = flags (@TVFF_...@)
2110  *              @double delta@ = maximum tolerable difference
2111  *              @const char *file@, @unsigned @lno@ = calling file and line
2112  *              @const char *expr@ = the expression to quote on failure
2113  *
2114  * Returns:     Nonzero if @t0@ and @t1@ are sufficiently close, otherwise
2115  *              zero.
2116  *
2117  * Use:         Check that values of @t0@ and @t1@ are sufficiently close.
2118  *              This is essentially the same as @tvec_claimeqish_float@, only
2119  *              it dumps the values as durations on a mismatch.
2120  */
2121
2122 int tvec_claimeqish_duration(struct tvec_state *tv,
2123                              double t0, double t1, unsigned f, double delta,
2124                              const char *file, unsigned lno,
2125                              const char *expr)
2126 {
2127   struct tvec_floatinfo fi;
2128   union tvec_misc arg;
2129
2130   fi.f = f; fi.min = fi.max = 0.0; fi.delta = delta; arg.p = &fi;
2131   tv->out[0].v.f = t0; tv->in[0].v.f = t1;
2132   return (tvec_claimeq(tv, &tvty_duration, &arg, file, lno, expr));
2133 }
2134
2135 /* --- @tvec_claimeq_duration@ --- *
2136  *
2137  * Arguments:   @struct tvec_state *tv@ = test-vector state
2138  *              @double t0, t1@ = two durations
2139  *              @const char *file@, @unsigned @lno@ = calling file and line
2140  *              @const char *expr@ = the expression to quote on failure
2141  *
2142  * Returns:     Nonzero if @t0@ and @t1@ are identical, otherwise zero.
2143  *
2144  * Use:         Check that values of @t0@ and @t1@ are identical.  The
2145  *              function is exactly equivalent to @tvec_claimeqish_duration@
2146  *              with @f == TVFF_EXACT@.
2147  */
2148
2149 int tvec_claimeq_duration(struct tvec_state *tv,
2150                           double t0, double t1,
2151                           const char *file, unsigned lno,
2152                           const char *expr)
2153 {
2154   return (tvec_claimeqish_duration(tv, t0, t1, TVFF_EXACT, 0.0,
2155                                    file, lno, expr));
2156 }
2157
2158 /*----- Enumerations ------------------------------------------------------*/
2159
2160 /* --- @init_tenum@ --- *
2161  *
2162  * Arguments:   @union tvec_regval *rv@ = register value
2163  *              @const struct tvec_regdef *rd@ = register definition
2164  *
2165  * Returns:     ---
2166  *
2167  * Use:         Initialize a register value.
2168  *
2169  *              Integer and floating-point enumeration values are initialized
2170  *              as their underlying representations.  Pointer enumerations
2171  *              are initialized to %|#nil|%.
2172  */
2173
2174 #define init_ienum init_int
2175 #define init_uenum init_uint
2176 #define init_fenum init_float
2177
2178 static void init_penum(union tvec_regval *rv, const struct tvec_regdef *rd)
2179   { rv->p = 0; }
2180
2181 /* --- @eq_tenum@ --- *
2182  *
2183  * Arguments:   @const union tvec_regval *rv0, *rv1@ = register values
2184  *              @const struct tvec_regdef *rd@ = register definition
2185  *
2186  * Returns:     Nonzero if the values are equal, zero if unequal
2187  *
2188  * Use:         Compare register values for equality.
2189  *
2190  *              Integer and floating-point enumeration values are compared as
2191  *              their underlying representations; in particular, floating-
2192  *              point enumerations may compare equal if their absolute or
2193  *              relative difference is sufficiently small.  Pointer
2194  *              enumerations are compared as pointers.
2195  */
2196
2197 #define eq_ienum eq_int
2198 #define eq_uenum eq_uint
2199
2200 static int eq_fenum(const union tvec_regval *rv0,
2201                     const union tvec_regval *rv1,
2202                     const struct tvec_regdef *rd)
2203 {
2204   const struct tvec_fenuminfo *ei = rd->arg.p;
2205   return (eqish_floating_p(rv0->f, rv1->f, ei->fi));
2206 }
2207
2208 static int eq_penum(const union tvec_regval *rv0,
2209                     const union tvec_regval *rv1,
2210                     const struct tvec_regdef *rd)
2211   { return (rv0->p == rv1->p); }
2212
2213 /* --- @tobuf_tenum@ --- *
2214  *
2215  * Arguments:   @buf *b@ = buffer
2216  *              @const union tvec_regval *rv@ = register value
2217  *              @const struct tvec_regdef *rd@ = register definition
2218  *
2219  * Returns:     Zero on success, %$-1$% on failure.
2220  *
2221  * Use:         Serialize a register value to a buffer.
2222  *
2223  *              Integer and floating-point enumeration values are serialized
2224  *              as their underlying representations.  Pointer enumerations
2225  *              are serialized as the signed integer index into the
2226  *              association table; %|#nil|% serializes as %$-1$%, and
2227  *              unrecognized pointers cause failure.
2228  */
2229
2230 #define tobuf_ienum tobuf_int
2231 #define tobuf_uenum tobuf_uint
2232 #define tobuf_fenum tobuf_float
2233
2234 static int tobuf_penum(buf *b, const union tvec_regval *rv,
2235                        const struct tvec_regdef *rd)
2236 {
2237   const struct tvec_penuminfo *pei = rd->arg.p;
2238   const struct tvec_passoc *pa;
2239   long i;
2240
2241   for (pa = pei->av, i = 0; pa->tag; pa++, i++)
2242     if (pa->p == rv->p) goto found;
2243   if (!rv->p) i = -1;
2244   else return (-1);
2245 found:
2246   return (signed_to_buf(b, i));
2247 }
2248
2249 /* --- @frombuf_tenum@ --- *
2250  *
2251  * Arguments:   @buf *b@ = buffer
2252  *              @union tvec_regval *rv@ = register value
2253  *              @const struct tvec_regdef *rd@ = register definition
2254  *
2255  * Returns:     Zero on success, %$-1$% on failure.
2256  *
2257  * Use:         Deserialize a register value from a buffer.
2258  *
2259  *              Integer and floating-point enumeration values are serialized
2260  *              as their underlying representations.  Pointer enumerations
2261  *              are serialized as the signed integer index into the
2262  *              association table; %|#nil|% serializes as %$-1$%; out-of-
2263  *              range indices cause failure.
2264  */
2265
2266 #define frombuf_ienum frombuf_int
2267 #define frombuf_uenum frombuf_uint
2268 #define frombuf_fenum frombuf_float
2269 static int frombuf_penum(buf *b, union tvec_regval *rv,
2270                         const struct tvec_regdef *rd)
2271 {
2272   const struct tvec_penuminfo *pei = rd->arg.p;
2273   const struct tvec_passoc *pa;
2274   long i, n;
2275
2276   for (pa = pei->av, n = 0; pa->tag; pa++, n++);
2277   if (signed_from_buf(b, &i)) return (-1);
2278   if (0 <= i && i < n) rv->p = (/*unconst*/ void *)pei->av[i].p;
2279   else if (i == -1) rv->p = 0;
2280   else { buf_break(b); return (-1); }
2281   return (0);
2282 }
2283
2284 /* --- @parse_tenum@ --- *
2285  *
2286  * Arguments:   @union tvec_regval *rv@ = register value
2287  *              @const struct tvec_regdef *rd@ = register definition
2288  *              @struct tvec_state *tv@ = test-vector state
2289  *
2290  * Returns:     Zero on success, %$-1$% on error.
2291  *
2292  * Use:         Parse a register value from an input file.
2293  *
2294  *              An enumerated value may be given by name or as a literal
2295  *              value.  For enumerations based on numeric types, the literal
2296  *              values can be written in the same syntax as the underlying
2297  *              values.  For enumerations based on pointers, the only
2298  *              permitted literal is %|#nil|%, which denotes a null pointer.
2299  */
2300
2301 #define DEFPARSE_ENUM(tag_, ty, slot)                                   \
2302   static int parse_##slot##enum(union tvec_regval *rv,                  \
2303                                 const struct tvec_regdef *rd,           \
2304                                 struct tvec_state *tv)                  \
2305   {                                                                     \
2306     const struct tvec_##slot##enuminfo *ei = rd->arg.p;                 \
2307     const struct tvec_##slot##assoc *a;                                 \
2308     dstr d = DSTR_INIT;                                                 \
2309     int rc;                                                             \
2310                                                                         \
2311     if (tvec_readword(tv, &d, 0,                                        \
2312                       ";", "%s tag or " LITSTR_##tag_, ei->name))       \
2313       { rc = -1; goto end; }                                            \
2314     for (a = ei->av; a->tag; a++)                                       \
2315       if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; }        \
2316     MISSING_##tag_                                                      \
2317     done:                                                               \
2318     if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }                  \
2319     rc = 0;                                                             \
2320   end:                                                                  \
2321     dstr_destroy(&d);                                                   \
2322     return (rc);                                                        \
2323   }
2324
2325 #define LITSTR_INT      "literal signed integer"
2326 #define FOUND_INT       rv->i = a->i;
2327 #define MISSING_INT     if (parse_signed(&rv->i, d.buf, ei->ir, tv))    \
2328                           { rc = -1; goto end; }
2329
2330 #define LITSTR_UINT     "literal unsigned integer"
2331 #define FOUND_UINT      rv->u = a->u;
2332 #define MISSING_UINT    if (parse_unsigned(&rv->u, d.buf, ei->ur, tv))  \
2333                           { rc = -1; goto end; }
2334
2335 #define LITSTR_FLT      "literal floating-point number, "               \
2336                           "`#-inf', `#+inf', or `#nan'"
2337 #define FOUND_FLT       rv->f = a->f;
2338 #define MISSING_FLT     if (parse_floating(&rv->f, 0, d.buf, ei->fi, tv)) \
2339                           { rc = -1; goto end; }
2340
2341 #define LITSTR_PTR      "`#nil'"
2342 #define FOUND_PTR       rv->p = (/*unconst*/ void *)a->p;
2343 #define MISSING_PTR     if (STRCMP(d.buf, ==, "#nil"))                  \
2344                           rv->p = 0;                                    \
2345                         else {                                          \
2346                           tvec_error(tv, "unknown `%s' value `%s'",     \
2347                                      ei->name, d.buf);                  \
2348                           rc = -1; goto end;                            \
2349                         }
2350
2351 TVEC_MISCSLOTS(DEFPARSE_ENUM)
2352
2353 #undef LITSTR_INT
2354 #undef FOUND_INT
2355 #undef MISSING_INT
2356
2357 #undef LITSTR_UINT
2358 #undef FOUND_UINT
2359 #undef MISSING_UINT
2360
2361 #undef LITSTR_FLT
2362 #undef FOUND_FLT
2363 #undef MISSING_FLT
2364
2365 #undef LITSTR_PTR
2366 #undef FOUND_PTR
2367 #undef MISSING_PTR
2368
2369 #undef DEFPARSE_ENUM
2370
2371 /* --- @dump_tenum@ --- *
2372  *
2373  * Arguments:   @const union tvec_regval *rv@ = register value
2374  *              @const struct tvec_regdef *rd@ = register definition
2375  *              @unsigned style@ = output style (@TVSF_...@)
2376  *              @const struct gprintf_ops *gops@, @void *gp@ = format output
2377  *
2378  * Returns:     ---
2379  *
2380  * Use:         Dump a register value to the format output.
2381  *
2382  *              Enumeration values are dumped as their symbolic names, if
2383  *              possible, with the underlying values provided as a comment
2384  *              unless compact output is requested, as for the underlying
2385  *              representation.  A null pointer is printed as %|#nil|%;
2386  *              non-null pointers are printed as %|#<TYPE PTR>|%, with the
2387  *              enumeration TYPE and the raw pointer PTR printed with the
2388  *              system's %|%p|% format specifier.
2389  */
2390
2391
2392 #define DEFDUMP_ENUM(tag_, ty, slot)                                    \
2393   static void dump_##slot##enum(const union tvec_regval *rv,            \
2394                                 const struct tvec_regdef *rd,           \
2395                                 unsigned style,                         \
2396                                 const struct gprintf_ops *gops, void *go) \
2397   {                                                                     \
2398     const struct tvec_##slot##enuminfo *ei = rd->arg.p;                 \
2399     const struct tvec_##slot##assoc *a;                                 \
2400                                                                         \
2401     if (style&TVSF_RAW) gprintf(gops, go, #slot "enum/%s:", ei->name);  \
2402     for (a = ei->av; a->tag; a++)                                       \
2403       if (rv->slot == a->slot) {                                        \
2404         gprintf(gops, go, "%s", a->tag);                                \
2405         if (style&TVSF_COMPACT) return;                                 \
2406         gprintf(gops, go, " ; = "); break;                              \
2407       }                                                                 \
2408                                                                         \
2409     PRINTRAW_##tag_                                                     \
2410   }
2411
2412 #define MAYBE_PRINT_EXTRA                                               \
2413         if (style&TVSF_COMPACT) /* nothing to do */;                    \
2414         else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; }  \
2415         else if (1) { gprintf(gops, go, " = "); goto _extra; }          \
2416         else _extra:
2417
2418 #define PRINTRAW_INT    gprintf(gops, go, "%ld", rv->i);                \
2419                         MAYBE_PRINT_EXTRA {                             \
2420                           format_signed_hex(gops, go, rv->i);           \
2421                           maybe_format_signed_char(gops, go, rv->i);    \
2422                         }
2423
2424 #define PRINTRAW_UINT   gprintf(gops, go, "%lu", rv->u);                \
2425                         MAYBE_PRINT_EXTRA {                             \
2426                           format_unsigned_hex(gops, go, rv->u);         \
2427                           maybe_format_unsigned_char(gops, go, rv->u);  \
2428                         }
2429
2430 #define PRINTRAW_FLT    format_floating(gops, go, rv->f);
2431
2432 #define PRINTRAW_PTR    if (!rv->p) gprintf(gops, go, "#nil");          \
2433                         else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
2434
2435 TVEC_MISCSLOTS(DEFDUMP_ENUM)
2436
2437 #undef PRINTRAW_INT
2438 #undef PRINTRAW_UINT
2439 #undef PRINTRAW_FLT
2440 #undef PRINTRAW_PTR
2441
2442 #undef MAYBE_PRINT_EXTRA
2443 #undef DEFDUMP_ENUM
2444
2445 /* Enumeration type definitions. */
2446 #define DEFTY_ENUM(tag, ty, slot)                                       \
2447   const struct tvec_regty tvty_##slot##enum = {                         \
2448     init_##slot##enum, trivial_release, eq_##slot##enum,                \
2449     tobuf_##slot##enum, frombuf_##slot##enum,                           \
2450     parse_##slot##enum, dump_##slot##enum                               \
2451   };
2452 TVEC_MISCSLOTS(DEFTY_ENUM)
2453 #undef DEFTY_ENUM
2454
2455 /* Predefined enumeration types. */
2456 static const struct tvec_iassoc bool_assoc[] = {
2457   { "nil",              0 },
2458   { "false",            0 },
2459   { "f",                0 },
2460   { "no",               0 },
2461   { "n",                0 },
2462   { "off",              0 },
2463
2464   { "t",                1 },
2465   { "true",             1 },
2466   { "yes",              1 },
2467   { "y",                1 },
2468   { "on",               1 },
2469
2470   TVEC_ENDENUM
2471 };
2472
2473 const struct tvec_ienuminfo tvenum_bool =
2474   { "bool", bool_assoc, &tvrange_int };
2475
2476 static const struct tvec_iassoc cmp_assoc[] = {
2477   { "<",                -1 },
2478   { "less",             -1 },
2479   { "lt",               -1 },
2480
2481   { "=",                 0 },
2482   { "equal",             0 },
2483   { "eq",                0 },
2484
2485   { ">",                +1 },
2486   { "greater",          +1 },
2487   { "gt",               +1 },
2488
2489   TVEC_ENDENUM
2490 };
2491
2492 const struct tvec_ienuminfo tvenum_cmp =
2493   { "cmp", cmp_assoc, &tvrange_int };
2494
2495 /* --- @tvec_claimeq_tenum@ --- *
2496  *
2497  * Arguments:   @struct tvec_state *tv@ = test-vector state
2498  *              @const struct tvec_typeenuminfo *ei@ = enumeration type info
2499  *              @ty t0, t1@ = two values
2500  *              @const char *file@, @unsigned @lno@ = calling file and line
2501  *              @const char *expr@ = the expression to quote on failure
2502  *
2503  * Returns:     Nonzero if @t0@ and @t1@ are equal, otherwise zero.
2504  *
2505  * Use:         Check that values of @t0@ and @t1@ are equal.  As for
2506  *              @tvec_claim@ above, a test case is automatically begun and
2507  *              ended if none is already underway.  If the values are
2508  *              unequal, then @tvec_fail@ is called, quoting @expr@, and the
2509  *              mismatched values are dumped: @t0@ is printed as the output
2510  *              value and @t1@ is printed as the input reference.
2511  */
2512
2513 #define DEFCLAIM(tag, ty, slot)                                         \
2514         int tvec_claimeq_##slot##enum                                   \
2515           (struct tvec_state *tv,                                       \
2516            const struct tvec_##slot##enuminfo *ei, ty e0, ty e1,        \
2517            const char *file, unsigned lno, const char *expr)            \
2518         {                                                               \
2519           union tvec_misc arg;                                          \
2520                                                                         \
2521           arg.p = ei;                                                   \
2522           tv->out[0].v.slot = GET_##tag(e0);                            \
2523           tv->in[0].v.slot = GET_##tag(e1);                             \
2524           return (tvec_claimeq(tv, &tvty_##slot##enum, &arg,            \
2525                                file, lno, expr));                       \
2526         }
2527 #define GET_INT(e) (e)
2528 #define GET_UINT(e) (e)
2529 #define GET_FLT(e) (e)
2530 #define GET_PTR(e) ((/*unconst*/ void *)(e))
2531 TVEC_MISCSLOTS(DEFCLAIM)
2532 #undef DEFCLAIM
2533 #undef GET_INT
2534 #undef GET_UINT
2535 #undef GET_FLT
2536 #undef GET_PTR
2537
2538 /*----- Flag types --------------------------------------------------------*/
2539
2540 /* Flag types are initialized, compared, and serialized as unsigned
2541  * integers.
2542  */
2543
2544 /* --- @parse_flags@ --- *
2545  *
2546  * Arguments:   @union tvec_regval *rv@ = register value
2547  *              @const struct tvec_regdef *rd@ = register definition
2548  *              @struct tvec_state *tv@ = test-vector state
2549  *
2550  * Returns:     Zero on success, %$-1$% on error.
2551  *
2552  * Use:         Parse a register value from an input file.
2553  *
2554  *              The input syntax is a sequence of items separated by `|'
2555  *              signs.  Each item may be the symbolic name of a field value,
2556  *              or a literal unsigned integer.  The masks associated with the
2557  *              given symbolic names must be disjoint.  The resulting
2558  *              numerical value is simply the bitwise OR of the given values.
2559  */
2560
2561 static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
2562                        struct tvec_state *tv)
2563 {
2564   const struct tvec_flaginfo *fi = rd->arg.p;
2565   const struct tvec_flag *f;
2566   unsigned long m = 0, v = 0, t;
2567   dstr d = DSTR_INIT;
2568   int ch, rc;
2569
2570   for (;;) {
2571
2572     /* Read the next item. */
2573     DRESET(&d);
2574     if (tvec_readword(tv, &d, 0, "|;", "%s flag name or integer", fi->name))
2575       { rc = -1; goto end; }
2576
2577     /* Try to find a matching entry in the table. */
2578     for (f = fi->fv; f->tag; f++)
2579       if (STRCMP(f->tag, ==, d.buf)) {
2580         if (m&f->m)
2581           { tvec_error(tv, "colliding flag setting"); rc = -1; goto end; }
2582         else
2583           { m |= f->m; v |= f->v; goto next; }
2584       }
2585
2586     /* Otherwise, try to parse it as a raw integer. */
2587     if (parse_unsigned(&t, d.buf, fi->range, tv))
2588       { rc = -1; goto end; }
2589     v |= t;
2590
2591   next:
2592     /* Advance to the next token.  If it's a separator then consume it, and
2593      * go round again.  Otherwise we stop here.
2594      */
2595     if (tvec_nexttoken(tv)) break;
2596     ch = getc(tv->fp);
2597       if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; }
2598       if (tvec_nexttoken(tv)) {
2599         tvec_syntax(tv, '\n', "%s flag name or integer", fi->name);
2600         rc = -1; goto end;
2601       }
2602   }
2603
2604   /* Done. */
2605   rv->u = v; rc = 0;
2606 end:
2607   dstr_destroy(&d);
2608   return (rc);
2609 }
2610
2611 /* --- @dump_flags@ --- *
2612  *
2613  * Arguments:   @const union tvec_regval *rv@ = register value
2614  *              @const struct tvec_regdef *rd@ = register definition
2615  *              @unsigned style@ = output style (@TVSF_...@)
2616  *              @const struct gprintf_ops *gops@, @void *gp@ = format output
2617  *
2618  * Returns:     ---
2619  *
2620  * Use:         Dump a register value to the format output.
2621  *
2622  *              The table of symbolic names and their associated values and
2623  *              masks is repeatedly scanned, in order, to find disjoint
2624  *              matches -- i.e., entries whose value matches the target value
2625  *              in the bit positions indicated by the mask, and whose mask
2626  *              doesn't overlap with any previously found matches; the names
2627  *              are then output, separated by `|'.  Any remaining nonzero
2628  *              bits not covered by any of the matching masks are output as a
2629  *              single literal integer, in hex.
2630  *
2631  *              Unless compact output is requested, or no symbolic names were
2632  *              found, the raw numeric value is also printed in hex, as a
2633  *              comment.
2634  */
2635
2636 static void dump_flags(const union tvec_regval *rv,
2637                        const struct tvec_regdef *rd,
2638                        unsigned style,
2639                        const struct gprintf_ops *gops, void *go)
2640 {
2641   const struct tvec_flaginfo *fi = rd->arg.p;
2642   const struct tvec_flag *f;
2643   unsigned long m = ~0ul, v = rv->u;
2644   const char *sep;
2645
2646   if (style&TVSF_RAW) gprintf(gops, go, "flags/%s:", fi->name);
2647
2648   for (f = fi->fv, sep = ""; f->tag; f++)
2649     if ((m&f->m) && (v&f->m) == f->v) {
2650       gprintf(gops, go, "%s%s", sep, f->tag); m &= ~f->m;
2651       sep = style&TVSF_COMPACT ? "|" : " | ";
2652     }
2653
2654   if (v&m) gprintf(gops, go, "%s0x%0*lx", sep, hex_width(v), v&m);
2655
2656   if (m != ~0ul && !(style&(TVSF_COMPACT | TVSF_RAW)))
2657     gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
2658 }
2659
2660 /* Flags type definition. */
2661 const struct tvec_regty tvty_flags = {
2662   init_uint, trivial_release, eq_uint,
2663   tobuf_uint, frombuf_uint,
2664   parse_flags, dump_flags
2665 };
2666
2667 /* --- @tvec_claimeq_flags@ --- *
2668  *
2669  * Arguments:   @struct tvec_state *tv@ = test-vector state
2670  *              @const struct tvec_flaginfo *fi@ = flags type info
2671  *              @unsigned long f0, f1@ = two values
2672  *              @const char *file@, @unsigned @lno@ = calling file and line
2673  *              @const char *expr@ = the expression to quote on failure
2674  *
2675  * Returns:     Nonzero if @f0@ and @f1@ are equal, otherwise zero.
2676  *
2677  * Use:         Check that values of @f0@ and @f1@ are equal.  As for
2678  *              @tvec_claim@ above, a test case is automatically begun and
2679  *              ended if none is already underway.  If the values are
2680  *              unequal, then @tvec_fail@ is called, quoting @expr@, and the
2681  *              mismatched values are dumped: @f0@ is printed as the output
2682  *              value and @f1@ is printed as the input reference.
2683  */
2684
2685 int tvec_claimeq_flags(struct tvec_state *tv,
2686                        const struct tvec_flaginfo *fi,
2687                        unsigned long f0, unsigned long f1,
2688                        const char *file, unsigned lno, const char *expr)
2689 {
2690   union tvec_misc arg;
2691
2692   arg.p = fi; tv->out[0].v.u = f0; tv->in[0].v.u = f1;
2693   return (tvec_claimeq(tv, &tvty_flags, &arg, file, lno, expr));
2694 }
2695
2696 /*----- Characters --------------------------------------------------------*/
2697
2698 /* Character values are initialized and compared as signed integers. */
2699
2700 /* --- @tobuf_char@ --- *
2701  *
2702  * Arguments:   @buf *b@ = buffer
2703  *              @const union tvec_regval *rv@ = register value
2704  *              @const struct tvec_regdef *rd@ = register definition
2705  *
2706  * Returns:     Zero on success, %$-1$% on failure.
2707  *
2708  * Use:         Serialize a register value to a buffer.
2709  *
2710  *              Character values are serialized as little-endian 32-bit
2711  *              unsigned integers, with %|EOF|% serialized as all-bits-set.
2712  */
2713
2714 static int tobuf_char(buf *b, const union tvec_regval *rv,
2715                       const struct tvec_regdef *rd)
2716 {
2717   uint32 u;
2718
2719   if (0 <= rv->i && rv->i <= UCHAR_MAX) u = rv->i;
2720   else if (rv->i == EOF) u = MASK32;
2721   else { buf_break(b); return (-1); }
2722   return (buf_putu32l(b, u));
2723 }
2724
2725 /* --- @frombuf_char@ --- *
2726  *
2727  * Arguments:   @buf *b@ = buffer
2728  *              @union tvec_regval *rv@ = register value
2729  *              @const struct tvec_regdef *rd@ = register definition
2730  *
2731  * Returns:     Zero on success, %$-1$% on failure.
2732  *
2733  * Use:         Deserialize a register value from a buffer.
2734  *
2735  *              Character values are serialized as little-endian 32-bit
2736  *              unsigned integers, with %|EOF|% serialized as all-bits-set.
2737  */
2738
2739 static int frombuf_char(buf *b, union tvec_regval *rv,
2740                         const struct tvec_regdef *rd)
2741 {
2742   uint32 u;
2743
2744   if (buf_getu32l(b, &u)) return (-1);
2745   if (0 <= u && u <= UCHAR_MAX) rv->i = u;
2746   else if (u == MASK32) rv->i = EOF;
2747   else { buf_break(b); return (-1); }
2748   return (0);
2749 }
2750
2751 /* --- @parse_char@ --- *
2752  *
2753  * Arguments:   @union tvec_regval *rv@ = register value
2754  *              @const struct tvec_regdef *rd@ = register definition
2755  *              @struct tvec_state *tv@ = test-vector state
2756  *
2757  * Returns:     Zero on success, %$-1$% on error.
2758  *
2759  * Use:         Parse a register value from an input file.
2760  *
2761  *              A character value can be given by symbolic name, with a
2762  *              leading `%|#|%'; or a character or `%|\|%'-escape sequence,
2763  *              optionally in single quotes.
2764  *
2765  *              The following escape sequences and character names are
2766  *              recognized.
2767  *
2768  *              * `%|#eof|%' is the special end-of-file marker.
2769  *
2770  *              * `%|#nul|%' is the NUL character, sometimes used to
2771  *                terminate strings.
2772  *
2773  *              * `%|bell|%', `%|bel|%', `%|ding|%', or `%|\a|%' is the BEL
2774  *                character used to ring the terminal bell (or do some other
2775  *                thing to attract the user's attention).
2776  *
2777  *              * %|#backspace|%, %|#bs|%, or %|\b|% is the backspace
2778  *                character, used to move the cursor backwords by one cell.
2779  *
2780  *              * %|#escape|% %|#esc|%, or%|\e|% is the escape character,
2781  *                used to introduce special terminal commands.
2782  *
2783  *              * %|#formfeed|%, %|#ff|%, or %|\f|% is the formfeed
2784  *                character, used to separate pages of text.
2785  *
2786  *              * %|#newline|%, %|#linefeed|%, %|#lf|%, %|#nl|%, or %|\n|% is
2787  *                the newline character, used to terminate lines of text or
2788  *                advance the cursor to the next line (perhaps without
2789  *                returning it to the start of the line).
2790  *
2791  *              * %|#return|%, %|#carriage-return|%, %|#cr|%, or %|\r|% is
2792  *                the carriage-return character, used to return the cursor to
2793  *                the start of the line.
2794  *
2795  *              * %|#tab|%, %|#horizontal-tab|%, %|#ht|%, or %|\t|% is the
2796  *                tab character, used to advance the cursor to the next tab
2797  *                stop on the current line.
2798  *
2799  *              * %|#vertical-tab|%, %|#vt|%, %|\v|% is the vertical tab
2800  *                character.
2801  *
2802  *              * %|#space|%, %|#spc|% is the space character.
2803  *
2804  *              * %|#delete|%, %|#del|% is the delete character, used to
2805  *                erase the most recent character.
2806  *
2807  *              * %|\'|% is the single-quote character.
2808  *
2809  *              * %|\\|% is the backslash character.
2810  *
2811  *              * %|\"|% is the double-quote character.
2812  *
2813  *              * %|\NNN|% or %|\{NNN}|% is the character with code NNN in
2814  *                octal.  The NNN may be up to three digits long.
2815  *
2816  *              * %|\xNN|% or %|\x{NN}|% is the character with code NNN in
2817  *                hexadecimal.
2818  */
2819
2820 static int parse_char(union tvec_regval *rv, const struct tvec_regdef *rd,
2821                       struct tvec_state *tv)
2822 {
2823   dstr d = DSTR_INIT;
2824   int ch, rc;
2825   unsigned f = 0;
2826 #define f_quote 1u
2827
2828   /* Inspect the character to see what we're up against. */
2829   ch = getc(tv->fp);
2830
2831   if (ch == '#') {
2832     /* It looks like a special token.  Push the `%|#|%' back and fetch the
2833      * whole word.  If there's just the `%|#|%' after all, then treat it as
2834      * literal.
2835      */
2836
2837     ungetc(ch, tv->fp);
2838     if (tvec_readword(tv, &d, 0, ";", "character name"))
2839       { rc = -1; goto end; }
2840     if (STRCMP(d.buf, !=, "#")) {
2841       if (read_charname(&ch, d.buf, RCF_EOFOK)) {
2842         rc = tvec_error(tv, "unknown character name `%s'", d.buf);
2843         goto end;
2844       }
2845       if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
2846       rv->i = ch; rc = 0; goto end;
2847     }
2848   }
2849
2850   /* If this is a single quote then we expect to see a matching one later,
2851    * and we should process backslash escapes.  Get the next character and see
2852    * what happens.
2853    */
2854   if (ch == '\'') { f |= f_quote; ch = getc(tv->fp); }
2855
2856   /* Main character dispatch. */
2857   switch (ch) {
2858
2859     case ';':
2860       /* Unquoted, semicolon begins a comment. */
2861       if (!(f&f_quote)) { rc = tvec_syntax(tv, ch, "character"); goto end; }
2862       else goto plain;
2863
2864     case '\n':
2865       /* A newline.  If we saw a single quote, then treat that as literal.
2866        * Otherwise this is an error.
2867        */
2868       if (!(f&f_quote)) goto nochar;
2869       else { f &= ~f_quote; ungetc(ch, tv->fp); ch = '\''; goto plain; }
2870
2871     case EOF:
2872       /* End-of-file.  Similar to newline, but with slightly different
2873        * effects on the parse state.
2874        */
2875       if (!(f&f_quote)) goto nochar;
2876       else { f &= ~f_quote; ch = '\''; goto plain; }
2877
2878     case '\'': nochar:
2879       /* A single quote.  This must be the second of a pair, and there should
2880        * have been a character or escape sequence between them.
2881        */
2882       rc = tvec_syntax(tv, ch, "character"); goto end;
2883
2884     case '\\':
2885       /* A backslash.  Read a character escape. */
2886       if (read_charesc(&ch, tv)) return (-1);
2887
2888     default: plain:
2889       /* Anything else.  Treat as literal. */
2890       rv->i = ch; break;
2891   }
2892
2893   /* If we saw an opening quote, then expect the closing quote. */
2894   if (f&f_quote) {
2895     ch = getc(tv->fp);
2896     if (ch != '\'') { rc = tvec_syntax(tv, ch, "`''"); goto end; }
2897   }
2898
2899   /* Done. */
2900   if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
2901   rc = 0;
2902 end:
2903   dstr_destroy(&d);
2904   return (rc);
2905
2906 #undef f_quote
2907 }
2908
2909 /* --- @dump_char@ --- *
2910  *
2911  * Arguments:   @const union tvec_regval *rv@ = register value
2912  *              @const struct tvec_regdef *rd@ = register definition
2913  *              @unsigned style@ = output style (@TVSF_...@)
2914  *              @const struct gprintf_ops *gops@, @void *gp@ = format output
2915  *
2916  * Returns:     ---
2917  *
2918  * Use:         Dump a register value to the format output.
2919  *
2920  *              Character values are dumped as their symbolic names, if any,
2921  *              or as a character or escape sequence within single quotes
2922  *              (which may be omitted in compact style).  If compact output
2923  *              is not requested, then the single-quoted representation (for
2924  *              characters dumped as symbolic names) and integer code in
2925  *              decimal and hex are printed as a comment.
2926  */
2927
2928 static void dump_char(const union tvec_regval *rv,
2929                       const struct tvec_regdef *rd,
2930                       unsigned style,
2931                       const struct gprintf_ops *gops, void *go)
2932 {
2933   const char *p;
2934   unsigned f = 0;
2935 #define f_semi 1u
2936
2937   if (style&TVSF_RAW) {
2938     /* Print the raw character unconditionally in single quotes. */
2939
2940     gprintf(gops, go, "char:'");
2941     format_char(gops, go, rv->i);
2942     gprintf(gops, go, "'");
2943   } else {
2944     /* Print ina pleasant human-readable way. */
2945
2946     /* Print a character name if we can find one. */
2947     p = find_charname(rv->i, (style&TVSF_COMPACT) ? CTF_SHORT : CTF_PREFER);
2948     if (p) {
2949       gprintf(gops, go, "%s", p);
2950       if (style&TVSF_COMPACT) return;
2951       else { gprintf(gops, go, " ;"); f |= f_semi; }
2952     }
2953
2954     /* If the character isn't @EOF@ then print it as a single-quoted thing.
2955      * In compact style, see if we can omit the quotes.
2956      */
2957     if (rv->i >= 0) {
2958       if (f&f_semi) gprintf(gops, go, " = ");
2959       switch (rv->i) {
2960         case ' ': case '\\': case '\'': quote:
2961           format_char(gops, go, rv->i);
2962           break;
2963         default:
2964           if (!(style&TVSF_COMPACT) || !isprint(rv->i)) goto quote;
2965           gprintf(gops, go, "%c", (int)rv->i);
2966           return;
2967       }
2968     }
2969
2970     /* And the character code as an integer. */
2971     if (!(style&TVSF_COMPACT)) {
2972       if (!(f&f_semi)) gprintf(gops, go, " ;");
2973       gprintf(gops, go, " = %ld = ", rv->i);
2974       format_signed_hex(gops, go, rv->i);
2975     }
2976   }
2977
2978 #undef f_semi
2979 }
2980
2981 /* Character type definition. */
2982 const struct tvec_regty tvty_char = {
2983   init_int, trivial_release, eq_int,
2984   tobuf_char, frombuf_char,
2985   parse_char, dump_char
2986 };
2987
2988 /* --- @tvec_claimeq_char@ --- *
2989  *
2990  * Arguments:   @struct tvec_state *tv@ = test-vector state
2991  *              @int ch0, ch1@ = two character codes
2992  *              @const char *file@, @unsigned @lno@ = calling file and line
2993  *              @const char *expr@ = the expression to quote on failure
2994  *
2995  * Returns:     Nonzero if @ch0@ and @ch1@ are equal, otherwise zero.
2996  *
2997  * Use:         Check that values of @ch0@ and @ch1@ are equal.  As for
2998  *              @tvec_claim@ above, a test case is automatically begun and
2999  *              ended if none is already underway.  If the values are
3000  *              unequal, then @tvec_fail@ is called, quoting @expr@, and the
3001  *              mismatched values are dumped: @ch0@ is printed as the output
3002  *              value and @ch1@ is printed as the input reference.
3003  */
3004
3005 int tvec_claimeq_char(struct tvec_state *tv, int c0, int c1,
3006                       const char *file, unsigned lno, const char *expr)
3007 {
3008   tv->out[0].v.i = c0; tv->in[0].v.i = c1;
3009   return (tvec_claimeq(tv, &tvty_char, 0, file, lno, expr));
3010 }
3011
3012 /*----- Text and byte strings ---------------------------------------------*/
3013
3014 /* --- @init_text@, @init_bytes@ --- *
3015  *
3016  * Arguments:   @union tvec_regval *rv@ = register value
3017  *              @const struct tvec_regdef *rd@ = register definition
3018  *
3019  * Returns:     ---
3020  *
3021  * Use:         Initialize a register value.
3022  *
3023  *              Text and binary string values are initialized with a null
3024  *              pointer and zero length.
3025  */
3026
3027 static void init_text(union tvec_regval *rv, const struct tvec_regdef *rd)
3028   { rv->text.p = 0; rv->text.sz = 0; }
3029
3030 static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd)
3031   { rv->bytes.p = 0; rv->bytes.sz = 0; }
3032
3033 /* --- @release_string@, @release_bytes@ --- *
3034  *
3035  * Arguments:   @const union tvec_regval *rv@ = register value
3036  *              @const struct tvec_regdef *rd@ = register definition
3037  *
3038  * Returns:     ---
3039  *
3040  * Use:         Release resources held by a register value.
3041  *
3042  *              Text and binary string buffers are freed.
3043  */
3044
3045 static void release_text(union tvec_regval *rv,
3046                          const struct tvec_regdef *rd)
3047   { xfree(rv->text.p); }
3048
3049 static void release_bytes(union tvec_regval *rv,
3050                           const struct tvec_regdef *rd)
3051   { xfree(rv->bytes.p); }
3052
3053 /* --- @eq_text@, @eq_bytes@ --- *
3054  *
3055  * Arguments:   @const union tvec_regval *rv0, *rv1@ = register values
3056  *              @const struct tvec_regdef *rd@ = register definition
3057  *
3058  * Returns:     Nonzero if the values are equal, zero if unequal
3059  *
3060  * Use:         Compare register values for equality.
3061  */
3062
3063 static int eq_text(const union tvec_regval *rv0,
3064                    const union tvec_regval *rv1,
3065                    const struct tvec_regdef *rd)
3066 {
3067   return (rv0->text.sz == rv1->text.sz &&
3068           (!rv0->text.sz ||
3069            MEMCMP(rv0->text.p, ==, rv1->text.p, rv1->text.sz)));
3070 }
3071
3072 static int eq_bytes(const union tvec_regval *rv0,
3073                     const union tvec_regval *rv1,
3074                     const struct tvec_regdef *rd)
3075 {
3076   return (rv0->bytes.sz == rv1->bytes.sz &&
3077           (!rv0->bytes.sz ||
3078            MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz)));
3079 }
3080
3081 /* --- @tobuf_text@, @tobuf_bytes@ --- *
3082  *
3083  * Arguments:   @buf *b@ = buffer
3084  *              @const union tvec_regval *rv@ = register value
3085  *              @const struct tvec_regdef *rd@ = register definition
3086  *
3087  * Returns:     Zero on success, %$-1$% on failure.
3088  *
3089  * Use:         Serialize a register value to a buffer.
3090  *
3091  *              Text and binary string values are serialized as a little-
3092  *              endian 64-bit length %$n$% in bytes followed by %$n$% bytes
3093  *              of string data.
3094  */
3095
3096 static int tobuf_text(buf *b, const union tvec_regval *rv,
3097                       const struct tvec_regdef *rd)
3098   { return (buf_putmem64l(b, rv->text.p, rv->text.sz)); }
3099
3100 static int tobuf_bytes(buf *b, const union tvec_regval *rv,
3101                        const struct tvec_regdef *rd)
3102   { return (buf_putmem64l(b, rv->bytes.p, rv->bytes.sz)); }
3103
3104 /* --- @frombuf_text@, @frombuf_bytes@ --- *
3105  *
3106  * Arguments:   @buf *b@ = buffer
3107  *              @union tvec_regval *rv@ = register value
3108  *              @const struct tvec_regdef *rd@ = register definition
3109  *
3110  * Returns:     Zero on success, %$-1$% on failure.
3111  *
3112  * Use:         Deserialize a register value from a buffer.
3113  *
3114  *              Text and binary string values are serialized as a little-
3115  *              endian 64-bit length %$n$% in bytes followed by %$n$% bytes
3116  *              of string data.
3117  */
3118
3119 static int frombuf_text(buf *b, union tvec_regval *rv,
3120                         const struct tvec_regdef *rd)
3121 {
3122   const void *p;
3123   size_t sz;
3124
3125   p = buf_getmem64l(b, &sz); if (!p) return (-1);
3126   tvec_alloctext(rv, sz); memcpy(rv->text.p, p, sz); rv->text.p[sz] = 0;
3127   return (0);
3128 }
3129
3130 static int frombuf_bytes(buf *b, union tvec_regval *rv,
3131                          const struct tvec_regdef *rd)
3132 {
3133   const void *p;
3134   size_t sz;
3135
3136   p = buf_getmem64l(b, &sz); if (!p) return (-1);
3137   tvec_allocbytes(rv, sz); memcpy(rv->bytes.p, p, sz);
3138   return (0);
3139 }
3140
3141 /* --- @check_string_length@ --- *
3142  *
3143  * Arguments:   @size_t sz@ = found string length
3144  *              @const struct tvec_urange *ur@ = acceptable range
3145  *              @struct tvec_state *tv@ = test-vector state
3146  *
3147  * Returns:     Zero on success, %$-1$% on error.
3148  *
3149  * Use:         Checks that @sz@ is within the bounds described by @ur@,
3150  *              reporting an error if not.
3151  */
3152
3153 static int check_string_length(size_t sz, const struct tvec_urange *ur,
3154                                struct tvec_state *tv)
3155 {
3156   if (ur && (ur->min > sz || sz > ur->max))
3157     return (tvec_error(tv,
3158                        "invalid string length %lu; must be in [%lu .. %lu]",
3159                        (unsigned long)sz, ur->min, ur->max));
3160   return (0);
3161 }
3162
3163 /* --- @parse_text@, @parse_bytes@ --- *
3164  *
3165  * Arguments:   @union tvec_regval *rv@ = register value
3166  *              @const struct tvec_regdef *rd@ = register definition
3167  *              @struct tvec_state *tv@ = test-vector state
3168  *
3169  * Returns:     Zero on success, %$-1$% on error.
3170  *
3171  * Use:         Parse a register value from an input file.
3172  *
3173  *              The input format for both kinds of strings is basically the
3174  *              same: a `compound string', consisting of
3175  *
3176  *                * single-quoted strings, which are interpreted entirely
3177  *                  literally, but can't contain single quotes or newlines;
3178  *
3179  *                * double-quoted strings, in which `%|\|%'-escapes are
3180  *                  interpreted as for characters;
3181  *
3182  *                * character names, marked by an initial `%|#|%' sign;
3183  *
3184  *                * special tokens marked by an initial `%|!|%' sign; or
3185  *
3186  *                * barewords interpreted according to the current coding
3187  *                  scheme.
3188  *
3189  *              The special tokens are
3190  *
3191  *                * `%|!bare|%', which causes subsequent sequences of
3192  *                  barewords to be treated as plain text;
3193  *
3194  *                * `%|!hex|%', `%|!base32|%', `%|!base64|%', which cause
3195  *                  subsequent barewords to be decoded in the requested
3196  *                  manner.
3197  *
3198  *                * `%|!repeat|% %$n$% %|{|% %%\textit{string}%% %|}|%',
3199  *                  which includes %$n$% copies of the (compound) string.
3200  *
3201  *              The only difference between text and binary strings is that
3202  *              the initial coding scheme is %|bare|% for text strings and
3203  *              %|hex|% for binary strings.
3204  */
3205
3206 static int parse_text(union tvec_regval *rv, const struct tvec_regdef *rd,
3207                       struct tvec_state *tv)
3208 {
3209   void *p = rv->text.p;
3210
3211   if (read_compound_string(&p, &rv->text.sz, TVCODE_BARE, 0, tv))
3212     return (-1);
3213   rv->text.p = p;
3214   if (check_string_length(rv->text.sz, rd->arg.p, tv)) return (-1);
3215   return (0);
3216 }
3217
3218 static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd,
3219                        struct tvec_state *tv)
3220 {
3221   void *p = rv->bytes.p;
3222
3223   if (read_compound_string(&p, &rv->bytes.sz, TVCODE_HEX, 0, tv))
3224     return (-1);
3225   rv->bytes.p = p;
3226   if (check_string_length(rv->bytes.sz, rd->arg.p, tv)) return (-1);
3227   return (0);
3228 }
3229
3230 /* --- @dump_text@, @dump_bytes@ --- *
3231  *
3232  * Arguments:   @const union tvec_regval *rv@ = register value
3233  *              @const struct tvec_regdef *rd@ = register definition
3234  *              @unsigned style@ = output style (@TVSF_...@)
3235  *              @const struct gprintf_ops *gops@, @void *gp@ = format output
3236  *
3237  * Returns:     ---
3238  *
3239  * Use:         Dump a register value to the format output.
3240  *
3241  *              Text string values are dumped as plain text, in double quotes
3242  *              if necessary, and using backslash escape sequences for
3243  *              nonprintable characters.  Unless compact output is requested,
3244  *              strings consisting of multiple lines are dumped with each
3245  *              line of the string on a separate output line.
3246  *
3247  *              Binary string values are dumped in hexadecimal.  In compact
3248  *              style, the output simply consists of a single block of hex
3249  *              digits.  Otherwise, the dump is a display consisting of
3250  *              groups of hex digits, with comments showing the offset (if
3251  *              the string is long enough) and the corresponding plain text.
3252  *
3253  *              Empty strings are dumped as %|#empty|%.
3254  */
3255
3256 static void dump_empty(const char *ty, unsigned style,
3257                        const struct gprintf_ops *gops, void *go)
3258 {
3259   if (style&TVSF_RAW) gprintf(gops, go, "%s:", ty);
3260   if (!(style&TVSF_COMPACT)) gprintf(gops, go, "#empty");
3261   if (!(style&(TVSF_COMPACT | TVSF_RAW))) gprintf(gops, go, " ; = ");
3262   if (!(style&TVSF_RAW)) gprintf(gops, go, "\"\"");
3263 }
3264
3265
3266 static void dump_text(const union tvec_regval *rv,
3267                       const struct tvec_regdef *rd,
3268                       unsigned style,
3269                       const struct gprintf_ops *gops, void *go)
3270 {
3271   const unsigned char *p, *q, *l;
3272   unsigned f = 0;
3273 #define f_nonword 1u
3274 #define f_newline 2u
3275
3276   if (!rv->text.sz) { dump_empty("text", style, gops, go); return; }
3277
3278   p = (const unsigned char *)rv->text.p; l = p + rv->text.sz;
3279   if (style&TVSF_RAW) { gprintf(gops, go, "text:"); goto quote; }
3280   else if (style&TVSF_COMPACT) goto quote;
3281
3282   switch (*p) {
3283     case '!': case '#': case ';': case '"': case '\'':
3284     case '(': case '{': case '[': case ']': case '}': case ')':
3285       f |= f_nonword; break;
3286   }
3287   for (q = p; q < l; q++)
3288     if (*q == '\n' && q != l - 1) f |= f_newline;
3289     else if (!*q || !ISGRAPH(*q) || *q == '\\') f |= f_nonword;
3290   if (f&f_newline) { gprintf(gops, go, "\n\t"); goto quote; }
3291   else if (f&f_nonword) goto quote;
3292
3293   gops->putm(go, (const char *)p, rv->text.sz);
3294   return;
3295
3296 quote:
3297   gprintf(gops, go, "\"");
3298   for (q = p; q < l; q++)
3299     if (!ISPRINT(*q) || *q == '"') {
3300       if (p < q) gops->putm(go, (const char *)p, q - p);
3301       if (*q != '\n' || (style&TVSF_COMPACT))
3302         format_charesc(gops, go, *q, FCF_BRACE);
3303       else {
3304         if (q + 1 == l) { gprintf(gops, go, "\\n\""); return; }
3305         else gprintf(gops, go, "\\n\"\n\t\"");
3306       }
3307       p = q + 1;
3308     }
3309   if (p < q) gops->putm(go, (const char *)p, q - p);
3310   gprintf(gops, go, "\"");
3311
3312 #undef f_nonword
3313 #undef f_newline
3314 }
3315
3316 static void dump_bytes(const union tvec_regval *rv,
3317                        const struct tvec_regdef *rd,
3318                        unsigned style,
3319                        const struct gprintf_ops *gops, void *go)
3320 {
3321   const unsigned char *p = rv->bytes.p, *l = p + rv->bytes.sz;
3322   size_t off, sz = rv->bytes.sz;
3323   unsigned i, n;
3324   int wd;
3325
3326   if (!rv->text.sz) { dump_empty("bytes", style, gops, go); return; }
3327
3328   if (style&(TVSF_COMPACT | TVSF_RAW)) {
3329     while (p < l) gprintf(gops, go, "%02x", *p++);
3330     return;
3331   }
3332
3333   if (sz > 16) gprintf(gops, go, "\n\t");
3334
3335   off = 0; wd = hex_width(sz);
3336   while (p < l) {
3337     if (l - p < 16) n = l - p;
3338     else n = 16;
3339
3340     for (i = 0; i < n; i++) {
3341       if (i < n) gprintf(gops, go, "%02x", p[i]);
3342       else gprintf(gops, go, "  ");
3343       if (i < n - 1 && i%4 == 3) gprintf(gops, go, " ");
3344     }
3345     gprintf(gops, go, " ; ");
3346     if (sz > 16) gprintf(gops, go, "[%0*lx] ", wd, (unsigned long)off);
3347     for (i = 0; i < n; i++)
3348       gprintf(gops, go, "%c", isprint(p[i]) ? p[i] : '.');
3349     p += n; off += n;
3350     if (p < l) gprintf(gops, go, "\n\t");
3351   }
3352 }
3353
3354 /* Text and byte string type definitions. */
3355 const struct tvec_regty tvty_text = {
3356   init_text, release_text, eq_text,
3357   tobuf_text, frombuf_text,
3358   parse_text, dump_text
3359 };
3360 const struct tvec_regty tvty_bytes = {
3361   init_bytes, release_bytes, eq_bytes,
3362   tobuf_bytes, frombuf_bytes,
3363   parse_bytes, dump_bytes
3364 };
3365
3366 /* --- @tvec_claimeq_text@ --- *
3367  *
3368  * Arguments:   @struct tvec_state *tv@ = test-vector state
3369  *              @const char *p0@, @size_t sz0@ = first string with length
3370  *              @const char *p1@, @size_t sz1@ = second string with length
3371  *              @const char *file@, @unsigned @lno@ = calling file and line
3372  *              @const char *expr@ = the expression to quote on failure
3373  *
3374  * Returns:     Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3375  *              zero.
3376  *
3377  * Use:         Check that strings at @p0@ and @p1@ are equal.  As for
3378  *              @tvec_claim@ above, a test case is automatically begun and
3379  *              ended if none is already underway.  If the values are
3380  *              unequal, then @tvec_fail@ is called, quoting @expr@, and the
3381  *              mismatched values are dumped: @p0@ is printed as the output
3382  *              value and @p1@ is printed as the input reference.
3383  */
3384
3385 int tvec_claimeq_text(struct tvec_state *tv,
3386                       const char *p0, size_t sz0,
3387                       const char *p1, size_t sz1,
3388                       const char *file, unsigned lno, const char *expr)
3389 {
3390   tv->out[0].v.text.p = (/*unconst*/ char *)p0; tv->out[0].v.text.sz = sz0;
3391   tv->in[0].v.text.p =(/*unconst*/ char *) p1; tv->in[0].v.text.sz = sz1;
3392   return (tvec_claimeq(tv, &tvty_text, 0, file, lno, expr));
3393 }
3394
3395 /* --- @tvec_claimeq_textz@ --- *
3396  *
3397  * Arguments:   @struct tvec_state *tv@ = test-vector state
3398  *              @const char *p0, *p1@ = two strings to compare
3399  *              @const char *file@, @unsigned @lno@ = calling file and line
3400  *              @const char *expr@ = the expression to quote on failure
3401  *
3402  * Returns:     Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3403  *              zero.
3404  *
3405  * Use:         Check that strings at @p0@ and @p1@ are equal, as for
3406  *              @tvec_claimeq_string@, except that the strings are assumed
3407  *              null-terminated, so their lengths don't need to be supplied
3408  *              explicitly.
3409  */
3410
3411 int tvec_claimeq_textz(struct tvec_state *tv,
3412                        const char *p0, const char *p1,
3413                        const char *file, unsigned lno, const char *expr)
3414 {
3415   tv->out[0].v.text.p = (/*unconst*/ char *)p0;
3416     tv->out[0].v.text.sz = strlen(p0);
3417   tv->in[0].v.text.p = (/*unconst*/ char *)p1;
3418     tv->in[0].v.text.sz = strlen(p1);
3419   return (tvec_claimeq(tv, &tvty_text, 0, file, lno, expr));
3420 }
3421
3422 /* --- @tvec_claimeq_bytes@ --- *
3423  *
3424  * Arguments:   @struct tvec_state *tv@ = test-vector state
3425  *              @const void *p0@, @size_t sz0@ = first string with length
3426  *              @const void *p1@, @size_t sz1@ = second string with length
3427  *              @const char *file@, @unsigned @lno@ = calling file and line
3428  *              @const char *expr@ = the expression to quote on failure
3429  *
3430  * Returns:     Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3431  *              zero.
3432  *
3433  * Use:         Check that binary strings at @p0@ and @p1@ are equal.  As for
3434  *              @tvec_claim@ above, a test case is automatically begun and
3435  *              ended if none is already underway.  If the values are
3436  *              unequal, then @tvec_fail@ is called, quoting @expr@, and the
3437  *              mismatched values are dumped: @p0@ is printed as the output
3438  *              value and @p1@ is printed as the input reference.
3439  */
3440
3441 int tvec_claimeq_bytes(struct tvec_state *tv,
3442                        const void *p0, size_t sz0,
3443                        const void *p1, size_t sz1,
3444                        const char *file, unsigned lno, const char *expr)
3445 {
3446   tv->out[0].v.bytes.p = (/*unconst*/ void *)p0;
3447     tv->out[0].v.bytes.sz = sz0;
3448   tv->in[0].v.bytes.p = (/*unconst*/ void *)p1;
3449     tv->in[0].v.bytes.sz = sz1;
3450   return (tvec_claimeq(tv, &tvty_bytes, 0, file, lno, expr));
3451 }
3452
3453 /* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
3454  *
3455  * Arguments:   @union tvec_regval *rv@ = register value
3456  *              @size_t sz@ = required size
3457  *
3458  * Returns:     ---
3459  *
3460  * Use:         Allocated space in a text or binary string register.  If the
3461  *              current register size is sufficient, its buffer is left
3462  *              alone; otherwise, the old buffer, if any, is freed and a
3463  *              fresh buffer allocated.  These functions are not intended to
3464  *              be used to adjust a buffer repeatedly, e.g., while building
3465  *              output incrementally: (a) they will perform badly, and (b)
3466  *              the old buffer contents are simply discarded if reallocation
3467  *              is necessary.  Instead, use a @dbuf@ or @dstr@.
3468  *
3469  *              The @tvec_alloctext@ function sneakily allocates an extra
3470  *              byte for a terminating zero.  The @tvec_allocbytes@ function
3471  *              doesn't do this.
3472  */
3473
3474 void tvec_alloctext(union tvec_regval *rv, size_t sz)
3475 {
3476   if (rv->text.sz <= sz) { xfree(rv->text.p); rv->text.p = xmalloc(sz + 1); }
3477   rv->text.sz = sz;
3478 }
3479
3480 void tvec_allocbytes(union tvec_regval *rv, size_t sz)
3481 {
3482   if (rv->bytes.sz < sz) { xfree(rv->bytes.p); rv->bytes.p = xmalloc(sz); }
3483   rv->bytes.sz = sz;
3484 }
3485
3486 /*----- Buffer type -------------------------------------------------------*/
3487
3488 /* --- @init_buffer@ --- *
3489  *
3490  * Arguments:   @union tvec_regval *rv@ = register value
3491  *              @const struct tvec_regdef *rd@ = register definition
3492  *
3493  * Returns:     ---
3494  *
3495  * Use:         Initialize a register value.
3496  *
3497  *              Buffer values values are initialized with a null pointer,
3498  *              zero length, and zero residue, modulus, and offset.
3499  */
3500
3501 static void init_buffer(union tvec_regval *rv, const struct tvec_regdef *rd)
3502   { rv->buf.p = 0; rv->buf.sz = rv->buf.a = rv->buf.m = rv->buf.off = 0; }
3503
3504 /* --- @release_buffer@, @release_bytes@ --- *
3505  *
3506  * Arguments:   @const union tvec_regval *rv@ = register value
3507  *              @const struct tvec_regdef *rd@ = register definition
3508  *
3509  * Returns:     ---
3510  *
3511  * Use:         Release resources held by a register value.
3512  *
3513  *              Buffers are freed.
3514  */
3515
3516 static void release_buffer(union tvec_regval *rv,
3517                            const struct tvec_regdef *rd)
3518   { if (rv->buf.p) xfree(rv->buf.p - rv->buf.off); }
3519
3520 /* --- @eq_buffer@ --- *
3521  *
3522  * Arguments:   @const union tvec_regval *rv0, *rv1@ = register values
3523  *              @const struct tvec_regdef *rd@ = register definition
3524  *
3525  * Returns:     Nonzero if the values are equal, zero if unequal
3526  *
3527  * Use:         Compare register values for equality.
3528  *
3529  *              Buffer values are equal if and only if their sizes and
3530  *              alignment parameters are equal; their contents are
3531  *              %%\emph{not}%% compared.
3532  */
3533
3534 static int eq_buffer(const union tvec_regval *rv0,
3535                      const union tvec_regval *rv1,
3536                      const struct tvec_regdef *rd)
3537 {
3538   return (rv0->buf.sz == rv1->buf.sz &&
3539           rv0->buf.a == rv1->buf.a &&
3540           rv0->buf.m == rv1->buf.m);
3541 }
3542
3543 /* --- @tobuf_buffer@ --- *
3544  *
3545  * Arguments:   @buf *b@ = buffer
3546  *              @const union tvec_regval *rv@ = register value
3547  *              @const struct tvec_regdef *rd@ = register definition
3548  *
3549  * Returns:     Zero on success, %$-1$% on failure.
3550  *
3551  * Use:         Serialize a register value to a buffer.
3552  *
3553  *              Buffer values are serialized as their lengths, residues, and
3554  *              moduli, as unsigned integers.
3555  */
3556
3557 static int tobuf_buffer(buf *b, const union tvec_regval *rv,
3558                          const struct tvec_regdef *rd)
3559 {
3560   return (unsigned_to_buf(b, rv->buf.sz) ||
3561           unsigned_to_buf(b, rv->buf.a) ||
3562           unsigned_to_buf(b, rv->buf.m));
3563 }
3564
3565 /* --- @frombuf_buffer@ --- *
3566  *
3567  * Arguments:   @buf *b@ = buffer
3568  *              @union tvec_regval *rv@ = register value
3569  *              @const struct tvec_regdef *rd@ = register definition
3570  *
3571  * Returns:     Zero on success, %$-1$% on failure.
3572  *
3573  * Use:         Deserialize a register value from a buffer.
3574  *
3575  *              Buffer values are serialized as just their lengths, as
3576  *              unsigned integers.  The buffer is allocated on
3577  *              deserialization and filled with a distinctive pattern.
3578  */
3579
3580 static int frombuf_buffer(buf *b, union tvec_regval *rv,
3581                           const struct tvec_regdef *rd)
3582 {
3583   unsigned long sz, a, m;
3584
3585   if (unsigned_from_buf(b, &sz)) return (-1);
3586   if (unsigned_from_buf(b, &a)) return (-1);
3587   if (unsigned_from_buf(b, &m)) return (-1);
3588   if (sz > (size_t)-1 || a > (size_t)-1 || m > (size_t)-1)
3589     { buf_break(b); return (-1); }
3590   rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m;
3591   return (0);
3592 }
3593
3594 /* --- @parse_buffer@ --- *
3595  *
3596  * Arguments:   @union tvec_regval *rv@ = register value
3597  *              @const struct tvec_regdef *rd@ = register definition
3598  *              @struct tvec_state *tv@ = test-vector state
3599  *
3600  * Returns:     Zero on success, %$-1$% on error.
3601  *
3602  * Use:         Parse a register value from an input file.
3603  *
3604  *              The input format for a buffer value is a size, followed by an
3605  *              optional `%|@$%' and an alignment quantum and a further
3606  *              optional `%|+|%' and an alignment offset.  The size, quantum,
3607  *              and offset are syntactically sizes.
3608  *
3609  *              The buffer is not allocated.
3610  */
3611
3612 static int parse_buffer(union tvec_regval *rv,
3613                         const struct tvec_regdef *rd,
3614                         struct tvec_state *tv)
3615 {
3616   unsigned long sz, a = 0, m = 0;
3617   int ch, rc;
3618
3619   if (parse_szint(tv, &sz, "@;", "buffer length")) { rc = -1; goto end; }
3620   if (check_unsigned_range(sz, &tvrange_size, tv, "buffer length"))
3621     { rc = -1; goto end; }
3622   if (check_string_length(sz, rd->arg.p, tv)) { rc = -1; goto end; }
3623
3624   tvec_skipspc(tv);
3625   ch = getc(tv->fp);
3626   if (ch == ';' || ch == '\n') { ungetc(ch, tv->fp); goto done; }
3627   else if (ch != '@') { rc = tvec_syntax(tv, ch, "`@'"); goto end; }
3628
3629   if (parse_szint(tv, &m, "+;", "alignment quantum")) { rc = -1; goto end; }
3630   if (check_unsigned_range(a, &tvrange_size, tv, "alignment quantum"))
3631     { rc = -1; goto end; }
3632   if (m == 1) m = 0;
3633
3634   tvec_skipspc(tv);
3635   ch = getc(tv->fp);
3636   if (ch == ';' || ch == '\n') { ungetc(ch, tv->fp); goto done; }
3637   else if (ch != '+') { rc = tvec_syntax(tv, ch, "`+'"); goto end; }
3638
3639   if (parse_szint(tv, &a, ";", "alignment offset")) { rc = -1; goto end; }
3640   if (check_unsigned_range(m, &tvrange_size, tv, "alignment offset"))
3641     { rc = -1; goto end; }
3642   if (a >= m) {
3643     rc = tvec_error(tv, "alignment offset %lu >= quantum %lu",
3644                     (unsigned long)a, (unsigned long)m);
3645     goto end;
3646   }
3647
3648 done:
3649   if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
3650   rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m;
3651   rc = 0;
3652 end:
3653   return (rc);
3654 }
3655
3656 /* --- @dump_buffer@ --- *
3657  *
3658  * Arguments:   @const union tvec_regval *rv@ = register value
3659  *              @const struct tvec_regdef *rd@ = register definition
3660  *              @unsigned style@ = output style (@TVSF_...@)
3661  *              @const struct gprintf_ops *gops@, @void *gp@ = format output
3662  *
3663  * Returns:     ---
3664  *
3665  * Use:         Dump a register value to the format output.
3666  *
3667  *              Buffer values are dumped as their size, with the alignment
3668  *              quantum and alignment offset if these are non-default.
3669  */
3670
3671 static void dump_buffer(const union tvec_regval *rv,
3672                         const struct tvec_regdef *rd,
3673                         unsigned style,
3674                         const struct gprintf_ops *gops, void *go)
3675 {
3676   format_size(gops, go, rv->buf.sz, style);
3677   if (rv->buf.m) {
3678     gprintf(gops, go, style&(TVSF_COMPACT | TVSF_RAW) ? "@" : " @ ");
3679     format_size(gops, go, rv->buf.m, style);
3680     if (rv->buf.a) {
3681       gprintf(gops, go, style&(TVSF_COMPACT | TVSF_RAW) ? "+" : " + ");
3682       format_size(gops, go, rv->buf.a, style);
3683     }
3684   }
3685   if (!(style&TVSF_COMPACT)) {
3686     gprintf(gops, go, " ; = %lu", (unsigned long)rv->buf.sz);
3687     if (rv->buf.m) {
3688       gprintf(gops, go, " @ %lu", (unsigned long)rv->buf.m);
3689       if (rv->buf.a) gprintf(gops, go, " + %lu", (unsigned long)rv->buf.a);
3690     }
3691     gprintf(gops, go, " = "); format_unsigned_hex(gops, go, rv->buf.sz);
3692     if (rv->buf.m) {
3693       gprintf(gops, go, " @ "); format_unsigned_hex(gops, go, rv->buf.m);
3694       if (rv->buf.a) {
3695         gprintf(gops, go, " + ");
3696         format_unsigned_hex(gops, go, rv->buf.a);
3697       }
3698     }
3699   }
3700 }
3701
3702 /* Buffer type definition. */
3703 const struct tvec_regty tvty_buffer = {
3704   init_buffer, release_buffer, eq_buffer,
3705   tobuf_buffer, frombuf_buffer,
3706   parse_buffer, dump_buffer
3707 };
3708
3709 /* --- @tvec_initbuffer@ --- *
3710  *
3711  * Arguments:   @union tvec_regval *rv@ = register value
3712  *              @const union tvec_regval *ref@ = source buffer
3713  *              @size_t sz@ = size to allocate
3714  *
3715  * Returns:     ---
3716  *
3717  * Use:         Initialize the alignment parameters in @rv@ to match @ref@,
3718  *              and the size to @sz@.
3719  */
3720
3721 void tvec_initbuffer(union tvec_regval *rv,
3722                      const union tvec_regval *ref, size_t sz)
3723   { rv->buf.sz = sz; rv->buf.a = ref->buf.a; rv->buf.m = ref->buf.m; }
3724
3725 /* --- @tvec_allocbuffer@ --- *
3726  *
3727  * Arguments:   @union tvec_regval *rv@ = register value
3728  *
3729  * Returns:     ---
3730  *
3731  * Use:         Allocate @sz@ bytes to the buffer and fill the space with a
3732  *              distinctive pattern.
3733  */
3734
3735 void tvec_allocbuffer(union tvec_regval *rv)
3736 {
3737   unsigned char *p; size_t n;
3738
3739   if (rv->buf.p) xfree(rv->buf.p - rv->buf.off);
3740
3741   if (rv->buf.m < 2) {
3742     rv->buf.p = xmalloc(rv->buf.sz); rv->buf.off = 0;
3743   } else {
3744     p = xmalloc(rv->buf.sz + rv->buf.m - 1);
3745     n = (size_t)p%rv->buf.m;
3746     rv->buf.off = (rv->buf.a - n + rv->buf.m)%rv->buf.m;
3747     rv->buf.p = p + rv->buf.off;
3748   }
3749   memset(rv->buf.p, '?', rv->buf.sz);
3750 }
3751
3752 /*----- That's all, folks -------------------------------------------------*/