@@@ man wip
[mLib] / test / tvec-types.c
CommitLineData
b64eb60f
MW
1/* -*-c-*-
2 *
3 * Types for the test-vector framework
4 *
5 * (c) 2023 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
16 *
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 * USA.
26 */
27
28/*----- Header files ------------------------------------------------------*/
29
30#include <assert.h>
31#include <ctype.h>
32#include <errno.h>
e63124bc 33#include <float.h>
b64eb60f 34#include <limits.h>
e63124bc 35#include <math.h>
b64eb60f
MW
36#include <stdio.h>
37#include <string.h>
38
39#include "buf.h"
40#include "codec.h"
41# include "base32.h"
42# include "base64.h"
43# include "hex.h"
44#include "dstr.h"
67b5031e 45#include "maths.h"
b64eb60f
MW
46#include "tvec.h"
47
48/*----- Preliminary utilities ---------------------------------------------*/
49
67b5031e
MW
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 */
3efcfd2d
MW
60
61static void trivial_release(union tvec_regval *rv,
62 const struct tvec_regdef *rd)
63 { ; }
64
67b5031e
MW
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
78static int unsigned_to_buf(buf *b, unsigned long u)
79 { kludge64 k; ASSIGN64(k, u); return (buf_putk64l(b, k)); }
80
b64eb60f
MW
81static 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
67b5031e
MW
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 */
b64eb60f
MW
103
104static 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);
adec5584 110 if (CMP64(k, >, ulmax)) { buf_break(b); return (-1); }
b64eb60f
MW
111 *u_out = GET64(unsigned long, k); return (0);
112}
113
67b5031e
MW
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
b64eb60f
MW
123static 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
67b5031e
MW
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
143static 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
147static 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
154static 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;
adec5584 164 else { buf_break(b); return (-1); }
67b5031e
MW
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 *
177 * Returns: Zero on success, or @-1@ on error.
178 *
179 * Use: Check that the integer is within bounds. If not, report a
180 * suitable error and return a failure indication.
181 */
182
882a39c1
MW
183static int check_signed_range(long i,
184 const struct tvec_irange *ir,
185 struct tvec_state *tv)
b64eb60f 186{
882a39c1 187 if (ir && (ir->min > i || i > ir->max)) {
b64eb60f
MW
188 tvec_error(tv, "integer %ld out of range (must be in [%ld .. %ld])",
189 i, ir->min, ir->max);
882a39c1
MW
190 return (-1);
191 }
192 return (0);
b64eb60f
MW
193}
194
882a39c1
MW
195static int check_unsigned_range(unsigned long u,
196 const struct tvec_urange *ur,
197 struct tvec_state *tv)
b64eb60f 198{
882a39c1 199 if (ur && (ur->min > u || u > ur->max)) {
b64eb60f
MW
200 tvec_error(tv, "integer %lu out of range (must be in [%lu .. %lu])",
201 u, ur->min, ur->max);
882a39c1
MW
202 return (-1);
203 }
204 return (0);
b64eb60f
MW
205}
206
67b5031e
MW
207/* --- @chtodig@ --- *
208 *
209 * Arguments: @int ch@ = a character
210 *
211 * Returns: The numeric value of the character as a digit, or @-1@ if
212 * it's not a digit. Letters count as extended digits starting
213 * with value 10; case is not significant.
214 */
215
3efcfd2d
MW
216static int chtodig(int ch)
217{
218 if ('0' <= ch && ch <= '9') return (ch - '0');
219 else if ('a' <= ch && ch <= 'z') return (ch - 'a' + 10);
220 else if ('A' <= ch && ch <= 'Z') return (ch - 'A' + 10);
221 else return (-1);
222}
223
67b5031e
MW
224/* --- @parse_unsigned_integer@, @parse_signed_integer@ --- *
225 *
226 * Arguments: @unsigned long *u_out@, @long *i_out@ = where to put the
227 * result
228 * @const char **q_out@ = where to put the end position
229 * @const char *p@ = pointer to the string to parse
230 *
231 * Returns: Zero on success, @-1@ on error.
232 *
233 * Use: Parse an integer from a string in the test-vector format.
234 * This is mostly extension of the traditional C @strtoul@
235 * format: supported inputs include:
236 *
237 * * NNN -- a decimal number (even if it starts with `0');
238 * * 0xNNN -- hexadecimal;
239 * * 0oNNN -- octal;
240 * * 0bNNN -- binary;
241 * * NNrNNN -- base NN.
242 *
243 * Furthermore, single underscores are permitted internally as
244 * an insignificant digit separator.
245 */
246
3efcfd2d
MW
247static int parse_unsigned_integer(unsigned long *u_out, const char **q_out,
248 const char *p)
249{
250 unsigned long u;
251 int ch, d, r;
252 const char *q;
253 unsigned f = 0;
67b5031e
MW
254#define f_implicit 1u /* implicitly reading base 10 */
255#define f_digit 2u /* read a real digit */
256#define f_uscore 4u /* found an underscore */
257
258 /* Initial setup
259 *
260 * This will deal with the traditional `0[box]...' prefixes. We'll leave
261 * our new `NNr...' syntax for later.
262 */
3efcfd2d
MW
263 if (p[0] != '0' || !p[1]) {
264 d = chtodig(*p); if (0 > d || d >= 10) return (-1);
265 r = 10; u = d; p++; f |= f_implicit | f_digit;
266 } else {
267 u = 0; d = chtodig(p[2]);
268 if (d < 0) { r = 10; f |= f_implicit | f_digit; p++; }
269 else if ((p[1] == 'x' || p[1] == 'X') && d < 16) { r = 16; p += 2; }
270 else if ((p[1] == 'o' || p[1] == 'O') && d < 8) { r = 8; p += 2; }
271 else if ((p[1] == 'b' || p[1] == 'B') && d < 2) { r = 2; p += 2; }
272 else { r = 10; f |= f_digit; p++; }
273 }
274
275 q = p;
276 for (;;) {
67b5031e
MW
277 /* Work through the string a character at a time. */
278
279 ch = *p; switch (ch) {
280
281 case '_':
282 /* An underscore is OK if we haven't just seen one. */
283
284 if (f&f_uscore) goto done;
285 p++; f = (f&~f_implicit) | f_uscore;
286 break;
287
288 case 'r': case 'R':
289 /* An `r' is OK if the number so far is small enough to be a sensible
290 * base, and we're scanning decimal implicitly.
291 */
292
293 if (!(f&f_implicit) || !u || u >= 36) goto done;
294 d = chtodig(p[1]); if (0 > d || d >= u) goto done;
295 r = u; u = d; f = (f&~f_implicit) | f_digit; p += 2; q = p;
296 break;
297
298 default:
299 /* Otherwise we expect a valid digit and accumulate it. */
300 d = chtodig(ch); if (d < 0 || d >= r) goto done;
301 if (u > ULONG_MAX/r) return (-1);
302 u *= r; if (u > ULONG_MAX - d) return (-1);
303 u += d; f = (f&~f_uscore) | f_digit; p++; q = p;
304 break;
3efcfd2d
MW
305 }
306 }
307
67b5031e 308done:
3efcfd2d
MW
309 if (!(f&f_digit)) return (-1);
310 *u_out = u; *q_out = q; return (0);
311
312#undef f_implicit
313#undef f_digit
314#undef f_uscore
315}
316
317static int parse_signed_integer(long *i_out, const char **q_out,
318 const char *p)
319{
320 unsigned long u;
321 unsigned f = 0;
322#define f_neg 1u
323
67b5031e 324 /* Read an initial sign. */
3efcfd2d
MW
325 if (*p == '+') p++;
326 else if (*p == '-') { f |= f_neg; p++; }
327
67b5031e 328 /* Scan an unsigned number. */
3efcfd2d
MW
329 if (parse_unsigned_integer(&u, q_out, p)) return (-1);
330
67b5031e 331 /* Check for signed overflow and apply the sign. */
3efcfd2d
MW
332 if (!(f&f_neg)) {
333 if (u > LONG_MAX) return (-1);
334 *i_out = u;
335 } else {
336 if (u && u - 1 > -(LONG_MIN + 1)) return (-1);
337 *i_out = u ? -(long)(u - 1) - 1 : 0;
338 }
339
340 return (0);
341
342#undef f_neg
343}
344
67b5031e
MW
345/* --- @parse_unsigned@, @parse_signed@ --- *
346 *
347 * Arguments: @unsigned long *u_out@ or @long *i_out@ = where to put the
348 * result
349 * @const char *p@ = string to parse
350 * @const struct tvec_urange *ur@ or
351 * @const struct tvec_irange *ir@ = range specification,
352 * or null
353 * @struct tvec_state *tv@ = test vector state
354 *
355 * Returns: Zero on success, @-1@ on error.
356 *
357 * Use: Parse and range-check an integer. Unlike @parse_(un)signed_
358 * integer@, these functions check that there's no cruft
359 * following the final digit, and report errors as they find
360 * them rather than leaving that to the caller.
361 */
362
363static int parse_unsigned(unsigned long *u_out, const char *p,
364 const struct tvec_urange *ur,
365 struct tvec_state *tv)
366{
367 unsigned long u;
368 const char *q;
369
370 if (parse_unsigned_integer(&u, &q, p))
371 return (tvec_error(tv, "invalid unsigned integer `%s'", p));
372 if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
373 if (check_unsigned_range(u, ur, tv)) return (-1);
374 *u_out = u; return (0);
375}
376
882a39c1
MW
377static int parse_signed(long *i_out, const char *p,
378 const struct tvec_irange *ir,
379 struct tvec_state *tv)
b64eb60f 380{
b64eb60f 381 long i;
3efcfd2d 382 const char *q;
b64eb60f 383
3efcfd2d
MW
384 if (parse_signed_integer(&i, &q, p))
385 return (tvec_error(tv, "invalid signed integer `%s'", p));
386 if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
e63124bc 387 if (check_signed_range(i, ir, tv)) return (-1);
3efcfd2d 388 *i_out = i; return (0);
b64eb60f 389}
adec5584
MW
390static const char size_units[] = "kMGTPEZY";
391
392/* --- @parse_size@ --- *
393 *
394 * Arguments: @struct tvec_state *tv@ = test-vector state
395 * @size_t *u_out@ = where to put the answer
396 * @const char *delims@ = delimiters
397 * @const char *what@ = description of what we're parsing
398 *
399 * Returns: Zero on success, %$-1$% on failure.
400 *
401 * Use: Parse a memory size.
402 */
403
404static int parse_size(struct tvec_state *tv, size_t *u_out,
405 const char *delims, const char *what)
406{
407 dstr d = DSTR_INIT;
408 const char *p, *unit;
409 unsigned long u, t;
410 int rc;
411 unsigned f = 0;
412#define f_range 1u
413
414 if (tvec_readword(tv, &d, 0, delims, what)) { rc = -1; goto end; }
415 p = d.buf;
416 if (parse_unsigned_integer(&u, &p, p)) goto bad;
417 if (!*p) tvec_readword(tv, &d, &p, delims, 0);
418
419 if (u > (size_t)-1) goto rangerr;
420 for (t = u, unit = size_units; *unit; unit++) {
421 if (t > (size_t)-1/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;
432end:
433 dstr_destroy(&d);
434 return (rc);
435
436bad:
437 tvec_error(tv, "invalid %s `%s'", what, d.buf);
438 rc = -1; goto end;
439
440rangerr:
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
460static 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}
b64eb60f 474
67b5031e 475/*----- Floating-point utilities ------------------------------------------*/
b64eb60f 476
67b5031e
MW
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 @y@ is sufficiently close to the
483 * reference @x@, or zero if it's definitely different.
484 */
3efcfd2d 485
67b5031e
MW
486static int eqish_floating_p(double x, double y,
487 const struct tvec_floatinfo *fi)
3efcfd2d 488{
67b5031e
MW
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 - y/x; if (t < 0) t = -t; return (t < fi->delta);
501 default:
502 abort();
503 }
b64eb60f
MW
504}
505
67b5031e
MW
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 */
3efcfd2d 516
e63124bc
MW
517static 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
67b5031e
MW
609/* --- @parse_floating@ --- *
610 *
611 * Arguments: @double *x_out@ = where to put the result
814e42ff 612 * @const char *q_out@ = where to leave end pointer, or null
67b5031e
MW
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
814e42ff 620 * necessary errors. If @q_out@ is not null then trailing
adec5584
MW
621 * material is permitted and a pointer to it (or the end of the
622 * string) is left in @*q_out@.
67b5031e 623 */
e63124bc 624
814e42ff 625static int parse_floating(double *x_out, const char **q_out, const char *p,
e63124bc
MW
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
67b5031e 634 /* Check for special tokens. */
e63124bc
MW
635 if (STRCMP(p, ==, "#nan")) {
636#ifdef NAN
adec5584 637 if (q_out) *q_out = p + strlen(p);
e63124bc
MW
638 x = NAN; rc = 0;
639#else
640 tvec_error(tv, "NaN not supported on this system");
641 rc = -1; goto end;
642#endif
67b5031e
MW
643 }
644
645 else if (STRCMP(p, ==, "#inf") ||
646 STRCMP(p, ==, "#+inf") || STRCMP(p, ==, "+#inf")) {
3efcfd2d 647#ifdef INFINITY
adec5584 648 if (q_out) *q_out = p + strlen(p);
e63124bc
MW
649 x = INFINITY; rc = 0;
650#else
651 tvec_error(tv, "infinity not supported on this system");
652 rc = -1; goto end;
653#endif
67b5031e
MW
654 }
655
656 else if (STRCMP(p, ==, "#-inf") || STRCMP(p, ==, "-#inf")) {
3efcfd2d 657#ifdef INFINITY
adec5584 658 if (q_out) *q_out = p + strlen(p);
e63124bc
MW
659 x = -INFINITY; rc = 0;
660#else
661 tvec_error(tv, "infinity not supported on this system");
662 rc = -1; goto end;
663#endif
67b5031e
MW
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 {
e63124bc
MW
670 pp = p;
671 if (*pp == '+' || *pp == '-') pp++;
672 if (*pp == '.') pp++;
673 if (!ISDIGIT(*pp)) {
3efcfd2d 674 tvec_syntax(tv, *p ? *p : fgetc(tv->fp), "floating-point number");
e63124bc
MW
675 rc = -1; goto end;
676 }
67b5031e
MW
677
678 /* Parse the number using the system parser. */
e63124bc
MW
679 olderr = errno; errno = 0;
680 x = strtod(p, &q);
adec5584
MW
681 if (q_out) *q_out = q;
682 else if (*q) { tvec_syntax(tv, *q, "end-of-line"); rc = -1; goto end; }
e63124bc 683 if (errno && (errno != ERANGE || (x > 0 ? -x : x) == HUGE_VAL)) {
814e42ff
MW
684 tvec_error(tv, "invalid floating-point number `%.*s': %s",
685 (int)(q - p), p, strerror(errno));
e63124bc
MW
686 rc = -1; goto end;
687 }
688 errno = olderr;
689 }
690
67b5031e 691 /* Check that the number is acceptable. */
e63124bc
MW
692 if (NANP(x) && fi && !(fi->f&TVFF_NANOK)) {
693 tvec_error(tv, "#nan not allowed here");
694 rc = -1; goto end;
695 }
67b5031e 696
e63124bc
MW
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
67b5031e
MW
715 /* All done. */
716 *x_out = x; rc = 0;
717end:
718 dstr_destroy(&d);
719 return (rc);
720}
721
722/*----- String utilities --------------------------------------------------*/
723
724/* Special character name table. */
725static 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
774static 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
796static 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
822static 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
860static 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
888static 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); }
e63124bc
MW
897}
898
67b5031e
MW
899static void maybe_format_signed_char
900 (const struct gprintf_ops *gops, void *go, long i)
b64eb60f 901{
67b5031e
MW
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); }
b64eb60f
MW
908}
909
67b5031e
MW
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
922static int read_charesc(int *ch_out, struct tvec_state *tv)
b64eb60f 923{
b64eb60f
MW
924 int ch, i, esc;
925 unsigned f = 0;
926#define f_brace 1u
927
e63124bc
MW
928 ch = getc(tv->fp);
929 switch (ch) {
67b5031e
MW
930
931 /* Things we shouldn't find. */
932 case EOF: case '\n': return (tvec_syntax(tv, ch, "string escape"));
933
934 /* Single-character escapes. */
e63124bc
MW
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
67b5031e 947 /* Hex escapes, with and without braces. */
e63124bc
MW
948 case 'x':
949 ch = getc(tv->fp);
950 if (ch == '{') { f |= f_brace; ch = getc(tv->fp); }
951 else f &= ~f_brace;
67b5031e
MW
952 esc = chtodig(ch);
953 if (esc < 0 || esc >= 16) return (tvec_syntax(tv, ch, "hex digit"));
e63124bc 954 for (;;) {
67b5031e
MW
955 ch = getc(tv->fp); i = chtodig(ch); if (i < 0 || i >= 16) break;
956 esc = 16*esc + i;
e63124bc
MW
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
67b5031e
MW
966 /* Other things, primarily octal escapes. */
967 case '{':
968 f |= f_brace; ch = getc(tv->fp);
969 /* fall through */
e63124bc
MW
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 }
67b5031e
MW
979 if (f&f_brace) {
980 ch = getc(tv->fp);
981 if (ch != '}') return (tvec_syntax(tv, ch, "`}'"));
982 }
e63124bc
MW
983 if (esc > UCHAR_MAX)
984 return (tvec_error(tv,
985 "character code %d out of range", esc));
67b5031e 986 *ch_out = esc; break;
e63124bc
MW
987 } else
988 return (tvec_syntax(tv, ch, "string escape"));
989 }
990
67b5031e 991 /* Done. */
e63124bc
MW
992 return (0);
993
994#undef f_brace
995}
996
67b5031e
MW
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
e63124bc
MW
1012static int read_quoted_string(dstr *d, int quote, struct tvec_state *tv)
1013{
1014 int ch;
b64eb60f
MW
1015
1016 for (;;) {
1017 ch = getc(tv->fp);
b64eb60f
MW
1018 switch (ch) {
1019 case EOF: case '\n':
e63124bc 1020 return (tvec_syntax(tv, ch, "`%c'", quote));
b64eb60f
MW
1021 case '\\':
1022 if (quote == '\'') goto ordinary;
e63124bc 1023 ch = getc(tv->fp); if (ch == '\n') { tv->lno++; break; }
67b5031e 1024 ungetc(ch, tv->fp); if (read_charesc(&ch, tv)) return (-1);
e63124bc 1025 goto ordinary;
b64eb60f
MW
1026 default:
1027 if (ch == quote) goto end;
1028 ordinary:
1029 DPUTC(d, ch);
1030 break;
1031 }
1032 }
1033
1034end:
1035 DPUTZ(d);
882a39c1 1036 return (0);
e63124bc 1037}
b64eb60f 1038
67b5031e
MW
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 */
b64eb60f
MW
1049
1050static 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:
882a39c1
MW
1060 tvec_syntax(tv, ch, "bareword");
1061 rc = -1; goto end;
b64eb60f
MW
1062 case '\n':
1063 if (s == ESCAPE) { tv->lno++; goto addch; }
1064 if (s == WORD) pos = d->len;
882a39c1 1065 ungetc(ch, tv->fp); if (tvec_nexttoken(tv)) { rc = -1; goto end; }
b64eb60f
MW
1066 DPUTC(d, ' '); s = SPACE;
1067 break;
67b5031e 1068 case '"': case '\'': case '!': case '#': case ')': case '}': case ']':
882a39c1 1069 if (s == SPACE) { ungetc(ch, tv->fp); goto done; }
b64eb60f
MW
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
1085done:
1086 if (s == SPACE) d->len = pos;
882a39c1
MW
1087 DPUTZ(d); rc = 0;
1088end:
1089 return (rc);
b64eb60f
MW
1090}
1091
67b5031e
MW
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
1107enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 };
b64eb60f
MW
1108static 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
67b5031e
MW
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
1144static 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
882a39c1 1179static int read_compound_string(void **p_inout, size_t *sz_inout,
67b5031e
MW
1180 unsigned code, unsigned f,
1181 struct tvec_state *tv)
b64eb60f 1182{
67b5031e 1183 const codec_class *ccl; unsigned cdf;
b64eb60f
MW
1184 codec *cdc;
1185 dstr d = DSTR_INIT, w = DSTR_INIT;
1186 char *p;
67b5031e
MW
1187 const char *q;
1188 void *pp = 0; size_t sz;
1189 unsigned long n;
882a39c1 1190 int ch, err, rc;
b64eb60f 1191
67b5031e
MW
1192 set_up_encoding(&ccl, &cdf, code); cdc = 0;
1193
1194 if (tvec_nexttoken(tv)) return (tvec_syntax(tv, fgetc(tv->fp), "string"));
b64eb60f
MW
1195 do {
1196 ch = getc(tv->fp);
67b5031e
MW
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;
adec5584 1222 DRESET(&w); tvec_readword(tv, &w, 0, ";", "character name");
67b5031e
MW
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;
b64eb60f 1234 ungetc(ch, tv->fp);
adec5584 1235 DRESET(&w); tvec_readword(tv, &w, 0, ";", "`!'-keyword");
67b5031e
MW
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);
adec5584 1254 if (tvec_readword(tv, &w, 0, ";{", "repeat count"))
67b5031e
MW
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 }
b64eb60f 1287 break;
67b5031e 1288
b64eb60f 1289 default:
67b5031e
MW
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);
adec5584
MW
1300 if (tvec_readword(tv, &w, 0, ";",
1301 "%s-encoded fragment", ccl->name))
67b5031e
MW
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;
b64eb60f
MW
1313 }
1314 } while (!tvec_nexttoken(tv));
1315
1316done:
67b5031e
MW
1317 /* Wrap things up. */
1318 if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
1319 cdc = 0;
b64eb60f
MW
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;
882a39c1 1323 rc = 0;
67b5031e 1324
882a39c1 1325end:
67b5031e
MW
1326 /* Clean up any debris. */
1327 if (cdc) cdc->ops->destroy(cdc);
1328 if (pp) xfree(pp);
b64eb60f 1329 dstr_destroy(&d); dstr_destroy(&w);
882a39c1 1330 return (rc);
b64eb60f
MW
1331}
1332
b64eb60f
MW
1333/*----- Signed and unsigned integer types ---------------------------------*/
1334
c81c35df
MW
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
b64eb60f
MW
1347static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd)
1348 { rv->i = 0; }
1349
1350static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd)
1351 { rv->u = 0; }
1352
c81c35df
MW
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
b64eb60f
MW
1363static 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
1367static 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
c81c35df
MW
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
b64eb60f
MW
1386static 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
1390static 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
c81c35df
MW
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
b64eb60f
MW
1408static int frombuf_int(buf *b, union tvec_regval *rv,
1409 const struct tvec_regdef *rd)
882a39c1 1410 { return (signed_from_buf(b, &rv->i)); }
b64eb60f
MW
1411
1412static int frombuf_uint(buf *b, union tvec_regval *rv,
1413 const struct tvec_regdef *rd)
1414 { return (unsigned_from_buf(b, &rv->u)); }
1415
c81c35df
MW
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
882a39c1
MW
1452static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd,
1453 struct tvec_state *tv)
b64eb60f
MW
1454{
1455 dstr d = DSTR_INIT;
882a39c1 1456 int rc;
b64eb60f 1457
adec5584
MW
1458 if (tvec_readword(tv, &d, 0, ";", "signed integer"))
1459 { rc = -1; goto end; }
c81c35df
MW
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; }
882a39c1
MW
1462 rc = 0;
1463end:
b64eb60f 1464 dstr_destroy(&d);
882a39c1 1465 return (rc);
b64eb60f
MW
1466}
1467
882a39c1
MW
1468static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd,
1469 struct tvec_state *tv)
b64eb60f
MW
1470{
1471 dstr d = DSTR_INIT;
882a39c1 1472 int rc;
b64eb60f 1473
adec5584
MW
1474 if (tvec_readword(tv, &d, 0, ";", "unsigned integer"))
1475 { rc = -1; goto end; }
c81c35df
MW
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; }
882a39c1
MW
1478 rc = 0;
1479end:
b64eb60f 1480 dstr_destroy(&d);
882a39c1 1481 return (rc);
b64eb60f
MW
1482}
1483
c81c35df
MW
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
b64eb60f
MW
1500static void dump_int(const union tvec_regval *rv,
1501 const struct tvec_regdef *rd,
e63124bc
MW
1502 unsigned style,
1503 const struct gprintf_ops *gops, void *go)
b64eb60f 1504{
b64eb60f 1505
e63124bc 1506 gprintf(gops, go, "%ld", rv->i);
b64eb60f 1507 if (!(style&TVSF_COMPACT)) {
3efcfd2d
MW
1508 gprintf(gops, go, " ; = ");
1509 format_signed_hex(gops, go, rv->i);
1510 maybe_format_signed_char(gops, go, rv->i);
b64eb60f
MW
1511 }
1512}
1513
1514static void dump_uint(const union tvec_regval *rv,
1515 const struct tvec_regdef *rd,
e63124bc
MW
1516 unsigned style,
1517 const struct gprintf_ops *gops, void *go)
b64eb60f 1518{
e63124bc
MW
1519 gprintf(gops, go, "%lu", rv->u);
1520 if (!(style&TVSF_COMPACT)) {
3efcfd2d
MW
1521 gprintf(gops, go, " ; = ");
1522 format_unsigned_hex(gops, go, rv->u);
1523 maybe_format_unsigned_char(gops, go, rv->u);
e63124bc 1524 }
b64eb60f
MW
1525}
1526
c81c35df 1527/* Integer type definitions. */
b64eb60f 1528const struct tvec_regty tvty_int = {
3efcfd2d 1529 init_int, trivial_release, eq_int,
b64eb60f
MW
1530 tobuf_int, frombuf_int,
1531 parse_int, dump_int
1532};
c81c35df
MW
1533const struct tvec_regty tvty_uint = {
1534 init_uint, trivial_release, eq_uint,
1535 tobuf_uint, frombuf_uint,
1536 parse_uint, dump_uint
1537};
b64eb60f 1538
c81c35df 1539/* Predefined integer ranges. */
b64eb60f
MW
1540const 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 };
b64eb60f
MW
1548const 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 },
13ee7406 1556 tvrange_u32 = { 0, 4294967295 };
b64eb60f 1557
67b5031e
MW
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
b64eb60f
MW
1575int tvec_claimeq_int(struct tvec_state *tv, long i0, long i1,
1576 const char *file, unsigned lno, const char *expr)
1577{
3efcfd2d 1578 tv->out[0].v.i = i0; tv->in[0].v.i = i1;
b64eb60f
MW
1579 return (tvec_claimeq(tv, &tvty_int, 0, file, lno, expr));
1580}
1581
67b5031e
MW
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
b64eb60f
MW
1599int tvec_claimeq_uint(struct tvec_state *tv,
1600 unsigned long u0, unsigned long u1,
1601 const char *file, unsigned lno, const char *expr)
1602{
3efcfd2d 1603 tv->out[0].v.u = u0; tv->in[0].v.u = u1;
b64eb60f
MW
1604 return (tvec_claimeq(tv, &tvty_uint, 0, file, lno, expr));
1605}
1606
3efcfd2d 1607/*----- Floating-point type -----------------------------------------------*/
e63124bc 1608
814e42ff 1609/* --- @int_float@ --- *
c81c35df
MW
1610 *
1611 * Arguments: @union tvec_regval *rv@ = register value
1612 * @const struct tvec_regdef *rd@ = register definition
1613 *
1614 * Returns: ---
1615 *
1616 * Use: Initialize a register value.
1617 *
1618 * Floating-point values are initialized to zero.
1619 */
1620
e63124bc
MW
1621static void init_float(union tvec_regval *rv, const struct tvec_regdef *rd)
1622 { rv->f = 0.0; }
e63124bc 1623
c81c35df
MW
1624/* --- @eq_float@ --- *
1625 *
1626 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1627 * @const struct tvec_regdef *rd@ = register definition
1628 *
1629 * Returns: Nonzero if the values are equal, zero if unequal
1630 *
1631 * Use: Compare register values for equality.
1632 *
1633 * Floating-point values may be considered equal if their
1634 * absolute or relative difference is sufficiently small, as
1635 * described in the register definition.
1636 */
1637
e63124bc
MW
1638static int eq_float(const union tvec_regval *rv0,
1639 const union tvec_regval *rv1,
1640 const struct tvec_regdef *rd)
1641 { return (eqish_floating_p(rv0->f, rv1->f, rd->arg.p)); }
1642
c81c35df
MW
1643/* --- @tobuf_float@ --- *
1644 *
1645 * Arguments: @buf *b@ = buffer
1646 * @const union tvec_regval *rv@ = register value
1647 * @const struct tvec_regdef *rd@ = register definition
1648 *
1649 * Returns: Zero on success, %$-1$% on failure.
1650 *
1651 * Use: Serialize a register value to a buffer.
1652 *
1653 * Floating-point values are serialized as little-endian
1654 * IEEE 754 Binary64.
1655 */
1656
e63124bc
MW
1657static int tobuf_float(buf *b, const union tvec_regval *rv,
1658 const struct tvec_regdef *rd)
1659 { return (buf_putf64l(b, rv->f)); }
c81c35df
MW
1660
1661/* --- @frombuf_float@ --- *
1662 *
1663 * Arguments: @buf *b@ = buffer
1664 * @union tvec_regval *rv@ = register value
1665 * @const struct tvec_regdef *rd@ = register definition
1666 *
1667 * Returns: Zero on success, %$-1$% on failure.
1668 *
1669 * Use: Deserialize a register value from a buffer.
1670 *
1671 * Floating-point values are serialized as little-endian
1672 * IEEE 754 Binary64.
1673 */
1674
e63124bc
MW
1675static int frombuf_float(buf *b, union tvec_regval *rv,
1676 const struct tvec_regdef *rd)
1677 { return (buf_getf64l(b, &rv->f)); }
1678
c81c35df
MW
1679/* --- @parse_float@ --- *
1680 *
1681 * Arguments: @union tvec_regval *rv@ = register value
1682 * @const struct tvec_regdef *rd@ = register definition
1683 * @struct tvec_state *tv@ = test-vector state
1684 *
1685 * Returns: Zero on success, %$-1$% on error.
1686 *
1687 * Use: Parse a register value from an input file.
1688 *
1689 * Floating-point values are either NaN (%|#nan|%, if supported
1690 * by the platform); positive or negative infinity (%|#inf|%,
1691 * %|+#inf|%, or %|#+inf|% (preferring the last), and %|-#inf|%
1692 * or %|#-inf|% (preferring the latter), if supported by the
1693 * platform); or a number in strtod(3) syntax.
1694 */
1695
e63124bc
MW
1696static int parse_float(union tvec_regval *rv, const struct tvec_regdef *rd,
1697 struct tvec_state *tv)
1698{
1699 dstr d = DSTR_INIT;
1700 int rc;
1701
adec5584 1702 if (tvec_readword(tv, &d, 0, ";", "floating-point number"))
e63124bc 1703 { rc = -1; goto end; }
814e42ff
MW
1704 if (parse_floating(&rv->f, 0, d.buf, rd->arg.p, tv))
1705 { rc = -1; goto end; }
c81c35df 1706 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
e63124bc
MW
1707 rc = 0;
1708end:
1709 dstr_destroy(&d);
1710 return (rc);
1711}
1712
c81c35df
MW
1713/* --- @dump_float@ --- *
1714 *
1715 * Arguments: @const union tvec_regval *rv@ = register value
1716 * @const struct tvec_regdef *rd@ = register definition
1717 * @unsigned style@ = output style (@TVSF_...@)
1718 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1719 *
1720 * Returns: ---
1721 *
1722 * Use: Dump a register value to the format output.
1723 *
1724 * Floating-point values are dumped in decimal or as a special
1725 * token beginning with `%|#|%'. Some effort is taken to ensure
1726 * that the output is sufficient to uniquely identify the
1727 * original value, but, honestly, C makes this really hard.
1728 */
1729
e63124bc
MW
1730static void dump_float(const union tvec_regval *rv,
1731 const struct tvec_regdef *rd,
1732 unsigned style,
1733 const struct gprintf_ops *gops, void *go)
1734 { format_floating(gops, go, rv->f); }
1735
c81c35df 1736/* Floating-point type definition. */
e63124bc 1737const struct tvec_regty tvty_float = {
3efcfd2d 1738 init_float, trivial_release, eq_float,
e63124bc
MW
1739 tobuf_float, frombuf_float,
1740 parse_float, dump_float
1741};
1742
c81c35df
MW
1743/* Predefined floating-point ranges. */
1744const struct tvec_floatinfo
1745 tvflt_finite = { TVFF_EXACT, -DBL_MAX, DBL_MAX, 0.0 },
1746 tvflt_nonneg = { TVFF_EXACT, 0, DBL_MAX, 0.0 };
1747
67b5031e
MW
1748/* --- @tvec_claimeqish_float@ --- *
1749 *
1750 * Arguments: @struct tvec_state *tv@ = test-vector state
1751 * @double f0, f1@ = two floating-point numbers
1752 * @unsigned f@ = flags (@TVFF_...@)
1753 * @double delta@ = maximum tolerable difference
1754 * @const char *file@, @unsigned @lno@ = calling file and line
1755 * @const char *expr@ = the expression to quote on failure
1756 *
1757 * Returns: Nonzero if @f0@ and @u1@ are sufficiently close, otherwise
1758 * zero.
1759 *
1760 * Use: Check that values of @f0@ and @f1@ are sufficiently close.
1761 * As for @tvec_claim@ above, a test case is automatically begun
1762 * and ended if none is already underway. If the values are
1763 * too far apart, then @tvec_fail@ is called, quoting @expr@,
1764 * and the mismatched values are dumped: @f0@ is printed as the
1765 * output value and @f1@ is printed as the input reference.
1766 *
1767 * The details for the comparison are as follows.
1768 *
1769 * * A NaN value matches any other NaN, and nothing else.
1770 *
1771 * * An infinity matches another infinity of the same sign,
1772 * and nothing else.
1773 *
1774 * * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any
1775 * representable number matches only itself: in particular,
1776 * positive and negative zero are considered distinct.
1777 * (This allows tests to check that they land on the correct
1778 * side of branch cuts, for example.)
1779 *
1780 * * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches
1781 * %$y$% when %$|x - y| < \delta$%.
1782 *
1783 * * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
1784 * %$y$% when %$|1 - y/x| < \delta$%. (Note that this
1785 * criterion is asymmetric FIXME
1786 */
1787
e63124bc
MW
1788int tvec_claimeqish_float(struct tvec_state *tv,
1789 double f0, double f1, unsigned f, double delta,
1790 const char *file, unsigned lno,
1791 const char *expr)
1792{
1793 struct tvec_floatinfo fi;
1794 union tvec_misc arg;
1795
1796 fi.f = f; fi.min = fi.max = 0.0; fi.delta = delta; arg.p = &fi;
3efcfd2d 1797 tv->out[0].v.f = f0; tv->in[0].v.f = f1;
e63124bc
MW
1798 return (tvec_claimeq(tv, &tvty_float, &arg, file, lno, expr));
1799}
e63124bc 1800
67b5031e
MW
1801/* --- @tvec_claimeq_float@ --- *
1802 *
1803 * Arguments: @struct tvec_state *tv@ = test-vector state
1804 * @double f0, f1@ = two floating-point numbers
1805 * @const char *file@, @unsigned @lno@ = calling file and line
1806 * @const char *expr@ = the expression to quote on failure
1807 *
1808 * Returns: Nonzero if @f0@ and @u1@ are identical, otherwise zero.
1809 *
1810 * Use: Check that values of @f0@ and @f1@ are identical. The
1811 * function is exactly equivalent to @tvec_claimeqish_float@
1812 * with @f == TVFF_EXACT@.
1813 */
1814
1815int tvec_claimeq_float(struct tvec_state *tv,
1816 double f0, double f1,
1817 const char *file, unsigned lno,
1818 const char *expr)
1819{
1820 return (tvec_claimeqish_float(tv, f0, f1, TVFF_EXACT, 0.0,
1821 file, lno, expr));
1822}
1823
814e42ff
MW
1824/*----- Durations ---------------------------------------------------------*/
1825
1826/* A duration is a floating-point number of seconds. Initialization and
1827 * teardown, equality comparison, and serialization are as for floating-point
1828 * values.
1829 */
1830
1831static const struct duration_unit {
1832 const char *unit;
1833 double scale;
1834 unsigned f;
1835#define DUF_PREFER 1u
1836} duration_units[] = {
1837 { "Ys", 1e+24, 0 },
1838 { "Zs", 1e+21, 0 },
1839 { "Es", 1e+18, 0 },
1840 { "Ps", 1e+15, 0 },
1841 { "Ts", 1e+12, 0 },
1842 { "Gs", 1e+9, 0 },
1843 { "Ms", 1e+6, 0 },
1844 { "ks", 1e+3, 0 },
1845 { "hs", 1e+2, 0 },
1846 { "das", 1e+1, 0 },
1847
1848 { "yr", 31557600.0, DUF_PREFER },
1849 { "y", 31557600.0, 0 },
1850 { "day", 86400.0, DUF_PREFER },
1851 { "dy", 86400.0, 0 },
1852 { "d", 86400.0, 0 },
1853 { "hr", 3600.0, DUF_PREFER },
1854 { "hour", 3600.0, 0 },
1855 { "h", 3600.0, 0 },
1856 { "min", 60.0, DUF_PREFER },
1857 { "m", 60.0, 0 },
1858
1859 { "s", 1.0, DUF_PREFER },
1860 { "sec", 1.0, 0 },
1861
1862 { "ds", 1e-1, 0 },
1863 { "cs", 1e-2, 0 },
1864 { "ms", 1e-3, DUF_PREFER },
1865 { "µs", 1e-6, DUF_PREFER },
1866 { "ns", 1e-9, DUF_PREFER },
1867 { "ps", 1e-12, DUF_PREFER },
1868 { "fs", 1e-15, DUF_PREFER },
1869 { "as", 1e-18, DUF_PREFER },
1870 { "zs", 1e-21, DUF_PREFER },
1871 { "ys", 1e-24, DUF_PREFER },
1872
1873 { 0 }
1874};
1875
13ee7406
MW
1876/* --- @tvec_parsedurunit@ --- *
1877 *
1878 * Arguments: @double *scale_out@ = where to leave the scale
1879 * @const char **p_inout@ = input unit string, updated
1880 *
1881 * Returns: Zero on success, %$-1$% on error.
1882 *
1883 * Use: If @*p_inout@ begins with a unit string followed by the end
1884 * of the string or some non-alphanumeric character, then store
1885 * the corresponding scale factor in @*scale_out@, advance
1886 * @*p_inout@ past the unit string, and return zero. Otherwise,
1887 * return %$-1$%.
1888 */
1889
1890int tvec_parsedurunit(double *scale_out, const char **p_inout)
1891{
1892 const char *p = *p_inout, *q;
1893 const struct duration_unit *u;
1894 size_t n;
1895
1896 while (ISSPACE(*p)) p++;
1897 for (q = p; *q && ISALNUM(*q); q++);
1898 n = q - p; if (!n) { *scale_out = 1.0; return (0); }
1899
1900 for (u = duration_units; u->unit; u++)
1901 if (STRNCMP(p, ==, u->unit, n) && !u->unit[n])
1902 { *scale_out = u->scale; *p_inout = q; return (0); }
1903 return (-1);
1904}
1905
814e42ff
MW
1906/* --- @parse_duration@ --- *
1907 *
1908 * Arguments: @union tvec_regval *rv@ = register value
1909 * @const struct tvec_regdef *rd@ = register definition
1910 * @struct tvec_state *tv@ = test-vector state
1911 *
1912 * Returns: Zero on success, %$-1$% on error.
1913 *
1914 * Use: Parse a register value from an input file.
1915 *
1916 * Duration values are finite nonnegative floating-point
1917 * numbers in @strtod@ syntax, optionally followed by a unit .
1918 */
1919
1920static int parse_duration(union tvec_regval *rv,
1921 const struct tvec_regdef *rd,
1922 struct tvec_state *tv)
1923{
1924 const struct duration_unit *u;
1925 const char *q;
adec5584 1926 dstr d = DSTR_INIT;
814e42ff
MW
1927 double t;
1928 int rc;
1929
adec5584 1930 if (tvec_readword(tv, &d, 0, ";", "duration")) { rc = -1; goto end; }
814e42ff
MW
1931 if (parse_floating(&t, &q, d.buf,
1932 rd->arg.p ? rd->arg.p : &tvflt_nonneg, tv))
1933 { rc = -1; goto end; }
1934
adec5584
MW
1935 if (!*q) tvec_readword(tv, &d, &q, ";", 0);
1936 if (*q) {
814e42ff
MW
1937 for (u = duration_units; u->unit; u++)
1938 if (STRCMP(q, ==, u->unit)) { t *= u->scale; goto found_unit; }
1939 rc = tvec_syntax(tv, *q, "end-of-line"); goto end;
1940 found_unit:;
1941 }
1942
1943 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
1944 rv->f = t; rc = 0;
1945end:
1946 dstr_destroy(&d);
1947 return (rc);
1948}
1949
1950/* --- @dump_duration@ --- *
1951 *
1952 * Arguments: @const union tvec_regval *rv@ = register value
1953 * @const struct tvec_regdef *rd@ = register definition
1954 * @unsigned style@ = output style (@TVSF_...@)
1955 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1956 *
1957 * Returns: ---
1958 *
1959 * Use: Dump a register value to the format output.
1960 *
1961 * Durations are dumped as a human-palatable scaled value with
1962 * unit, and, if compact style is not requested, as a raw number
1963 * of seconds at full precision as a comment.
1964 */
1965
1966static void dump_duration(const union tvec_regval *rv,
1967 const struct tvec_regdef *rd,
1968 unsigned style,
1969 const struct gprintf_ops *gops, void *go)
1970{
1971 const struct duration_unit *u;
1972 double t = rv->f;
1973
1974 if (!t) u = 0;
1975 else {
1976 for (u = duration_units; u->scale > t && u[1].unit; u++);
1977 t /= u->scale;
1978 }
1979
1980 gprintf(gops, go, "%.4g %s", t, u ? u->unit : "s");
1981 if (!(style&TVSF_COMPACT)) {
1982 gprintf(gops, go, "; = ");
1983 format_floating(gops, go, rv->f);
1984 gprintf(gops, go, " s");
1985 }
1986}
1987
1988/* Duration type definition. */
1989const struct tvec_regty tvty_duration = {
1990 init_float, trivial_release, eq_float,
1991 tobuf_float, frombuf_float,
1992 parse_duration, dump_duration
1993};
1994
b64eb60f
MW
1995/*----- Enumerations ------------------------------------------------------*/
1996
c81c35df
MW
1997/* --- @init_tenum@ --- *
1998 *
1999 * Arguments: @union tvec_regval *rv@ = register value
2000 * @const struct tvec_regdef *rd@ = register definition
2001 *
2002 * Returns: ---
2003 *
2004 * Use: Initialize a register value.
2005 *
2006 * Integer and floating-point enumeration values are initialized
2007 * as their underlying representations. Pointer enumerations
2008 * are initialized to %|#nil|%.
2009 */
2010
3efcfd2d
MW
2011#define init_ienum init_int
2012#define init_uenum init_uint
2013#define init_fenum init_float
c81c35df 2014
3efcfd2d
MW
2015static void init_penum(union tvec_regval *rv, const struct tvec_regdef *rd)
2016 { rv->p = 0; }
b64eb60f 2017
c81c35df
MW
2018/* --- @eq_tenum@ --- *
2019 *
2020 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
2021 * @const struct tvec_regdef *rd@ = register definition
2022 *
2023 * Returns: Nonzero if the values are equal, zero if unequal
2024 *
2025 * Use: Compare register values for equality.
2026 *
2027 * Integer and floating-point enumeration values are compared as
2028 * their underlying representations; in particular, floating-
2029 * point enumerations may compare equal if their absolute or
2030 * relative difference is sufficiently small. Pointer
2031 * enumerations are compared as pointers.
2032 */
2033
3efcfd2d
MW
2034#define eq_ienum eq_int
2035#define eq_uenum eq_uint
c81c35df 2036
3efcfd2d
MW
2037static int eq_fenum(const union tvec_regval *rv0,
2038 const union tvec_regval *rv1,
2039 const struct tvec_regdef *rd)
b64eb60f 2040{
3efcfd2d
MW
2041 const struct tvec_fenuminfo *ei = rd->arg.p;
2042 return (eqish_floating_p(rv0->f, rv1->f, ei->fi));
b64eb60f 2043}
c81c35df 2044
3efcfd2d
MW
2045static int eq_penum(const union tvec_regval *rv0,
2046 const union tvec_regval *rv1,
2047 const struct tvec_regdef *rd)
2048 { return (rv0->p == rv1->p); }
b64eb60f 2049
c81c35df
MW
2050/* --- @tobuf_tenum@ --- *
2051 *
2052 * Arguments: @buf *b@ = buffer
2053 * @const union tvec_regval *rv@ = register value
2054 * @const struct tvec_regdef *rd@ = register definition
2055 *
2056 * Returns: Zero on success, %$-1$% on failure.
2057 *
2058 * Use: Serialize a register value to a buffer.
2059 *
2060 * Integer and floating-point enumeration values are serialized
2061 * as their underlying representations. Pointer enumerations
2062 * are serialized as the signed integer index into the
2063 * association table; %|#nil|% serializes as %$-1$%, and
2064 * unrecognized pointers cause failure.
2065 */
2066
3efcfd2d
MW
2067#define tobuf_ienum tobuf_int
2068#define tobuf_uenum tobuf_uint
2069#define tobuf_fenum tobuf_float
c81c35df 2070
3efcfd2d
MW
2071static int tobuf_penum(buf *b, const union tvec_regval *rv,
2072 const struct tvec_regdef *rd)
b64eb60f 2073{
3efcfd2d 2074 const struct tvec_penuminfo *pei = rd->arg.p;
e63124bc
MW
2075 const struct tvec_passoc *pa;
2076 long i;
b64eb60f 2077
3efcfd2d
MW
2078 for (pa = pei->av, i = 0; pa->tag; pa++, i++)
2079 if (pa->p == rv->p) goto found;
2080 if (!rv->p) i = -1;
2081 else return (-1);
2082found:
2083 return (signed_to_buf(b, i));
b64eb60f
MW
2084}
2085
c81c35df
MW
2086/* --- @frombuf_tenum@ --- *
2087 *
2088 * Arguments: @buf *b@ = buffer
2089 * @union tvec_regval *rv@ = register value
2090 * @const struct tvec_regdef *rd@ = register definition
2091 *
2092 * Returns: Zero on success, %$-1$% on failure.
2093 *
2094 * Use: Deserialize a register value from a buffer.
2095 *
2096 * Integer and floating-point enumeration values are serialized
2097 * as their underlying representations. Pointer enumerations
2098 * are serialized as the signed integer index into the
2099 * association table; %|#nil|% serializes as %$-1$%; out-of-
2100 * range indices cause failure.
2101 */
2102
3efcfd2d
MW
2103#define frombuf_ienum frombuf_int
2104#define frombuf_uenum frombuf_uint
2105#define frombuf_fenum frombuf_float
2106static int frombuf_penum(buf *b, union tvec_regval *rv,
b64eb60f
MW
2107 const struct tvec_regdef *rd)
2108{
3efcfd2d 2109 const struct tvec_penuminfo *pei = rd->arg.p;
e63124bc
MW
2110 const struct tvec_passoc *pa;
2111 long i, n;
b64eb60f 2112
3efcfd2d
MW
2113 for (pa = pei->av, n = 0; pa->tag; pa++, n++);
2114 if (signed_from_buf(b, &i)) return (-1);
2115 if (0 <= i && i < n) rv->p = (/*unconst*/ void *)pei->av[i].p;
2116 else if (i == -1) rv->p = 0;
adec5584 2117 else { buf_break(b); return (-1); }
3efcfd2d 2118 return (0);
b64eb60f
MW
2119}
2120
c81c35df
MW
2121/* --- @parse_tenum@ --- *
2122 *
2123 * Arguments: @union tvec_regval *rv@ = register value
2124 * @const struct tvec_regdef *rd@ = register definition
2125 * @struct tvec_state *tv@ = test-vector state
2126 *
2127 * Returns: Zero on success, %$-1$% on error.
2128 *
2129 * Use: Parse a register value from an input file.
2130 *
2131 * An enumerated value may be given by name or as a literal
2132 * value. For enumerations based on numeric types, the literal
2133 * values can be written in the same syntax as the underlying
2134 * values. For enumerations based on pointers, the only
2135 * permitted literal is %|#nil|%, which denotes a null pointer.
2136 */
2137
3efcfd2d
MW
2138#define DEFPARSE_ENUM(tag_, ty, slot) \
2139 static int parse_##slot##enum(union tvec_regval *rv, \
2140 const struct tvec_regdef *rd, \
2141 struct tvec_state *tv) \
2142 { \
2143 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2144 const struct tvec_##slot##assoc *a; \
2145 dstr d = DSTR_INIT; \
2146 int rc; \
2147 \
adec5584 2148 if (tvec_readword(tv, &d, 0, ";", "enumeration tag or " LITSTR_##tag_)) \
3efcfd2d
MW
2149 { rc = -1; goto end; } \
2150 for (a = ei->av; a->tag; a++) \
2151 if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; } \
2152 MISSING_##tag_ \
2153 done: \
2154 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } \
2155 rc = 0; \
2156 end: \
2157 dstr_destroy(&d); \
2158 return (rc); \
2159 }
b64eb60f 2160
3efcfd2d
MW
2161#define LITSTR_INT "literal signed integer"
2162#define FOUND_INT rv->i = a->i;
2163#define MISSING_INT if (parse_signed(&rv->i, d.buf, ei->ir, tv)) \
2164 { rc = -1; goto end; }
2165
2166#define LITSTR_UINT "literal unsigned integer"
2167#define FOUND_UINT rv->u = a->u;
2168#define MISSING_UINT if (parse_unsigned(&rv->u, d.buf, ei->ur, tv)) \
2169 { rc = -1; goto end; }
2170
2171#define LITSTR_FLT "literal floating-point number, " \
2172 "`#-inf', `#+inf', or `#nan'"
2173#define FOUND_FLT rv->f = a->f;
814e42ff 2174#define MISSING_FLT if (parse_floating(&rv->f, 0, d.buf, ei->fi, tv)) \
3efcfd2d
MW
2175 { rc = -1; goto end; }
2176
2177#define LITSTR_PTR "`#nil'"
2178#define FOUND_PTR rv->p = (/*unconst*/ void *)a->p;
2179#define MISSING_PTR if (STRCMP(d.buf, ==, "#nil")) \
2180 rv->p = 0; \
2181 else { \
2182 tvec_error(tv, "unknown `%s' value `%s'", \
2183 ei->name, d.buf); \
2184 rc = -1; goto end; \
2185 }
2186
2187TVEC_MISCSLOTS(DEFPARSE_ENUM)
2188
2189#undef LITSTR_INT
2190#undef FOUND_INT
2191#undef MISSING_INT
2192
2193#undef LITSTR_UINT
2194#undef FOUND_UINT
2195#undef MISSING_UINT
2196
2197#undef LITSTR_FLT
2198#undef FOUND_FLT
2199#undef MISSING_FLT
2200
2201#undef LITSTR_PTR
2202#undef FOUND_PTR
2203#undef MISSING_PTR
2204
2205#undef DEFPARSE_ENUM
2206
c81c35df
MW
2207/* --- @dump_tenum@ --- *
2208 *
2209 * Arguments: @const union tvec_regval *rv@ = register value
2210 * @const struct tvec_regdef *rd@ = register definition
2211 * @unsigned style@ = output style (@TVSF_...@)
2212 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2213 *
2214 * Returns: ---
2215 *
2216 * Use: Dump a register value to the format output.
2217 *
2218 * Enumeration values are dumped as their symbolic names, if
2219 * possible, with the underlying values provided as a comment
2220 * unless compact output is requested, as for the underlying
2221 * representation. A null pointer is printed as %|#nil|%;
2222 * non-null pointers are printed as %|#<TYPE PTR>|%, with the
2223 * enumeration TYPE and the raw pointer PTR printed with the
2224 * system's %|%p|% format specifier.
2225 */
2226
2227
3efcfd2d
MW
2228#define DEFDUMP_ENUM(tag_, ty, slot) \
2229 static void dump_##slot##enum(const union tvec_regval *rv, \
2230 const struct tvec_regdef *rd, \
2231 unsigned style, \
2232 const struct gprintf_ops *gops, void *go) \
2233 { \
2234 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2235 const struct tvec_##slot##assoc *a; \
2236 \
2237 for (a = ei->av; a->tag; a++) \
2238 if (rv->slot == a->slot) { \
2239 gprintf(gops, go, "%s", a->tag); \
2240 if (style&TVSF_COMPACT) return; \
2241 gprintf(gops, go, " ; = "); break; \
2242 } \
2243 \
2244 PRINTRAW_##tag_ \
b64eb60f
MW
2245 }
2246
3efcfd2d 2247#define MAYBE_PRINT_EXTRA \
c81c35df 2248 if (style&TVSF_COMPACT) /* nothing to do */; \
3efcfd2d
MW
2249 else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; } \
2250 else if (1) { gprintf(gops, go, " = "); goto _extra; } \
2251 else _extra:
b64eb60f 2252
3efcfd2d
MW
2253#define PRINTRAW_INT gprintf(gops, go, "%ld", rv->i); \
2254 MAYBE_PRINT_EXTRA { \
2255 format_signed_hex(gops, go, rv->i); \
2256 maybe_format_signed_char(gops, go, rv->i); \
2257 }
b64eb60f 2258
3efcfd2d
MW
2259#define PRINTRAW_UINT gprintf(gops, go, "%lu", rv->u); \
2260 MAYBE_PRINT_EXTRA { \
2261 format_unsigned_hex(gops, go, rv->u); \
2262 maybe_format_unsigned_char(gops, go, rv->u); \
2263 }
2264
2265#define PRINTRAW_FLT format_floating(gops, go, rv->f);
2266
2267#define PRINTRAW_PTR if (!rv->p) gprintf(gops, go, "#nil"); \
e63124bc 2268 else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
b64eb60f 2269
3efcfd2d 2270TVEC_MISCSLOTS(DEFDUMP_ENUM)
b64eb60f 2271
3efcfd2d
MW
2272#undef PRINTRAW_INT
2273#undef PRINTRAW_UINT
2274#undef PRINTRAW_FLT
2275#undef PRINTRAW_PTR
2276
2277#undef MAYBE_PRINT_EXTRA
2278#undef DEFDUMP_ENUM
2279
c81c35df 2280/* Enumeration type definitions. */
3efcfd2d
MW
2281#define DEFTY_ENUM(tag, ty, slot) \
2282 const struct tvec_regty tvty_##slot##enum = { \
2283 init_##slot##enum, trivial_release, eq_##slot##enum, \
2284 tobuf_##slot##enum, frombuf_##slot##enum, \
2285 parse_##slot##enum, dump_##slot##enum \
2286 };
2287TVEC_MISCSLOTS(DEFTY_ENUM)
2288#undef DEFTY_ENUM
b64eb60f 2289
c81c35df 2290/* Predefined enumeration types. */
e63124bc
MW
2291static const struct tvec_iassoc bool_assoc[] = {
2292 { "nil", 0 },
2293 { "false", 0 },
2294 { "f", 0 },
2295 { "no", 0 },
2296 { "n", 0 },
2297 { "off", 0 },
2298
2299 { "t", 1 },
2300 { "true", 1 },
2301 { "yes", 1 },
2302 { "y", 1 },
2303 { "on", 1 },
2304
20ba6b0b 2305 TVEC_ENDENUM
e63124bc
MW
2306};
2307
2308const struct tvec_ienuminfo tvenum_bool =
3efcfd2d 2309 { "bool", bool_assoc, &tvrange_int };
e63124bc 2310
20ba6b0b
MW
2311static const struct tvec_iassoc cmp_assoc[] = {
2312 { "<", -1 },
2313 { "less", -1 },
2314 { "lt", -1 },
2315
2316 { "=", 0 },
2317 { "equal", 0 },
2318 { "eq", 0 },
2319
2320 { ">", +1 },
2321 { "greater", +1 },
2322 { "gt", +1 },
2323
2324 TVEC_ENDENUM
2325};
2326
2327const struct tvec_ienuminfo tvenum_cmp =
2328 { "cmp", cmp_assoc, &tvrange_int };
2329
67b5031e
MW
2330/* --- @tvec_claimeq_tenum@ --- *
2331 *
2332 * Arguments: @struct tvec_state *tv@ = test-vector state
2333 * @const struct tvec_typeenuminfo *ei@ = enumeration type info
2334 * @ty t0, t1@ = two values
2335 * @const char *file@, @unsigned @lno@ = calling file and line
2336 * @const char *expr@ = the expression to quote on failure
2337 *
2338 * Returns: Nonzero if @t0@ and @t1@ are equal, otherwise zero.
2339 *
2340 * Use: Check that values of @t0@ and @t1@ are equal. As for
2341 * @tvec_claim@ above, a test case is automatically begun and
2342 * ended if none is already underway. If the values are
2343 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2344 * mismatched values are dumped: @t0@ is printed as the output
2345 * value and @t1@ is printed as the input reference.
2346 */
2347
b64eb60f 2348#define DEFCLAIM(tag, ty, slot) \
e63124bc
MW
2349 int tvec_claimeq_##slot##enum \
2350 (struct tvec_state *tv, \
2351 const struct tvec_##slot##enuminfo *ei, ty e0, ty e1, \
2352 const char *file, unsigned lno, const char *expr) \
b64eb60f
MW
2353 { \
2354 union tvec_misc arg; \
2355 \
b64eb60f 2356 arg.p = ei; \
3efcfd2d
MW
2357 tv->out[0].v.slot = GET_##tag(e0); \
2358 tv->in[0].v.slot = GET_##tag(e1); \
2359 return (tvec_claimeq(tv, &tvty_##slot##enum, &arg, \
2360 file, lno, expr)); \
b64eb60f
MW
2361 }
2362#define GET_INT(e) (e)
2363#define GET_UINT(e) (e)
e63124bc 2364#define GET_FLT(e) (e)
b64eb60f
MW
2365#define GET_PTR(e) ((/*unconst*/ void *)(e))
2366TVEC_MISCSLOTS(DEFCLAIM)
2367#undef DEFCLAIM
2368#undef GET_INT
2369#undef GET_UINT
e63124bc 2370#undef GET_FLT
b64eb60f
MW
2371#undef GET_PTR
2372
2373/*----- Flag types --------------------------------------------------------*/
2374
c81c35df
MW
2375/* Flag types are initialized, compared, and serialized as unsigned
2376 * integers.
2377 */
2378
2379/* --- @parse_flags@ --- *
2380 *
2381 * Arguments: @union tvec_regval *rv@ = register value
2382 * @const struct tvec_regdef *rd@ = register definition
2383 * @struct tvec_state *tv@ = test-vector state
2384 *
2385 * Returns: Zero on success, %$-1$% on error.
2386 *
2387 * Use: Parse a register value from an input file.
2388 *
2389 * The input syntax is a sequence of items separated by `|'
2390 * signs. Each item may be the symbolic name of a field value,
2391 * or a literal unsigned integer. The masks associated with the
2392 * given symbolic names must be disjoint. The resulting
2393 * numerical value is simply the bitwise OR of the given values.
2394 */
2395
882a39c1
MW
2396static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
2397 struct tvec_state *tv)
b64eb60f
MW
2398{
2399 const struct tvec_flaginfo *fi = rd->arg.p;
2400 const struct tvec_flag *f;
2401 unsigned long m = 0, v = 0, t;
2402 dstr d = DSTR_INIT;
882a39c1 2403 int ch, rc;
b64eb60f
MW
2404
2405 for (;;) {
c81c35df
MW
2406
2407 /* Read the next item. */
882a39c1 2408 DRESET(&d);
adec5584 2409 if (tvec_readword(tv, &d, 0, "|;", "flag name or integer"))
882a39c1 2410 { rc = -1; goto end; }
b64eb60f 2411
c81c35df 2412 /* Try to find a matching entry in the table. */
b64eb60f
MW
2413 for (f = fi->fv; f->tag; f++)
2414 if (STRCMP(f->tag, ==, d.buf)) {
882a39c1
MW
2415 if (m&f->m)
2416 { tvec_error(tv, "colliding flag setting"); rc = -1; goto end; }
2417 else
2418 { m |= f->m; v |= f->v; goto next; }
b64eb60f
MW
2419 }
2420
c81c35df 2421 /* Otherwise, try to parse it as a raw integer. */
e63124bc
MW
2422 if (parse_unsigned(&t, d.buf, fi->range, tv))
2423 { rc = -1; goto end; }
882a39c1 2424 v |= t;
c81c35df 2425
b64eb60f 2426 next:
c81c35df
MW
2427 /* Advance to the next token. If it's a separator then consume it, and
2428 * go round again. Otherwise we stop here.
2429 */
b64eb60f 2430 if (tvec_nexttoken(tv)) break;
882a39c1
MW
2431 ch = getc(tv->fp);
2432 if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; }
c81c35df 2433 if (tvec_nexttoken(tv))
882a39c1 2434 { tvec_syntax(tv, '\n', "flag name or integer"); rc = -1; goto end; }
b64eb60f 2435 }
c81c35df
MW
2436
2437 /* Done. */
2438 rv->u = v; rc = 0;
882a39c1
MW
2439end:
2440 dstr_destroy(&d);
2441 return (rc);
b64eb60f
MW
2442}
2443
c81c35df
MW
2444/* --- @dump_flags@ --- *
2445 *
2446 * Arguments: @const union tvec_regval *rv@ = register value
2447 * @const struct tvec_regdef *rd@ = register definition
2448 * @unsigned style@ = output style (@TVSF_...@)
2449 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2450 *
2451 * Returns: ---
2452 *
2453 * Use: Dump a register value to the format output.
2454 *
2455 * The table of symbolic names and their associated values and
2456 * masks is repeatedly scanned, in order, to find disjoint
2457 * matches -- i.e., entries whose value matches the target value
2458 * in the bit positions indicated by the mask, and whose mask
2459 * doesn't overlap with any previously found matches; the names
2460 * are then output, separated by `|'. Any remaining nonzero
2461 * bits not covered by any of the matching masks are output as a
2462 * single literal integer, in hex.
2463 *
2464 * Unless compact output is requested, or no symbolic names were
2465 * found, the raw numeric value is also printed in hex, as a
2466 * comment.
2467 */
2468
b64eb60f
MW
2469static void dump_flags(const union tvec_regval *rv,
2470 const struct tvec_regdef *rd,
e63124bc
MW
2471 unsigned style,
2472 const struct gprintf_ops *gops, void *go)
b64eb60f
MW
2473{
2474 const struct tvec_flaginfo *fi = rd->arg.p;
2475 const struct tvec_flag *f;
c81c35df 2476 unsigned long m = ~0ul, v = rv->u;
b64eb60f
MW
2477 const char *sep;
2478
2479 for (f = fi->fv, sep = ""; f->tag; f++)
2480 if ((m&f->m) && (v&f->m) == f->v) {
e63124bc 2481 gprintf(gops, go, "%s%s", sep, f->tag); m &= ~f->m;
b64eb60f
MW
2482 sep = style&TVSF_COMPACT ? "|" : " | ";
2483 }
2484
e63124bc 2485 if (v&m) gprintf(gops, go, "%s0x%0*lx", sep, hex_width(v), v&m);
b64eb60f 2486
c81c35df 2487 if (m != ~0ul && !(style&TVSF_COMPACT))
e63124bc 2488 gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
b64eb60f
MW
2489}
2490
c81c35df 2491/* Flags type definition. */
b64eb60f 2492const struct tvec_regty tvty_flags = {
3efcfd2d 2493 init_uint, trivial_release, eq_uint,
b64eb60f
MW
2494 tobuf_uint, frombuf_uint,
2495 parse_flags, dump_flags
2496};
2497
67b5031e
MW
2498/* --- @tvec_claimeq_flags@ --- *
2499 *
2500 * Arguments: @struct tvec_state *tv@ = test-vector state
2501 * @const struct tvec_flaginfo *fi@ = flags type info
2502 * @unsigned long f0, f1@ = two values
2503 * @const char *file@, @unsigned @lno@ = calling file and line
2504 * @const char *expr@ = the expression to quote on failure
2505 *
2506 * Returns: Nonzero if @f0@ and @f1@ are equal, otherwise zero.
2507 *
2508 * Use: Check that values of @f0@ and @f1@ are equal. As for
2509 * @tvec_claim@ above, a test case is automatically begun and
2510 * ended if none is already underway. If the values are
2511 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2512 * mismatched values are dumped: @f0@ is printed as the output
2513 * value and @f1@ is printed as the input reference.
2514 */
2515
b64eb60f
MW
2516int tvec_claimeq_flags(struct tvec_state *tv,
2517 const struct tvec_flaginfo *fi,
2518 unsigned long f0, unsigned long f1,
2519 const char *file, unsigned lno, const char *expr)
2520{
2521 union tvec_misc arg;
2522
3efcfd2d 2523 arg.p = fi; tv->out[0].v.u = f0; tv->in[0].v.u = f1;
b64eb60f
MW
2524 return (tvec_claimeq(tv, &tvty_flags, &arg, file, lno, expr));
2525}
2526
e63124bc
MW
2527/*----- Characters --------------------------------------------------------*/
2528
c81c35df
MW
2529/* Character values are initialized and compared as signed integers. */
2530
2531/* --- @tobuf_char@ --- *
2532 *
2533 * Arguments: @buf *b@ = buffer
2534 * @const union tvec_regval *rv@ = register value
2535 * @const struct tvec_regdef *rd@ = register definition
2536 *
2537 * Returns: Zero on success, %$-1$% on failure.
2538 *
2539 * Use: Serialize a register value to a buffer.
2540 *
2541 * Character values are serialized as little-endian 32-bit
2542 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2543 */
2544
e63124bc 2545static int tobuf_char(buf *b, const union tvec_regval *rv,
67b5031e 2546 const struct tvec_regdef *rd)
e63124bc
MW
2547{
2548 uint32 u;
c81c35df 2549
e63124bc
MW
2550 if (0 <= rv->i && rv->i <= UCHAR_MAX) u = rv->i;
2551 else if (rv->i == EOF) u = MASK32;
adec5584 2552 else { buf_break(b); return (-1); }
e63124bc
MW
2553 return (buf_putu32l(b, u));
2554}
2555
c81c35df
MW
2556/* --- @frombuf_char@ --- *
2557 *
2558 * Arguments: @buf *b@ = buffer
2559 * @union tvec_regval *rv@ = register value
2560 * @const struct tvec_regdef *rd@ = register definition
2561 *
2562 * Returns: Zero on success, %$-1$% on failure.
2563 *
2564 * Use: Deserialize a register value from a buffer.
2565 *
2566 * Character values are serialized as little-endian 32-bit
2567 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2568 */
2569
e63124bc 2570static int frombuf_char(buf *b, union tvec_regval *rv,
67b5031e 2571 const struct tvec_regdef *rd)
e63124bc
MW
2572{
2573 uint32 u;
2574
2575 if (buf_getu32l(b, &u)) return (-1);
2576 if (0 <= u && u <= UCHAR_MAX) rv->i = u;
2577 else if (u == MASK32) rv->i = EOF;
adec5584 2578 else { buf_break(b); return (-1); }
e63124bc
MW
2579 return (0);
2580}
2581
c81c35df
MW
2582/* --- @parse_char@ --- *
2583 *
2584 * Arguments: @union tvec_regval *rv@ = register value
2585 * @const struct tvec_regdef *rd@ = register definition
2586 * @struct tvec_state *tv@ = test-vector state
2587 *
2588 * Returns: Zero on success, %$-1$% on error.
2589 *
2590 * Use: Parse a register value from an input file.
2591 *
2592 * A character value can be given by symbolic name, with a
2593 * leading `%|#|%'; or a character or `%|\|%'-escape sequence,
2594 * optionally in single quotes.
2595 *
2596 * The following escape sequences and character names are
2597 * recognized.
2598 *
2599 * * `%|#eof|%' is the special end-of-file marker.
2600 *
2601 * * `%|#nul|%' is the NUL character, sometimes used to
2602 * terminate strings.
2603 *
2604 * * `%|bell|%', `%|bel|%', `%|ding|%', or `%|\a|%' is the BEL
2605 * character used to ring the terminal bell (or do some other
2606 * thing to attract the user's attention).
2607 *
2608 * * %|#backspace|%, %|#bs|%, or %|\b|% is the backspace
2609 * character, used to move the cursor backwords by one cell.
2610 *
2611 * * %|#escape|% %|#esc|%, or%|\e|% is the escape character,
2612 * used to introduce special terminal commands.
2613 *
2614 * * %|#formfeed|%, %|#ff|%, or %|\f|% is the formfeed
2615 * character, used to separate pages of text.
2616 *
2617 * * %|#newline|%, %|#linefeed|%, %|#lf|%, %|#nl|%, or %|\n|% is
2618 * the newline character, used to terminate lines of text or
2619 * advance the cursor to the next line (perhaps without
2620 * returning it to the start of the line).
2621 *
2622 * * %|#return|%, %|#carriage-return|%, %|#cr|%, or %|\r|% is
2623 * the carriage-return character, used to return the cursor to
2624 * the start of the line.
2625 *
2626 * * %|#tab|%, %|#horizontal-tab|%, %|#ht|%, or %|\t|% is the
2627 * tab character, used to advance the cursor to the next tab
2628 * stop on the current line.
2629 *
2630 * * %|#vertical-tab|%, %|#vt|%, %|\v|% is the vertical tab
2631 * character.
2632 *
2633 * * %|#space|%, %|#spc|% is the space character.
2634 *
2635 * * %|#delete|%, %|#del|% is the delete character, used to
2636 * erase the most recent character.
2637 *
2638 * * %|\'|% is the single-quote character.
2639 *
2640 * * %|\\|% is the backslash character.
2641 *
2642 * * %|\"|% is the double-quote character.
2643 *
2644 * * %|\NNN|% or %|\{NNN}|% is the character with code NNN in
2645 * octal. The NNN may be up to three digits long.
2646 *
2647 * * %|\xNN|% or %|\x{NN}|% is the character with code NNN in
2648 * hexadecimal.
2649 */
2650
e63124bc
MW
2651static int parse_char(union tvec_regval *rv, const struct tvec_regdef *rd,
2652 struct tvec_state *tv)
2653{
2654 dstr d = DSTR_INIT;
2655 int ch, rc;
2656 unsigned f = 0;
2657#define f_quote 1u
2658
c81c35df 2659 /* Inspect the character to see what we're up against. */
e63124bc 2660 ch = getc(tv->fp);
c81c35df 2661
e63124bc 2662 if (ch == '#') {
c81c35df
MW
2663 /* It looks like a special token. Push the `%|#|%' back and fetch the
2664 * whole word. If there's just the `%|#|%' after all, then treat it as
2665 * literal.
2666 */
2667
e63124bc 2668 ungetc(ch, tv->fp);
adec5584
MW
2669 if (tvec_readword(tv, &d, 0, ";", "character name"))
2670 { rc = -1; goto end; }
c81c35df
MW
2671 if (STRCMP(d.buf, !=, "#")) {
2672 if (read_charname(&ch, d.buf, RCF_EOFOK)) {
2673 rc = tvec_error(tv, "unknown character name `%s'", d.buf);
2674 goto end;
2675 }
2676 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
2677 rv->i = ch; rc = 0; goto end;
e63124bc 2678 }
e63124bc
MW
2679 }
2680
c81c35df
MW
2681 /* If this is a single quote then we expect to see a matching one later,
2682 * and we should process backslash escapes. Get the next character and see
2683 * what happens.
2684 */
e63124bc 2685 if (ch == '\'') { f |= f_quote; ch = getc(tv->fp); }
c81c35df
MW
2686
2687 /* Main character dispatch. */
e63124bc 2688 switch (ch) {
c81c35df 2689
67b5031e 2690 case ';':
c81c35df 2691 /* Unquoted, semicolon begins a comment. */
67b5031e 2692 if (!(f&f_quote)) { rc = tvec_syntax(tv, ch, "character"); goto end; }
c81c35df
MW
2693 else goto plain;
2694
67b5031e 2695 case '\n':
c81c35df
MW
2696 /* A newline. If we saw a single quote, then treat that as literal.
2697 * Otherwise this is an error.
2698 */
2699 if (!(f&f_quote)) goto nochar;
2700 else { f &= ~f_quote; ungetc(ch, tv->fp); ch = '\''; goto plain; }
2701
67b5031e 2702 case EOF:
c81c35df
MW
2703 /* End-of-file. Similar to newline, but with slightly different
2704 * effects on the parse state.
2705 */
2706 if (!(f&f_quote)) goto nochar;
2707 else { f &= ~f_quote; ch = '\''; goto plain; }
2708
2709 case '\'': nochar:
2710 /* A single quote. This must be the second of a pair, and there should
2711 * have been a character or escape sequence between them.
2712 */
e63124bc 2713 rc = tvec_syntax(tv, ch, "character"); goto end;
c81c35df 2714
e63124bc 2715 case '\\':
c81c35df 2716 /* A backslash. Read a character escape. */
67b5031e 2717 if (read_charesc(&ch, tv)) return (-1);
c81c35df 2718
e63124bc 2719 default: plain:
c81c35df 2720 /* Anything else. Treat as literal. */
e63124bc
MW
2721 rv->i = ch; break;
2722 }
c81c35df
MW
2723
2724 /* If we saw an opening quote, then expect the closing quote. */
e63124bc
MW
2725 if (f&f_quote) {
2726 ch = getc(tv->fp);
2727 if (ch != '\'') { rc = tvec_syntax(tv, ch, "`''"); goto end; }
2728 }
c81c35df
MW
2729
2730 /* Done. */
e63124bc
MW
2731 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
2732 rc = 0;
2733end:
2734 dstr_destroy(&d);
2735 return (rc);
2736
2737#undef f_quote
2738}
2739
c81c35df
MW
2740/* --- @dump_char@ --- *
2741 *
2742 * Arguments: @const union tvec_regval *rv@ = register value
2743 * @const struct tvec_regdef *rd@ = register definition
2744 * @unsigned style@ = output style (@TVSF_...@)
2745 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2746 *
2747 * Returns: ---
2748 *
2749 * Use: Dump a register value to the format output.
2750 *
2751 * Character values are dumped as their symbolic names, if any,
2752 * or as a character or escape sequence within single quotes
2753 * (which may be omitted in compact style). If compact output
2754 * is not requested, then the single-quoted representation (for
2755 * characters dumped as symbolic names) and integer code in
2756 * decimal and hex are printed as a comment.
2757 */
2758
e63124bc
MW
2759static void dump_char(const union tvec_regval *rv,
2760 const struct tvec_regdef *rd,
2761 unsigned style,
2762 const struct gprintf_ops *gops, void *go)
2763{
67b5031e
MW
2764 const char *p;
2765 unsigned f = 0;
2766#define f_semi 1u
2767
c81c35df 2768 /* Print a character name if we can find one. */
67b5031e
MW
2769 p = find_charname(rv->i, (style&TVSF_COMPACT) ? CTF_SHORT : CTF_PREFER);
2770 if (p) {
2771 gprintf(gops, go, "%s", p);
2772 if (style&TVSF_COMPACT) return;
2773 else { gprintf(gops, go, " ;"); f |= f_semi; }
2774 }
2775
c81c35df
MW
2776 /* If the character isn't @EOF@ then print it as a single-quoted thing.
2777 * In compact style, see if we can omit the quotes.
2778 */
67b5031e
MW
2779 if (rv->i >= 0) {
2780 if (f&f_semi) gprintf(gops, go, " = ");
2781 switch (rv->i) {
2782 case ' ': case '\\': case '\'': quote:
2783 format_char(gops, go, rv->i);
2784 break;
2785 default:
2786 if (!(style&TVSF_COMPACT) || !isprint(rv->i)) goto quote;
2787 gprintf(gops, go, "%c", (int)rv->i);
2788 return;
2789 }
2790 }
e63124bc 2791
c81c35df 2792 /* And the character code as an integer. */
e63124bc 2793 if (!(style&TVSF_COMPACT)) {
67b5031e
MW
2794 if (!(f&f_semi)) gprintf(gops, go, " ;");
2795 gprintf(gops, go, " = %ld = ", rv->i);
3efcfd2d 2796 format_signed_hex(gops, go, rv->i);
e63124bc 2797 }
67b5031e
MW
2798
2799#undef f_semi
e63124bc
MW
2800}
2801
c81c35df 2802/* Character type definition. */
e63124bc 2803const struct tvec_regty tvty_char = {
3efcfd2d 2804 init_int, trivial_release, eq_int,
e63124bc
MW
2805 tobuf_char, frombuf_char,
2806 parse_char, dump_char
2807};
2808
67b5031e
MW
2809/* --- @tvec_claimeq_char@ --- *
2810 *
2811 * Arguments: @struct tvec_state *tv@ = test-vector state
2812 * @int ch0, ch1@ = two character codes
2813 * @const char *file@, @unsigned @lno@ = calling file and line
2814 * @const char *expr@ = the expression to quote on failure
2815 *
2816 * Returns: Nonzero if @ch0@ and @ch1@ are equal, otherwise zero.
2817 *
2818 * Use: Check that values of @ch0@ and @ch1@ are equal. As for
2819 * @tvec_claim@ above, a test case is automatically begun and
2820 * ended if none is already underway. If the values are
2821 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2822 * mismatched values are dumped: @ch0@ is printed as the output
2823 * value and @ch1@ is printed as the input reference.
2824 */
2825
e63124bc
MW
2826int tvec_claimeq_char(struct tvec_state *tv, int c0, int c1,
2827 const char *file, unsigned lno, const char *expr)
2828{
3efcfd2d 2829 tv->out[0].v.i = c0; tv->in[0].v.i = c1;
e63124bc
MW
2830 return (tvec_claimeq(tv, &tvty_char, 0, file, lno, expr));
2831}
2832
b64eb60f
MW
2833/*----- Text and byte strings ---------------------------------------------*/
2834
c81c35df
MW
2835/* --- @init_text@, @init_bytes@ --- *
2836 *
2837 * Arguments: @union tvec_regval *rv@ = register value
2838 * @const struct tvec_regdef *rd@ = register definition
2839 *
2840 * Returns: ---
2841 *
2842 * Use: Initialize a register value.
2843 *
2844 * Text and binary string values are initialized with a null
2845 * pointer and zero length.
2846 */
2847
2848static void init_text(union tvec_regval *rv, const struct tvec_regdef *rd)
2849 { rv->text.p = 0; rv->text.sz = 0; }
b64eb60f
MW
2850
2851static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd)
2852 { rv->bytes.p = 0; rv->bytes.sz = 0; }
2853
c81c35df
MW
2854/* --- @release_string@, @release_bytes@ --- *
2855 *
2856 * Arguments: @const union tvec_regval *rv@ = register value
2857 * @const struct tvec_regdef *rd@ = register definition
2858 *
2859 * Returns: ---
2860 *
2861 * Use: Release resources held by a register value.
2862 *
2863 * Text and binary string buffers are freed.
2864 */
2865
2866static void release_text(union tvec_regval *rv,
2867 const struct tvec_regdef *rd)
2868 { xfree(rv->text.p); }
b64eb60f
MW
2869
2870static void release_bytes(union tvec_regval *rv,
2871 const struct tvec_regdef *rd)
2872 { xfree(rv->bytes.p); }
2873
c81c35df
MW
2874/* --- @eq_text@, @eq_bytes@ --- *
2875 *
2876 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
2877 * @const struct tvec_regdef *rd@ = register definition
2878 *
2879 * Returns: Nonzero if the values are equal, zero if unequal
2880 *
2881 * Use: Compare register values for equality.
2882 */
2883
2884static int eq_text(const union tvec_regval *rv0,
2885 const union tvec_regval *rv1,
2886 const struct tvec_regdef *rd)
b64eb60f 2887{
c81c35df
MW
2888 return (rv0->text.sz == rv1->text.sz &&
2889 (!rv0->text.sz ||
2890 MEMCMP(rv0->text.p, ==, rv1->text.p, rv1->text.sz)));
b64eb60f
MW
2891}
2892
2893static int eq_bytes(const union tvec_regval *rv0,
2894 const union tvec_regval *rv1,
2895 const struct tvec_regdef *rd)
2896{
2897 return (rv0->bytes.sz == rv1->bytes.sz &&
2898 (!rv0->bytes.sz ||
2899 MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz)));
2900}
2901
c81c35df
MW
2902/* --- @tobuf_text@, @tobuf_bytes@ --- *
2903 *
2904 * Arguments: @buf *b@ = buffer
2905 * @const union tvec_regval *rv@ = register value
2906 * @const struct tvec_regdef *rd@ = register definition
2907 *
2908 * Returns: Zero on success, %$-1$% on failure.
2909 *
2910 * Use: Serialize a register value to a buffer.
2911 *
2912 * Text and binary string values are serialized as a little-
2913 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
2914 * of string data.
2915 */
2916
2917static int tobuf_text(buf *b, const union tvec_regval *rv,
2918 const struct tvec_regdef *rd)
2919 { return (buf_putmem64l(b, rv->text.p, rv->text.sz)); }
b64eb60f
MW
2920
2921static int tobuf_bytes(buf *b, const union tvec_regval *rv,
2922 const struct tvec_regdef *rd)
c81c35df 2923 { return (buf_putmem64l(b, rv->bytes.p, rv->bytes.sz)); }
b64eb60f 2924
c81c35df
MW
2925/* --- @frombuf_text@, @frombuf_bytes@ --- *
2926 *
2927 * Arguments: @buf *b@ = buffer
2928 * @union tvec_regval *rv@ = register value
2929 * @const struct tvec_regdef *rd@ = register definition
2930 *
2931 * Returns: Zero on success, %$-1$% on failure.
2932 *
2933 * Use: Deserialize a register value from a buffer.
2934 *
2935 * Text and binary string values are serialized as a little-
2936 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
2937 * of string data.
2938 */
2939
2940static int frombuf_text(buf *b, union tvec_regval *rv,
2941 const struct tvec_regdef *rd)
b64eb60f
MW
2942{
2943 const void *p;
2944 size_t sz;
2945
c81c35df
MW
2946 p = buf_getmem64l(b, &sz); if (!p) return (-1);
2947 tvec_alloctext(rv, sz); memcpy(rv->text.p, p, sz); rv->text.p[sz] = 0;
b64eb60f
MW
2948 return (0);
2949}
2950
2951static int frombuf_bytes(buf *b, union tvec_regval *rv,
2952 const struct tvec_regdef *rd)
2953{
2954 const void *p;
2955 size_t sz;
2956
c81c35df 2957 p = buf_getmem64l(b, &sz); if (!p) return (-1);
b64eb60f
MW
2958 tvec_allocbytes(rv, sz); memcpy(rv->bytes.p, p, sz);
2959 return (0);
2960}
2961
c81c35df
MW
2962/* --- @check_string_length@ --- *
2963 *
2964 * Arguments: @size_t sz@ = found string length
2965 * @const struct tvec_urange *ur@ = acceptable range
2966 * @struct tvec_state *tv@ = test-vector state
2967 *
2968 * Returns: Zero on success, %$-1$% on error.
2969 *
2970 * Use: Checks that @sz@ is within the bounds described by @ur@,
2971 * reporting an error if not.
2972 */
2973
882a39c1
MW
2974static int check_string_length(size_t sz, const struct tvec_urange *ur,
2975 struct tvec_state *tv)
b64eb60f
MW
2976{
2977 if (ur && (ur->min > sz || sz > ur->max))
882a39c1 2978 return (tvec_error(tv,
67b5031e 2979 "invalid string length %lu; must be in [%lu .. %lu]",
882a39c1
MW
2980 (unsigned long)sz, ur->min, ur->max));
2981 return (0);
b64eb60f
MW
2982}
2983
c81c35df
MW
2984/* --- @parse_text@, @parse_bytes@ --- *
2985 *
2986 * Arguments: @union tvec_regval *rv@ = register value
2987 * @const struct tvec_regdef *rd@ = register definition
2988 * @struct tvec_state *tv@ = test-vector state
2989 *
2990 * Returns: Zero on success, %$-1$% on error.
2991 *
2992 * Use: Parse a register value from an input file.
2993 *
2994 * The input format for both kinds of strings is basically the
2995 * same: a `compound string', consisting of
2996 *
2997 * * single-quoted strings, which are interpreted entirely
2998 * literally, but can't contain single quotes or newlines;
2999 *
3000 * * double-quoted strings, in which `%|\|%'-escapes are
3001 * interpreted as for characters;
3002 *
3003 * * character names, marked by an initial `%|#|%' sign;
3004 *
3005 * * special tokens marked by an initial `%|!|%' sign; or
3006 *
3007 * * barewords interpreted according to the current coding
3008 * scheme.
3009 *
3010 * The special tokens are
3011 *
3012 * * `%|!bare|%', which causes subsequent sequences of
3013 * barewords to be treated as plain text;
3014 *
3015 * * `%|!hex|%', `%|!base32|%', `%|!base64|%', which cause
3016 * subsequent barewords to be decoded in the requested
3017 * manner.
3018 *
3019 * * `%|!repeat|% %$n$% %|{|% %%\textit{string}%% %|}|%',
3020 * which includes %$n$% copies of the (compound) string.
3021 *
3022 * The only difference between text and binary strings is that
3023 * the initial coding scheme is %|bare|% for text strings and
3024 * %|hex|% for binary strings.
3025 */
3026
3027static int parse_text(union tvec_regval *rv, const struct tvec_regdef *rd,
3028 struct tvec_state *tv)
b64eb60f 3029{
c81c35df 3030 void *p = rv->text.p;
b64eb60f 3031
c81c35df 3032 if (read_compound_string(&p, &rv->text.sz, TVCODE_BARE, 0, tv))
67b5031e 3033 return (-1);
c81c35df
MW
3034 rv->text.p = p;
3035 if (check_string_length(rv->text.sz, rd->arg.p, tv)) return (-1);
882a39c1 3036 return (0);
b64eb60f
MW
3037}
3038
882a39c1
MW
3039static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd,
3040 struct tvec_state *tv)
b64eb60f
MW
3041{
3042 void *p = rv->bytes.p;
3043
67b5031e
MW
3044 if (read_compound_string(&p, &rv->bytes.sz, TVCODE_HEX, 0, tv))
3045 return (-1);
882a39c1
MW
3046 rv->bytes.p = p;
3047 if (check_string_length(rv->bytes.sz, rd->arg.p, tv)) return (-1);
3048 return (0);
b64eb60f
MW
3049}
3050
c81c35df
MW
3051/* --- @dump_text@, @dump_bytes@ --- *
3052 *
3053 * Arguments: @const union tvec_regval *rv@ = register value
3054 * @const struct tvec_regdef *rd@ = register definition
3055 * @unsigned style@ = output style (@TVSF_...@)
3056 * @const struct gprintf_ops *gops@, @void *gp@ = format output
3057 *
3058 * Returns: ---
3059 *
3060 * Use: Dump a register value to the format output.
3061 *
3062 * Text string values are dumped as plain text, in double quotes
3063 * if necessary, and using backslash escape sequences for
3064 * nonprintable characters. Unless compact output is requested,
3065 * strings consisting of multiple lines are dumped with each
3066 * line of the string on a separate output line.
3067 *
3068 * Binary string values are dumped in hexadecimal. In compact
3069 * style, the output simply consists of a single block of hex
3070 * digits. Otherwise, the dump is a display consisting of
3071 * groups of hex digits, with comments showing the offset (if
3072 * the string is long enough) and the corresponding plain text.
3073 *
3074 * Empty strings are dumped as %|""|%.
3075 */
3076
3077static void dump_text(const union tvec_regval *rv,
3078 const struct tvec_regdef *rd,
3079 unsigned style,
3080 const struct gprintf_ops *gops, void *go)
b64eb60f
MW
3081{
3082 const unsigned char *p, *q, *l;
b64eb60f
MW
3083 unsigned f = 0;
3084#define f_nonword 1u
3085#define f_newline 2u
3086
c81c35df 3087 if (!rv->text.sz) { gprintf(gops, go, "\"\""); return; }
b64eb60f 3088
c81c35df 3089 p = (const unsigned char *)rv->text.p; l = p + rv->text.sz;
67b5031e
MW
3090 switch (*p) {
3091 case '!': case '#': case ';': case '"': case '\'':
3092 case '(': case '{': case '[': case ']': case '}': case ')':
3093 f |= f_nonword; break;
3094 }
b64eb60f
MW
3095 for (q = p; q < l; q++)
3096 if (*q == '\n' && q != l - 1) f |= f_newline;
3097 else if (!*q || !isgraph(*q) || *q == '\\') f |= f_nonword;
e63124bc 3098 if (f&f_newline) { gprintf(gops, go, "\n\t"); goto quote; }
b64eb60f 3099 else if (f&f_nonword) goto quote;
67b5031e 3100
c81c35df 3101 gops->putm(go, (const char *)p, rv->text.sz);
67b5031e 3102 return;
b64eb60f
MW
3103
3104quote:
e63124bc 3105 gprintf(gops, go, "\"");
b64eb60f 3106 for (q = p; q < l; q++)
e63124bc
MW
3107 if (!isprint(*q) || *q == '"') {
3108 if (p < q) gops->putm(go, (const char *)p, q - p);
67b5031e 3109 if (*q != '\n' || (style&TVSF_COMPACT))
3efcfd2d 3110 format_charesc(gops, go, *q, FCF_BRACE);
67b5031e
MW
3111 else {
3112 if (q + 1 == l) { gprintf(gops, go, "\\n\""); return; }
3113 else gprintf(gops, go, "\\n\"\n\t\"");
3114 }
3115 p = q + 1;
b64eb60f 3116 }
e63124bc
MW
3117 if (p < q) gops->putm(go, (const char *)p, q - p);
3118 gprintf(gops, go, "\"");
b64eb60f
MW
3119
3120#undef f_nonword
3121#undef f_newline
3122}
3123
3124static void dump_bytes(const union tvec_regval *rv,
3125 const struct tvec_regdef *rd,
e63124bc
MW
3126 unsigned style,
3127 const struct gprintf_ops *gops, void *go)
b64eb60f
MW
3128{
3129 const unsigned char *p = rv->bytes.p, *l = p + rv->bytes.sz;
3130 size_t off, sz = rv->bytes.sz;
3131 unsigned i, n;
3132 int wd;
3133
3134 if (!sz) {
e63124bc 3135 gprintf(gops, go, style&TVSF_COMPACT ? "\"\"" : "\"\" ; empty");
b64eb60f
MW
3136 return;
3137 }
3138
3139 if (style&TVSF_COMPACT) {
e63124bc 3140 while (p < l) gprintf(gops, go, "%02x", *p++);
b64eb60f
MW
3141 return;
3142 }
3143
e63124bc 3144 if (sz > 16) gprintf(gops, go, "\n\t");
b64eb60f
MW
3145
3146 off = 0; wd = hex_width(sz);
3147 while (p < l) {
3148 if (l - p < 16) n = l - p;
3149 else n = 16;
3150
67b5031e 3151 for (i = 0; i < n; i++) {
e63124bc
MW
3152 if (i < n) gprintf(gops, go, "%02x", p[i]);
3153 else gprintf(gops, go, " ");
67b5031e 3154 if (i < n - 1 && i%4 == 3) gprintf(gops, go, " ");
b64eb60f 3155 }
e63124bc
MW
3156 gprintf(gops, go, " ; ");
3157 if (sz > 16) gprintf(gops, go, "[%0*lx] ", wd, (unsigned long)off);
b64eb60f 3158 for (i = 0; i < n; i++)
e63124bc 3159 gprintf(gops, go, "%c", isprint(p[i]) ? p[i] : '.');
b64eb60f 3160 p += n; off += n;
e63124bc 3161 if (p < l) gprintf(gops, go, "\n\t");
b64eb60f
MW
3162 }
3163}
3164
c81c35df
MW
3165/* Text and byte string type definitions. */
3166const struct tvec_regty tvty_text = {
3167 init_text, release_text, eq_text,
3168 tobuf_text, frombuf_text,
3169 parse_text, dump_text
b64eb60f 3170};
b64eb60f 3171const struct tvec_regty tvty_bytes = {
e63124bc 3172 init_bytes, release_bytes, eq_bytes,
b64eb60f
MW
3173 tobuf_bytes, frombuf_bytes,
3174 parse_bytes, dump_bytes
3175};
3176
c81c35df 3177/* --- @tvec_claimeq_text@ --- *
67b5031e
MW
3178 *
3179 * Arguments: @struct tvec_state *tv@ = test-vector state
3180 * @const char *p0@, @size_t sz0@ = first string with length
3181 * @const char *p1@, @size_t sz1@ = second string with length
3182 * @const char *file@, @unsigned @lno@ = calling file and line
3183 * @const char *expr@ = the expression to quote on failure
3184 *
3185 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3186 * zero.
3187 *
3188 * Use: Check that strings at @p0@ and @p1@ are equal. As for
3189 * @tvec_claim@ above, a test case is automatically begun and
3190 * ended if none is already underway. If the values are
3191 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3192 * mismatched values are dumped: @p0@ is printed as the output
3193 * value and @p1@ is printed as the input reference.
3194 */
3195
c81c35df
MW
3196int tvec_claimeq_text(struct tvec_state *tv,
3197 const char *p0, size_t sz0,
3198 const char *p1, size_t sz1,
3199 const char *file, unsigned lno, const char *expr)
b64eb60f 3200{
c81c35df
MW
3201 tv->out[0].v.text.p = (/*unconst*/ char *)p0; tv->out[0].v.text.sz = sz0;
3202 tv->in[0].v.text.p =(/*unconst*/ char *) p1; tv->in[0].v.text.sz = sz1;
3203 return (tvec_claimeq(tv, &tvty_text, 0, file, lno, expr));
b64eb60f
MW
3204}
3205
c81c35df 3206/* --- @tvec_claimeq_textz@ --- *
67b5031e
MW
3207 *
3208 * Arguments: @struct tvec_state *tv@ = test-vector state
3209 * @const char *p0, *p1@ = two strings to compare
3210 * @const char *file@, @unsigned @lno@ = calling file and line
3211 * @const char *expr@ = the expression to quote on failure
3212 *
3213 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3214 * zero.
3215 *
3216 * Use: Check that strings at @p0@ and @p1@ are equal, as for
3217 * @tvec_claimeq_string@, except that the strings are assumed
3218 * null-terminated, so their lengths don't need to be supplied
3219 * explicitly.
3220 */
3221
c81c35df
MW
3222int tvec_claimeq_textz(struct tvec_state *tv,
3223 const char *p0, const char *p1,
3224 const char *file, unsigned lno, const char *expr)
b64eb60f 3225{
c81c35df
MW
3226 tv->out[0].v.text.p = (/*unconst*/ char *)p0;
3227 tv->out[0].v.text.sz = strlen(p0);
3228 tv->in[0].v.text.p = (/*unconst*/ char *)p1;
3229 tv->in[0].v.text.sz = strlen(p1);
3230 return (tvec_claimeq(tv, &tvty_text, 0, file, lno, expr));
b64eb60f
MW
3231}
3232
67b5031e
MW
3233/* --- @tvec_claimeq_bytes@ --- *
3234 *
3235 * Arguments: @struct tvec_state *tv@ = test-vector state
3236 * @const void *p0@, @size_t sz0@ = first string with length
3237 * @const void *p1@, @size_t sz1@ = second string with length
3238 * @const char *file@, @unsigned @lno@ = calling file and line
3239 * @const char *expr@ = the expression to quote on failure
3240 *
3241 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3242 * zero.
3243 *
3244 * Use: Check that binary strings at @p0@ and @p1@ are equal. As for
3245 * @tvec_claim@ above, a test case is automatically begun and
3246 * ended if none is already underway. If the values are
3247 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3248 * mismatched values are dumped: @p0@ is printed as the output
3249 * value and @p1@ is printed as the input reference.
3250 */
3251
b64eb60f
MW
3252int tvec_claimeq_bytes(struct tvec_state *tv,
3253 const void *p0, size_t sz0,
3254 const void *p1, size_t sz1,
3255 const char *file, unsigned lno, const char *expr)
3256{
3efcfd2d
MW
3257 tv->out[0].v.bytes.p = (/*unconst*/ void *)p0;
3258 tv->out[0].v.bytes.sz = sz0;
3259 tv->in[0].v.bytes.p = (/*unconst*/ void *)p1;
3260 tv->in[0].v.bytes.sz = sz1;
b64eb60f
MW
3261 return (tvec_claimeq(tv, &tvty_bytes, 0, file, lno, expr));
3262}
3263
c81c35df 3264/* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
67b5031e
MW
3265 *
3266 * Arguments: @union tvec_regval *rv@ = register value
3267 * @size_t sz@ = required size
3268 *
3269 * Returns: ---
3270 *
3271 * Use: Allocated space in a text or binary string register. If the
3272 * current register size is sufficient, its buffer is left
3273 * alone; otherwise, the old buffer, if any, is freed and a
3274 * fresh buffer allocated. These functions are not intended to
3275 * be used to adjust a buffer repeatedly, e.g., while building
3276 * output incrementally: (a) they will perform badly, and (b)
3277 * the old buffer contents are simply discarded if reallocation
3278 * is necessary. Instead, use a @dbuf@ or @dstr@.
3279 *
c81c35df 3280 * The @tvec_alloctext@ function sneakily allocates an extra
67b5031e
MW
3281 * byte for a terminating zero. The @tvec_allocbytes@ function
3282 * doesn't do this.
3283 */
3284
c81c35df 3285void tvec_alloctext(union tvec_regval *rv, size_t sz)
67b5031e 3286{
c81c35df
MW
3287 if (rv->text.sz <= sz) { xfree(rv->text.p); rv->text.p = xmalloc(sz + 1); }
3288 rv->text.sz = sz;
67b5031e
MW
3289}
3290
3291void tvec_allocbytes(union tvec_regval *rv, size_t sz)
3292{
3293 if (rv->bytes.sz < sz) { xfree(rv->bytes.p); rv->bytes.p = xmalloc(sz); }
3294 rv->bytes.sz = sz;
3295}
3296
b64eb60f
MW
3297/*----- Buffer type -------------------------------------------------------*/
3298
adec5584
MW
3299/* --- @init_buffer@ --- *
3300 *
3301 * Arguments: @union tvec_regval *rv@ = register value
3302 * @const struct tvec_regdef *rd@ = register definition
3303 *
3304 * Returns: ---
3305 *
3306 * Use: Initialize a register value.
3307 *
3308 * Buffer values values are initialized with a null pointer,
3309 * zero length, and zero residue, modulus, and offset.
3310 */
3311
3312static void init_buffer(union tvec_regval *rv, const struct tvec_regdef *rd)
3313 { rv->buf.p = 0; rv->buf.sz = rv->buf.a = rv->buf.m = rv->buf.off = 0; }
3314
3315/* --- @release_buffer@, @release_bytes@ --- *
3316 *
3317 * Arguments: @const union tvec_regval *rv@ = register value
3318 * @const struct tvec_regdef *rd@ = register definition
3319 *
3320 * Returns: ---
3321 *
3322 * Use: Release resources held by a register value.
3323 *
3324 * Buffers are freed.
3325 */
3326
3327static void release_buffer(union tvec_regval *rv,
3328 const struct tvec_regdef *rd)
3329 { if (rv->buf.p) xfree(rv->buf.p - rv->buf.off); }
c81c35df
MW
3330
3331/* --- @eq_buffer@ --- *
3332 *
3333 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
3334 * @const struct tvec_regdef *rd@ = register definition
3335 *
3336 * Returns: Nonzero if the values are equal, zero if unequal
3337 *
3338 * Use: Compare register values for equality.
3339 *
adec5584
MW
3340 * Buffer values are equal if and only if their sizes and
3341 * alignment parameters are equal; their contents are
3342 * %%\emph{not}%% compared.
c81c35df
MW
3343 */
3344
b64eb60f
MW
3345static int eq_buffer(const union tvec_regval *rv0,
3346 const union tvec_regval *rv1,
3347 const struct tvec_regdef *rd)
adec5584
MW
3348{
3349 return (rv0->buf.sz == rv1->buf.sz &&
3350 rv0->buf.a == rv1->buf.a &&
3351 rv0->buf.m == rv1->buf.m);
3352}
b64eb60f 3353
c81c35df
MW
3354/* --- @tobuf_buffer@ --- *
3355 *
3356 * Arguments: @buf *b@ = buffer
3357 * @const union tvec_regval *rv@ = register value
3358 * @const struct tvec_regdef *rd@ = register definition
3359 *
3360 * Returns: Zero on success, %$-1$% on failure.
3361 *
3362 * Use: Serialize a register value to a buffer.
3363 *
adec5584
MW
3364 * Buffer values are serialized as their lengths, residues, and
3365 * moduli, as unsigned integers.
c81c35df
MW
3366 */
3367
b64eb60f
MW
3368static int tobuf_buffer(buf *b, const union tvec_regval *rv,
3369 const struct tvec_regdef *rd)
adec5584
MW
3370{
3371 return (unsigned_to_buf(b, rv->buf.sz) ||
3372 unsigned_to_buf(b, rv->buf.a) ||
3373 unsigned_to_buf(b, rv->buf.m));
3374}
c81c35df
MW
3375
3376/* --- @frombuf_buffer@ --- *
3377 *
3378 * Arguments: @buf *b@ = buffer
3379 * @union tvec_regval *rv@ = register value
3380 * @const struct tvec_regdef *rd@ = register definition
3381 *
3382 * Returns: Zero on success, %$-1$% on failure.
3383 *
3384 * Use: Deserialize a register value from a buffer.
3385 *
3386 * Buffer values are serialized as just their lengths, as
3387 * unsigned integers. The buffer is allocated on
3388 * deserialization and filled with a distinctive pattern.
3389 */
3390
b64eb60f
MW
3391static int frombuf_buffer(buf *b, union tvec_regval *rv,
3392 const struct tvec_regdef *rd)
3393{
adec5584 3394 unsigned long sz, a, m;
b64eb60f 3395
adec5584
MW
3396 if (unsigned_from_buf(b, &sz)) return (-1);
3397 if (unsigned_from_buf(b, &a)) return (-1);
3398 if (unsigned_from_buf(b, &m)) return (-1);
3399 if (sz > (size_t)-1 || a > (size_t)-1 || m > (size_t)-1)
3400 { buf_break(b); return (-1); }
3401 rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m;
b64eb60f
MW
3402 return (0);
3403}
3404
c81c35df
MW
3405/* --- @parse_buffer@ --- *
3406 *
3407 * Arguments: @union tvec_regval *rv@ = register value
3408 * @const struct tvec_regdef *rd@ = register definition
3409 * @struct tvec_state *tv@ = test-vector state
3410 *
3411 * Returns: Zero on success, %$-1$% on error.
3412 *
3413 * Use: Parse a register value from an input file.
3414 *
3415 * The input format for a buffer value consists of an unsigned
3416 * integer followed by an optional unit specifier consisting of
3417 * an SI unit prefix and (optionally) the letter `B'. Unit
3418 * prefixes denote %%\emph{binary}%% multipliers, not decimal.
3419 *
3420 * The buffer is allocated and filled with a distinctive
3421 * pattern.
3422 */
3423
882a39c1
MW
3424static int parse_buffer(union tvec_regval *rv,
3425 const struct tvec_regdef *rd,
3426 struct tvec_state *tv)
b64eb60f 3427{
13ee7406 3428 size_t sz, a = 0, m = 0;
adec5584 3429 int ch, rc;
b64eb60f 3430
adec5584
MW
3431 if (parse_size(tv, &sz, "@;", "buffer length")) { rc = -1; goto end; }
3432 if (check_string_length(sz, rd->arg.p, tv)) { rc = -1; goto end; }
b64eb60f 3433
adec5584
MW
3434 tvec_skipspc(tv);
3435 ch = getc(tv->fp);
3436 if (ch == ';' || ch == '\n') { ungetc(ch, tv->fp); goto done; }
3437 else if (ch != '@') { rc = tvec_syntax(tv, ch, "`@'"); goto end; }
3438
3439 if (parse_size(tv, &m, "+;", "alignment quantum")) { rc = -1; goto end; }
3440 if (m == 1) m = 0;
3441
3442 tvec_skipspc(tv);
3443 ch = getc(tv->fp);
3444 if (ch == ';' || ch == '\n') { ungetc(ch, tv->fp); goto done; }
3445 else if (ch != '+') { rc = tvec_syntax(tv, ch, "`+'"); goto end; }
3446
3447 if (parse_size(tv, &a, ";", "alignment offset")) { rc = -1; goto end; }
3448 if (a >= m) {
3449 rc = tvec_error(tv, "alignment offset %lu >= quantum %lu",
3450 (unsigned long)a, (unsigned long)m);
3451 goto end;
b64eb60f 3452 }
b64eb60f 3453
adec5584 3454done:
882a39c1 3455 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
adec5584 3456 rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m;
882a39c1
MW
3457 rc = 0;
3458end:
adec5584 3459 return (rc);
b64eb60f
MW
3460}
3461
c81c35df
MW
3462/* --- @dump_buffer@ --- *
3463 *
3464 * Arguments: @const union tvec_regval *rv@ = register value
3465 * @const struct tvec_regdef *rd@ = register definition
3466 * @unsigned style@ = output style (@TVSF_...@)
3467 * @const struct gprintf_ops *gops@, @void *gp@ = format output
3468 *
3469 * Returns: ---
3470 *
3471 * Use: Dump a register value to the format output.
3472 *
3473 * Buffer values are dumped as their size with an appropriate
3474 * unit specifier. A unit prefix is only used if the size is an
3475 * exact multiple of the relevant power of two.
3476 */
3477
b64eb60f
MW
3478static void dump_buffer(const union tvec_regval *rv,
3479 const struct tvec_regdef *rd,
e63124bc
MW
3480 unsigned style,
3481 const struct gprintf_ops *gops, void *go)
b64eb60f 3482{
adec5584
MW
3483 format_size(gops, go, rv->buf.sz, style);
3484 if (rv->buf.m) {
3485 gprintf(gops, go, style&TVSF_COMPACT ? "@" : " @ ");
3486 format_size(gops, go, rv->buf.m, style);
3487 if (rv->buf.a) {
3488 gprintf(gops, go, style&TVSF_COMPACT ? "+" : " + ");
3489 format_size(gops, go, rv->buf.a, style);
3490 }
3491 }
3492 if (!(style&TVSF_COMPACT)) {
13ee7406 3493 gprintf(gops, go, " ; = %lu", (unsigned long)rv->buf.sz);
adec5584 3494 if (rv->buf.m) {
13ee7406
MW
3495 gprintf(gops, go, " @ %lu", (unsigned long)rv->buf.m);
3496 if (rv->buf.a) gprintf(gops, go, " + %lu", (unsigned long)rv->buf.a);
adec5584
MW
3497 }
3498 gprintf(gops, go, " = "); format_unsigned_hex(gops, go, rv->buf.sz);
3499 if (rv->buf.m) {
3500 gprintf(gops, go, " @ "); format_unsigned_hex(gops, go, rv->buf.m);
3501 if (rv->buf.a) {
3502 gprintf(gops, go, " + ");
3503 format_unsigned_hex(gops, go, rv->buf.a);
3504 }
3505 }
b64eb60f
MW
3506 }
3507}
3508
c81c35df 3509/* Buffer type definition. */
b64eb60f 3510const struct tvec_regty tvty_buffer = {
adec5584 3511 init_buffer, release_buffer, eq_buffer,
b64eb60f
MW
3512 tobuf_buffer, frombuf_buffer,
3513 parse_buffer, dump_buffer
3514};
3515
adec5584
MW
3516/* --- @tvec_initbuffer@ --- *
3517 *
3518 * Arguments: @union tvec_regval *rv@ = register value
d056fbdf 3519 * @const union tvec_regval *ref@ = source buffer
adec5584
MW
3520 * @size_t sz@ = size to allocate
3521 *
3522 * Returns: ---
3523 *
d056fbdf 3524 * Use: Initialize the alignment parameters in @rv@ to match @ref@,
adec5584
MW
3525 * and the size to @sz@.
3526 */
3527
3528void tvec_initbuffer(union tvec_regval *rv,
d056fbdf
MW
3529 const union tvec_regval *ref, size_t sz)
3530 { rv->buf.sz = sz; rv->buf.a = ref->buf.a; rv->buf.m = ref->buf.m; }
adec5584
MW
3531
3532/* --- @tvec_allocbuffer@ --- *
3533 *
3534 * Arguments: @union tvec_regval *rv@ = register value
3535 *
3536 * Returns: ---
3537 *
3538 * Use: Allocate @sz@ bytes to the buffer and fill the space with a
3539 * distinctive pattern.
3540 */
3541
3542void tvec_allocbuffer(union tvec_regval *rv)
3543{
3544 unsigned char *p; size_t n;
3545
3546 if (rv->buf.p) xfree(rv->buf.p - rv->buf.off);
3547
3548 if (rv->buf.m < 2) {
3549 rv->buf.p = xmalloc(rv->buf.sz); rv->buf.off = 0;
3550 } else {
3551 p = xmalloc(rv->buf.sz + rv->buf.m - 1);
3552 n = (size_t)p%rv->buf.m;
3553 rv->buf.off = (rv->buf.a - n + rv->buf.m)%rv->buf.m;
3554 rv->buf.p = p + rv->buf.off;
3555 }
3556 memset(rv->buf.p, '?', rv->buf.sz);
3557}
3558
b64eb60f 3559/*----- That's all, folks -------------------------------------------------*/