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