chiark / gitweb /
@@@ tty commentary
[mLib] / ui / tty.c
1 /* -*-c-*-
2  *
3  * Terminal handling
4  *
5  * (c) 2024 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 <ctype.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <unistd.h>
41 #include <termios.h>
42 #include <sys/ioctl.h>
43 #include <sys/select.h>
44
45 #ifdef HAVE_TERMCAP
46 #  include <termcap.h>
47 #endif
48
49 #ifdef HAVE_TERMINFO
50 #  include <curses.h>
51 #  include <term.h>
52 #  undef erase
53 #  undef inch
54 #  undef move
55 #endif
56
57 #ifdef HAVE_UNIBILIUM
58 #  include <unibilium.h>
59 #endif
60
61 #include "alloc.h"
62 #include "gprintf.h"
63 #include "macros.h"
64 #include "str.h"
65 #include "tty.h"
66
67 /*----- Miscellaneous preliminaries ---------------------------------------*/
68
69 /* Buffer size parameters. */
70 #define BUFSZ 4096                      /* size of a buffer */
71 #define THRESH (BUFSZ/2)                /* threshold for buffering */
72
73 /* Incorporate the published control-block structure into our more elaborate
74  * object model.
75  */
76 #define TTY_BASEPFX struct tty tty
77 #define TTY_BASEUSFX struct tty tty
78
79 struct tty_ops {
80   void (*release)(struct tty */*tty*/);
81         /* Free any resources held by the backend. */
82
83   /* The following operations handle the correspondingly named interface
84    * functions.
85    */
86   int (*setattr)(struct tty */*tty*/,
87                  const struct gprintf_ops */*gops*/, void */*go*/,
88                  const struct tty_attr */*a*/);
89   int (*setmodes)(struct tty */*tty*/,
90                   const struct gprintf_ops */*gops*/, void */*go*/,
91                   uint32 /*modes_bic*/, uint32 /*modes_xor*/);
92   int (*move)(struct tty */*tty*/,
93               const struct gprintf_ops */*gops*/, void */*go*/,
94               unsigned /*orig*/, int /*y*/, int /*x*/);
95   int (*repeat)(struct tty */*tty*/,
96                 const struct gprintf_ops */*gops*/, void */*go*/,
97                 int /*ch*/, unsigned /*n*/);
98   int (*erase)(struct tty */*tty*/,
99                const struct gprintf_ops */*gops*/, void */*go*/,
100                unsigned /*f*/);
101   int (*erch)(struct tty */*tty*/,
102               const struct gprintf_ops */*gops*/, void */*go*/,
103               unsigned /*n*/);
104   int (*ins)(struct tty */*tty*/,
105              const struct gprintf_ops */*gops*/, void */*go*/,
106              unsigned /*f*/, unsigned /*n*/);
107   int (*inch)(struct tty */*tty*/,
108               const struct gprintf_ops */*gops*/, void */*go*/,
109               int /*ch*/);
110   int (*del)(struct tty */*tty*/,
111              const struct gprintf_ops */*gops*/, void */*go*/,
112              unsigned /*f*/, unsigned /*n*/);
113 };
114 #define TTY_BASEOPSPFX struct tty_ops tty
115 #define TTY_BASEOPSUXFX struct tty_ops tty
116
117 /*----- Common support machinery ------------------------------------------*/
118
119 /* --- @CHECK@ --- *
120  *
121  * Arguments@   @expr@ = expression to evaluate
122  *
123  * Use:         Evaluate @expr@.  If the result is (strictly) negative, then
124  *              set @rc = -1@ and transfer control to the label @end@.
125  */
126
127 #define CHECK(expr) do { if ((expr) < 0) { rc = -1; goto end; } } while (0)
128
129 /* --- @debug@ --- *
130  *
131  * Arguments:   @const char *fmt@ = format control string
132  *              @...@ = format arguemnts
133  *
134  * Returns:     ---
135  *
136  * Use:         Maybe report a debugging message to standard error.
137  */
138
139 static PRINTF_LIKE(1, 2) void debug(const char *fmt, ...)
140 {
141   const char *p;
142   va_list ap;
143
144   p = getenv("MLIB_TTY_DEBUG");
145   if (p && *p != 'n' && *p != '0') {
146     va_start(ap, fmt);
147     fputs("mLib TTY: ", stderr);
148     vfprintf(stderr, fmt, ap);
149     fputc('\n', stderr);
150     va_end(ap);
151   }
152 }
153
154 /* --- @common_init@ --- *
155  *
156  * Arguments:   @struct tty *tty@ = pointer to terminal control block
157  *              @FILE *fp@ = output file stream
158  *              @speed_t *ospeed_out@ = where to put encoded output rate, or
159  *                      null
160  *
161  * Returns:     ---
162  *
163  * Use:         Perform general initialization on the terminal control
164  *              block.
165  *
166  *              Specifically, this fills in the @fpout@, @baud@, @ht@, and
167  *              @wd@ slots.  The width and height come from the kernel, or,
168  *              failing that, the environment.
169  *
170  *              For `termcap''s benefit, store the system-encoded output baud
171  *              rate in @*ospeed_out@, if it can be found.
172  */
173
174 static void common_init(struct tty *tty, FILE *fp, speed_t *ospeed_out)
175 {
176   static const struct baudtab { speed_t code; unsigned baud; } baudtab[] = {
177   /*
178      ;;; The baud-rate table is very boring to type.  To make life less
179      ;;; awful, put the rates in this list and evaluate the code to get Emacs
180      ;;; to regenerate it.
181
182      (let ((bauds '(50 75 110 134 150 200 300 600 1200 1800 2400 4800 9600
183                     19200 38400 57600 115200 230400 460800 500000 576000
184                     921600 1000000 1152000 1500000 2000000 2500000 3000000
185                     3500000 4000000)))
186        (save-excursion
187          (goto-char (point-min))
188          (search-forward (concat "***" "BEGIN baudlist" "***"))
189          (beginning-of-line 2)
190          (delete-region (point)
191                         (progn
192                           (search-forward "***END***")
193                           (beginning-of-line)
194                           (point)))
195          (dolist (baud (sort (copy-list bauds) #'<))
196            (insert (format "#ifdef B%d\n    { B%d, %d },\n#endif\n"
197                            baud baud baud)))))
198   */
199     /***BEGIN baudlist***/
200 #ifdef B50
201     { B50, 50 },
202 #endif
203 #ifdef B75
204     { B75, 75 },
205 #endif
206 #ifdef B110
207     { B110, 110 },
208 #endif
209 #ifdef B134
210     { B134, 134 },
211 #endif
212 #ifdef B150
213     { B150, 150 },
214 #endif
215 #ifdef B200
216     { B200, 200 },
217 #endif
218 #ifdef B300
219     { B300, 300 },
220 #endif
221 #ifdef B600
222     { B600, 600 },
223 #endif
224 #ifdef B1200
225     { B1200, 1200 },
226 #endif
227 #ifdef B1800
228     { B1800, 1800 },
229 #endif
230 #ifdef B2400
231     { B2400, 2400 },
232 #endif
233 #ifdef B4800
234     { B4800, 4800 },
235 #endif
236 #ifdef B9600
237     { B9600, 9600 },
238 #endif
239 #ifdef B19200
240     { B19200, 19200 },
241 #endif
242 #ifdef B38400
243     { B38400, 38400 },
244 #endif
245 #ifdef B57600
246     { B57600, 57600 },
247 #endif
248 #ifdef B115200
249     { B115200, 115200 },
250 #endif
251 #ifdef B230400
252     { B230400, 230400 },
253 #endif
254 #ifdef B460800
255     { B460800, 460800 },
256 #endif
257 #ifdef B500000
258     { B500000, 500000 },
259 #endif
260 #ifdef B576000
261     { B576000, 576000 },
262 #endif
263 #ifdef B921600
264     { B921600, 921600 },
265 #endif
266 #ifdef B1000000
267     { B1000000, 1000000 },
268 #endif
269 #ifdef B1152000
270     { B1152000, 1152000 },
271 #endif
272 #ifdef B1500000
273     { B1500000, 1500000 },
274 #endif
275 #ifdef B2000000
276     { B2000000, 2000000 },
277 #endif
278 #ifdef B2500000
279     { B2500000, 2500000 },
280 #endif
281 #ifdef B3000000
282     { B3000000, 3000000 },
283 #endif
284 #ifdef B3500000
285     { B3500000, 3500000 },
286 #endif
287 #ifdef B4000000
288     { B4000000, 4000000 },
289 #endif
290     /***END***/
291     { 0, 0 }
292   };
293
294   const struct baudtab *b;
295   const char *p; int n;
296   struct termios c;
297   speed_t code;
298
299   /* Save the output stream. */
300   tty->fpout = fp;
301
302   /* Determine the output baud rate.  Unhelpfully, the kernel provides a
303    * weird code, so we have to convert it into an actual rate in bits per
304    * second.
305    */
306   tty->baud = 0; tty->wd = tty->ht = 0;
307   if (fp && !tcgetattr(fileno(fp), &c)) {
308     code = cfgetospeed(&c); if (ospeed_out) *ospeed_out = code;
309     for (b = baudtab; b->baud; b++)
310       if (b->code == code) { tty->baud = b->baud; break; }
311     tty_resized(tty);
312   }
313
314   /* If the kernel didn't tell us the terminal dimensions, try to read them
315    * from the environment.
316    */
317   if (!tty->wd)
318     { p = getenv("COLUMNS"); if (p) { n = atoi(p); if (n) tty->wd = n; } }
319   if (!tty->ht)
320     { p = getenv("LINES");   if (p) { n = atoi(p); if (n) tty->ht = n; } }
321 }
322
323 /* --- @env_colour_caps@ --- *
324  *
325  * Arguments:   @unsigned *caps_inout@ = attribute capabilities to update
326  *              @unsigned f@ = flags
327  *
328  * Returns:     ---
329  *
330  * Use:         Check the %|FORCE_COLOR|% environment variable and update the
331  *              capabilities as required.
332  *
333  *              The %|FORCE_COLOR|% variable originates with the Node
334  *              community, with two objectives: (a) to convey policy
335  *              regarding whether to produce coloured output, and (b) to
336  *              describe the colour capabilities of the terminal, because the
337  *              traditional mechanisms are deemed inadequate.
338  *
339  *              The following values have defined meanings.
340  *
341  *                * Unset or empty: no effect.
342  *
343  *                * %|0|%: monochrome; don't produce colour.
344  *
345  *                * %|1|%: 16 colours; 1-bit-per-channel colours are
346  *                  available, with an additional common brightness bit.
347  *
348  *                * %|2|%: 256 colours; the `xterm' 256-bit palette is
349  *                  available, consisting of the 1-bit-per-channel colours
350  *                  with common brightness bit, a 6 Ã— 6 Ã— 6 colour cube, and
351  *                  a 24-level greyscale ramp.
352  *
353  *                * %|3|%: full 24-bit colour.
354  *
355  *                * Anything else: a request to use colour if available.
356  *                  This is ignored here, in the expectation that it will be
357  *                  given effect elsewhere, e.g., by @ttycolour_enablep@.
358  *
359  *              If @ECCF_SET@ is set, then set or clear capabilities as
360  *              required.  Otherwise, clear capability bits which are denied
361  *              by the variable setting, but no bits will be set.  (This
362  *              latter is necessary for backends which use terminal
363  *              databases, since they can't be expected to make up the
364  *              necessary control sequences for themselves.)
365  */
366
367 #define ECCF_SET 1u
368 static void env_colour_caps(unsigned *caps_inout, unsigned f)
369 {
370   const char *p;
371   unsigned caps = *caps_inout, mask;
372
373   p = getenv("FORCE_COLOR"); if (!p) return;
374   switch (*p) {
375     case '0':
376       mask = 0;
377       break;
378     case '1':
379       mask = TTACF_FG | TTACF_BG | TTACF_1BPC | TTACF_1BPCBR;
380       break;
381     case '2':
382       mask = TTACF_FG | TTACF_BG |
383              TTACF_1BPC | TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
384       break;
385     case '3':
386       mask = TTACF_FG | TTACF_BG |
387              TTACF_1BPC | TTACF_1BPCBR | TTACF_8BPC;
388       break;
389     default:
390       return;
391   }
392   if (!(f&ECCF_SET)) caps &= mask;
393   else caps = (caps&~(TTACF_CSPCMASK | TTACF_FG | TTACF_BG)) | mask;
394
395   *caps_inout = caps;
396 }
397
398 /* --- @clamp_colours@ --- *
399  *
400  * Arguments:   @uint32 *space_out, *colour_out@ = selected space and colour
401  *              @uint32 space, colour@ = requested space and colour
402  *              @uint32 acaps@ = terminal's attribute capability mask
403  *
404  * Returns:     ---
405  *
406  * Use:         Select the best approximation to the requested colour which
407  *              can be accommodated by the terminal.
408  */
409
410 /* #define DEBUG_CLAMP */
411
412 static void clamp_colours(uint32 *space_out, uint32 *colour_out,
413                           uint32 space, uint32 colour, uint32 acaps)
414 {
415   unsigned r, g, b, rr, gg, bb, y, t, u;
416   uint32 best_colour = 0, best_space; int best_error;
417
418 #ifdef DEBUG_CLAMP
419 #  define D(x) x
420 #else
421 #  define D(x)
422 #endif
423
424   /* Check the colour space.  If it's one that the terminal can handle, then
425    * return the colour unchanged.  Otherwise, extract the channel components
426    * for the next step.
427    */
428   switch (space) {
429
430     case TTCSPC_NONE:
431       /* No colour wanted at all.  There's nothing to do here. */
432
433       *space_out = TTCSPC_NONE; *colour_out = 0;
434       return;
435
436     case TTCSPC_1BPC:
437       /* One-bit-per-channel colour.
438        *
439        * There's no standardized mapping for these; indeed, they're commonly
440        * configurable by users.  Since there are also `bright' versions of
441        * each, it's probably not right to just map zero to zero and one to
442        * full-scale.
443        */
444
445       if (colour >= 8) goto inval;
446       if (acaps&(TTACF_1BPC | TTACF_1BPCBR))
447         { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
448
449 #define C1BPC_FULL 0xcc
450 #define C1BPC_CVT(col, bit)                                             \
451         (C1BPC_FULL&((((col)&(1 << (bit))) << (8 - (bit))) - 1))
452       r = C1BPC_CVT(colour, 0);
453       g = C1BPC_CVT(colour, 1);
454       b = C1BPC_CVT(colour, 2);
455       break;
456
457     case TTCSPC_1BPCBR:
458       /* One-bit-per-channel colour, with global brightness.  Again, there's
459        * no standardized mapping.  Apply a boost across all three channels.
460        */
461
462       if (colour >= 16) goto inval;
463       if (!(colour&TT1BPC_BRI) && (acaps&(TTACF_1BPC | TTACF_1BPCBR)))
464         { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
465       else if (acaps&TTACF_1BPCBR)
466         { *space_out = TTCSPC_1BPCBR; *colour_out = colour; return; }
467
468 #define C1BPC_BRIGHT 0x33
469       r = C1BPC_CVT(colour, 0) + C1BPC_BRIGHT;
470       g = C1BPC_CVT(colour, 1) + C1BPC_BRIGHT;
471       b = C1BPC_CVT(colour, 2) + C1BPC_BRIGHT;
472 #undef C1BPC_CVT
473       break;
474
475     case TTCSPC_4LPC:
476       /* Four-levels-per-channel colour.  These are part of an `indexed'
477        * colour space which is theoretically controlled by applications but
478        * (a) that's rare, and (b) worrying about that won't do us any good.
479        *
480        * Each channel has four levels, but they're not assigned linearly.
481        */
482
483       if (colour >= 64) goto inval;
484       if (acaps&TTACF_4LPC)
485         { *space_out = TTCSPC_4LPC; *colour_out = colour; return; }
486
487 #define C4LPC_L0 0
488 #define C4LPC_L1 139
489 #define C4LPC_L2 205
490 #define C4LPC_L3 255
491 #define C4LPC_CVT(ch) (t = (ch), t == 0 ? C4LPC_L0 :                    \
492                                  t == 1 ? C4LPC_L1 :                    \
493                                  t == 2 ? C4LPC_L2 :                    \
494                                           C4LPC_L3)
495       r = C4LPC_CVT(TTCOL_2BR(colour));
496       g = C4LPC_CVT(TTCOL_2BG(colour));
497       b = C4LPC_CVT(TTCOL_2BB(colour));
498 #undef C4LPC_CVT
499       break;
500
501     case TTCSPC_8LGS:
502       /* Eight-levels greyscale.  Again, these are part of an `indexed'
503        * colour space which is under application control, and, again, the
504        * levels aren't linear, but they're not far off linear.
505        */
506       if (colour >= 8) goto inval;
507       if (acaps&TTACF_8LGS)
508         { *space_out = TTCSPC_8LGS; *colour_out = colour; return; }
509
510       r = g = b = 255*(colour ? colour + 3 : 2)/11;
511       break;
512
513     case TTCSPC_6LPC:
514       /* Six-levels-per-channel colour.  Again, `indexed' colour space under
515        * application control.  This time the mapping is essentially liner.
516        */
517
518       if (colour >= 216) goto inval;
519       if (acaps&TTACF_6LPC)
520         { *space_out = TTCSPC_6LPC; *colour_out = colour; return; }
521
522 #define C6LPC_CVT(ch) (t = (ch), t ? (40*t + 55) : 0)
523       r = C6LPC_CVT(TTCOL_6LR(colour));
524       g = C6LPC_CVT(TTCOL_6LG(colour));
525       b = C6LPC_CVT(TTCOL_6LB(colour));
526 #undef C6LPC_CVT
527       break;
528
529     case TTCSPC_24LGS:
530       /* Twenty-four-levels greyscale.  Same story. */
531
532       if (colour >= 24) goto inval;
533       if (acaps&TTACF_24LGS)
534         { *space_out = TTCSPC_24LGS; *colour_out = colour; return; }
535
536       r = g = b = 10*colour + 8;
537       break;
538
539     case TTCSPC_8BPC:
540       /* Eight-bits-per-channel colour.  No conversion to apply here. */
541
542       if (colour >= 0x01000000) goto inval;
543       if (acaps&TTACF_8BPC)
544         { *space_out = TTCSPC_8BPC; *colour_out = colour; return; }
545
546       r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
547       break;
548
549     default:
550       /* Anything else. */
551
552       goto inval;
553   }
554
555   /* We didn't get an exact match, so we'll have to make do with what we've
556    * got.
557    */
558   best_error = -1; best_space = TTCSPC_NONE; best_colour = 0;
559   D( fprintf(stderr, "\n;; APPROX space %u, colour 0x%lx = %u/%u/%u\n",
560              space, (unsigned long)colour, r, g, b); )
561
562   /* Approximate colour weightings for human colour vision. */
563 #define RWT 2
564 #define GWT 4
565 #define BWT 1
566 #define TOTWT (RWT + GWT + BWT)
567
568   /* Determine the optimal grey approximation for this colour. */
569   y = (RWT*r + GWT*g + BWT*b + TOTWT/2)/TOTWT;
570
571 #define TRY_APPROX(rr, gg, bb, spc, clr) do {                           \
572   /* If the approximation (RR, GG, BB) is closer to the current best    \
573    * then accept SPC and CLR as the new best space/colour option.       \
574    */                                                                   \
575                                                                         \
576   int _r_err = (rr) - r, _g_err = (gg) - g, _b_err = (bb) - b;          \
577   int _err;                                                             \
578                                                                         \
579   if (_r_err < 0) _r_err = -_r_err;                                     \
580   if (_g_err < 0) _g_err = -_g_err;                                     \
581   if (_b_err < 0) _b_err = -_b_err;                                     \
582                                                                         \
583   _err = RWT*_r_err + GWT*_g_err + BWT*_b_err;                          \
584   D( fprintf(stderr,                                                    \
585              ";;   candidate space %u, colour 0x%lx = %u/%u/%u; "       \
586                   "error = %d\n",                                       \
587              (spc), (unsigned long)(clr), (rr), (gg), (bb), _err); )    \
588   if (best_error < 0 || _err < best_error) {                            \
589     best_error = _err; best_space = (spc); best_colour = (clr);         \
590     D( fprintf(stderr, ";;\tNEW BEST APPROXIMATION\n"); )               \
591   }                                                                     \
592 } while (0)
593
594   if (!(acaps&(TTACF_4LPC | TTACF_6LPC | TTACF_8BPC))) {
595     /* The one-bit-per-channel colours are very variable, but there's little
596      * choice, so we'll have to try.  We assume the same mapping as on the
597      * way in.
598      */
599
600     if (acaps&(TTACF_1BPC | TTACF_1BPCBR)) {
601       /* One-bit-per-channel colour. */
602
603 #define C1BPC_APPROX(cc, c, bit) do {                                   \
604       if ((c) <= C1BPC_FULL/2) (cc) = 0;                                \
605       else { (cc) = C1BPC_FULL; t |= (bit); }                           \
606 } while (0)
607       t = 0;
608       C1BPC_APPROX(rr, r, TT1BPC_RED);
609       C1BPC_APPROX(gg, g, TT1BPC_GRN);
610       C1BPC_APPROX(bb, b, TT1BPC_BLU);
611 #undef C1BPC_APPROX
612       TRY_APPROX(rr, gg, bb, TTCSPC_1BPC, t);
613     }
614
615     if (acaps&TTACF_1BPCBR) {
616       /* One-bit-per-channel colour, with global brightness. */
617
618 #define C1BPCBR_APPROX(cc, c, bit) do {                                 \
619       if ((c) <= C1BPC_FULL/2 + C1BPC_BRIGHT) (cc) = C1BPC_BRIGHT;      \
620       else { (cc) = 255; t |= (bit); }                                  \
621 } while (0)
622       t = TT1BPC_BRI;
623       C1BPCBR_APPROX(rr, r, TT1BPC_RED);
624       C1BPCBR_APPROX(gg, g, TT1BPC_GRN);
625       C1BPCBR_APPROX(bb, b, TT1BPC_BLU);
626 #undef C1BPCBR_APPROX
627       TRY_APPROX(rr, gg, bb, TTCSPC_1BPCBR, t);
628     }
629   }
630
631   if (acaps&TTACF_4LPC) {
632     /* Four-levels-per-channel colour. */
633
634 #define C4LPC_APPROX(cc, c, sh) do {                                    \
635     unsigned _c = (c);                                                  \
636                                                                         \
637     if (_c > (C4LPC_L2 + C4LPC_L3)/2)                                   \
638       { (cc) = C4LPC_L3; t |= 3 << (sh); }                              \
639     else if (_c > (C4LPC_L1 + C4LPC_L2)/2)                              \
640       { (cc) = C4LPC_L2; t |= 2 << (sh); }                              \
641     else if (_c > (C4LPC_L0 + C4LPC_L1)/2)                              \
642       { (cc) = C4LPC_L1; t |= 1 << (sh); }                              \
643     else                                                                \
644       (cc) = C4LPC_L0;                                                  \
645 } while (0)
646     t = 0;
647     C4LPC_APPROX(rr, r, 4);
648     C4LPC_APPROX(gg, g, 2);
649     C4LPC_APPROX(bb, b, 0);
650 #undef C4LPC_APPROX
651     TRY_APPROX(rr, gg, bb, TTCSPC_4LPC, t);
652   }
653
654   if (acaps&TTACF_8LGS) {
655     /* Eight-levels greyscale. */
656
657     u = (11*y)/255;
658     if (u <= 2) { u = 2; t = 0; }
659     else if (u == 3) { u = 4; t = 1; }
660     else if (u == 11) { u = 10; t = 7; }
661     else t = u - 3;
662     u = (255*u)/11; TRY_APPROX(u, u, u, TTCSPC_8LGS, t);
663   }
664
665   if (acaps&TTACF_6LPC) {
666     /* Six-levels-per-channel colour. */
667
668 #define C6LPC_APPROX(cc, c, f) do {                                     \
669     unsigned _c = (c);                                                  \
670                                                                         \
671     if (_c < 36) (cc) = 0;                                              \
672     else { u = (_c - 36)/40; t += (f)*u; (cc) = 40*u + 55; }            \
673 } while (0)
674     t = 0;
675     C6LPC_APPROX(rr, r, 36);
676     C6LPC_APPROX(gg, g,  6);
677     C6LPC_APPROX(bb, b,  1);
678 #undef C6LPC_APPROX
679     TRY_APPROX(rr, gg, bb, TTCSPC_6LPC, t);
680   }
681
682   if (acaps&TTACF_24LGS) {
683     /* Twenty-four-levels greyscale. */
684
685     if (y < 3) { t = 0; u = 8; }
686     else if (y >= 243) { t = 23; u = 238; }
687     else { t = (y - 3)/10; u = 10*t + 8; }
688     TRY_APPROX(u, u, u, TTCSPC_24LGS, t);
689   }
690
691   if (acaps&TTACF_8BPC) {
692     /* Eight-bits-per-channel colour. */
693
694     if (best_error) {
695       D( fprintf(stderr, ";;   accept exact 8bpc colour\n"); )
696       best_error = 0; best_space = TTCSPC_8BPC;
697       best_colour = TTCOL_MK8B(r, g, b);
698     }
699   }
700
701   /* Done. */
702   *space_out = best_space; *colour_out = best_colour;
703   return;
704
705 inval:
706   /* Invalid colour selection.  Ignore this. */
707   *space_out = TTCSPC_NONE; *colour_out = 0;
708
709 #undef C1BPC_FULL
710 #undef C1BPC_BRIGHT
711
712 #undef C4LPC_L0
713 #undef C4LPC_L1
714 #undef C4LPC_L2
715 #undef C4LPC_L3
716
717 #undef TRY_APPROX
718 #undef TRY_RGB
719 #undef TRY_GREY
720
721 #undef RWT
722 #undef GWT
723 #undef BWT
724 #undef TOTWT
725
726 #undef SET_RANGE
727 #undef CHECK_RANGE
728
729 #undef D
730 }
731
732 /* --- @clamp_attr@ --- *
733  *
734  * Arguments:   @struct tty_attr *a_out@ = selected attributes
735  *              @const struct tty_attr *a@ = requested attributes
736  *              @uint32 acaps@ = terminal's attribute capability mask
737  *
738  * Returns:     ---
739  *
740  * Use:         Select the closest approximation to the requested attributes
741  *              which can be accommodated by the terminal.
742  */
743
744 static void clamp_attr(struct tty_attr *a_out,
745                        const struct tty_attr *a, uint32 acaps)
746 {
747   uint32 ff = 0, f = a ? a->f : 0, t;
748
749   /* Line attributes. */
750   t = (f&TTAF_LNMASK) >> TTAF_LNSHIFT;
751   switch (t) {
752     case TTLN_NONE:
753       break;
754     case TTLN_ULINE:
755       if (!acaps&TTACF_ULINE) t = TTLN_NONE;
756       break;
757     case TTLN_UULINE:
758       if (acaps&TTACF_UULINE) ;
759       else if (acaps&TTACF_ULINE) t = TTLN_ULINE;
760       else t = TTLN_NONE;
761       break;
762     default:
763       t = TTLN_NONE;
764   }
765   ff |= t << TTAF_LNSHIFT;
766
767   /* Text weight. */
768   t = (f&TTAF_WTMASK) >> TTAF_WTSHIFT;
769   switch (t) {
770     case TTWT_MED: break;
771     case TTWT_BOLD: if (!(acaps&TTACF_BOLD)) t = TTWT_MED; break;
772     case TTWT_DIM: if (!(acaps&TTACF_DIM)) t = TTWT_MED; break;
773     default: t = TTWT_MED; break;
774   }
775   ff |= t << TTAF_WTSHIFT;
776
777   /* Other text attributes. */
778   if (acaps&TTACF_STRIKE) ff |= f&TTAF_STRIKE;
779   if (acaps&TTACF_ITAL) ff |= f&TTAF_ITAL;
780   if (acaps&TTACF_INVV) ff |= f&TTAF_INVV;
781
782   /* Foreground and background colours. */
783   if (!(acaps&TTACF_FG))
784     a_out->fg = 0;
785   else {
786     clamp_colours(&t, &a_out->fg,
787                   (f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a ? a->fg : 0,
788                   acaps);
789     ff |= t << TTAF_FGSPCSHIFT;
790   }
791   if (!(acaps&TTACF_BG))
792     a_out->bg = 0;
793   else {
794     clamp_colours(&t, &a_out->bg,
795                   (f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a ? a->bg : 0,
796                   acaps);
797     ff |= t << TTAF_BGSPCSHIFT;
798   }
799
800   /* All done. */
801   a_out->f = ff; a_out->_res0 = 0;
802 }
803
804 /* --- @stupid_repeat@ --- *
805  *
806  * Arguments:   @struct tty *tty@ = control block pointer
807  *              @const struct gprintf_ops *gops, void *go@ = output
808  *                      destination
809  *              @int ch@ = character to write
810  *              @unsigned n@ = number of copies
811  *
812  * Returns:     Zero on success, %$-1$% on error.
813  *
814  * Use:         Write @n@ copies of the character @ch@ to the terminal, the
815  *              hard way.  This function tries to be reasonably efficient, by
816  *              transmitting buffers rather than exercising the output
817  *              machinery for each individual character.
818  */
819
820 static int stupid_repeat(struct tty *tty,
821                          const struct gprintf_ops *gops, void *go,
822                          int ch, unsigned n)
823 {
824   char buf[4096];
825   unsigned nn;
826   int rc;
827
828   if (n < sizeof(buf))
829     { memset(buf, ch, n); CHECK(gops->putm(go, buf, n)); }
830   else {
831     memset(buf, ch, sizeof(buf));
832     nn = sizeof(buf);
833     for (;;) {
834       CHECK(gops->putm(go, buf, nn));
835       n -= nn; if (!n) break;
836       if (n < nn) nn = n;
837     }
838   }
839   rc = 0;
840 end:
841   return (rc);
842 }
843
844 /*----- Common machinery for %|termcap|% and %|terminfo|% -----------------*/
845
846 #if defined(HAVE_TERMINFO) ||                                           \
847     defined(HAVE_TERMCAP) ||                                            \
848     defined(HAVE_UNIBILIUM)
849
850 #if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
851
852 /* Global state.
853  *
854  * The `termcap' and `terminfo' functions call a user-provided function to
855  * actually send control codes to the terminal.  The bad news is that
856  * `termcap' doesn't provide any way to pass information to the output
857  * function beyond the character to be sent, and `terminfo' doesn't fix this
858  * mistake.  So we must save the necessary context as global variables.  For
859  * good measure, at least some implementations ignore errors from the output
860  * function, so we must keep track of them ourselves.  More global variables.
861  *
862  * It's worse.  Both libraries maintain significant global state of their
863  * own.  And, at least with the `ncurses' implementation, the two share the
864  * same global state.  The only thing to do is maintain a big interlock to
865  * make sure that only one is active at a time.
866  */
867 static const struct gprintf_ops *global_gops; /* output operations ... */
868 static void *global_gout;               /* and context, for @caps_putch */
869 static char global_buf[BUFSZ];          /* a big output buffer */
870 static size_t global_len;               /* length of buffer used */
871 static int global_err;                  /* error latch, zero if all ok */
872 static struct tty *global_lock = 0;     /* interlock for global state */
873
874 /* --- @caps_claim@ --- *
875  *
876  * Arguments:   ---
877  *
878  * Returns:     Zero on success, %$-1$% if already claimed.
879  *
880  * Use:         Return %$-1$% if the interlock is already held.  This is a
881  *              function to call near the beginning of initializing a new
882  *              control block, before the common global state gets
883  *              clobbered.  If initialization is successful, the caller is
884  *              expected to actually store the control block pointer in
885  *              @global_lock@ themselves.
886  */
887
888 static int caps_claim(void)
889 {
890   if (global_lock)
891     { debug("termcap/terminfo terminal already open"); return (-1); }
892   else
893     return (0);
894 }
895
896 /* --- @caps_release@ --- *
897  *
898  * Arguments:   @struct tty *tty@ = control block pointer for current lock
899  *                      holder
900  *
901  * Returns:     ---
902  *
903  * Use:         Release the lock.
904  */
905
906 static void caps_release(struct tty *tty)
907   { assert(global_lock == tty); global_lock = 0; }
908
909 /* --- @caps_prepout@ --- *
910  *
911  * Arguments:   @struct tty *tty@ = control block pointer (ignored)
912  *              @const struct gprintf_ops *gops, void *go@ = output
913  *                      destination
914  *
915  * Returns:     ---
916  *
917  * Use:         Prepare output to the given destination.  Check that the
918  *              output buffer is initially empty (i.e., that it was properly
919  *              flushed last time), and make a note of the output
920  *              destination.
921  */
922
923 static void caps_prepout(struct tty *tty,
924                          const struct gprintf_ops *gops, void *go)
925   { assert(!global_len); global_gops = gops; global_gout = go; }
926
927 /* --- @caps_putch@ --- *
928  *
929  * Arguments:   @int ch@ = character to write
930  *
931  * Returns:     Nonnegative on success, negative on failure.  (But @tputs@
932  *              ignores this.)
933  *
934  * Use:         Output the character @ch@.
935  */
936
937 static int caps_putch(int ch)
938 {
939   size_t n;
940
941   /* By policy, we flush the buffer as soon as it becomes full, so there
942    * should always be space for at least one byte.
943    */
944   n = global_len; assert(n < BUFSZ);
945   global_buf[n++] = ch;
946
947   /* If the buffer is now full, then flush it. */
948   if (n >= BUFSZ) {
949     if (global_gops->putm(global_gout, global_buf, n)) global_err = -1;
950     n = 0;
951   }
952
953   /* Done. */
954   global_len = n; return (0);
955 }
956
957 /* --- @caps_flush@ --- *
958  *
959  * Arguments:   @struct tty *tty@ = control block pointer (ignored)
960  *
961  * Returns:     Zero for success, %$-1$% if error pending.
962  *
963  * Use:         Flush the output buffer to the backend.  If an error is
964  *              pending, clear it and return failure.
965  */
966
967 static int caps_flush(struct tty *tty)
968 {
969   int rc = global_err;
970
971   if (global_len) {
972     if (global_gops->putm(global_gout, global_buf, global_len)) rc = -1;
973     global_len = 0;
974   }
975   global_err = 0; return (rc);
976 }
977
978 #endif
979
980 /* The list of interesting capabilities.
981  *
982  * We never actually need all of these: some are only needed if others are
983  * unavailable.  But the list isn't too huge, so we'll live with it.
984  *
985  * The main thing is that each capability has three different names: the
986  * `full' name (corresponding to a `terminfo' variable name), the `terminfo'
987  * capability name, as used in terminal descriptions, and the two-character
988  * `termcap' name.  Unibilium uses the long names, but to reduce typing, the
989  * `unibi_' prefix is omitted here.  (Annoyingly, in `ncurses', at least, the
990  * `variable' names are `secretly' macros referencing a current state, and
991  * premature expansion causes misery, so I've left the leading underscores in
992  * place as a countermeasure.)  Internally, we use the short `terminfo'
993  * names, since they generally express the most useful information in the
994  * smallest space.
995  */
996
997 #define BASICCAPS(_bool, _int, _str)                                    \
998   _str(_repeat_char, rep, rp)                                           \
999   _str(_pad_char, pad, pc) _int(_padding_baud_rate, pb, pb)             \
1000   _bool(_no_pad_char, npc, NP) _bool(_xon_xoff, xon, xo)                \
1001   _bool(_move_insert_mode, mir, mi) _bool(_move_standout_mode, msgr, ms)
1002
1003 #define ATTRCAPS(_bool, _int, _str)                                     \
1004   _str(_exit_attribute_mode, sgr0, me)                                  \
1005   _str(_enter_underline_mode, smul, us)                                 \
1006     _str(_exit_underline_mode, rmul, ue)                                \
1007   _str(_enter_italics_mode, sitm, ZH) _str(_exit_italics_mode, ritm, ZR) \
1008   _str(_enter_bold_mode, bold, md) _str(_enter_dim_mode, dim, mh)       \
1009   _str(_enter_reverse_mode, rev, mr)                                    \
1010   COLOURCAPS(_bool, _int, _str)
1011
1012 #define COLOURCAPS(_bool, _int, _str)                                   \
1013   _str(_set_a_foreground, setaf, AF) _str(_set_a_background, setab, AB) \
1014   _str(_orig_pair, op, op) _int(_max_colors, colors, Co)
1015
1016 #define MODECAPS(_bool, _int, _str)                                     \
1017   _str(_carriage_return, cr, cr) _str(_newline, nel, nw)                \
1018   _str(_enter_am_mode, smam, SA) _str(_exit_am_mode, rmam, RA)          \
1019   _str(_enter_ca_mode, smcup, ti) _str(_exit_ca_mode, rmcup, te)        \
1020   _str(_cursor_normal, cnorm, vs) _str(_cursor_invisible, civis, vi)    \
1021   _str(_enter_insert_mode, smir, im) _str(_exit_insert_mode, rmir, ei)  \
1022   _str(_enter_delete_mode, smdc, dm) _str(_exit_delete_mode, rmdc, ed)
1023
1024 #define MOVECAPS(_bool, _int, _str)                                     \
1025   _str(_cursor_home, home, ho)                                          \
1026   _str(_cursor_address, cup, cm)                                        \
1027   _str(_row_address, vpa, cv) _str(_column_address, hpa, ch)            \
1028   _str(_cursor_left, cub1, le) _str(_parm_left_cursor, cub, LE)         \
1029   _str(_cursor_right, cuf1, nd) _str(_parm_right_cursor, cuf, RI)       \
1030   _str(_cursor_up, cuu1, up) _str(_parm_up_cursor, cuu, UP)             \
1031   _str(_cursor_down, cud1, do) _str(_parm_down_cursor, cud, DO)
1032
1033 #define SCROLLCAPS(_bool, _int, _str)                                   \
1034   _str(_change_scroll_region, csr, cs)                                  \
1035   _str(_scroll_forward, ind, sf) _str(_parm_index, indn, SF)            \
1036   _str(_scroll_reverse, ri, sr) _str(_parm_rindex, rin, SR)
1037
1038 #define ERASECAPS(_bool, _int, _str)                                    \
1039   _str(_erase_chars, ech, ec)                                           \
1040   _str(_clr_bol, el1, cb) _str(_clr_eol, el, ce)                        \
1041   _str(_clr_eos, ed, cd) _str(_clear_screen, clear, cl)
1042
1043 #define INSDELCAPS(_bool, _int, _str)                                   \
1044   _str(_insert_character, ich1, ic) _str(_parm_ich, ich, IC)            \
1045   _str(_insert_padding, ip, ip)                                         \
1046   _str(_insert_line, il1, al) _str(_parm_insert_line, il, AL)           \
1047   _str(_delete_character, dch1, dc) _str(_parm_dch, dch, DC)            \
1048   _str(_delete_line, dl1, dl) _str(_parm_delete_line, dl, DL)
1049
1050 #define STORECAPS(_bool, _int, _str)                                    \
1051   BASICCAPS(_bool, _int, _str)                                          \
1052   ATTRCAPS(_bool, _int, _str)                                           \
1053   MODECAPS(_bool, _int, _str)                                           \
1054   MOVECAPS(_bool, _int, _str)                                           \
1055   SCROLLCAPS(_bool, _int, _str)                                         \
1056   ERASECAPS(_bool, _int, _str)                                          \
1057   INSDELCAPS(_bool, _int, _str)
1058
1059 #ifdef HAVE_UNIBILIUM
1060 #  define UNIBI_(x) unibi##x
1061 #else
1062 #  define UNIBI_(x) 0
1063 #endif
1064
1065 #define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
1066   /* Expand a capability triple into a group of three usable C arguments.  If
1067    * Unibilium isn't available, then we can use nonsense for its cap index.
1068    */
1069
1070 /* Some other capabilities which we want to refer to during initialization,
1071  * but don't need to keep around.
1072  */
1073 #define CAP_XMC CAPREF(_magic_cookie_glitch, xmc, sg)
1074 #define CAP_BCE CAPREF(_back_color_erase, bce, ut)
1075 #define CAP_XHPA CAPREF(_row_addr_glitch, xvpa, YD)
1076 #define CAP_XVPA CAPREF(_col_addr_glitch, xhpa, YA)
1077 #define CAP_AM CAPREF(_auto_right_margin, am, am)
1078 #define CAP_XENL CAPREF(_eat_newline_glitch, xenl, xn)
1079 #define CAP_HT CAPREF(_lines, lines, li)
1080 #define CAP_WD CAPREF(_columns, cols, co)
1081
1082 /* Additional operations required of terminal backends which make use of the
1083  * common capability machinery.
1084  */
1085 struct tty_capopslots {
1086
1087   /* Retrieving capabilities. */
1088   int (*boolcap)(struct tty */*tty*/,
1089                  int /*uix*/, const char */*info*/, const char */*cap*/);
1090   int (*intcap)(struct tty */*tty*/,
1091                 int /*uix*/, const char */*info*/, const char */*cap*/);
1092   const char *(*strcap)(struct tty */*tty*/,
1093                         int /*uix*/, const char */*info*/,
1094                         const char */*cap*/);
1095
1096   /* Preparing and completing output. */
1097   void (*prepout)(struct tty */*tty*/,
1098                   const struct gprintf_ops */*gops*/, void */*go*/);
1099   int (*flush)(struct tty */*tty*/);
1100
1101   /* Writing capabilities with various kinds of arguments. */
1102   int (*put0)(struct tty */*tty*/, unsigned /*npad*/, const char */*cap*/);
1103   int (*put1i)(struct tty */*tty*/,
1104                unsigned /*npad*/, const char */*cap*/, int /*i0*/);
1105   int (*put2i)(struct tty */*tty*/,
1106                unsigned /*npad*/,
1107                const char */*cap*/, int /*i0*/, int /*i1*/);
1108
1109   /* Determine the cost of a capability. */
1110   size_t (*cost)(struct tty */*tty*/,
1111                  unsigned /*npad*/, const char */*cap*/, int /*i0*/);
1112 };
1113 #define TTY_CAPOPSPFX TTY_BASEOPSPFX; struct tty_capopslots cap
1114 struct tty_capops { TTY_CAPOPSPFX; };
1115 #define TTY_CAPOPSUSFX struct tty_capops cap; TTY_BASEOPSUXFX
1116 union tty_capopsu { TTY_CAPOPSUSFX; };
1117
1118 /* An extension of the control block to track the above capabilities. */
1119 struct tty_capslots {
1120 #define DEF_BOOLCAP(uix, info, cap) unsigned info : 1;
1121 #define DEF_INTCAP(uix, info, cap) int info;
1122 #define DEF_STRCAP(uix, info, cap) const char *info;
1123   STORECAPS(DEF_BOOLCAP, DEF_INTCAP, DEF_STRCAP)
1124 #undef DEF_BOOLCAP
1125 #undef DEF_INTCAP
1126 #undef DEF_STRCAP
1127 #define LEN_BOOLCAP(uix, info, cap)
1128 #define LEN_INTCAP(uix, info, cap)
1129 #define LEN_STRCAP(uix, info, cap) unsigned info##_cost;
1130     ATTRCAPS(LEN_BOOLCAP, LEN_INTCAP, LEN_STRCAP)
1131 #undef LEN_BOOLCAP
1132 #undef LEN_INTCAP
1133 #undef LEN_STRCAP
1134 };
1135 #define TTY_CAPSPFX                                                     \
1136   struct tty tty;                                                       \
1137   struct tty_capslots cap
1138 struct tty_caps { TTY_CAPSPFX; };
1139 #define TTY_CAPSUSFX                                                    \
1140   struct tty_caps cap;                                                  \
1141   struct tty tty
1142 union tty_capsu { TTY_CAPSUSFX; };
1143
1144 /* ---- @init_caps@ --- *
1145  *
1146  * Arguments:   @struct tty *tty@ = control block pointer
1147  *
1148  * Returns:     ---
1149  *
1150  * Use:         Populate the capabilities in the terminal control block, and
1151  *              advertise the results to the public part.  Set @ht@ and @wd@
1152  *              from the terminal description if they've not been set
1153  *              already.
1154  */
1155
1156 static void init_caps(struct tty_caps *t)
1157 {
1158   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1159   int wd, ht;
1160
1161   t->tty.acaps = t->tty.ocaps = 0;
1162   t->tty.st.modes = 0; t->tty.st.attr.f = 0;
1163   t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
1164
1165   /* Inhale all of the interesting terminal capabilities. */
1166 #define GETBOOL(var, info, cap_)                                        \
1167     t->cap.info = ops->cap.boolcap(&t->tty, CAPREF(var, info, cap_));
1168 #define GETINT(var, info, cap_)                                         \
1169     t->cap.info = ops->cap.intcap(&t->tty, CAPREF(var, info, cap_));
1170 #define GETSTR(var, info, cap_)                                         \
1171     t->cap.info = ops->cap.strcap(&t->tty, CAPREF(var, info, cap_));
1172   STORECAPS(GETBOOL, GETINT, GETSTR)
1173 #undef GETBOOL
1174 #undef GETINT
1175 #undef GETSTR
1176
1177 #define COST_BOOLCAP(uix, info, cap_)
1178 #define COST_INTCAP(uix, info, cap_)
1179 #define COST_STRCAP(uix, info, cap_)                                    \
1180     t->cap.info##_cost =                                                \
1181       ops->cap.cost(&t->tty, 1, t->cap.info, t->cap.colors - 1);
1182   ATTRCAPS(COST_BOOLCAP, COST_INTCAP, COST_STRCAP)
1183 #undef COST_BOOLCAP
1184 #undef COST_INTCAP
1185 #undef COST_STRCAP
1186
1187 #define CLEAR_BOOL(uix, info, cap_) t->cap.info = 0;
1188 #define CLEAR_INT(uix, info, cap_) t->cap.info = 0;
1189 #define CLEAR_STR(uix, info, cap_) t->cap.info = 0;
1190 #define CLEARCAPS(caplist)                                              \
1191     do { caplist(CLEAR_BOOL, CLEAR_INT, CLEAR_STR) } while (0)
1192
1193   /* Basic capabilities. */
1194   if (!t->cap.cr) t->cap.cr = "\r";
1195   if (!t->cap.nel) t->cap.nel = "\r\n";
1196
1197   /* Attribute capabilities. */
1198   if (ops->cap.intcap(&t->tty, CAP_XMC) > 0)
1199     CLEARCAPS(ATTRCAPS);
1200   else {
1201     if (t->cap.smul && (t->cap.rmul || t->cap.sgr0))
1202       t->tty.acaps |= TTACF_ULINE;
1203     if (t->cap.bold && t->cap.sgr0)
1204       t->tty.acaps |= TTACF_BOLD;
1205     if (t->cap.dim && t->cap.sgr0)
1206       t->tty.acaps |= TTACF_DIM;
1207     if (t->cap.sitm && (t->cap.ritm || t->cap.sgr0))
1208       t->tty.acaps |= TTACF_ITAL;
1209     if (t->cap.rev && t->cap.sgr0)
1210       t->tty.acaps |= TTACF_INVV;
1211
1212     if ((t->cap.setaf || t->cap.setab) && (t->cap.op || t->cap.sgr0)) {
1213       if (t->cap.setaf) t->tty.acaps |= TTACF_FG;
1214       if (t->cap.setab) t->tty.acaps |= TTACF_BG;
1215       t->tty.acaps |= TTACF_1BPC;
1216       if (t->cap.colors >= 16777216)
1217         t->tty.acaps |= TTACF_1BPC | TTACF_8BPC;
1218       else if (t->cap.colors >= 256)
1219         t->tty.acaps |= TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
1220       else if (t->cap.colors == 88)
1221         t->tty.acaps |= TTACF_1BPCBR | TTACF_4LPC | TTACF_8LGS;
1222       else if (t->cap.colors >= 16)
1223         t->tty.acaps |= TTACF_1BPCBR;
1224       if (ops->cap.boolcap(&t->tty, CAP_BCE)) t->tty.ocaps |= TTCF_BGER;
1225       env_colour_caps(&t->tty.acaps, 0);
1226     }
1227   }
1228
1229   /* Motion capabilities. */
1230   if (ops->cap.boolcap(&t->tty, CAP_XHPA)) t->cap.hpa = 0;
1231   if (ops->cap.boolcap(&t->tty, CAP_XVPA)) t->cap.vpa = 0;
1232   if (!t->cap.cub1) t->cap.cub1 = "\b";
1233   if ((t->cap.cuf || t->cap.cuf1) &&
1234       (t->cap.cuu || t->cap.cuu1) &&
1235       (t->cap.cud || t->cap.cud1)) {
1236     t->tty.ocaps |= TTCF_RELMV;
1237     if (t->cap.vpa) t->tty.ocaps |= TTCF_ABSMV | TTCF_MIXMV;
1238     else if (t->cap.home) t->tty.ocaps |= TTCF_ABSMV;
1239   } else if (t->cap.cup ||
1240              (t->cap.hpa && t->cap.vpa) ||
1241              (t->cap.home && (t->cap.cuf || t->cap.cuf1)))
1242     t->tty.ocaps |= TTCF_ABSMV;
1243
1244   /* Mode capabilities. */
1245   if (t->cap.smam && t->cap.rmam) t->tty.ocaps |= TTMF_AUTOM;
1246   if (t->cap.smcup && t->cap.rmcup) t->tty.ocaps |= TTMF_FSCRN;
1247   if (t->cap.smir && t->cap.rmir) t->tty.ocaps |= TTMF_INS;
1248   if (t->cap.smdc && t->cap.rmdc) t->tty.ocaps |= TTMF_DEL;
1249   if (t->cap.cnorm && t->cap.civis)
1250     { t->tty.ocaps |= TTMF_CVIS; t->tty.st.modes |= TTMF_CVIS; }
1251   if (ops->cap.boolcap(&t->tty, CAP_AM)) {
1252     t->tty.st.modes |= TTMF_AUTOM;
1253     if (ops->cap.boolcap(&t->tty, CAP_XENL)) t->tty.ocaps |= TTCF_MMARG;
1254   }
1255
1256   /* Erasure. */
1257   if (t->cap.ech) t->tty.ocaps |= TTCF_ERCH;
1258   if (t->cap.el1) t->tty.ocaps |= TTCF_ERBOL;
1259   if (t->cap.el) t->tty.ocaps |= TTCF_EREOL;
1260   if (t->cap.ed) t->tty.ocaps |= TTCF_EREOD;
1261   if (t->cap.clear || (t->cap.ed && t->cap.home)) t->tty.ocaps |= TTCF_ERDSP;
1262
1263   /* Insertion and deletion. */
1264   if (t->cap.ich || t->cap.ich1) t->tty.ocaps |= TTCF_INSCH;
1265   if (t->cap.il || t->cap.il1) t->tty.ocaps |= TTCF_INSLN;
1266   if (t->cap.dch || t->cap.dch1) t->tty.ocaps |= TTCF_DELCH;
1267   if (t->cap.dl || t->cap.dl1) t->tty.ocaps |= TTCF_DELLN;
1268
1269   /* Geometry. */
1270   if (!t->tty.wd)
1271     { wd = ops->cap.intcap(&t->tty, CAP_WD); if (wd > 0) t->tty.wd = wd; }
1272   if (!t->tty.ht)
1273     { ht = ops->cap.intcap(&t->tty, CAP_HT); if (ht > 0) t->tty.ht = ht; }
1274
1275 #undef CLEAR_BOOL
1276 #undef CLEAR_INT
1277 #undef CLEAR_STR
1278 #undef CLEARCAPS
1279 }
1280
1281 /* --- @caps_padchars@ --- *
1282  *
1283  * Arguments:   @struct tty *tty@ = extended control block pointer
1284  *              @unsigned delay@ = tenths of milliseconds required
1285  *              @unsigned f@ = flags
1286  *
1287  * Returns:     The number of padding characters to send.
1288  *
1289  * Use:         Determine the number of padding characters to send to achieve
1290  *              a delay of a given duration.  If @CPF_FORCE@ is set, then
1291  *              ignore the @pb@ and @xon@ capabilities.
1292  */
1293
1294 #define CPF_FORCE 1u
1295 static size_t caps_padchars(struct tty *tty, unsigned delay, unsigned f)
1296 {
1297   struct tty_caps *t = (struct tty_caps *)tty;
1298
1299   if (!(f&CPF_FORCE) && (t->tty.baud < t->cap.pb || t->cap.xon)) {
1300     /* We're not forced to pad, and the baud rate is sufficiently low or we
1301      * have flow control, then there's nothing to do.
1302      */
1303
1304     return (0);
1305   } else {
1306     /* We're transmitting at R b/s, and we want to send N B of data so that
1307      * this takes D/10000 s.  We're likely sending either seven bits with
1308      * parity or eight bits without, plus one stop bit, per character, so we
1309      * must send 9 N b, which will take 9 N/R s = D/10000 s.  Rearranging
1310      * gives
1311      *
1312      *         N = R D/90000 ,
1313      *
1314      * and we should round upwards.
1315      */
1316
1317     return ((unsigned long)t->tty.baud*delay + 89999/90000);
1318   }
1319 }
1320
1321 #if defined(HAVE_TERMCAP) || defined(HAVE_TERMINFO)
1322
1323 /* --- @caps_cost@ --- *
1324  *
1325  * Arguments:   @struct tty *tty@ = control block pointer
1326  *              @unsigned npad@ = number of lines affected
1327  *              @const char *ctrl@ = formatted control string
1328  *
1329  * Returns:     A linear `cost' for sending the capability.
1330  *
1331  * Use:         Determines the cost for a `termcap' or `terminfo' capability
1332  *              by parsing a @tputs@-format string.
1333  */
1334
1335 static unsigned scan_delay(const char **p_inout, unsigned npad)
1336 {
1337   const char *p = *p_inout;
1338   unsigned t, f;
1339
1340 #define f_padmul 1u
1341
1342   f = 0; t = 0;
1343   while (ISDIGIT(*p)) t = 10*t + (*p++ - '0');
1344   t *= 10;
1345   if (*p == '.') {
1346     p++;
1347     if (ISDIGIT(*p)) {
1348       t += *p++ - '0';
1349       while (ISDIGIT(*p)) p++;
1350     }
1351   }
1352   for (;;)
1353     switch (*p) {
1354       case '*': p++; f |= f_padmul; break;
1355       case '/': p++; break;
1356       default: goto done;
1357     }
1358 done:
1359   if (f&f_padmul) t *= npad;
1360   *p_inout = p; return (npad);
1361
1362 #undef f_padmul
1363 }
1364
1365 static size_t caps_cost(struct tty *tty, unsigned npad, const char *ctrl)
1366 {
1367   size_t n;
1368   unsigned t;
1369   const char *p;
1370
1371   p = ctrl;
1372   if (ISDIGIT(*p)) {
1373     /* The string starts with a number, so it's a `termcap'-style string with
1374      * a leading millisecond count.
1375      */
1376
1377     t = scan_delay(&p, npad);
1378     n = strlen(p);
1379   } else {
1380     /* No initial number.  Search for `terminfo'-style %|$<NNN.N[*|/]|%
1381      * droppings.
1382      */
1383
1384     n = 0; t = 0;
1385     while (*p)
1386       if (*p != '$' || p[1] != '<' || (!ISDIGIT(p[2] && p[2] != '>')))
1387         { n++; p++; }
1388       else {
1389         p += 2; t += scan_delay(&p, npad);
1390         if (*p == '>') p++;
1391       }
1392   }
1393
1394   /* All done. */
1395   return (n + caps_padchars(tty, t, CPF_FORCE));
1396 }
1397
1398 #endif
1399
1400 /* Macros for formatting capabilities.  The @...V@ macros evaluate the cap
1401  * name, while the unmarked macros interpret it as a slot name.
1402  */
1403 #define PUT0V(npad, cap_)                                               \
1404   CHECK(ops->cap.put0(&t->tty, (npad), (cap_)))
1405 #define PUT1IV(npad, cap_, i0)                                          \
1406   CHECK(ops->cap.put1i(&t->tty, (npad), (cap_), (i0)))
1407 #define PUT2IV(npad, cap_, i0, i1)                                      \
1408   CHECK(ops->cap.put2i(&t->tty, (npad), (cap_), (i0), (i1)))
1409
1410 #define PUT0(npad, name) PUT0V(npad, t->cap.name)
1411 #define PUT1I(npad, name, i0) PUT1IV(npad, t->cap.name, i0)
1412 #define PUT2I(npad, name, i0, i1) PUT2IV(npad, t->cap.name, i0, i1)
1413
1414 /* --- @caps_iterate@ --- *
1415  *
1416  * Arguments:   @struct tty_caps *t@ = extended control block pointer
1417  *              @const char *cap1, *capn@ = capability strings for single and
1418  *                      multiple operations
1419  *              @unsigned f@ = flags
1420  *              @unsigned npad@ = number of lines affected
1421  *              @unsigned n@ = number of operations to do
1422  *
1423  * Returns:     Zero on success, %$-1$% on error.
1424  *
1425  * Use:         Sends control codes to do some operation @n@ times.
1426  *
1427  *              The capability string @cap1@ should do the operation once,
1428  *              while @capn@ should do it some number of times as requested
1429  *              by an argument.  The single-operation cap is likely better
1430  *              for a single use.  Either or both capabilities might be
1431  *              missing.
1432  */
1433
1434 #define CIF_PADMUL 1u
1435 static int caps_iterate(struct tty_caps *t,
1436                         const char *cap1, const char *capn,
1437                         unsigned f, unsigned npad, unsigned n)
1438 {
1439   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1440   unsigned max, nn;
1441   int rc;
1442
1443   if (cap1 && (n == 1 || !capn))
1444     while (n--) PUT0V(npad, cap1);
1445   else {
1446     max = npad && (f&CIF_PADMUL) ? INT_MAX/npad : INT_MAX;
1447     while (n) {
1448       nn = n; if (nn > max) nn = max;
1449       PUT1IV(npad, capn, nn);
1450       n -= nn;
1451     }
1452   }
1453   rc = 0;
1454 end:
1455   return (rc);
1456 }
1457
1458 /* --- @caps_setcolour@ --- *
1459  *
1460  * Arguments:   @struct tty_caps *t@ = extended control block pointer
1461  *              @const char *cap@ = capability string to apply (typically
1462  *                      %|setaf|% or %|setab|%)
1463  *              @uint32 spc, clr@ = the colour space and number
1464  *
1465  * Returns:     Zero on success, %$-1$% on error.
1466  *
1467  * Use:         Emit the correct control string to set the foreground or
1468  *              background colour as indicated.
1469  */
1470
1471 static int caps_setcolour(struct tty_caps *t,
1472                           const char *cap, uint32 spc, uint32 clr)
1473 {
1474   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1475   int rc;
1476
1477   switch (spc) {
1478     case TTCSPC_1BPC: case TTCSPC_1BPCBR: PUT1IV(0, cap, clr);       break;
1479     case TTCSPC_4LPC: case TTCSPC_6LPC:   PUT1IV(0, cap, clr +  16); break;
1480     case TTCSPC_8LGS:                     PUT1IV(0, cap, clr +  80); break;
1481     case TTCSPC_24LGS:                    PUT1IV(0, cap, clr + 232); break;
1482
1483     case TTCSPC_8BPC:
1484       /* There's an unfortunate ambiguity in the %|setaf|% conventions.  The
1485        * first eight colours should be dark shades of blue, but in fact
1486        * they're interpreted as the one-bit-per-channel basic colours by
1487        * common `terminfo' settings.  Notice and kludge by adding a little
1488        * red.  This will tinge the colour magenta, but all such colours are
1489        * so dark as to be effectively black anyway, so I doubt that this will
1490        * be noticeable.
1491        */
1492       if (spc == TTCSPC_8BPC && clr < 8) clr += 65536;
1493       PUT1IV(0, cap, clr); break;
1494
1495     /* case TTCSPC_NONE: */
1496     default: rc = -1; goto end;
1497   }
1498   rc = 0;
1499 end:
1500   return (rc);
1501 }
1502
1503 /* --- @caps_setattr_internal@ --- *
1504  *
1505  * Arguments:   @struct tty_caps *t@ = extended control block pointer
1506  *              @const struct tty_attr *a@ = attribute to set, already
1507  *                      clamped
1508  *
1509  * Returns:     Zero on success, %$-1$% on error.
1510  *
1511  * Use:         Internal version of @caps_setattr@.  Assumes that @prepout@
1512  *              has already been called, and that @flush@ will be called
1513  *              later.
1514  *
1515  *              This is split out from @caps_setattr@ because it's used
1516  *              (e.g., by @caps_move@) to restore the capabilities after
1517  *              moving the cursor on a terminal which doesn't advertise
1518  *              %|msgr|%.
1519  */
1520
1521 static int caps_setattr_internal(struct tty_caps *t,
1522                                  const struct tty_attr *a)
1523 {
1524   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1525   uint32 diff, m;
1526   int rc;
1527   unsigned c, z;
1528
1529   /* Work out what, if anything, needs doing. */
1530   diff = a->f ^ t->tty.st.attr.f;
1531
1532   /* Form a basic strategy.
1533    *
1534    * In the general case, we're applying some attributes (say bold face) and
1535    * cancelling others (say italics).  We likely have a big club for the
1536    * latter in the form of the %|sgr0|% capability, which cancels all
1537    * attributes and is likely fairly cheap.  On the other hand, some -- but,
1538    * infuriatingly, not all -- attributes have a cap string for cancelling
1539    * them, which may or may not be present.
1540    *
1541    * Hence, if we're holding over a lot of attributes from the previous
1542    * state, and cancelling just a small number, then it's likely better to
1543    * just cancel the individual attributes which need it, because otherwise
1544    * we'd have to reapply all of the ones we didn't actually mean to change.
1545    *
1546    * Add up the costs of each approach and use the cheaper one.  Of course,
1547    * if we're forced into one or the other, then we have no choice.
1548    *
1549    * One point worth noting is that we don't make use of the general %|sgr|%
1550    * capability here.  For one thing, %|sgr|% takes nine arguments -- so it
1551    * can't be used from `termcap'.  But, more importantly, we don't know, in
1552    * any particular case, what it does to other attributes.
1553    */
1554   if (t->cap.sgr0) {
1555
1556     /* First, add up the costs of cancelling the individual attributes which
1557      * are no longer wanted.  If we find that we can't cancel one, then skip
1558      * ahead.
1559      */
1560     c = 0;
1561 #define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
1562 #define ADDCOST(cap_) do {                                              \
1563     if (t->cap.cap_) c += t->cap.cap_##_cost;                           \
1564     else goto sgr0;                                                     \
1565 } while (0)
1566     if (CLEARP(TTAF_LNMASK)) ADDCOST(rmul);
1567     if (CLEARP(TTAF_WTMASK)) goto sgr0;
1568     if (diff&~a->f&TTAF_INVV) goto sgr0;
1569     if (diff&~a->f&TTAF_ITAL) ADDCOST(ritm);
1570     if (CLEARP(TTAF_FGSPCMASK) || CLEARP(TTAF_BGSPCMASK)) ADDCOST(op);
1571 #undef CLEARP
1572 #undef ADDCOST
1573
1574     /* Now, add up the costs of reapplying all of the attributes which aren't
1575      * supposed to change.  (Attributes which are changing don't count here
1576      * because we'll have to fiddle with them anyway.)
1577      */
1578     z = 0;
1579     if (!(diff&TTAF_LNMASK))
1580       switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1581         case TTLN_ULINE: z += t->cap.smul_cost; break;
1582       }
1583     if (!(diff&TTAF_WTMASK))
1584       switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1585         case TTWT_BOLD: z += t->cap.bold_cost; break;
1586         case TTWT_DIM: z += t->cap.dim_cost; break;
1587       }
1588     m = a->f&~diff;
1589     if (m&TTAF_INVV) z += t->cap.rev_cost;
1590     if (m&TTAF_ITAL) z += t->cap.sitm_cost;
1591 #define COLOURCOST(G, g, cap_) do {                                     \
1592     if (!(diff&TTAF_##G##SPCMASK) &&                                    \
1593         (a->f&TTAF_##G##SPCMASK) && a->g == t->tty.st.attr.g)           \
1594       z += t->cap.cap_##_cost;                                          \
1595 } while (0)
1596     COLOURCOST(FG, fg, setaf);
1597     COLOURCOST(BG, bg, setab);
1598 #undef COLOURCOST
1599
1600     if (z + t->cap.sgr0_cost < c) {
1601     sgr0:
1602       /* We've decided to clear everything and start over. */
1603
1604       PUT0(0, sgr0); diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
1605     }
1606   }
1607
1608   /* Line style. */
1609   if (diff&TTAF_LNMASK)
1610     switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1611       case TTLN_NONE: PUT0(0, rmul); break;
1612       case TTLN_ULINE: PUT0(0, smul); break;
1613       /* case TTLN_UULINE: */
1614       /* case TTLN_STRIKE: */
1615       default: rc = -1; goto end;
1616     }
1617
1618   /* Text weight. */
1619   if (diff&TTAF_WTMASK)
1620     switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1621       /* case TTWT_MED: */
1622       case TTWT_BOLD: PUT0(0, bold); break;
1623       case TTWT_DIM: PUT0(0, dim); break;
1624       default: rc = -1; goto end;
1625     }
1626
1627   /* Other text effects. */
1628   if (diff&a->f&TTAF_INVV) PUT0(0, rev);
1629   if (diff&TTAF_ITAL) {
1630     if (a->f&TTAF_ITAL) PUT0(0, sitm);
1631     else PUT0(0, ritm);
1632   }
1633
1634   /* Colours. */
1635   if (((diff&TTAF_FGSPCMASK) && !(a->f&TTAF_FGSPCMASK)) ||
1636       ((diff&TTAF_BGSPCMASK) && !(a->f&TTAF_BGSPCMASK))) {
1637     /* There's no capability string for resetting just the foreground
1638      * or background colours to the defaults, so deal with that here.
1639      */
1640
1641     PUT0(0, op);
1642     diff = (diff&~(TTAF_FGSPCMASK | TTAF_BGSPCMASK)) |
1643            (a->f&(TTAF_FGSPCMASK | TTAF_BGSPCMASK));
1644   }
1645   if ((diff&TTAF_FGSPCMASK) || a->fg != t->tty.st.attr.fg)
1646     CHECK(caps_setcolour(t, t->cap.setaf,
1647                          (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
1648   if ((diff&TTAF_BGSPCMASK) || a->bg != t->tty.st.attr.bg)
1649     CHECK(caps_setcolour(t, t->cap.setab,
1650                          (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
1651
1652   /* All done. */
1653   rc = 0;
1654 end:
1655   t->tty.st.attr = *a; return (rc);
1656
1657 #undef f_clrall
1658 }
1659
1660 /* --- @caps_setattr@ --- *
1661  *
1662  * Arguments:   @struct tty *tty@ = control block pointer
1663  *              @const struct gprintf_ops *gops, void *go@ = output
1664  *                      destination
1665  *              @const struct tty_attr *a@ = attribute to set, already
1666  *                      clamped
1667  *
1668  * Returns:     Zero on success, %$-1$% on error.
1669  *
1670  * Use:         Arrange to display future characters with the display
1671  *              attributes indicated by @a@.
1672  */
1673
1674 static int caps_setattr(struct tty *tty,
1675                         const struct gprintf_ops *gops, void *go,
1676                         const struct tty_attr *a)
1677 {
1678   struct tty_caps *t = (struct tty_caps *)tty;
1679   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1680   int rc;
1681
1682   /* Hand off to @caps_setattr_internal@. */
1683   ops->cap.prepout(&t->tty, gops, go);
1684   rc = caps_setattr_internal(t, a);
1685   if (ops->cap.flush(&t->tty)) rc = -1;
1686   return (rc);
1687 }
1688
1689 /* --- @caps_setmodes@ --- *
1690  *
1691  * Arguments:   @struct tty *tty@ = control block pointer
1692  *              @const struct gprintf_ops *gops, void *go@ = output
1693  *                      destination
1694  *              @uint32 modes_bic, modes_xor@ = masks to apply to the modes
1695  *                      settings
1696  *
1697  * Returns:     Zero on success, %$-1$% on error.
1698  *
1699  * Use:         Set the requested terminal modes.
1700  */
1701
1702 static int caps_setmodes(struct tty *tty,
1703                          const struct gprintf_ops *gops, void *go,
1704                          uint32 modes_bic, uint32 modes_xor)
1705 {
1706   struct tty_caps *t = (struct tty_caps *)tty;
1707   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1708   uint32 modes, diff;
1709   const char *cap;
1710   int rc;
1711
1712   /* Figure out which modes to set. */
1713   modes = (t->tty.st.modes&~modes_bic) ^ modes_xor;
1714   diff = modes ^ t->tty.st.modes;
1715
1716   /* Prepare output. */
1717   ops->cap.prepout(&t->tty, gops, go);
1718
1719   /* Automatic margins. */
1720   if (diff&TTMF_AUTOM) {
1721     if (modes&TTMF_AUTOM) PUT0(0, smam);
1722     else PUT0(0, rmam);
1723   }
1724
1725   /* Full-screen. */
1726   if (diff&TTMF_FSCRN) {
1727     if (modes&TTMF_FSCRN) PUT0(0, smcup);
1728     else PUT0(0, rmcup);
1729   }
1730
1731   /* Cursor visibility. */
1732   if (diff&TTMF_CVIS) {
1733     if (modes&TTMF_CVIS) PUT0(0, civis);
1734     else PUT0(0, cnorm);
1735   }
1736
1737   /* Auto-insert. */
1738   if (diff&TTMF_INS) {
1739     cap = modes&TTMF_INS ? t->cap.smir : t->cap.rmir;
1740     if (cap) PUT0V(0, cap);
1741     else if (!t->cap.ich) { rc = -1; goto end; }
1742   }
1743
1744   /* Delete characters. */
1745   if (diff&TTMF_DEL) {
1746     cap = modes&TTMF_DEL ? t->cap.smdc : t->cap.rmdc;
1747     if (cap) PUT0V(0, cap);
1748     else if (!t->cap.dch && !t->cap.dch1) { rc = -1; goto end; }
1749   }
1750
1751   /* Done. */
1752   rc = 0;
1753 end:
1754   if (ops->cap.flush(&t->tty)) rc = -1;
1755   t->tty.st.modes = modes; return (rc);
1756 }
1757
1758 /* --- @caps_move_relative@ --- *
1759  *
1760  * Arguments:   @struct tty_caps *t@ = extended control block pointer
1761  *              @int delta@ = number of places to move
1762  *              @const char *fw1, *fwn, *rv1, *rvn@ = capability strings for
1763  *                      to move by a single or multiple places, in the
1764  *                      forward (positive) or backwards (negative) direction
1765  *
1766  * Returns:     Zero on success, %$-1$% on error.
1767  *
1768  * Use:         Perform a relative motion, making the most of the supplied
1769  *              capabilities.
1770  */
1771
1772 static int caps_move_relative(struct tty_caps *t,
1773                               int delta,
1774                               const char *fw1, const char *fwn,
1775                               const char *rv1, const char *rvn)
1776 {
1777   const char *mv1, *mvn;
1778
1779   if (!delta) return (0);
1780   else if (delta > 0) { mv1 = fw1; mvn = fwn; }
1781   else { mv1 = rv1; mvn = rvn; delta = - delta; }
1782   return (caps_iterate(t, mv1, mvn, 0, 0, delta));
1783 }
1784
1785 /* --- @caps_move_absx@ --- *
1786  *
1787  * Arguments:   @struct tty_caps *t@ = extended control block pointer
1788  *              @int x@ = column to move the cursor to
1789  *
1790  * Returns:     Zero on success, %$-1$% on error.
1791  *
1792  * Use:         Move the cursor to the given column in the current line.
1793  *              This is possible even if we have only relative motion
1794  *              because we can use the carriage return.
1795  */
1796
1797 static int caps_move_absx(struct tty_caps *t, int x)
1798 {
1799   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1800   int rc;
1801
1802   if (t->cap.hpa)
1803     PUT1I(1, hpa, x);
1804   else {
1805     PUT0(1, cr);
1806     CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1807   }
1808   rc = 0;
1809 end:
1810   return (rc);
1811 }
1812
1813 /* --- @caps_move@ --- *
1814  *
1815  * Arguments:   @struct tty *tty@ = control block pointer
1816  *              @const struct gprintf_ops *gops, void *go@ = output
1817  *                      destination
1818  *              @unsigned orig@ = origin
1819  *              @int y, x@ = new cursor position
1820  *
1821  * Returns:     Zero on success, %$-1$% on error.
1822  *
1823  * Use:         Move the cursor.
1824  */
1825
1826 static int caps_move(struct tty *tty,
1827                      const struct gprintf_ops *gops, void *go,
1828                      unsigned orig, int y, int x)
1829 {
1830   struct tty_caps *t = (struct tty_caps *)tty;
1831   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1832   struct tty_attr a, aa = TTY_ATTR_INIT;
1833   int rc;
1834
1835   /* Check that the arguments are basically sensible. */
1836   if ((!(orig&TTOF_YCUR) && y < 0) || (!(orig&TTOF_XCUR) && x < 0))
1837     { rc = -1; goto end; }
1838
1839   /* Prepare for output. */
1840   ops->cap.prepout(&t->tty, gops, go);
1841
1842   /* If it's unsafe to move with insert mode on, then turn it off. */
1843   if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, rmir);
1844
1845   /* If it's unsafe to move with attributes set then make a copy of them and
1846    * clear them all.
1847    */
1848   a = t->tty.st.attr;
1849   if (!t->cap.msgr && a.f) {
1850     if (t->cap.sgr0) PUT0(0, sgr0);
1851     else CHECK(caps_setattr_internal(t, &aa));
1852     t->tty.st.attr.f = 0; t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
1853   }
1854
1855   /* Main dispatch. */
1856   switch (orig) {
1857     case TTORG_HOME:
1858       /* Fully absolute movement.
1859        *
1860        * Moving to the home position is likely easy and fast.  Use general
1861        * cursor positioning if available.  Otherwise, try to set the vertical
1862        * and horizontal positions separately.  If all that fails, move to the
1863        * home position and use relative motion.
1864        */
1865
1866       if (t->cap.home && !x && !y)
1867         PUT0(1, home);
1868       else if (t->cap.cup)
1869         PUT2I(1, cup, y, x);
1870       else if (t->cap.vpa) {
1871         PUT1I(1, vpa, y);
1872         CHECK(caps_move_absx(t, x));
1873       } else if (t->cap.home) {
1874         PUT0(1, home);
1875         CHECK(caps_iterate(t, t->cap.cud1, t->cap.cud, 0, 1, y));
1876         CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1877       } else
1878         { rc = -1; goto end; }
1879       break;
1880
1881     case TTORG_CUR:
1882       /* Fully relative movement.  This is easy. */
1883
1884       CHECK(caps_move_relative(t, y,
1885                                t->cap.cud1, t->cap.cud,
1886                                t->cap.cuu1, t->cap.cuu));
1887       CHECK(caps_move_relative(t, x,
1888                                t->cap.cuf1, t->cap.cuf,
1889                                t->cap.cub1, t->cap.cub));
1890       break;
1891
1892     case TTOF_XHOME | TTOF_YCUR:
1893       /* Absolute horizontal movement, with relative vertical movement.
1894        *
1895        * If we want to move to the start of the next line, this is a
1896        * `newline' operation.  Otherwise, use two separate motions.
1897        */
1898
1899       if (x == 0 && y == 1)
1900         PUT0(1, nel);
1901       else {
1902         CHECK(caps_move_relative(t, y,
1903                                  t->cap.cud1, t->cap.cud,
1904                                  t->cap.cuu1, t->cap.cuu));
1905         CHECK(caps_move_absx(t, x));
1906       }
1907       break;
1908
1909     case TTOF_XCUR | TTOF_YHOME:
1910       /* Relative horizontal movement, with absolute vertical movement.
1911        *
1912        * The only thing to try is two separate motions.
1913        */
1914
1915       PUT1I(1, vpa, y);
1916       CHECK(caps_move_relative(t, x,
1917                                t->cap.cuf1, t->cap.cuf,
1918                                t->cap.cub1, t->cap.cub));
1919       break;
1920
1921     default:
1922       /* Anything else was an error. */
1923
1924       rc = -1; goto end;
1925       break;
1926   }
1927
1928   /* If we turned off insert mode, we can turn it back on now. */
1929   if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, smir);
1930
1931   /* And if we turned off any attributes, we can turn them back on. */
1932   if (!t->cap.msgr && a.f)
1933     CHECK(caps_setattr_internal(t, &a));
1934
1935   /* All done. */
1936   rc = 0;
1937 end:
1938   if (ops->cap.flush(&t->tty)) rc = -1;
1939   return (rc);
1940 }
1941
1942 /* --- @caps_repeat@ --- *
1943  *
1944  * Arguments:   @struct tty *tty@ = control block pointer
1945  *              @const struct gprintf_ops *gops, void *go@ = output
1946  *                      destination
1947  *              @int ch@ = character to write
1948  *              @unsigned n@ = number of copies
1949  *
1950  * Returns:     Zero on success, %$-1$% on error.
1951  *
1952  * Use:         Write @n@ copies of the character @ch@ to the terminal.
1953  */
1954
1955 static int caps_repeat(struct tty *tty,
1956                        const struct gprintf_ops *gops, void *go,
1957                        int ch, unsigned n)
1958 {
1959   struct tty_caps *t = (struct tty_caps *)tty;
1960   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1961   unsigned wd, nn;
1962   int rc;
1963
1964   /* Prepare for output. */
1965   ops->cap.prepout(&t->tty, gops, go);
1966
1967   /* If there isn't a capability for this then do it the stupid way. */
1968   if (!t->cap.rep)
1969     CHECK(stupid_repeat(tty, gops, go, ch, n));
1970   else {
1971     wd = t->tty.wd;
1972     while (n) {
1973       nn = n; if (nn > INT_MAX) nn = INT_MAX;
1974       PUT2I((nn + wd - 1)/wd, rep, ch, nn);
1975       n -= nn;
1976     }
1977   }
1978
1979   /* Done. */
1980   rc = 0;
1981 end:
1982   if (ops->cap.flush(&t->tty)) rc = -1;
1983   return (rc);
1984 }
1985
1986 /* --- @caps_erase@ --- *
1987  *
1988  * Arguments:   @struct tty *tty@ = control block pointer
1989  *              @const struct gprintf_ops *gops, void *go@ = output
1990  *                      destination
1991  *              @unsigned f@ = flags
1992  *
1993  * Returns:     Zero on success, %$-1$% on error.
1994  *
1995  * Use:         Erase portions of the current line or the whole display.
1996  */
1997
1998 static int caps_erase(struct tty *tty,
1999                       const struct gprintf_ops *gops, void *go,
2000                       unsigned f)
2001 {
2002   struct tty_caps *t = (struct tty_caps *)tty;
2003   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2004   int rc;
2005
2006   /* Prepare for output. */
2007   ops->cap.prepout(&t->tty, gops, go);
2008
2009   if (!(f&TTEF_DSP)) {
2010     /* Erase line.  This is easy. */
2011
2012     if (f&TTEF_BEGIN) PUT0(1, el1);
2013     if (f&TTEF_END) PUT0(1, el);
2014   } else {
2015     /* Erase display.  Note that `terminfo' lacks a capability for `erase
2016      * from start of display'.
2017      */
2018
2019     switch (f&(TTEF_BEGIN | TTEF_END)) {
2020       case 0:
2021         /* Nothing to do. */
2022
2023         break;
2024
2025       case TTEF_BEGIN | TTEF_END:
2026         /* Erase the whole display.  If we have a `clear', then use it;
2027          * otherwise, synthesize it from `home' and `erase to end'.
2028          */
2029
2030         if (t->cap.clear) PUT0(t->tty.ht, clear);
2031         else { PUT0(1, home); PUT0(t->tty.ht, ed); }
2032         break;
2033
2034       case TTEF_END:
2035         /* Erase to the end of the display. */
2036
2037         PUT0(t->tty.ht, ed);
2038         break;
2039
2040       default:
2041         /* Anything else is wrong. */
2042
2043         rc = -1; goto end;
2044         break;
2045     }
2046   }
2047
2048   /* Done. */
2049   rc = 0;
2050 end:
2051   if (ops->cap.flush(&t->tty)) rc = -1;
2052   return (rc);
2053 }
2054
2055 /* --- @caps_erch@ --- *
2056  *
2057  * Arguments:   @struct tty *tty@ = control block pointer
2058  *              @const struct gprintf_ops *gops, void *go@ = output
2059  *                      destination
2060  *              @unsigned n@ = number of characters to erase
2061  *
2062  * Returns:     Zero on success, %$-1$% on error.
2063  *
2064  * Use:         Erase a number of characters, starting from and including the
2065  *              current cursor position.
2066  */
2067
2068 static int caps_erch(struct tty *tty,
2069                      const struct gprintf_ops *gops, void *go,
2070                      unsigned n)
2071 {
2072   struct tty_caps *t = (struct tty_caps *)tty;
2073   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2074   int rc;
2075
2076   /* Prepare for output. */
2077   ops->cap.prepout(&t->tty, gops, go);
2078
2079   /* Produce the control sequence. */
2080   if (n) PUT1I(1, ech, n);
2081
2082   /* Done. */
2083   rc = 0;
2084 end:
2085   if (ops->cap.flush(&t->tty)) rc = -1;
2086   return (rc);
2087 }
2088
2089 /* --- @caps_ins@ --- *
2090  *
2091  * Arguments:   @struct tty *tty@ = control block pointer
2092  *              @const struct gprintf_ops *gops, void *go@ = output
2093  *                      destination
2094  *              @unsigned f@ = flags
2095  *              @unsigned n@ = number of items to insert
2096  *
2097  * Returns:     Zero on success, %$-1$% on error.
2098  *
2099  * Use:         Insert a number of blank characters or lines.
2100  */
2101
2102 static int caps_ins(struct tty *tty,
2103                     const struct gprintf_ops *gops, void *go,
2104                     unsigned f, unsigned n)
2105 {
2106   struct tty_caps *t = (struct tty_caps *)tty;
2107   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2108   int rc;
2109
2110   /* Prepare for output. */
2111   ops->cap.prepout(&t->tty, gops, go);
2112
2113   /* Produce the control sequence. */
2114   if (f&TTIDF_LN)
2115     CHECK(caps_iterate(t, t->cap.il1, t->cap.il, CIF_PADMUL, 1, n));
2116   else
2117     CHECK(caps_iterate(t, t->cap.ich1, t->cap.ich, 0, 1, n));
2118
2119   /* Done. */
2120   rc = 0;
2121 end:
2122   if (ops->cap.flush(&t->tty)) rc = -1;
2123   return (rc);
2124 }
2125
2126 /* --- @caps_inch@ --- *
2127  *
2128  * Arguments:   @struct tty *tty@ = control block pointer
2129  *              @const struct gprintf_ops *gops, void *go@ = output
2130  *                      destination
2131  *              @int ch@ = character to insert
2132  *
2133  * Returns:     Zero on success, %$-1$% on error.
2134  *
2135  * Use:         Insert a single character.
2136  */
2137
2138 static int caps_inch(struct tty *tty,
2139                      const struct gprintf_ops *gops, void *go,
2140                      int ch)
2141 {
2142   struct tty_caps *t = (struct tty_caps *)tty;
2143   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2144   int rc;
2145
2146   /* Prepare for output. */
2147   ops->cap.prepout(&t->tty, gops, go);
2148
2149   /* If the terminal has an insert mode, but it's not turned on, then this
2150    * isn't going to work.
2151    */
2152   if (t->cap.smir ? !(t->tty.st.modes&TTMF_INS) : !t->cap.ich)
2153     { rc = -1; goto end; }
2154
2155   /* If there's an insert-one-character sequence, then send that. */
2156   if (t->cap.ich) PUT0(1, ich);
2157
2158   /* Put the actual character. */
2159   CHECK(gops->putch(go, ch));
2160
2161   /* Some terminals need post-insertion padding, which turns up here. */
2162   if (t->cap.ip) PUT0(1, ip);
2163
2164   /* Done. */
2165   rc = 0;
2166 end:
2167   if (ops->cap.flush(&t->tty)) rc = -1;
2168   return (rc);
2169 }
2170
2171 /* --- @caps_del@ --- *
2172  *
2173  * Arguments:   @struct tty *tty@ = control block pointer
2174  *              @const struct gprintf_ops *gops, void *go@ = output
2175  *                      destination
2176  *              @unsigned f@ = flags
2177  *              @unsigned n@ = number of items to delete
2178  *
2179  * Returns:     Zero on success, %$-1$% on error.
2180  *
2181  * Use:         Delete a number of characters or lines.
2182  */
2183
2184 static int caps_del(struct tty *tty,
2185                     const struct gprintf_ops *gops, void *go,
2186                     unsigned f, unsigned n)
2187 {
2188   struct tty_caps *t = (struct tty_caps *)tty;
2189   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2190   int rc;
2191
2192   /* Prepare for output. */
2193   ops->cap.prepout(&t->tty, gops, go);
2194
2195   /* Send the control sequence. */
2196   if (n) {
2197     if (f&TTIDF_LN)
2198       CHECK(caps_iterate(t, t->cap.dl1, t->cap.dl, CIF_PADMUL, 1, n));
2199     else
2200       CHECK(caps_iterate(t, t->cap.dch1, t->cap.dch, 0, 1, n));
2201   }
2202
2203   /* Done. */
2204   rc = 0;
2205 end:
2206   if (ops->cap.flush(&t->tty)) rc = -1;
2207   return (rc);
2208 }
2209
2210 #undef PUT0V
2211 #undef PUT1IV
2212 #undef PUT2IV
2213 #undef PUT0
2214 #undef PUT1I
2215 #undef PUT2I
2216
2217 /* The operation functions for terminal backends based on `terminfo'. */
2218 #define TTY_CAPOPS                                                      \
2219   caps_setattr, caps_setmodes,                                          \
2220   caps_move, caps_repeat,                                               \
2221   caps_erase, caps_erch, caps_ins, caps_inch, caps_del
2222
2223 #endif
2224
2225 /*----- Termcap -----------------------------------------------------------*/
2226
2227 #ifdef HAVE_TERMCAP
2228
2229 /* So `termcap' is pretty bad, actually.  It reads the terminal description
2230  * from a file into a caller-provided buffer without knowing how big the
2231  * buffer is.  The @tgetstr@ function partially decodes a string capability
2232  * into another caller-provided buffer with no bounds checking.
2233  *
2234  * And then there's the way that pieces of `termcap' communicate with each
2235  * other through global variables which the caller has to set for it.
2236  *
2237  * Finally, while output can be redirected under application control, output
2238  * is strictly one-character-at-a-time, and there's no way to pass additional
2239  * context information to the character-output function, so we need yet more
2240  * global state.
2241  *
2242  * I'm not a fan.
2243  */
2244
2245 /* Additional state required by `termcap'. */
2246 struct tty_termcapslots {
2247   char capbuf[4096], *capcur;           /* string caps, and cursor */
2248 };
2249 struct tty_termcap { TTY_CAPSPFX; struct tty_termcapslots tc; };
2250 union tty_termcapu { struct tty_termcap tc; TTY_CAPSUSFX; };
2251
2252 /* --- @termcap_boolcap@, @termcap_intcap@, @termcap_strcap@ --- *
2253  *
2254  * Arguments:   @struct tty *tty@ = control block pointer
2255  *              @int uix, const char *info, const char *cap@ = names for the
2256  *                      requested capability (as Unibilium index, and
2257  *                      `terminfo' and `termcap' names
2258  *
2259  * Returns:     The requested capability value.  On failure,
2260  *              @termcap_boolcap@ returns false, @termcap_intcap@ returns
2261  *              %$-1$%, and @termcap_strcap@ returns a null pointer.
2262  *
2263  * Use:         Return a requested boolean, integer, or string capability.
2264  */
2265
2266 static int termcap_boolcap(struct tty *tty,
2267                            int uix, const char *info, const char *cap)
2268 {
2269   int p;
2270
2271   p = tgetflag(cap); assert(p >= 0);
2272   return (p);
2273 }
2274
2275 static int termcap_intcap(struct tty *tty,
2276                           int uix, const char *info, const char *cap)
2277 {
2278   int n;
2279
2280   n = tgetnum(cap); assert(n >= -1);
2281   return (n);
2282 }
2283
2284 static const char *termcap_strcap(struct tty *tty,
2285                                   int uix, const char *info,
2286                                   const char *cap)
2287 {
2288   struct tty_termcap *t = (struct tty_termcap *)tty;
2289   const char *p;
2290   size_t n;
2291
2292   /* Try to detect overruns.  This is gnarly because C and `termcap'.  We're
2293    * not allowed to compare out-of-bounds pointers.  The best I can manage is
2294    * to assume that @capcur@ is maintained within @capbuf@, and check that
2295    * the length of the new capability didn't push us over the limit.  Things
2296    * are even harder because, e.g., `ncurses' doesn't actually make use of
2297    * our @capbuf@ at all.
2298    *
2299    * (Of course, if it did, it's probably too late to expect anyhing good,
2300    * but maybe we can apply the emergency brakes before things get too bad.)
2301    */
2302   n = t->tc.capcur - t->tc.capbuf;
2303   p = tgetstr(cap, &t->tc.capcur);
2304     assert(p != (const char *)-1);
2305     assert(!p || n + strlen(p) < sizeof(t->tc.capbuf));
2306   return (p);
2307 }
2308
2309 /* --- @termcap_put0@, @termcap_put1i@, @termcap_put2i@ --- *
2310  *
2311  * Arguments:   @struct tty *tty@ = control block pointer
2312  *              @unsigned npad@ = number of lines affected, for padding
2313  *              @const char *cap@ = capability string to write, or null
2314  *              @int i0, i1@ = integer parameters
2315  *
2316  * Returns:     Zero on success, %$-1$% on error.
2317  *
2318  * Use:         Format a capability string and send it to the terminal.
2319  */
2320
2321 static int termcap_put0(struct tty *tty,
2322                         unsigned npad, const char *cap)
2323 {
2324   if (!cap) return (-1);
2325   return (tputs(cap, npad, caps_putch));
2326 }
2327
2328 static int termcap_put1i(struct tty *tty,
2329                          unsigned npad, const char *cap, int i0)
2330 {
2331   if (!cap) return (-1);
2332   return (tputs(tgoto(cap, -1, i0), npad, caps_putch) == OK ? 0 : -1);
2333 }
2334
2335 static int termcap_put2i(struct tty *tty,
2336                          unsigned npad, const char *cap, int i0, int i1)
2337 {
2338   if (!cap) return (-1);
2339   return (tputs(tgoto(cap, i1, i0), npad, caps_putch) == OK ? 0 : -1);
2340 }
2341
2342 /* --- @termcap_cost@ --- *
2343  *
2344  * Arguments:   @struct tty *tty@ = control block pointer
2345  *              @unsigned npad@ = number of lines affected
2346  *              @const char *cap@ = capability string to measure, or null
2347  *              @int i0@ = argument
2348  *
2349  * Returns:     A linear cost for using the capability.  This is meaningless
2350  *              if @cap@ is null.
2351  */
2352
2353 static size_t termcap_cost(struct tty *tty,
2354                            unsigned npad, const char *cap, int i0)
2355   { return (cap ? caps_cost(tty, npad, tgoto(cap, 0, i0)) : 0); }
2356
2357 /* The `termcap' operations table. */
2358 static const union tty_capopsu termcap_ops = { {
2359   { caps_release, TTY_CAPOPS },
2360   { termcap_boolcap, termcap_intcap, termcap_strcap,
2361     caps_prepout, caps_flush,
2362     termcap_put0, termcap_put1i, termcap_put2i,
2363     termcap_cost }
2364 } };
2365
2366 /* --- @termcap_init@ --- *
2367  *
2368  * Arguments:   @FILE *fp@ = the output stream
2369  *
2370  * Returns:     A pointer to a fresh terminal control block, or null on
2371  *              failure.
2372  *
2373  * Use:         Initialize a terminal for use through `termcap'.
2374  */
2375
2376 static struct tty *termcap_init(FILE *fp)
2377 {
2378   union tty_termcapu *u = 0; struct tty *ret = 0;
2379   char termbuf[4096];
2380   const char *term;
2381   speed_t spd = B0;
2382   char *t;
2383
2384   /* The `termcap' library makes use of global state, so we can only have one
2385    * at a time.  Traditional `termcap' is actually less bad in this sense,
2386    * but the `ncurses' emulation is tangled up with its `terminfo' machinery,
2387    * which is backed by a large amount of global state.  So check that there
2388    * aren't likely to be conflicts.
2389    */
2390   if (caps_claim()) goto end;
2391
2392   /* Get the terminal name and try to find the terminal description. */
2393   term = getenv("TERM"); if (!term) goto end;
2394   if (tgetent(termbuf, term) < 1) goto end;
2395
2396   /* Set up a fresh control block. */
2397   XNEW(u);
2398   u->tc.tc.capcur = u->tc.tc.capbuf;
2399   u->tty.ops = &termcap_ops.tty;
2400   common_init(&u->tty, fp, &spd);
2401   init_caps(&u->cap);
2402
2403   /* Set the `termcap' global variables. */
2404   t = tgetstr("bc", &u->tc.tc.capcur); BC = t ? t : "\b";
2405   UP = UNCONST(char, u->cap.cap.cuu1);
2406   PC = u->cap.cap.pad ? *u->cap.cap.pad : 0;
2407   ospeed = spd;
2408
2409   /* All done. */
2410   ret = &u->tty; u = 0;
2411 end:
2412   xfree(u); global_lock = ret; return (ret);
2413 }
2414
2415 #endif
2416
2417 /*----- Terminfo ----------------------------------------------------------*/
2418
2419 #ifdef HAVE_TERMINFO
2420
2421 /* So `terminfo' is pretty bad too, actually.  The good news is that
2422  * `termcap''s potential buffer overruns are gone.  The bad news is that
2423  * `terminfo' goes all in on global state.  Rather than being put in a
2424  * caller-provided buffer, the terminal description is read into global
2425  * state.
2426  *
2427  * Annoyingly, `terminfo' adopts the same `tputs' machinery as `termcap', and
2428  * therefore shares the one-character-at-a-time output and inability to pass
2429  * contextual information through to the ouptut function.
2430  *
2431  * I'm not a fan of `terminfo' either.
2432  */
2433
2434 /* --- @terminfo_boolcap@, @terminfo_intcap@, @terminfo_strcap@ --- *
2435  *
2436  * Arguments:   @struct tty *tty@ = control block pointer
2437  *              @int uix, const char *info, const char *cap@ = names for the
2438  *                      requested capability (as Unibilium index, and
2439  *                      `terminfo' and `termcap' names
2440  *
2441  * Returns:     The requested capability value.  On failure,
2442  *              @termcap_boolcap@ returns false, @termcap_intcap@ returns
2443  *              %$-1$%, and @termcap_strcap@ returns a null pointer.
2444  *
2445  * Use:         Return a requested boolean, integer, or string capability.
2446  */
2447
2448 static int terminfo_boolcap(struct tty *tty,
2449                             int uix, const char *info, const char *cap)
2450 {
2451   int p;
2452
2453   p = tigetflag(info); assert(p >= 0);
2454   return (p);
2455 }
2456
2457 static int terminfo_intcap(struct tty *tty,
2458                            int uix, const char *info, const char *cap)
2459 {
2460   int n;
2461
2462   n = tigetnum(info); assert(n >= -1);
2463   return (n);
2464 }
2465
2466 static const char *terminfo_strcap(struct tty *tty,
2467                                    int uix, const char *info,
2468                                    const char *cap)
2469 {
2470   const char *p;
2471
2472   p = tigetstr(info); assert(p != (const char *)-1);
2473   return (p);
2474 }
2475
2476 /* --- @terminfo_put0@, @terminfo_put1i@, @terminfo_put2i@ --- *
2477  *
2478  * Arguments:   @struct tty *tty@ = control block pointer
2479  *              @unsigned npad@ = number of lines affected, for padding
2480  *              @const char *cap@ = capability string to write, or null
2481  *              @int i0, i1@ = integer parameters
2482  *
2483  * Returns:     Zero on success, %$-1$% on error.
2484  *
2485  * Use:         Format a capability string and send it to the terminal.
2486  */
2487
2488 static int terminfo_put0(struct tty *tty,
2489                          unsigned npad, const char *cap)
2490 {
2491   if (!cap) return (-1);
2492   return (tputs(cap, npad, caps_putch));
2493 }
2494
2495 static int terminfo_put1i(struct tty *tty,
2496                           unsigned npad, const char *cap, int i0)
2497 {
2498   if (!cap) return (-1);
2499   return (tputs(tparm(cap, i0), npad, caps_putch) == OK ? 0 : -1);
2500 }
2501
2502 static int terminfo_put2i(struct tty *tty,
2503                           unsigned npad, const char *cap, int i0, int i1)
2504 {
2505   if (!cap) return (-1);
2506   return (tputs(tparm(cap, i0, i1), npad, caps_putch) == OK ? 0 : -1);
2507 }
2508
2509 /* --- @terminfo_cost@ --- *
2510  *
2511  * Arguments:   @struct tty *tty@ = control block pointer
2512  *              @unsigned npad@ = number of lines affected
2513  *              @const char *cap@ = capability string to measure, or null
2514  *              @int i0@ = argument
2515  *
2516  * Returns:     A linear cost for using the capability.  This is meaningless
2517  *              if @cap@ is null.
2518  */
2519
2520 static size_t terminfo_cost(struct tty *tty,
2521                             unsigned npad, const char *cap, int i0)
2522   { return (cap ? caps_cost(tty, npad, tparm(cap, i0)) : 0); }
2523
2524 /* The `terminfo' operations table. */
2525 static const union tty_capopsu terminfo_ops = { {
2526   { caps_release, TTY_CAPOPS },
2527   { terminfo_boolcap, terminfo_intcap, terminfo_strcap,
2528     caps_prepout, caps_flush,
2529     terminfo_put0, terminfo_put1i, terminfo_put2i,
2530     terminfo_cost }
2531 } };
2532
2533 /* --- @terminfo_init@ --- *
2534  *
2535  * Arguments:   @FILE *fp@ = the output stream
2536  *
2537  * Returns:     A pointer to a fresh terminal control block, or null on
2538  *              failure.
2539  *
2540  * Use:         Initialize a terminal for use through `terminfo'.
2541  */
2542
2543 static struct tty *terminfo_init(FILE *fp)
2544 {
2545   union tty_capsu *u = 0; struct tty *ret = 0;
2546   int err;
2547
2548   /* The `termcap' library makes extensive use of global state, so we can
2549    * only have one at a time.  Check that there aren't likely to be
2550    * conflicts.
2551    */
2552   if (caps_claim()) goto end;
2553
2554   /* Get the terminal description. */
2555   if (setupterm(0, fp ? fileno(fp) : -1, &err) != OK || err < 1) goto end;
2556
2557   /* Set up a fresh control block. */
2558   XNEW(u);
2559   u->tty.ops = &terminfo_ops.tty;
2560   common_init(&u->tty, fp, 0);
2561   init_caps(&u->cap);
2562
2563   /* All done. */
2564   ret = &u->tty; u = 0;
2565 end:
2566   xfree(u); global_lock = ret; return (ret);
2567 }
2568
2569 #endif
2570
2571 /*----- Unibilium ---------------------------------------------------------*/
2572
2573 #ifdef HAVE_UNIBILIUM
2574
2575 /* Unibilium is a `terminfo' library for the 21st century.  It avoids all of
2576  * the mistakes of the `termcap' and `terminfo' interfaces.  Honestly, it's a
2577  * bit too `modern' for my tastes: the type bureaucracy involved with
2578  * `unibi_var_t' seems unnecessarily heavyweight (but won't actually bother
2579  * us much here).
2580  *
2581  * It's not all sunshine and roses.  The library makes a principled decision
2582  * to avoid actually messing with terminals directly at all, which seems
2583  * fair enough -- even laudable, maybe.  But there's also no help provided to
2584  * generate padding characters, so we have to do that by ourselves, which
2585  * ends up being perhaps the most complicated part of this whole business.
2586  *
2587  * Also, while the caller-provided output functions can at least be passed a
2588  * context pointer, they can't report errors, which means that we must leave
2589  * a pending error indication.
2590  *
2591  * Oh, and the name is actually really difficult to type.
2592  */
2593
2594 /* Additional state required for Unibilium. */
2595 struct tty_unibislots {
2596   unibi_term *ut;                       /* the terminal description */
2597   unibi_var_t dy[26], st[26];           /* variables used by cap strings */
2598   const struct gprintf_ops *gops; void *go; /* output destination */
2599   char buf[BUFSZ]; size_t n;            /* output buffer and length */
2600   int err;                              /* pending error indication */
2601 };
2602 struct tty_unibilium { TTY_CAPSPFX; struct tty_unibislots u; };
2603 union tty_unibiliumu { struct tty_unibilium u; TTY_CAPSUSFX; };
2604
2605 /* --- @termunibi_release@ --- *
2606  *
2607  * Arguments:   @struct tty *tty@ = control block pointer
2608  *
2609  * Returns:     ---
2610  *
2611  * Use:         Release resources held by the control block.
2612  */
2613
2614 static void termunibi_release(struct tty *tty)
2615 {
2616   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2617
2618   unibi_destroy(t->u.ut);
2619 }
2620
2621 /* --- @termcap_boolcap@, @termcap_intcap@, @termcap_strcap@ --- *
2622  *
2623  * Arguments:   @struct tty *tty@ = control block pointer
2624  *              @int uix, const char *info, const char *cap@ = names for the
2625  *                      requested capability (as Unibilium index, and
2626  *                      `terminfo' and `termcap' names
2627  *
2628  * Returns:     The requested capability value.  On failure,
2629  *              @termcap_boolcap@ returns false, @termcap_intcap@ returns
2630  *              %$-1$%, and @termcap_strcap@ returns a null pointer.
2631  *
2632  * Use:         Return a requested boolean, integer, or string capability.
2633  */
2634
2635 static int termunibi_boolcap(struct tty *tty,
2636                             int uix, const char *info, const char *cap)
2637 {
2638   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2639
2640   return (unibi_get_bool(t->u.ut, uix));
2641 }
2642
2643 static int termunibi_intcap(struct tty *tty,
2644                            int uix, const char *info, const char *cap)
2645 {
2646   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2647
2648   return (unibi_get_num(t->u.ut, uix));
2649 }
2650
2651 static const char *termunibi_strcap(struct tty *tty,
2652                                    int uix, const char *info,
2653                                    const char *cap)
2654 {
2655   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2656
2657   return (unibi_get_str(t->u.ut, uix));
2658 }
2659
2660 /* --- @termunibi_prepout@ --- *
2661  *
2662  * Arguments:   @struct tty *tty@ = control block pointer (ignored)
2663  *              @const struct gprintf_ops *gops, void *go@ = output
2664  *                      destination
2665  *
2666  * Returns:     ---
2667  *
2668  * Use:         Prepare output to the given destination.  Check that the
2669  *              output buffer is initially empty (i.e., that it was properly
2670  *              flushed last time), and make a note of the output
2671  *              destination.
2672  */
2673
2674 static void termunibi_prepout(struct tty *tty,
2675                               const struct gprintf_ops *gops, void *go)
2676 {
2677   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2678
2679   assert(!t->u.n); t->u.gops = gops; t->u.go = go;
2680 }
2681
2682 /* Context for output operations. */
2683 struct termunibi_outctx {
2684   struct tty_unibilium *t;              /* control block pointer */
2685   unsigned npad;                        /* number of lines, for padding */
2686 };
2687
2688 /* --- @termunibi_putm@ --- *
2689  *
2690  * Arguments:   @void *ctx@ = output context pointer
2691  *              @const char *p, size_t sz@ = buffer to write
2692  *
2693  * Returns:     ---
2694  *
2695  * Use:         Send the material in the buffer to the current output
2696  *              destination.
2697  */
2698
2699 static void termunibi_putm(void *ctx, const char *p, size_t sz)
2700 {
2701   struct termunibi_outctx *out = ctx;
2702   struct tty_unibilium *t = out->t;
2703   size_t n;
2704
2705   /* Find out how much space is left in the buffer.  As for the common
2706    * `termcap'/`terminfo' machinery above, by policy, we leave the buffer
2707    * with at least one byte of space.
2708    */
2709   n = BUFSZ - t->u.n; assert(n > 0);
2710
2711   /* Save the output into the buffer. */
2712   if (sz < n) {
2713     /* There's enough space in the buffer for everything.  Just store all of
2714      * it.
2715      */
2716
2717     memcpy(t->u.buf + t->u.n, p, sz); t->u.n += sz;
2718   } else if (sz - n < THRESH) {
2719     /* We don't really have enough left over to be worth cycling the output
2720      * machinery again yet.
2721      */
2722
2723     memcpy(t->u.buf + t->u.n, p, n); p += n; sz -= n;
2724     if (t->u.gops->putm(t->u.go, t->u.buf, BUFSZ)) t->u.err = -1;
2725     memcpy(t->u.buf, p, sz); t->u.n = sz;
2726   } else {
2727     /* There's enough that it's worth avoiding the copy.  Flush whatever's in
2728      * the buffer, and inhale the entire new input in one go, leaving the
2729      * buffer empty.
2730      */
2731
2732     if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) t->u.err = -1;
2733       t->u.n = 0;
2734     if (t->u.gops->putm(t->u.go, p, sz)) t->u.err = -1;
2735   }
2736 }
2737
2738 /* --- @termunibi_pad@ --- *
2739  *
2740  * Arguments:   @void *ctx@ = control block pointer
2741  *              @size_t delay@ = tenths of milliseconds to pad
2742  *              @int mulp@ = nonzero if padding is per line
2743  *              @int forcep@ = nonzero if padding is unconditional
2744  *                      (regardless of, e.g., baud rate or flow control)
2745  *
2746  * Returns:     ---
2747  *
2748  * Use:         Pad for a given duration.
2749  */
2750
2751 static void termunibi_pad(void *ctx, size_t delay, int mulp, int forcep)
2752 {
2753   struct termunibi_outctx *out = ctx;
2754   struct tty_unibilium *t = out->t;
2755   struct timeval tv;
2756   unsigned long d;
2757   int pc;
2758   size_t sz, n;
2759
2760   /* Determine the actual delay. */
2761   if (mulp) d = (unsigned long)delay*out->npad;
2762   else d = delay;
2763
2764   /* There's nothing to do unless (a) we're forced, or (b) the baud rate is
2765    * high enough and flow control isn't advertised.
2766    */
2767   if (forcep || (t->tty.baud >= t->cap.pb && !t->cap.xon)) {
2768
2769     if (t->cap.npc) {
2770       /* There's no safe pad character to send, so we'll just have to wait
2771        * for long enough.
2772        *
2773        * If output is buffered downstream of us then this isn't going to work
2774        * correctly.  Honestly, that's a problem for the other output drivers
2775        * too, and it only comes into clear focus here.
2776        */
2777
2778       /* Flush the output. */
2779       if (t->u.n) {
2780         if (t->u.gops->putm(t->u.go, t->u.buf, BUFSZ)) t->u.err = -1;
2781         t->u.n = 0;
2782       }
2783       if (t->tty.fpout) fflush(t->tty.fpout);
2784
2785       /* Do nothing for a little while. */
2786       tv.tv_sec = d/10000; tv.tv_usec = 100*(d%10000);
2787       select(0, 0, 0, 0, &tv);
2788     } else {
2789       /* There's a pad character, so figure out how many we need and send
2790        * them.
2791        */
2792
2793       /* Determine the number of pad character and the pad value. */
2794       sz = caps_padchars(&t->tty, d, forcep ? CPF_FORCE : 0);
2795       pc = t->cap.pad ? *t->cap.pad : 0;
2796
2797       /* Work out how much buffer space is available.  Again, by policy, the
2798        * buffer is always left with at least one byte of capacity.
2799        */
2800       n = BUFSZ - t->u.n; assert(n);
2801       if (sz < n) {
2802         /* The padding will fit in the buffer. */
2803
2804         memset(t->u.buf + t->u.n, pc, sz); t->u.n += sz;
2805       } else if (sz - n < BUFSZ) {
2806         /* There's not enough to fill the buffer a second time.  Do this in
2807          * two pieces.
2808          */
2809
2810         memset(t->u.buf + t->u.n, pc, n); sz -= n;
2811         if (t->u.gops->putm(t->u.go, t->u.buf, BUFSZ)) t->u.err = -1;
2812         memset(t->u.buf, pc, sz); t->u.n = sz;
2813       } else {
2814         /* We'll fill the whole buffer at least once.  Flush what we have
2815          * immediately and then do whole buffer-full sends.
2816          */
2817
2818         if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) t->u.err = -1;
2819         memset(t->u.buf, pc, BUFSZ);
2820         while (sz >= BUFSZ) {
2821           if (t->u.gops->putm(t->u.go, t->u.buf, BUFSZ)) t->u.err = -1;
2822           sz -= BUFSZ;
2823         }
2824         t->u.n = sz;
2825       }
2826     }
2827   }
2828 }
2829
2830 /* --- @termunibi_flush@ --- *
2831  *
2832  * Arguments:   @struct tty *tty@ = control block pointer (ignored)
2833  *
2834  * Returns:     Zero for success, %$-1$% if error pending.
2835  *
2836  * Use:         Flush the output buffer to the backend.  If an error is
2837  *              pending, clear it and return failure.
2838  */
2839
2840 static int termunibi_flush(struct tty *tty)
2841 {
2842   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2843   int rc = t->u.err;
2844
2845   if (t->u.n) {
2846     if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) rc = -1;
2847     t->u.n = 0;
2848   }
2849   t->u.err = 0; return (rc);
2850 }
2851
2852 /* --- @termunibi_put0@, @termunibi_put1i@, @termunibi_put2i@ --- *
2853  *
2854  * Arguments:   @struct tty *tty@ = control block pointer
2855  *              @unsigned npad@ = number of lines affected, for padding
2856  *              @const char *cap@ = capability string to write, or null
2857  *              @int i0, i1@ = integer parameters
2858  *
2859  * Returns:     Zero on success, %$-1$% on error.
2860  *
2861  * Use:         Format a capability string and send it to the terminal.
2862  */
2863
2864 static int termunibi_put0(struct tty *tty,
2865                           unsigned npad, const char *cap)
2866 {
2867   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2868   struct termunibi_outctx out;
2869   unibi_var_t arg[9];
2870
2871   if (!cap) return (-1);
2872   out.t = t; out.npad = npad;
2873   unibi_format(t->u.dy, t->u.st, cap, arg,
2874                termunibi_putm, &out,
2875                termunibi_pad, &out);
2876   return (0);
2877 }
2878
2879 static int termunibi_put1i(struct tty *tty,
2880                            unsigned npad, const char *cap, int i0)
2881 {
2882   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2883   struct termunibi_outctx out;
2884   unibi_var_t arg[9];
2885
2886   if (!cap) return (-1);
2887   arg[0] = unibi_var_from_num(i0);
2888   out.t = t; out.npad = npad;
2889   unibi_format(t->u.dy, t->u.st, cap, arg,
2890                termunibi_putm, &out,
2891                termunibi_pad, &out);
2892   return (0);
2893 }
2894
2895 static int termunibi_put2i(struct tty *tty,
2896                            unsigned npad,
2897                            const char *cap, int i0, int i1)
2898 {
2899   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2900   struct termunibi_outctx out;
2901   unibi_var_t arg[9];
2902
2903   if (!cap) return (-1);
2904   arg[0] = unibi_var_from_num(i0);
2905   arg[1] = unibi_var_from_num(i1);
2906   out.t = t; out.npad = npad;
2907   unibi_format(t->u.dy, t->u.st, cap, arg,
2908                termunibi_putm, &out,
2909                termunibi_pad, &out);
2910   return (0);
2911 }
2912
2913 /* Context for cost counting. */
2914 struct termunibi_costctx {
2915   struct tty_unibilium *t;              /* terminal control block */
2916   unsigned npad;                        /* number of lines affected */
2917   size_t n;                             /* accumulated cost, in chars */
2918 };
2919
2920 /* --- @termunibi_costputm@ --- *
2921  *
2922  * Arguments:   @void *ctx@ = output context pointer
2923  *              @const char *p, size_t sz@ = buffer to write
2924  *
2925  * Returns:     ---
2926  *
2927  * Use:         Count the number of characters written.
2928  */
2929
2930 static void termunibi_costputm(void *ctx, const char *p, size_t sz)
2931   { struct termunibi_costctx *c = ctx; c->n += sz; }
2932
2933 /* --- @termunibi_costpad@ --- *
2934  *
2935  * Arguments:   @void *ctx@ = control block pointer
2936  *              @size_t delay@ = tenths of milliseconds to pad
2937  *              @int mulp@ = nonzero if padding is per line
2938  *              @int forcep@ = nonzero if padding is unconditional
2939  *                      (regardless of, e.g., baud rate or flow control)
2940  *
2941  * Returns:     ---
2942  *
2943  * Use:         Count the character equivalent of the requested delay.
2944  */
2945
2946 static void termunibi_costpad(void *ctx, size_t delay, int mulp, int forcep)
2947 {
2948   struct termunibi_costctx *c = ctx;
2949
2950   if (mulp) delay *= c->npad;
2951   c->n += caps_padchars(&c->t->tty, delay, CPF_FORCE);
2952 }
2953
2954 /* --- @termunibi_cost@ --- *
2955  *
2956  * Arguments:   @struct tty *tty@ = control block pointer
2957  *              @unsigned npad@ = number of lines affected
2958  *              @const char *cap@ = capability string to measure, or null
2959  *              @int i0@ = argument
2960  *
2961  * Returns:     A linear cost for using the capability.  This is meaningless
2962  *              if @cap@ is null.
2963  */
2964
2965 static size_t termunibi_cost(struct tty *tty,
2966                               unsigned npad, const char *cap, int i0)
2967 {
2968   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2969   struct termunibi_costctx c;
2970   unibi_var_t arg[9];
2971
2972   if (!cap) return (0);
2973   arg[0] = unibi_var_from_num(i0);
2974   c.t = t; c.npad = npad; c.n = 0;
2975   unibi_format(t->u.dy, t->u.st, cap, arg,
2976                termunibi_costputm, &c,
2977                termunibi_costpad, &c);
2978   return (c.n);
2979 }
2980
2981 /* The `terminfo' operations table. */
2982 static const union tty_capopsu termunibi_ops = { {
2983   { termunibi_release, TTY_CAPOPS },
2984   { termunibi_boolcap, termunibi_intcap, termunibi_strcap,
2985     termunibi_prepout, termunibi_flush,
2986     termunibi_put0, termunibi_put1i, termunibi_put2i,
2987     termunibi_cost }
2988 } };
2989
2990 /* --- @termunibi_init@ --- *
2991  *
2992  * Arguments:   @FILE *fp@ = the output stream
2993  *
2994  * Returns:     A pointer to a fresh terminal control block, or null on
2995  *              failure.
2996  *
2997  * Use:         Initialize a terminal for use through Unibilium.
2998  */
2999
3000 static struct tty *termunibi_init(FILE *fp)
3001 {
3002   union tty_unibiliumu *u = 0; struct tty *ret = 0;
3003   unibi_term *ut = 0;
3004
3005   /* Collect the terminal description. */
3006   ut = unibi_from_env(); if (!ut) goto end;
3007
3008   /* Set up a fresh control block. */
3009   XNEW(u);
3010   u->tty.ops = &termunibi_ops.tty;
3011   u->u.u.ut = ut; ut = 0;
3012   common_init(&u->tty, fp, 0);
3013   init_caps(&u->cap);
3014
3015   /* Initialize the buffer state. */
3016   u->u.u.n = 0; u->u.u.err = 0;
3017
3018   /* All done. */
3019   ret = &u->tty; u = 0;
3020 end:
3021   xfree(u); if (ut) unibi_destroy(ut);
3022   return (ret);
3023 }
3024
3025 #endif
3026
3027 /*----- ANSI terminals ----------------------------------------------------*/
3028
3029 /* Here we dispense with external libraries and take a wild guess that the
3030  * terminal accepts some variant of ANSI control sequences.
3031  *
3032  * Here's a quick reference of the control sequences that we use here.
3033  *
3034  *   * CUP: \33 [ Y ; X H               `cursor position' [vt100]
3035  *
3036  *   * CUU/CUD/CUR/CUL: \33 [ N A/B/C/D `cursor up/down/right/left'
3037  *
3038  *   * DCH: \33 [ N P                   `delete character' [vt220]
3039  *                                              (single char only in vt102?)
3040  *
3041  *   * DL: \33 [ N M                    `delete line' [vt220]
3042  *                                              (single line only in vt102?)
3043  *
3044  *   * ECH: \33 [ N X                   `erase characters' [vt220]
3045  *
3046  *   * ED: \33 [ P J                    `erase in display'
3047  *       P = 0                          erase to end-of-screen [vt100]
3048  *       P = 1                          erase from start-of-screen [vt100]
3049  *       P = 2                          erase entire screen [vt100]
3050  *
3051  *   * EL: \33 [ P K                    `erase in line'
3052  *       P = 0                          erase to end-of-line [vt100]
3053  *       P = 1                          erase from start-of-line [vt100]
3054  *       P = 2                          erase entire line [vt100]
3055  *
3056  *   * HPA/VPA: \33 [ I G/d             `horizontal/vertical position
3057  *                                              absolute' [ecma48-4]
3058  *
3059  *   * ICH: \33 [ N @                   `insert character' [vt220]
3060  *                                              (single char only in vt102?)
3061  *
3062  *   * IL: \33 [ N L                    `insert line' [vt220]
3063  *                                              (single line only in vt102?)
3064  *
3065  *   * SGR: \33 [ P ; ... m             `select graphics rendition'
3066  *       P = 0                          cancel all attributes [vt100]
3067  *       P = 1                          bold [vt100]
3068  *       P = 2                          dim [ecma48-4]
3069  *       P = 3                          italics [ecma48-4]
3070  *       P = 4                          underline [vt100]
3071  *       P = 7                          inverse video [vt100]
3072  *       P = 9                          strikeout [ecma48-4]
3073  *       P = 21                         double underline [ecma48-4]
3074  *       P = 22                         cancal bold/dim [vt220]
3075  *       P = 24                         cancel underline [vt220]
3076  *       P = 27                         cancel inverse video [vt220]
3077  *       P = 30 + 4 R + 2 G + B         set 1BPC foreground [ecma48-4]
3078  *       P = 38 : 2 : ? : R : G : B     set foreground [iso8613-6]
3079  *       P = 38 : 5 : N                 set foreground [iso8613-6, xterm]
3080  *       P = 39                         cancel foreground [ecma48-4]
3081  *       P = 40--49                     as above, for background
3082  *       P = 90 + 4 R + 2 G + B         set bright 1BPC foreground [xterm]
3083  *       P = 100 + 4 R + 2 G + B        set bright 1BPC background [xterm]
3084  *
3085  *   * SM/RM: \33 [ P ; ... h/l `set/reset modes'
3086  *       M = 4                          insert [vt220]
3087  *
3088  *   * SM, RM: \33 [ ? P ; ... h/l      `set/reset private modes'
3089  *       M = 7                          auto right margin [vt100]
3090  *       M = 25                         visible cursor [vt220]
3091  *       M = 1049                       alternate screen [xterm]
3092  *
3093  *   * \33 [ P ; X ; Y t                `window manipulation'
3094  *       P = 22, X = 0                  save title and icon [xterm]
3095  *       P = 23, X = 0                  restore title and icon [xterm]
3096  */
3097
3098 /* Additional state required for ANSI terminals. */
3099 struct tty_ansislots {
3100   unsigned f;
3101 #define TAF_CNCATTR 1u                  /*   attributes can be cancelled */
3102 #define TAF_EDITN 2u                    /*   insert/delete multiple */
3103 #define TAF_SEMI 4u                     /*   semicolons in CSI 38 m colour */
3104 };
3105 struct tty_ansi { TTY_BASEPFX; struct tty_ansislots ansi; };
3106 union tty_ansiu { struct tty_ansi ansi; TTY_BASEUSFX; };
3107
3108 /* --- @ansi_release@ --- *
3109  *
3110  * Arguments:   @struct tty *tty@ = control block pointer
3111  *
3112  * Returns:     ---
3113  *
3114  * Use:         Release resources held by the control block.  (There's
3115  *              actually nothing to do here.)
3116  */
3117
3118 static void ansi_release(struct tty *tty) { ; }
3119
3120 /* Some simple macros we use throughout. */
3121 #define PUTCH(ch) CHECK(gops->putch(go, (ch)))
3122 #define PUTLIT(lit) CHECK(gops->putm(go, (lit), sizeof(lit) - 1))
3123
3124 /* Shared macro for @ansi_setcolour@ and @ansi_setattr@.  The output has the
3125  * form `ESC [ ... ; ... ; ... m', so there's a semicolon before each
3126  * parameter other than the first.  The @TAF_SEMI@ flag here keeps track of
3127  * whether a semicolon is wanted before the next item, and this macro is
3128  * responsible for writing it when necessary.
3129  */
3130 #define SEMI do {                                                       \
3131   if (!(f&TAF_SEMI)) f |= TAF_SEMI;                                     \
3132   else PUTCH(';');                                                      \
3133 } while (0)
3134
3135 /* --- @ansi_setcolour@ --- *
3136  *
3137  * Arguments:   @struct tty_caps *t@ = extended control block pointer
3138  *              @unsigned *f_inout@ = flags (updated)
3139  *              @const struct gprintf_ops *gops, void *go@ = output
3140  *                      destination
3141  *              @int norm, br@ = normal and bright base codes -- 30 and 90
3142  *                      for foreground, or 40 and 100 for background
3143  *              @uint32 spc, clr@ = the colour space and number
3144  *
3145  * Returns:     Zero on success, %$-1$% on error.
3146  *
3147  * Use:         Emit the correct control string to set the foreground or
3148  *              background colour as indicated.
3149  */
3150
3151 static int ansi_setcolour(struct tty_ansi *t, unsigned *f_inout,
3152                           const struct gprintf_ops *gops, void *go,
3153                           int norm, int br,
3154                           uint32 spc, uint32 clr)
3155 {
3156   unsigned f = *f_inout;
3157   int rc;
3158
3159   switch (spc) {
3160     case TTCSPC_NONE:
3161       SEMI; CHECK(gprintf(gops, go, "%d", norm + 9));
3162       break;
3163     case TTCSPC_1BPC:
3164       SEMI; CHECK(gprintf(gops, go, "%d", norm + clr));
3165       break;
3166     case TTCSPC_1BPCBR:
3167       SEMI; CHECK(gprintf(gops, go, "%d", br + (clr&~TT1BPC_BRI)));
3168       break;
3169     case TTCSPC_4LPC: case TTCSPC_6LPC:
3170       SEMI;
3171       if (t->ansi.f&TAF_SEMI)
3172         CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 16));
3173       else
3174         CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 16));
3175       break;
3176     case TTCSPC_8LGS:
3177       SEMI;
3178       if (t->ansi.f&TAF_SEMI)
3179         CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 80));
3180       else
3181         CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 80));
3182       break;
3183     case TTCSPC_24LGS:
3184       SEMI;
3185       if (t->ansi.f&TAF_SEMI)
3186         CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 232));
3187       else
3188         CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 232));
3189       break;
3190     case TTCSPC_8BPC:
3191       SEMI;
3192       if (t->ansi.f&TAF_SEMI)
3193         CHECK(gprintf(gops, go, "%d;2;%d;%d;%d", norm + 8,
3194                       TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
3195       else
3196         CHECK(gprintf(gops, go, "%d:2::%d:%d:%d", norm + 8,
3197                       TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
3198       break;
3199     default:
3200       rc = -1; goto end;
3201   }
3202
3203   rc = 0;
3204 end:
3205   *f_inout = f; return (rc);
3206 }
3207
3208 /* --- @ansi_setattr@ --- *
3209  *
3210  * Arguments:   @struct tty *tty@ = control block pointer
3211  *              @const struct gprintf_ops *gops, void *go@ = output
3212  *                      destination
3213  *              @const struct tty_attr *a@ = attribute to set, already
3214  *                      clamped
3215  *
3216  * Returns:     Zero on success, %$-1$% on error.
3217  *
3218  * Use:         Arrange to display future characters with the display
3219  *              attributes indicated by @a@.
3220  */
3221
3222 static int ansi_setattr(struct tty *tty,
3223                         const struct gprintf_ops *gops, void *go,
3224                         const struct tty_attr *a)
3225 {
3226   struct tty_ansi *t = (struct tty_ansi *)tty;
3227   uint32 diff, m;
3228   int rc = 0;
3229   unsigned z, c, f = 0;
3230
3231   /* Work out what, if anything, needs doing.  Since we start by writing
3232    * `ESC [' unconditionally below, if there's actually no work to do at all,
3233    * we need to stop early.
3234    */
3235   diff = a->f ^ t->tty.st.attr.f;
3236   if (!diff && a->fg == t->tty.st.attr.fg && a->bg == t->tty.st.attr.bg)
3237     { rc = 0; goto end; }
3238
3239   /* We're committed.  Write the start of the `SGR' sequence. */
3240   PUTLIT("\33[");
3241
3242   /* Form a basic strategy.
3243    *
3244    * As for @caps_setattr@ above, we need to decide betwen cancelling
3245    * individual attributes or resetting the whole lot and reinstating the
3246    * attributes which were cancelled unnecessarily.
3247    */
3248
3249   /* Start by adding up the costs of cancelling individual attributes.
3250    * Early terminals can't do this, but we'll deal with that problem later.
3251    */
3252   c = 0;
3253 #define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
3254   if (CLEARP(TTAF_LNMASK)) c += 3;
3255   if (CLEARP(TTAF_WTMASK)) c += 3;
3256   if (diff&~a->f&TTAF_INVV) c += 3;
3257   if (diff&~a->f&TTAF_STRIKE) c += 3;
3258   if (diff&~a->f&TTAF_ITAL) c += 3;
3259   if (CLEARP(TTAF_FGSPCMASK)) c += 3;
3260   if (CLEARP(TTAF_BGSPCMASK)) c += 3;
3261 #undef CLEARP
3262
3263   /* Count up the cost of cancelling everything and then putting back the
3264    * ones that we actually wanted.
3265    */
3266   z = 0;
3267   if (!(diff&TTAF_LNMASK))
3268     switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
3269       case TTLN_ULINE: z += 2; break;
3270       case TTLN_UULINE: z += 3; break;
3271     }
3272   if (!(diff&TTAF_WTMASK))
3273     switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
3274       case TTWT_BOLD: z += 2; break;
3275       case TTWT_DIM: z += 2; break;
3276     }
3277   m = a->f&~diff;
3278   if (m&TTAF_INVV) z += 2;
3279   if (m&TTAF_STRIKE) z += 2;
3280   if (m&TTAF_ITAL) z += 2;
3281 #define COLOURCOST(G, g) do {                                           \
3282   if (!(diff&TTAF_##G##SPCMASK) &&                                      \
3283       (a->f&TTAF_##G##SPCMASK) && a->g == t->tty.st.attr.g)             \
3284     switch ((a->f&TTAF_##G##SPCMASK) >> TTAF_##G##SPCSHIFT) {           \
3285       case TTCSPC_1BPC: case TTCSPC_1BPCBR: z += 3; break;              \
3286       case TTCSPC_4LPC: case TTCSPC_8LGS: z += 8; break;                \
3287       case TTCSPC_6LPC: case TTCSPC_24LGS: z += 9; break;               \
3288       case TTCSPC_8BPC: z += 16; break;                                 \
3289     }                                                                   \
3290 } while (0)
3291   COLOURCOST(FG, fg); COLOURCOST(BG, bg);
3292 #undef COLOURCOST
3293
3294   /* If cancelling everything is cheaper, or we can't cancel individual
3295    * attributes after all, then reset everything.
3296    */
3297   if (z < c || (c && !(t->ansi.f&TAF_CNCATTR)))
3298     { SEMI; diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg = 0; }
3299
3300   /* Line style. */
3301   if (diff&TTAF_LNMASK)
3302     switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
3303       case TTLN_NONE: SEMI; PUTLIT("24"); break;
3304       case TTLN_ULINE: SEMI; PUTCH('4'); break;
3305       case TTLN_UULINE: SEMI; PUTLIT("21"); break;
3306       default: rc = -1; goto end;
3307     }
3308
3309   /* Text weight. */
3310   if (diff&TTAF_WTMASK)
3311     switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
3312       case TTWT_MED: SEMI; PUTLIT("22"); break;
3313       case TTWT_BOLD: SEMI; PUTCH('1'); break;
3314       case TTWT_DIM: SEMI; PUTCH('2'); break;
3315       default: rc = -1; goto end;
3316     }
3317
3318   /* Other text effects. */
3319   if (diff&TTAF_INVV)
3320     { SEMI; if (a->f&TTAF_INVV) PUTCH('7'); else PUTLIT("27"); }
3321   if (diff&TTAF_STRIKE)
3322     { SEMI; if (a->f&TTAF_STRIKE) PUTCH('9'); else PUTLIT("29"); }
3323   if (diff&TTAF_ITAL)
3324     { SEMI; if (a->f&TTAF_ITAL) PUTCH('3'); else PUTLIT("23"); }
3325
3326   /* Colours. */
3327   if (diff&TTAF_FGSPCMASK || a->fg != tty->st.attr.fg)
3328     CHECK(ansi_setcolour(t, &f, gops, go, 30, 90,
3329                          (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
3330   if (diff&TTAF_BGSPCMASK || a->bg != tty->st.attr.bg)
3331     CHECK(ansi_setcolour(t, &f, gops, go, 40, 100,
3332                          (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
3333
3334   /* All done. */
3335   PUTCH('m'); rc = 0;
3336 end:
3337   t->tty.st.attr = *a; return (rc);
3338 }
3339
3340 /* --- @ansi_setmodes@ --- *
3341  *
3342  * Arguments:   @struct tty *tty@ = control block pointer
3343  *              @const struct gprintf_ops *gops, void *go@ = output
3344  *                      destination
3345  *              @uint32 modes_bic, modes_xor@ = masks to apply to the modes
3346  *                      settings
3347  *
3348  * Returns:     Zero on success, %$-1$% on error.
3349  *
3350  * Use:         Set the requested terminal modes.
3351  */
3352
3353 static int ansi_setmodes(struct tty *tty,
3354                          const struct gprintf_ops *gops, void *go,
3355                          uint32 modes_bic, uint32 modes_xor)
3356 {
3357   uint32 modes, diff;
3358   int rc;
3359
3360   /* Figure out which modes to set. */
3361   modes = (tty->st.modes&~modes_bic) ^ modes_xor;
3362   diff = modes ^ tty->st.modes;
3363
3364   /* Auto-margins. */
3365   if (diff&TTMF_AUTOM) {
3366     if (modes&TTMF_AUTOM) PUTLIT("\33[?7h");
3367     else PUTLIT("\33[?7l");
3368   }
3369
3370   /* Fullscreen. */
3371   if (diff&TTMF_FSCRN) {
3372     if (modes&TTMF_FSCRN) PUTLIT("\33[?1049h\33[22;0;0t");
3373     else PUTLIT("\33[?1049l\33[23;0;0t");
3374   }
3375
3376   /* Visible cursor. */
3377   if (diff&TTMF_CVIS) {
3378     if (modes&TTMF_CVIS) PUTLIT("\33[?25h");
3379     else PUTLIT("\33[?25l");
3380   }
3381
3382   /* Insert. */
3383   if (diff&TTMF_INS) {
3384     if (modes&TTMF_INS) PUTLIT("\33[4h");
3385     else PUTLIT("\33[4l");
3386   }
3387
3388   /* Done. */
3389   rc = 0;
3390 end:
3391   tty->st.modes = modes;
3392   return (rc);
3393 }
3394
3395 /* --- @ansi_move@ --- *
3396  *
3397  * Arguments:   @struct tty *tty@ = control block pointer
3398  *              @const struct gprintf_ops *gops, void *go@ = output
3399  *                      destination
3400  *              @unsigned orig@ = origin
3401  *              @int y, x@ = new cursor position
3402  *
3403  * Returns:     Zero on success, %$-1$% on error.
3404  *
3405  * Use:         Move the cursor.
3406  */
3407
3408 static int ansi_move(struct tty *tty,
3409                      const struct gprintf_ops *gops, void *go,
3410                      unsigned orig, int y, int x)
3411 {
3412   static const char bs[4] = { '\b', '\b', '\b', '\b' };
3413   int rc;
3414
3415   /* Check that the arguments are basically sensible. */
3416   if ((!(orig&TTOF_YCUR) && y < 0) || (!(orig&TTOF_XCUR) && x < 0))
3417     { rc = -1; goto end; }
3418
3419   if (orig == TTORG_HOME) {
3420     /* Fully absolute positioning.  We can omit the arguments for the topmost
3421      * row and/or leftmost column.
3422      */
3423
3424     if (!x) {
3425       if (!y) PUTLIT("\33[H");
3426       else CHECK(gprintf(gops, go, "\33[%dH", y + 1));
3427     } else {
3428       if (!y) CHECK(gprintf(gops, go, "\33[;%dH", x + 1));
3429       else CHECK(gprintf(gops, go, "\33[%d,%dH", y + 1, x + 1));
3430     }
3431   } else if (orig == (TTOF_XHOME | TTOF_YCUR) && x == 0 && y == 1) {
3432     /* Special case for starting a new line. */
3433
3434     PUTLIT("\r\n");
3435   } else {
3436     /* More complex motion.  Handle the horizontal and vertical motions
3437      * separately.
3438      */
3439
3440     /* First, vertical motion.  This is simpler. */
3441     if (!(orig&TTOF_YCUR)) CHECK(gprintf(gops, go, "\33[%dd", y + 1));
3442     else if (y == -1) PUTLIT("\33[A");
3443     else if (y < 0) CHECK(gprintf(gops, go, "\33[%dA", -y));
3444     else if (y == +1) PUTLIT("\33[B"); /* not %|^J|%! */
3445     else if (y > 1) CHECK(gprintf(gops, go, "\33[%dB", y));
3446
3447     /* Next, horizontal motion. */
3448     if (!(orig&TTOF_XCUR)) {
3449       /* Absolute positioning. Use a carriage return if (a) we want the
3450        * leftmost column anyway, or (b) the terminal can't handle absolute
3451        * horizontal positioning.
3452        */
3453
3454       if (!x)
3455         PUTCH('\r');
3456       else if (tty->ocaps&TTCF_MIXMV)
3457         CHECK(gprintf(gops, go, "\33[%dG", x + 1));
3458       else
3459         CHECK(gprintf(gops, go, "\r\33[%dC", x));
3460     } else {
3461       /* Relative positioning.  We can go back one space simply using the
3462        * backspace control sequence.  The `CUB' sequence is at least four
3463        * characters long.
3464        */
3465
3466       if (x == -1) PUTCH('\b');
3467       else if (-4 <= x && x < -1) CHECK(gops->putm(go, bs, -x));
3468       else if (x < 0) CHECK(gprintf(gops, go, "\33[%dD", -x));
3469       else if (x == +1) PUTLIT("\33[C");
3470       else if (x > 0) CHECK(gprintf(gops, go, "\33[%dC", x));
3471     }
3472   }
3473
3474   /* Done. */
3475   rc = 0;
3476 end:
3477   return (rc);
3478 }
3479
3480 /* --- @ansi_erase@ --- *
3481  *
3482  * Arguments:   @struct tty *tty@ = control block pointer
3483  *              @const struct gprintf_ops *gops, void *go@ = output
3484  *                      destination
3485  *              @unsigned f@ = flags
3486  *
3487  * Returns:     Zero on success, %$-1$% on error.
3488  *
3489  * Use:         Erase portions of the current line or the whole display.
3490  */
3491
3492 static int ansi_erase(struct tty *tty,
3493                       const struct gprintf_ops *gops, void *go,
3494                       unsigned f)
3495 {
3496   int rc;
3497
3498   if (f&TTEF_DSP)
3499     switch (f&(TTEF_BEGIN | TTEF_END)) {
3500       case 0: break;
3501       case TTEF_BEGIN: PUTLIT("\33[1J"); break;
3502       case TTEF_END: PUTLIT("\33[J"); break;
3503       case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2J"); break;
3504     }
3505   else
3506     switch (f&(TTEF_BEGIN | TTEF_END)) {
3507       case 0: break;
3508       case TTEF_BEGIN: PUTLIT("\33[1K"); break;
3509       case TTEF_END: PUTLIT("\33[K"); break;
3510       case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2K"); break;
3511     }
3512   rc = 0;
3513 end:
3514   return (rc);
3515 }
3516
3517 /* --- @ansi_erch@ --- *
3518  *
3519  * Arguments:   @struct tty *tty@ = control block pointer
3520  *              @const struct gprintf_ops *gops, void *go@ = output
3521  *                      destination
3522  *              @unsigned n@ = number of characters to erase
3523  *
3524  * Returns:     Zero on success, %$-1$% on error.
3525  *
3526  * Use:         Erase a number of characters, starting from and including the
3527  *              current cursor position.
3528  */
3529
3530 static int ansi_erch(struct tty *tty,
3531                      const struct gprintf_ops *gops, void *go,
3532                      unsigned n)
3533 {
3534   int rc;
3535
3536   if (n == 1) PUTLIT("\33[X");
3537   else if (n) CHECK(gprintf(gops, go, "\33[%uX", n));
3538   rc = 0;
3539 end:
3540   return (rc);
3541 }
3542
3543 /* --- @caps_ins@ --- *
3544  *
3545  * Arguments:   @struct tty *tty@ = control block pointer
3546  *              @const struct gprintf_ops *gops, void *go@ = output
3547  *                      destination
3548  *              @unsigned f@ = flags
3549  *              @unsigned n@ = number of items to insert
3550  *
3551  * Returns:     Zero on success, %$-1$% on error.
3552  *
3553  * Use:         Insert a number of blank characters or lines.
3554  */
3555
3556 static int ansi_ins(struct tty *tty,
3557                     const struct gprintf_ops *gops, void *go,
3558                     unsigned f, unsigned n)
3559 {
3560   int rc;
3561
3562   if (f&TTIDF_LN) {
3563     if (n == 1) PUTLIT("\33[L");
3564     else if (n) CHECK(gprintf(gops, go, "\33[%uL", n));
3565   } else {
3566     if (n == 1) PUTLIT("\33[@");
3567     else if (n) CHECK(gprintf(gops, go, "\33[%u@", n));
3568   }
3569   rc = 0;
3570 end:
3571   return (rc);
3572 }
3573
3574 /* --- @caps_inch@ --- *
3575  *
3576  * Arguments:   @struct tty *tty@ = control block pointer
3577  *              @const struct gprintf_ops *gops, void *go@ = output
3578  *                      destination
3579  *              @int ch@ = character to insert
3580  *
3581  * Returns:     Zero on success, %$-1$% on error.
3582  *
3583  * Use:         Insert a single character.
3584  */
3585
3586 static int ansi_inch(struct tty *tty,
3587                      const struct gprintf_ops *gops, void *go,
3588                      int ch)
3589 {
3590   /* There's actually nothing to do here except write the character. */
3591   if (!(tty->st.modes&TTMF_INS)) return (-1);
3592   else return (gops->putch(go, ch));
3593 }
3594
3595 /* --- @caps_del@ --- *
3596  *
3597  * Arguments:   @struct tty *tty@ = control block pointer
3598  *              @const struct gprintf_ops *gops, void *go@ = output
3599  *                      destination
3600  *              @unsigned f@ = flags
3601  *              @unsigned n@ = number of items to delete
3602  *
3603  * Returns:     Zero on success, %$-1$% on error.
3604  *
3605  * Use:         Delete a number of characters or lines.
3606  */
3607
3608 static int ansi_del(struct tty *tty,
3609                     const struct gprintf_ops *gops, void *go,
3610                     unsigned f, unsigned n)
3611 {
3612   int rc;
3613
3614   if (f&TTIDF_LN) {
3615     if (n == 1) PUTLIT("\33[M");
3616     else if (n) CHECK(gprintf(gops, go, "\33[%uM", n));
3617   } else {
3618     if (n == 1) PUTLIT("\33[P");
3619     else if (n) CHECK(gprintf(gops, go, "\33[%uP", n));
3620   }
3621   rc = 0;
3622 end:
3623   return (rc);
3624 }
3625
3626 #undef PUTCH
3627 #undef PUTLIT
3628 #undef SEMI
3629
3630 #undef CHECK
3631
3632 /* The ANSI operations table. */
3633 static const struct tty_ops ansi_ops = {
3634   ansi_release,
3635   ansi_setattr, ansi_setmodes,
3636   ansi_move, stupid_repeat,
3637   ansi_erase, ansi_erch, ansi_ins, ansi_inch, ansi_del
3638 };
3639
3640 /* --- @termcap_init@ --- *
3641  *
3642  * Arguments:   @FILE *fp@ = the output stream
3643  *
3644  * Returns:     A pointer to a fresh terminal control block, or null on
3645  *              failure.
3646  *
3647  * Use:         Initialize a terminal for use with ANSI control sequences.
3648  */
3649
3650 static struct tty *ansi_init(FILE *fp)
3651 {
3652   /* Useful collections of capabilities. */
3653 #define COLS_NO 0
3654 #define COLS_8 (TTACF_FG | TTACF_BG | TTACF_1BPC)
3655 #define COLS_16 (COLS_8 | TTACF_1BPCBR)
3656 #define COLS_88 (COLS_16 | TTACF_4LPC | TTACF_8LGS)
3657 #define COLS_256 (COLS_16 | TTACF_6LPC | TTACF_24LGS)
3658 #define COLS_16M (COLS_256 | TTACF_8BPC)
3659
3660 #define EDIT_OPS (TTCF_ERCH |                                           \
3661                   TTCF_DELCH | TTCF_DELLN |                             \
3662                   TTCF_INSCH | TTCF_INSLN)
3663
3664   /* Table of user-settable flags. */
3665   static const struct flagmap {
3666     const char *name;
3667     uint32 acaps, ocaps;
3668     unsigned tf;
3669   } flagmap[] = {
3670     { "dim",            TTACF_DIM,      0,              0 },
3671     { "uuline",         TTACF_UULINE,   0,              0 },
3672     { "strike",         TTACF_STRIKE,   0,              0 },
3673     { "ital",           TTACF_ITAL,     0,              0 },
3674     { "cvis",           0,              TTMF_CVIS,      0 },
3675     { "fscrn",          0,              TTMF_FSCRN,     0 },
3676     { "insmode",        0,              TTMF_INS,       0 },
3677     { "hvpa"    ,       0,              TTCF_MIXMV,     0 },
3678     { "edit",           0,              EDIT_OPS,       0 },
3679     { "cncattr",        0,              0,              TAF_CNCATTR },
3680     { "editn",          0,              0,              TAF_EDITN },
3681     { "semi",           0,              0,              TAF_SEMI },
3682     { 0,                0,              0,              0 }
3683   };
3684
3685   /* Tables of enumerated setting values. */
3686   static const struct kw { const char *name; uint32 val; }
3687     kw_colours[] = {
3688       { "no",           COLS_NO },
3689       { "8",            COLS_8 },
3690       { "16",           COLS_16 },
3691       { "88",           COLS_88 },
3692       { "256",          COLS_256 },
3693       { "16m",          COLS_16M },
3694       { 0,              0 }
3695     };
3696
3697   /* Table of enumerated settings. */
3698   static const struct enummap {
3699     const char *name;
3700     uint32 mask;
3701     const struct kw *kw;
3702   } enummap[] = {
3703     { "colours",        TTACF_CSPCMASK | TTACF_FG | TTACF_BG,
3704                                                         kw_colours },
3705     { 0,                0,                              0 }
3706   };
3707
3708   /* Table of recognized terminal types. */
3709   static const struct termmap {
3710     const char *pat;
3711     unsigned acaps, ocaps, tf;
3712   } termmap[] = {
3713
3714 #define VT100_ACAPS (TTACF_ULINE | TTACF_BOLD | TTACF_INVV)
3715 #define VT100_OCAPS (TTMF_AUTOM |                                       \
3716                      TTCF_RELMV | TTCF_ABSMV |                          \
3717                      TTCF_MMARG |                                       \
3718                      TTCF_ERBOL | TTCF_EREOL |                          \
3719                      TTCF_ERBOD | TTCF_EREOD | TTCF_ERDSP)
3720 #define VT100_TF (0)
3721
3722 #define VT102_ACAPS (VT100_ACAPS)
3723 #define VT102_OCAPS (VT100_OCAPS |                                      \
3724                      TTMF_INS |                                         \
3725                      TTCF_INSCH | TTCF_INSLN | TTCF_DELCH | TTCF_DELLN)
3726 #define VT102_TF (VT100_TF)
3727
3728 #define VT220_ACAPS (VT102_ACAPS)
3729 #define VT220_OCAPS (VT102_OCAPS | TTMF_CVIS | TTCF_ERCH)
3730 #define VT220_TF (VT102_TF | TAF_CNCATTR | TAF_EDITN)
3731
3732 #define ECMA48_ACAPS (VT220_ACAPS | TTACF_DIM)
3733 #define ECMA48_OCAPS (VT220_OCAPS | TTCF_MIXMV)
3734 #define ECMA48_TF (VT220_TF)
3735
3736 #define XTERM_ACAPS (ECMA48_ACAPS)
3737 #define XTERM_OCAPS (ECMA48_OCAPS | TTMF_FSCRN)
3738 #define XTERM_TF (ECMA48_TF)
3739
3740 #define STRIKE TTACF_STRIKE
3741 #define ITAL TTACF_ITAL
3742 #define SEMI TAF_SEMI
3743
3744 #define T(pat, base, cols, acaps, ocaps, tf)                            \
3745   { pat,                                                                \
3746     base##_ACAPS | COLS_##cols | (acaps),                               \
3747     base##_OCAPS | (ocaps), base##_TF | (tf) }
3748
3749     T("color_xterm",    XTERM,  8,      STRIKE | ITAL,  0,      0),
3750
3751     T("gnome",          XTERM,  16M,    STRIKE | ITAL,  0,      SEMI),
3752   /*T("gonme-*"         XTERM,  16M,    STRIKE | ITAL,  0,      SEMI),*/
3753
3754     T("linux",          XTERM,  16,     0,              0,      0),
3755
3756     T("putty",          XTERM,  16M,    0,              0,      SEMI),
3757
3758     T("vt100*",         VT100,  NO,     0,              0,      0),
3759     T("vt102*",         VT102,  NO,     0,              0,      0),
3760     T("vt[2-5][0-9][0-9]*", VT220, NO,  0,              0,      0),
3761
3762     T("vte",            XTERM,  16M,    STRIKE | ITAL,  0,      SEMI),
3763   /*T("vte-*"           XTERM,  16M,    STRIKE | ITAL,  0,      SEMI),*/
3764
3765     T("win",            XTERM,  16M,    0,              0,      SEMI),
3766
3767     T("xterm",          XTERM,  16M,    STRIKE | ITAL,  0,      0),
3768     T("xterm-color",    XTERM,  8,      STRIKE | ITAL,  0,      0),
3769     T("xterm-16color",  XTERM,  16,     STRIKE | ITAL,  0,      0),
3770     T("xterm-88color",  XTERM,  88,     STRIKE | ITAL,  0,      SEMI),
3771     T("xterm-256color", XTERM,  256,    STRIKE | ITAL,  0,      SEMI),
3772     T("xterm-direct",   XTERM,  16M,    STRIKE | ITAL,  0,      0),
3773     T("xterm-*",        XTERM,  16M,    STRIKE | ITAL,  0,      0),
3774
3775   /*T("*-color",        XTERM,  16,     0,              0,      0),*/
3776   /*T("*-16color",      XTERM,  16,     0,              0,      0),*/
3777     T("*-88color",      XTERM,  88,     0,              0,      SEMI),
3778     T("*-256color",     XTERM,  256,    0,              0,      SEMI),
3779     T("*-direct",       XTERM,  16M,    0,              0,      SEMI),
3780
3781     T("*",              XTERM,  16,     0,              0,      0),
3782     { 0,                                0,              0,      0 }
3783
3784 #undef VT100_ACAPS
3785 #undef VT100_OCAPS
3786 #undef VT100_TF
3787
3788 #undef VT102_ACAPS
3789 #undef VT102_OCAPS
3790 #undef VT102_TF
3791
3792 #undef VT220_ACAPS
3793 #undef VT220_OCAPS
3794 #undef VT220_TF
3795
3796 #undef ECMA48_ACAPS
3797 #undef ECMA48_OCAPS
3798 #undef ECMA48_TF
3799
3800 #undef XTERM_ACAPS
3801 #undef XTERM_OCAPS
3802 #undef XTERM_TF
3803
3804 #undef STRIKE
3805 #undef ITAL
3806 #undef SEMI
3807   };
3808
3809 #undef COLS_NO
3810 #undef COLS_8
3811 #undef COLS_16
3812 #undef COLS_88
3813 #undef COLS_256
3814 #undef COLS_16M
3815
3816 #undef EDIT_OPS
3817
3818   union tty_ansiu *u = 0; struct tty *ret = 0;
3819   const char *term, *config, *p, *l;
3820   const struct kw *kw;
3821   const struct enummap *em;
3822   const struct flagmap *fm;
3823   const struct termmap *tm;
3824   size_t n, nn;
3825   unsigned
3826     acaps = 0, ocaps = 0, tf = 0,
3827     acapset = 0, ocapset = 0, tfset = 0,
3828     f = 0;
3829 #define f_sense 1u
3830
3831   /* Read the environment variables. */
3832   config = getenv("MLIB_TTY_ANSICONFIG");
3833   term = getenv("TERM");
3834
3835   /* We're not going to touch Emacs's `dumb' terminal. */
3836   if (term && STRCMP(term, ==, "dumb")) goto end;
3837
3838   /* Scan the user configuration first.  We'll keep track of masks for the
3839    * capabilities that we've set so that we don't clobber them later.
3840    */
3841   if (config) {
3842     l = config + strlen(config);
3843     for (;;) {
3844
3845       /* Skip spaces. */
3846       for (;;)
3847         if (config >= l) goto done_config;
3848         else if (!ISSPACE(*config)) break;
3849         else config++;
3850
3851       /* Find the length of the next space-sparated word. */
3852       for (p = config + 1; p < l && !ISSPACE(*p); p++);
3853
3854       if (*config == '+' || *config == '-') {
3855         /* We've found a flag setting. */
3856
3857         /* Track whether we're supposed to set or clear it. */
3858         if (*config == '+') f |= f_sense;
3859         else f &= ~f_sense;
3860         config++; n = p - config;
3861
3862         /* Search for the flag name. */
3863         for (fm = flagmap; fm->name; fm++)
3864           if (STRNCMP(config, ==, fm->name, n) && !fm->name[n])
3865             goto found_flag;
3866         debug("unknown flag `%.*s'", (int)n, config); goto next_config;
3867
3868       found_flag:
3869         /* Found it.  Check that we've not already seen this one. */
3870         if ((acapset&fm->acaps) || (ocapset&fm->ocaps) || (tfset&fm->tf)) {
3871           debug("duplicate setting for `%s'", fm->name);
3872           goto next_config;
3873         }
3874
3875         /* Apply the setting. */
3876         if (f&f_sense)
3877           { acaps |= fm->acaps; ocaps |= fm->ocaps; tf |= fm->tf; }
3878         acapset |= fm->acaps; ocapset |= fm->ocaps; tfset |= fm->tf;
3879       } else {
3880         /* Must be an assignment of an enumerated setting. */
3881
3882         n = p - config;
3883
3884         /* Find the `%|=|%' separator. */
3885         p = memchr(config, '=', n);
3886         if (!p) {
3887           debug("missing `=' in setting `%.*s'", (int)n, config);
3888           goto next_config;
3889         }
3890
3891         /* Search for the setting name. */
3892         nn = p - config;
3893         for (em = enummap; em->name; em++)
3894           if (STRNCMP(config, ==, em->name, nn) && !em->name[nn])
3895             goto found_enum;
3896         debug("unknown setting `%.*s'", (int)nn, config); goto next_config;
3897
3898       found_enum:
3899         /* Found it.  Check that we've not already seen this one. */
3900         if (acapset&em->mask) {
3901           debug("duplicate setting for `%s'", em->name);
3902           goto next_config;
3903         }
3904
3905         /* Now search for the value. */
3906         p++; nn = n - nn - 1;
3907         for (kw = em->kw; kw->name; kw++)
3908           if (STRNCMP(p, ==, kw->name, nn) && !kw->name[nn])
3909             goto found_kw;
3910         debug("unknown `%s' value `%.*s", em->name, (int)nn, p);
3911         goto next_config;
3912
3913       found_kw:
3914         /* Found that too.  Apply the setting. */
3915         acaps |= kw->val; acapset |= em->mask;
3916       }
3917
3918     next_config:
3919       /* Move on to the next word. */
3920       config += n;
3921     }
3922   done_config:;
3923   }
3924
3925   /* Next, check our built-in table of terminal idiosyncrasies. */
3926   if (term) {
3927     for (tm = termmap; tm->pat; tm++)
3928       if (str_match(tm->pat, term))
3929         goto found_term;
3930     assert(0);
3931   found_term:
3932     acaps |= tm->acaps&~acapset;
3933     ocaps |= tm->ocaps&~ocapset;
3934     tf |= tm->tf&~tfset;
3935   }
3936
3937   /* If the user hasn't already set how we handle colour, then check the
3938    * environment for more general advice.  Unlike `terminfo'-based backends,
3939    * we can handle colour upgrades as well as downgrades.
3940    */
3941   if (!(acapset&TTACF_CSPCMASK)) env_colour_caps(&acaps, ECCF_SET);
3942
3943   /* If we can handle background colours, then we can erase to background. */
3944   if (acaps&TTACF_BG) ocaps |= TTCF_BGER;
3945
3946   /* Set up a fresh control block. */
3947   XNEW(u);
3948   u->tty.ops = &ansi_ops;
3949   u->tty.acaps = acaps;
3950   u->tty.ocaps = ocaps;
3951   u->ansi.ansi.f = tf;
3952   u->tty.wd = 80; u->tty.ht = 25;
3953   u->tty.st.modes = TTMF_AUTOM | (u->tty.ocaps&TTMF_CVIS);
3954   u->tty.st.attr.f = 0; u->tty.st.attr.fg = u->tty.st.attr.bg = 0;
3955   common_init(&u->ansi.tty, fp, 0);
3956
3957   /* All done. */
3958   ret = &u->tty; u = 0;
3959 end:
3960   xfree(u); return (ret);
3961
3962 #undef f_sense
3963 }
3964
3965 /*----- Backend selection -------------------------------------------------*/
3966
3967 /* --- @tty_open@ --- *
3968  *
3969  * Arguments:   @FILE *fp@ = stream open on terminal, or null
3970  *              @unsigned f@ = flags (@TTF_...@)
3971  *              @const unsigned *backends@ = ordered list of backends to try
3972  *
3973  * Returns:     Pointer to terminal control block, or null on error.
3974  *
3975  * Use:         Open a terminal and return a @struct tty *@ terminal control
3976  *              block pointer.
3977  *
3978  *              If @fp@ is provided, then it will be used for terminal
3979  *              output.  If @fp@ is @stdout@, then input (not currently
3980  *              supported) will come from @stdin@; otherwise, input comes
3981  *              from @fp@.  If @fp@ is null and @TTF_OPEN@ is set, then
3982  *              @tty_open@ will attempt to open a terminal for itself: if
3983  *              @stdin@ is interactive then it used for input; if @stdout@ --
3984  *              or, failing that, @stderr@ -- is interactive, then it is used
3985  *              for output; otherwise %|/dev/tty|% is opened and used.  If
3986  *              @fp@ is null and @TTF_OPEN@ is not set, then a usable
3987  *              terminal control block is still returned, but output cannot
3988  *              be sent directly to the terminal -- since there isn't one.
3989  *
3990  *              If @TTF_BORROW@ is set, then the stream will not be closed by
3991  *              @tty_close@.  (This flag is ignored if @fp@ is null.)
3992  *
3993  *              If @beckends@ is provided, then it points to a vector of
3994  *              @TTBK_...@ constants describing the backends to be tried in
3995  *              order.  The vector is terminated by @TTBK_END@; if this is
3996  *              found, then a null pointer is returned.
3997  *
3998  *              A null control block pointer is valid for all @tty@
3999  *              functions: most will just immediately report failure, but
4000  *              there won't be any crashing.
4001  */
4002
4003 struct tty *tty_open(FILE *fp, unsigned f, const unsigned *backends)
4004 {
4005   static const struct betab {
4006     const char *name; unsigned code;
4007     struct tty *(*init)(FILE */*fp*/);
4008   } betab[] = {
4009 #ifdef HAVE_UNIBILIUM
4010     { "unibilium",      TTBK_UNIBI,     termunibi_init },
4011 #endif
4012 #ifdef HAVE_TERMINFO
4013     { "terminfo",       TTBK_TERMINFO,  terminfo_init },
4014 #endif
4015 #ifdef HAVE_TERMCAP
4016     { "termcap",        TTBK_TERMCAP,   termcap_init },
4017 #endif
4018     { "ansi",           TTBK_ANSI,      ansi_init },
4019     { 0,                0,              0 }
4020   };
4021
4022   const struct betab *bt;
4023   const char *config, *p, *l;
4024   struct tty *tty = 0;
4025   FILE *fpin = 0;
4026   size_t n;
4027
4028   if (fp || !(f&TTF_OPEN))
4029     fpin = fp != stdout ? fp : isatty(STDIN_FILENO) ? stdin : 0;
4030   else {
4031     if (isatty(STDIN_FILENO)) fpin = stdin;
4032     else fpin = 0;
4033     if (isatty(STDOUT_FILENO)) { fp = stdout; f |= TTF_BORROW; }
4034     else if (isatty(STDERR_FILENO)) { fp = stderr; f |= TTF_BORROW; }
4035     else {
4036       fp = fopen("/dev/tty", "r+"); if (!fp) goto end;
4037       fpin = fp; f &= ~TTF_BORROW;
4038     }
4039   }
4040
4041   config = getenv("MLIB_TTY_BACKENDS");
4042   if (config) {
4043     l = config + strlen(config);
4044     for (;;) {
4045       for (;;)
4046         if (config >= l) goto done_config;
4047         else if (!ISSPACE(*config)) break;
4048         else config++;
4049
4050       for (p = config + 1; p < l && !ISSPACE(*p); p++);
4051       n = p - config;
4052
4053       for (bt = betab; bt->name; bt++)
4054         if (STRNCMP(config, ==, bt->name, n) && !bt->name[n])
4055           goto found_byname;
4056       debug("unknown backend `%.*s'", (int)n, config); goto next_config;
4057     found_byname:
4058       tty = bt->init(fp); if (tty) goto found;
4059       debug("failed to initialize `%s'", bt->name);
4060     next_config:
4061       config += n;
4062     }
4063   done_config:;
4064   } else if (backends)
4065     while (*backends) {
4066       for (bt = betab; bt->name; bt++)
4067         if (*backends == bt->code) goto found_bycode;
4068       debug("unknown backend code %u", *backends); goto next_code;
4069     found_bycode:
4070       tty = bt->init(fp); if (tty) goto found;
4071       debug("failed to initialize `%s'", bt->name);
4072     next_code:
4073       backends++;
4074     }
4075   else
4076     for (bt = betab; bt->name; bt++) {
4077       tty = bt->init(fp); if (tty) goto found;
4078       debug("failed to initialize `%s'", bt->name);
4079     }
4080
4081   debug("all backends failed"); goto end;
4082 found:
4083   debug("selected backend `%s'", bt->name);
4084   tty->fpin = fpin; tty->f = f; fp = 0;
4085 end:
4086   if (fp && !(f&TTF_BORROW)) fclose(fp);
4087   return (tty);
4088 }
4089
4090 /* --- @tty_close@ --- *
4091  *
4092  * Arguments:   @struct tty *tty@ = control block pointer
4093  *
4094  * Returns:     ---
4095  *
4096  * Use:         Closes a terminal, releasing the control block and any
4097  *              resources it held.  In particular, if the terminal was opened
4098  *              without @TTF_BORROW@, then the output stream is closed.
4099  */
4100
4101 void tty_close(struct tty *tty)
4102 {
4103   if (tty) {
4104     if (tty->fpout && !(tty->f&TTF_BORROW)) fclose(tty->fpout);
4105     tty->ops->release(tty); xfree(tty);
4106   }
4107 }
4108
4109 /* --- @tty_resized@ --- *
4110  *
4111  * Arguments:   @struct tty *tty@ = control block pointer
4112  *
4113  * Returns:     Zero if the size hasn't changed, %$+1$% if the size has
4114  *              changed, or %$-1$% on error.
4115  *
4116  * Use:         Update the terminal width and height.  Call this after
4117  *              receiving @SIGWINCH@, or otherwise periodically, to avoid
4118  *              making a mess.
4119  */
4120
4121 int tty_resized(struct tty *tty)
4122 {
4123   struct winsize ws;
4124
4125   if (!tty || !tty->fpout) { errno = ENOTTY; return (-1); }
4126   else if (ioctl(fileno(tty->fpout), TIOCGWINSZ, &ws)) return (-1);
4127   else if (tty->wd == ws.ws_col && tty->ht == ws.ws_row) return (0);
4128   else { tty->wd = ws.ws_col; tty->ht = ws.ws_row; return (1); }
4129 }
4130
4131 /*----- Terminal operations -----------------------------------------------*/
4132
4133 /* --- @tty_setattr@, @tty_setattrg@--- *
4134  *
4135  * Arguments:   @struct tty *tty@ = control block pointer
4136  *              @const struct gprintf_ops *gops, void *go@ = output
4137  *                      destination
4138  *              @const struct tty_attr *a@ = pointer to attributes to set, or
4139  *                      null
4140  *
4141  * Returns:     Zero on success, %$-1$% on error.
4142  *
4143  * Use:         Set the indicated formatting attributes on following output.
4144  *              If @a@ is null, then clear all attributes, just as if
4145  *              @a->f == 0@.
4146  *
4147  *              If you only ever request attributes which are advertised in
4148  *              the terminals capapbility masked, then you'll always get what
4149  *              you asked for.  Otherwise, the provided attributes will be
4150  *              `clamped', i.e., modified so as to accommodate the terminal's
4151  *              shortcomings.  In simple cases, unsupported attributes may
4152  *              just be dropped; but they can also be substituted, e.g.,
4153  *              single underlining for double, or approximate colours for
4154  *              unsupported colours.
4155  */
4156
4157 int tty_setattr(struct tty *tty, const struct tty_attr *a)
4158 {
4159   struct tty_attr aa;
4160
4161   if (!tty || !tty->fpout)
4162     return (-1);
4163   else {
4164     clamp_attr(&aa, a, tty->acaps);
4165     return (tty->ops->setattr(tty, &file_printops, tty->fpout, &aa));
4166   }
4167 }
4168
4169 int tty_setattrg(struct tty *tty,
4170                  const struct gprintf_ops *gops, void *go,
4171                  const struct tty_attr *a)
4172 {
4173   struct tty_attr aa;
4174
4175   if (!tty)
4176     return (-1);
4177   else {
4178     clamp_attr(&aa, a, tty->acaps);
4179     return (tty->ops->setattr(tty, gops, go, &aa));
4180   }
4181 }
4182
4183 /* --- @tty_setattrlist@, @tty_setattrlistg@ --- *
4184  *
4185  * Arguments:   @struct tty *tty@ = control block pointer
4186  *              @const struct gprintf_ops *gops, void *go@ = output
4187  *                      destination
4188  *              @const struct tty_attrlist *aa@ = pointer to attribute list
4189  *                      `menu'
4190  *
4191  * Returns:     Zero on success, %$-1$% on error.
4192  *
4193  * Use:         Search the list for an entry matching the terminal's
4194  *              capabilities, i.e., @tty->acaps&a->cap_mask == a->cap_eq@.
4195  *              The attributes in the first such entry are set, as if by
4196  *              @tty_setattr@.
4197  *
4198  *              The list is terminated by an entry with @cap_mask == 0@ --
4199  *              though it will be checked like any other before ending the
4200  *              search.  In particular, this means that an entry with
4201  *              @cap_mask == cap_eq == 0@ is a `catch-all', and its
4202  *              attributes will be set if no earlier matching entry could be
4203  *              found, while an entry with @cap_mask == 0@ and @cap_eq != 0@
4204  *              terminates the search without setting any attributes.
4205  */
4206
4207 int tty_setattrlist(struct tty *tty, const struct tty_attrlist *aa)
4208 {
4209   if (!tty || !tty->fpout) return (-1);
4210   else return (tty_setattrlistg(tty, &file_printops, tty->fpout, aa));
4211 }
4212
4213 int tty_setattrlistg(struct tty *tty,
4214                      const struct gprintf_ops *gops, void *go,
4215                      const struct tty_attrlist *aa)
4216 {
4217   if (!tty) return (-1);
4218   for (;; aa++)
4219     if ((tty->acaps&aa->cap_mask) == aa->cap_eq)
4220       return (tty->ops->setattr(tty, gops, go, &aa->attr));
4221     else if (!aa->cap_mask)
4222       return (0);
4223 }
4224
4225 /* --- @tty_setmodes@, @tty_setmodesg@ --- *
4226  *
4227  * Arguments:   @struct tty *tty@ = control block pointer
4228  *              @const struct gprintf_ops *gops, void *go@ = output
4229  *                      destination
4230  *              @uint32 modes_bic, modes_xor@ = masks to apply to the modes
4231  *                      settings
4232  *
4233  * Returns:     Zero on success, %$-1$% on error.
4234  *
4235  * Use:         Adjust the terminal display modes: specifically, the modes
4236  *              are adjusted to be @(modes&~modes_bic) ^ modes_xor@.
4237  *              Mode bits which aren't supported by the terminal are
4238  *              ignored.
4239  */
4240
4241 int tty_setmodes(struct tty *tty, uint32 modes_bic, uint32 modes_xor)
4242 {
4243   if (!tty || !tty->fpout) return (-1);
4244   else return (tty->ops->setmodes(tty, &file_printops, tty->fpout,
4245                                   modes_bic, modes_xor));
4246 }
4247
4248 int tty_setmodesg(struct tty *tty,
4249                   const struct gprintf_ops *gops, void *go,
4250                   uint32 modes_bic, uint32 modes_xor)
4251 {
4252   if (!tty) return (-1);
4253   else return (tty->ops->setmodes(tty, gops, go, modes_bic, modes_xor));
4254 }
4255
4256 /* --- @tty_move@, @tty_moveg@ --- *
4257  *
4258  * Arguments:   @struct tty *tty@ = control block pointer
4259  *              @const struct gprintf_ops *gops, void *go@ = output
4260  *                      destination
4261  *              @unsigned orig@ = origin
4262  *              @int y, x@ = new cursor position
4263  *
4264  * Returns:     Zero on success, %$-1$% on error.
4265  *
4266  * Use:         Move the cursor.  Coordinates are numbered starting with 0.
4267  *
4268  *              The @y@ position is interpreted relative to the origin given
4269  *              by @orig@: if @TTOF_YCUR@ is set, then the motion is relative
4270  *              to the current cursor row; otherwise, it is relative to
4271  *              the top `home' row.  Similarly, the @x@ position is
4272  *              interpreted relative to the current cursor column if
4273  *              @TTOF_XCUR is set, otherwise relative to the leftmost
4274  *              column.
4275  *
4276  *              Not all terminals are capable of all kinds of motions:
4277  *              @TTCF_ABSMV@ is set if absolute motion is possible, and
4278  *              @TTCF_RELMV@ is set if relative motion is possible.  The
4279  *              @TTCF_MIXMV@ bit indicates that the combination of absolute-y
4280  *              and relative-x motion is possible; note that the combination
4281  *              of relative-y and absolute-x is always possible if relative
4282  *              motion is possible at all.
4283  *
4284  *              The above notwithstanding, all terminals are assumed capable
4285  *              of moving the cursor to the start of either the current line
4286  *              @tty_move(tty, TTOF_YCUR | TTOF_XHOME, 0, 0)@, or of the next
4287  *              line @tty_move(tty, TTOF_YCUR | TTOF_XHOME, +1, 0)@.
4288  */
4289
4290 int tty_move(struct tty *tty, unsigned orig, int y, int x)
4291 {
4292   if (!tty || !tty->fpout) return (-1);
4293   else return (tty->ops->move(tty, &file_printops, tty->fpout, orig, y, x));
4294 }
4295
4296 int tty_moveg(struct tty *tty,
4297               const struct gprintf_ops *gops, void *go,
4298               unsigned orig, int y, int x)
4299 {
4300   if (!tty) return (-1);
4301   else return (tty->ops->move(tty, gops, go, orig, y, x));
4302 }
4303
4304 /* --- @tty_repeat@, @tty_repeatg@ --- *
4305  *
4306  * Arguments:   @struct tty *tty@ = control block pointer
4307  *              @const struct gprintf_ops *gops, void *go@ = output
4308  *                      destination
4309  *              @int ch@ = character to write
4310  *              @unsigned n@ = number of copies
4311  *
4312  * Returns:     Zero on success, %$-1$% on error.
4313  *
4314  * Use:         Write @n@ copies of the character @ch@ to the terminal.
4315  *              (Some terminals have a special control sequence for doing
4316  *              this.)
4317  */
4318
4319 int tty_repeat(struct tty *tty, int ch, unsigned n)
4320 {
4321   if (!tty || !tty->fpout) return (-1);
4322   else return (tty->ops->repeat(tty, &file_printops, tty->fpout, ch, n));
4323 }
4324
4325 int tty_repeatg(struct tty *tty,
4326                 const struct gprintf_ops *gops, void *go,
4327                 int ch, unsigned n)
4328 {
4329   if (!tty) return (-1);
4330   else return (tty->ops->repeat(tty, gops, go, ch, n));
4331 }
4332
4333 /* --- @tty_erase@, @tty_eraseg@ --- *
4334  *
4335  * Arguments:   @struct tty *tty@ = control block pointer
4336  *              @const struct gprintf_ops *gops, void *go@ = output
4337  *                      destination
4338  *              @unsigned f@ = flags
4339  *
4340  * Returns:     Zero on success, %$-1$% on error.
4341  *
4342  * Use:         Erase portions of the current line or the whole display.
4343  *
4344  *              If @TTEF_DSP@ is set, then the whole display is affected.  If
4345  *              @TTEF_BEGIN@ is set, then the display is erased starting from
4346  *              the top left and ending at and including the cursor
4347  *              position.  If @TTEF_END@ is set, then the display is erased
4348  *              starting from and including the cursor position, and ending
4349  *              at the bottom right.  If both flags are set, then,
4350  *              additionally, the cursor is moved to its `home' position at
4351  *              the top left.
4352  *
4353  *              If @TTF_DSP@ is not set, then the current line is affected.
4354  *              If @TTEF_BEGIN@ is set, then the line is erased starting from
4355  *              the left and ending at and including the cursor position.  If
4356  *              @TTEF_END@ is set, then the line is erased starting from and
4357  *              including the cursor position, and ending at the right hand
4358  *              side.
4359  *
4360  *              If the @TTCF_BGER@ capability is set, then the erased
4361  *              positions take on the current background colour; otherwise,
4362  *              they have the default background colour.
4363  */
4364
4365 int tty_erase(struct tty *tty, unsigned f)
4366 {
4367   if (!tty || !tty->fpout) return (-1);
4368   else return (tty->ops->erase(tty, &file_printops, tty->fpout, f));
4369 }
4370
4371 int tty_eraseg(struct tty *tty,
4372                const struct gprintf_ops *gops, void *go,
4373                unsigned f)
4374 {
4375   if (!tty) return (-1);
4376   else return (tty->ops->erase(tty, gops, go, f));
4377 }
4378
4379 /* --- @tty_erch@, @tty_erchg@ --- *
4380  *
4381  * Arguments:   @struct tty *tty@ = control block pointer
4382  *              @const struct gprintf_ops *gops, void *go@ = output
4383  *                      destination
4384  *              @unsigned n@ = number of characters to erase
4385  *
4386  * Returns:     Zero on success, %$-1$% on error.
4387  *
4388  * Use:         Erase a number of characters, starting from and including the
4389  *              current cursor position.
4390  *
4391  *              If the @TTCF_BGER@ capability is set, then the erased
4392  *              positions take on the current background colour; otherwise,
4393  *              they have the default background colour.
4394  */
4395
4396 int tty_erch(struct tty *tty, unsigned n)
4397 {
4398   if (!tty || !tty->fpout) return (-1);
4399   else return (tty->ops->erch(tty, &file_printops, tty->fpout, n));
4400 }
4401
4402 int tty_erchg(struct tty *tty,
4403               const struct gprintf_ops *gops, void *go,
4404               unsigned n)
4405 {
4406   if (!tty) return (-1);
4407   else return (tty->ops->erch(tty, gops, go, n));
4408 }
4409
4410 /* --- @tty_ins@, @tty_insg@ --- *
4411  *
4412  * Arguments:   @struct tty *tty@ = control block pointer
4413  *              @const struct gprintf_ops *gops, void *go@ = output
4414  *                      destination
4415  *              @unsigned f@ = flags
4416  *              @unsigned n@ = number of items to insert
4417  *
4418  * Returns:     Zero on success, %$-1$% on error.
4419  *
4420  * Use:         Insert a number of blank characters or lines.
4421  *
4422  *              If @TTIDF_LN@ is set, then insert @n@ blank lines above the
4423  *              current line.  The cursor must be at the far left of the
4424  *              line.
4425  *
4426  *              Otherwise, insert @n@ empty character spaces at the cursor
4427  *              position.
4428  */
4429
4430 int tty_ins(struct tty *tty, unsigned f, unsigned n)
4431 {
4432   if (!tty || !tty->fpout) return (-1);
4433   else return (tty->ops->ins(tty, &file_printops, tty->fpout, f, n));
4434 }
4435
4436 int tty_insg(struct tty *tty,
4437              const struct gprintf_ops *gops, void *go,
4438              unsigned f, unsigned n)
4439 {
4440   if (!tty) return (-1);
4441   else return (tty->ops->ins(tty, gops, go, f, n));
4442 }
4443
4444 /* --- @tty_inch@ --- *
4445  *
4446  * Arguments:   @struct tty *tty@ = control block pointer
4447  *              @const struct gprintf_ops *gops, void *go@ = output
4448  *                      destination
4449  *              @int ch@ = character to insert
4450  *
4451  * Returns:     Zero on success, %$-1$% on error.
4452  *
4453  * Use:         Insert a single character.
4454  *
4455  *              If the @TTMF_INS@ mode is advertised, then insert mode must
4456  *              be set before calling this function.
4457  */
4458
4459 int tty_inch(struct tty *tty, int ch)
4460 {
4461   if (!tty || !tty->fpout) return (-1);
4462   else return (tty->ops->inch(tty, &file_printops, tty->fpout, ch));
4463 }
4464
4465 int tty_inchg(struct tty *tty,
4466               const struct gprintf_ops *gops, void *go,
4467               int ch)
4468 {
4469   if (!tty) return (-1);
4470   else return (tty->ops->inch(tty, gops, go, ch));
4471 }
4472
4473 /* --- @tty_del@, @tty_delg@ --- *
4474  *
4475  * Arguments:   @struct tty *tty@ = control block pointer
4476  *              @const struct gprintf_ops *gops, void *go@ = output
4477  *                      destination
4478  *              @unsigned f@ = flags
4479  *              @unsigned n@ = number of items to delete
4480  *
4481  * Returns:     Zero on success, %$-1$% on error.
4482  *
4483  * Use:         Delete a number of characters or lines.
4484  *
4485  *              If @TTIDF_LN@ is set, then delete @n@ blank lines, starting
4486  *              with the current line line.  The cursor must be at the far
4487  *              left of the line.
4488  *
4489  *              Otherwise, delete @n@ characters at the cursor position.
4490  */
4491
4492 int tty_del(struct tty *tty, unsigned f, unsigned n)
4493 {
4494   if (!tty || !tty->fpout) return (-1);
4495   else return (tty->ops->del(tty, &file_printops, tty->fpout, f, n));
4496 }
4497
4498 int tty_delg(struct tty *tty,
4499              const struct gprintf_ops *gops, void *go,
4500              unsigned f, unsigned n)
4501 {
4502   if (!tty) return (-1);
4503   else return (tty->ops->del(tty, gops, go, f, n));
4504 }
4505
4506 /* --- @tty_restore@, @tty_restoreg@ --- *
4507  *
4508  * Arguments:   @struct tty *tty@ = control block pointer
4509  *              @const struct gprintf_ops *gops, void *go@ = output
4510  *                      destination
4511  *              @const struct tty_state *st@ = state to restore
4512  *
4513  * Returns:     Zero on success, %$-1$% on error.
4514  *
4515  * Use:         Restore the terminal modes and attributes to match a
4516  *              state previously captured by copying @tty->st@.
4517  */
4518
4519 int tty_restore(struct tty *tty, const struct tty_state *st)
4520 {
4521   if (!tty || !tty->fpout) return (-1);
4522   else return (tty_restoreg(tty, &file_printops, tty->fpout, st));
4523 }
4524
4525 int tty_restoreg(struct tty *tty,
4526                  const struct gprintf_ops *gops, void *go,
4527                  const struct tty_state *st)
4528 {
4529   int rc;
4530
4531   if (!tty ||
4532       tty->ops->setmodes(tty, gops, go, MASK32, st->modes) ||
4533       tty->ops->setattr(tty, gops, go, &st->attr))
4534     { rc = -1; goto end; }
4535   rc = 0;
4536 end:
4537   return (rc);
4538 }
4539
4540 /*----- That's all, folks -------------------------------------------------*/