chiark / gitweb /
@@@ tvec and tty mess
[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 /*----- Operations table --------------------------------------------------*/
68
69 /* Incorporate the published control-block structure into our more elaborate
70  * object model.
71  */
72 #define TTY_BASEPFX struct tty tty
73 #define TTY_BASEUSFX struct tty tty
74
75 struct tty_ops {
76   void (*release)(struct tty */*tty*/);
77         /* Free any resources held by the backend. */
78
79   /* The following operations handle the correspondingly named interface
80    * functions.
81    */
82   int (*setattr)(struct tty */*tty*/,
83                  const struct gprintf_ops */*gops*/, void */*go*/,
84                  const struct tty_attr */*a*/);
85   int (*setmodes)(struct tty */*tty*/,
86                   const struct gprintf_ops */*gops*/, void */*go*/,
87                   uint32 /*modes_bic*/, uint32 /*modes_xor*/);
88   int (*move)(struct tty */*tty*/,
89               const struct gprintf_ops */*gops*/, void */*go*/,
90               unsigned /*orig*/, int /*y*/, int /*x*/);
91   int (*repeat)(struct tty */*tty*/,
92                 const struct gprintf_ops */*gops*/, void */*go*/,
93                 int /*ch*/, unsigned /*n*/);
94   int (*erase)(struct tty */*tty*/,
95                const struct gprintf_ops */*gops*/, void */*go*/,
96                unsigned /*f*/);
97   int (*erch)(struct tty */*tty*/,
98               const struct gprintf_ops */*gops*/, void */*go*/,
99               unsigned /*n*/);
100   int (*ins)(struct tty */*tty*/,
101              const struct gprintf_ops */*gops*/, void */*go*/,
102              unsigned /*f*/, unsigned /*n*/);
103   int (*inch)(struct tty */*tty*/,
104               const struct gprintf_ops */*gops*/, void */*go*/,
105               int /*ch*/);
106   int (*del)(struct tty */*tty*/,
107              const struct gprintf_ops */*gops*/, void */*go*/,
108              unsigned /*f*/, unsigned /*n*/);
109 };
110 #define TTY_BASEOPSPFX struct tty_ops tty
111 #define TTY_BASEOPSUXFX struct tty_ops tty
112
113 /*----- Common support machinery ------------------------------------------*/
114
115 /* --- @CHECK@ --- *
116  *
117  * Arguments@   @expr@ = expression to evaluate
118  *
119  * Use:         Evaluate @expr@.  If the result is (strictly) negative, then
120  *              set @rc = -1@ and transfer control to the label @end@.
121  */
122
123 #define CHECK(expr) do { if ((expr) < 0) { rc = -1; goto end; } } while (0)
124
125 /* --- @debug@ --- *
126  *
127  * Arguments:   @const char *fmt@ = format control string
128  *              @...@ = format arguemnts
129  *
130  * Returns:     ---
131  *
132  * Use:         Maybe report a debugging message to standard error.
133  */
134
135 static PRINTF_LIKE(1, 2) void debug(const char *fmt, ...)
136 {
137   const char *p;
138   va_list ap;
139
140   p = getenv("MLIB_TTY_DEBUG");
141   if (p && *p != 'n' && *p != '0') {
142     va_start(ap, fmt);
143     fputs("mLib TTY: ", stderr);
144     vfprintf(stderr, fmt, ap);
145     fputc('\n', stderr);
146     va_end(ap);
147   }
148 }
149
150 /* --- @common_init@ --- *
151  *
152  * Arguments:   @struct tty *tty@ = pointer to terminal control block
153  *              @FILE *fp@ = output file stream
154  *
155  * Returns:     ---
156  *
157  * Use:         Perform general initialization on the terminal control
158  *              block.
159  *
160  *              Specifically, this fills in the @fpout@, @baud@, @ht@, and
161  *              @wd@ slots.  The width and height come from the kernel, or,
162  *              failing that, the environment.
163  */
164
165 static void common_init(struct tty *tty, FILE *fp)
166 {
167   static const struct baudtab { speed_t code; unsigned baud; } baudtab[] = {
168   /*
169      ;;; The baud-rate table is very boring to type.  To make life less
170      ;;; awful, put the rates in this list and evaluate the code to get Emacs
171      ;;; to regenerate it.
172
173      (let ((bauds '(50 75 110 134 150 200 300 600 1200 1800 2400 4800 9600
174                     19200 38400 57600 115200 230400 460800 500000 576000
175                     921600 1000000 1152000 1500000 2000000 2500000 3000000
176                     3500000 4000000)))
177        (save-excursion
178          (goto-char (point-min))
179          (search-forward (concat "***" "BEGIN baudlist" "***"))
180          (beginning-of-line 2)
181          (delete-region (point)
182                         (progn
183                           (search-forward "***END***")
184                           (beginning-of-line)
185                           (point)))
186          (dolist (baud (sort (copy-list bauds) #'<))
187            (insert (format "#ifdef B%d\n    { B%d, %d },\n#endif\n"
188                            baud baud baud)))))
189   */
190     /***BEGIN baudlist***/
191 #ifdef B50
192     { B50, 50 },
193 #endif
194 #ifdef B75
195     { B75, 75 },
196 #endif
197 #ifdef B110
198     { B110, 110 },
199 #endif
200 #ifdef B134
201     { B134, 134 },
202 #endif
203 #ifdef B150
204     { B150, 150 },
205 #endif
206 #ifdef B200
207     { B200, 200 },
208 #endif
209 #ifdef B300
210     { B300, 300 },
211 #endif
212 #ifdef B600
213     { B600, 600 },
214 #endif
215 #ifdef B1200
216     { B1200, 1200 },
217 #endif
218 #ifdef B1800
219     { B1800, 1800 },
220 #endif
221 #ifdef B2400
222     { B2400, 2400 },
223 #endif
224 #ifdef B4800
225     { B4800, 4800 },
226 #endif
227 #ifdef B9600
228     { B9600, 9600 },
229 #endif
230 #ifdef B19200
231     { B19200, 19200 },
232 #endif
233 #ifdef B38400
234     { B38400, 38400 },
235 #endif
236 #ifdef B57600
237     { B57600, 57600 },
238 #endif
239 #ifdef B115200
240     { B115200, 115200 },
241 #endif
242 #ifdef B230400
243     { B230400, 230400 },
244 #endif
245 #ifdef B460800
246     { B460800, 460800 },
247 #endif
248 #ifdef B500000
249     { B500000, 500000 },
250 #endif
251 #ifdef B576000
252     { B576000, 576000 },
253 #endif
254 #ifdef B921600
255     { B921600, 921600 },
256 #endif
257 #ifdef B1000000
258     { B1000000, 1000000 },
259 #endif
260 #ifdef B1152000
261     { B1152000, 1152000 },
262 #endif
263 #ifdef B1500000
264     { B1500000, 1500000 },
265 #endif
266 #ifdef B2000000
267     { B2000000, 2000000 },
268 #endif
269 #ifdef B2500000
270     { B2500000, 2500000 },
271 #endif
272 #ifdef B3000000
273     { B3000000, 3000000 },
274 #endif
275 #ifdef B3500000
276     { B3500000, 3500000 },
277 #endif
278 #ifdef B4000000
279     { B4000000, 4000000 },
280 #endif
281     /***END***/
282     { 0, 0 }
283   };
284
285   const struct baudtab *b;
286   const char *p; int n;
287   struct termios c;
288   speed_t code;
289
290   /* Save the output stream. */
291   tty->fpout = fp;
292
293   /* Determine the output baud rate.  Unhelpfully, the kernel provides a
294    * weird code, so we have to convert it into an actual rate in bits per
295    * second.
296    */
297   tty->baud = 0; tty->wd = tty->ht = 0;
298   if (fp && !tcgetattr(fileno(fp), &c)) {
299     code = cfgetospeed(&c);
300     for (b = baudtab; b->baud; b++)
301       if (b->code == code) { tty->baud = b->baud; goto found_baud; }
302   found_baud:
303     tty_resized(tty);
304   }
305
306   /* If the kernel didn't tell us the terminal dimensions, try to read them
307    * from the environment.
308    */
309   if (!tty->wd)
310     { p = getenv("COLUMNS"); if (p) { n = atoi(p); if (n) tty->wd = n; } }
311   if (!tty->ht)
312     { p = getenv("LINES");   if (p) { n = atoi(p); if (n) tty->ht = n; } }
313 }
314
315 /* --- @env_colour_caps@ --- *
316  *
317  * Arguments:   @unsigned *caps_inout@ = attribute capabilities to update
318  *              @unsigned f@ = flags
319  *
320  * Returns:     ---
321  *
322  * Use:         Check the %|FORCE_COLOR|% environment variable and update the
323  *              capabilities as required.
324  *
325  *              The %|FORCE_COLOR|% variable originates with the Node
326  *              community, with two objectives: (a) to convey policy
327  *              regarding whether to produce coloured output, and (b) to
328  *              describe the colour capabilities of the terminal, because the
329  *              traditional mechanisms are deemed inadequate.
330  *
331  *              The following values have defined meanings.
332  *
333  *                * Unset or empty: no effect.
334  *
335  *                * %|0|%: monochrome; don't produce colour.
336  *
337  *                * %|1|%: 16 colours; 1-bit-per-channel colours are
338  *                  available, with an additional common brightness bit.
339  *
340  *                * %|2|%: 256 colours; the `xterm' 256-bit palette is
341  *                  available, consisting of the 1-bit-per-channel colours
342  *                  with common brightness bit, a 6 Ã— 6 Ã— 6 colour cube, and
343  *                  a 24-level greyscale ramp.
344  *
345  *                * %|3|%: full 24-bit colour.
346  *
347  *                * Anything else: a request to use colour if available.
348  *                  This is ignored here, in the expectation that it will be
349  *                  given effect elsewhere, e.g., by @ttycolour_enablep@.
350  *
351  *              If @ECCF_SET@ is set, then set or clear capabilities as
352  *              required.  Otherwise, clear capability bits which are denied
353  *              by the variable setting, but no bits will be set.  (This
354  *              latter is necessary for backends which use terminal
355  *              databases, since they can't be expected to make up the
356  *              necessary control sequences for themselves.)
357  */
358
359 #define ECCF_SET 1u
360 static void env_colour_caps(unsigned *caps_inout, unsigned f)
361 {
362   const char *p;
363   unsigned caps = *caps_inout, mask;
364
365   p = getenv("FORCE_COLOR"); if (!p) return;
366   switch (*p) {
367     case '0':
368       mask = 0;
369       break;
370     case '1':
371       mask = TTACF_FG | TTACF_BG | TTACF_1BPC | TTACF_1BPCBR;
372       break;
373     case '2':
374       mask = TTACF_FG | TTACF_BG |
375              TTACF_1BPC | TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
376       break;
377     case '3':
378       mask = TTACF_FG | TTACF_BG |
379              TTACF_1BPC | TTACF_1BPCBR | TTACF_8BPC;
380       break;
381     default:
382       return;
383   }
384   if (!(f&ECCF_SET)) caps &= mask;
385   else caps = (caps&~(TTACF_CSPCMASK | TTACF_FG | TTACF_BG)) | mask;
386
387   *caps_inout = caps;
388 }
389
390 /* --- @clamp_colours@ --- *
391  *
392  * Arguments:   @uint32 *space_out, *colour_out@ = selected space and colour
393  *              @uint32 space, colour@ = requested space and colour
394  *              @uint32 acaps@ = terminal's attribute capability mask
395  *
396  * Returns:     ---
397  *
398  * Use:         Select the best approximation to the requested colour which
399  *              can be accommodated by the terminal.
400  */
401
402 /* #define DEBUG_CLAMP */
403
404 static void clamp_colours(uint32 *space_out, uint32 *colour_out,
405                           uint32 space, uint32 colour, uint32 acaps)
406 {
407   unsigned r, g, b, rr, gg, bb, y, t, u;
408   uint32 best_colour = 0, best_space; int best_error;
409
410 #ifdef DEBUG_CLAMP
411 #  define D(x) x
412 #else
413 #  define D(x)
414 #endif
415
416   /* Check the colour space.  If it's one that the terminal can handle, then
417    * return the colour unchanged.  Otherwise, extract the channel components
418    * for the next step.
419    */
420   switch (space) {
421
422     case TTCSPC_NONE:
423       /* No colour wanted at all.  There's nothing to do here. */
424
425       *space_out = TTCSPC_NONE; *colour_out = 0;
426       return;
427
428     case TTCSPC_1BPC:
429       /* One-bit-per-channel colour.
430        *
431        * There's no standardized mapping for these; indeed, they're commonly
432        * configurable by users.  Since there are also `bright' versions of
433        * each, it's probably not right to just map zero to zero and one to
434        * full-scale.
435        */
436
437       if (colour >= 8) goto inval;
438       if (acaps&(TTACF_1BPC | TTACF_1BPCBR))
439         { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
440
441 #define C1BPC_FULL 0xcc
442 #define C1BPC_CVT(col, bit)                                             \
443         (C1BPC_FULL&((((col)&(1 << (bit))) << (8 - (bit))) - 1))
444       r = C1BPC_CVT(colour, 0);
445       g = C1BPC_CVT(colour, 1);
446       b = C1BPC_CVT(colour, 2);
447       break;
448
449     case TTCSPC_1BPCBR:
450       /* One-bit-per-channel colour, with global brightness.  Again, there's
451        * no standardized mapping.  Apply a boost across all three channels.
452        */
453
454       if (colour >= 16) goto inval;
455       if (!(colour&TT1BPC_BRI) && (acaps&(TTACF_1BPC | TTACF_1BPCBR)))
456         { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
457       else if (acaps&TTACF_1BPCBR)
458         { *space_out = TTCSPC_1BPCBR; *colour_out = colour; return; }
459
460 #define C1BPC_BRIGHT 0x33
461       r = C1BPC_CVT(colour, 0) + C1BPC_BRIGHT;
462       g = C1BPC_CVT(colour, 1) + C1BPC_BRIGHT;
463       b = C1BPC_CVT(colour, 2) + C1BPC_BRIGHT;
464 #undef C1BPC_CVT
465       break;
466
467     case TTCSPC_4LPC:
468       /* Four-levels-per-channel colour.  These are part of an `indexed'
469        * colour space which is theoretically controlled by applications but
470        * (a) that's rare, and (b) worrying about that won't do us any good.
471        *
472        * Each channel has four levels, but they're not assigned linearly.
473        */
474
475       if (colour >= 64) goto inval;
476       if (acaps&TTACF_4LPC)
477         { *space_out = TTCSPC_4LPC; *colour_out = colour; return; }
478
479 #define C4LPC_L0 0
480 #define C4LPC_L1 139
481 #define C4LPC_L2 205
482 #define C4LPC_L3 255
483 #define C4LPC_CVT(ch) (t = (ch), t == 0 ? C4LPC_L0 :                    \
484                                  t == 1 ? C4LPC_L1 :                    \
485                                  t == 2 ? C4LPC_L2 :                    \
486                                           C4LPC_L3)
487       r = C4LPC_CVT(TTCOL_2BR(colour));
488       g = C4LPC_CVT(TTCOL_2BG(colour));
489       b = C4LPC_CVT(TTCOL_2BB(colour));
490 #undef C4LPC_CVT
491       break;
492
493     case TTCSPC_8LGS:
494       /* Eight-levels greyscale.  Again, these are part of an `indexed'
495        * colour space which is under application control, and, again, the
496        * levels aren't linear, but they're not far off linear.
497        */
498       if (colour >= 8) goto inval;
499       if (acaps&TTACF_8LGS)
500         { *space_out = TTCSPC_8LGS; *colour_out = colour; return; }
501
502       r = g = b = 255*(colour ? colour + 3 : 2)/11;
503       break;
504
505     case TTCSPC_6LPC:
506       /* Six-levels-per-channel colour.  Again, `indexed' colour space under
507        * application control.  This time the mapping is essentially liner.
508        */
509
510       if (colour >= 216) goto inval;
511       if (acaps&TTACF_6LPC)
512         { *space_out = TTCSPC_6LPC; *colour_out = colour; return; }
513
514 #define C6LPC_CVT(ch) (t = (ch), t ? (40*t + 55) : 0)
515       r = C6LPC_CVT(TTCOL_6LR(colour));
516       g = C6LPC_CVT(TTCOL_6LG(colour));
517       b = C6LPC_CVT(TTCOL_6LB(colour));
518 #undef C6LPC_CVT
519       break;
520
521     case TTCSPC_24LGS:
522       /* Twenty-four-levels greyscale.  Same story. */
523
524       if (colour >= 24) goto inval;
525       if (acaps&TTACF_24LGS)
526         { *space_out = TTCSPC_24LGS; *colour_out = colour; return; }
527
528       r = g = b = 10*colour + 8;
529       break;
530
531     case TTCSPC_8BPC:
532       /* Eight-bits-per-channel colour.  No conversion to apply here. */
533
534       if (colour >= 0x01000000) goto inval;
535       if (acaps&TTACF_8BPC)
536         { *space_out = TTCSPC_8BPC; *colour_out = colour; return; }
537
538       r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
539       break;
540
541     default:
542       /* Anything else. */
543
544       goto inval;
545   }
546
547   /* We didn't get an exact match, so we'll have to make do with what we've
548    * got.
549    */
550   best_error = -1; best_space = TTCSPC_NONE; best_colour = 0;
551   D( fprintf(stderr, "\n;; APPROX space %u, colour 0x%lx = %u/%u/%u\n",
552              space, (unsigned long)colour, r, g, b); )
553
554   /* Approximate colour weightings for human colour vision. */
555 #define RWT 2
556 #define GWT 4
557 #define BWT 1
558 #define TOTWT (RWT + GWT + BWT)
559
560   /* Determine the optimal grey approximation for this colour. */
561   y = (RWT*r + GWT*g + BWT*b + TOTWT/2)/TOTWT;
562
563 #define TRY_APPROX(rr, gg, bb, spc, clr) do {                           \
564   /* If the approximation (RR, GG, BB) is closer to the current best    \
565    * then accept SPC and CLR as the new best space/colour option.       \
566    */                                                                   \
567                                                                         \
568   int _r_err = (rr) - r, _g_err = (gg) - g, _b_err = (bb) - b;          \
569   int _err;                                                             \
570                                                                         \
571   if (_r_err < 0) _r_err = -_r_err;                                     \
572   if (_g_err < 0) _g_err = -_g_err;                                     \
573   if (_b_err < 0) _b_err = -_b_err;                                     \
574                                                                         \
575   _err = RWT*_r_err + GWT*_g_err + BWT*_b_err;                          \
576   D( fprintf(stderr,                                                    \
577              ";;   candidate space %u, colour 0x%lx = %u/%u/%u; "       \
578                   "error = %d\n",                                       \
579              (spc), (unsigned long)(clr), (rr), (gg), (bb), _err); )    \
580   if (best_error < 0 || _err < best_error) {                            \
581     best_error = _err; best_space = (spc); best_colour = (clr);         \
582     D( fprintf(stderr, ";;\tNEW BEST APPROXIMATION\n"); )               \
583   }                                                                     \
584 } while (0)
585
586   if (!(acaps&(TTACF_4LPC | TTACF_6LPC | TTACF_8BPC))) {
587     /* The one-bit-per-channel colours are very variable, but there's little
588      * choice, so we'll have to try.  We assume the same mapping as on the
589      * way in.
590      */
591
592     if (acaps&(TTACF_1BPC | TTACF_1BPCBR)) {
593       /* One-bit-per-channel colour. */
594
595 #define C1BPC_APPROX(cc, c, bit) do {                                   \
596       if ((c) <= C1BPC_FULL/2) (cc) = 0;                                \
597       else { (cc) = C1BPC_FULL; t |= (bit); }                           \
598 } while (0)
599       t = 0;
600       C1BPC_APPROX(rr, r, TT1BPC_RED);
601       C1BPC_APPROX(gg, g, TT1BPC_GRN);
602       C1BPC_APPROX(bb, b, TT1BPC_BLU);
603 #undef C1BPC_APPROX
604       TRY_APPROX(rr, gg, bb, TTCSPC_1BPC, t);
605     }
606
607     if (acaps&TTACF_1BPCBR) {
608       /* One-bit-per-channel colour, with global brightness. */
609
610 #define C1BPCBR_APPROX(cc, c, bit) do {                                 \
611       if ((c) <= C1BPC_FULL/2 + C1BPC_BRIGHT) (cc) = C1BPC_BRIGHT;      \
612       else { (cc) = 255; t |= (bit); }                                  \
613 } while (0)
614       t = TT1BPC_BRI;
615       C1BPCBR_APPROX(rr, r, TT1BPC_RED);
616       C1BPCBR_APPROX(gg, g, TT1BPC_GRN);
617       C1BPCBR_APPROX(bb, b, TT1BPC_BLU);
618 #undef C1BPCBR_APPROX
619       TRY_APPROX(rr, gg, bb, TTCSPC_1BPCBR, t);
620     }
621   }
622
623   if (acaps&TTACF_4LPC) {
624     /* Four-levels-per-channel colour. */
625
626 #define C4LPC_APPROX(cc, c, sh) do {                                    \
627     unsigned _c = (c);                                                  \
628                                                                         \
629     if (_c > (C4LPC_L2 + C4LPC_L3)/2)                                   \
630       { (cc) = C4LPC_L3; t |= 3 << (sh); }                              \
631     else if (_c > (C4LPC_L1 + C4LPC_L2)/2)                              \
632       { (cc) = C4LPC_L2; t |= 2 << (sh); }                              \
633     else if (_c > (C4LPC_L0 + C4LPC_L1)/2)                              \
634       { (cc) = C4LPC_L1; t |= 1 << (sh); }                              \
635     else                                                                \
636       (cc) = C4LPC_L0;                                                  \
637 } while (0)
638     t = 0;
639     C4LPC_APPROX(rr, r, 4);
640     C4LPC_APPROX(gg, g, 2);
641     C4LPC_APPROX(bb, b, 0);
642 #undef C4LPC_APPROX
643     TRY_APPROX(rr, gg, bb, TTCSPC_4LPC, t);
644   }
645
646   if (acaps&TTACF_8LGS) {
647     /* Eight-levels greyscale. */
648
649     u = (11*y)/255;
650     if (u <= 2) { u = 2; t = 0; }
651     else if (u == 3) { u = 4; t = 1; }
652     else if (u == 11) { u = 10; t = 7; }
653     else t = u - 3;
654     u = (255*u)/11; TRY_APPROX(u, u, u, TTCSPC_8LGS, t);
655   }
656
657   if (acaps&TTACF_6LPC) {
658     /* Six-levels-per-channel colour. */
659
660 #define C6LPC_APPROX(cc, c, f) do {                                     \
661     unsigned _c = (c);                                                  \
662                                                                         \
663     if (_c < 36) (cc) = 0;                                              \
664     else { u = (_c - 36)/40; t += (f)*u; (cc) = 40*u + 55; }            \
665 } while (0)
666     t = 0;
667     C6LPC_APPROX(rr, r, 36);
668     C6LPC_APPROX(gg, g,  6);
669     C6LPC_APPROX(bb, b,  1);
670 #undef C6LPC_APPROX
671     TRY_APPROX(rr, gg, bb, TTCSPC_6LPC, t);
672   }
673
674   if (acaps&TTACF_24LGS) {
675     /* Twenty-four-levels greyscale. */
676
677     if (y < 3) { t = 0; u = 8; }
678     else if (y >= 243) { t = 23; u = 238; }
679     else { t = (y - 3)/10; u = 10*t + 8; }
680     TRY_APPROX(u, u, u, TTCSPC_24LGS, t);
681   }
682
683   if (acaps&TTACF_8BPC) {
684     /* Eight-bits-per-channel colour. */
685
686     if (best_error) {
687       D( fprintf(stderr, ";;   accept exact 8bpc colour\n"); )
688       best_error = 0; best_space = TTCSPC_8BPC;
689       best_colour = TTCOL_MK8B(r, g, b);
690     }
691   }
692
693   /* Done. */
694   *space_out = best_space; *colour_out = best_colour;
695   return;
696
697 inval:
698   /* Invalid colour selection.  Ignore this. */
699   *space_out = TTCSPC_NONE; *colour_out = 0;
700
701 #undef C1BPC_FULL
702 #undef C1BPC_BRIGHT
703
704 #undef C4LPC_L0
705 #undef C4LPC_L1
706 #undef C4LPC_L2
707 #undef C4LPC_L3
708
709 #undef TRY_APPROX
710 #undef TRY_RGB
711 #undef TRY_GREY
712
713 #undef RWT
714 #undef GWT
715 #undef BWT
716 #undef TOTWT
717
718 #undef SET_RANGE
719 #undef CHECK_RANGE
720
721 #undef D
722 }
723
724 /* --- @clamp_attr@ --- *
725  *
726  * Arguments:   @struct tty_attr *a_out@ = selected attributes
727  *              @const struct tty_attr *a@ = requested attributes
728  *              @uint32 acaps@ = terminal's attribute capability mask
729  *
730  * Returns:     ---
731  *
732  * Use:         Select the closest approximation to the requested attributes
733  *              which can be accommodated by the terminal.
734  */
735
736 static void clamp_attr(struct tty_attr *a_out,
737                        const struct tty_attr *a, uint32 acaps)
738 {
739   uint32 ff = 0, f = a ? a->f : 0, t;
740
741   /* Line attributes. */
742   t = (f&TTAF_LNMASK) >> TTAF_LNSHIFT;
743   switch (t) {
744     case TTLN_NONE:
745       break;
746     case TTLN_ULINE:
747       if (!acaps&TTACF_ULINE) t = TTLN_NONE;
748       break;
749     case TTLN_UULINE:
750       if (acaps&TTACF_UULINE) ;
751       else if (acaps&TTACF_ULINE) t = TTLN_ULINE;
752       else t = TTLN_NONE;
753       break;
754     default:
755       t = TTLN_NONE;
756   }
757   ff |= t << TTAF_LNSHIFT;
758
759   /* Text weight. */
760   t = (f&TTAF_WTMASK) >> TTAF_WTSHIFT;
761   switch (t) {
762     case TTWT_MED: break;
763     case TTWT_BOLD: if (!(acaps&TTACF_BOLD)) t = TTWT_MED; break;
764     case TTWT_DIM: if (!(acaps&TTACF_DIM)) t = TTWT_MED; break;
765     default: t = TTWT_MED; break;
766   }
767   ff |= t << TTAF_WTSHIFT;
768
769   /* Other text attributes. */
770   if (acaps&TTACF_STRIKE) ff |= f&TTAF_STRIKE;
771   if (acaps&TTACF_ITAL) ff |= f&TTAF_ITAL;
772   if (acaps&TTACF_INVV) ff |= f&TTAF_INVV;
773
774   /* Foreground and background colours. */
775   if (!(acaps&TTACF_FG))
776     a_out->fg = 0;
777   else {
778     clamp_colours(&t, &a_out->fg,
779                   (f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a ? a->fg : 0,
780                   acaps);
781     ff |= t << TTAF_FGSPCSHIFT;
782   }
783   if (!(acaps&TTACF_BG))
784     a_out->bg = 0;
785   else {
786     clamp_colours(&t, &a_out->bg,
787                   (f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a ? a->bg : 0,
788                   acaps);
789     ff |= t << TTAF_BGSPCSHIFT;
790   }
791
792   /* All done. */
793   a_out->f = ff; a_out->_res0 = 0;
794 }
795
796 /* --- @stupid_repeat@ --- *
797  *
798  * Arguments:   @struct tty *tty@ = control block pointer
799  *              @const struct gprintf_ops *gops, void *go@ = output
800  *                      destination
801  *              @int ch@ = character to write
802  *              @unsigned n@ = number of copies
803  *
804  * Returns:     Zero on success, %$-1$% on error.
805  *
806  * Use:         Write @n@ copies of the character @ch@ to the terminal, the
807  *              hard way.  This function tries to be reasonably efficient, by
808  *              transmitting buffers rather than exercising the output
809  *              machinery for each individual character.
810  */
811
812 static int stupid_repeat(struct tty *tty,
813                          const struct gprintf_ops *gops, void *go,
814                          int ch, unsigned n)
815 {
816   char buf[4096];
817   unsigned nn;
818   int rc;
819
820   if (n < sizeof(buf))
821     { memset(buf, ch, n); CHECK(gops->putm(go, buf, n)); }
822   else {
823     memset(buf, ch, sizeof(buf));
824     nn = sizeof(buf);
825     for (;;) {
826       CHECK(gops->putm(go, buf, nn));
827       n -= nn; if (!n) break;
828       if (n < nn) nn = n;
829     }
830   }
831   rc = 0;
832 end:
833   return (rc);
834 }
835
836 /*----- Common machinery for %|termcap|% and %|terminfo|% -----------------*/
837
838 #if defined(HAVE_TERMINFO) ||                                           \
839     defined(HAVE_TERMCAP) ||                                            \
840     defined(HAVE_UNIBILIUM)
841
842 #if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
843
844 /* Global state.
845  *
846  * The `termcap' and `terminfo' functions call a user-provided function to
847  * actually send control codes to the terminal.  The bad news is that
848  * `termcap' doesn't provide any way to pass information to the output
849  * function beyond the character to be sent, and `terminfo' doesn't fix this
850  * mistake.  So we must save the necessary context as global variables.  For
851  * good measure, at least some implementations ignore errors from the output
852  * function, so we must keep track of them ourselves.  More global variables.
853  *
854  * It's worse.  Both libraries maintain significant global state of their
855  * own.  And, at least with the `ncurses' implementation, the two share the
856  * same global state.  The only thing to do is maintain a big interlock to
857  * make sure that only one is active at a time.
858  */
859 static const struct gprintf_ops *global_gops; /* output operations ... */
860 static void *global_gout;               /* and context, for @caps_putch */
861 static char global_buf[4096];           /* a big output buffer */
862 static size_t global_len;               /* length of buffer used */
863 static int global_err;                  /* error latch, zero if all ok */
864 static struct tty *global_lock = 0;     /* interlock for global state */
865
866 /* --- @caps_claim@ --- *
867  *
868  * Arguments:   ---
869  *
870  * Returns:     Zero on success, %$-1$% if already claimed.
871  *
872  * Use:         Return %$-1$% if the interlock is already held.  This is a
873  *              function to call near the beginning of initializing a new
874  *              control block, before the common global state gets
875  *              clobbered.  If initialization is successful, the caller is
876  *              expected to actually store the control block pointer in
877  *              @global_lock@ themselves.
878  */
879
880 static int caps_claim(void)
881 {
882   if (global_lock)
883     { debug("termcap/terminfo terminal already open"); return (-1); }
884   else
885     return (0);
886 }
887
888 /* --- @caps_claim@, @caps_release@ --- *
889  *
890  * Arguments:   @struct tty *tty@ = control block pointer for current lock
891  *                      holder
892  *
893  * Returns:     ---
894  *
895  * Use:         Release the lock.
896  */
897
898 static void caps_release(struct tty *tty)
899   { assert(global_lock == tty); global_lock = 0; }
900
901 /* --- @caps_putch@ --- *
902  *
903  * Arguments:   @int ch@ = character to write
904  *
905  * Returns:     Nonnegative on success, negative on failure.  (But @tputs@
906  *              ignores this.)
907  *
908  * Use:         Output the character @ch@.
909  */
910
911 static int caps_putch(int ch)
912 {
913   if (global_len >= sizeof(global_buf)) {
914     if (global_gops->putm(global_gout, global_buf, global_len))
915       global_err = -1;
916     global_len = 0;
917   }
918   global_buf[global_len++] = ch;
919   return (0);
920 }
921
922 /* --- @caps_prepout@ --- *
923  *
924  * Arguments:   @struct tty *tty@ = control block pointer (ignored)
925  *              @const struct gprintf_ops *gops, void *go@ = output
926  *                      destination
927  *
928  * Returns:     ---
929  *
930  * Use:         Prepare output to the given destination.
931  */
932
933 static void caps_prepout(struct tty *tty,
934                          const struct gprintf_ops *gops, void *go)
935   { assert(!global_len); global_gops = gops; global_gout = go; }
936
937 /* --- @caps_flush@ --- *
938  *
939  * Arguments:   @struct tty *tty@ = control block pointer (ignored)
940  *
941  * Returns:     Zero for success, %$-1$% if error pending.
942  *
943  * Use:         Flush the output buffer to the backend.  If an error is
944  *              pending, clear it and return failure.
945  */
946
947 static int caps_flush(struct tty *tty)
948 {
949   int rc = global_err;
950
951   if (global_len) {
952     if (global_gops->putm(global_gout, global_buf, global_len)) rc = -1;
953     global_len = 0;
954   }
955   global_err = 0; return (rc);
956 }
957
958 #endif
959
960 /* The list of interesting capabilities.
961  *
962  * We never actually need all of these: some are only needed if others are
963  * unavailable.  But the list isn't too huge, so we'll live with it.
964  *
965  * The main thing is that each capability has three different names: the
966  * `full' name (corresponding to a `terminfo' variable name), the `terminfo'
967  * capability name, as used in terminal descriptions, and the two-character
968  * `termcap' name.  Unibilium uses the long names, but to reduce typing, the
969  * `unibi_' prefix is omitted here.  (Annoyingly, in `ncurses', at least, the
970  * `variable' names are `secretly' macros referencing a current state, and
971  * premature expansion causes misery, so I've left the leading underscores in
972  * place as a countermeasure.)  Internally, we use the short `terminfo'
973  * names, since they generally express the most useful information in the
974  * smallest space.
975  */
976
977 #define BASICCAPS(_bool, _int, _str)                                    \
978   _str(_repeat_char, rep, rp)                                           \
979   _str(_pad_char, pad, pc) _int(_padding_baud_rate, pb, pb)             \
980   _bool(_no_pad_char, npc, NP) _bool(_xon_xoff, xon, xo)                \
981   _bool(_move_insert_mode, mir, mi) _bool(_move_standout_mode, msgr, ms)
982
983 #define ATTRCAPS(_bool, _int, _str)                                     \
984   _str(_exit_attribute_mode, sgr0, me)                                  \
985   _str(_enter_underline_mode, smul, us)                                 \
986     _str(_exit_underline_mode, rmul, ue)                                \
987   _str(_enter_italics_mode, sitm, ZH) _str(_exit_italics_mode, ritm, ZR) \
988   _str(_enter_bold_mode, bold, md) _str(_enter_dim_mode, dim, mh)       \
989   _str(_enter_reverse_mode, rev, mr)                                    \
990   COLOURCAPS(_bool, _int, _str)
991
992 #define COLOURCAPS(_bool, _int, _str)                                   \
993   _str(_set_a_foreground, setaf, AF) _str(_set_a_background, setab, AB) \
994   _str(_orig_pair, op, op) _int(_max_colors, colors, Co)
995
996 #define MODECAPS(_bool, _int, _str)                                     \
997   _str(_carriage_return, cr, cr) _str(_newline, nel, nw)                \
998   _str(_enter_am_mode, smam, SA) _str(_exit_am_mode, rmam, RA)          \
999   _str(_enter_ca_mode, smcup, ti) _str(_exit_ca_mode, rmcup, te)        \
1000   _str(_cursor_normal, cnorm, vs) _str(_cursor_invisible, civis, vi)    \
1001   _str(_enter_insert_mode, smir, im) _str(_exit_insert_mode, rmir, ei)  \
1002   _str(_enter_delete_mode, smdc, dm) _str(_exit_delete_mode, rmdc, ed)
1003
1004 #define MOVECAPS(_bool, _int, _str)                                     \
1005   _str(_cursor_home, home, ho)                                          \
1006   _str(_cursor_address, cup, cm)                                        \
1007   _str(_row_address, vpa, cv) _str(_column_address, hpa, ch)            \
1008   _str(_cursor_left, cub1, le) _str(_parm_left_cursor, cub, LE)         \
1009   _str(_cursor_right, cuf1, nd) _str(_parm_right_cursor, cuf, RI)       \
1010   _str(_cursor_up, cuu1, up) _str(_parm_up_cursor, cuu, UP)             \
1011   _str(_cursor_down, cud1, do) _str(_parm_down_cursor, cud, DO)
1012
1013 #define SCROLLCAPS(_bool, _int, _str)                                   \
1014   _str(_change_scroll_region, csr, cs)                                  \
1015   _str(_scroll_forward, ind, sf) _str(_parm_index, indn, SF)            \
1016   _str(_scroll_reverse, ri, sr) _str(_parm_rindex, rin, SR)
1017
1018 #define ERASECAPS(_bool, _int, _str)                                    \
1019   _str(_erase_chars, ech, ec)                                           \
1020   _str(_clr_bol, el1, cb) _str(_clr_eol, el, ce)                        \
1021   _str(_clr_eos, ed, cd) _str(_clear_screen, clear, cl)
1022
1023 #define INSDELCAPS(_bool, _int, _str)                                   \
1024   _str(_insert_character, ich1, ic) _str(_parm_ich, ich, IC)            \
1025   _str(_insert_padding, ip, ip)                                         \
1026   _str(_insert_line, il1, al) _str(_parm_insert_line, il, AL)           \
1027   _str(_delete_character, dch1, dc) _str(_parm_dch, dch, DC)            \
1028   _str(_delete_line, dl1, dl) _str(_parm_delete_line, dl, DL)
1029
1030 #define STORECAPS(_bool, _int, _str)                                    \
1031   BASICCAPS(_bool, _int, _str)                                          \
1032   ATTRCAPS(_bool, _int, _str)                                           \
1033   MODECAPS(_bool, _int, _str)                                           \
1034   MOVECAPS(_bool, _int, _str)                                           \
1035   SCROLLCAPS(_bool, _int, _str)                                         \
1036   ERASECAPS(_bool, _int, _str)                                          \
1037   INSDELCAPS(_bool, _int, _str)
1038
1039 #ifdef HAVE_UNIBILIUM
1040 #  define UNIBI_(x) unibi##x
1041 #else
1042 #  define UNIBI_(x) 0
1043 #endif
1044
1045 #define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
1046   /* Expand a capability triple into a group of three usable C arguments.  If
1047    * Unibilium isn't available, then we can use nonsense for its cap index.
1048    */
1049
1050 /* Some other capabilities which we want to refer to during initialization,
1051  * but don't need to keep around.
1052  */
1053 #define CAP_XMC CAPREF(_magic_cookie_glitch, xmc, sg)
1054 #define CAP_BCE CAPREF(_back_color_erase, bce, ut)
1055 #define CAP_XHPA CAPREF(_row_addr_glitch, xvpa, YD)
1056 #define CAP_XVPA CAPREF(_col_addr_glitch, xhpa, YA)
1057 #define CAP_AM CAPREF(_auto_right_margin, am, am)
1058 #define CAP_XENL CAPREF(_eat_newline_glitch, xenl, xn)
1059 #define CAP_HT CAPREF(_lines, lines, li)
1060 #define CAP_WD CAPREF(_columns, cols, co)
1061
1062 /* Additional operations required of terminal backends which make use of the
1063  * common capability machinery.
1064  */
1065 struct tty_capopslots {
1066
1067   /* Retrieving capabilities. */
1068   int (*boolcap)(struct tty */*tty*/,
1069                  int /*uix*/, const char */*info*/, const char */*cap*/);
1070   int (*intcap)(struct tty */*tty*/,
1071                 int /*uix*/, const char */*info*/, const char */*cap*/);
1072   const char *(*strcap)(struct tty */*tty*/,
1073                         int /*uix*/, const char */*info*/,
1074                         const char */*cap*/);
1075
1076   /* Preparing and completing output. */
1077   void (*prepout)(struct tty */*tty*/,
1078                   const struct gprintf_ops */*gops*/, void */*go*/);
1079   int (*flush)(struct tty */*tty*/);
1080
1081   /* Writing capabilities with various kinds of arguments. */
1082   int (*put0)(struct tty */*tty*/, unsigned /*npad*/, const char */*cap*/);
1083   int (*put1i)(struct tty */*tty*/,
1084                unsigned /*npad*/, const char */*cap*/, int /*i0*/);
1085   int (*put2i)(struct tty */*tty*/,
1086                unsigned /*npad*/,
1087                const char */*cap*/, int /*i0*/, int /*i1*/);
1088 };
1089 #define TTY_CAPOPSPFX TTY_BASEOPSPFX; struct tty_capopslots cap
1090 struct tty_capops { TTY_CAPOPSPFX; };
1091 #define TTY_CAPOPSUSFX struct tty_capops cap; TTY_BASEOPSUXFX
1092 union tty_capopsu { TTY_CAPOPSUSFX; };
1093
1094 /* An extension of the control block to track the above capabilities. */
1095 struct tty_capslots {
1096 #define DEF_BOOLCAP(uix, info, cap) unsigned info : 1;
1097 #define DEF_INTCAP(uix, info, cap) int info;
1098 #define DEF_STRCAP(uix, info, cap) const char *info;
1099   STORECAPS(DEF_BOOLCAP, DEF_INTCAP, DEF_STRCAP)
1100 #undef DEF_BOOLCAP
1101 #undef DEF_INTCAP
1102 #undef DEF_STRCAP
1103 #define LEN_BOOLCAP(uix, info, cap)
1104 #define LEN_INTCAP(uix, info, cap)
1105 #define LEN_STRCAP(uix, info, cap) unsigned info##_cost;
1106     ATTRCAPS(LEN_BOOLCAP, LEN_INTCAP, LEN_STRCAP)
1107 #undef LEN_BOOLCAP
1108 #undef LEN_INTCAP
1109 #undef LEN_STRCAP
1110 };
1111 #define TTY_CAPSPFX                                                     \
1112   struct tty tty;                                                       \
1113   struct tty_capslots cap
1114 struct tty_caps { TTY_CAPSPFX; };
1115 #define TTY_CAPSUSFX                                                    \
1116   struct tty_caps cap;                                                  \
1117   struct tty tty
1118 union tty_capsu { TTY_CAPSUSFX; };
1119
1120 /* ---- @init_caps@ --- *
1121  *
1122  * Arguments:   @struct tty *tty@ = control block pointer
1123  *
1124  * Returns:     ---
1125  *
1126  * Use:         Populate the capabilities in the terminal control block, and
1127  *              advertise the results to the public part.  Set @ht@ and @wd@
1128  *              from the terminal description if they've not been set
1129  *              already.
1130  */
1131
1132 static void init_caps(struct tty_caps *t)
1133 {
1134   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1135   int wd, ht;
1136
1137   t->tty.acaps = t->tty.ocaps = 0;
1138   t->tty.st.modes = 0; t->tty.st.attr.f = 0;
1139   t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
1140
1141   /* Inhale all of the interesting terminal capabilities. */
1142 #define GETBOOL(var, info, cap_)                                        \
1143     t->cap.info = ops->cap.boolcap(&t->tty, CAPREF(var, info, cap_));
1144 #define GETINT(var, info, cap_)                                         \
1145     t->cap.info = ops->cap.intcap(&t->tty, CAPREF(var, info, cap_));
1146 #define GETSTR(var, info, cap_)                                         \
1147     t->cap.info = ops->cap.strcap(&t->tty, CAPREF(var, info, cap_));
1148   STORECAPS(GETBOOL, GETINT, GETSTR)
1149 #undef GETBOOL
1150 #undef GETINT
1151 #undef GETSTR
1152
1153 #define COST_BOOLCAP(uix, info, cap_)
1154 #define COST_INTCAP(uix, info, cap_)
1155 #define COST_STRCAP(uix, info, cap_)                                    \
1156     if (!t->cap.info) t->cap.info##_cost = -1;                          \
1157     else t->cap.info##_cost =                                           \
1158            strlen(tgoto(t->cap.info, 0, t->cap.colors - 1));
1159   ATTRCAPS(COST_BOOLCAP, COST_INTCAP, COST_STRCAP)
1160 #undef COST_BOOLCAP
1161 #undef COST_INTCAP
1162 #undef COST_STRCAP
1163
1164 #define CLEAR_BOOL(uix, info, cap_) t->cap.info = 0;
1165 #define CLEAR_INT(uix, info, cap_) t->cap.info = 0;
1166 #define CLEAR_STR(uix, info, cap_) t->cap.info = 0;
1167 #define CLEARCAPS(caplist)                                              \
1168     do { caplist(CLEAR_BOOL, CLEAR_INT, CLEAR_STR) } while (0)
1169
1170   /* Basic capabilities. */
1171   if (!t->cap.cr) t->cap.cr = "\r";
1172   if (!t->cap.nel) t->cap.nel = "\r\n";
1173
1174   /* Attribute capabilities. */
1175   if (ops->cap.intcap(&t->tty, CAP_XMC) > 0)
1176     CLEARCAPS(ATTRCAPS);
1177   else {
1178     if (t->cap.smul && (t->cap.rmul || t->cap.sgr0))
1179       t->tty.acaps |= TTACF_ULINE;
1180     if (t->cap.bold && t->cap.sgr0)
1181       t->tty.acaps |= TTACF_BOLD;
1182     if (t->cap.dim && t->cap.sgr0)
1183       t->tty.acaps |= TTACF_DIM;
1184     if (t->cap.sitm && (t->cap.ritm || t->cap.sgr0))
1185       t->tty.acaps |= TTACF_ITAL;
1186     if (t->cap.rev && t->cap.sgr0)
1187       t->tty.acaps |= TTACF_INVV;
1188
1189     if ((t->cap.setaf || t->cap.setab) && (t->cap.op || t->cap.sgr0)) {
1190       if (t->cap.setaf) t->tty.acaps |= TTACF_FG;
1191       if (t->cap.setab) t->tty.acaps |= TTACF_BG;
1192       t->tty.acaps |= TTACF_1BPC;
1193       if (t->cap.colors >= 16777216)
1194         t->tty.acaps |= TTACF_1BPC | TTACF_8BPC;
1195       else if (t->cap.colors >= 256)
1196         t->tty.acaps |= TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
1197       else if (t->cap.colors == 88)
1198         t->tty.acaps |= TTACF_1BPCBR | TTACF_4LPC | TTACF_8LGS;
1199       else if (t->cap.colors >= 16)
1200         t->tty.acaps |= TTACF_1BPCBR;
1201       if (ops->cap.boolcap(&t->tty, CAP_BCE)) t->tty.ocaps |= TTCF_BGER;
1202       env_colour_caps(&t->tty.acaps, 0);
1203     }
1204   }
1205
1206   /* Motion capabilities. */
1207   if (ops->cap.boolcap(&t->tty, CAP_XHPA)) t->cap.hpa = 0;
1208   if (ops->cap.boolcap(&t->tty, CAP_XVPA)) t->cap.vpa = 0;
1209   if (!t->cap.cub1) t->cap.cub1 = "\b";
1210   if ((t->cap.cuf || t->cap.cuf1) &&
1211       (t->cap.cuu || t->cap.cuu1) &&
1212       (t->cap.cud || t->cap.cud1)) {
1213     t->tty.ocaps |= TTCF_RELMV;
1214     if (t->cap.vpa) t->tty.ocaps |= TTCF_ABSMV | TTCF_MIXMV;
1215     else if (t->cap.home) t->tty.ocaps |= TTCF_ABSMV;
1216   } else if (t->cap.cup ||
1217              (t->cap.hpa && t->cap.vpa) ||
1218              (t->cap.home && (t->cap.cuf || t->cap.cuf1)))
1219     t->tty.ocaps |= TTCF_ABSMV;
1220
1221   /* Mode capabilities. */
1222   if (t->cap.smam && t->cap.rmam) t->tty.ocaps |= TTMF_AUTOM;
1223   if (t->cap.smcup && t->cap.rmcup) t->tty.ocaps |= TTMF_FSCRN;
1224   if (t->cap.smir && t->cap.rmir) t->tty.ocaps |= TTMF_INS;
1225   if (t->cap.smdc && t->cap.rmdc) t->tty.ocaps |= TTMF_DEL;
1226   if (t->cap.cnorm && t->cap.civis)
1227     { t->tty.ocaps |= TTMF_CVIS; t->tty.st.modes |= TTMF_CVIS; }
1228   if (ops->cap.boolcap(&t->tty, CAP_AM)) {
1229     t->tty.st.modes |= TTMF_AUTOM;
1230     if (ops->cap.boolcap(&t->tty, CAP_XENL)) t->tty.ocaps |= TTCF_MMARG;
1231   }
1232
1233   /* Erasure. */
1234   if (t->cap.ech) t->tty.ocaps |= TTCF_ERCH;
1235   if (t->cap.el1) t->tty.ocaps |= TTCF_ERBOL;
1236   if (t->cap.el) t->tty.ocaps |= TTCF_EREOL;
1237   if (t->cap.ed) t->tty.ocaps |= TTCF_EREOD;
1238   if (t->cap.clear || (t->cap.ed && t->cap.home)) t->tty.ocaps |= TTCF_ERDSP;
1239
1240   /* Insertion and deletion. */
1241   if (t->cap.ich || t->cap.ich1) t->tty.ocaps |= TTCF_INSCH;
1242   if (t->cap.il || t->cap.il1) t->tty.ocaps |= TTCF_INSLN;
1243   if (t->cap.dch || t->cap.dch1) t->tty.ocaps |= TTCF_DELCH;
1244   if (t->cap.dl || t->cap.dl1) t->tty.ocaps |= TTCF_DELLN;
1245
1246   /* Geometry. */
1247   if (!t->tty.wd)
1248     { wd = ops->cap.intcap(&t->tty, CAP_WD); if (wd > 0) t->tty.wd = wd; }
1249   if (!t->tty.ht)
1250     { ht = ops->cap.intcap(&t->tty, CAP_HT); if (ht > 0) t->tty.ht = ht; }
1251
1252 #undef CLEAR_BOOL
1253 #undef CLEAR_INT
1254 #undef CLEAR_STR
1255 #undef CLEARCAPS
1256 }
1257
1258 /* Macros for formatting capabilities. */
1259 #define PUT0V(npad, cap_)                                               \
1260   CHECK(ops->cap.put0(&t->tty, (npad), (cap_)))
1261 #define PUT1IV(npad, cap_, i0)                                          \
1262   CHECK(ops->cap.put1i(&t->tty, (npad), (cap_), (i0)))
1263 #define PUT2IV(npad, cap_, i0, i1)                                      \
1264   CHECK(ops->cap.put2i(&t->tty, (npad), (cap_), (i0), (i1)))
1265
1266 #define PUT0(npad, name) PUT0V(npad, t->cap.name)
1267 #define PUT1I(npad, name, i0) PUT1IV(npad, t->cap.name, i0)
1268 #define PUT2I(npad, name, i0, i1) PUT2IV(npad, t->cap.name, i0, i1)
1269
1270 /* --- @caps_setcolour@ --- *
1271  *
1272  * Arguments:   @struct tty_caps *t@ = extended control block pointer
1273  *              
1274  */
1275 static int caps_setcolour(struct tty_caps *t,
1276                           const char *cap, uint32 spc, uint32 clr)
1277 {
1278   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1279   int rc;
1280
1281   switch (spc) {
1282     case TTCSPC_1BPC: case TTCSPC_1BPCBR: PUT1IV(0, cap, clr);       break;
1283     case TTCSPC_4LPC: case TTCSPC_6LPC:   PUT1IV(0, cap, clr +  16); break;
1284     case TTCSPC_8LGS:                     PUT1IV(0, cap, clr +  80); break;
1285     case TTCSPC_24LGS:                    PUT1IV(0, cap, clr + 232); break;
1286
1287     case TTCSPC_8BPC:
1288       /* There's an unfortunate ambiguity in the %|setaf|% conventions.  The
1289        * first eight colours should be dark shades of blue, but in fact
1290        * they're interpreted as the one-bit-per-channel basic colours by
1291        * common `terminfo' settings.  Notice and kludge by adding a little
1292        * red.  This will tinge the colour magenta, but all such colours are
1293        * so dark as to be effectively black anyway, so I doubt that this will
1294        * be noticeable.
1295        */
1296       if (spc == TTCSPC_8BPC && clr < 8) clr += 65536;
1297       PUT1IV(0, cap, clr); break;
1298
1299     /* case TTCSPC_NONE: */
1300     default: rc = -1; goto end;
1301   }
1302   rc = 0;
1303 end:
1304   return (rc);
1305 }
1306
1307 static int caps_setattr_internal(struct tty_caps *t,
1308                                  const struct tty_attr *a)
1309 {
1310   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1311   uint32 diff;
1312   int rc;
1313   unsigned c, z, f = 0;
1314 #define f_clrall 1u
1315
1316   /* Work out what needs doing. */
1317   diff = a->f ^ t->tty.st.attr.f;
1318
1319   /* Some terminals might not be able to clear individual attributes that
1320    * they can set, and some capabilities for turning attributes on don't even
1321    * have a corresponding attribute for turning them off again individually,
1322    * so we have to use %|sgr0|% to start from scratch.  Of course, if we need
1323    * to do that, we need to restore the other active attributes, so we must
1324    * check up front.
1325    */
1326
1327   c = 0;
1328 #define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
1329 #define ADDCOST(cap_) do {                                              \
1330   if (t->cap.cap_) c += t->cap.cap_##_cost;                             \
1331   else f |= f_clrall;                                                   \
1332 } while (0)
1333   if (CLEARP(TTAF_LNMASK)) ADDCOST(rmul);
1334   if (CLEARP(TTAF_WTMASK)) f |= f_clrall;
1335   if (diff&~a->f&TTAF_INVV) f |= f_clrall;
1336   if (diff&~a->f&TTAF_ITAL) ADDCOST(ritm);
1337   if (CLEARP(TTAF_FGSPCMASK) || CLEARP(TTAF_BGSPCMASK)) ADDCOST(op);
1338 #undef CLEARP
1339 #undef ADDCOST
1340
1341   z = 0;
1342   switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1343     case TTLN_ULINE: z += t->cap.smul_cost; break;
1344   }
1345   switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1346     case TTWT_BOLD: z += t->cap.bold_cost; break;
1347     case TTWT_DIM: z += t->cap.dim_cost; break;
1348   }
1349   if (a->f&TTAF_INVV) z += t->cap.rev_cost;
1350   if (a->f&TTAF_ITAL) z += t->cap.sitm_cost;
1351   if (a->f&TTAF_FGSPCMASK) z += t->cap.setaf_cost;
1352   if (a->f&TTAF_BGSPCMASK) z += t->cap.setab_cost;
1353
1354   if ((t->cap.sgr0 && z + t->cap.sgr0_cost < c) || (f&f_clrall))
1355     { PUT0(0, sgr0); diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg; }
1356
1357   /* Line style. */
1358   if (diff&TTAF_LNMASK)
1359     switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1360       case TTLN_NONE: PUT0(0, rmul); break;
1361       case TTLN_ULINE: PUT0(0, smul); break;
1362       /* case TTLN_UULINE: */
1363       /* case TTLN_STRIKE: */
1364       default: rc = -1; goto end;
1365     }
1366
1367   /* Text weight. */
1368   if (diff&TTAF_WTMASK)
1369     switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1370       /* case TTWT_MED: */
1371       case TTWT_BOLD: PUT0(0, bold); break;
1372       case TTWT_DIM: PUT0(0, dim); break;
1373       default: rc = -1; goto end;
1374     }
1375
1376   /* Other text effects. */
1377   if (diff&a->f&TTAF_INVV) PUT0(0, rev);
1378   if (diff&TTAF_ITAL) {
1379     if (a->f&TTAF_ITAL) PUT0(0, sitm);
1380     else PUT0(0, ritm);
1381   }
1382
1383   /* Colours. */
1384   if (((diff&TTAF_FGSPCMASK) && !(a->f&TTAF_FGSPCMASK)) ||
1385       ((diff&TTAF_BGSPCMASK) && !(a->f&TTAF_BGSPCMASK))) {
1386     /* There's no capability string for resetting just the foreground
1387      * or background colours to the defaults, so deal with that here.
1388      */
1389
1390     PUT0(0, op);
1391     diff = (diff&~(TTAF_FGSPCMASK | TTAF_BGSPCMASK)) |
1392            (a->f&(TTAF_FGSPCMASK | TTAF_BGSPCMASK));
1393   }
1394   if ((diff&TTAF_FGSPCMASK) || a->fg != t->tty.st.attr.fg)
1395     CHECK(caps_setcolour(t, t->cap.setaf,
1396                          (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
1397   if ((diff&TTAF_BGSPCMASK) || a->bg != t->tty.st.attr.bg)
1398     CHECK(caps_setcolour(t, t->cap.setab,
1399                          (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
1400
1401   /* All done. */
1402   rc = 0;
1403 end:
1404   t->tty.st.attr = *a; return (rc);
1405
1406 #undef f_clrall
1407 }
1408
1409 static int caps_setattr(struct tty *tty,
1410                         const struct gprintf_ops *gops, void *go,
1411                         const struct tty_attr *a)
1412 {
1413   struct tty_caps *t = (struct tty_caps *)tty;
1414   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1415   int rc;
1416
1417   ops->cap.prepout(&t->tty, gops, go);
1418   rc = caps_setattr_internal(t, a);
1419   if (ops->cap.flush(&t->tty)) rc = -1;
1420   return (rc);
1421 }
1422
1423 static int caps_setmodes(struct tty *tty,
1424                          const struct gprintf_ops *gops, void *go,
1425                          uint32 modes_bic, uint32 modes_xor)
1426 {
1427   struct tty_caps *t = (struct tty_caps *)tty;
1428   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1429   uint32 modes, diff;
1430   const char *cap;
1431   int rc;
1432
1433   /* Figure out which modes to set. */
1434   modes = (t->tty.st.modes&~modes_bic) ^ modes_xor;
1435   diff = modes ^ t->tty.st.modes;
1436
1437   /* Prepare output. */
1438   ops->cap.prepout(&t->tty, gops, go);
1439
1440   /* Automatic margins. */
1441   if (diff&TTMF_AUTOM) {
1442     if (modes&TTMF_AUTOM) PUT0(0, smam);
1443     else PUT0(0, rmam);
1444   }
1445
1446   /* Full-screen. */
1447   if (diff&TTMF_FSCRN) {
1448     if (modes&TTMF_FSCRN) PUT0(0, smcup);
1449     else PUT0(0, rmcup);
1450   }
1451
1452   /* Cursor visibility. */
1453   if (diff&TTMF_CVIS) {
1454     if (modes&TTMF_CVIS) PUT0(0, civis);
1455     else PUT0(0, cnorm);
1456   }
1457
1458   /* Auto-insert. */
1459   if (diff&TTMF_INS) {
1460     cap = modes&TTMF_INS ? t->cap.smir : t->cap.rmir;
1461     if (cap) PUT0V(0, cap);
1462     else if (!t->cap.ich) { rc = -1; goto end; }
1463   }
1464
1465   /* Delete characters. */
1466   if (diff&TTMF_DEL) {
1467     cap = modes&TTMF_DEL ? t->cap.smdc : t->cap.rmdc;
1468     if (cap) PUT0V(0, cap);
1469     else if (!t->cap.dch && !t->cap.dch1) { rc = -1; goto end; }
1470   }
1471
1472   /* Done. */
1473   rc = 0;
1474 end:
1475   if (ops->cap.flush(&t->tty)) rc = -1;
1476   t->tty.st.modes = modes; return (rc);
1477 }
1478
1479 #define CIF_PADMUL 1u
1480 static int caps_iterate(struct tty_caps *t,
1481                         const char *cap1, const char *capn,
1482                         unsigned f, unsigned npad, unsigned n)
1483 {
1484   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1485   unsigned max, nn;
1486   int rc;
1487
1488   if (cap1 && (n == 1 || !capn))
1489     while (n--) PUT0V(npad, cap1);
1490   else {
1491     max = npad && (f&CIF_PADMUL) ? INT_MAX/npad : INT_MAX;
1492     while (n) {
1493       nn = n; if (nn > max) nn = max;
1494       PUT1IV(npad, capn, nn);
1495       n -= nn;
1496     }
1497   }
1498   rc = 0;
1499 end:
1500   return (rc);
1501 }
1502
1503 static int caps_move_relative(struct tty_caps *t,
1504                               int delta,
1505                               const char *fw1, const char *fwn,
1506                               const char *rv1, const char *rvn)
1507 {
1508   const char *mv1, *mvn;
1509
1510   if (!delta) return (0);
1511   else if (delta > 0) { mv1 = fw1; mvn = fwn; }
1512   else { mv1 = rv1; mvn = rvn; delta = - delta; }
1513   return (caps_iterate(t, mv1, mvn, 0, 0, delta));
1514 }
1515
1516 static int caps_move(struct tty *tty,
1517                      const struct gprintf_ops *gops, void *go,
1518                      unsigned orig, int y, int x)
1519 {
1520   struct tty_caps *t = (struct tty_caps *)tty;
1521   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1522   struct tty_attr a;
1523   int rc;
1524
1525   ops->cap.prepout(&t->tty, gops, go);
1526
1527   if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, rmir);
1528
1529   a = t->tty.st.attr;
1530   if (!t->cap.msgr && a.f) { PUT0(0, sgr0); t->tty.st.attr.f = 0; }
1531
1532   switch (orig) {
1533     case TTORG_HOME:
1534       if (t->cap.home && !x && !y)
1535         PUT0(1, home);
1536       else if (t->cap.cup)
1537         PUT2I(1, cup, y, x);
1538       else if (t->cap.vpa) {
1539         PUT1I(1, vpa, y);
1540         if (t->cap.hpa)
1541           PUT1I(1, hpa, x);
1542         else {
1543           PUT0(1, cr);
1544           CHECK(caps_move_relative(t, x,
1545                                    t->cap.cuf1, t->cap.cuf,
1546                                    t->cap.cub1, t->cap.cub));
1547         }
1548       } else if (t->cap.home) {
1549         PUT0(1, home);
1550         CHECK(caps_iterate(t, t->cap.cud1, t->cap.cud, 0, 1, y));
1551         CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1552       } else
1553         { rc = -1; goto end; }
1554       break;
1555
1556     case TTORG_CUR:
1557       CHECK(caps_move_relative(t, y,
1558                                t->cap.cud1, t->cap.cud,
1559                                t->cap.cuu1, t->cap.cuu));
1560       CHECK(caps_move_relative(t, x,
1561                                t->cap.cuf1, t->cap.cuf,
1562                                t->cap.cub1, t->cap.cub));
1563       break;
1564
1565     case TTOF_XHOME | TTOF_YCUR:
1566       if (x == 0 && y == 1)
1567         PUT0(1, nel);
1568       else {
1569         CHECK(caps_move_relative(t, y,
1570                                  t->cap.cud1, t->cap.cud,
1571                                  t->cap.cuu1, t->cap.cuu));
1572         if (t->cap.hpa && x)
1573           PUT1I(1, hpa, x);
1574         else {
1575           PUT0(1, cr);
1576           CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1577         }
1578       }
1579       break;
1580
1581     case TTOF_XCUR | TTOF_YHOME:
1582       PUT1I(1, vpa, y);
1583       CHECK(caps_move_relative(t, x,
1584                                t->cap.cuf1, t->cap.cuf,
1585                                t->cap.cub1, t->cap.cub));
1586       break;
1587
1588     default:
1589       rc = -1; goto end;
1590       break;
1591   }
1592
1593   if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, smir);
1594
1595   if (!t->cap.msgr && a.f)
1596     CHECK(caps_setattr_internal(t, &a));
1597
1598   rc = 0;
1599 end:
1600   if (ops->cap.flush(&t->tty)) rc = -1;
1601   return (rc);
1602 }
1603
1604 static int caps_repeat(struct tty *tty,
1605                        const struct gprintf_ops *gops, void *go,
1606                        int ch, unsigned n)
1607 {
1608   struct tty_caps *t = (struct tty_caps *)tty;
1609   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1610   unsigned wd, nn;
1611   int rc;
1612
1613   ops->cap.prepout(&t->tty, gops, go);
1614   if (!t->cap.rep)
1615     CHECK(stupid_repeat(tty, gops, go, ch, n));
1616   else {
1617     wd = t->tty.wd;
1618     while (n) {
1619       nn = n; if (nn > INT_MAX) nn = INT_MAX;
1620       PUT2I((nn + wd - 1)/wd, rep, ch, nn);
1621       n -= nn;
1622     }
1623   }
1624   rc = 0;
1625 end:
1626   if (ops->cap.flush(&t->tty)) rc = -1;
1627   return (rc);
1628 }
1629
1630 static int caps_erase(struct tty *tty,
1631                       const struct gprintf_ops *gops, void *go,
1632                       unsigned f)
1633 {
1634   struct tty_caps *t = (struct tty_caps *)tty;
1635   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1636   int rc;
1637
1638   ops->cap.prepout(&t->tty, gops, go);
1639   if (f&TTEF_DSP)
1640     switch (f&(TTEF_BEGIN | TTEF_END)) {
1641       case 0:
1642         break;
1643       case TTEF_BEGIN | TTEF_END:
1644         if (t->cap.clear) PUT0(t->tty.ht, clear);
1645         else { PUT0(1, home); PUT0(t->tty.ht, ed); }
1646         break;
1647       case TTEF_END:
1648         PUT0(t->tty.ht, ed);
1649         break;
1650       default:
1651         rc = -1; goto end;
1652         break;
1653     }
1654   else {
1655     if (f&TTEF_BEGIN) PUT0(1, el1);
1656     if (f&TTEF_END) PUT0(1, el);
1657   }
1658   rc = 0;
1659 end:
1660   if (ops->cap.flush(&t->tty)) rc = -1;
1661   return (rc);
1662 }
1663
1664 static int caps_erch(struct tty *tty,
1665                      const struct gprintf_ops *gops, void *go,
1666                      unsigned n)
1667 {
1668   struct tty_caps *t = (struct tty_caps *)tty;
1669   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1670   int rc;
1671
1672   ops->cap.prepout(&t->tty, gops, go);
1673   if (n) PUT1I(1, ech, n);
1674   rc = 0;
1675 end:
1676   if (ops->cap.flush(&t->tty)) rc = -1;
1677   return (rc);
1678 }
1679
1680 static int caps_ins(struct tty *tty,
1681                     const struct gprintf_ops *gops, void *go,
1682                     unsigned f, unsigned n)
1683 {
1684   struct tty_caps *t = (struct tty_caps *)tty;
1685   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1686   int rc;
1687
1688   ops->cap.prepout(&t->tty, gops, go);
1689   if (f&TTIDF_LN)
1690     CHECK(caps_iterate(t, t->cap.il1, t->cap.il, CIF_PADMUL, 1, n));
1691   else
1692     CHECK(caps_iterate(t, t->cap.ich1, t->cap.ich, 0, 1, n));
1693   rc = 0;
1694 end:
1695   if (ops->cap.flush(&t->tty)) rc = -1;
1696   return (rc);
1697 }
1698
1699 static int caps_inch(struct tty *tty,
1700                      const struct gprintf_ops *gops, void *go,
1701                      int ch)
1702 {
1703   struct tty_caps *t = (struct tty_caps *)tty;
1704   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1705   int rc;
1706
1707   ops->cap.prepout(&t->tty, gops, go);
1708   if (t->cap.smir ? !(t->tty.st.modes&TTMF_INS) : !t->cap.ich)
1709     { rc = -1; goto end; }
1710   if (t->cap.ich) PUT0(1, ich);
1711   CHECK(gops->putch(go, ch));
1712   if (t->cap.ip) PUT0(1, ip);
1713   rc = 0;
1714 end:
1715   if (ops->cap.flush(&t->tty)) rc = -1;
1716   return (rc);
1717 }
1718
1719 static int caps_del(struct tty *tty,
1720                     const struct gprintf_ops *gops, void *go,
1721                     unsigned f, unsigned n)
1722 {
1723   struct tty_caps *t = (struct tty_caps *)tty;
1724   const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1725   int rc;
1726
1727   ops->cap.prepout(&t->tty, gops, go);
1728   if (n) {
1729     if (f&TTIDF_LN)
1730       CHECK(caps_iterate(t, t->cap.dl1, t->cap.dl, CIF_PADMUL, 1, n));
1731     else
1732       CHECK(caps_iterate(t, t->cap.dch1, t->cap.dch, 0, 1, n));
1733   }
1734   rc = 0;
1735 end:
1736   if (ops->cap.flush(&t->tty)) rc = -1;
1737   return (rc);
1738 }
1739
1740 #undef PUT0V
1741 #undef PUT1IV
1742 #undef PUT2IV
1743 #undef PUT0
1744 #undef PUT1I
1745 #undef PUT2I
1746
1747 #define TTY_CAPOPS                                                      \
1748   caps_setattr, caps_setmodes,                                          \
1749   caps_move, caps_repeat,                                               \
1750   caps_erase, caps_erch, caps_ins, caps_inch, caps_del
1751
1752 #endif
1753
1754 /*----- Termcap -----------------------------------------------------------*/
1755
1756 #ifdef HAVE_TERMCAP
1757
1758 struct tty_termcapslots {
1759   char termbuf[4096], capbuf[4096], *capcur;
1760 };
1761 struct tty_termcap { TTY_CAPSPFX; struct tty_termcapslots tc; };
1762 union tty_termcapu { struct tty_termcap tc; TTY_CAPSUSFX; };
1763
1764 static int termcap_boolcap(struct tty *tty,
1765                            int uix, const char *info, const char *cap)
1766 {
1767   int p;
1768
1769   p = tgetflag(cap); assert(p >= 0);
1770   return (p);
1771 }
1772
1773 static int termcap_intcap(struct tty *tty,
1774                           int uix, const char *info, const char *cap)
1775 {
1776   int n;
1777
1778   n = tgetnum(cap); assert(n >= -1);
1779   return (n);
1780 }
1781
1782 static const char *termcap_strcap(struct tty *tty,
1783                                   int uix, const char *info,
1784                                   const char *cap)
1785 {
1786   struct tty_termcap *t = (struct tty_termcap *)tty;
1787   const char *p;
1788
1789   p = tgetstr(cap, &t->tc.capcur); assert(p != (const char *)-1);
1790   return (p);
1791 }
1792
1793 static int termcap_put0(struct tty *tty,
1794                         unsigned npad, const char *cap)
1795 {
1796   if (!cap) return (-1);
1797   return (tputs(cap, npad, caps_putch));
1798 }
1799
1800 static int termcap_put1i(struct tty *tty,
1801                          unsigned npad, const char *cap, int i0)
1802 {
1803   if (!cap) return (-1);
1804   return (tputs(tgoto(cap, -1, i0), npad, caps_putch) == OK ? 0 : -1);
1805 }
1806
1807 static int termcap_put2i(struct tty *tty,
1808                          unsigned npad, const char *cap, int i0, int i1)
1809 {
1810   if (!cap) return (-1);
1811   return (tputs(tgoto(cap, i1, i0), npad, caps_putch) == OK ? 0 : -1);
1812 }
1813
1814 static const union tty_capopsu termcap_ops = { {
1815   { caps_release, TTY_CAPOPS },
1816   { termcap_boolcap, termcap_intcap, termcap_strcap,
1817     caps_prepout, caps_flush,
1818     termcap_put0, termcap_put1i, termcap_put2i }
1819 } };
1820
1821 static struct tty *termcap_init(FILE *fp)
1822 {
1823   union tty_termcapu *u = 0; struct tty *ret = 0;
1824   const char *term;
1825
1826   if (caps_claim()) goto end;
1827   term = getenv("TERM"); if (!term) goto end;
1828   XNEW(u);
1829   if (tgetent(u->tc.tc.termbuf, term) < 1) goto end;
1830   u->tc.tc.capcur = u->tc.tc.capbuf;
1831   u->tty.ops = &termcap_ops.tty;
1832   common_init(&u->tty, fp);
1833   init_caps(&u->cap);
1834   ret = &u->tty; u = 0;
1835 end:
1836   xfree(u); global_lock = ret; return (ret);
1837 }
1838
1839 #endif
1840
1841 /*----- Terminfo ----------------------------------------------------------*/
1842
1843 #ifdef HAVE_TERMINFO
1844
1845 static int terminfo_boolcap(struct tty *tty,
1846                             int uix, const char *info, const char *cap)
1847 {
1848   int p;
1849
1850   p = tigetflag(info); assert(p >= 0);
1851   return (p);
1852 }
1853
1854 static int terminfo_intcap(struct tty *tty,
1855                            int uix, const char *info, const char *cap)
1856 {
1857   int n;
1858
1859   n = tigetnum(info); assert(n >= -1);
1860   return (n);
1861 }
1862
1863 static const char *terminfo_strcap(struct tty *tty,
1864                                    int uix, const char *info,
1865                                    const char *cap)
1866 {
1867   const char *p;
1868
1869   p = tigetstr(info); assert(p != (const char *)-1);
1870   return (p);
1871 }
1872
1873 static int terminfo_put0(struct tty *tty,
1874                          unsigned npad, const char *cap)
1875 {
1876   if (!cap) return (-1);
1877   return (tputs(cap, npad, caps_putch));
1878 }
1879
1880 static int terminfo_put1i(struct tty *tty,
1881                           unsigned npad, const char *cap, int i0)
1882 {
1883   if (!cap) return (-1);
1884   return (tputs(tparm(cap, i0), npad, caps_putch) == OK ? 0 : -1);
1885 }
1886
1887 static int terminfo_put2i(struct tty *tty,
1888                           unsigned npad, const char *cap, int i0, int i1)
1889 {
1890   if (!cap) return (-1);
1891   return (tputs(tparm(cap, i0, i1), npad, caps_putch) == OK ? 0 : -1);
1892 }
1893
1894 static const union tty_capopsu terminfo_ops = { {
1895   { caps_release, TTY_CAPOPS },
1896   { terminfo_boolcap, terminfo_intcap, terminfo_strcap,
1897     caps_prepout, caps_flush,
1898     terminfo_put0, terminfo_put1i, terminfo_put2i }
1899 } };
1900
1901 static struct tty *terminfo_init(FILE *fp)
1902 {
1903   union tty_capsu *u = 0; struct tty *ret = 0;
1904   int err;
1905
1906   if (caps_claim()) goto end;
1907   if (setupterm(0, fp ? fileno(fp) : -1, &err) != OK || err < 1) goto end;
1908   XNEW(u);
1909   u->tty.ops = &terminfo_ops.tty;
1910   common_init(&u->tty, fp);
1911   init_caps(&u->cap);
1912   ret = &u->tty; u = 0;
1913 end:
1914   xfree(u); global_lock = ret; return (ret);
1915 }
1916
1917 #endif
1918
1919 /*----- Unibilium ---------------------------------------------------------*/
1920
1921 #ifdef HAVE_UNIBILIUM
1922
1923 struct tty_unibislots {
1924   unibi_term *ut;
1925   unibi_var_t dy[26], st[26];
1926   const struct gprintf_ops *gops; void *go;
1927   char buf[4096]; size_t n;
1928   int err;
1929 };
1930 struct tty_unibilium { TTY_CAPSPFX; struct tty_unibislots u; };
1931 union tty_unibiliumu { struct tty_unibilium u; TTY_CAPSUSFX; };
1932
1933 static int termunibi_boolcap(struct tty *tty,
1934                             int uix, const char *info, const char *cap)
1935 {
1936   struct tty_unibilium *t = (struct tty_unibilium *)tty;
1937
1938   return (unibi_get_bool(t->u.ut, uix));
1939 }
1940
1941 static int termunibi_intcap(struct tty *tty,
1942                            int uix, const char *info, const char *cap)
1943 {
1944   struct tty_unibilium *t = (struct tty_unibilium *)tty;
1945
1946   return (unibi_get_num(t->u.ut, uix));
1947 }
1948
1949 static const char *termunibi_strcap(struct tty *tty,
1950                                    int uix, const char *info,
1951                                    const char *cap)
1952 {
1953   struct tty_unibilium *t = (struct tty_unibilium *)tty;
1954
1955   return (unibi_get_str(t->u.ut, uix));
1956 }
1957
1958 struct termunibi_outctx {
1959   struct tty_unibilium *t;
1960 };
1961
1962 static void termunibi_putm(void *ctx, const char *p, size_t sz)
1963 {
1964   struct tty_unibilium *t = ctx;
1965   size_t n;
1966
1967   n = sizeof(t->u.buf) - t->u.n;
1968   if (sz <= n)
1969     { memcpy(t->u.buf + t->u.n, p, sz); t->u.n += sz; }
1970   else {
1971     if (n) { memcpy(t->u.buf + t->u.n, p, n); p += n; sz -= n; }
1972     for (;;) {
1973       if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
1974         t->u.err = -1;
1975       if (sz <= sizeof(t->u.buf)) break;
1976       memcpy(t->u.buf, p, sizeof(t->u.buf));
1977         p += sizeof(t->u.buf); sz -= sizeof(t->u.buf);
1978     }
1979     memcpy(t->u.buf, p, sz); t->u.n = sz;
1980   }
1981 }
1982
1983 static void termunibi_pad(void *ctx, size_t ms, int mulp, int forcep)
1984 {
1985   struct tty_unibilium *t = ctx;
1986   struct timeval tv;
1987   int pc;
1988   size_t sz, n;
1989
1990   /* Based on 7 data bits, 1 stop bit, 1 parity bit. */
1991 #define BITS_PER_KB 9000
1992
1993   if (forcep || (t->tty.baud >= t->cap.pb && !t->cap.xon)) {
1994     if (t->cap.npc) {
1995       tv.tv_sec = ms/1000; tv.tv_usec = 1000*(ms%1000);
1996       if (t->u.n) {
1997         if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
1998           t->u.err = -1;
1999         t->u.n = 0;
2000       }
2001       if (t->tty.fpout) fflush(t->tty.fpout);
2002       select(0, 0, 0, 0, &tv);
2003     } else {
2004       pc = t->cap.pad ? *t->cap.pad : 0;
2005       sz = (ms*t->tty.baud + BITS_PER_KB - 1)/BITS_PER_KB;
2006       n = sizeof(t->u.buf) - t->u.n;
2007       if (sz <= n)
2008         { memset(t->u.buf + t->u.n, pc, sz); t->u.n += sz; }
2009       else {
2010         if (n) { memset(t->u.buf + t->u.n, pc, sz); sz -= n; }
2011         if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
2012           t->u.err = -1;
2013         if (sz < sizeof(t->u.buf))
2014           memset(t->u.buf, pc, sz);
2015         else {
2016           memset(t->u.buf, pc, sizeof(t->u.buf));
2017           do {
2018             if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
2019               t->u.err = -1;
2020             sz -= sizeof(t->u.buf);
2021           } while (sz > sizeof(t->u.buf));
2022         }
2023         t->u.n = sz;
2024       }
2025     }
2026   }
2027
2028 #undef BITS_PER_KB
2029 }
2030
2031 static void termunibi_prepout(struct tty *tty,
2032                               const struct gprintf_ops *gops, void *go)
2033 {
2034   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2035
2036   assert(!t->u.n); t->u.gops = gops; t->u.go = go;
2037 }
2038
2039 static int termunibi_flush(struct tty *tty)
2040 {
2041   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2042   int rc = t->u.err;
2043
2044   if (t->u.n) {
2045     if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) rc = -1;
2046     t->u.n = 0;
2047   }
2048   t->u.err = 0; return (rc);
2049 }
2050
2051 static int termunibi_put0(struct tty *tty,
2052                           unsigned npad, const char *cap)
2053 {
2054   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2055   unibi_var_t arg[9];
2056
2057   if (!cap) return (-1);
2058   unibi_format(t->u.dy, t->u.st, cap, arg,
2059                termunibi_putm, t,
2060                termunibi_pad, t);
2061   return (0);
2062 }
2063
2064 static int termunibi_put1i(struct tty *tty,
2065                            unsigned npad, const char *cap, int i0)
2066 {
2067   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2068   unibi_var_t arg[9];
2069
2070   if (!cap) return (-1);
2071   arg[0] = unibi_var_from_num(i0);
2072   unibi_format(t->u.dy, t->u.st, cap, arg,
2073                termunibi_putm, t,
2074                termunibi_pad, t);
2075   return (0);
2076 }
2077
2078 static int termunibi_put2i(struct tty *tty,
2079                            unsigned npad,
2080                            const char *cap, int i0, int i1)
2081 {
2082   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2083   unibi_var_t arg[9];
2084
2085   if (!cap) return (-1);
2086   arg[0] = unibi_var_from_num(i0);
2087   arg[1] = unibi_var_from_num(i1);
2088   unibi_format(t->u.dy, t->u.st, cap, arg,
2089                termunibi_putm, t,
2090                termunibi_pad, t);
2091   return (0);
2092 }
2093
2094 static void termunibi_release(struct tty *tty)
2095 {
2096   struct tty_unibilium *t = (struct tty_unibilium *)tty;
2097
2098   unibi_destroy(t->u.ut);
2099 }
2100
2101 static const union tty_capopsu termunibi_ops = { {
2102   { termunibi_release, TTY_CAPOPS },
2103   { termunibi_boolcap, termunibi_intcap, termunibi_strcap,
2104     termunibi_prepout, termunibi_flush,
2105     termunibi_put0, termunibi_put1i, termunibi_put2i }
2106 } };
2107
2108 static struct tty *termunibi_init(FILE *fp)
2109 {
2110   union tty_unibiliumu *u = 0; struct tty *ret = 0;
2111   unibi_term *ut = 0;
2112   const char *term;
2113
2114   term = getenv("TERM"); if (!term) goto end;
2115   ut = unibi_from_term(term); if (!ut) goto end;
2116   XNEW(u);
2117   u->tty.ops = &termunibi_ops.tty;
2118   u->u.u.ut = ut; ut = 0;
2119   u->u.u.n = 0; u->u.u.err = 0;
2120   common_init(&u->tty, fp);
2121   init_caps(&u->cap);
2122   ret = &u->tty; u = 0;
2123 end:
2124   xfree(u); if (ut) unibi_destroy(ut);
2125   return (ret);
2126 }
2127
2128 #endif
2129
2130 /*----- ANSI terminals ----------------------------------------------------*/
2131
2132 struct tty_ansislots {
2133   unsigned f;
2134 #define TAF_CNCATTR 1u                  /*   attributes can be cancelled */
2135 #define TAF_EDITN 2u                    /*   insert/delete multiple */
2136 #define TAF_SEMI 4u                     /*   semicolons in CSI 38 m colour */
2137 };
2138 struct tty_ansi { TTY_BASEPFX; struct tty_ansislots ansi; };
2139 union tty_ansiu { struct tty_ansi ansi; TTY_BASEUSFX; };
2140
2141 /* Control sequences.
2142  *
2143  *   * CUP: \33 [ Y ; X H               `cursor position' [vt100]
2144  *
2145  *   * CUU/CUD/CUR/CUL: \33 [ N A/B/C/D `cursor up/down/right/left'
2146  *
2147  *   * DCH: \33 [ N P                   `delete character' [vt220]
2148  *                                              (single char only in vt102?)
2149  *
2150  *   * DL: \33 [ N M                    `delete line' [vt220]
2151  *                                              (single line only in vt102?)
2152  *
2153  *   * ECH: \33 [ N X                   `erase characters' [vt220]
2154  *
2155  *   * ED: \33 [ P J                    `erase in display'
2156  *       P = 0                          erase to end-of-screen [vt100]
2157  *       P = 1                          erase from start-of-screen [vt100]
2158  *       P = 2                          erase entire screen [vt100]
2159  *
2160  *   * EL: \33 [ P K                    `erase in line'
2161  *       P = 0                          erase to end-of-line [vt100]
2162  *       P = 1                          erase from start-of-line [vt100]
2163  *       P = 2                          erase entire line [vt100]
2164  *
2165  *   * HPA/VPA: \33 [ I G/d             `horizontal/vertical position
2166  *                                              absolute' [ecma48-4]
2167  *
2168  *   * ICH: \33 [ N @                   `insert character' [vt220]
2169  *                                              (single char only in vt102?)
2170  *
2171  *   * IL: \33 [ N L                    `insert line' [vt220]
2172  *                                              (single line only in vt102?)
2173  *
2174  *   * SGR: \33 [ P ; ... m             `select graphics rendition'
2175  *       P = 0                          cancel all attributes [vt100]
2176  *       P = 1                          bold [vt100]
2177  *       P = 2                          dim [ecma48-4]
2178  *       P = 3                          italics [ecma48-4]
2179  *       P = 4                          underline [vt100]
2180  *       P = 7                          inverse video [vt100]
2181  *       P = 9                          strikeout [ecma48-4]
2182  *       P = 21                         double underline [ecma48-4]
2183  *       P = 22                         cancal bold/dim [vt220]
2184  *       P = 24                         cancel underline [vt220]
2185  *       P = 27                         cancel inverse video [vt220]
2186  *       P = 30 + 4 R + 2 G + B         set 1BPC foreground [ecma48-4]
2187  *       P = 38 : 2 : ? : R : G : B     set foreground [iso8613-6]
2188  *       P = 38 : 5 : N                 set foreground [iso8613-6, xterm]
2189  *       P = 39                         cancel foreground [ecma48-4]
2190  *       P = 40--49                     as above, for background
2191  *       P = 90 + 4 R + 2 G + B         set bright 1BPC foreground [xterm]
2192  *       P = 100 + 4 R + 2 G + B        set bright 1BPC background [xterm]
2193  *
2194  *   * SM/RM: \33 [ P ; ... h/l `set/reset modes'
2195  *       M = 4                          insert [vt220]
2196  *
2197  *   * SM, RM: \33 [ ? P ; ... h/l      `set/reset private modes'
2198  *       M = 7                          auto right margin [vt100]
2199  *       M = 25                         visible cursor [vt220]
2200  *       M = 1049                       alternate screen [xterm]
2201  *
2202  *   * \33 [ P ; X ; Y t                `window manipulation'
2203  *       P = 22, X = 0                  save title and icon [xterm]
2204  *       P = 23, X = 0                  restore title and icon [xterm]
2205  */
2206
2207 static void ansi_release(struct tty *tty) { ; }
2208
2209 #define PUTCH(ch) CHECK(gops->putch(go, (ch)))
2210 #define PUTLIT(lit) CHECK(gops->putm(go, (lit), sizeof(lit) - 1))
2211 #define SEMI do {                                                       \
2212   if (!(f&TAF_SEMI)) f |= TAF_SEMI;                                     \
2213   else PUTCH(';');                                                      \
2214 } while (0)
2215
2216 static int ansi_setcolour(struct tty_ansi *t, unsigned *f_inout,
2217                           const struct gprintf_ops *gops, void *go,
2218                           int norm, int br,
2219                           uint32 spc, uint32 clr)
2220 {
2221   unsigned f = *f_inout;
2222   int rc;
2223
2224   switch (spc) {
2225     case TTCSPC_NONE:
2226       SEMI; CHECK(gprintf(gops, go, "%d", norm + 9));
2227       break;
2228     case TTCSPC_1BPC:
2229       SEMI; CHECK(gprintf(gops, go, "%d", norm + clr));
2230       break;
2231     case TTCSPC_1BPCBR:
2232       SEMI; CHECK(gprintf(gops, go, "%d", br + (clr&~TT1BPC_BRI)));
2233       break;
2234     case TTCSPC_4LPC: case TTCSPC_6LPC:
2235       SEMI;
2236       if (t->ansi.f&TAF_SEMI)
2237         CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 16));
2238       else
2239         CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 16));
2240       break;
2241     case TTCSPC_8LGS:
2242       SEMI;
2243       if (t->ansi.f&TAF_SEMI)
2244         CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 80));
2245       else
2246         CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 80));
2247       break;
2248     case TTCSPC_24LGS:
2249       SEMI;
2250       if (t->ansi.f&TAF_SEMI)
2251         CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 232));
2252       else
2253         CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 232));
2254       break;
2255     case TTCSPC_8BPC:
2256       SEMI;
2257       if (t->ansi.f&TAF_SEMI)
2258         CHECK(gprintf(gops, go, "%d;2;%d;%d;%d", norm + 8,
2259                       TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
2260       else
2261         CHECK(gprintf(gops, go, "%d:2::%d:%d:%d", norm + 8,
2262                       TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
2263       break;
2264     default:
2265       rc = -1; goto end;
2266   }
2267
2268   rc = 0;
2269 end:
2270   *f_inout = f; return (rc);
2271 }
2272
2273 static int ansi_setattr(struct tty *tty,
2274                         const struct gprintf_ops *gops, void *go,
2275                         const struct tty_attr *a)
2276 {
2277   struct tty_ansi *t = (struct tty_ansi *)tty;
2278   uint32 diff;
2279   int rc = 0;
2280   unsigned z, c, f = 0;
2281
2282   diff = a->f ^ t->tty.st.attr.f;
2283   if (!diff && a->fg == t->tty.st.attr.fg && a->bg == t->tty.st.attr.bg)
2284     return (0);
2285
2286   c = 0;
2287 #define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
2288   if (CLEARP(TTAF_LNMASK)) c += 3;
2289   if (CLEARP(TTAF_WTMASK)) c += 3;
2290   if (diff&~a->f&TTAF_INVV) c += 3;
2291   if (diff&~a->f&TTAF_STRIKE) c += 3;
2292   if (diff&~a->f&TTAF_ITAL) c += 3;
2293   if (CLEARP(TTAF_FGSPCMASK)) c += 3;
2294   if (CLEARP(TTAF_BGSPCMASK)) c += 3;
2295 #undef CLEARP
2296
2297   z = 0;
2298   switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
2299     case TTLN_ULINE: z += 2; break;
2300     case TTLN_UULINE: z += 3; break;
2301   }
2302   if (a->f&TTAF_WTMASK) z += 2;
2303   if (a->f&TTAF_INVV) z += 2;
2304   if (a->f&TTAF_STRIKE) z += 2;
2305   if (a->f&TTAF_ITAL) z += 2;
2306 #define COLOURCOST(col) do {                                            \
2307   switch ((a->f&TTAF_##col##SPCMASK) >> TTAF_##col##SPCSHIFT) {         \
2308     case TTCSPC_1BPC: case TTCSPC_1BPCBR: z += 3; break;                \
2309     case TTCSPC_4LPC: case TTCSPC_8LGS: z += 8; break;                  \
2310     case TTCSPC_6LPC: case TTCSPC_24LGS: z += 9; break;                 \
2311     case TTCSPC_8BPC: z += 16; break;                                   \
2312   }                                                                     \
2313 } while (0)
2314   COLOURCOST(FG); COLOURCOST(BG);
2315 #undef COLOURCOST
2316
2317   PUTLIT("\33[");
2318
2319   if (z < c || (c && !(t->ansi.f&TAF_CNCATTR)))
2320     { SEMI; diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg = 0; }
2321
2322   if (diff&TTAF_LNMASK)
2323     switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
2324       case TTLN_NONE: SEMI; PUTLIT("24"); break;
2325       case TTLN_ULINE: SEMI; PUTCH('4'); break;
2326       case TTLN_UULINE: SEMI; PUTLIT("21"); break;
2327       default: rc = -1; goto end;
2328     }
2329
2330   if (diff&TTAF_WTMASK)
2331     switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
2332       case TTWT_MED: SEMI; PUTLIT("22"); break;
2333       case TTWT_BOLD: SEMI; PUTCH('1'); break;
2334       case TTWT_DIM: SEMI; PUTCH('2'); break;
2335       default: rc = -1; goto end;
2336     }
2337
2338   if (diff&TTAF_INVV)
2339     { SEMI; if (a->f&TTAF_INVV) PUTCH('7'); else PUTLIT("27"); }
2340   if (diff&TTAF_STRIKE)
2341     { SEMI; if (a->f&TTAF_STRIKE) PUTCH('9'); else PUTLIT("29"); }
2342   if (diff&TTAF_ITAL)
2343     { SEMI; if (a->f&TTAF_ITAL) PUTCH('3'); else PUTLIT("23"); }
2344
2345   if (diff&TTAF_FGSPCMASK || a->fg != tty->st.attr.fg)
2346     CHECK(ansi_setcolour(t, &f, gops, go, 30, 90,
2347                          (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
2348   if (diff&TTAF_BGSPCMASK || a->bg != tty->st.attr.bg)
2349     CHECK(ansi_setcolour(t, &f, gops, go, 40, 100,
2350                          (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
2351
2352   PUTCH('m'); rc = 0;
2353 end:
2354   t->tty.st.attr = *a; return (rc);
2355
2356 }
2357
2358 static int ansi_setmodes(struct tty *tty,
2359                          const struct gprintf_ops *gops, void *go,
2360                          uint32 modes_bic, uint32 modes_xor)
2361 {
2362   uint32 modes, diff;
2363   int rc;
2364
2365   /* Figure out which modes to set. */
2366   modes = (tty->st.modes&~modes_bic) ^ modes_xor;
2367   diff = modes ^ tty->st.modes;
2368
2369   if (diff&TTMF_AUTOM) {
2370     if (modes&TTMF_AUTOM) PUTLIT("\33[?7h");
2371     else PUTLIT("\33[?7l");
2372   }
2373
2374   if (diff&TTMF_FSCRN) {
2375     if (modes&TTMF_FSCRN) PUTLIT("\33[?1049h\33[22;0;0t");
2376     else PUTLIT("\33[?1049l\33[23;0;0t");
2377   }
2378
2379   if (diff&TTMF_CVIS) {
2380     if (modes&TTMF_CVIS) PUTLIT("\33[?25h");
2381     else PUTLIT("\33[?25l");
2382   }
2383
2384   if (diff&TTMF_INS) {
2385     if (modes&TTMF_INS) PUTLIT("\33[4h");
2386     else PUTLIT("\33[4l");
2387   }
2388
2389   rc = 0;
2390 end:
2391   tty->st.modes = modes;
2392   return (rc);
2393 }
2394
2395 static int ansi_move(struct tty *tty,
2396                      const struct gprintf_ops *gops, void *go,
2397                      unsigned orig, int y, int x)
2398 {
2399   int rc;
2400
2401   if (orig == TTORG_HOME) {
2402     if (!x) {
2403       if (!y) PUTLIT("\33[H");
2404       else CHECK(gprintf(gops, go, "\33[%dH", y + 1));
2405     } else {
2406       if (!y) CHECK(gprintf(gops, go, "\33[;%dH", x + 1));
2407       else CHECK(gprintf(gops, go, "\33[%d,%dH", y + 1, x + 1));
2408     }
2409   } else if (orig == (TTOF_XHOME | TTOF_YCUR) && x == 0 && y == 1)
2410     PUTLIT("\r\n");
2411   else {
2412     if (!(orig&TTOF_YCUR)) CHECK(gprintf(gops, go, "\33[%dd", y + 1));
2413     else if (y == -1) PUTLIT("\33[A");
2414     else if (y < 0) CHECK(gprintf(gops, go, "\33[%dA", -y));
2415     else if (y == +1) PUTLIT("\33[B"); /* not %|^J|%! */
2416     else if (y > 1) CHECK(gprintf(gops, go, "\33[%dB", y));
2417     if (!(orig&TTOF_XCUR)) {
2418       if (!x)
2419         PUTCH('\r');
2420       else if (tty->ocaps&TTCF_MIXMV)
2421         CHECK(gprintf(gops, go, "\33[%dG", x + 1));
2422       else
2423         CHECK(gprintf(gops, go, "\r\33[%dC", x));
2424     } else {
2425       if (x == -1) PUTCH('\b');
2426       else if (x < 0) CHECK(gprintf(gops, go, "\33[%dD", -x));
2427       else if (x == +1) PUTLIT("\33[C");
2428       else if (x > 0) CHECK(gprintf(gops, go, "\33[%dC", x));
2429     }
2430   }
2431   rc = 0;
2432 end:
2433   return (rc);
2434 }
2435
2436 static int ansi_erase(struct tty *tty,
2437                       const struct gprintf_ops *gops, void *go,
2438                       unsigned f)
2439 {
2440   int rc;
2441
2442   if (f&TTEF_DSP)
2443     switch (f&(TTEF_BEGIN | TTEF_END)) {
2444       case 0: break;
2445       case TTEF_BEGIN: PUTLIT("\33[1J"); break;
2446       case TTEF_END: PUTLIT("\33[J"); break;
2447       case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2J"); break;
2448     }
2449   else
2450     switch (f&(TTEF_BEGIN | TTEF_END)) {
2451       case 0: break;
2452       case TTEF_BEGIN: PUTLIT("\33[1K"); break;
2453       case TTEF_END: PUTLIT("\33[K"); break;
2454       case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2K"); break;
2455     }
2456   rc = 0;
2457 end:
2458   return (rc);
2459 }
2460
2461 static int ansi_erch(struct tty *tty,
2462                      const struct gprintf_ops *gops, void *go,
2463                      unsigned n)
2464 {
2465   int rc;
2466
2467   if (n == 1) PUTLIT("\33[X");
2468   else if (n) CHECK(gprintf(gops, go, "\33[%uX", n));
2469   rc = 0;
2470 end:
2471   return (rc);
2472 }
2473
2474 static int ansi_ins(struct tty *tty,
2475                     const struct gprintf_ops *gops, void *go,
2476                     unsigned f, unsigned n)
2477 {
2478   int rc;
2479
2480   if (f&TTIDF_LN) {
2481     if (n == 1) PUTLIT("\33[L");
2482     else if (n) CHECK(gprintf(gops, go, "\33[%uL", n));
2483   } else {
2484     if (n == 1) PUTLIT("\33[@");
2485     else if (n) CHECK(gprintf(gops, go, "\33[%u@", n));
2486   }
2487   rc = 0;
2488 end:
2489   return (rc);
2490 }
2491
2492 static int ansi_inch(struct tty *tty,
2493                      const struct gprintf_ops *gops, void *go,
2494                      int ch)
2495 {
2496   if (!(tty->st.modes&TTMF_INS)) return (-1);
2497   else return (gops->putch(go, ch));
2498 }
2499
2500 static int ansi_del(struct tty *tty,
2501                     const struct gprintf_ops *gops, void *go,
2502                     unsigned f, unsigned n)
2503 {
2504   int rc;
2505
2506   if (f&TTIDF_LN) {
2507     if (n == 1) PUTLIT("\33[M");
2508     else if (n) CHECK(gprintf(gops, go, "\33[%uM", n));
2509   } else {
2510     if (n == 1) PUTLIT("\33[P");
2511     else if (n) CHECK(gprintf(gops, go, "\33[%uP", n));
2512   }
2513   rc = 0;
2514 end:
2515   return (rc);
2516 }
2517
2518 #undef PUTCH
2519 #undef PUTLIT
2520 #undef SEMI
2521
2522 #undef CHECK
2523
2524 static const struct tty_ops ansi_ops = {
2525   ansi_release,
2526   ansi_setattr, ansi_setmodes,
2527   ansi_move, stupid_repeat,
2528   ansi_erase, ansi_erch, ansi_ins, ansi_inch, ansi_del
2529 };
2530
2531 static struct tty *ansi_init(FILE *fp)
2532 {
2533 #define COLS_NO 0
2534 #define COLS_8 (TTACF_FG | TTACF_BG | TTACF_1BPC)
2535 #define COLS_16 (COLS_8 | TTACF_1BPCBR)
2536 #define COLS_88 (COLS_16 | TTACF_4LPC | TTACF_8LGS)
2537 #define COLS_256 (COLS_16 | TTACF_6LPC | TTACF_24LGS)
2538 #define COLS_16M (COLS_256 | TTACF_8BPC)
2539
2540 #define EDIT_OPS (TTCF_ERCH |                                           \
2541                   TTCF_DELCH | TTCF_DELLN |                             \
2542                   TTCF_INSCH | TTCF_INSLN)
2543
2544   static const struct flagmap {
2545     const char *name;
2546     uint32 acaps, ocaps;
2547     unsigned tf;
2548   } flagmap[] = {
2549     { "dim",            TTACF_DIM,      0,              0 },
2550     { "uuline",         TTACF_UULINE,   0,              0 },
2551     { "strike",         TTACF_STRIKE,   0,              0 },
2552     { "ital",           TTACF_ITAL,     0,              0 },
2553     { "cvis",           0,              TTMF_CVIS,      0 },
2554     { "fscrn",          0,              TTMF_FSCRN,     0 },
2555     { "insmode",        0,              TTMF_INS,       0 },
2556     { "hvpa"    ,       0,              TTCF_MIXMV,     0 },
2557     { "edit",           0,              EDIT_OPS,       0 },
2558     { "cncattr",        0,              0,              TAF_CNCATTR },
2559     { "editn",          0,              0,              TAF_EDITN },
2560     { "semi",           0,              0,              TAF_SEMI },
2561     { 0,                0,              0,              0 }
2562   };
2563
2564 #undef EDIT_OPS
2565
2566   static const struct kw { const char *name; uint32 val; }
2567     kw_colours[] = {
2568       { "no",           COLS_NO },
2569       { "8",            COLS_8 },
2570       { "16",           COLS_16 },
2571       { "88",           COLS_88 },
2572       { "256",          COLS_256 },
2573       { "16m",          COLS_16M },
2574       { 0,              0 }
2575     };
2576
2577   static const struct enummap {
2578     const char *name;
2579     uint32 mask;
2580     const struct kw *kw;
2581   } enummap[] = {
2582     { "colours",        TTACF_CSPCMASK | TTACF_FG | TTACF_BG,
2583                                                         kw_colours },
2584     { 0,                0,                              0 }
2585   };
2586
2587
2588   static const struct termmap {
2589     const char *pat;
2590     unsigned acaps, ocaps, tf;
2591   } termmap[] = {
2592
2593 #define VT100_ACAPS (TTACF_ULINE | TTACF_BOLD | TTACF_INVV)
2594 #define VT100_OCAPS (TTMF_AUTOM |                                       \
2595                      TTCF_RELMV | TTCF_ABSMV |                          \
2596                      TTCF_MMARG |                                       \
2597                      TTCF_ERBOL | TTCF_EREOL |                          \
2598                      TTCF_ERBOD | TTCF_EREOD | TTCF_ERDSP)
2599 #define VT100_TF (0)
2600
2601 #define VT102_ACAPS (VT100_ACAPS)
2602 #define VT102_OCAPS (VT100_OCAPS |                                      \
2603                      TTMF_INS |                                         \
2604                      TTCF_INSCH | TTCF_INSLN | TTCF_DELCH | TTCF_DELLN)
2605 #define VT102_TF (VT100_TF)
2606
2607 #define VT220_ACAPS (VT102_ACAPS)
2608 #define VT220_OCAPS (VT102_OCAPS | TTMF_CVIS | TTCF_ERCH)
2609 #define VT220_TF (VT102_TF | TAF_CNCATTR | TAF_EDITN)
2610
2611 #define ECMA48_ACAPS (VT220_ACAPS | TTACF_DIM)
2612 #define ECMA48_OCAPS (VT220_OCAPS | TTCF_MIXMV)
2613 #define ECMA48_TF (VT220_TF)
2614
2615 #define XTERM_ACAPS (ECMA48_ACAPS)
2616 #define XTERM_OCAPS (ECMA48_OCAPS | TTMF_FSCRN)
2617 #define XTERM_TF (ECMA48_TF)
2618
2619 #define STRIKE TTACF_STRIKE
2620 #define ITAL TTACF_ITAL
2621 #define SEMI TAF_SEMI
2622
2623 #define T(pat, base, cols, acaps, ocaps, tf)                            \
2624   { pat,                                                                \
2625     base##_ACAPS | COLS_##cols | (acaps),                               \
2626     base##_OCAPS | (ocaps), base##_TF | (tf) }
2627
2628     T("color_xterm",    XTERM,  8,      STRIKE | ITAL,  0,      0),
2629
2630     T("gnome",          XTERM,  16M,    STRIKE | ITAL,  0,      SEMI),
2631   /*T("gonme-*"         XTERM,  16M,    STRIKE | ITAL,  0,      SEMI),*/
2632
2633     T("linux",          XTERM,  16,     0,              0,      0),
2634
2635     T("putty",          XTERM,  16M,    0,              0,      SEMI),
2636
2637     T("vt100*",         VT100,  NO,     0,              0,      0),
2638     T("vt102*",         VT102,  NO,     0,              0,      0),
2639     T("vt[2-5][0-9][0-9]*", VT220, NO,  0,              0,      0),
2640
2641     T("vte",            XTERM,  16M,    STRIKE | ITAL,  0,      SEMI),
2642   /*T("vte-*"           XTERM,  16M,    STRIKE | ITAL,  0,      SEMI),*/
2643
2644     T("win",            XTERM,  16M,    0,              0,      SEMI),
2645
2646     T("xterm",          XTERM,  16M,    STRIKE | ITAL,  0,      0),
2647     T("xterm-color",    XTERM,  8,      STRIKE | ITAL,  0,      0),
2648     T("xterm-16color",  XTERM,  16,     STRIKE | ITAL,  0,      0),
2649     T("xterm-88color",  XTERM,  88,     STRIKE | ITAL,  0,      SEMI),
2650     T("xterm-256color", XTERM,  256,    STRIKE | ITAL,  0,      SEMI),
2651     T("xterm-direct",   XTERM,  16M,    STRIKE | ITAL,  0,      0),
2652     T("xterm-*",        XTERM,  16M,    STRIKE | ITAL,  0,      0),
2653
2654   /*T("*-color",        XTERM,  16,     0,              0,      0),*/
2655   /*T("*-16color",      XTERM,  16,     0,              0,      0),*/
2656     T("*-88color",      XTERM,  88,     0,              0,      SEMI),
2657     T("*-256color",     XTERM,  256,    0,              0,      SEMI),
2658     T("*-direct",       XTERM,  16M,    0,              0,      SEMI),
2659
2660     T("*",              XTERM,  16,     0,              0,      0),
2661     { 0,                                0,              0,      0 }
2662
2663 #undef VT100_ACAPS
2664 #undef VT100_OCAPS
2665 #undef VT100_TF
2666
2667 #undef VT102_ACAPS
2668 #undef VT102_OCAPS
2669 #undef VT102_TF
2670
2671 #undef VT220_ACAPS
2672 #undef VT220_OCAPS
2673 #undef VT220_TF
2674
2675 #undef ECMA48_ACAPS
2676 #undef ECMA48_OCAPS
2677 #undef ECMA48_TF
2678
2679 #undef XTERM_ACAPS
2680 #undef XTERM_OCAPS
2681 #undef XTERM_TF
2682
2683 #undef STRIKE
2684 #undef ITAL
2685 #undef SEMI
2686   };
2687
2688 #undef COLS_NO
2689 #undef COLS_8
2690 #undef COLS_16
2691 #undef COLS_88
2692 #undef COLS_256
2693 #undef COLS_16M
2694
2695   union tty_ansiu *u = 0; struct tty *ret = 0;
2696   const char *term, *config, *p, *l;
2697   const struct kw *kw;
2698   const struct enummap *em;
2699   const struct flagmap *fm;
2700   const struct termmap *tm;
2701   size_t n, nn;
2702   unsigned
2703     acaps = 0, ocaps = 0, tf = 0,
2704     acapset = 0, ocapset = 0, tfset = 0,
2705     f = 0;
2706 #define f_sense 1u
2707
2708   config = getenv("MLIB_TTY_ANSICONFIG");
2709   term = getenv("TERM");
2710
2711   if (term && STRCMP(term, ==, "dumb")) goto end;
2712
2713   if (config) {
2714     l = config + strlen(config);
2715     for (;;) {
2716
2717       for (;;)
2718         if (config >= l) goto done_config;
2719         else if (!ISSPACE(*config)) break;
2720         else config++;
2721
2722       for (p = config + 1; p < l && !ISSPACE(*p); p++);
2723       if (*config == '+' || *config == '-') {
2724         if (*config == '+') f |= f_sense;
2725         else f &= ~f_sense;
2726         config++; n = p - config;
2727
2728         for (fm = flagmap; fm->name; fm++)
2729           if (STRNCMP(config, ==, fm->name, n) && !fm->name[n])
2730             goto found_flag;
2731         debug("unknown flag `%.*s'", (int)n, config); goto next_config;
2732       found_flag:
2733         if ((acapset&fm->acaps) || (ocapset&fm->ocaps) || (tfset&fm->tf)) {
2734           debug("duplicate setting for `%s'", fm->name);
2735           goto next_config;
2736         }
2737         if (f&f_sense)
2738           { acaps |= fm->acaps; ocaps |= fm->ocaps; tf |= fm->tf; }
2739         acapset |= fm->acaps; ocapset |= fm->ocaps; tfset |= fm->tf;
2740       } else {
2741         n = p - config;
2742         p = memchr(config, '=', n);
2743         if (!p) {
2744           debug("missing `=' in setting `%.*s'", (int)n, config);
2745           goto next_config;
2746         }
2747         nn = p - config;
2748         for (em = enummap; em->name; em++)
2749           if (STRNCMP(config, ==, em->name, nn) && !em->name[nn])
2750             goto found_enum;
2751         debug("unknown setting `%.*s'", (int)nn, config); goto next_config;
2752       found_enum:
2753         p++; nn = n - nn - 1;
2754         for (kw = em->kw; kw->name; kw++)
2755           if (STRNCMP(p, ==, kw->name, nn) && !kw->name[nn])
2756             goto found_kw;
2757         debug("unknown `%s' value `%.*s", em->name, (int)nn, p);
2758         goto next_config;
2759       found_kw:
2760         if (acapset&em->mask) {
2761           debug("duplicate setting for `%s'", em->name);
2762           goto next_config;
2763         }
2764         acaps |= kw->val; acapset |= em->mask;
2765       }
2766
2767     next_config:
2768       config += n;
2769     }
2770   done_config:;
2771   }
2772
2773   if (term) {
2774     for (tm = termmap; tm->pat; tm++)
2775       if (str_match(tm->pat, term))
2776         goto found_term;
2777     assert(0);
2778   found_term:
2779     acaps |= tm->acaps&~acapset;
2780     ocaps |= tm->ocaps&~ocapset;
2781     tf |= tm->tf&~tfset;
2782   }
2783
2784   if (!(acapset&TTACF_CSPCMASK)) env_colour_caps(&acaps, ECCF_SET);
2785   if (acaps&TTACF_CSPCMASK) ocaps |= TTCF_BGER;
2786
2787   XNEW(u);
2788   u->tty.ops = &ansi_ops;
2789   u->tty.acaps = acaps;
2790   u->tty.ocaps = ocaps;
2791   u->ansi.ansi.f = tf;
2792   u->tty.wd = 80; u->tty.ht = 25;
2793   u->tty.st.modes = TTMF_AUTOM | (u->tty.ocaps&TTMF_CVIS);
2794   u->tty.st.attr.f = 0; u->tty.st.attr.fg = u->tty.st.attr.bg = 0;
2795   common_init(&u->ansi.tty, fp);
2796   ret = &u->tty; u = 0;
2797 end:
2798   xfree(u); return (ret);
2799
2800 #undef f_sense
2801 }
2802
2803 /*----- Backend selection -------------------------------------------------*/
2804
2805 struct tty *tty_open(FILE *fp, unsigned f, const unsigned *backends)
2806 {
2807   static const struct betab {
2808     const char *name; unsigned code;
2809     struct tty *(*init)(FILE */*fp*/);
2810   } betab[] = {
2811 #ifdef HAVE_UNIBILIUM
2812     { "unibilium",      TTBK_UNIBI,     termunibi_init },
2813 #endif
2814 #ifdef HAVE_TERMINFO
2815     { "terminfo",       TTBK_TERMINFO,  terminfo_init },
2816 #endif
2817 #ifdef HAVE_TERMCAP
2818     { "termcap",        TTBK_TERMCAP,   termcap_init },
2819 #endif
2820     { "ansi",           TTBK_ANSI,      ansi_init },
2821     { 0,                0,              0 }
2822   };
2823
2824   const struct betab *bt;
2825   const char *config, *p, *l;
2826   struct tty *tty = 0;
2827   FILE *fpin = 0;
2828   size_t n;
2829
2830   if (fp || !(f&TTF_OPEN))
2831     fpin = fp != stdout ? fp : isatty(STDIN_FILENO) ? stdin : 0;
2832   else {
2833     if (isatty(STDIN_FILENO)) fpin = stdin;
2834     else fpin = 0;
2835     if (isatty(STDOUT_FILENO)) { fp = stdout; f |= TTF_BORROW; }
2836     else if (isatty(STDERR_FILENO)) { fp = stderr; f |= TTF_BORROW; }
2837     else {
2838       fp = fopen("/dev/tty", "r+"); if (!fp) goto end;
2839       fpin = fp; f &= ~TTF_BORROW;
2840     }
2841   }
2842
2843   config = getenv("MLIB_TTY_BACKENDS");
2844   if (config) {
2845     l = config + strlen(config);
2846     for (;;) {
2847       for (;;)
2848         if (config >= l) goto done_config;
2849         else if (!ISSPACE(*config)) break;
2850         else config++;
2851
2852       for (p = config + 1; p < l && !ISSPACE(*p); p++);
2853       n = p - config;
2854
2855       for (bt = betab; bt->name; bt++)
2856         if (STRNCMP(config, ==, bt->name, n) && !bt->name[n])
2857           goto found_byname;
2858       debug("unknown backend `%.*s'", (int)n, config); goto next_config;
2859     found_byname:
2860       tty = bt->init(fp); if (tty) goto found;
2861       debug("failed to initialize `%s'", bt->name);
2862     next_config:
2863       config += n;
2864     }
2865   done_config:;
2866   } else if (backends)
2867     while (*backends) {
2868       for (bt = betab; bt->name; bt++)
2869         if (*backends == bt->code) goto found_bycode;
2870       debug("unknown backend code %u", *backends); goto next_code;
2871     found_bycode:
2872       tty = bt->init(fp); if (tty) goto found;
2873       debug("failed to initialize `%s'", bt->name);
2874     next_code:
2875       backends++;
2876     }
2877   else
2878     for (bt = betab; bt->name; bt++) {
2879       tty = bt->init(fp); if (tty) goto found;
2880       debug("failed to initialize `%s'", bt->name);
2881     }
2882
2883   debug("all backends failed"); goto end;
2884 found:
2885   debug("selected backend `%s'", bt->name);
2886   tty->fpin = fpin; tty->f = f; fp = 0;
2887 end:
2888   if (fp && !(f&TTF_BORROW)) fclose(fp);
2889   return (tty);
2890 }
2891
2892 void tty_close(struct tty *tty)
2893 {
2894   if (tty) {
2895     if (tty->fpout && !(tty->f&TTF_BORROW)) fclose(tty->fpout);
2896     tty->ops->release(tty); xfree(tty);
2897   }
2898 }
2899
2900 int tty_resized(struct tty *tty)
2901 {
2902   struct winsize ws;
2903
2904   if (!tty || !tty->fpout) { errno = ENOTTY; return (-1); }
2905   else if (ioctl(fileno(tty->fpout), TIOCGWINSZ, &ws)) return (-1);
2906   else if (tty->wd == ws.ws_col && tty->ht == ws.ws_row) return (0);
2907   else { tty->wd = ws.ws_col; tty->ht = ws.ws_row; return (1); }
2908 }
2909
2910 /*----- Terminal operations -----------------------------------------------*/
2911
2912 int tty_setattr(struct tty *tty, const struct tty_attr *a)
2913 {
2914   struct tty_attr aa;
2915
2916   if (!tty || !tty->fpout)
2917     return (-1);
2918   else {
2919     clamp_attr(&aa, a, tty->acaps);
2920     return (tty->ops->setattr(tty, &file_printops, tty->fpout, &aa));
2921   }
2922 }
2923
2924 int tty_setattrg(struct tty *tty,
2925                  const struct gprintf_ops *gops, void *go,
2926                  const struct tty_attr *a)
2927 {
2928   struct tty_attr aa;
2929
2930   if (!tty)
2931     return (-1);
2932   else {
2933     clamp_attr(&aa, a, tty->acaps);
2934     return (tty->ops->setattr(tty, gops, go, &aa));
2935   }
2936 }
2937
2938 int tty_setattrlist(struct tty *tty, const struct tty_attrlist *aa)
2939 {
2940   if (!tty || !tty->fpout) return (-1);
2941   else return (tty_setattrlistg(tty, &file_printops, tty->fpout, aa));
2942 }
2943
2944 int tty_setattrlistg(struct tty *tty,
2945                      const struct gprintf_ops *gops, void *go,
2946                      const struct tty_attrlist *aa)
2947 {
2948   if (!tty) return (-1);
2949   for (;; aa++)
2950     if ((tty->acaps&aa->cap_mask) == aa->cap_eq)
2951       return (tty->ops->setattr(tty, gops, go, &aa->attr));
2952     else if (!aa->cap_mask)
2953       return (0);
2954 }
2955
2956 int tty_setmodes(struct tty *tty, uint32 modes_bic, uint32 modes_xor)
2957 {
2958   if (!tty || !tty->fpout) return (-1);
2959   else return (tty->ops->setmodes(tty, &file_printops, tty->fpout,
2960                                   modes_bic, modes_xor));
2961 }
2962
2963 int tty_setmodesg(struct tty *tty,
2964                   const struct gprintf_ops *gops, void *go,
2965                   uint32 modes_bic, uint32 modes_xor)
2966 {
2967   if (!tty) return (-1);
2968   else return (tty->ops->setmodes(tty, gops, go, modes_bic, modes_xor));
2969 }
2970
2971 int tty_move(struct tty *tty, unsigned orig, int y, int x)
2972 {
2973   if (!tty || !tty->fpout) return (-1);
2974   else return (tty->ops->move(tty, &file_printops, tty->fpout, orig, y, x));
2975 }
2976
2977 int tty_moveg(struct tty *tty,
2978               const struct gprintf_ops *gops, void *go,
2979               unsigned orig, int y, int x)
2980 {
2981   if (!tty) return (-1);
2982   else return (tty->ops->move(tty, gops, go, orig, y, x));
2983 }
2984
2985 int tty_repeat(struct tty *tty, int ch, unsigned n)
2986 {
2987   if (!tty || !tty->fpout) return (-1);
2988   else return (tty->ops->repeat(tty, &file_printops, tty->fpout, ch, n));
2989 }
2990
2991 int tty_repeatg(struct tty *tty,
2992                 const struct gprintf_ops *gops, void *go,
2993                 int ch, unsigned n)
2994 {
2995   if (!tty) return (-1);
2996   else return (tty->ops->repeat(tty, gops, go, ch, n));
2997 }
2998
2999 int tty_erase(struct tty *tty, unsigned f)
3000 {
3001   if (!tty || !tty->fpout) return (-1);
3002   else return (tty->ops->erase(tty, &file_printops, tty->fpout, f));
3003 }
3004
3005 int tty_eraseg(struct tty *tty,
3006                const struct gprintf_ops *gops, void *go,
3007                unsigned f)
3008 {
3009   if (!tty) return (-1);
3010   else return (tty->ops->erase(tty, gops, go, f));
3011 }
3012
3013 int tty_erch(struct tty *tty, unsigned n)
3014 {
3015   if (!tty || !tty->fpout) return (-1);
3016   else return (tty->ops->erch(tty, &file_printops, tty->fpout, n));
3017 }
3018
3019 int tty_erchg(struct tty *tty,
3020               const struct gprintf_ops *gops, void *go,
3021               unsigned n)
3022 {
3023   if (!tty) return (-1);
3024   else return (tty->ops->erch(tty, gops, go, n));
3025 }
3026
3027 int tty_ins(struct tty *tty, unsigned f, unsigned n)
3028 {
3029   if (!tty || !tty->fpout) return (-1);
3030   else return (tty->ops->ins(tty, &file_printops, tty->fpout, f, n));
3031 }
3032
3033 int tty_insg(struct tty *tty,
3034              const struct gprintf_ops *gops, void *go,
3035              unsigned f, unsigned n)
3036 {
3037   if (!tty) return (-1);
3038   else return (tty->ops->ins(tty, gops, go, f, n));
3039 }
3040
3041 int tty_inch(struct tty *tty, int ch)
3042 {
3043   if (!tty || !tty->fpout) return (-1);
3044   else return (tty->ops->inch(tty, &file_printops, tty->fpout, ch));
3045 }
3046
3047 int tty_inchg(struct tty *tty,
3048               const struct gprintf_ops *gops, void *go,
3049               int ch)
3050 {
3051   if (!tty) return (-1);
3052   else return (tty->ops->inch(tty, gops, go, ch));
3053 }
3054
3055 int tty_del(struct tty *tty, unsigned f, unsigned n)
3056 {
3057   if (!tty || !tty->fpout) return (-1);
3058   else return (tty->ops->del(tty, &file_printops, tty->fpout, f, n));
3059 }
3060
3061 int tty_delg(struct tty *tty,
3062              const struct gprintf_ops *gops, void *go,
3063              unsigned f, unsigned n)
3064 {
3065   if (!tty) return (-1);
3066   else return (tty->ops->del(tty, gops, go, f, n));
3067 }
3068
3069 int tty_restore(struct tty *tty, const struct tty_state *st)
3070 {
3071   if (!tty || !tty->fpout) return (-1);
3072   else return (tty_restoreg(tty, &file_printops, tty->fpout, st));
3073 }
3074
3075 int tty_restoreg(struct tty *tty,
3076                  const struct gprintf_ops *gops, void *go,
3077                  const struct tty_state *st)
3078 {
3079   int rc;
3080
3081   if (!tty ||
3082       tty->ops->setmodes(tty, gops, go, MASK32, st->modes) ||
3083       tty->ops->setattr(tty, gops, go, &st->attr))
3084     { rc = -1; goto end; }
3085   rc = 0;
3086 end:
3087   return (rc);
3088 }
3089
3090 /*----- That's all, folks -------------------------------------------------*/