3 * Types for the test-vector framework
5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
48 /*----- Preliminary utilities ---------------------------------------------*/
50 /* --- @trivial_release@ --- *
52 * Arguments: @union tvec_regval *rv@ = a register value
53 * @const struct tvec_regdef@ = the register definition
57 * Use: Does nothing. Used for register values which don't retain
61 static void trivial_release(union tvec_regval
*rv
,
62 const struct tvec_regdef
*rd
)
65 /*----- Integer utilities -------------------------------------------------*/
67 /* --- @unsigned_to_buf@, @signed_to_buf@ --- *
69 * Arguments: @buf *b@ = buffer to write on
70 * @unsigned long u@ or @long i@ = integer to write
72 * Returns: Zero on success, @-1@ on failure.
74 * Use: Write @i@ to the buffer, in big-endian (two's-complement, it
78 static int unsigned_to_buf(buf
*b
, unsigned long u
)
79 { kludge64 k
; ASSIGN64(k
, u
); return (buf_putk64l(b
, k
)); }
81 static int signed_to_buf(buf
*b
, long i
)
87 if (i
>= 0) ASSIGN64(k
, u
);
88 else { ASSIGN64(k
, ~u
); CPL64(k
, k
); }
89 return (buf_putk64l(b
, k
));
92 /* --- @unsigned_from_buf@, @signed_from_buf@ --- *
94 * Arguments: @buf *b@ = buffer to write on
95 * @unsigned long *u_out@ or @long *i_out@ = where to put the
98 * Returns: Zero on success, @-1@ on failure.
100 * Use: Read an integer, in big-endian (two's-complement, if signed)
101 * format, from the buffer.
104 static int unsigned_from_buf(buf
*b
, unsigned long *u_out
)
108 ASSIGN64(ulmax
, ULONG_MAX
);
109 if (buf_getk64l(b
, &k
)) return (-1);
110 if (CMP64(k
, >, ulmax
)) return (-1);
111 *u_out
= GET64(unsigned long, k
); return (0);
114 /* --- @hex_width@ --- *
116 * Arguments: @unsigned long u@ = an integer
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.
123 static int hex_width(unsigned long u
)
128 for (t
= u
>> 4, wd
= 4; t
>>= wd
, wd
*= 2, t
; );
132 /* --- @format_unsigned_hex@, @format_signed_hex@ --- *
134 * Arguments: @const struct gprintf_ops *gops@ = print operations
135 * @void *go@ = print destination
136 * @unsigned long u@ or @long i@ = integer to print
140 * Use: Print an unsigned or signed integer in hexadecimal.
143 static void format_unsigned_hex(const struct gprintf_ops
*gops
, void *go
,
145 { gprintf(gops
, go
, "0x%0*lx", hex_width(u
), u
); }
147 static void format_signed_hex(const struct gprintf_ops
*gops
, void *go
,
150 unsigned long u
= i
>= 0 ? i
: -(unsigned long)i
;
151 gprintf(gops
, go
, "%s0x%0*lx", i
< 0 ?
"-" : "", hex_width(u
), u
);
154 static int signed_from_buf(buf
*b
, long *i_out
)
156 kludge64 k
, lmax
, not_lmin
;
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
);
163 if (CMP64(k
, <=, not_lmin
)) *i_out
= -(long)GET64(unsigned long, k
) - 1;
169 /* --- @check_unsigned_range@, @check_signed_range@ --- *
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,
175 * @struct tvec_state *tv@ = test vector state
177 * Returns: Zero on success, or @-1@ on error.
179 * Use: Check that the integer is within bounds. If not, report a
180 * suitable error and return a failure indication.
183 static int check_signed_range(long i
,
184 const struct tvec_irange
*ir
,
185 struct tvec_state
*tv
)
187 if (ir
&& (ir
->min
> i
|| i
> ir
->max
)) {
188 tvec_error(tv
, "integer %ld out of range (must be in [%ld .. %ld])",
189 i
, ir
->min
, ir
->max
);
195 static int check_unsigned_range(unsigned long u
,
196 const struct tvec_urange
*ur
,
197 struct tvec_state
*tv
)
199 if (ur
&& (ur
->min
> u
|| u
> ur
->max
)) {
200 tvec_error(tv
, "integer %lu out of range (must be in [%lu .. %lu])",
201 u
, ur
->min
, ur
->max
);
207 /* --- @chtodig@ --- *
209 * Arguments: @int ch@ = a character
211 * Returns: The numeric value of the character as a digit, or @-1@ if
212 * it's not a digit. Letters count as extended digits starting
213 * with value 10; case is not significant.
216 static int chtodig(int ch
)
218 if ('0' <= ch
&& ch
<= '9') return (ch
- '0');
219 else if ('a' <= ch
&& ch
<= 'z') return (ch
- 'a' + 10);
220 else if ('A' <= ch
&& ch
<= 'Z') return (ch
- 'A' + 10);
224 /* --- @parse_unsigned_integer@, @parse_signed_integer@ --- *
226 * Arguments: @unsigned long *u_out@, @long *i_out@ = where to put the
228 * @const char **q_out@ = where to put the end position
229 * @const char *p@ = pointer to the string to parse
231 * Returns: Zero on success, @-1@ on error.
233 * Use: Parse an integer from a string in the test-vector format.
234 * This is mostly extension of the traditional C @strtoul@
235 * format: supported inputs include:
237 * * NNN -- a decimal number (even if it starts with `0');
238 * * 0xNNN -- hexadecimal;
241 * * NNrNNN -- base NN.
243 * Furthermore, single underscores are permitted internally as
244 * an insignificant digit separator.
247 static int parse_unsigned_integer(unsigned long *u_out
, const char **q_out
,
254 #define f_implicit 1u /* implicitly reading base 10 */
255 #define f_digit 2u /* read a real digit */
256 #define f_uscore 4u /* found an underscore */
260 * This will deal with the traditional `0[box]...' prefixes. We'll leave
261 * our new `NNr...' syntax for later.
263 if (p
[0] != '0' || !p
[1]) {
264 d
= chtodig(*p
); if (0 > d
|| d
>= 10) return (-1);
265 r
= 10; u
= d
; p
++; f
|= f_implicit
| f_digit
;
267 u
= 0; d
= chtodig(p
[2]);
268 if (d
< 0) { r
= 10; f
|= f_implicit
| f_digit
; p
++; }
269 else if ((p
[1] == 'x' || p
[1] == 'X') && d
< 16) { r
= 16; p
+= 2; }
270 else if ((p
[1] == 'o' || p
[1] == 'O') && d
< 8) { r
= 8; p
+= 2; }
271 else if ((p
[1] == 'b' || p
[1] == 'B') && d
< 2) { r
= 2; p
+= 2; }
272 else { r
= 10; f
|= f_digit
; p
++; }
277 /* Work through the string a character at a time. */
279 ch
= *p
; switch (ch
) {
282 /* An underscore is OK if we haven't just seen one. */
284 if (f
&f_uscore
) goto done
;
285 p
++; f
= (f
&~f_implicit
) | f_uscore
;
289 /* An `r' is OK if the number so far is small enough to be a sensible
290 * base, and we're scanning decimal implicitly.
293 if (!(f
&f_implicit
) || !u
|| u
>= 36) goto done
;
294 d
= chtodig(p
[1]); if (0 > d
|| d
>= u
) goto done
;
295 r
= u
; u
= d
; f
= (f
&~f_implicit
) | f_digit
; p
+= 2; q
= p
;
299 /* Otherwise we expect a valid digit and accumulate it. */
300 d
= chtodig(ch
); if (d
< 0 || d
>= r
) goto done
;
301 if (u
> ULONG_MAX
/r
) return (-1);
302 u
*= r
; if (u
> ULONG_MAX
- d
) return (-1);
303 u
+= d
; f
= (f
&~f_uscore
) | f_digit
; p
++; q
= p
;
309 if (!(f
&f_digit
)) return (-1);
310 *u_out
= u
; *q_out
= q
; return (0);
317 static int parse_signed_integer(long *i_out
, const char **q_out
,
324 /* Read an initial sign. */
326 else if (*p
== '-') { f
|= f_neg
; p
++; }
328 /* Scan an unsigned number. */
329 if (parse_unsigned_integer(&u
, q_out
, p
)) return (-1);
331 /* Check for signed overflow and apply the sign. */
333 if (u
> LONG_MAX
) return (-1);
336 if (u
&& u
- 1 > -(LONG_MIN
+ 1)) return (-1);
337 *i_out
= u ?
-(long)(u
- 1) - 1 : 0;
345 /* --- @parse_unsigned@, @parse_signed@ --- *
347 * Arguments: @unsigned long *u_out@ or @long *i_out@ = where to put the
349 * @const char *p@ = string to parse
350 * @const struct tvec_urange *ur@ or
351 * @const struct tvec_irange *ir@ = range specification,
353 * @struct tvec_state *tv@ = test vector state
355 * Returns: Zero on success, @-1@ on error.
357 * Use: Parse and range-check an integer. Unlike @parse_(un)signed_
358 * integer@, these functions check that there's no cruft
359 * following the final digit, and report errors as they find
360 * them rather than leaving that to the caller.
363 static int parse_unsigned(unsigned long *u_out
, const char *p
,
364 const struct tvec_urange
*ur
,
365 struct tvec_state
*tv
)
370 if (parse_unsigned_integer(&u
, &q
, p
))
371 return (tvec_error(tv
, "invalid unsigned integer `%s'", p
));
372 if (*q
) return (tvec_syntax(tv
, *q
, "end-of-line"));
373 if (check_unsigned_range(u
, ur
, tv
)) return (-1);
374 *u_out
= u
; return (0);
377 static int parse_signed(long *i_out
, const char *p
,
378 const struct tvec_irange
*ir
,
379 struct tvec_state
*tv
)
384 if (parse_signed_integer(&i
, &q
, p
))
385 return (tvec_error(tv
, "invalid signed integer `%s'", p
));
386 if (*q
) return (tvec_syntax(tv
, *q
, "end-of-line"));
387 if (check_signed_range(i
, ir
, tv
)) return (-1);
388 *i_out
= i
; return (0);
391 /*----- Floating-point utilities ------------------------------------------*/
393 /* --- @eqish_floating_p@ --- *
395 * Arguments: @double x, y@ = two numbers to compare
396 * @const struct tvec_floatinfo *fi@ = floating-point info
398 * Returns: Nonzero if the comparand @y@ is sufficiently close to the
399 * reference @x@, or zero if it's definitely different.
402 static int eqish_floating_p(double x
, double y
,
403 const struct tvec_floatinfo
*fi
)
407 if (NANP(x
)) return (NANP(y
)); else if (NANP(y
)) return (0);
408 if (INFP(x
)) return (x
== y
); else if (INFP(y
)) return (0);
410 switch (fi ? fi
->f
&TVFF_EQMASK
: TVFF_EXACT
) {
412 return (x
== y
&& NEGP(x
) == NEGP(y
));
414 t
= x
- y
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
416 t
= 1.0 - y
/x
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
422 /* --- @format_floating@ --- *
424 * Arguments: @const struct gprintf_ops *gops@ = print operations
425 * @void *go@ = print destination
426 * @double x@ = number to print
430 * Use: Print a floating-point number, accurately.
433 static void format_floating(const struct gprintf_ops
*gops
, void *go
,
439 gprintf(gops
, go
, "#nan");
441 gprintf(gops
, go
, x
> 0 ?
"#+inf" : "#-inf");
443 /* Ugh. C doesn't provide any function for just printing a
444 * floating-point number /correctly/, i.e., so that you can read the
445 * result back and recover the number you first thought of. There are
446 * complicated algorithms published for doing this, but I really don't
447 * want to get into that here. So we have this.
449 * The sign doesn't cause significant difficulty so we're going to ignore
450 * it for now. So suppose we're given a number %$x = f b^e$%, in
451 * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with
452 * %$0 \le f < 1$%. We're going to convert it into the nearest integer
453 * of the form %$X = F B^E$%, with similar conditions, only with the
454 * additional requirement that %$X$% is normalized, i.e., that %$X = 0$%
455 * or %$F \ge B^{-N}$%.
457 * We're rounding to the nearest such %$X$%. If there is to be ambiguity
458 * in the conversion, then some %$x = f b^e$% and the next smallest
459 * representable number %$x' = x + b^{e-n}$% must both map to the same
460 * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than
461 * any other number representable in the target system. The nest larger
462 * number is %$X' = X + B^{E-N}$%; the next smaller number will normally
463 * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number
464 * is actually %$X - B^{E-N-1}$%. We ignore this latter possibility in
465 * the pursuit of a conservative estimate (though actually it doesn't
468 * If both %$x$% and %$x'$% map to %$X$% then we must have
469 * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%;
470 * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%,
471 * and secondly %$b^{e-n} \le B^{E-N}$%. Since these inequalities are in
472 * opposite senses, we can divide, giving
474 * %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% ,
478 * %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% .
480 * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be
481 * possible, it must be the case that
483 * %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% .
485 * Then rearrange and take logarithms, obtaining
487 * %$(N - 1) \log B < n \log b$% ,
491 * %$N < n \log b/\log B + 1$% .
493 * Recall that this is a necessary condition for a collision to occur; we
494 * are therefore safe whenever
496 * %$N \ge n \log b/\log B + 1$% ;
498 * so, taking ceilings,
500 * %$N \ge \lceil n \log b/\log B \rceil + 1$% .
502 * So that's why we have this.
504 * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that
505 * we can calculate this without ending up on the wrong side of an
508 * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only
509 * as a constant. Except that modern compilers are more than clever
510 * enough to work out that this is a constant anyway.
512 * This is sometimes an overestimate: we'll print out meaningless digits
513 * that don't represent anything we actually know about the number in
514 * question. To fix that, we'd need a complicated algorithm like Steele
515 * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm
516 * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to
517 * something else in difficult situations).
520 prec
= ceil(DBL_MANT_DIG
*log(FLT_RADIX
)/log(10)) + 1;
521 gprintf(gops
, go
, "%.*g", prec
, x
);
525 /* --- @parse_floating@ --- *
527 * Arguments: @double *x_out@ = where to put the result
528 * @const char *p@ = string to parse
529 * @const struct tvec_floatinfo *fi@ = floating-point info
530 * @struct tvec_state *tv@ = test vector state
532 * Returns: Zero on success, @-1@ on error.
534 * Use: Parse a floating-point number from a string. Reports any
538 static int parse_floating(double *x_out
, const char *p
,
539 const struct tvec_floatinfo
*fi
,
540 struct tvec_state
*tv
)
542 const char *pp
; char *q
;
547 /* Check for special tokens. */
548 if (STRCMP(p
, ==, "#nan")) {
552 tvec_error(tv
, "NaN not supported on this system");
557 else if (STRCMP(p
, ==, "#inf") ||
558 STRCMP(p
, ==, "#+inf") || STRCMP(p
, ==, "+#inf")) {
560 x
= INFINITY
; rc
= 0;
562 tvec_error(tv
, "infinity not supported on this system");
567 else if (STRCMP(p
, ==, "#-inf") || STRCMP(p
, ==, "-#inf")) {
569 x
= -INFINITY
; rc
= 0;
571 tvec_error(tv
, "infinity not supported on this system");
576 /* Check that this looks like a number, so we can exclude `strtod'
577 * recognizing its own non-finite number tokens.
581 if (*pp
== '+' || *pp
== '-') pp
++;
582 if (*pp
== '.') pp
++;
584 tvec_syntax(tv
, *p ?
*p
: fgetc(tv
->fp
), "floating-point number");
588 /* Parse the number using the system parser. */
589 olderr
= errno
; errno
= 0;
592 tvec_syntax(tv
, *q
, "end-of-line");
595 if (errno
&& (errno
!= ERANGE
|| (x
> 0 ?
-x
: x
) == HUGE_VAL
)) {
596 tvec_error(tv
, "invalid floating-point number `%s': %s",
603 /* Check that the number is acceptable. */
604 if (NANP(x
) && fi
&& !(fi
->f
&TVFF_NANOK
)) {
605 tvec_error(tv
, "#nan not allowed here");
609 if (fi
&& ((!(fi
->f
&TVFF_NOMIN
) && x
< fi
->min
) ||
610 (!(fi
->f
&TVFF_NOMAX
) && x
> fi
->max
))) {
611 dstr_puts(&d
, "floating-point number ");
612 format_floating(&dstr_printops
, &d
, x
);
613 dstr_puts(&d
, " out of range (must be in ");
614 if (fi
->f
&TVFF_NOMIN
)
615 dstr_puts(&d
, "(#-inf");
617 { dstr_putc(&d
, '['); format_floating(&dstr_printops
, &d
, fi
->min
); }
618 dstr_puts(&d
, " .. ");
619 if (fi
->f
&TVFF_NOMAX
)
620 dstr_puts(&d
, "#+inf)");
622 { format_floating(&dstr_printops
, &d
, fi
->max
); dstr_putc(&d
, ']'); }
623 dstr_putc(&d
, ')'); dstr_putz(&d
);
624 tvec_error(tv
, "%s", d
.buf
); rc
= -1; goto end
;
634 /*----- String utilities --------------------------------------------------*/
636 /* Special character name table. */
637 static const struct chartab
{
638 const char *name
; /* character name */
639 int ch
; /* character value */
640 unsigned f
; /* flags: */
641 #define CTF_PREFER 1u /* preferred name */
642 #define CTF_SHORT 2u /* short name (compact style) */
644 { "#eof", EOF
, CTF_PREFER
| CTF_SHORT
},
645 { "#nul", '\0', CTF_PREFER
},
646 { "#bell", '\a', CTF_PREFER
},
647 { "#ding", '\a', 0 },
648 { "#bel", '\a', CTF_SHORT
},
649 { "#backspace", '\b', CTF_PREFER
},
650 { "#bs", '\b', CTF_SHORT
},
651 { "#escape", '\x1b', CTF_PREFER
},
652 { "#esc", '\x1b', CTF_SHORT
},
653 { "#formfeed", '\f', CTF_PREFER
},
654 { "#ff", '\f', CTF_SHORT
},
655 { "#newline", '\n', CTF_PREFER
},
656 { "#linefeed", '\n', 0 },
657 { "#lf", '\n', CTF_SHORT
},
659 { "#return", '\r', CTF_PREFER
},
660 { "#carriage-return", '\r', 0 },
661 { "#cr", '\r', CTF_SHORT
},
662 { "#tab", '\t', CTF_PREFER
| CTF_SHORT
},
663 { "#horizontal-tab", '\t', 0 },
665 { "#vertical-tab", '\v', CTF_PREFER
},
666 { "#vt", '\v', CTF_SHORT
},
667 { "#space", ' ', 0 },
668 { "#spc", ' ', CTF_SHORT
},
669 { "#delete", '\x7f', CTF_PREFER
},
670 { "#del", '\x7f', CTF_SHORT
},
674 /* --- @find_charname@ --- *
676 * Arguments: @int ch@ = character to match
677 * @unsigned f@ = flags (@CTF_...@) to match
679 * Returns: The name of the character, or null if no match is found.
681 * Use: Looks up a name for a character. Specifically, it returns
682 * the first entry in the @chartab@ table which matches @ch@ and
683 * which has one of the flags @f@ set.
686 static const char *find_charname(int ch
, unsigned f
)
688 const struct chartab
*ct
;
690 for (ct
= chartab
; ct
->name
; ct
++)
691 if (ct
->ch
== ch
&& (ct
->f
&f
)) return (ct
->name
);
695 /* --- @read_charname@ --- *
697 * Arguments: @int *ch_out@ = where to put the character
698 * @const char *p@ = character name
699 * @unsigned f@ = flags (@TCF_...@)
701 * Returns: Zero if a match was found, @-1@ if not.
703 * Use: Looks up a character by name. If @RCF_EOFOK@ is set in @f@,
704 * then the @EOF@ marker can be matched; otherwise it can't.
708 static int read_charname(int *ch_out
, const char *p
, unsigned f
)
710 const struct chartab
*ct
;
712 for (ct
= chartab
; ct
->name
; ct
++)
713 if (STRCMP(p
, ==, ct
->name
) && ((f
&RCF_EOFOK
) || ct
->ch
>= 0))
714 { *ch_out
= ct
->ch
; return (0); }
718 /* --- @format_charesc@ --- *
720 * Arguments: @const struct gprintf_ops *gops@ = print operations
721 * @void *go@ = print destination
722 * @int ch@ = character to format
723 * @unsigned f@ = flags (@FCF_...@)
727 * Use: Format a character as an escape sequence, possibly as part of
728 * a larger string. If @FCF_BRACE@ is set in @f@, then put
729 * braces around a `\x...' code, so that it's suitable for use
730 * in a longer string.
734 static void format_charesc(const struct gprintf_ops
*gops
, void *go
,
738 case '\a': gprintf(gops
, go
, "\\a"); break;
739 case '\b': gprintf(gops
, go
, "\\b"); break;
740 case '\x1b': gprintf(gops
, go
, "\\e"); break;
741 case '\f': gprintf(gops
, go
, "\\f"); break;
742 case '\r': gprintf(gops
, go
, "\\r"); break;
743 case '\n': gprintf(gops
, go
, "\\n"); break;
744 case '\t': gprintf(gops
, go
, "\\t"); break;
745 case '\v': gprintf(gops
, go
, "\\v"); break;
746 case '\\': gprintf(gops
, go
, "\\\\"); break;
747 case '\'': gprintf(gops
, go
, "\\'"); break;
749 if (f
&FCF_BRACE
) gprintf(gops
, go
, "\\{0}");
750 else gprintf(gops
, go
, "\\0");
754 gprintf(gops
, go
, "\\x{%0*x}", hex_width(UCHAR_MAX
), ch
);
756 gprintf(gops
, go
, "\\x%0*x", hex_width(UCHAR_MAX
), ch
);
761 /* --- @format_char@ --- *
763 * Arguments: @const struct gprintf_ops *gops@ = print operations
764 * @void *go@ = print destination
765 * @int ch@ = character to format
769 * Use: Format a single character.
772 static void format_char(const struct gprintf_ops
*gops
, void *go
, int ch
)
775 case '\\': case '\'': escape
:
776 gprintf(gops
, go
, "'");
777 format_charesc(gops
, go
, ch
, 0);
778 gprintf(gops
, go
, "'");
781 if (!isprint(ch
)) goto escape
;
782 gprintf(gops
, go
, "'%c'", ch
);
787 /* --- @maybe_format_unsigned_char@, @maybe_format_signed_char@ --- *
789 * Arguments: @const struct gprintf_ops *gops@ = print operations
790 * @void *go@ = print destination
791 * @unsigned long u@ or @long i@ = an integer
795 * Use: Format a (signed or unsigned) integer as a character, if it's
796 * in range, printing something like `= 'q''. It's assumed that
797 * a comment marker has already been output.
800 static void maybe_format_unsigned_char
801 (const struct gprintf_ops
*gops
, void *go
, unsigned long u
)
805 p
= find_charname(u
, CTF_PREFER
);
806 if (p
) gprintf(gops
, go
, " = %s", p
);
808 { gprintf(gops
, go
, " = "); format_char(gops
, go
, u
); }
811 static void maybe_format_signed_char
812 (const struct gprintf_ops
*gops
, void *go
, long i
)
816 p
= find_charname(i
, CTF_PREFER
);
817 if (p
) gprintf(gops
, go
, " = %s", p
);
818 if (0 <= i
&& i
< UCHAR_MAX
)
819 { gprintf(gops
, go
, " = "); format_char(gops
, go
, i
); }
822 /* --- @read_charesc@ --- *
824 * Arguments: @int *ch_out@ = where to put the result
825 * @struct tvec_state *tv@ = test vector state
827 * Returns: Zero on success, @-1@ on error.
829 * Use: Parse and convert an escape sequence from @tv@'s input
830 * stream, assuming that the initial `\' has already been read.
831 * Reports errors as appropriate.
834 static int read_charesc(int *ch_out
, struct tvec_state
*tv
)
843 /* Things we shouldn't find. */
844 case EOF
: case '\n': return (tvec_syntax(tv
, ch
, "string escape"));
846 /* Single-character escapes. */
847 case '\'': *ch_out
= '\''; break;
848 case '\\': *ch_out
= '\\'; break;
849 case '"': *ch_out
= '"'; break;
850 case 'a': *ch_out
= '\a'; break;
851 case 'b': *ch_out
= '\b'; break;
852 case 'e': *ch_out
= '\x1b'; break;
853 case 'f': *ch_out
= '\f'; break;
854 case 'n': *ch_out
= '\n'; break;
855 case 'r': *ch_out
= '\r'; break;
856 case 't': *ch_out
= '\t'; break;
857 case 'v': *ch_out
= '\v'; break;
859 /* Hex escapes, with and without braces. */
862 if (ch
== '{') { f
|= f_brace
; ch
= getc(tv
->fp
); }
865 if (esc
< 0 || esc
>= 16) return (tvec_syntax(tv
, ch
, "hex digit"));
867 ch
= getc(tv
->fp
); i
= chtodig(ch
); if (i
< 0 || i
>= 16) break;
870 return (tvec_error(tv
,
871 "character code %d out of range", esc
));
873 if (!(f
&f_brace
)) ungetc(ch
, tv
->fp
);
874 else if (ch
!= '}') return (tvec_syntax(tv
, ch
, "`}'"));
878 /* Other things, primarily octal escapes. */
880 f
|= f_brace
; ch
= getc(tv
->fp
);
883 if ('0' <= ch
&& ch
< '8') {
884 i
= 1; esc
= ch
- '0';
887 if ('0' > ch
|| ch
>= '8') { ungetc(ch
, tv
->fp
); break; }
888 esc
= 8*esc
+ ch
- '0';
889 i
++; if (i
>= 3) break;
893 if (ch
!= '}') return (tvec_syntax(tv
, ch
, "`}'"));
896 return (tvec_error(tv
,
897 "character code %d out of range", esc
));
898 *ch_out
= esc
; break;
900 return (tvec_syntax(tv
, ch
, "string escape"));
909 /* --- @read_quoted_string@ --- *
911 * Arguments: @dstr *d@ = string to write to
912 * @int quote@ = initial quote, `'' or `"'
913 * @struct tvec_state *tv@ = test vector state
915 * Returns: Zero on success, @-1@ on error.
917 * Use: Read the rest of a quoted string into @d@, reporting errors
920 * A single-quoted string is entirely literal. A double-quoted
921 * string may contain C-like escapes.
924 static int read_quoted_string(dstr
*d
, int quote
, struct tvec_state
*tv
)
932 return (tvec_syntax(tv
, ch
, "`%c'", quote
));
934 if (quote
== '\'') goto ordinary
;
935 ch
= getc(tv
->fp
); if (ch
== '\n') { tv
->lno
++; break; }
936 ungetc(ch
, tv
->fp
); if (read_charesc(&ch
, tv
)) return (-1);
939 if (ch
== quote
) goto end
;
951 /* --- @collect_bare@ --- *
953 * Arguments: @dstr *d@ = string to write to
954 * @struct tvec_state *tv@ = test vector state
956 * Returns: Zero on success, @-1@ on error.
958 * Use: Read barewords and the whitespace between them. Stop when we
959 * encounter something which can't start a bareword.
962 static int collect_bare(dstr
*d
, struct tvec_state
*tv
)
965 enum { WORD
, SPACE
, ESCAPE
}; unsigned s
= WORD
;
972 tvec_syntax(tv
, ch
, "bareword");
975 if (s
== ESCAPE
) { tv
->lno
++; goto addch
; }
976 if (s
== WORD
) pos
= d
->len
;
977 ungetc(ch
, tv
->fp
); if (tvec_nexttoken(tv
)) { rc
= -1; goto end
; }
978 DPUTC(d
, ' '); s
= SPACE
;
980 case '"': case '\'': case '!': case '#': case ')': case '}': case ']':
981 if (s
== SPACE
) { ungetc(ch
, tv
->fp
); goto done
; }
987 if (s
!= ESCAPE
&& isspace(ch
)) {
988 if (s
== WORD
) pos
= d
->len
;
989 DPUTC(d
, ch
); s
= SPACE
;
993 DPUTC(d
, ch
); s
= WORD
;
998 if (s
== SPACE
) d
->len
= pos
;
1004 /* --- @set_up_encoding@ --- *
1006 * Arguments: @const codec_class **ccl_out@ = where to put the class
1007 * @unsigned *f_out@ = where to put the flags
1008 * @unsigned code@ = the coding scheme to use (@TVEC_...@)
1012 * Use: Helper for @read_compound_string@ below.
1014 * Return the appropriate codec class and flags for @code@.
1015 * Leaves @*ccl_out@ null if the coding scheme doesn't have a
1016 * backing codec class (e.g., @TVCODE_BARE@).
1019 enum { TVCODE_BARE
, TVCODE_HEX
, TVCODE_BASE64
, TVCODE_BASE32
};
1020 static void set_up_encoding(const codec_class
**ccl_out
, unsigned *f_out
,
1025 *ccl_out
= 0; *f_out
= 0;
1028 *ccl_out
= &hex_class
; *f_out
= CDCF_IGNCASE
;
1031 *ccl_out
= &base32_class
; *f_out
= CDCF_IGNCASE
| CDCF_IGNEQPAD
;
1034 *ccl_out
= &base64_class
; *f_out
= CDCF_IGNEQPAD
;
1041 /* --- @flush_codec@ --- *
1043 * Arguments: @codec *cdc@ = a codec, or null
1044 * @dstr *d@ = output string
1045 * @struct tvec_state *tv@ = test vector state
1047 * Returns: Zero on success, @-1@ on error.
1049 * Use: Helper for @read_compound_string@ below.
1051 * Flush out any final buffered material from @cdc@, and check
1052 * that it's in a good state. Frees the codec on success. Does
1053 * nothing if @cdc@ is null.
1056 static int flush_codec(codec
*cdc
, dstr
*d
, struct tvec_state
*tv
)
1061 err
= cdc
->ops
->code(cdc
, 0, 0, d
);
1063 return (tvec_error(tv
, "invalid %s sequence end: %s",
1064 cdc
->ops
->c
->name
, codec_strerror(err
)));
1065 cdc
->ops
->destroy(cdc
);
1070 /* --- @read_compound_string@ --- *
1072 * Arguments: @void **p_inout@ = address of output buffer pointer
1073 * @size_t *sz_inout@ = address of buffer size
1074 * @unsigned code@ = initial interpretation of barewords
1075 * @unsigned f@ = other flags (@RCSF_...@)
1076 * @struct tvec_state *tv@ = test vector state
1078 * Returns: Zero on success, @-1@ on error.
1080 * Use: Parse a compound string, i.e., a sequence of stringish pieces
1081 * which might be quoted strings, character names, or barewords
1082 * to be decoded accoding to @code@, interspersed with
1083 * additional directives.
1085 * If the initial buffer pointer is non-null and sufficiently
1086 * large, then it will be reused; otherwise, it is freed and a
1087 * fresh, sufficiently large buffer is allocated and returned.
1090 #define RCSF_NESTED 1u
1091 static int read_compound_string(void **p_inout
, size_t *sz_inout
,
1092 unsigned code
, unsigned f
,
1093 struct tvec_state
*tv
)
1095 const codec_class
*ccl
; unsigned cdf
;
1097 dstr d
= DSTR_INIT
, w
= DSTR_INIT
;
1100 void *pp
= 0; size_t sz
;
1104 set_up_encoding(&ccl
, &cdf
, code
); cdc
= 0;
1106 if (tvec_nexttoken(tv
)) return (tvec_syntax(tv
, fgetc(tv
->fp
), "string"));
1111 case ')': case ']': case '}':
1112 /* Close brackets. Leave these for recursive caller if there is one,
1116 if (!(f
&RCSF_NESTED
))
1117 { rc
= tvec_syntax(tv
, ch
, "string"); goto end
; }
1118 ungetc(ch
, tv
->fp
); goto done
;
1120 case '"': case '\'':
1121 /* Quotes. Read a quoted string. */
1123 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1125 if (read_quoted_string(&d
, ch
, tv
)) { rc
= -1; goto end
; }
1129 /* A named character. */
1132 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1134 DRESET(&w
); tvec_readword(tv
, &w
, ";", "character name");
1135 if (read_charname(&ch
, w
.buf
, RCF_EOFOK
)) {
1136 rc
= tvec_error(tv
, "unknown character name `%s'", d
.buf
);
1139 DPUTC(&d
, ch
); break;
1142 /* A magic keyword. */
1144 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1147 DRESET(&w
); tvec_readword(tv
, &w
, ";", "`!'-keyword");
1149 /* Change bareword coding system. */
1150 if (STRCMP(w
.buf
, ==, "!bare"))
1151 { code
= TVCODE_BARE
; set_up_encoding(&ccl
, &cdf
, code
); }
1152 else if (STRCMP(w
.buf
, ==, "!hex"))
1153 { code
= TVCODE_HEX
; set_up_encoding(&ccl
, &cdf
, code
); }
1154 else if (STRCMP(w
.buf
, ==, "!base32"))
1155 { code
= TVCODE_BASE32
; set_up_encoding(&ccl
, &cdf
, code
); }
1156 else if (STRCMP(w
.buf
, ==, "!base64"))
1157 { code
= TVCODE_BASE64
; set_up_encoding(&ccl
, &cdf
, code
); }
1159 /* Repeated substrings. */
1160 else if (STRCMP(w
.buf
, ==, "!repeat")) {
1161 if (tvec_nexttoken(tv
)) {
1162 rc
= tvec_syntax(tv
, fgetc(tv
->fp
), "repeat count");
1166 if (tvec_readword(tv
, &w
, ";{", "repeat count"))
1167 { rc
= -1; goto end
; }
1168 if (parse_unsigned_integer(&n
, &q
, w
.buf
)) {
1169 rc
= tvec_error(tv
, "invalid repeat count `%s'", w
.buf
);
1172 if (*q
) { rc
= tvec_syntax(tv
, *q
, "`{'"); goto end
; }
1173 if (tvec_nexttoken(tv
))
1174 { rc
= tvec_syntax(tv
, fgetc(tv
->fp
), "`{'"); goto end
; }
1175 ch
= getc(tv
->fp
); if (ch
!= '{')
1176 { rc
= tvec_syntax(tv
, ch
, "`{'"); goto end
; }
1178 if (read_compound_string(&pp
, &sz
, code
, f
| RCSF_NESTED
, tv
))
1179 { rc
= -1; goto end
; }
1180 ch
= getc(tv
->fp
); if (ch
!= '}')
1181 { rc
= tvec_syntax(tv
, ch
, "`}'"); goto end
; }
1183 if (n
> (size_t)-1/sz
)
1184 { rc
= tvec_error(tv
, "repeat size out of range"); goto end
; }
1185 dstr_ensure(&d
, n
*sz
);
1187 { memset(d
.buf
+ d
.len
, *(unsigned char *)pp
, n
); d
.len
+= n
; }
1189 for (; n
--; d
.len
+= sz
) memcpy(d
.buf
+ d
.len
, pp
, sz
);
1194 /* Anything else is an error. */
1196 tvec_error(tv
, "unknown string keyword `%s'", w
.buf
);
1202 /* A bareword. Process it according to the current coding system. */
1207 if (collect_bare(&d
, tv
)) goto done
;
1211 ungetc(ch
, tv
->fp
); DRESET(&w
);
1212 if (tvec_readword(tv
, &w
, ";", "%s-encoded fragment", ccl
->name
))
1213 { rc
= -1; goto end
; }
1214 if (!cdc
) cdc
= ccl
->decoder(cdf
);
1215 err
= cdc
->ops
->code(cdc
, w
.buf
, w
.len
, &d
);
1217 tvec_error(tv
, "invalid %s fragment `%s': %s",
1218 ccl
->name
, w
.buf
, codec_strerror(err
));
1225 } while (!tvec_nexttoken(tv
));
1228 /* Wrap things up. */
1229 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1231 if (*sz_inout
<= d
.len
)
1232 { xfree(*p_inout
); *p_inout
= xmalloc(d
.len
+ 1); }
1233 p
= *p_inout
; memcpy(p
, d
.buf
, d
.len
); p
[d
.len
] = 0; *sz_inout
= d
.len
;
1237 /* Clean up any debris. */
1238 if (cdc
) cdc
->ops
->destroy(cdc
);
1240 dstr_destroy(&d
); dstr_destroy(&w
);
1244 /*----- Skeleton ----------------------------------------------------------*/
1246 static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd)
1247 static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd)
1248 static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1,
1249 const struct tvec_regdef *rd)
1250 static int tobuf_...(buf *b, const union tvec_regval *rv,
1251 const struct tvec_regdef *rd)
1252 static int frombuf_...(buf *b, union tvec_regval *rv,
1253 const struct tvec_regdef *rd)
1254 static int parse_...(union tvec_regval *rv, const struct tvec_regdef *rd,
1255 struct tvec_state *tv)
1256 static void dump_...(const union tvec_regval *rv,
1257 const struct tvec_regdef *rd,
1258 struct tvec_state *tv, unsigned style)
1260 const struct tvec_regty tvty_... = {
1261 init_..., release_..., eq_...,
1262 tobuf_..., frombuf_...,
1266 /*----- Signed and unsigned integer types ---------------------------------*/
1268 static void init_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1271 static void init_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1274 static int eq_int(const union tvec_regval
*rv0
, const union tvec_regval
*rv1
,
1275 const struct tvec_regdef
*rd
)
1276 { return (rv0
->i
== rv1
->i
); }
1278 static int eq_uint(const union tvec_regval
*rv0
,
1279 const union tvec_regval
*rv1
,
1280 const struct tvec_regdef
*rd
)
1281 { return (rv0
->u
== rv1
->u
); }
1283 static int tobuf_int(buf
*b
, const union tvec_regval
*rv
,
1284 const struct tvec_regdef
*rd
)
1285 { return (signed_to_buf(b
, rv
->i
)); }
1287 static int tobuf_uint(buf
*b
, const union tvec_regval
*rv
,
1288 const struct tvec_regdef
*rd
)
1289 { return (unsigned_to_buf(b
, rv
->u
)); }
1291 static int frombuf_int(buf
*b
, union tvec_regval
*rv
,
1292 const struct tvec_regdef
*rd
)
1293 { return (signed_from_buf(b
, &rv
->i
)); }
1295 static int frombuf_uint(buf
*b
, union tvec_regval
*rv
,
1296 const struct tvec_regdef
*rd
)
1297 { return (unsigned_from_buf(b
, &rv
->u
)); }
1299 static int parse_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1300 struct tvec_state
*tv
)
1305 if (tvec_readword(tv
, &d
, ";", "signed integer"))
1306 { rc
= -1; goto end
; }
1307 if (parse_signed(&rv
->i
, d
.buf
, rd
->arg
.p
, tv
))
1308 { rc
= -1; goto end
; }
1309 if (tvec_flushtoeol(tv
, 0))
1310 { rc
= -1; goto end
; }
1317 static int parse_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1318 struct tvec_state
*tv
)
1323 if (tvec_readword(tv
, &d
, ";", "unsigned integer"))
1324 { rc
= -1; goto end
; }
1325 if (parse_unsigned(&rv
->u
, d
.buf
, rd
->arg
.p
, tv
))
1326 { rc
= -1; goto end
; }
1327 if (tvec_flushtoeol(tv
, 0))
1328 { rc
= -1; goto end
; }
1335 static void dump_int(const union tvec_regval
*rv
,
1336 const struct tvec_regdef
*rd
,
1338 const struct gprintf_ops
*gops
, void *go
)
1341 gprintf(gops
, go
, "%ld", rv
->i
);
1342 if (!(style
&TVSF_COMPACT
)) {
1343 gprintf(gops
, go
, " ; = ");
1344 format_signed_hex(gops
, go
, rv
->i
);
1345 maybe_format_signed_char(gops
, go
, rv
->i
);
1349 static void dump_uint(const union tvec_regval
*rv
,
1350 const struct tvec_regdef
*rd
,
1352 const struct gprintf_ops
*gops
, void *go
)
1354 gprintf(gops
, go
, "%lu", rv
->u
);
1355 if (!(style
&TVSF_COMPACT
)) {
1356 gprintf(gops
, go
, " ; = ");
1357 format_unsigned_hex(gops
, go
, rv
->u
);
1358 maybe_format_unsigned_char(gops
, go
, rv
->u
);
1362 const struct tvec_regty tvty_int
= {
1363 init_int
, trivial_release
, eq_int
,
1364 tobuf_int
, frombuf_int
,
1368 const struct tvec_irange
1369 tvrange_schar
= { SCHAR_MIN
, SCHAR_MAX
},
1370 tvrange_short
= { SHRT_MIN
, SHRT_MAX
},
1371 tvrange_int
= { INT_MIN
, INT_MAX
},
1372 tvrange_long
= { LONG_MIN
, LONG_MAX
},
1373 tvrange_sbyte
= { -128, 127 },
1374 tvrange_i16
= { -32768, +32767 },
1375 tvrange_i32
= { -2147483648, 2147483647 };
1377 const struct tvec_regty tvty_uint
= {
1378 init_uint
, trivial_release
, eq_uint
,
1379 tobuf_uint
, frombuf_uint
,
1380 parse_uint
, dump_uint
1383 const struct tvec_urange
1384 tvrange_uchar
= { 0, UCHAR_MAX
},
1385 tvrange_ushort
= { 0, USHRT_MAX
},
1386 tvrange_uint
= { 0, UINT_MAX
},
1387 tvrange_ulong
= { 0, ULONG_MAX
},
1388 tvrange_size
= { 0, (size_t)-1 },
1389 tvrange_byte
= { 0, 255 },
1390 tvrange_u16
= { 0, 65535 },
1391 tvrange_u32
= { 0, 4294967296 };
1393 /* --- @tvec_claimeq_int@ --- *
1395 * Arguments: @struct tvec_state *tv@ = test-vector state
1396 * @long i0, i1@ = two signed integers
1397 * @const char *file@, @unsigned @lno@ = calling file and line
1398 * @const char *expr@ = the expression to quote on failure
1400 * Returns: Nonzero if @i0@ and @i1@ are equal, otherwise zero.
1402 * Use: Check that values of @i0@ and @i1@ are equal. As for
1403 * @tvec_claim@ above, a test case is automatically begun and
1404 * ended if none is already underway. If the values are
1405 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1406 * mismatched values are dumped: @i0@ is printed as the output
1407 * value and @i1@ is printed as the input reference.
1410 int tvec_claimeq_int(struct tvec_state
*tv
, long i0
, long i1
,
1411 const char *file
, unsigned lno
, const char *expr
)
1413 tv
->out
[0].v
.i
= i0
; tv
->in
[0].v
.i
= i1
;
1414 return (tvec_claimeq(tv
, &tvty_int
, 0, file
, lno
, expr
));
1417 /* --- @tvec_claimeq_uint@ --- *
1419 * Arguments: @struct tvec_state *tv@ = test-vector state
1420 * @unsigned long u0, u1@ = two unsigned integers
1421 * @const char *file@, @unsigned @lno@ = calling file and line
1422 * @const char *expr@ = the expression to quote on failure
1424 * Returns: Nonzero if @u0@ and @u1@ are equal, otherwise zero.
1426 * Use: Check that values of @u0@ and @u1@ are equal. As for
1427 * @tvec_claim@ above, a test case is automatically begun and
1428 * ended if none is already underway. If the values are
1429 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1430 * mismatched values are dumped: @u0@ is printed as the output
1431 * value and @u1@ is printed as the input reference.
1434 int tvec_claimeq_uint(struct tvec_state
*tv
,
1435 unsigned long u0
, unsigned long u1
,
1436 const char *file
, unsigned lno
, const char *expr
)
1438 tv
->out
[0].v
.u
= u0
; tv
->in
[0].v
.u
= u1
;
1439 return (tvec_claimeq(tv
, &tvty_uint
, 0, file
, lno
, expr
));
1442 /*----- Floating-point type -----------------------------------------------*/
1444 static void init_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1447 static int eq_float(const union tvec_regval
*rv0
,
1448 const union tvec_regval
*rv1
,
1449 const struct tvec_regdef
*rd
)
1450 { return (eqish_floating_p(rv0
->f
, rv1
->f
, rd
->arg
.p
)); }
1452 static int tobuf_float(buf
*b
, const union tvec_regval
*rv
,
1453 const struct tvec_regdef
*rd
)
1454 { return (buf_putf64l(b
, rv
->f
)); }
1455 static int frombuf_float(buf
*b
, union tvec_regval
*rv
,
1456 const struct tvec_regdef
*rd
)
1457 { return (buf_getf64l(b
, &rv
->f
)); }
1459 static int parse_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1460 struct tvec_state
*tv
)
1465 if (tvec_readword(tv
, &d
, ";", "floating-point number"))
1466 { rc
= -1; goto end
; }
1467 if (parse_floating(&rv
->f
, d
.buf
, rd
->arg
.p
, tv
))
1468 { rc
= -1; goto end
; }
1469 if (tvec_flushtoeol(tv
, 0))
1470 { rc
= -1; goto end
; }
1477 static void dump_float(const union tvec_regval
*rv
,
1478 const struct tvec_regdef
*rd
,
1480 const struct gprintf_ops
*gops
, void *go
)
1481 { format_floating(gops
, go
, rv
->f
); }
1483 const struct tvec_regty tvty_float
= {
1484 init_float
, trivial_release
, eq_float
,
1485 tobuf_float
, frombuf_float
,
1486 parse_float
, dump_float
1489 /* --- @tvec_claimeqish_float@ --- *
1491 * Arguments: @struct tvec_state *tv@ = test-vector state
1492 * @double f0, f1@ = two floating-point numbers
1493 * @unsigned f@ = flags (@TVFF_...@)
1494 * @double delta@ = maximum tolerable difference
1495 * @const char *file@, @unsigned @lno@ = calling file and line
1496 * @const char *expr@ = the expression to quote on failure
1498 * Returns: Nonzero if @f0@ and @u1@ are sufficiently close, otherwise
1501 * Use: Check that values of @f0@ and @f1@ are sufficiently close.
1502 * As for @tvec_claim@ above, a test case is automatically begun
1503 * and ended if none is already underway. If the values are
1504 * too far apart, then @tvec_fail@ is called, quoting @expr@,
1505 * and the mismatched values are dumped: @f0@ is printed as the
1506 * output value and @f1@ is printed as the input reference.
1508 * The details for the comparison are as follows.
1510 * * A NaN value matches any other NaN, and nothing else.
1512 * * An infinity matches another infinity of the same sign,
1515 * * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any
1516 * representable number matches only itself: in particular,
1517 * positive and negative zero are considered distinct.
1518 * (This allows tests to check that they land on the correct
1519 * side of branch cuts, for example.)
1521 * * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches
1522 * %$y$% when %$|x - y| < \delta$%.
1524 * * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
1525 * %$y$% when %$|1 - y/x| < \delta$%. (Note that this
1526 * criterion is asymmetric FIXME
1529 int tvec_claimeqish_float(struct tvec_state
*tv
,
1530 double f0
, double f1
, unsigned f
, double delta
,
1531 const char *file
, unsigned lno
,
1534 struct tvec_floatinfo fi
;
1535 union tvec_misc arg
;
1537 fi
.f
= f
; fi
.min
= fi
.max
= 0.0; fi
.delta
= delta
; arg
.p
= &fi
;
1538 tv
->out
[0].v
.f
= f0
; tv
->in
[0].v
.f
= f1
;
1539 return (tvec_claimeq(tv
, &tvty_float
, &arg
, file
, lno
, expr
));
1542 /* --- @tvec_claimeq_float@ --- *
1544 * Arguments: @struct tvec_state *tv@ = test-vector state
1545 * @double f0, f1@ = two floating-point numbers
1546 * @const char *file@, @unsigned @lno@ = calling file and line
1547 * @const char *expr@ = the expression to quote on failure
1549 * Returns: Nonzero if @f0@ and @u1@ are identical, otherwise zero.
1551 * Use: Check that values of @f0@ and @f1@ are identical. The
1552 * function is exactly equivalent to @tvec_claimeqish_float@
1553 * with @f == TVFF_EXACT@.
1556 int tvec_claimeq_float(struct tvec_state
*tv
,
1557 double f0
, double f1
,
1558 const char *file
, unsigned lno
,
1561 return (tvec_claimeqish_float(tv
, f0
, f1
, TVFF_EXACT
, 0.0,
1565 const struct tvec_floatinfo
1566 tvflt_finite
= { TVFF_EXACT
, -DBL_MAX
, DBL_MAX
, 0.0 },
1567 tvflt_nonneg
= { TVFF_EXACT
, 0, DBL_MAX
, 0.0 };
1569 /*----- Enumerations ------------------------------------------------------*/
1571 #define init_ienum init_int
1572 #define init_uenum init_uint
1573 #define init_fenum init_float
1574 static void init_penum(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1577 #define eq_ienum eq_int
1578 #define eq_uenum eq_uint
1579 static int eq_fenum(const union tvec_regval
*rv0
,
1580 const union tvec_regval
*rv1
,
1581 const struct tvec_regdef
*rd
)
1583 const struct tvec_fenuminfo
*ei
= rd
->arg
.p
;
1584 return (eqish_floating_p(rv0
->f
, rv1
->f
, ei
->fi
));
1586 static int eq_penum(const union tvec_regval
*rv0
,
1587 const union tvec_regval
*rv1
,
1588 const struct tvec_regdef
*rd
)
1589 { return (rv0
->p
== rv1
->p
); }
1591 #define tobuf_ienum tobuf_int
1592 #define tobuf_uenum tobuf_uint
1593 #define tobuf_fenum tobuf_float
1594 static int tobuf_penum(buf
*b
, const union tvec_regval
*rv
,
1595 const struct tvec_regdef
*rd
)
1597 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
1598 const struct tvec_passoc
*pa
;
1601 for (pa
= pei
->av
, i
= 0; pa
->tag
; pa
++, i
++)
1602 if (pa
->p
== rv
->p
) goto found
;
1606 return (signed_to_buf(b
, i
));
1609 #define frombuf_ienum frombuf_int
1610 #define frombuf_uenum frombuf_uint
1611 #define frombuf_fenum frombuf_float
1612 static int frombuf_penum(buf
*b
, union tvec_regval
*rv
,
1613 const struct tvec_regdef
*rd
)
1615 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
1616 const struct tvec_passoc
*pa
;
1619 for (pa
= pei
->av
, n
= 0; pa
->tag
; pa
++, n
++);
1620 if (signed_from_buf(b
, &i
)) return (-1);
1621 if (0 <= i
&& i
< n
) rv
->p
= (/*unconst*/ void *)pei
->av
[i
].p
;
1622 else if (i
== -1) rv
->p
= 0;
1627 #define DEFPARSE_ENUM(tag_, ty, slot) \
1628 static int parse_##slot##enum(union tvec_regval *rv, \
1629 const struct tvec_regdef *rd, \
1630 struct tvec_state *tv) \
1632 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
1633 const struct tvec_##slot##assoc *a; \
1634 dstr d = DSTR_INIT; \
1637 if (tvec_readword(tv, &d, ";", "enumeration tag or " LITSTR_##tag_)) \
1638 { rc = -1; goto end; } \
1639 for (a = ei->av; a->tag; a++) \
1640 if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; } \
1643 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } \
1650 #define LITSTR_INT "literal signed integer"
1651 #define FOUND_INT rv->i = a->i;
1652 #define MISSING_INT if (parse_signed(&rv->i, d.buf, ei->ir, tv)) \
1653 { rc = -1; goto end; }
1655 #define LITSTR_UINT "literal unsigned integer"
1656 #define FOUND_UINT rv->u = a->u;
1657 #define MISSING_UINT if (parse_unsigned(&rv->u, d.buf, ei->ur, tv)) \
1658 { rc = -1; goto end; }
1660 #define LITSTR_FLT "literal floating-point number, " \
1661 "`#-inf', `#+inf', or `#nan'"
1662 #define FOUND_FLT rv->f = a->f;
1663 #define MISSING_FLT if (parse_floating(&rv->f, d.buf, ei->fi, tv)) \
1664 { rc = -1; goto end; }
1666 #define LITSTR_PTR "`#nil'"
1667 #define FOUND_PTR rv->p = (/*unconst*/ void *)a->p;
1668 #define MISSING_PTR if (STRCMP(d.buf, ==, "#nil")) \
1671 tvec_error(tv, "unknown `%s' value `%s'", \
1673 rc = -1; goto end; \
1676 TVEC_MISCSLOTS(DEFPARSE_ENUM
)
1694 #undef DEFPARSE_ENUM
1696 #define DEFDUMP_ENUM(tag_, ty, slot) \
1697 static void dump_##slot##enum(const union tvec_regval *rv, \
1698 const struct tvec_regdef *rd, \
1700 const struct gprintf_ops *gops, void *go) \
1702 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
1703 const struct tvec_##slot##assoc *a; \
1705 for (a = ei->av; a->tag; a++) \
1706 if (rv->slot == a->slot) { \
1707 gprintf(gops, go, "%s", a->tag); \
1708 if (style&TVSF_COMPACT) return; \
1709 gprintf(gops, go, " ; = "); break; \
1715 #define MAYBE_PRINT_EXTRA \
1716 if (style&TVSF_COMPACT) ; \
1717 else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; } \
1718 else if (1) { gprintf(gops, go, " = "); goto _extra; } \
1721 #define PRINTRAW_INT gprintf(gops, go, "%ld", rv->i); \
1722 MAYBE_PRINT_EXTRA { \
1723 format_signed_hex(gops, go, rv->i); \
1724 maybe_format_signed_char(gops, go, rv->i); \
1727 #define PRINTRAW_UINT gprintf(gops, go, "%lu", rv->u); \
1728 MAYBE_PRINT_EXTRA { \
1729 format_unsigned_hex(gops, go, rv->u); \
1730 maybe_format_unsigned_char(gops, go, rv->u); \
1733 #define PRINTRAW_FLT format_floating(gops, go, rv->f);
1735 #define PRINTRAW_PTR if (!rv->p) gprintf(gops, go, "#nil"); \
1736 else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
1738 TVEC_MISCSLOTS(DEFDUMP_ENUM
)
1741 #undef PRINTRAW_UINT
1745 #undef MAYBE_PRINT_EXTRA
1748 #define DEFTY_ENUM(tag, ty, slot) \
1749 const struct tvec_regty tvty_##slot##enum = { \
1750 init_##slot##enum, trivial_release, eq_##slot##enum, \
1751 tobuf_##slot##enum, frombuf_##slot##enum, \
1752 parse_##slot##enum, dump_##slot##enum \
1754 TVEC_MISCSLOTS(DEFTY_ENUM
)
1757 static const struct tvec_iassoc bool_assoc
[] = {
1774 const struct tvec_ienuminfo tvenum_bool
=
1775 { "bool", bool_assoc
, &tvrange_int
};
1777 static const struct tvec_iassoc cmp_assoc
[] = {
1793 const struct tvec_ienuminfo tvenum_cmp
=
1794 { "cmp", cmp_assoc
, &tvrange_int
};
1796 /* --- @tvec_claimeq_tenum@ --- *
1798 * Arguments: @struct tvec_state *tv@ = test-vector state
1799 * @const struct tvec_typeenuminfo *ei@ = enumeration type info
1800 * @ty t0, t1@ = two values
1801 * @const char *file@, @unsigned @lno@ = calling file and line
1802 * @const char *expr@ = the expression to quote on failure
1804 * Returns: Nonzero if @t0@ and @t1@ are equal, otherwise zero.
1806 * Use: Check that values of @t0@ and @t1@ are equal. As for
1807 * @tvec_claim@ above, a test case is automatically begun and
1808 * ended if none is already underway. If the values are
1809 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1810 * mismatched values are dumped: @t0@ is printed as the output
1811 * value and @t1@ is printed as the input reference.
1814 #define DEFCLAIM(tag, ty, slot) \
1815 int tvec_claimeq_##slot##enum \
1816 (struct tvec_state *tv, \
1817 const struct tvec_##slot##enuminfo *ei, ty e0, ty e1, \
1818 const char *file, unsigned lno, const char *expr) \
1820 union tvec_misc arg; \
1823 tv->out[0].v.slot = GET_##tag(e0); \
1824 tv->in[0].v.slot = GET_##tag(e1); \
1825 return (tvec_claimeq(tv, &tvty_##slot##enum, &arg, \
1826 file, lno, expr)); \
1828 #define GET_INT(e) (e)
1829 #define GET_UINT(e) (e)
1830 #define GET_FLT(e) (e)
1831 #define GET_PTR(e) ((/*unconst*/ void *)(e))
1832 TVEC_MISCSLOTS(DEFCLAIM
)
1839 /*----- Flag types --------------------------------------------------------*/
1841 static int parse_flags(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1842 struct tvec_state
*tv
)
1844 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
1845 const struct tvec_flag
*f
;
1846 unsigned long m
= 0, v
= 0, t
;
1852 if (tvec_readword(tv
, &d
, "|;", "flag name or integer"))
1853 { rc
= -1; goto end
; }
1855 for (f
= fi
->fv
; f
->tag
; f
++)
1856 if (STRCMP(f
->tag
, ==, d
.buf
)) {
1858 { tvec_error(tv
, "colliding flag setting"); rc
= -1; goto end
; }
1860 { m
|= f
->m
; v
|= f
->v
; goto next
; }
1863 if (parse_unsigned(&t
, d
.buf
, fi
->range
, tv
))
1864 { rc
= -1; goto end
; }
1867 if (tvec_nexttoken(tv
)) break;
1869 if (ch
!= '|') { tvec_syntax(tv
, ch
, "`|'"); rc
= -1; goto end
; }
1870 if (tvec_nexttoken(tv
))
1871 { tvec_syntax(tv
, '\n', "flag name or integer"); rc
= -1; goto end
; }
1880 static void dump_flags(const union tvec_regval
*rv
,
1881 const struct tvec_regdef
*rd
,
1883 const struct gprintf_ops
*gops
, void *go
)
1885 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
1886 const struct tvec_flag
*f
;
1887 unsigned long m
= ~(unsigned long)0, v
= rv
->u
;
1890 for (f
= fi
->fv
, sep
= ""; f
->tag
; f
++)
1891 if ((m
&f
->m
) && (v
&f
->m
) == f
->v
) {
1892 gprintf(gops
, go
, "%s%s", sep
, f
->tag
); m
&= ~f
->m
;
1893 sep
= style
&TVSF_COMPACT ?
"|" : " | ";
1896 if (v
&m
) gprintf(gops
, go
, "%s0x%0*lx", sep
, hex_width(v
), v
&m
);
1898 if (!(style
&TVSF_COMPACT
))
1899 gprintf(gops
, go
, " ; = 0x%0*lx", hex_width(rv
->u
), rv
->u
);
1902 const struct tvec_regty tvty_flags
= {
1903 init_uint
, trivial_release
, eq_uint
,
1904 tobuf_uint
, frombuf_uint
,
1905 parse_flags
, dump_flags
1908 /* --- @tvec_claimeq_flags@ --- *
1910 * Arguments: @struct tvec_state *tv@ = test-vector state
1911 * @const struct tvec_flaginfo *fi@ = flags type info
1912 * @unsigned long f0, f1@ = two values
1913 * @const char *file@, @unsigned @lno@ = calling file and line
1914 * @const char *expr@ = the expression to quote on failure
1916 * Returns: Nonzero if @f0@ and @f1@ are equal, otherwise zero.
1918 * Use: Check that values of @f0@ and @f1@ are equal. As for
1919 * @tvec_claim@ above, a test case is automatically begun and
1920 * ended if none is already underway. If the values are
1921 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1922 * mismatched values are dumped: @f0@ is printed as the output
1923 * value and @f1@ is printed as the input reference.
1926 int tvec_claimeq_flags(struct tvec_state
*tv
,
1927 const struct tvec_flaginfo
*fi
,
1928 unsigned long f0
, unsigned long f1
,
1929 const char *file
, unsigned lno
, const char *expr
)
1931 union tvec_misc arg
;
1933 arg
.p
= fi
; tv
->out
[0].v
.u
= f0
; tv
->in
[0].v
.u
= f1
;
1934 return (tvec_claimeq(tv
, &tvty_flags
, &arg
, file
, lno
, expr
));
1937 /*----- Characters --------------------------------------------------------*/
1939 static int tobuf_char(buf
*b
, const union tvec_regval
*rv
,
1940 const struct tvec_regdef
*rd
)
1943 if (0 <= rv
->i
&& rv
->i
<= UCHAR_MAX
) u
= rv
->i
;
1944 else if (rv
->i
== EOF
) u
= MASK32
;
1946 return (buf_putu32l(b
, u
));
1949 static int frombuf_char(buf
*b
, union tvec_regval
*rv
,
1950 const struct tvec_regdef
*rd
)
1954 if (buf_getu32l(b
, &u
)) return (-1);
1955 if (0 <= u
&& u
<= UCHAR_MAX
) rv
->i
= u
;
1956 else if (u
== MASK32
) rv
->i
= EOF
;
1961 static int parse_char(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1962 struct tvec_state
*tv
)
1972 if (tvec_readword(tv
, &d
, ";", "character name")) { rc
= -1; goto end
; }
1973 if (read_charname(&ch
, d
.buf
, RCF_EOFOK
)) {
1974 rc
= tvec_error(tv
, "unknown character name `%s'", d
.buf
);
1977 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1978 rv
->i
= ch
; rc
= 0; goto end
;
1981 if (ch
== '\'') { f
|= f_quote
; ch
= getc(tv
->fp
); }
1984 if (!(f
&f_quote
)) { rc
= tvec_syntax(tv
, ch
, "character"); goto end
; }
1988 { f
&= ~f_quote
; ungetc(ch
, tv
->fp
); ch
= '\''; goto plain
; }
1990 if (f
&f_quote
) { f
&= ~f_quote
; ch
= '\''; goto plain
; }
1993 rc
= tvec_syntax(tv
, ch
, "character"); goto end
;
1995 if (read_charesc(&ch
, tv
)) return (-1);
2001 if (ch
!= '\'') { rc
= tvec_syntax(tv
, ch
, "`''"); goto end
; }
2003 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
2012 static void dump_char(const union tvec_regval
*rv
,
2013 const struct tvec_regdef
*rd
,
2015 const struct gprintf_ops
*gops
, void *go
)
2021 p
= find_charname(rv
->i
, (style
&TVSF_COMPACT
) ? CTF_SHORT
: CTF_PREFER
);
2023 gprintf(gops
, go
, "%s", p
);
2024 if (style
&TVSF_COMPACT
) return;
2025 else { gprintf(gops
, go
, " ;"); f
|= f_semi
; }
2029 if (f
&f_semi
) gprintf(gops
, go
, " = ");
2031 case ' ': case '\\': case '\'': quote
:
2032 format_char(gops
, go
, rv
->i
);
2035 if (!(style
&TVSF_COMPACT
) || !isprint(rv
->i
)) goto quote
;
2036 gprintf(gops
, go
, "%c", (int)rv
->i
);
2041 if (!(style
&TVSF_COMPACT
)) {
2042 if (!(f
&f_semi
)) gprintf(gops
, go
, " ;");
2043 gprintf(gops
, go
, " = %ld = ", rv
->i
);
2044 format_signed_hex(gops
, go
, rv
->i
);
2050 const struct tvec_regty tvty_char
= {
2051 init_int
, trivial_release
, eq_int
,
2052 tobuf_char
, frombuf_char
,
2053 parse_char
, dump_char
2056 /* --- @tvec_claimeq_char@ --- *
2058 * Arguments: @struct tvec_state *tv@ = test-vector state
2059 * @int ch0, ch1@ = two character codes
2060 * @const char *file@, @unsigned @lno@ = calling file and line
2061 * @const char *expr@ = the expression to quote on failure
2063 * Returns: Nonzero if @ch0@ and @ch1@ are equal, otherwise zero.
2065 * Use: Check that values of @ch0@ and @ch1@ are equal. As for
2066 * @tvec_claim@ above, a test case is automatically begun and
2067 * ended if none is already underway. If the values are
2068 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2069 * mismatched values are dumped: @ch0@ is printed as the output
2070 * value and @ch1@ is printed as the input reference.
2073 int tvec_claimeq_char(struct tvec_state
*tv
, int c0
, int c1
,
2074 const char *file
, unsigned lno
, const char *expr
)
2076 tv
->out
[0].v
.i
= c0
; tv
->in
[0].v
.i
= c1
;
2077 return (tvec_claimeq(tv
, &tvty_char
, 0, file
, lno
, expr
));
2080 /*----- Text and byte strings ---------------------------------------------*/
2082 static void init_string(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
2083 { rv
->str
.p
= 0; rv
->str
.sz
= 0; }
2085 static void init_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
2086 { rv
->bytes
.p
= 0; rv
->bytes
.sz
= 0; }
2088 static void release_string(union tvec_regval
*rv
,
2089 const struct tvec_regdef
*rd
)
2090 { xfree(rv
->str
.p
); }
2092 static void release_bytes(union tvec_regval
*rv
,
2093 const struct tvec_regdef
*rd
)
2094 { xfree(rv
->bytes
.p
); }
2096 static int eq_string(const union tvec_regval
*rv0
,
2097 const union tvec_regval
*rv1
,
2098 const struct tvec_regdef
*rd
)
2100 return (rv0
->str
.sz
== rv1
->str
.sz
&&
2102 MEMCMP(rv0
->str
.p
, ==, rv1
->str
.p
, rv1
->str
.sz
)));
2105 static int eq_bytes(const union tvec_regval
*rv0
,
2106 const union tvec_regval
*rv1
,
2107 const struct tvec_regdef
*rd
)
2109 return (rv0
->bytes
.sz
== rv1
->bytes
.sz
&&
2111 MEMCMP(rv0
->bytes
.p
, ==, rv1
->bytes
.p
, rv1
->bytes
.sz
)));
2114 static int tobuf_string(buf
*b
, const union tvec_regval
*rv
,
2115 const struct tvec_regdef
*rd
)
2116 { return (buf_putmem32l(b
, rv
->str
.p
, rv
->str
.sz
)); }
2118 static int tobuf_bytes(buf
*b
, const union tvec_regval
*rv
,
2119 const struct tvec_regdef
*rd
)
2120 { return (buf_putmem32l(b
, rv
->bytes
.p
, rv
->bytes
.sz
)); }
2122 static int frombuf_string(buf
*b
, union tvec_regval
*rv
,
2123 const struct tvec_regdef
*rd
)
2128 p
= buf_getmem32l(b
, &sz
); if (!p
) return (-1);
2129 tvec_allocstring(rv
, sz
); memcpy(rv
->str
.p
, p
, sz
); rv
->str
.p
[sz
] = 0;
2133 static int frombuf_bytes(buf
*b
, union tvec_regval
*rv
,
2134 const struct tvec_regdef
*rd
)
2139 p
= buf_getmem32l(b
, &sz
); if (!p
) return (-1);
2140 tvec_allocbytes(rv
, sz
); memcpy(rv
->bytes
.p
, p
, sz
);
2144 static int check_string_length(size_t sz
, const struct tvec_urange
*ur
,
2145 struct tvec_state
*tv
)
2147 if (ur
&& (ur
->min
> sz
|| sz
> ur
->max
))
2148 return (tvec_error(tv
,
2149 "invalid string length %lu; must be in [%lu .. %lu]",
2150 (unsigned long)sz
, ur
->min
, ur
->max
));
2154 static int parse_string(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2155 struct tvec_state
*tv
)
2157 void *p
= rv
->str
.p
;
2159 if (read_compound_string(&p
, &rv
->str
.sz
, TVCODE_BARE
, 0, tv
))
2162 if (check_string_length(rv
->str
.sz
, rd
->arg
.p
, tv
)) return (-1);
2166 static int parse_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2167 struct tvec_state
*tv
)
2169 void *p
= rv
->bytes
.p
;
2171 if (read_compound_string(&p
, &rv
->bytes
.sz
, TVCODE_HEX
, 0, tv
))
2174 if (check_string_length(rv
->bytes
.sz
, rd
->arg
.p
, tv
)) return (-1);
2178 static void dump_string(const union tvec_regval
*rv
,
2179 const struct tvec_regdef
*rd
,
2181 const struct gprintf_ops
*gops
, void *go
)
2183 const unsigned char *p
, *q
, *l
;
2185 #define f_nonword 1u
2186 #define f_newline 2u
2188 if (!rv
->str
.sz
) { gprintf(gops
, go
, "\"\""); return; }
2190 p
= (const unsigned char *)rv
->str
.p
; l
= p
+ rv
->str
.sz
;
2192 case '!': case '#': case ';': case '"': case '\'':
2193 case '(': case '{': case '[': case ']': case '}': case ')':
2194 f
|= f_nonword
; break;
2196 for (q
= p
; q
< l
; q
++)
2197 if (*q
== '\n' && q
!= l
- 1) f
|= f_newline
;
2198 else if (!*q
|| !isgraph(*q
) || *q
== '\\') f
|= f_nonword
;
2199 if (f
&f_newline
) { gprintf(gops
, go
, "\n\t"); goto quote
; }
2200 else if (f
&f_nonword
) goto quote
;
2202 gops
->putm(go
, (const char *)p
, rv
->str
.sz
);
2206 gprintf(gops
, go
, "\"");
2207 for (q
= p
; q
< l
; q
++)
2208 if (!isprint(*q
) || *q
== '"') {
2209 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
2210 if (*q
!= '\n' || (style
&TVSF_COMPACT
))
2211 format_charesc(gops
, go
, *q
, FCF_BRACE
);
2213 if (q
+ 1 == l
) { gprintf(gops
, go
, "\\n\""); return; }
2214 else gprintf(gops
, go
, "\\n\"\n\t\"");
2218 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
2219 gprintf(gops
, go
, "\"");
2225 static void dump_bytes(const union tvec_regval
*rv
,
2226 const struct tvec_regdef
*rd
,
2228 const struct gprintf_ops
*gops
, void *go
)
2230 const unsigned char *p
= rv
->bytes
.p
, *l
= p
+ rv
->bytes
.sz
;
2231 size_t off
, sz
= rv
->bytes
.sz
;
2236 gprintf(gops
, go
, style
&TVSF_COMPACT ?
"\"\"" : "\"\" ; empty");
2240 if (style
&TVSF_COMPACT
) {
2241 while (p
< l
) gprintf(gops
, go
, "%02x", *p
++);
2245 if (sz
> 16) gprintf(gops
, go
, "\n\t");
2247 off
= 0; wd
= hex_width(sz
);
2249 if (l
- p
< 16) n
= l
- p
;
2252 for (i
= 0; i
< n
; i
++) {
2253 if (i
< n
) gprintf(gops
, go
, "%02x", p
[i
]);
2254 else gprintf(gops
, go
, " ");
2255 if (i
< n
- 1 && i
%4 == 3) gprintf(gops
, go
, " ");
2257 gprintf(gops
, go
, " ; ");
2258 if (sz
> 16) gprintf(gops
, go
, "[%0*lx] ", wd
, (unsigned long)off
);
2259 for (i
= 0; i
< n
; i
++)
2260 gprintf(gops
, go
, "%c", isprint(p
[i
]) ? p
[i
] : '.');
2262 if (p
< l
) gprintf(gops
, go
, "\n\t");
2266 const struct tvec_regty tvty_string
= {
2267 init_string
, release_string
, eq_string
,
2268 tobuf_string
, frombuf_string
,
2269 parse_string
, dump_string
2272 const struct tvec_regty tvty_bytes
= {
2273 init_bytes
, release_bytes
, eq_bytes
,
2274 tobuf_bytes
, frombuf_bytes
,
2275 parse_bytes
, dump_bytes
2278 /* --- @tvec_claimeq_string@ --- *
2280 * Arguments: @struct tvec_state *tv@ = test-vector state
2281 * @const char *p0@, @size_t sz0@ = first string with length
2282 * @const char *p1@, @size_t sz1@ = second string with length
2283 * @const char *file@, @unsigned @lno@ = calling file and line
2284 * @const char *expr@ = the expression to quote on failure
2286 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
2289 * Use: Check that strings at @p0@ and @p1@ are equal. As for
2290 * @tvec_claim@ above, a test case is automatically begun and
2291 * ended if none is already underway. If the values are
2292 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2293 * mismatched values are dumped: @p0@ is printed as the output
2294 * value and @p1@ is printed as the input reference.
2297 int tvec_claimeq_string(struct tvec_state
*tv
,
2298 const char *p0
, size_t sz0
,
2299 const char *p1
, size_t sz1
,
2300 const char *file
, unsigned lno
, const char *expr
)
2302 tv
->out
[0].v
.str
.p
= (/*unconst*/ char *)p0
; tv
->out
[0].v
.str
.sz
= sz0
;
2303 tv
->in
[0].v
.str
.p
=(/*unconst*/ char *) p1
; tv
->in
[0].v
.str
.sz
= sz1
;
2304 return (tvec_claimeq(tv
, &tvty_string
, 0, file
, lno
, expr
));
2307 /* --- @tvec_claimeq_strz@ --- *
2309 * Arguments: @struct tvec_state *tv@ = test-vector state
2310 * @const char *p0, *p1@ = two strings to compare
2311 * @const char *file@, @unsigned @lno@ = calling file and line
2312 * @const char *expr@ = the expression to quote on failure
2314 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
2317 * Use: Check that strings at @p0@ and @p1@ are equal, as for
2318 * @tvec_claimeq_string@, except that the strings are assumed
2319 * null-terminated, so their lengths don't need to be supplied
2323 int tvec_claimeq_strz(struct tvec_state
*tv
,
2324 const char *p0
, const char *p1
,
2325 const char *file
, unsigned lno
, const char *expr
)
2327 tv
->out
[0].v
.str
.p
= (/*unconst*/ char *)p0
;
2328 tv
->out
[0].v
.str
.sz
= strlen(p0
);
2329 tv
->in
[0].v
.str
.p
= (/*unconst*/ char *)p1
;
2330 tv
->in
[0].v
.str
.sz
= strlen(p1
);
2331 return (tvec_claimeq(tv
, &tvty_string
, 0, file
, lno
, expr
));
2334 /* --- @tvec_claimeq_bytes@ --- *
2336 * Arguments: @struct tvec_state *tv@ = test-vector state
2337 * @const void *p0@, @size_t sz0@ = first string with length
2338 * @const void *p1@, @size_t sz1@ = second string with length
2339 * @const char *file@, @unsigned @lno@ = calling file and line
2340 * @const char *expr@ = the expression to quote on failure
2342 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
2345 * Use: Check that binary strings at @p0@ and @p1@ are equal. As for
2346 * @tvec_claim@ above, a test case is automatically begun and
2347 * ended if none is already underway. If the values are
2348 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2349 * mismatched values are dumped: @p0@ is printed as the output
2350 * value and @p1@ is printed as the input reference.
2353 int tvec_claimeq_bytes(struct tvec_state
*tv
,
2354 const void *p0
, size_t sz0
,
2355 const void *p1
, size_t sz1
,
2356 const char *file
, unsigned lno
, const char *expr
)
2358 tv
->out
[0].v
.bytes
.p
= (/*unconst*/ void *)p0
;
2359 tv
->out
[0].v
.bytes
.sz
= sz0
;
2360 tv
->in
[0].v
.bytes
.p
= (/*unconst*/ void *)p1
;
2361 tv
->in
[0].v
.bytes
.sz
= sz1
;
2362 return (tvec_claimeq(tv
, &tvty_bytes
, 0, file
, lno
, expr
));
2365 /* --- @tvec_allocstring@, @tvec_allocbytes@ --- *
2367 * Arguments: @union tvec_regval *rv@ = register value
2368 * @size_t sz@ = required size
2372 * Use: Allocated space in a text or binary string register. If the
2373 * current register size is sufficient, its buffer is left
2374 * alone; otherwise, the old buffer, if any, is freed and a
2375 * fresh buffer allocated. These functions are not intended to
2376 * be used to adjust a buffer repeatedly, e.g., while building
2377 * output incrementally: (a) they will perform badly, and (b)
2378 * the old buffer contents are simply discarded if reallocation
2379 * is necessary. Instead, use a @dbuf@ or @dstr@.
2381 * The @tvec_allocstring@ function sneakily allocates an extra
2382 * byte for a terminating zero. The @tvec_allocbytes@ function
2386 void tvec_allocstring(union tvec_regval
*rv
, size_t sz
)
2388 if (rv
->str
.sz
<= sz
) { xfree(rv
->str
.p
); rv
->str
.p
= xmalloc(sz
+ 1); }
2392 void tvec_allocbytes(union tvec_regval
*rv
, size_t sz
)
2394 if (rv
->bytes
.sz
< sz
) { xfree(rv
->bytes
.p
); rv
->bytes
.p
= xmalloc(sz
); }
2398 /*----- Buffer type -------------------------------------------------------*/
2400 static int eq_buffer(const union tvec_regval
*rv0
,
2401 const union tvec_regval
*rv1
,
2402 const struct tvec_regdef
*rd
)
2403 { return (rv0
->bytes
.sz
== rv1
->bytes
.sz
); }
2405 static int tobuf_buffer(buf
*b
, const union tvec_regval
*rv
,
2406 const struct tvec_regdef
*rd
)
2407 { return (unsigned_to_buf(b
, rv
->bytes
.sz
)); }
2409 static int frombuf_buffer(buf
*b
, union tvec_regval
*rv
,
2410 const struct tvec_regdef
*rd
)
2414 if (unsigned_from_buf(b
, &u
)) return (-1);
2415 if (u
> (size_t)-1) return (-1);
2416 tvec_allocbytes(rv
, u
); memset(rv
->bytes
.p
, '!', u
);
2420 static const char units
[] = "kMGTPEZY";
2422 static int parse_buffer(union tvec_regval
*rv
,
2423 const struct tvec_regdef
*rd
,
2424 struct tvec_state
*tv
)
2427 const char *q
, *unit
;
2434 if (tvec_readword(tv
, &d
, ";", "buffer length")) { rc
= -1; goto end
; }
2435 if (parse_unsigned_integer(&u
, &q
, d
.buf
)) goto bad
;
2437 tvec_skipspc(tv
); pos
= d
.len
;
2438 if (!tvec_readword(tv
, &d
, ";", 0)) pos
++;
2442 if (u
> (size_t)-1) goto rangerr
;
2443 for (t
= u
, unit
= units
; *unit
; unit
++) {
2444 if (t
> (size_t)-1/1024) f
|= f_range
;
2447 if (f
&f_range
) goto rangerr
;
2453 if (check_string_length(u
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
2455 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
2456 tvec_allocbytes(rv
, u
); memset(rv
->bytes
.p
, '?', u
);
2459 DDESTROY(&d
); return (rc
);
2462 tvec_error(tv
, "invalid buffer length `%s'", d
.buf
);
2466 tvec_error(tv
, "buffer length `%s' out of range", d
.buf
);
2472 static void dump_buffer(const union tvec_regval
*rv
,
2473 const struct tvec_regdef
*rd
,
2475 const struct gprintf_ops
*gops
, void *go
)
2478 unsigned long u
= rv
->bytes
.sz
;
2481 gprintf(gops
, go
, "%lu B", u
);
2483 for (unit
= units
, u
/= 1024; !(u
%1024) && unit
[1]; u
/= 1024, unit
++);
2484 gprintf(gops
, go
, "%lu %cB", u
, *unit
);
2488 const struct tvec_regty tvty_buffer
= {
2489 init_bytes
, release_bytes
, eq_buffer
,
2490 tobuf_buffer
, frombuf_buffer
,
2491 parse_buffer
, dump_buffer
2494 /*----- That's all, folks -------------------------------------------------*/