chiark / gitweb /
014423e0a831defd07a54ae0be9398ec322f430e
[mLib] / test / tvec-output.c
1 /* -*-c-*-
2  *
3  * Test vector output management
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 "config.h"
31
32 #include <assert.h>
33 #include <ctype.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #include <unistd.h>
39 #include <sys/stat.h>
40
41 #include "alloc.h"
42 #include "bench.h"
43 #include "dstr.h"
44 #include "macros.h"
45 #include "quis.h"
46 #include "report.h"
47 #include "ttycolour.h"
48
49 #include "tvec.h"
50 #include "tvec-bench.h"
51 #include "tvec-output.h"
52
53 /*----- Common machinery --------------------------------------------------*/
54
55 /* --- @regdisp@ --- *
56  *
57  * Arguments:   @unsigned disp@ = a @TVRD_...@ disposition code
58  *
59  * Returns:     A human-readable adjective describing the register
60  *              disposition.
61  */
62
63 static const char *regdisp(unsigned disp)
64 {
65   switch (disp) {
66     case TVRD_INPUT: return "input";
67     case TVRD_OUTPUT: return "output";
68     case TVRD_MATCH: return "matched";
69     case TVRD_FOUND: return "found";
70     case TVRD_EXPECT: return "expected";
71     default: abort();
72   }
73 }
74
75 /* --- @interpret_boolean@, @getenv_boolean@ --- *
76  *
77  * Arguments:   @const char *val@ = a string
78  *              @const char *var@ = environment variable name
79  *              @int dflt@ = default value
80  *              @const char *what, ...@ = format string describing where the
81  *                      setting came from
82  *
83  * Returns:     @0@ if the string, or variable,  is set to something
84  *              falseish, @1@ if it's set to something truish, or @dflt@
85  *              otherwise.
86  */
87
88 static PRINTF_LIKE(3, 4)
89   int interpret_boolean(const char *val, int dflt, const char *what, ...)
90 {
91   dstr d = DSTR_INIT;
92   va_list ap;
93   int rc;
94
95   if (!val)
96     rc = dflt;
97   else if (STRCMP(val, ==, "y") || STRCMP(val, ==, "yes") ||
98            STRCMP(val, ==, "t") || STRCMP(val, ==, "true") ||
99            STRCMP(val, ==, "on") || STRCMP(val, ==, "force") ||
100            STRCMP(val, ==, "1"))
101     rc = 1;
102   else if (STRCMP(val, ==, "n") || STRCMP(val, ==, "no") ||
103            STRCMP(val, ==, "nil") || STRCMP(val, ==, "f") ||
104              STRCMP(val, ==, "false") ||
105            STRCMP(val, ==, "off") || STRCMP(val, ==, "inhibit") ||
106            STRCMP(val, ==, "0"))
107     rc = 0;
108   else {
109     va_start(ap, what); dstr_vputf(&d, what, &ap); va_end(ap);
110     moan("ignoring unexpected value `%s' for %s", val, d.buf);
111     rc = dflt;
112   }
113   dstr_destroy(&d); return (rc);
114 }
115
116 static int getenv_boolean(const char *var, int dflt)
117 {
118   return (interpret_boolean(getenv(var), dflt,
119                             "environment variable `%s'", var));
120 }
121
122 /* --- @register_maxnamelen@ --- *
123  *
124  * Arguments:   @const struct tvec_state *tv@ = test vector state
125  *
126  * Returns:     The maximum length of a register name in the current test.
127  */
128
129 static int register_maxnamelen(const struct tvec_state *tv)
130 {
131   const struct tvec_regdef *rd;
132   int maxlen = 10, n;
133
134   for (rd = tv->test->regs; rd->name; rd++)
135     { n = strlen(rd->name); if (n > maxlen) maxlen = n; }
136   return (maxlen);
137 }
138
139 /* --- @print_ident@ --- *
140  *
141  * Arguments:   @struct tvec_state *tv@ = test-vector state
142  *              @unsigned style@ = style to use for register dumps
143  *              @const struct gprintf_ops *gops@ = output operations
144  *              @void *go@ = output state
145  *
146  * Returns:     ---
147  *
148  * Use:         Write a benchmark identification to the output.
149  */
150
151 static void print_ident(struct tvec_state *tv, unsigned style,
152                         const struct gprintf_ops *gops, void *go)
153 {
154   const struct tvec_regdef *rd;
155   unsigned f = 0;
156
157 #define f_any 1u
158
159   for (rd = tv->test->regs; rd->name; rd++)
160     if (rd->f&TVRF_ID) {
161       if (!(f&f_any)) f |= f_any;
162       else if (style&TVSF_RAW) gops->putch(go, ' ');
163       else gprintf(gops, go, ", ");
164       gprintf(gops, go, "%s", rd->name);
165       if (style&TVSF_RAW) gops->putch(go, '=');
166       else gprintf(gops, go, " = ");
167       rd->ty->dump(&TVEC_REG(tv, in, rd->i)->v, rd, style, gops, go);
168     }
169
170 #undef f_any
171 }
172
173 /*----- Output layout -----------------------------------------------------*/
174
175 /* We have two main jobs in output layout: trimming trailing blanks; and
176  * adding a prefix to each line.
177  *
178  * This is somehow much more complicated than it ought to be.
179  */
180
181 struct layout {
182   FILE *fp;                              /* output file */
183   const char *prefix, *pfxtail, *pfxlim; /* prefix pointers */
184   dstr w;                               /* trailing whitespace */
185   dstr ctrl;                            /* control sequence for next word */
186   unsigned f;                           /* flags */
187 #define LYTF_NEWL 1u                    /*   start of output line */
188 };
189
190 /* Support macros.  These assume `lyt' is defined as a pointer to the `struct
191  * layout' state.
192  */
193
194 #define SPLIT_RANGE(tail, base, limit) do {                             \
195   /* Set TAIL to point just after the last nonspace character between   \
196    * BASE and LIMIT.  If there are no nonspace characters, then set     \
197    * TAIL to equal BASE.                                                \
198    */                                                                   \
199                                                                         \
200   for (tail = limit; tail > base && ISSPACE(tail[-1]); tail--);         \
201 } while (0)
202
203 #define PUT_RANGE(base, limit) do {                                     \
204   /* Write the range of characters between BASE and LIMIT to the output \
205    * file.  Return immediately on error.                                \
206    */                                                                   \
207                                                                         \
208   size_t _n = limit - base;                                             \
209   if (_n && fwrite(base, 1, _n, lyt->fp) < _n) return (-1);             \
210 } while (0)
211
212 #define PUT_CHAR(ch) do {                                               \
213   /* Write CH to the output. Return immediately on error. */            \
214                                                                         \
215   if (putc(ch, lyt->fp) == EOF) return (-1);                            \
216 } while (0)
217
218 #define PUT_PREFIX do {                                                 \
219   /* Output the prefix, if there is one.  Return immediately on error. */ \
220                                                                         \
221   if (lyt->prefix) PUT_RANGE(lyt->prefix, lyt->pfxlim);                 \
222 } while (0)
223
224 #define PUT_SAVED do {                                                  \
225   /* Output the saved trailing blank material in the buffer. */         \
226                                                                         \
227   size_t _n = lyt->w.len;                                               \
228   if (_n && fwrite(lyt->w.buf, 1, _n, lyt->fp) < _n) return (-1);       \
229 } while (0)
230
231 #define PUT_CTRL do {                                                   \
232   /* Output the accumulated control string. */                          \
233                                                                         \
234   size_t _n = lyt->ctrl.len;                                            \
235   if (_n && fwrite(lyt->ctrl.buf, 1, _n, lyt->fp) < _n) return (-1);    \
236 } while (0)
237
238 #define PUT_PFXINB do {                                                 \
239   /* Output the initial nonblank portion of the prefix, if there is     \
240    * one.  Return immediately on error.                                 \
241    */                                                                   \
242                                                                         \
243   if (lyt->prefix) PUT_RANGE(lyt->prefix, lyt->pfxtail);                \
244 } while (0)
245
246 #define SAVE_PFXTAIL do {                                               \
247   /* Save the trailing blank portion of the prefix. */                  \
248                                                                         \
249   if (lyt->prefix)                                                      \
250     DPUTM(&lyt->w, lyt->pfxtail, lyt->pfxlim - lyt->pfxtail);           \
251 } while (0)
252
253 /* --- @set_layout_prefix@ --- *
254  *
255  * Arguments:   @struct layout *lyt@ = layout state
256  *              @const char *prefix@ = new prefix string or null
257  *
258  * Returns:     ---
259  *
260  * Use:         Change the configured prefix string.  The change takes effect
261  *              at the start of the next line (or the current line if it's
262  *              empty or only whitespace so far).
263  */
264
265 static void set_layout_prefix(struct layout *lyt, const char *prefix)
266 {
267   const char *q, *l;
268
269  if (!prefix || !*prefix)
270     lyt->prefix = lyt->pfxtail = lyt->pfxlim = 0;
271   else {
272     lyt->prefix = prefix;
273     l = lyt->pfxlim = prefix + strlen(prefix);
274     SPLIT_RANGE(q, prefix, l); lyt->pfxtail = q;
275   }
276 }
277
278 /* --- @init_layout@ --- *
279  *
280  * Arguments:   @struct layout *lyt@ = layout state to initialize
281  *              @FILE *fp@ = output file
282  *              @const char *prefix@ = prefix string (or null if empty)
283  *
284  * Returns:     ---
285  *
286  * Use:         Initialize a layout state.
287  */
288
289 static void init_layout(struct layout *lyt, FILE *fp, const char *prefix)
290 {
291   lyt->fp = fp;
292   lyt->f = LYTF_NEWL;
293   dstr_create(&lyt->w); dstr_create(&lyt->ctrl);
294   set_layout_prefix(lyt, prefix);
295 }
296
297 /* --- @destroy_layout@ --- *
298  *
299  * Arguments:   @struct layout *lyt@ = layout state
300  *              @unsigned f@ = flags (@DLF_...@)
301  *
302  * Returns:     ---
303  *
304  * Use:         Releases a layout state and the resources it holds.
305  *              Close the file if @DLF_CLOSE@ is set in @f@; otherwise leave
306  *              it open (in case it's @stderr@ or something).
307  */
308
309 #define DLF_CLOSE 1u
310 static void destroy_layout(struct layout *lyt, unsigned f)
311 {
312   if (f&DLF_CLOSE) fclose(lyt->fp);
313   dstr_destroy(&lyt->w); dstr_destroy(&lyt->ctrl);
314 }
315
316 /* --- @layout_char@ --- *
317  *
318  * Arguments:   @struct layout *lyt@ = layout state
319  *              @int ch@ = character to write
320  *
321  * Returns:     Zero on success, @-1@ on failure.
322  *
323  * Use:         Write a single character to the output.
324  */
325
326 static int layout_char(struct layout *lyt, int ch)
327 {
328   if (ch == '\n') {
329     if (lyt->f&LYTF_NEWL) PUT_PFXINB;
330     PUT_CHAR('\n'); lyt->f |= LYTF_NEWL; DRESET(&lyt->w);
331   } else if (isspace(ch))
332     DPUTC(&lyt->w, ch);
333   else {
334     if (lyt->f&LYTF_NEWL) { PUT_PFXINB; lyt->f &= ~LYTF_NEWL; }
335     PUT_SAVED; PUT_CHAR(ch); DRESET(&lyt->w);
336   }
337   return (0);
338 }
339
340 /* --- @layout_string@ --- *
341  *
342  * Arguments:   @struct layout *lyt@ = layout state
343  *              @const char *p@ = string to write
344  *              @size_t sz@ = length of string
345  *
346  * Returns:     Zero on success, @-1@ on failure.
347  *
348  * Use:         Write a string to the output.
349  */
350
351 static int layout_string(struct layout *lyt, const char *p, size_t sz)
352 {
353   const char *q, *r, *l = p + sz;
354
355   /* This is rather vexing.  There are a small number of jobs to do, but the
356    * logic for deciding which to do when gets rather hairy if, as I've tried
357    * here, one aims to minimize the number of decisions being checked, so
358    * it's worth canning them into macros.
359    *
360    * Here, a `blank' is a whitespace character other than newline.  The input
361    * buffer consists of one or more `segments', each of which consists of:
362    *
363    *   * an initial portion, which is either empty or ends with a nonblank
364    *     character;
365    *
366    *   * a suffix which consists only of blanks; and
367    *
368    *   * an optional newline.
369    *
370    * All segments except the last end with a newline.
371    */
372
373 #define SPLIT_SEGMENT do {                                              \
374   /* Determine the bounds of the current segment.  If there is a final  \
375    * newline, then q is non-null and points to this newline; otherwise, \
376    * q is null.  The initial portion of the segment lies between p .. r \
377    * and the blank suffix lies between r .. q (or r .. l if q is null). \
378    * This sounds awkward, but the suffix is only relevant if there is   \
379    * no newline.                                                        \
380    */                                                                   \
381                                                                         \
382   q = memchr(p, '\n', l - p); SPLIT_RANGE(r, p, q ? q : l);             \
383 } while (0)
384
385 #define PUT_NONBLANK do {                                               \
386   /* Output the initial portion of the segment. */                      \
387                                                                         \
388   PUT_RANGE(p, r);                                                      \
389 } while (0)
390
391 #define PUT_NEWLINE do {                                                \
392   /* Write a newline, and advance to the next segment. */               \
393                                                                         \
394   PUT_CHAR('\n'); p = q + 1;                                            \
395 } while (0)
396
397 #define SAVE_TAIL do {                                                  \
398   /* Save the trailing blank portion of the segment in the buffer.      \
399    * Assumes that there is no newline, since otherwise the suffix would \
400    * be omitted.                                                        \
401    */                                                                   \
402                                                                         \
403   DPUTM(&lyt->w, r, l - r);                                             \
404 } while (0)
405
406   /* Determine the bounds of the first segment.  Handling this is the most
407    * complicated part of this function.
408    */
409   SPLIT_SEGMENT;
410
411   if (!q) {
412     /* This is the only segment.  We'll handle the whole thing here.
413      *
414      * If there's an initial nonblank portion, then we need to write that
415      * out.  Furthermore, if we're at the start of the line then we'll need
416      * to write the prefix, and if there's saved blank material then we'll
417      * need to write that.  Otherwise, there's only blank stuff, which we
418      * accumulate in the buffer.
419      *
420      * If we're at the start of a line here, then put the prefix followed by
421      * any saved whitespace, and then our initial nonblank portion.  Then
422      * save our new trailing space.
423      */
424
425     if (r > p) {
426       if (lyt->f&LYTF_NEWL) { PUT_PREFIX; lyt->f &= ~LYTF_NEWL; }
427       PUT_SAVED; PUT_CTRL; PUT_NONBLANK;
428       DRESET(&lyt->w); DRESET(&lyt->ctrl);
429     }
430     SAVE_TAIL;
431     return (0);
432   }
433
434   /* There is at least one more segment, so we know that there'll be a line
435    * to output.
436    */
437   if (r > p) {
438     if (lyt->f&LYTF_NEWL) PUT_PREFIX;
439     PUT_SAVED; PUT_CTRL; PUT_NONBLANK;
440     DRESET(&lyt->ctrl);
441   } else if (lyt->f&LYTF_NEWL)
442     PUT_PFXINB;
443   PUT_NEWLINE; DRESET(&lyt->w);
444   SPLIT_SEGMENT;
445
446   /* Main loop over whole segments with trailing newlines.  For each one, we
447    * know that we're starting at the beginning of a line and there's a final
448    * newline, so we write the initial prefix and drop the trailing blanks.
449    */
450   while (q) {
451     if (r > p) { PUT_PREFIX; PUT_CTRL; PUT_NONBLANK; DRESET(&lyt->ctrl); }
452     else PUT_PFXINB;
453     PUT_NEWLINE;
454     SPLIT_SEGMENT;
455   }
456
457   /* At the end, there's no final newline.  If there's nonblank material,
458    * then we can write the prefix and the nonblank stuff.  Otherwise, stash
459    * the blank stuff (including the trailing blanks of the prefix) and leave
460    * the newline flag set.
461    */
462   if (r > p) { PUT_PREFIX; PUT_NONBLANK; lyt->f &= ~LYTF_NEWL; }
463   else { lyt->f |= LYTF_NEWL; SAVE_PFXTAIL; }
464   SAVE_TAIL;
465
466 #undef SPLIT_SEGMENT
467 #undef PUT_NONBLANK
468 #undef PUT_NEWLINE
469 #undef SAVE_TAIL
470
471   return (0);
472 }
473
474 #undef SPLIT_RANGE
475 #undef PUT_RANGE
476 #undef PUT_PREFIX
477 #undef PUT_PFXINB
478 #undef PUT_SAVED
479 #undef PUT_CHAR
480 #undef SAVE_PFXTAIL
481
482 /*----- Human-readable output ---------------------------------------------*/
483
484 /* Predefined attributes. */
485 #define HIGHLIGHTS(_st, _)                                              \
486   _(_st, LOCFN,    "lf", TC_FG(CYAN))   /* location filename */         \
487   _(_st, LOCLN,    "ln", TC_FG(CYAN))   /* location line number */      \
488   _(_st, LOCSEP,   "ls", TC_FG(BRBLUE)) /* location separator `:' */    \
489   _(_st, INFO,     "mi", 0)             /* information */               \
490   _(_st, NOTE,     "mn", TC_FG(YELLOW)) /* notices */                   \
491   _(_st, ERR,      "me", TC_FG(MAGENTA) | TCAF_BOLD) /* error messages */       \
492   _(_st, UNKLEV,   "mu", TC_FG(WHITE) | TC_BG(RED) | TCAF_BOLD)         \
493   _(_st, DSINPUT,  "di", 0)             /* disposition for input value */ \
494   _(_st, DSOUTPUT, "do", 0)             /* ... unsolicited output */    \
495   _(_st, DSMATCH,  "dm", 0)             /* ... matching output */       \
496   _(_st, DSFOUND,  "df", 0)             /* ... incorrect output */      \
497   _(_st, DSEXPECT, "dx", 0)             /* ... reference output */      \
498   _(_st, RNINPUT,  "ri", 0)             /* register name for input value */ \
499   _(_st, RNOUTPUT, "ro", 0)             /* ... unsolicited output */    \
500   _(_st, RNMATCH,  "rm", 0)             /* ... matching output */       \
501   _(_st, RNFOUND,  "rf", 0)             /* ... incorrect output */      \
502   _(_st, RNEXPECT, "rx", 0)             /* ... reference output */      \
503   _(_st, VINPUT,   "vi", 0)             /* input value */               \
504   _(_st, VOUTPUT,  "vo", 0)             /* unsolicited output value */  \
505   _(_st, VMATCH,   "vm", 0)             /* matching output value */     \
506   _(_st, VFOUND,   "vf", TC_FG(BRRED))  /* incorrect output value */    \
507   _(_st, VEXPECT,  "vx", TC_FG(GREEN))  /* reference output value */    \
508   _(_st, VUNSET,   "vu", TC_FG(YELLOW)) /* register not set */          \
509   _(_st, LOSE,     "ol", TC_FG(RED) | TCAF_BOLD) /* report failure */   \
510   _(_st, SKIP,     "os", TC_FG(YELLOW)) /* report a skipped test/group */ \
511   _(_st, XFAIL,    "ox", TC_FG(BLUE) | TCAF_BOLD) /* report expected fail */ \
512   _(_st, WIN,      "ow", TC_FG(GREEN))  /* report success */            \
513   _(_st, SBLOSE,   "sl", TC_FG(RED) | TCAF_BOLD) /* scoreboard failure */       \
514   _(_st, SBSKIP,   "ss", TC_FG(YELLOW)) /* scoreboard skipped test */   \
515   _(_st, SBXFAIL,  "sx", TC_FG(BLUE) | TCAF_BOLD) /* scoreboard xfail */        \
516   _(_st, SBWIN,    "sw", 0)             /* scoreboard success */
517
518 TTYCOLOUR_DEFENUM(HIGHLIGHTS, HL_);
519 #define HL_PLAIN (-1)
520
521 /* Scoreboard indicators. */
522 static const char scoreboard[] = { 'x', '_', 'o', '.' };
523
524 struct human_output {
525   struct tvec_output _o;                /* output base class */
526   struct tvec_state *tv;                /* stashed testing state */
527   arena *a;                             /* arena for memory allocation */
528   struct layout lyt;                    /* output layout */
529   char *outbuf; size_t outsz;           /* buffer for formatted output */
530   dstr scoreboard;                      /* history of test group results */
531   unsigned short attr[HL__LIMIT];       /* highlight attribute map */
532   struct ttycolour_state tc;            /* terminal colour state */
533   int maxlen;                           /* longest register name */
534   unsigned f;                           /* flags */
535                                         /*   bits 0--7 from @TVHF_...@ */
536 #define HOF_DUPERR 0x0100u              /*   duplicate errors to stderr */
537 #define HOF_PROGRESS 0x0200u            /*   progress display is active */
538 };
539
540 /* --- @setattr@, @setattr_layout@ --- *
541  *
542  * Arguments:   @struct human_output *h@ = output state
543  *              @int hi@ = highlight code to set
544  *
545  * Returns:     ---
546  *
547  * Use:         Send a control sequence to the output stream so that
548  *              subsequent text is printed with the given attributes.
549  */
550
551 static void setattr_common(struct human_output *h,
552                            const struct gprintf_ops *gops, void *go, int hl)
553 {
554   if (h->f&TVHF_COLOUR)
555     ttycolour_setattr(gops, go, &h->tc, hl < 0 ? 0 : h->attr[hl]);
556 }
557
558 static void setattr(struct human_output *h, int hl)
559   { setattr_common(h, &file_printops, h->lyt.fp, hl); }
560
561 static void setattr_layout(struct human_output *h, int hl)
562   { setattr_common(h, &dstr_printops, &h->lyt.ctrl, hl); }
563
564 /* --- @clear_progress@ --- *
565  *
566  * Arguments:   @struct human_output *h@ = output state
567  *
568  * Returns:     ---
569  *
570  * Use:         Remove the progress display from the terminal.
571  *
572  *              If the progress display isn't active then do nothing.
573  */
574
575 static void clear_progress(struct human_output *h)
576 {
577   size_t i, n;
578
579   if (h->f&HOF_PROGRESS) {
580     n = strlen(h->tv->test->name) + 2 + h->scoreboard.len;
581     for (i = 0; i < n; i++) fputs("\b \b", h->lyt.fp);
582     h->f &= ~HOF_PROGRESS;
583   }
584 }
585
586 /* --- @write_scoreboard_char@ --- *
587  *
588  * Arguments:   @struct human_output *h@ = output state
589  *              @int ch@ = scoreboard character to print
590  *
591  * Returns:     ---
592  *
593  * Use:         Write a scoreboard character, indicating the outcome of a
594  *              test, to the output stream, with appropriate highlighting.
595  */
596
597 static void write_scoreboard_char(struct human_output *h, int ch)
598 {
599   assert(0 <= ch && ch < TVOUT_LIMIT);
600   setattr(h, HL_SBLOSE + ch); putc(scoreboard[ch], h->lyt.fp);
601 }
602
603 /* --- @show_progress@ --- *
604  *
605  * Arguments:   @struct human_output *h@ = output state
606  *
607  * Returns:     ---
608  *
609  * Use:         Show the progress display, with the record of outcomes for
610  *              the current test group.
611  *
612  *              If the progress display is already active, or the output
613  *              stream is not interactive, then nothing happens.
614  */
615
616 static void show_progress(struct human_output *h)
617 {
618   struct tvec_state *tv = h->tv;
619   const char *p, *l;
620
621   if (tv->test && (h->f&TVHF_TTY) && !(h->f&HOF_PROGRESS)) {
622     fprintf(h->lyt.fp, "%s: ", tv->test->name);
623     for (p = h->scoreboard.buf, l = p + h->scoreboard.len; p < l; p++)
624       write_scoreboard_char(h, *p);
625     setattr(h, HL_PLAIN);
626     fflush(h->lyt.fp); h->f |= HOF_PROGRESS;
627   }
628 }
629
630 /* --- @human_writech@, @human_write@, @human_writef@ --- *
631  *
632  * Arguments:   @void *go@ = output sink, secretly a @struct human_output@
633  *              @int ch@ = character to write
634  *              @const char *@p@, @size_t sz@ = string (with explicit length)
635  *                      to write
636  *              @const char *p, ...@ = format control string and arguments to
637  *                      write
638  *
639  * Returns:     ---
640  *
641  * Use:         Write characters, strings, or formatted strings to the
642  *              output, applying appropriate layout.
643  *
644  *              For the human output driver, the layout machinery just strips
645  *              trailing spaces.
646  */
647
648 static int human_writech(void *go, int ch)
649   { struct human_output *h = go; return (layout_char(&h->lyt, ch)); }
650
651 static int human_writem(void *go, const char *p, size_t sz)
652   { struct human_output *h = go; return (layout_string(&h->lyt, p, sz)); }
653
654 static int human_nwritef(void *go, size_t maxsz, const char *p, ...)
655 {
656   struct human_output *h = go;
657   size_t n;
658   va_list ap;
659
660   va_start(ap, p);
661   n = gprintf_memputf(h->a, &h->outbuf, &h->outsz, maxsz, p, ap);
662   va_end(ap);
663   if (layout_string(&h->lyt, h->outbuf, n)) return (-1);
664   return (n);
665 }
666
667 static const struct gprintf_ops human_printops =
668   { human_writech, human_writem, human_nwritef };
669
670 /* --- @human_bsession@ --- *
671  *
672  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
673  *                       @struct human_output@
674  *              @struct tvec_state *tv@ = the test state producing output
675  *
676  * Returns:     ---
677  *
678  * Use:         Begin a test session.
679  *
680  *              The human driver just records the test state for later
681  *              reference.
682  */
683
684 static void human_bsession(struct tvec_output *o, struct tvec_state *tv)
685   { struct human_output *h = (struct human_output *)o; h->tv = tv; }
686
687 /* --- @human_report_unusual@ --- *
688  *
689  * Arguments:   @struct human_output *h@ = output sink
690  *              @unsigned nxfail, nskip@ = number of expected failures and
691  *                      skipped tests
692  *
693  * Returns:     ---
694  *
695  * Use:         Write (directly on the output stream) a note about expected
696  *              failures and/or skipped tests, if there were any.
697  */
698
699 static void human_report_unusual(struct human_output *h,
700                                  unsigned nxfail, unsigned nskip)
701 {
702   unsigned f = 0;
703 #define f_any 1u
704
705   if (nxfail) {
706     fprintf(h->lyt.fp, "%s%u ", f&f_any ? ", " : " (", nxfail);
707     setattr(h, HL_XFAIL);
708     fprintf(h->lyt.fp, "expected %s", nxfail == 1 ? "failure" : "failures");
709     setattr(h, HL_PLAIN);
710     f |= f_any;
711   }
712
713   if (nskip) {
714     fprintf(h->lyt.fp, "%s%u ", f&f_any ? ", " : " (", nskip);
715     setattr(h, HL_SKIP); fputs("skipped", h->lyt.fp); setattr(h, HL_PLAIN);
716     f |= f_any;
717   }
718
719   if (f&f_any) fputc(')', h->lyt.fp);
720
721 #undef f_any
722 }
723
724 /* --- @human_esession@ --- *
725  *
726  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
727  *                       @struct human_output@
728  *
729  * Returns:     Suggested exit code.
730  *
731  * Use:         End a test session.
732  *
733  *              The human driver prints a final summary of the rest results
734  *              and returns a suitable exit code.
735  */
736
737 static int human_esession(struct tvec_output *o)
738 {
739   struct human_output *h = (struct human_output *)o;
740   struct tvec_state *tv = h->tv;
741   unsigned
742     all_win = tv->all[TVOUT_WIN], grps_win = tv->grps[TVOUT_WIN],
743     all_xfail = tv->all[TVOUT_XFAIL],
744     all_lose = tv->all[TVOUT_LOSE], grps_lose = tv->grps[TVOUT_LOSE],
745     all_skip = tv->all[TVOUT_SKIP], grps_skip = tv->grps[TVOUT_SKIP],
746     all_pass = all_win + all_xfail, all_run = all_pass + all_lose,
747     grps_run = grps_win + grps_lose;
748
749   if (!all_lose) {
750     setattr(h, HL_WIN); fputs("PASSED", h->lyt.fp); setattr(h, HL_PLAIN);
751     fprintf(h->lyt.fp, " %s%u %s",
752             !(all_skip || grps_skip) ? "all " : "",
753             all_pass, all_pass == 1 ? "test" : "tests");
754     human_report_unusual(h, all_xfail, all_skip);
755     fprintf(h->lyt.fp, " in %u %s",
756             grps_win, grps_win == 1 ? "group" : "groups");
757     human_report_unusual(h, 0, grps_skip);
758   } else {
759     setattr(h, HL_LOSE); fputs("FAILED", h->lyt.fp); setattr(h, HL_PLAIN);
760     fprintf(h->lyt.fp, " %u out of %u %s",
761             all_lose, all_run, all_run == 1 ? "test" : "tests");
762     human_report_unusual(h, all_xfail, all_skip);
763     fprintf(h->lyt.fp, " in %u out of %u %s",
764             grps_lose, grps_run, grps_run == 1 ? "group" : "groups");
765     human_report_unusual(h, 0, grps_skip);
766   }
767   fputc('\n', h->lyt.fp);
768
769   if (tv->f&TVSF_ERROR) {
770     setattr(h, HL_ERR); fputs("ERRORS", h->lyt.fp); setattr(h, HL_PLAIN);
771     fputs(" found in input; tests may not have run correctly\n", h->lyt.fp);
772   }
773
774   h->tv = 0; return (tv->f&TVSF_ERROR ? 2 : all_lose ? 1 : 0);
775 }
776
777 /* --- @human_bgroup@ --- *
778  *
779  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
780  *                       @struct human_output@
781  *
782  * Returns:     ---
783  *
784  * Use:         Begin a test group.
785  *
786  *              The human driver determines the length of the longest
787  *              register name, resets the group progress scoreboard, and
788  *              activates the progress display.
789  */
790
791 static void human_bgroup(struct tvec_output *o)
792 {
793   struct human_output *h = (struct human_output *)o;
794
795   h->maxlen = register_maxnamelen(h->tv);
796   dstr_reset(&h->scoreboard); show_progress(h);
797 }
798
799 /* --- @human_skipgroup@ --- *
800  *
801  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
802  *                       @struct human_output@
803  *              @const char *excuse@, @va_list *ap@ = reason for skipping the
804  *                      group, or null
805  *
806  * Returns:     ---
807  *
808  * Use:         Report that a test group is being skipped.
809  *
810  *              The human driver just reports the situation to its output
811  *              stream.
812  */
813
814 static void human_skipgroup(struct tvec_output *o,
815                             const char *excuse, va_list *ap)
816 {
817   struct human_output *h = (struct human_output *)o;
818
819   if (!(h->f&TVHF_TTY))
820     fprintf(h->lyt.fp, "%s ", h->tv->test->name);
821   else {
822     show_progress(h); h->f &= ~HOF_PROGRESS;
823     if (h->scoreboard.len) putc(' ', h->lyt.fp);
824   }
825   setattr(h, HL_SKIP); fputs("skipped", h->lyt.fp); setattr(h, HL_PLAIN);
826   if (excuse) { fputs(": ", h->lyt.fp); vfprintf(h->lyt.fp, excuse, *ap); }
827   fputc('\n', h->lyt.fp);
828 }
829
830 /* --- @human_egroup@ --- *
831  *
832  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
833  *                       @struct human_output@
834  *
835  * Returns:     ---
836  *
837  * Use:         Report that a test group has finished.
838  *
839  *              The human driver reports a summary of the group's tests.
840  */
841
842 static void human_egroup(struct tvec_output *o)
843 {
844   struct human_output *h = (struct human_output *)o;
845   struct tvec_state *tv = h->tv;
846   unsigned win = tv->curr[TVOUT_WIN], xfail = tv->curr[TVOUT_XFAIL],
847     lose = tv->curr[TVOUT_LOSE], skip = tv->curr[TVOUT_SKIP],
848     run = win + lose + xfail;
849
850   if (h->f&TVHF_TTY) h->f &= ~HOF_PROGRESS;
851   else fprintf(h->lyt.fp, "%s:", h->tv->test->name);
852
853   if (lose) {
854     fprintf(h->lyt.fp, " %u/%u ", lose, run);
855     setattr(h, HL_LOSE); fputs("FAILED", h->lyt.fp); setattr(h, HL_PLAIN);
856     human_report_unusual(h, xfail, skip);
857   } else {
858     fputc(' ', h->lyt.fp); setattr(h, HL_WIN);
859     fputs("ok", h->lyt.fp); setattr(h, HL_PLAIN);
860     human_report_unusual(h, xfail, skip);
861   }
862   fputc('\n', h->lyt.fp);
863 }
864
865 /* --- @human_btest@ --- *
866  *
867  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
868  *                       @struct human_output@
869  *
870  * Returns:     ---
871  *
872  * Use:         Report that a test is starting.
873  *
874  *              The human driver makes sure the progress display is active.
875  */
876
877 static void human_btest(struct tvec_output *o)
878   { struct human_output *h = (struct human_output *)o; show_progress(h); }
879
880 /* --- @human_report_location@ --- *
881  *
882  * Arguments:   @struct human_output *h@ = output state
883  *              @FILE *fp@ = stream to write the location on
884  *              @const char *file@ = filename
885  *              @unsigned lno@ = line number
886  *
887  * Returns:     ---
888  *
889  * Use:         Print the filename and line number to the output stream @fp@.
890  *              Also, if appropriate, print interleaved highlighting control
891  *              codes to our usual output stream.  If @file@ is null then do
892  *              nothing.
893  */
894
895 static void human_report_location(struct human_output *h, FILE *fp,
896                                   const char *file, unsigned lno)
897 {
898   if (!file)
899     /* nothing to do */;
900   else if (fp != h->lyt.fp || !(h->f&TVHF_COLOUR))
901     fprintf(fp, "%s:%u: ", file, lno);
902   else {
903     setattr(h, HL_LOCFN);       fputs(file, fp);
904     setattr(h, HL_LOCSEP);      fputc(':', fp);
905     setattr(h, HL_LOCLN);       fprintf(fp, "%u", lno);
906     setattr(h, HL_LOCSEP);      fputc(':', fp);
907     setattr(h, HL_PLAIN);       fputc(' ', fp);
908   }
909 }
910
911 /* --- @human_outcome@, @human_skip@, @human_fail@ --- *
912  *
913  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
914  *                       @struct human_output@
915  *              @unsigned attr@ = attribute to apply to the outcome
916  *              @const char *outcome@ = outcome string to report
917  *              @const char *detail@, @va_list *ap@ = a detail message
918  *              @const char *excuse@, @va_list *ap@ = reason for skipping the
919  *                      test
920  *
921  * Returns:     ---
922  *
923  * Use:         Report that a test has been skipped or failed.
924  *
925  *              The human driver reports the situation on its output stream.
926  */
927
928 static void human_outcome(struct tvec_output *o,
929                           unsigned attr, const char *outcome,
930                           const char *detail, va_list *ap)
931 {
932   struct human_output *h = (struct human_output *)o;
933   struct tvec_state *tv = h->tv;
934
935   clear_progress(h);
936   human_report_location(h, h->lyt.fp, tv->infile, tv->test_lno);
937   fprintf(h->lyt.fp, "`%s' ", tv->test->name);
938   setattr(h, attr); fputs(outcome, h->lyt.fp); setattr(h, HL_PLAIN);
939   if (detail) { fputs(": ", h->lyt.fp); vfprintf(h->lyt.fp, detail, *ap); }
940   fputc('\n', h->lyt.fp);
941 }
942
943 static void human_skip(struct tvec_output *o,
944                        const char *excuse, va_list *ap)
945   { human_outcome(o, HL_SKIP, "skipped", excuse, ap); }
946 static void human_fail(struct tvec_output *o,
947                        const char *detail, va_list *ap)
948   { human_outcome(o, HL_LOSE, "FAILED", detail, ap); }
949
950 /* --- @human_dumpreg@ --- *
951  *
952  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
953  *                       @struct human_output@
954  *              @unsigned disp@ = register disposition
955  *              @const union tvec_regval *rv@ = register value
956  *              @const struct tvec_regdef *rd@ = register definition
957  *
958  * Returns:     ---
959  *
960  * Use:         Dump a register.
961  *
962  *              The human driver applies highlighting to mismatching output
963  *              registers, but otherwise delegates to the register type
964  *              handler and the layout machinery.
965  */
966
967 static void human_dumpreg(struct tvec_output *o,
968                           unsigned disp, const union tvec_regval *rv,
969                           const struct tvec_regdef *rd)
970 {
971   struct human_output *h = (struct human_output *)o;
972   const char *ds = regdisp(disp); int n = strlen(ds) + strlen(rd->name);
973
974   clear_progress(h);
975   gprintf(&human_printops, h, "%*s", 10 + h->maxlen - n, "");
976   setattr_layout(h, HL_DSINPUT + disp);
977   gprintf(&human_printops, h, "%s", ds);
978   setattr(h, HL_PLAIN); layout_char(&h->lyt, ' ');
979   setattr_layout(h, HL_RNINPUT + disp);
980   gprintf(&human_printops, h, "%s", rd->name);
981   setattr(h, HL_PLAIN); gprintf(&human_printops, h, " = ");
982   if (!rv) {
983     setattr_layout(h, HL_VUNSET);
984     gprintf(&human_printops, h, "#unset");
985   } else {
986     setattr_layout(h, HL_VINPUT + disp);
987     rd->ty->dump(rv, rd, 0, &human_printops, h);
988   }
989   setattr(h, HL_PLAIN); layout_char(&h->lyt, '\n');
990 }
991
992 /* --- @human_etest@ --- *
993  *
994  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
995  *                       @struct human_output@
996  *              @unsigned outcome@ = the test outcome
997  *
998  * Returns:     ---
999  *
1000  * Use:         Report that a test has finished.
1001  *
1002  *              The human driver reactivates the progress display, if
1003  *              necessary, and adds a new character for the completed test.
1004  */
1005
1006 static void human_etest(struct tvec_output *o, unsigned outcome)
1007 {
1008   struct human_output *h = (struct human_output *)o;
1009
1010   if (h->f&TVHF_TTY) {
1011     show_progress(h);
1012     dstr_putc(&h->scoreboard, outcome); write_scoreboard_char(h, outcome);
1013     setattr(h, HL_PLAIN); fflush(h->lyt.fp);
1014   }
1015 }
1016
1017 /* --- @human_report@ --- *
1018  *
1019  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1020  *                       @struct human_output@
1021  *              @unsigned level@ = message level (@TVLEV_...@)
1022  *              @const char *msg@, @va_list *ap@ = format string and
1023  *                      arguments
1024  *
1025  * Returns:     ---
1026  *
1027  * Use:         Report a message to the user.
1028  *
1029  *              The human driver arranges to show the message on @stderr@ as
1030  *              well as the usual output, with a certain amount of
1031  *              intelligence in case they're both actually the same device.
1032  */
1033
1034 static void human_report(struct tvec_output *o, unsigned level,
1035                          const char *msg, va_list *ap)
1036 {
1037   struct human_output *h = (struct human_output *)o;
1038   struct tvec_state *tv = h->tv;
1039   const char *levstr; unsigned levhl;
1040   dstr d = DSTR_INIT;
1041   unsigned f = 0;
1042 #define f_progress 1u
1043
1044   dstr_vputf(&d, msg, ap); dstr_putc(&d, '\n');
1045
1046   switch (level) {
1047 #define CASE(tag, name, val)                                            \
1048     case TVLEV_##tag: levstr = name; levhl = HL_##tag; break;
1049     TVEC_LEVELS(CASE)
1050     default: levstr = "??"; levhl = HL_UNKLEV; break;
1051   }
1052
1053   if (h->f&HOF_PROGRESS) { clear_progress(h); f |= f_progress; }
1054
1055   if (h->f&HOF_DUPERR) {
1056     fprintf(stderr, "%s: ", QUIS);
1057     human_report_location(h, stderr, tv->infile, tv->lno);
1058     fprintf(stderr, "%s: ", levstr);
1059     fwrite(d.buf, 1, d.len, stderr);
1060   }
1061
1062   human_report_location(h, h->lyt.fp, tv->infile, tv->lno);
1063   setattr(h, levhl); fputs(levstr, h->lyt.fp);
1064   setattr(h, HL_PLAIN); fputs(": ", h->lyt.fp);
1065   fwrite(d.buf, 1, d.len, h->lyt.fp);
1066
1067   if (f&f_progress) show_progress(h);
1068   dstr_destroy(&d);
1069
1070 #undef f_progress
1071 }
1072
1073 /* --- @human_bbench@ --- *
1074  *
1075  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1076  *                       @struct human_output@
1077  *              @const char *desc@ = adhoc test description
1078  *              @unsigned unit@ = measurement unit (@BTU_...@)
1079  *
1080  * Returns:     ---
1081  *
1082  * Use:         Report that a benchmark has started.
1083  *
1084  *              The human driver just prints the start of the benchmark
1085  *              report.
1086  */
1087
1088 static void human_bbench(struct tvec_output *o,
1089                          const char *desc, unsigned unit)
1090 {
1091   struct human_output *h = (struct human_output *)o;
1092   struct tvec_state *tv = h->tv;
1093
1094   clear_progress(h);
1095   gprintf(&human_printops, h, "%s ", tv->test->name);
1096   if (desc) gprintf(&human_printops, h, "%s", desc);
1097   else print_ident(tv, TVSF_COMPACT, &human_printops, h);
1098   gprintf(&human_printops, h, ": ");
1099   if (h->f&TVHF_TTY) fflush(h->lyt.fp);
1100 }
1101
1102 /* --- @human_ebench@ --- *
1103  *
1104  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1105  *                       @struct human_output@
1106  *              @const char *desc@ = adhoc test description
1107  *              @unsigned unit@ = measurement unit (@BTU_...@)
1108  *              @const struct bench_timing *t@ = measurement
1109  *
1110  * Returns:     ---
1111  *
1112  * Use:         Report a benchmark's results.
1113  *
1114  *              The human driver just delegates to the default benchmark
1115  *              reporting, via the layout machinery.
1116  */
1117
1118 static void human_ebench(struct tvec_output *o,
1119                          const char *desc, unsigned unit,
1120                          const struct bench_timing *t)
1121 {
1122   struct human_output *h = (struct human_output *)o;
1123
1124   tvec_benchreport(&human_printops, h, unit, 0, t);
1125   layout_char(&h->lyt, '\n');
1126 }
1127
1128 static const struct tvec_benchoutops human_benchops =
1129   { human_bbench, human_ebench };
1130
1131 /* --- @human_extend@ --- *
1132  *
1133  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1134  *                       @struct human_output@
1135  *              @const char *name@ = extension name
1136  *
1137  * Returns:     A pointer to the extension implementation, or null.
1138  */
1139
1140 static const void *human_extend(struct tvec_output *o, const char *name)
1141 {
1142   if (STRCMP(name, ==, TVEC_BENCHOUTEXT)) return (&human_benchops);
1143   else return (0);
1144 }
1145
1146 /* --- @human_destroy@ --- *
1147  *
1148  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1149  *                       @struct human_output@
1150  *
1151  * Returns:     ---
1152  *
1153  * Use:         Release the resources held by the output driver.
1154  */
1155
1156 static void human_destroy(struct tvec_output *o)
1157 {
1158   struct human_output *h = (struct human_output *)o;
1159
1160   destroy_layout(&h->lyt,
1161                  h->lyt.fp == stdout || h->lyt.fp == stderr ? 0 : DLF_CLOSE);
1162   dstr_destroy(&h->scoreboard);
1163   x_free(h->a, h->outbuf); x_free(h->a, h);
1164 }
1165
1166 static const struct tvec_outops human_ops = {
1167   human_bsession, human_esession,
1168   human_bgroup, human_skipgroup, human_egroup,
1169   human_btest, human_skip, human_fail, human_dumpreg, human_etest,
1170   human_report, human_extend, human_destroy
1171 };
1172
1173 /* --- @tvec_humanoutput@ --- *
1174  *
1175  * Arguments:   @FILE *fp@ = output file to write on
1176  *              @unsigned f, m@ = flags and mask
1177  *
1178  * Returns:     An output formatter.
1179  *
1180  * Use:         Return an output formatter which writes on @fp@ with the
1181  *              expectation that a human will interpret the output.
1182  *
1183  *              The flags @f@ and mask @m@ operate together.  Flag bits not
1184  *              covered by the mask must be left clear, i.e., @f&~m$ must be
1185  *              zero; the semantics are that a set mask bit indicates that
1186  *              the corresponding bit of @f@ should control the indicated
1187  *              behaviour; a clear mask bit indicates that a suitable default
1188  *              should be chosen based on environmental conditions.
1189  *
1190  *              If @TVHF_TTY@ is set, then the output shows a `scoreboard'
1191  *              indicating the outcome of each test case attempted, providing
1192  *              a visual indication of progress.  If @TVHF_COLOUR@ is set,
1193  *              then the output uses control codes for colour and other
1194  *              highlighting.  It is unusual to set @TVHF_COLOUR@ without
1195  *              @TVHF_TTY@, this is permitted anyway.
1196  *
1197  *              The environment variables %|TVEC_TTY|% and %|TVEC_COLOUR|%
1198  *              provide default values for these settings.  If they are not
1199  *              set, then @TVHF_TTY@ is set if @fp@ refers to a terminal, and
1200  *              @TVHF_COLOUR@ is set if @TVHF_TTY@ is set and, additionally,
1201  *              the %|TERM|% environment variable is set to a value other
1202  *              than %|dumb|%.
1203  */
1204
1205 struct tvec_output *tvec_humanoutput(FILE *fp, unsigned f, unsigned m)
1206 {
1207   struct human_output *h;
1208   const char *p;
1209   struct stat st_out, st_err;
1210   int rc_out, rc_err;
1211
1212   static const struct ttycolour_style hltab[] =
1213     TTYCOLOUR_INITTAB(HIGHLIGHTS);
1214
1215   assert(!(f&~m));
1216
1217   if (!(m&TVHF_TTY))
1218     switch (getenv_boolean("TVEC_TTY", -1)) {
1219       case 1: f |= TVHF_TTY; break;
1220       case 0: break;
1221       default:
1222         if (isatty(fileno(fp))) f |= TVHF_TTY;
1223         break;
1224     }
1225   if (!(m&TVHF_COLOUR))
1226     switch (getenv_boolean("TVEC_COLOUR", -1)) {
1227       case 1: f |= TVHF_COLOUR; break;
1228       case 0: break;
1229       default:
1230         if (f&TVHF_TTY) {
1231           p = getenv("TERM");
1232           if (p && STRCMP(p, !=, "dumb")) f |= TVHF_COLOUR;
1233         }
1234         break;
1235     }
1236
1237   /* Decide whether to write copies of reports to stderr.
1238    *
1239    * There's not much point if the output is already going to stderr.
1240    * Otherwise, check to see whether stdout is the same underlying file.
1241    */
1242   if (fp != stderr) {
1243     rc_out = fstat(fileno(fp), &st_out);
1244     rc_err = fstat(STDERR_FILENO, &st_err);
1245     if (!rc_err && (rc_out ||
1246                     st_out.st_dev != st_err.st_dev ||
1247                     st_out.st_ino != st_err.st_ino))
1248       f |= HOF_DUPERR;
1249   }
1250
1251   XNEW(h); h->a = arena_global; h->_o.ops = &human_ops;
1252   h->f = f;
1253
1254   /* Initialize the colour tables. */
1255   if (h->f&TVHF_COLOUR) {
1256     ttycolour_config(h->attr, "TVEC_COLOURS", TCIF_GETENV | TCIF_REPORT,
1257                      hltab);
1258     ttycolour_init(&h->tc);
1259   }
1260
1261   init_layout(&h->lyt, fp, 0);
1262   h->outbuf = 0; h->outsz = 0;
1263
1264   dstr_create(&h->scoreboard);
1265   return (&h->_o);
1266 }
1267
1268 /*----- Machine-readable output -------------------------------------------*/
1269
1270 struct machine_output {
1271   struct tvec_output _o;                /* output base class */
1272   struct tvec_state *tv;                /* stashed testing state */
1273   arena *a;                             /* arena for memory allocation */
1274   FILE *fp;                             /* output stream */
1275   char *outbuf; size_t outsz;           /* buffer for formatted output */
1276   unsigned grpix, testix;               /* group and test indices */
1277   unsigned f;                           /* flags */
1278 #define MF_BENCH 1u                     /*   current test is a benchmark */
1279 };
1280
1281 /* --- @machine_writech@, @machine_write@, @machine_writef@ --- *
1282  *
1283  * Arguments:   @void *go@ = output sink, secretly a @struct machine_output@
1284  *              @int ch@ = character to write
1285  *              @const char *@p@, @size_t sz@ = string (with explicit length)
1286  *                      to write
1287  *              @const char *p, ...@ = format control string and arguments to
1288  *                      write
1289  *
1290  * Returns:     ---
1291  *
1292  * Use:         Write characters, strings, or formatted strings to the
1293  *              output, applying appropriate quoting.
1294  */
1295
1296 static void machine_escape(struct machine_output *m, int ch)
1297 {
1298   switch (ch) {
1299     case 0: fputs("\\0", m->fp); break;
1300     case '\a': fputs("\\a", m->fp); break;
1301     case '\b': fputs("\\b", m->fp); break;
1302     case '\x1b': fputs("\\e", m->fp); break;
1303     case '\f': fputs("\\f", m->fp); break;
1304     case '\n': fputs("\\n", m->fp); break;
1305     case '\r': fputs("\\r", m->fp); break;
1306     case '\t': fputs("\\t", m->fp); break;
1307     case '\v': fputs("\\v", m->fp); break;
1308     case '"': fputs("\\\"", m->fp); break;
1309     case '\\': fputs("\\\\", m->fp); break;
1310     default: fprintf(m->fp, "\\x{%02x}", ch);
1311   }
1312 }
1313
1314 static int machine_writech(void *go, int ch)
1315 {
1316   struct machine_output *m = go;
1317
1318   if (ISPRINT(ch)) putc(ch, m->fp);
1319   else machine_escape(m, ch);
1320   return (1);
1321 }
1322
1323 static int machine_writem(void *go, const char *p, size_t sz)
1324 {
1325   struct machine_output *m = go;
1326   const char *q, *l = p + sz;
1327
1328   for (;;) {
1329     q = p;
1330     for (;;) {
1331       if (q >= l) goto final;
1332       if (!ISPRINT(*q) || *q == '\\' || *q == '"') break;
1333       q++;
1334     }
1335     if (p < q) fwrite(p, 1, q - p, m->fp);
1336     p = q; machine_escape(m, (unsigned char)*p++);
1337   }
1338 final:
1339   if (p < l) fwrite(p, 1, l - p, m->fp);
1340   return (sz);
1341 }
1342
1343 static int machine_nwritef(void *go, size_t maxsz, const char *p, ...)
1344 {
1345   struct machine_output *m = go;
1346   int n;
1347   va_list ap;
1348
1349   va_start(ap, p);
1350   n = gprintf_memputf(m->a, &m->outbuf, &m->outsz, maxsz, p, ap);
1351   va_end(ap);
1352   return (machine_writem(m, m->outbuf, n));
1353 }
1354
1355 static const struct gprintf_ops machine_printops =
1356   { machine_writech, machine_writem, machine_nwritef };
1357
1358 /* --- @machine_maybe_quote@ --- *
1359  *
1360  * Arguments:   @struct machine_output *m@ = output sink
1361  *              @const char *p@ = pointer to string
1362  *
1363  * Returns:     ---
1364  *
1365  * Use:         Print the string @p@, quoting it if necessary.
1366  */
1367
1368 static void machine_maybe_quote(struct machine_output *m, const char *p)
1369 {
1370   const char *q;
1371
1372   for (q = p; *q; q++)
1373     if (!ISPRINT(*q) || ISSPACE(*q)) goto quote;
1374   fputs(p, m->fp); return;
1375 quote:
1376   putc('"', m->fp); machine_writem(m, p, strlen(p)); putc('"', m->fp);
1377 }
1378
1379 /* --- @machine_bsession@ --- *
1380  *
1381  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1382  *                       @struct machine_output@
1383  *              @struct tvec_state *tv@ = the test state producing output
1384  *
1385  * Returns:     ---
1386  *
1387  * Use:         Begin a test session.
1388  *
1389  *              The TAP driver records the test state for later reference,
1390  *              initializes the group index counter, and prints the version
1391  *              number.
1392  */
1393
1394 static void machine_bsession(struct tvec_output *o, struct tvec_state *tv)
1395 {
1396   struct machine_output *m = (struct machine_output *)o;
1397
1398   m->tv = tv; m->grpix = 0;
1399 }
1400
1401 /* --- @machine_show_stats@ --- *
1402  *
1403  * Arguments:   @struct machine_output *m@ = output sink
1404  *              @unsigned *out[TVOUT_LIMIT]@ = outcome counter table
1405  *
1406  * Returns:     ---
1407  *
1408  * Use:         Print a machine readable outcome statistics table
1409  */
1410
1411 static void machine_show_stats(struct machine_output *m,
1412                                unsigned out[TVOUT_LIMIT])
1413 {
1414   static const char *outtab[] = { "lose", "skip", "xfail", "win" };
1415   unsigned i;
1416
1417   for (i = 0; i < TVOUT_LIMIT; i++) {
1418     if (i) putc(' ', m->fp);
1419     fprintf(m->fp, "%s=%u", outtab[i], out[i]);
1420   }
1421 }
1422
1423 /* --- @machine_esession@ --- *
1424  *
1425  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1426  *                       @struct machine_output@
1427  *
1428  * Returns:     Suggested exit code.
1429  *
1430  * Use:         End a test session.
1431  *
1432  *              The TAP driver prints a final summary of the rest results
1433  *              and returns a suitable exit code.  If errors occurred, it
1434  *              instead prints a `Bail out!' line forcing the reader to
1435  *              report a failure.
1436  */
1437
1438 static int machine_esession(struct tvec_output *o)
1439 {
1440   struct machine_output *m = (struct machine_output *)o;
1441   struct tvec_state *tv = m->tv;
1442
1443   fputs("END groups: ", m->fp);
1444   machine_show_stats(m, tv->grps);
1445   fputs("; tests: ", m->fp);
1446   machine_show_stats(m, tv->all);
1447   putc('\n', m->fp);
1448   m->tv = 0; return (tv->f&TVSF_ERROR ? 2 : tv->all[TVOUT_LOSE] ? 1 : 0);
1449 }
1450
1451 /* --- @machine_bgroup@ --- *
1452  *
1453  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1454  *                       @struct machine_output@
1455  *
1456  * Returns:     ---
1457  *
1458  * Use:         Begin a test group.
1459  *
1460  *              The TAP driver determines the length of the longest
1461  *              register name, resets the group progress scoreboard, and
1462  *              activates the progress display.
1463  */
1464
1465 static void machine_bgroup(struct tvec_output *o)
1466 {
1467   struct machine_output *m = (struct machine_output *)o;
1468   struct tvec_state *tv = m->tv;
1469
1470   fputs("BGROUP ", m->fp);
1471   machine_maybe_quote(m, tv->test->name);
1472   putc('\n', m->fp);
1473   m->grpix++; m->testix = 0;
1474 }
1475
1476 /* --- @machine_skipgroup@ --- *
1477  *
1478  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1479  *                       @struct machine_output@
1480  *              @const char *excuse@, @va_list *ap@ = reason for skipping the
1481  *                      group, or null
1482  *
1483  * Returns:     ---
1484  *
1485  * Use:         Report that a test group is being skipped.
1486  *
1487  *              The TAP driver just reports the situation to its output
1488  *              stream.
1489  */
1490
1491 static void machine_skipgroup(struct tvec_output *o,
1492                           const char *excuse, va_list *ap)
1493 {
1494   struct machine_output *m = (struct machine_output *)o;
1495   struct tvec_state *tv = m->tv;
1496
1497   fputs("SKIPGRP ", m->fp);
1498   machine_maybe_quote(m, tv->test->name);
1499   if (excuse) {
1500     fputs(" \"", m->fp);
1501     vgprintf(&machine_printops, m, excuse, ap);
1502     putc('\"', m->fp);
1503   }
1504   putc('\n', m->fp);
1505 }
1506
1507 /* --- @machine_egroup@ --- *
1508  *
1509  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1510  *                       @struct machine_output@
1511  *
1512  * Returns:     ---
1513  *
1514  * Use:         Report that a test group has finished.
1515  *
1516  *              The TAP driver reports a summary of the group's tests.
1517  */
1518
1519 static void machine_egroup(struct tvec_output *o)
1520 {
1521   struct machine_output *m = (struct machine_output *)o;
1522   struct tvec_state *tv = m->tv;
1523
1524   if (!(tv->f&TVSF_SKIP)) {
1525     fputs("EGROUP ", m->fp); machine_maybe_quote(m, tv->test->name);
1526     putc(' ', m->fp); machine_show_stats(m, tv->curr);
1527     putc('\n', m->fp);
1528   }
1529 }
1530
1531 /* --- @machine_btest@ --- *
1532  *
1533  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1534  *                       @struct machine_output@
1535  *
1536  * Returns:     ---
1537  *
1538  * Use:         Report that a test is starting.
1539  *
1540  *              The TAP driver advances its test counter.  (We could do this
1541  *              by adding up up the counters in @tv->curr@, and add on the
1542  *              current test, but it's easier this way.)
1543  */
1544
1545 static void machine_btest(struct tvec_output *o) { ; }
1546
1547 /* --- @machine_report_location@ --- *
1548  *
1549  * Arguments:   @struct human_output *h@ = output state
1550  *              @const char *file@ = filename
1551  *              @unsigned lno@ = line number
1552  *
1553  * Returns:     ---
1554  *
1555  * Use:         Print the filename and line number to the output stream.
1556  */
1557
1558 static void machine_report_location(struct machine_output *m,
1559                                     const char *file, unsigned lno)
1560 {
1561   if (file) {
1562     putc(' ', m->fp);
1563     machine_maybe_quote(m, file);
1564     fprintf(m->fp, ":%u", lno);
1565   }
1566 }
1567
1568 /* --- @machine_outcome@, @machine_skip@, @machine_fail@ --- *
1569  *
1570  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1571  *                       @struct machine_output@
1572  *              @const char *outcome@ = outcome string to report
1573  *              @const char *detail@, @va_list *ap@ = a detail message
1574  *              @const char *excuse@, @va_list *ap@ = reason for skipping the
1575  *                      test
1576  *
1577  * Returns:     ---
1578  *
1579  * Use:         Report that a test has been skipped or failed.
1580  */
1581
1582 static void machine_outcome(struct tvec_output *o, const char *outcome,
1583                             const char *detail, va_list *ap)
1584 {
1585   struct machine_output *m = (struct machine_output *)o;
1586   struct tvec_state *tv = m->tv;
1587
1588   fprintf(m->fp, "%s %u", outcome, m->testix);
1589   machine_report_location(m, tv->infile, tv->test_lno);
1590   if (detail)
1591     { putc(' ', m->fp); vgprintf(&machine_printops, m, detail, ap); }
1592   putc('\n', m->fp);
1593 }
1594
1595 static void machine_skip(struct tvec_output *o,
1596                          const char *excuse, va_list *ap)
1597   { machine_outcome(o, "SKIP", excuse, ap); }
1598 static void machine_fail(struct tvec_output *o,
1599                          const char *detail, va_list *ap)
1600   { machine_outcome(o, "FAIL", detail, ap); }
1601
1602 /* --- @machine_dumpreg@ --- *
1603  *
1604  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1605  *                       @struct machine_output@
1606  *              @unsigned disp@ = register disposition
1607  *              @const union tvec_regval *rv@ = register value
1608  *              @const struct tvec_regdef *rd@ = register definition
1609  *
1610  * Returns:     ---
1611  *
1612  * Use:         Dump a register.
1613  *
1614  *              The machine driver applies highlighting to mismatching output
1615  *              registers, but otherwise delegates to the register type
1616  *              handler.
1617  */
1618
1619 static void machine_dumpreg(struct tvec_output *o,
1620                         unsigned disp, const union tvec_regval *rv,
1621                         const struct tvec_regdef *rd)
1622 {
1623   struct machine_output *m = (struct machine_output *)o;
1624   unsigned f = 0;
1625 #define f_reg 1u
1626 #define f_nl 2u
1627
1628   switch (disp) {
1629     case TVRD_INPUT: fputs("\tINPUT ", m->fp); f |= f_reg | f_nl; break;
1630     case TVRD_OUTPUT: fputs("\tOUTPUT ", m->fp); f |= f_reg | f_nl; break;
1631     case TVRD_MATCH: fputs("\tMATCH ", m->fp); f |= f_reg | f_nl; break;
1632     case TVRD_FOUND: fputs("\tMISMATCH ", m->fp); f |= f_reg; break;
1633     case TVRD_EXPECT: fputs(" /= ", m->fp); f |= f_nl; break;
1634     default: abort();
1635   }
1636
1637   if (f&f_reg) fprintf(m->fp, "%s = ", rd->name);
1638   if (!rv) fputs("#unset", m->fp);
1639   else rd->ty->dump(rv, rd, TVSF_RAW, &file_printops, m->fp);
1640   if (f&f_nl) putc('\n', m->fp);
1641
1642 #undef f_reg
1643 #undef f_newline
1644 }
1645
1646 /* --- @machine_etest@ --- *
1647  *
1648  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1649  *                       @struct machine_output@
1650  *              @unsigned outcome@ = the test outcome
1651  *
1652  * Returns:     ---
1653  *
1654  * Use:         Report that a test has finished.
1655  *
1656  *              The machine driver reports the outcome of the test, if that's
1657  *              not already decided.
1658  */
1659
1660 static void machine_etest(struct tvec_output *o, unsigned outcome)
1661 {
1662   struct machine_output *m = (struct machine_output *)o;
1663
1664   if (!(m->f&MF_BENCH)) switch (outcome) {
1665     case TVOUT_WIN: machine_outcome(o, "WIN", 0, 0); break;
1666     case TVOUT_XFAIL: machine_outcome(o, "XFAIL", 0, 0); break;
1667   }
1668   m->testix++; m->f &= ~MF_BENCH;
1669 }
1670
1671 /* --- @machine_report@ --- *
1672  *
1673  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1674  *                       @struct machine_output@
1675  *              @unsigned level@ = message level (@TVLEV_...@)
1676  *              @const char *msg@, @va_list *ap@ = format string and
1677  *                      arguments
1678  *
1679  * Returns:     ---
1680  *
1681  * Use:         Report a message to the user.
1682  *
1683  *              Each report level has its own output tag
1684  */
1685
1686 static void machine_report(struct tvec_output *o, unsigned level,
1687                            const char *msg, va_list *ap)
1688 {
1689   struct machine_output *m = (struct machine_output *)o;
1690   struct tvec_state *tv = m->tv;
1691   const char *p;
1692
1693   for (p = tvec_strlevel(level); *p; p++) putc(TOUPPER(*p), m->fp);
1694   machine_report_location(m, tv->infile, tv->lno);
1695   fputs(" \"", m->fp); vgprintf(&machine_printops, m, msg, ap);
1696   putc('"', m->fp);
1697   putc('\n', m->fp);
1698 }
1699
1700 /* --- @machine_bbench@ --- *
1701  *
1702  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1703  *                       @struct machine_output@
1704  *              @const char *desc@ = adhoc test description
1705  *              @unsigned unit@ = measurement unit (@BTU_...@)
1706  *
1707  * Returns:     ---
1708  *
1709  * Use:         Report that a benchmark has started.
1710  *
1711  *              The machine driver does nothing here.  All of the reporting
1712  *              happens in @machine_ebench@.
1713  */
1714
1715 static void machine_bbench(struct tvec_output *o,
1716                            const char *desc, unsigned unit)
1717   { ; }
1718
1719 /* --- @machine_ebench@ --- *
1720  *
1721  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1722  *                       @struct machine_output@
1723  *              @const char *desc@ = adhoc test description
1724  *              @unsigned unit@ = measurement unit (@BTU_...@)
1725  *              @const struct bench_timing *t@ = measurement
1726  *
1727  * Returns:     ---
1728  *
1729  * Use:         Report a benchmark's results.
1730  *
1731  *              The machine driver prints the result as a `BENCH' output
1732  *              line, in raw format.
1733  */
1734
1735 static void machine_ebench(struct tvec_output *o,
1736                            const char *desc, unsigned unit,
1737                            const struct bench_timing *t)
1738 {
1739   struct machine_output *m = (struct machine_output *)o;
1740   struct tvec_state *tv = m->tv;
1741
1742   fprintf(m->fp, "BENCH %u", m->testix);
1743   machine_report_location(m, tv->infile, tv->test_lno);
1744   putc(' ', m->fp);
1745   if (desc) machine_maybe_quote(m, desc);
1746   else print_ident(tv, TVSF_RAW, &file_printops, m->fp);
1747   fputs(": ", m->fp);
1748   tvec_benchreport(&file_printops, m->fp, unit, TVSF_RAW, t);
1749   putc('\n', m->fp); m->f |= MF_BENCH;
1750 }
1751
1752 static const struct tvec_benchoutops machine_benchops =
1753   { machine_bbench, machine_ebench };
1754
1755 /* --- @machine_extend@ --- *
1756  *
1757  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1758  *                       @struct machine_output@
1759  *              @const char *name@ = extension name
1760  *
1761  * Returns:     A pointer to the extension implementation, or null.
1762  */
1763
1764 static const void *machine_extend(struct tvec_output *o, const char *name)
1765 {
1766   if (STRCMP(name, ==, TVEC_BENCHOUTEXT)) return (&machine_benchops);
1767   else return (0);
1768 }
1769
1770 /* --- @machine_destroy@ --- *
1771  *
1772  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1773  *                       @struct machine_output@
1774  *
1775  * Returns:     ---
1776  *
1777  * Use:         Release the resources held by the output driver.
1778  */
1779
1780 static void machine_destroy(struct tvec_output *o)
1781 {
1782   struct machine_output *m = (struct machine_output *)o;
1783
1784   if (m->fp != stdout && m->fp != stderr) fclose(m->fp);
1785   x_free(m->a, m->outbuf); x_free(m->a, m);
1786 }
1787
1788 static const struct tvec_outops machine_ops = {
1789   machine_bsession, machine_esession,
1790   machine_bgroup, machine_skipgroup, machine_egroup,
1791   machine_btest, machine_skip, machine_fail, machine_dumpreg, machine_etest,
1792   machine_report, machine_extend, machine_destroy
1793 };
1794
1795 /* --- @tvec_machineoutput@ --- *
1796  *
1797  * Arguments:   @FILE *fp@ = output file to write on
1798  *
1799  * Returns:     An output formatter.
1800  *
1801  * Use:         Return an output formatter which writes on @fp@ in a
1802  *              moderately simple machine-readable format.
1803  */
1804
1805 struct tvec_output *tvec_machineoutput(FILE *fp)
1806 {
1807   struct machine_output *m;
1808
1809   XNEW(m); m->a = arena_global; m->_o.ops = &machine_ops;
1810   m->f = 0; m->fp = fp; m->outbuf = 0; m->outsz = 0; m->testix = 0;
1811   return (&m->_o);
1812 }
1813
1814 /*----- Perl's `Test Anything Protocol' -----------------------------------*/
1815
1816 struct tap_output {
1817   struct tvec_output _o;                /* output base class */
1818   struct tvec_state *tv;                /* stashed testing state */
1819   arena *a;                             /* arena for memory allocation */
1820   struct layout lyt;                    /* output layout */
1821   char *outbuf; size_t outsz;           /* buffer for formatted output */
1822   unsigned grpix, testix;               /* group and test indices */
1823   unsigned previx;                      /* previously reported test index */
1824   int maxlen;                           /* longest register name */
1825 };
1826
1827 /* --- @tap_writech@, @tap_write@, @tap_writef@ --- *
1828  *
1829  * Arguments:   @void *go@ = output sink, secretly a @struct tap_output@
1830  *              @int ch@ = character to write
1831  *              @const char *@p@, @size_t sz@ = string (with explicit length)
1832  *                      to write
1833  *              @const char *p, ...@ = format control string and arguments to
1834  *                      write
1835  *
1836  * Returns:     ---
1837  *
1838  * Use:         Write characters, strings, or formatted strings to the
1839  *              output, applying appropriate layout.
1840  *
1841  *              For the TAP output driver, the layout machinery prefixes each
1842  *              line with `    ## ' and strips trailing spaces.
1843  */
1844
1845 static int tap_writech(void *go, int ch)
1846 {
1847   struct tap_output *t = go;
1848
1849   if (layout_char(&t->lyt, ch)) return (-1);
1850   else return (1);
1851 }
1852
1853 static int tap_writem(void *go, const char *p, size_t sz)
1854 {
1855   struct tap_output *t = go;
1856
1857   if (layout_string(&t->lyt, p, sz)) return (-1);
1858   else return (sz);
1859 }
1860
1861 static int tap_nwritef(void *go, size_t maxsz, const char *p, ...)
1862 {
1863   struct tap_output *t = go;
1864   size_t n;
1865   va_list ap;
1866
1867   va_start(ap, p);
1868   n = gprintf_memputf(t->a, &t->outbuf, &t->outsz, maxsz, p, ap);
1869   va_end(ap);
1870   if (layout_string(&t->lyt, t->outbuf, n)) return (-1);
1871   return (n);
1872 }
1873
1874 static const struct gprintf_ops tap_printops =
1875   { tap_writech, tap_writem, tap_nwritef };
1876
1877 /* --- @tap_bsession@ --- *
1878  *
1879  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1880  *                       @struct tap_output@
1881  *              @struct tvec_state *tv@ = the test state producing output
1882  *
1883  * Returns:     ---
1884  *
1885  * Use:         Begin a test session.
1886  *
1887  *              The TAP driver records the test state for later reference,
1888  *              initializes the group index counter, and prints the version
1889  *              number.
1890  */
1891
1892 static void tap_bsession(struct tvec_output *o, struct tvec_state *tv)
1893 {
1894   struct tap_output *t = (struct tap_output *)o;
1895
1896   t->tv = tv; t->grpix = 0;
1897   fputs("TAP version 13\n", t->lyt.fp); /* but secretly 14 really */
1898 }
1899
1900 /* --- @tap_esession@ --- *
1901  *
1902  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1903  *                       @struct tap_output@
1904  *
1905  * Returns:     Suggested exit code.
1906  *
1907  * Use:         End a test session.
1908  *
1909  *              The TAP driver prints a final summary of the rest results
1910  *              and returns a suitable exit code.  If errors occurred, it
1911  *              instead prints a `Bail out!' line forcing the reader to
1912  *              report a failure.
1913  */
1914
1915 static int tap_esession(struct tvec_output *o)
1916 {
1917   struct tap_output *t = (struct tap_output *)o;
1918   struct tvec_state *tv = t->tv;
1919
1920   if (tv->f&TVSF_ERROR) {
1921     fputs("Bail out!  "
1922           "Errors found in input; tests may not have run correctly\n",
1923           t->lyt.fp);
1924     return (2);
1925   }
1926
1927   fprintf(t->lyt.fp, "1..%u\n", t->grpix);
1928   t->tv = 0; return (tv->all[TVOUT_LOSE] ? 1 : 0);
1929 }
1930
1931 /* --- @tap_bgroup@ --- *
1932  *
1933  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1934  *                       @struct tap_output@
1935  *
1936  * Returns:     ---
1937  *
1938  * Use:         Begin a test group.
1939  *
1940  *              The TAP driver determines the length of the longest
1941  *              register name, resets the group progress scoreboard, and
1942  *              activates the progress display.
1943  */
1944
1945 static void tap_bgroup(struct tvec_output *o)
1946 {
1947   struct tap_output *t = (struct tap_output *)o;
1948   struct tvec_state *tv = t->tv;
1949
1950   t->grpix++; t->testix = t->previx = 0;
1951   t->maxlen = register_maxnamelen(t->tv);
1952   fprintf(t->lyt.fp, "# Subtest: %s\n", tv->test->name);
1953 }
1954
1955 /* --- @tap_skipgroup@ --- *
1956  *
1957  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1958  *                       @struct tap_output@
1959  *              @const char *excuse@, @va_list *ap@ = reason for skipping the
1960  *                      group, or null
1961  *
1962  * Returns:     ---
1963  *
1964  * Use:         Report that a test group is being skipped.
1965  *
1966  *              The TAP driver just reports the situation to its output
1967  *              stream.
1968  */
1969
1970 static void tap_skipgroup(struct tvec_output *o,
1971                           const char *excuse, va_list *ap)
1972 {
1973   struct tap_output *t = (struct tap_output *)o;
1974
1975   fprintf(t->lyt.fp, "    1..%u\n", t->testix);
1976   fprintf(t->lyt.fp, "ok %u %s # SKIP", t->grpix, t->tv->test->name);
1977   if (excuse) { fputc(' ', t->lyt.fp); vfprintf(t->lyt.fp, excuse, *ap); }
1978   fputc('\n', t->lyt.fp);
1979 }
1980
1981 /* --- @tap_egroup@ --- *
1982  *
1983  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
1984  *                       @struct tap_output@
1985  *
1986  * Returns:     ---
1987  *
1988  * Use:         Report that a test group has finished.
1989  *
1990  *              The TAP driver reports a summary of the group's tests.
1991  */
1992
1993 static void tap_egroup(struct tvec_output *o)
1994 {
1995   struct tap_output *t = (struct tap_output *)o;
1996   struct tvec_state *tv = t->tv;
1997
1998   fprintf(t->lyt.fp, "    1..%u\n", t->testix);
1999   fprintf(t->lyt.fp, "%s %u - %s\n",
2000           tv->curr[TVOUT_LOSE] ? "not ok" : "ok",
2001           t->grpix, tv->test->name);
2002 }
2003
2004 /* --- @tap_btest@ --- *
2005  *
2006  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2007  *                       @struct tap_output@
2008  *
2009  * Returns:     ---
2010  *
2011  * Use:         Report that a test is starting.
2012  *
2013  *              The TAP driver advances its test counter.  (We could do this
2014  *              by adding up up the counters in @tv->curr@, and add on the
2015  *              current test, but it's easier this way.)
2016  */
2017
2018 static void tap_btest(struct tvec_output *o)
2019   { struct tap_output *t = (struct tap_output *)o; t->testix++; }
2020
2021 /* --- @tap_outcome@, @tap_skip@, @tap_fail@ --- *
2022  *
2023  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2024  *                       @struct tap_output@
2025  *              @const char *head, *tail@ = outcome strings to report
2026  *              @const char *detail@, @va_list *ap@ = a detail message
2027  *              @const char *excuse@, @va_list *ap@ = reason for skipping the
2028  *                      test
2029  *
2030  * Returns:     ---
2031  *
2032  * Use:         Report that a test has been skipped or failed.
2033  *
2034  *              The TAP driver reports the situation on its output stream.
2035  *              TAP only allows us to report a single status for each
2036  *              subtest, so we notice when we've already reported a status
2037  *              for the current test and convert the second report as a
2038  *              comment.  This should only happen in the case of multiple
2039  *              failures.
2040  */
2041
2042 static void tap_outcome(struct tvec_output *o,
2043                         const char *head, const char *tail,
2044                         const char *detail, va_list *ap)
2045 {
2046   struct tap_output *t = (struct tap_output *)o;
2047   struct tvec_state *tv = t->tv;
2048
2049   fprintf(t->lyt.fp, "    %s %u - %s:%u%s",
2050           t->testix == t->previx ? "##" : head,
2051           t->testix, tv->infile, tv->test_lno, tail);
2052   if (detail)
2053     { fputc(' ', t->lyt.fp); vfprintf(t->lyt.fp, detail, *ap); }
2054   fputc('\n', t->lyt.fp);
2055   t->previx = t->testix;
2056 }
2057
2058 static void tap_skip(struct tvec_output *o, const char *excuse, va_list *ap)
2059   { tap_outcome(o, "ok", " # SKIP", excuse, ap); }
2060 static void tap_fail(struct tvec_output *o, const char *detail, va_list *ap)
2061   { tap_outcome(o, "not ok", "", detail, ap); }
2062
2063 /* --- @tap_dumpreg@ --- *
2064  *
2065  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2066  *                       @struct tap_output@
2067  *              @unsigned disp@ = register disposition
2068  *              @const union tvec_regval *rv@ = register value
2069  *              @const struct tvec_regdef *rd@ = register definition
2070  *
2071  * Returns:     ---
2072  *
2073  * Use:         Dump a register.
2074  *
2075  *              The TAP driver applies highlighting to mismatching output
2076  *              registers, but otherwise delegates to the register type
2077  *              handler and the layout machinery.  The result is that the
2078  *              register dump is marked as a comment and indented.
2079  */
2080
2081 static void tap_dumpreg(struct tvec_output *o,
2082                         unsigned disp, const union tvec_regval *rv,
2083                         const struct tvec_regdef *rd)
2084 {
2085   struct tap_output *t = (struct tap_output *)o;
2086   const char *ds = regdisp(disp); int n = strlen(ds) + strlen(rd->name);
2087
2088   set_layout_prefix(&t->lyt, "    ## ");
2089   gprintf(&tap_printops, t, "%*s%s %s = ",
2090           10 + t->maxlen - n, "", ds, rd->name);
2091   if (!rv) gprintf(&tap_printops, t, "#<unset>");
2092   else rd->ty->dump(rv, rd, 0, &tap_printops, t);
2093   layout_char(&t->lyt, '\n');
2094 }
2095
2096 /* --- @tap_etest@ --- *
2097  *
2098  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2099  *                       @struct tap_output@
2100  *              @unsigned outcome@ = the test outcome
2101  *
2102  * Returns:     ---
2103  *
2104  * Use:         Report that a test has finished.
2105  *
2106  *              The TAP driver reports the outcome of the test, if that's not
2107  *              already decided.
2108  */
2109
2110 static void tap_etest(struct tvec_output *o, unsigned outcome)
2111 {
2112   switch (outcome) {
2113     case TVOUT_WIN:
2114       tap_outcome(o, "ok", "", 0, 0);
2115       break;
2116     case TVOUT_XFAIL:
2117       tap_outcome(o, "not ok", " # TODO expected failure", 0, 0);
2118       break;
2119   }
2120 }
2121
2122 /* --- @tap_report@ --- *
2123  *
2124  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2125  *                       @struct tap_output@
2126  *              @unsigned level@ = message level (@TVLEV_...@)
2127  *              @const char *msg@, @va_list *ap@ = format string and
2128  *                      arguments
2129  *
2130  * Returns:     ---
2131  *
2132  * Use:         Report a message to the user.
2133  *
2134  *              Messages are reported as comments, so that they can be
2135  *              accumulated by the reader.  An error will cause a later
2136  *              bailout or, if we crash before then, a missing plan line,
2137  *              either of which will cause the reader to report a serious
2138  *              problem.
2139  */
2140
2141 static void tap_report(struct tvec_output *o, unsigned level,
2142                        const char *msg, va_list *ap)
2143 {
2144   struct tap_output *t = (struct tap_output *)o;
2145   struct tvec_state *tv = t->tv;
2146
2147   if (tv->test) set_layout_prefix(&t->lyt, "    ## ");
2148   else set_layout_prefix(&t->lyt, "## ");
2149
2150   if (tv->infile) gprintf(&tap_printops, t, "%s:%u: ", tv->infile, tv->lno);
2151   gprintf(&tap_printops, t, "%s: ", tvec_strlevel(level));
2152   vgprintf(&tap_printops, t, msg, ap);
2153   layout_char(&t->lyt, '\n');
2154 }
2155
2156 /* --- @tap_extend@ --- *
2157  *
2158  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2159  *                       @struct tap_output@
2160  *              @const char *name@ = extension name
2161  *
2162  * Returns:     A pointer to the extension implementation, or null.
2163  */
2164
2165 static const void *tap_extend(struct tvec_output *o, const char *name)
2166   { return (0); }
2167
2168 /* --- @tap_destroy@ --- *
2169  *
2170  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2171  *                       @struct tap_output@
2172  *
2173  * Returns:     ---
2174  *
2175  * Use:         Release the resources held by the output driver.
2176  */
2177
2178 static void tap_destroy(struct tvec_output *o)
2179 {
2180   struct tap_output *t = (struct tap_output *)o;
2181
2182   destroy_layout(&t->lyt,
2183                  t->lyt.fp == stdout || t->lyt.fp == stderr ? 0 : DLF_CLOSE);
2184   x_free(t->a, t->outbuf); x_free(t->a, t);
2185 }
2186
2187 static const struct tvec_outops tap_ops = {
2188   tap_bsession, tap_esession,
2189   tap_bgroup, tap_skipgroup, tap_egroup,
2190   tap_btest, tap_skip, tap_fail, tap_dumpreg, tap_etest,
2191   tap_report, tap_extend, tap_destroy
2192 };
2193
2194 /* --- @tvec_tapoutput@ --- *
2195  *
2196  * Arguments:   @FILE *fp@ = output file to write on
2197  *
2198  * Returns:     An output formatter.
2199  *
2200  * Use:         Return an output formatter which writes on @fp@ in `TAP'
2201  *              (`Test Anything Protocol') format.
2202  *
2203  *              TAP comes from the Perl community, but has spread rather
2204  *              further.  This driver produces TAP version 14, but pretends
2205  *              to be version 13.  The driver produces a TAP `test point' --
2206  *              i.e., a result reported as `ok' or `not ok' -- for each input
2207  *              test group.  Failure reports and register dumps are produced
2208  *              as diagnostic messages before the final group result.  (TAP
2209  *              permits structuerd YAML data after the test-point result,
2210  *              which could be used to report details, but (a) postponing the
2211  *              details until after the report is inconvenient, and (b) there
2212  *              is no standardization for the YAML anyway, so in practice
2213  *              it's no more useful than the unstructured diagnostics.
2214  */
2215
2216 struct tvec_output *tvec_tapoutput(FILE *fp)
2217 {
2218   struct tap_output *t;
2219
2220   XNEW(t); t->a = arena_global; t->_o.ops = &tap_ops;
2221   init_layout(&t->lyt, fp, 0);
2222   t->outbuf = 0; t->outsz = 0;
2223   return (&t->_o);
2224 }
2225
2226 /*----- Automake support --------------------------------------------------*/
2227
2228 struct automake_output {
2229   struct tvec_output _o;
2230   arena *a;                             /* arena */
2231   struct tvec_state *tv;                /* test-vector state */
2232   struct tvec_output *progress;         /* real-time progress output */
2233   struct tvec_output *log;              /* log file output */
2234   FILE *trs;                            /* test result stream */
2235 };
2236
2237 /* --- @am_bsession@ --- *
2238  *
2239  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2240  *                       @struct automake_output@
2241  *              @struct tvec_state *tv@ = the test state producing output
2242  *
2243  * Returns:     ---
2244  *
2245  * Use:         Begin a test session.
2246  *
2247  *              The Automake driver passes the event on to its subordinates.
2248  */
2249
2250 static void am_bsession(struct tvec_output *o, struct tvec_state *tv)
2251 {
2252   struct automake_output *am = (struct automake_output *)o;
2253
2254   am->tv = tv;
2255   human_bsession(am->progress, tv);
2256   machine_bsession(am->log, tv);
2257 }
2258
2259 /* --- @am_esession@ --- *
2260  *
2261  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2262  *                       @struct automake_output@
2263  *
2264  * Returns:     Suggested exit code.
2265  *
2266  * Use:         End a test session.
2267  *
2268  *              The Automake driver completes the test-results file and
2269  *              passes  the event on to its subordinates.
2270  */
2271
2272 static void am_report_unusual(struct automake_output *am,
2273                               unsigned xfail, unsigned skip)
2274 {
2275   unsigned f = 0;
2276 #define f_any 1u
2277
2278   if (xfail) {
2279     fprintf(am->trs, "%s%u expected %s", f&f_any ? ", " : " (",
2280             xfail, xfail == 1 ? "failure" : "failures");
2281     f |= f_any;
2282   }
2283   if (skip) {
2284     fprintf(am->trs, "%s%u skipped", f&f_any ? ", " : " (", skip);
2285     f |= f_any;
2286   }
2287   if (f&f_any) fputc(')', am->trs);
2288
2289 #undef f_any
2290 }
2291
2292 static int am_esession(struct tvec_output *o)
2293 {
2294   struct automake_output *am = (struct automake_output *)o;
2295   struct tvec_state *tv = am->tv;
2296   unsigned
2297     all_win = tv->all[TVOUT_WIN], grps_win = tv->grps[TVOUT_WIN],
2298     all_xfail = tv->all[TVOUT_XFAIL],
2299     all_lose = tv->all[TVOUT_LOSE], grps_lose = tv->grps[TVOUT_LOSE],
2300     all_skip = tv->all[TVOUT_SKIP], grps_skip = tv->grps[TVOUT_SKIP],
2301     all_pass = all_win + all_xfail, all_run = all_pass + all_lose,
2302     grps_run = grps_win + grps_lose;
2303
2304   human_esession(am->progress);
2305   machine_esession(am->log);
2306
2307   fputs(":test-global-result: ", am->trs);
2308   if (tv->f&TVSF_ERROR) fputs("ERRORS; ", am->trs);
2309   if (!all_lose) {
2310     fprintf(am->trs, "PASSED %s%u %s",
2311             !(all_skip || grps_skip) ? "all " : "",
2312             all_win, all_win == 1 ? "test" : "tests");
2313     am_report_unusual(am, all_xfail, all_skip);
2314     fprintf(am->trs, " in %u %s",
2315             grps_win, grps_win == 1 ? "group" : "groups");
2316     am_report_unusual(am, 0, grps_skip);
2317   } else {
2318     fprintf(am->trs, "FAILED %u out of %u %s",
2319             all_lose, all_run, all_run == 1 ? "test" : "tests");
2320     am_report_unusual(am, all_xfail, all_skip);
2321     fprintf(am->trs, " in %u out of %u %s",
2322             grps_lose, grps_run, grps_run == 1 ? "group" : "groups");
2323     am_report_unusual(am, 0, grps_skip);
2324   }
2325   fputc('\n', am->trs);
2326
2327   fprintf(am->trs, ":copy-in-global-log: %s\n",
2328           !all_lose && !(tv->f&TVSF_ERROR) ? "no" : "yes");
2329   fprintf(am->trs, ":recheck: %s\n",
2330           !all_lose && !(tv->f&TVSF_ERROR) ? "no" : "yes");
2331
2332   return (0);
2333 }
2334
2335 /* --- @am_bgroup@ --- *
2336  *
2337  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2338  *                       @struct automake_output@
2339  *
2340  * Returns:     ---
2341  *
2342  * Use:         Begin a test group.
2343  *
2344  *              The Automake driver passes the event on to its subordinates.
2345  */
2346
2347 static void am_bgroup(struct tvec_output *o)
2348 {
2349   struct automake_output *am = (struct automake_output *)o;
2350
2351   human_bgroup(am->progress);
2352   machine_bgroup(am->log);
2353 }
2354
2355 /* --- @am_skipgroup@ --- *
2356  *
2357  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2358  *                       @struct automake_output@
2359  *              @const char *excuse@, @va_list *ap@ = reason for skipping the
2360  *                      group, or null
2361  *
2362  * Returns:     ---
2363  *
2364  * Use:         Report that a test group is being skipped.
2365  *
2366  *              The Automake driver makes a note in the test-results file and
2367  *              passes the event on to its subordinates.
2368  */
2369
2370 static void am_skipgroup(struct tvec_output *o,
2371                          const char *excuse, va_list *ap)
2372 {
2373   struct automake_output *am = (struct automake_output *)o;
2374   struct tvec_state *tv = am->tv;
2375
2376   fprintf(am->trs, ":test-result: SKIP %s\n", tv->test->name);
2377   human_skipgroup(am->progress, excuse, ap);
2378   machine_skipgroup(am->log, excuse, ap);
2379 }
2380
2381 /* --- @am_egroup@ --- *
2382  *
2383  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2384  *                       @struct automake_output@
2385  *
2386  * Returns:     ---
2387  *
2388  * Use:         Report that a test group has finished.
2389  *
2390  *              The Automake driver makes a note in the test-results file and
2391  *              passes the event on to its subordinates.
2392  */
2393
2394 static void am_egroup(struct tvec_output *o)
2395 {
2396   struct automake_output *am = (struct automake_output *)o;
2397   struct tvec_state *tv = am->tv;
2398
2399   fprintf(am->trs, ":test-result: %s %s\n",
2400           tv->curr[TVOUT_LOSE] ? "FAIL" : "PASS", tv->test->name);
2401   human_egroup(am->progress);
2402   machine_egroup(am->log);
2403 }
2404
2405 /* --- @am_btest@ --- *
2406  *
2407  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2408  *                       @struct automake_output@
2409  *
2410  * Returns:     ---
2411  *
2412  * Use:         Report that a test is starting.
2413  *
2414  *              The Automake driver passes the event on to its subordinates.
2415  */
2416
2417 static void am_btest(struct tvec_output *o)
2418 {
2419   struct automake_output *am = (struct automake_output *)o;
2420
2421   human_btest(am->progress);
2422   machine_btest(am->log);
2423 }
2424
2425 /* --- @am_skip@, @am_fail@ --- *
2426  *
2427  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2428  *                       @struct automake_output@
2429  *              @const char *head, *tail@ = outcome strings to report
2430  *              @const char *detail@, @va_list *ap@ = a detail message
2431  *              @const char *excuse@, @va_list *ap@ = reason for skipping the
2432  *                      test
2433  *
2434  * Returns:     ---
2435  *
2436  * Use:         Report that a test has been skipped or failed.
2437  *
2438  *              The Automake driver passes the event on to its subordinates.
2439  */
2440
2441 static void am_skip(struct tvec_output *o, const char *excuse, va_list *ap)
2442 {
2443   struct automake_output *am = (struct automake_output *)o;
2444
2445   human_skip(am->progress, excuse, ap);
2446   machine_skip(am->log, excuse, ap);
2447 }
2448
2449 static void am_fail(struct tvec_output *o, const char *detail, va_list *ap)
2450 {
2451   struct automake_output *am = (struct automake_output *)o;
2452
2453   human_fail(am->progress, detail, ap);
2454   machine_fail(am->log, detail, ap);
2455 }
2456
2457 /* --- @am_dumpreg@ --- *
2458  *
2459  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2460  *                       @struct automake_output@
2461  *              @unsigned disp@ = register disposition
2462  *              @const union tvec_regval *rv@ = register value
2463  *              @const struct tvec_regdef *rd@ = register definition
2464  *
2465  * Returns:     ---
2466  *
2467  * Use:         Dump a register.
2468  *
2469  *              The Automake driver passes the event on to its subordinates.
2470  */
2471
2472 static void am_dumpreg(struct tvec_output *o,
2473                        unsigned disp, const union tvec_regval *rv,
2474                        const struct tvec_regdef *rd)
2475 {
2476   struct automake_output *am = (struct automake_output *)o;
2477
2478   human_dumpreg(am->progress, disp, rv, rd);
2479   machine_dumpreg(am->log, disp, rv, rd);
2480 }
2481
2482 /* --- @am_etest@ --- *
2483  *
2484  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2485  *                       @struct automake_output@
2486  *              @unsigned outcome@ = the test outcome
2487  *
2488  * Returns:     ---
2489  *
2490  * Use:         Report that a test has finished.
2491  *
2492  *              The Automake driver passes the event on to its subordinates.
2493  */
2494
2495 static void am_etest(struct tvec_output *o, unsigned outcome)
2496 {
2497   struct automake_output *am = (struct automake_output *)o;
2498
2499   human_etest(am->progress, outcome);
2500   machine_etest(am->log, outcome);
2501 }
2502
2503 /* --- @am_report@ --- *
2504  *
2505  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2506  *                       @struct automake_output@
2507  *              @unsigned level@ = message level (@TVLEV_...@)
2508  *              @const char *msg@, @va_list *ap@ = format string and
2509  *                      arguments
2510  *
2511  * Returns:     ---
2512  *
2513  * Use:         Report a message to the user.
2514  *
2515  *              The Automake driver passes the event on to its subordinates.
2516  */
2517
2518 static void am_report(struct tvec_output *o, unsigned level,
2519                       const char *msg, va_list *ap)
2520 {
2521   struct automake_output *am = (struct automake_output *)o;
2522
2523   human_report(am->progress, level, msg, ap);
2524   machine_report(am->log, level, msg, ap);
2525 }
2526
2527 /* --- @am_bbench@ --- *
2528  *
2529  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2530  *                       @struct automake_output@
2531  *              @const char *desc@ = adhoc test description
2532  *              @unsigned unit@ = measurement unit (@BTU_...@)
2533  *
2534  * Returns:     ---
2535  *
2536  * Use:         Report that a benchmark has started.
2537  *
2538  *              The Automake driver passes the event on to its subordinates.
2539  */
2540
2541 static void am_bbench(struct tvec_output *o,
2542                       const char *desc, unsigned unit)
2543 {
2544   struct automake_output *am = (struct automake_output *)o;
2545
2546   human_bbench(am->progress, desc, unit);
2547   machine_bbench(am->progress, desc, unit);
2548 }
2549
2550 /* --- @am_ebench@ --- *
2551  *
2552  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2553  *                       @struct automake_output@
2554  *              @const char *desc@ = adhoc test description
2555  *              @unsigned unit@ = measurement unit (@BTU_...@)
2556  *              @const struct bench_timing *t@ = measurement
2557  *
2558  * Returns:     ---
2559  *
2560  * Use:         Report a benchmark's results.
2561  *
2562  *              The Automake driver passes the event on to its subordinates.
2563  */
2564
2565 static void am_ebench(struct tvec_output *o,
2566                          const char *desc, unsigned unit,
2567                          const struct bench_timing *t)
2568 {
2569   struct automake_output *am = (struct automake_output *)o;
2570
2571   human_ebench(am->progress, desc, unit, t);
2572   machine_ebench(am->progress, desc, unit, t);
2573 }
2574
2575 static const struct tvec_benchoutops am_benchops =
2576   { am_bbench, am_ebench };
2577
2578 /* --- @am_extend@ --- *
2579  *
2580  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2581  *                       @struct automake_output@
2582  *              @const char *name@ = extension name
2583  *
2584  * Returns:     A pointer to the extension implementation, or null.
2585  */
2586
2587 static const void *am_extend(struct tvec_output *o, const char *name)
2588 {
2589   if (STRCMP(name, ==, TVEC_BENCHOUTEXT)) return (&am_benchops);
2590   else return (0);
2591 }
2592
2593 /* --- @am_destroy@ --- *
2594  *
2595  * Arguments:   @struct tvec_output *o@ = output sink, secretly a
2596  *                       @struct automake_output@
2597  *
2598  * Returns:     ---
2599  *
2600  * Use:         Release the resources held by the output driver.
2601  */
2602
2603 static void am_destroy(struct tvec_output *o)
2604 {
2605   struct automake_output *am = (struct automake_output *)o;
2606
2607   human_destroy(am->progress);
2608   machine_destroy(am->log);
2609   fclose(am->trs); x_free(am->a, am);
2610 }
2611
2612 static const struct tvec_outops automake_ops = {
2613   am_bsession, am_esession,
2614   am_bgroup, am_skipgroup, am_egroup,
2615   am_btest, am_skip, am_fail, am_dumpreg, am_etest,
2616   am_report, am_extend, am_destroy
2617 };
2618
2619 /* --- @tvec_amoutput@ --- *
2620  *
2621  * Arguments:   @const struct tvec_amargs *a@ = arguments from Automake
2622  *                      command-line protocol
2623  *
2624  * Returns:     An output formatter.
2625  *
2626  * Use:         Returns an output formatter which writes on standard output
2627  *              in human format, pretending that the output is to a terminal
2628  *              (in order to cope with %%\manpage{make}{1}%%'s output-
2629  *              buffering behaviour, writes to the log file @a->log@ in
2630  *              machine-readable format, and writes an Automake rST-format
2631  *              test result file to @a->trs@.  The `test name' is currently
2632  *              ignored, because the framework has its own means of
2633  *              determining test names.
2634  */
2635
2636 struct tvec_output *tvec_amoutput(const struct tvec_amargs *a)
2637 {
2638   struct automake_output *am;
2639   unsigned f;
2640
2641   f = TVHF_TTY;
2642   if (a->f&TVAF_COLOUR) f |= TVHF_COLOUR;
2643
2644   XNEW(am); am->a = arena_global; am->_o.ops = &automake_ops;
2645   am->progress = tvec_humanoutput(stdout, f, TVHF_TTY | TVHF_COLOUR);
2646   am->log = tvec_machineoutput(a->log); am->trs = a->trs;
2647   return (&am->_o);
2648 }
2649
2650 /*----- Default output ----------------------------------------------------*/
2651
2652 /* --- @tvec_dfltoutput@ --- *
2653  *
2654  * Arguments:   @FILE *fp@ = output file to write on
2655  *
2656  * Returns:     An output formatter.
2657  *
2658  * Use:         Selects and instantiates an output formatter suitable for
2659  *              writing on @fp@.  The policy is subject to change, but
2660  *              currently the `human' output format is selected if @fp@ is
2661  *              interactive (i.e., if @isatty(fileno(fp))@ is true), and
2662  *              otherwise the `machine' format is used.
2663  */
2664
2665 struct tvec_output *tvec_dfltoutput(FILE *fp)
2666 {
2667   int ttyp = getenv_boolean("TVEC_TTY", -1);
2668
2669   if (ttyp == -1) ttyp = isatty(fileno(fp));
2670   if (ttyp) return (tvec_humanoutput(fp, 0, 0));
2671   else return (tvec_machineoutput(fp));
2672 }
2673
2674 /*----- That's all, folks -------------------------------------------------*/