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