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