5 * (c) 2024 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 /*----- Header files ------------------------------------------------------*/
38 #include <sys/types.h>
42 #include <sys/ioctl.h>
43 #include <sys/select.h>
58 # include <unibilium.h>
67 /*----- Miscellaneous preliminaries ---------------------------------------*/
69 /* Buffer size parameters. */
70 #define BUFSZ 4096 /* size of a buffer */
71 #define THRESH (BUFSZ/2) /* threshold for buffering */
73 /* Incorporate the published control-block structure into our more elaborate
76 #define TTY_BASEPFX struct tty tty
77 #define TTY_BASEUSFX struct tty tty
80 void (*release)(struct tty */*tty*/);
81 /* Free any resources held by the backend. */
83 /* The following operations handle the correspondingly named interface
86 int (*setattr)(struct tty */*tty*/,
87 const struct gprintf_ops */*gops*/, void */*go*/,
88 const struct tty_attr */*a*/);
89 int (*setmodes)(struct tty */*tty*/,
90 const struct gprintf_ops */*gops*/, void */*go*/,
91 uint32 /*modes_bic*/, uint32 /*modes_xor*/);
92 int (*move)(struct tty */*tty*/,
93 const struct gprintf_ops */*gops*/, void */*go*/,
94 unsigned /*orig*/, int /*y*/, int /*x*/);
95 int (*repeat)(struct tty */*tty*/,
96 const struct gprintf_ops */*gops*/, void */*go*/,
97 int /*ch*/, unsigned /*n*/);
98 int (*erase)(struct tty */*tty*/,
99 const struct gprintf_ops */*gops*/, void */*go*/,
101 int (*erch)(struct tty */*tty*/,
102 const struct gprintf_ops */*gops*/, void */*go*/,
104 int (*ins)(struct tty */*tty*/,
105 const struct gprintf_ops */*gops*/, void */*go*/,
106 unsigned /*f*/, unsigned /*n*/);
107 int (*inch)(struct tty */*tty*/,
108 const struct gprintf_ops */*gops*/, void */*go*/,
110 int (*del)(struct tty */*tty*/,
111 const struct gprintf_ops */*gops*/, void */*go*/,
112 unsigned /*f*/, unsigned /*n*/);
114 #define TTY_BASEOPSPFX struct tty_ops tty
115 #define TTY_BASEOPSUXFX struct tty_ops tty
117 /*----- Common support machinery ------------------------------------------*/
121 * Arguments@ @expr@ = expression to evaluate
123 * Use: Evaluate @expr@. If the result is (strictly) negative, then
124 * set @rc = -1@ and transfer control to the label @end@.
127 #define CHECK(expr) do { if ((expr) < 0) { rc = -1; goto end; } } while (0)
131 * Arguments: @const char *fmt@ = format control string
132 * @...@ = format arguemnts
136 * Use: Maybe report a debugging message to standard error.
139 static PRINTF_LIKE(1, 2) void debug(const char *fmt, ...)
144 p = getenv("MLIB_TTY_DEBUG");
145 if (p && *p != 'n' && *p != '0') {
147 fputs("mLib TTY: ", stderr);
148 vfprintf(stderr, fmt, ap);
154 /* --- @common_init@ --- *
156 * Arguments: @struct tty *tty@ = pointer to terminal control block
157 * @FILE *fp@ = output file stream
158 * @speed_t *ospeed_out@ = where to put encoded output rate, or
163 * Use: Perform general initialization on the terminal control
166 * Specifically, this fills in the @fpout@, @baud@, @ht@, and
167 * @wd@ slots. The width and height come from the kernel, or,
168 * failing that, the environment.
170 * For `termcap''s benefit, store the system-encoded output baud
171 * rate in @*ospeed_out@, if it can be found.
174 static void common_init(struct tty *tty, FILE *fp, speed_t *ospeed_out)
176 static const struct baudtab { speed_t code; unsigned baud; } baudtab[] = {
178 ;;; The baud-rate table is very boring to type. To make life less
179 ;;; awful, put the rates in this list and evaluate the code to get Emacs
180 ;;; to regenerate it.
182 (let ((bauds '(50 75 110 134 150 200 300 600 1200 1800 2400 4800 9600
183 19200 38400 57600 115200 230400 460800 500000 576000
184 921600 1000000 1152000 1500000 2000000 2500000 3000000
187 (goto-char (point-min))
188 (search-forward (concat "***" "BEGIN baudlist" "***"))
189 (beginning-of-line 2)
190 (delete-region (point)
192 (search-forward "***END***")
195 (dolist (baud (sort (copy-list bauds) #'<))
196 (insert (format "#ifdef B%d\n { B%d, %d },\n#endif\n"
199 /***BEGIN baudlist***/
267 { B1000000, 1000000 },
270 { B1152000, 1152000 },
273 { B1500000, 1500000 },
276 { B2000000, 2000000 },
279 { B2500000, 2500000 },
282 { B3000000, 3000000 },
285 { B3500000, 3500000 },
288 { B4000000, 4000000 },
294 const struct baudtab *b;
295 const char *p; int n;
299 /* Save the output stream. */
302 /* Determine the output baud rate. Unhelpfully, the kernel provides a
303 * weird code, so we have to convert it into an actual rate in bits per
306 tty->baud = 0; tty->wd = tty->ht = 0;
307 if (fp && !tcgetattr(fileno(fp), &c)) {
308 code = cfgetospeed(&c); if (ospeed_out) *ospeed_out = code;
309 for (b = baudtab; b->baud; b++)
310 if (b->code == code) { tty->baud = b->baud; break; }
314 /* If the kernel didn't tell us the terminal dimensions, try to read them
315 * from the environment.
318 { p = getenv("COLUMNS"); if (p) { n = atoi(p); if (n) tty->wd = n; } }
320 { p = getenv("LINES"); if (p) { n = atoi(p); if (n) tty->ht = n; } }
323 /* --- @env_colour_caps@ --- *
325 * Arguments: @unsigned *caps_inout@ = attribute capabilities to update
326 * @unsigned f@ = flags
330 * Use: Check the %|FORCE_COLOR|% environment variable and update the
331 * capabilities as required.
333 * The %|FORCE_COLOR|% variable originates with the Node
334 * community, with two objectives: (a) to convey policy
335 * regarding whether to produce coloured output, and (b) to
336 * describe the colour capabilities of the terminal, because the
337 * traditional mechanisms are deemed inadequate.
339 * The following values have defined meanings.
341 * * Unset or empty: no effect.
343 * * %|0|%: monochrome; don't produce colour.
345 * * %|1|%: 16 colours; 1-bit-per-channel colours are
346 * available, with an additional common brightness bit.
348 * * %|2|%: 256 colours; the `xterm' 256-bit palette is
349 * available, consisting of the 1-bit-per-channel colours
350 * with common brightness bit, a 6 × 6 × 6 colour cube, and
351 * a 24-level greyscale ramp.
353 * * %|3|%: full 24-bit colour.
355 * * Anything else: a request to use colour if available.
356 * This is ignored here, in the expectation that it will be
357 * given effect elsewhere, e.g., by @ttycolour_enablep@.
359 * If @ECCF_SET@ is set, then set or clear capabilities as
360 * required. Otherwise, clear capability bits which are denied
361 * by the variable setting, but no bits will be set. (This
362 * latter is necessary for backends which use terminal
363 * databases, since they can't be expected to make up the
364 * necessary control sequences for themselves.)
368 static void env_colour_caps(unsigned *caps_inout, unsigned f)
371 unsigned caps = *caps_inout, mask;
373 p = getenv("FORCE_COLOR"); if (!p) return;
379 mask = TTACF_FG | TTACF_BG | TTACF_1BPC | TTACF_1BPCBR;
382 mask = TTACF_FG | TTACF_BG |
383 TTACF_1BPC | TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
386 mask = TTACF_FG | TTACF_BG |
387 TTACF_1BPC | TTACF_1BPCBR | TTACF_8BPC;
392 if (!(f&ECCF_SET)) caps &= mask;
393 else caps = (caps&~(TTACF_CSPCMASK | TTACF_FG | TTACF_BG)) | mask;
398 /* --- @clamp_colours@ --- *
400 * Arguments: @uint32 *space_out, *colour_out@ = selected space and colour
401 * @uint32 space, colour@ = requested space and colour
402 * @uint32 acaps@ = terminal's attribute capability mask
406 * Use: Select the best approximation to the requested colour which
407 * can be accommodated by the terminal.
410 /* #define DEBUG_CLAMP */
412 static void clamp_colours(uint32 *space_out, uint32 *colour_out,
413 uint32 space, uint32 colour, uint32 acaps)
415 unsigned r, g, b, rr, gg, bb, y, t, u;
416 uint32 best_colour = 0, best_space; int best_error;
424 /* Check the colour space. If it's one that the terminal can handle, then
425 * return the colour unchanged. Otherwise, extract the channel components
431 /* No colour wanted at all. There's nothing to do here. */
433 *space_out = TTCSPC_NONE; *colour_out = 0;
437 /* One-bit-per-channel colour.
439 * There's no standardized mapping for these; indeed, they're commonly
440 * configurable by users. Since there are also `bright' versions of
441 * each, it's probably not right to just map zero to zero and one to
445 if (colour >= 8) goto inval;
446 if (acaps&(TTACF_1BPC | TTACF_1BPCBR))
447 { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
449 #define C1BPC_FULL 0xcc
450 #define C1BPC_CVT(col, bit) \
451 (C1BPC_FULL&((((col)&(1 << (bit))) << (8 - (bit))) - 1))
452 r = C1BPC_CVT(colour, 0);
453 g = C1BPC_CVT(colour, 1);
454 b = C1BPC_CVT(colour, 2);
458 /* One-bit-per-channel colour, with global brightness. Again, there's
459 * no standardized mapping. Apply a boost across all three channels.
462 if (colour >= 16) goto inval;
463 if (!(colour&TT1BPC_BRI) && (acaps&(TTACF_1BPC | TTACF_1BPCBR)))
464 { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
465 else if (acaps&TTACF_1BPCBR)
466 { *space_out = TTCSPC_1BPCBR; *colour_out = colour; return; }
468 #define C1BPC_BRIGHT 0x33
469 r = C1BPC_CVT(colour, 0) + C1BPC_BRIGHT;
470 g = C1BPC_CVT(colour, 1) + C1BPC_BRIGHT;
471 b = C1BPC_CVT(colour, 2) + C1BPC_BRIGHT;
476 /* Four-levels-per-channel colour. These are part of an `indexed'
477 * colour space which is theoretically controlled by applications but
478 * (a) that's rare, and (b) worrying about that won't do us any good.
480 * Each channel has four levels, but they're not assigned linearly.
483 if (colour >= 64) goto inval;
484 if (acaps&TTACF_4LPC)
485 { *space_out = TTCSPC_4LPC; *colour_out = colour; return; }
491 #define C4LPC_CVT(ch) (t = (ch), t == 0 ? C4LPC_L0 : \
492 t == 1 ? C4LPC_L1 : \
493 t == 2 ? C4LPC_L2 : \
495 r = C4LPC_CVT(TTCOL_2BR(colour));
496 g = C4LPC_CVT(TTCOL_2BG(colour));
497 b = C4LPC_CVT(TTCOL_2BB(colour));
502 /* Eight-levels greyscale. Again, these are part of an `indexed'
503 * colour space which is under application control, and, again, the
504 * levels aren't linear, but they're not far off linear.
506 if (colour >= 8) goto inval;
507 if (acaps&TTACF_8LGS)
508 { *space_out = TTCSPC_8LGS; *colour_out = colour; return; }
510 r = g = b = 255*(colour ? colour + 3 : 2)/11;
514 /* Six-levels-per-channel colour. Again, `indexed' colour space under
515 * application control. This time the mapping is essentially liner.
518 if (colour >= 216) goto inval;
519 if (acaps&TTACF_6LPC)
520 { *space_out = TTCSPC_6LPC; *colour_out = colour; return; }
522 #define C6LPC_CVT(ch) (t = (ch), t ? (40*t + 55) : 0)
523 r = C6LPC_CVT(TTCOL_6LR(colour));
524 g = C6LPC_CVT(TTCOL_6LG(colour));
525 b = C6LPC_CVT(TTCOL_6LB(colour));
530 /* Twenty-four-levels greyscale. Same story. */
532 if (colour >= 24) goto inval;
533 if (acaps&TTACF_24LGS)
534 { *space_out = TTCSPC_24LGS; *colour_out = colour; return; }
536 r = g = b = 10*colour + 8;
540 /* Eight-bits-per-channel colour. No conversion to apply here. */
542 if (colour >= 0x01000000) goto inval;
543 if (acaps&TTACF_8BPC)
544 { *space_out = TTCSPC_8BPC; *colour_out = colour; return; }
546 r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
555 /* We didn't get an exact match, so we'll have to make do with what we've
558 best_error = -1; best_space = TTCSPC_NONE; best_colour = 0;
559 D( fprintf(stderr, "\n;; APPROX space %u, colour 0x%lx = %u/%u/%u\n",
560 space, (unsigned long)colour, r, g, b); )
562 /* Approximate colour weightings for human colour vision. */
566 #define TOTWT (RWT + GWT + BWT)
568 /* Determine the optimal grey approximation for this colour. */
569 y = (RWT*r + GWT*g + BWT*b + TOTWT/2)/TOTWT;
571 #define TRY_APPROX(rr, gg, bb, spc, clr) do { \
572 /* If the approximation (RR, GG, BB) is closer to the current best \
573 * then accept SPC and CLR as the new best space/colour option. \
576 int _r_err = (rr) - r, _g_err = (gg) - g, _b_err = (bb) - b; \
579 if (_r_err < 0) _r_err = -_r_err; \
580 if (_g_err < 0) _g_err = -_g_err; \
581 if (_b_err < 0) _b_err = -_b_err; \
583 _err = RWT*_r_err + GWT*_g_err + BWT*_b_err; \
585 ";; candidate space %u, colour 0x%lx = %u/%u/%u; " \
587 (spc), (unsigned long)(clr), (rr), (gg), (bb), _err); ) \
588 if (best_error < 0 || _err < best_error) { \
589 best_error = _err; best_space = (spc); best_colour = (clr); \
590 D( fprintf(stderr, ";;\tNEW BEST APPROXIMATION\n"); ) \
594 if (!(acaps&(TTACF_4LPC | TTACF_6LPC | TTACF_8BPC))) {
595 /* The one-bit-per-channel colours are very variable, but there's little
596 * choice, so we'll have to try. We assume the same mapping as on the
600 if (acaps&(TTACF_1BPC | TTACF_1BPCBR)) {
601 /* One-bit-per-channel colour. */
603 #define C1BPC_APPROX(cc, c, bit) do { \
604 if ((c) <= C1BPC_FULL/2) (cc) = 0; \
605 else { (cc) = C1BPC_FULL; t |= (bit); } \
608 C1BPC_APPROX(rr, r, TT1BPC_RED);
609 C1BPC_APPROX(gg, g, TT1BPC_GRN);
610 C1BPC_APPROX(bb, b, TT1BPC_BLU);
612 TRY_APPROX(rr, gg, bb, TTCSPC_1BPC, t);
615 if (acaps&TTACF_1BPCBR) {
616 /* One-bit-per-channel colour, with global brightness. */
618 #define C1BPCBR_APPROX(cc, c, bit) do { \
619 if ((c) <= C1BPC_FULL/2 + C1BPC_BRIGHT) (cc) = C1BPC_BRIGHT; \
620 else { (cc) = 255; t |= (bit); } \
623 C1BPCBR_APPROX(rr, r, TT1BPC_RED);
624 C1BPCBR_APPROX(gg, g, TT1BPC_GRN);
625 C1BPCBR_APPROX(bb, b, TT1BPC_BLU);
626 #undef C1BPCBR_APPROX
627 TRY_APPROX(rr, gg, bb, TTCSPC_1BPCBR, t);
631 if (acaps&TTACF_4LPC) {
632 /* Four-levels-per-channel colour. */
634 #define C4LPC_APPROX(cc, c, sh) do { \
637 if (_c > (C4LPC_L2 + C4LPC_L3)/2) \
638 { (cc) = C4LPC_L3; t |= 3 << (sh); } \
639 else if (_c > (C4LPC_L1 + C4LPC_L2)/2) \
640 { (cc) = C4LPC_L2; t |= 2 << (sh); } \
641 else if (_c > (C4LPC_L0 + C4LPC_L1)/2) \
642 { (cc) = C4LPC_L1; t |= 1 << (sh); } \
647 C4LPC_APPROX(rr, r, 4);
648 C4LPC_APPROX(gg, g, 2);
649 C4LPC_APPROX(bb, b, 0);
651 TRY_APPROX(rr, gg, bb, TTCSPC_4LPC, t);
654 if (acaps&TTACF_8LGS) {
655 /* Eight-levels greyscale. */
658 if (u <= 2) { u = 2; t = 0; }
659 else if (u == 3) { u = 4; t = 1; }
660 else if (u == 11) { u = 10; t = 7; }
662 u = (255*u)/11; TRY_APPROX(u, u, u, TTCSPC_8LGS, t);
665 if (acaps&TTACF_6LPC) {
666 /* Six-levels-per-channel colour. */
668 #define C6LPC_APPROX(cc, c, f) do { \
671 if (_c < 36) (cc) = 0; \
672 else { u = (_c - 36)/40; t += (f)*u; (cc) = 40*u + 55; } \
675 C6LPC_APPROX(rr, r, 36);
676 C6LPC_APPROX(gg, g, 6);
677 C6LPC_APPROX(bb, b, 1);
679 TRY_APPROX(rr, gg, bb, TTCSPC_6LPC, t);
682 if (acaps&TTACF_24LGS) {
683 /* Twenty-four-levels greyscale. */
685 if (y < 3) { t = 0; u = 8; }
686 else if (y >= 243) { t = 23; u = 238; }
687 else { t = (y - 3)/10; u = 10*t + 8; }
688 TRY_APPROX(u, u, u, TTCSPC_24LGS, t);
691 if (acaps&TTACF_8BPC) {
692 /* Eight-bits-per-channel colour. */
695 D( fprintf(stderr, ";; accept exact 8bpc colour\n"); )
696 best_error = 0; best_space = TTCSPC_8BPC;
697 best_colour = TTCOL_MK8B(r, g, b);
702 *space_out = best_space; *colour_out = best_colour;
706 /* Invalid colour selection. Ignore this. */
707 *space_out = TTCSPC_NONE; *colour_out = 0;
732 /* --- @clamp_attr@ --- *
734 * Arguments: @struct tty_attr *a_out@ = selected attributes
735 * @const struct tty_attr *a@ = requested attributes
736 * @uint32 acaps@ = terminal's attribute capability mask
740 * Use: Select the closest approximation to the requested attributes
741 * which can be accommodated by the terminal.
744 static void clamp_attr(struct tty_attr *a_out,
745 const struct tty_attr *a, uint32 acaps)
747 uint32 ff = 0, f = a ? a->f : 0, t;
749 /* Line attributes. */
750 t = (f&TTAF_LNMASK) >> TTAF_LNSHIFT;
755 if (!acaps&TTACF_ULINE) t = TTLN_NONE;
758 if (acaps&TTACF_UULINE) ;
759 else if (acaps&TTACF_ULINE) t = TTLN_ULINE;
765 ff |= t << TTAF_LNSHIFT;
768 t = (f&TTAF_WTMASK) >> TTAF_WTSHIFT;
770 case TTWT_MED: break;
771 case TTWT_BOLD: if (!(acaps&TTACF_BOLD)) t = TTWT_MED; break;
772 case TTWT_DIM: if (!(acaps&TTACF_DIM)) t = TTWT_MED; break;
773 default: t = TTWT_MED; break;
775 ff |= t << TTAF_WTSHIFT;
777 /* Other text attributes. */
778 if (acaps&TTACF_STRIKE) ff |= f&TTAF_STRIKE;
779 if (acaps&TTACF_ITAL) ff |= f&TTAF_ITAL;
780 if (acaps&TTACF_INVV) ff |= f&TTAF_INVV;
782 /* Foreground and background colours. */
783 if (!(acaps&TTACF_FG))
786 clamp_colours(&t, &a_out->fg,
787 (f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a ? a->fg : 0,
789 ff |= t << TTAF_FGSPCSHIFT;
791 if (!(acaps&TTACF_BG))
794 clamp_colours(&t, &a_out->bg,
795 (f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a ? a->bg : 0,
797 ff |= t << TTAF_BGSPCSHIFT;
801 a_out->f = ff; a_out->_res0 = 0;
804 /* --- @stupid_repeat@ --- *
806 * Arguments: @struct tty *tty@ = control block pointer
807 * @const struct gprintf_ops *gops, void *go@ = output
809 * @int ch@ = character to write
810 * @unsigned n@ = number of copies
812 * Returns: Zero on success, %$-1$% on error.
814 * Use: Write @n@ copies of the character @ch@ to the terminal, the
815 * hard way. This function tries to be reasonably efficient, by
816 * transmitting buffers rather than exercising the output
817 * machinery for each individual character.
820 static int stupid_repeat(struct tty *tty,
821 const struct gprintf_ops *gops, void *go,
829 { memset(buf, ch, n); CHECK(gops->putm(go, buf, n)); }
831 memset(buf, ch, sizeof(buf));
834 CHECK(gops->putm(go, buf, nn));
835 n -= nn; if (!n) break;
844 /*----- Common machinery for %|termcap|% and %|terminfo|% -----------------*/
846 #if defined(HAVE_TERMINFO) || \
847 defined(HAVE_TERMCAP) || \
848 defined(HAVE_UNIBILIUM)
850 #if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
854 * The `termcap' and `terminfo' functions call a user-provided function to
855 * actually send control codes to the terminal. The bad news is that
856 * `termcap' doesn't provide any way to pass information to the output
857 * function beyond the character to be sent, and `terminfo' doesn't fix this
858 * mistake. So we must save the necessary context as global variables. For
859 * good measure, at least some implementations ignore errors from the output
860 * function, so we must keep track of them ourselves. More global variables.
862 * It's worse. Both libraries maintain significant global state of their
863 * own. And, at least with the `ncurses' implementation, the two share the
864 * same global state. The only thing to do is maintain a big interlock to
865 * make sure that only one is active at a time.
867 static const struct gprintf_ops *global_gops; /* output operations ... */
868 static void *global_gout; /* and context, for @caps_putch */
869 static char global_buf[BUFSZ]; /* a big output buffer */
870 static size_t global_len; /* length of buffer used */
871 static int global_err; /* error latch, zero if all ok */
872 static struct tty *global_lock = 0; /* interlock for global state */
874 /* --- @caps_claim@ --- *
878 * Returns: Zero on success, %$-1$% if already claimed.
880 * Use: Return %$-1$% if the interlock is already held. This is a
881 * function to call near the beginning of initializing a new
882 * control block, before the common global state gets
883 * clobbered. If initialization is successful, the caller is
884 * expected to actually store the control block pointer in
885 * @global_lock@ themselves.
888 static int caps_claim(void)
891 { debug("termcap/terminfo terminal already open"); return (-1); }
896 /* --- @caps_release@ --- *
898 * Arguments: @struct tty *tty@ = control block pointer for current lock
903 * Use: Release the lock.
906 static void caps_release(struct tty *tty)
907 { assert(global_lock == tty); global_lock = 0; }
909 /* --- @caps_prepout@ --- *
911 * Arguments: @struct tty *tty@ = control block pointer (ignored)
912 * @const struct gprintf_ops *gops, void *go@ = output
917 * Use: Prepare output to the given destination. Check that the
918 * output buffer is initially empty (i.e., that it was properly
919 * flushed last time), and make a note of the output
923 static void caps_prepout(struct tty *tty,
924 const struct gprintf_ops *gops, void *go)
925 { assert(!global_len); global_gops = gops; global_gout = go; }
927 /* --- @caps_putch@ --- *
929 * Arguments: @int ch@ = character to write
931 * Returns: Nonnegative on success, negative on failure. (But @tputs@
934 * Use: Output the character @ch@.
937 static int caps_putch(int ch)
941 /* By policy, we flush the buffer as soon as it becomes full, so there
942 * should always be space for at least one byte.
944 n = global_len; assert(n < BUFSZ);
945 global_buf[n++] = ch;
947 /* If the buffer is now full, then flush it. */
949 if (global_gops->putm(global_gout, global_buf, n)) global_err = -1;
954 global_len = n; return (0);
957 /* --- @caps_flush@ --- *
959 * Arguments: @struct tty *tty@ = control block pointer (ignored)
961 * Returns: Zero for success, %$-1$% if error pending.
963 * Use: Flush the output buffer to the backend. If an error is
964 * pending, clear it and return failure.
967 static int caps_flush(struct tty *tty)
972 if (global_gops->putm(global_gout, global_buf, global_len)) rc = -1;
975 global_err = 0; return (rc);
980 /* The list of interesting capabilities.
982 * We never actually need all of these: some are only needed if others are
983 * unavailable. But the list isn't too huge, so we'll live with it.
985 * The main thing is that each capability has three different names: the
986 * `full' name (corresponding to a `terminfo' variable name), the `terminfo'
987 * capability name, as used in terminal descriptions, and the two-character
988 * `termcap' name. Unibilium uses the long names, but to reduce typing, the
989 * `unibi_' prefix is omitted here. (Annoyingly, in `ncurses', at least, the
990 * `variable' names are `secretly' macros referencing a current state, and
991 * premature expansion causes misery, so I've left the leading underscores in
992 * place as a countermeasure.) Internally, we use the short `terminfo'
993 * names, since they generally express the most useful information in the
997 #define BASICCAPS(_bool, _int, _str) \
998 _str(_repeat_char, rep, rp) \
999 _str(_pad_char, pad, pc) _int(_padding_baud_rate, pb, pb) \
1000 _bool(_no_pad_char, npc, NP) _bool(_xon_xoff, xon, xo) \
1001 _bool(_move_insert_mode, mir, mi) _bool(_move_standout_mode, msgr, ms)
1003 #define ATTRCAPS(_bool, _int, _str) \
1004 _str(_exit_attribute_mode, sgr0, me) \
1005 _str(_enter_underline_mode, smul, us) \
1006 _str(_exit_underline_mode, rmul, ue) \
1007 _str(_enter_italics_mode, sitm, ZH) _str(_exit_italics_mode, ritm, ZR) \
1008 _str(_enter_bold_mode, bold, md) _str(_enter_dim_mode, dim, mh) \
1009 _str(_enter_reverse_mode, rev, mr) \
1010 COLOURCAPS(_bool, _int, _str)
1012 #define COLOURCAPS(_bool, _int, _str) \
1013 _str(_set_a_foreground, setaf, AF) _str(_set_a_background, setab, AB) \
1014 _str(_orig_pair, op, op) _int(_max_colors, colors, Co)
1016 #define MODECAPS(_bool, _int, _str) \
1017 _str(_carriage_return, cr, cr) _str(_newline, nel, nw) \
1018 _str(_enter_am_mode, smam, SA) _str(_exit_am_mode, rmam, RA) \
1019 _str(_enter_ca_mode, smcup, ti) _str(_exit_ca_mode, rmcup, te) \
1020 _str(_cursor_normal, cnorm, vs) _str(_cursor_invisible, civis, vi) \
1021 _str(_enter_insert_mode, smir, im) _str(_exit_insert_mode, rmir, ei) \
1022 _str(_enter_delete_mode, smdc, dm) _str(_exit_delete_mode, rmdc, ed)
1024 #define MOVECAPS(_bool, _int, _str) \
1025 _str(_cursor_home, home, ho) \
1026 _str(_cursor_address, cup, cm) \
1027 _str(_row_address, vpa, cv) _str(_column_address, hpa, ch) \
1028 _str(_cursor_left, cub1, le) _str(_parm_left_cursor, cub, LE) \
1029 _str(_cursor_right, cuf1, nd) _str(_parm_right_cursor, cuf, RI) \
1030 _str(_cursor_up, cuu1, up) _str(_parm_up_cursor, cuu, UP) \
1031 _str(_cursor_down, cud1, do) _str(_parm_down_cursor, cud, DO)
1033 #define SCROLLCAPS(_bool, _int, _str) \
1034 _str(_change_scroll_region, csr, cs) \
1035 _str(_scroll_forward, ind, sf) _str(_parm_index, indn, SF) \
1036 _str(_scroll_reverse, ri, sr) _str(_parm_rindex, rin, SR)
1038 #define ERASECAPS(_bool, _int, _str) \
1039 _str(_erase_chars, ech, ec) \
1040 _str(_clr_bol, el1, cb) _str(_clr_eol, el, ce) \
1041 _str(_clr_eos, ed, cd) _str(_clear_screen, clear, cl)
1043 #define INSDELCAPS(_bool, _int, _str) \
1044 _str(_insert_character, ich1, ic) _str(_parm_ich, ich, IC) \
1045 _str(_insert_padding, ip, ip) \
1046 _str(_insert_line, il1, al) _str(_parm_insert_line, il, AL) \
1047 _str(_delete_character, dch1, dc) _str(_parm_dch, dch, DC) \
1048 _str(_delete_line, dl1, dl) _str(_parm_delete_line, dl, DL)
1050 #define STORECAPS(_bool, _int, _str) \
1051 BASICCAPS(_bool, _int, _str) \
1052 ATTRCAPS(_bool, _int, _str) \
1053 MODECAPS(_bool, _int, _str) \
1054 MOVECAPS(_bool, _int, _str) \
1055 SCROLLCAPS(_bool, _int, _str) \
1056 ERASECAPS(_bool, _int, _str) \
1057 INSDELCAPS(_bool, _int, _str)
1059 #ifdef HAVE_UNIBILIUM
1060 # define UNIBI_(x) unibi##x
1062 # define UNIBI_(x) 0
1065 #define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
1066 /* Expand a capability triple into a group of three usable C arguments. If
1067 * Unibilium isn't available, then we can use nonsense for its cap index.
1070 /* Some other capabilities which we want to refer to during initialization,
1071 * but don't need to keep around.
1073 #define CAP_XMC CAPREF(_magic_cookie_glitch, xmc, sg)
1074 #define CAP_BCE CAPREF(_back_color_erase, bce, ut)
1075 #define CAP_XHPA CAPREF(_row_addr_glitch, xvpa, YD)
1076 #define CAP_XVPA CAPREF(_col_addr_glitch, xhpa, YA)
1077 #define CAP_AM CAPREF(_auto_right_margin, am, am)
1078 #define CAP_XENL CAPREF(_eat_newline_glitch, xenl, xn)
1079 #define CAP_HT CAPREF(_lines, lines, li)
1080 #define CAP_WD CAPREF(_columns, cols, co)
1082 /* Additional operations required of terminal backends which make use of the
1083 * common capability machinery.
1085 struct tty_capopslots {
1087 /* Retrieving capabilities. */
1088 int (*boolcap)(struct tty */*tty*/,
1089 int /*uix*/, const char */*info*/, const char */*cap*/);
1090 int (*intcap)(struct tty */*tty*/,
1091 int /*uix*/, const char */*info*/, const char */*cap*/);
1092 const char *(*strcap)(struct tty */*tty*/,
1093 int /*uix*/, const char */*info*/,
1094 const char */*cap*/);
1096 /* Preparing and completing output. */
1097 void (*prepout)(struct tty */*tty*/,
1098 const struct gprintf_ops */*gops*/, void */*go*/);
1099 int (*flush)(struct tty */*tty*/);
1101 /* Writing capabilities with various kinds of arguments. */
1102 int (*put0)(struct tty */*tty*/, unsigned /*npad*/, const char */*cap*/);
1103 int (*put1i)(struct tty */*tty*/,
1104 unsigned /*npad*/, const char */*cap*/, int /*i0*/);
1105 int (*put2i)(struct tty */*tty*/,
1107 const char */*cap*/, int /*i0*/, int /*i1*/);
1109 /* Determine the cost of a capability. */
1110 size_t (*cost)(struct tty */*tty*/,
1111 unsigned /*npad*/, const char */*cap*/, int /*i0*/);
1113 #define TTY_CAPOPSPFX TTY_BASEOPSPFX; struct tty_capopslots cap
1114 struct tty_capops { TTY_CAPOPSPFX; };
1115 #define TTY_CAPOPSUSFX struct tty_capops cap; TTY_BASEOPSUXFX
1116 union tty_capopsu { TTY_CAPOPSUSFX; };
1118 /* An extension of the control block to track the above capabilities. */
1119 struct tty_capslots {
1120 #define DEF_BOOLCAP(uix, info, cap) unsigned info : 1;
1121 #define DEF_INTCAP(uix, info, cap) int info;
1122 #define DEF_STRCAP(uix, info, cap) const char *info;
1123 STORECAPS(DEF_BOOLCAP, DEF_INTCAP, DEF_STRCAP)
1127 #define LEN_BOOLCAP(uix, info, cap)
1128 #define LEN_INTCAP(uix, info, cap)
1129 #define LEN_STRCAP(uix, info, cap) unsigned info##_cost;
1130 ATTRCAPS(LEN_BOOLCAP, LEN_INTCAP, LEN_STRCAP)
1135 #define TTY_CAPSPFX \
1137 struct tty_capslots cap
1138 struct tty_caps { TTY_CAPSPFX; };
1139 #define TTY_CAPSUSFX \
1140 struct tty_caps cap; \
1142 union tty_capsu { TTY_CAPSUSFX; };
1144 /* ---- @init_caps@ --- *
1146 * Arguments: @struct tty *tty@ = control block pointer
1150 * Use: Populate the capabilities in the terminal control block, and
1151 * advertise the results to the public part. Set @ht@ and @wd@
1152 * from the terminal description if they've not been set
1156 static void init_caps(struct tty_caps *t)
1158 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1161 t->tty.acaps = t->tty.ocaps = 0;
1162 t->tty.st.modes = 0; t->tty.st.attr.f = 0;
1163 t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
1165 /* Inhale all of the interesting terminal capabilities. */
1166 #define GETBOOL(var, info, cap_) \
1167 t->cap.info = ops->cap.boolcap(&t->tty, CAPREF(var, info, cap_));
1168 #define GETINT(var, info, cap_) \
1169 t->cap.info = ops->cap.intcap(&t->tty, CAPREF(var, info, cap_));
1170 #define GETSTR(var, info, cap_) \
1171 t->cap.info = ops->cap.strcap(&t->tty, CAPREF(var, info, cap_));
1172 STORECAPS(GETBOOL, GETINT, GETSTR)
1177 #define COST_BOOLCAP(uix, info, cap_)
1178 #define COST_INTCAP(uix, info, cap_)
1179 #define COST_STRCAP(uix, info, cap_) \
1180 t->cap.info##_cost = \
1181 ops->cap.cost(&t->tty, 1, t->cap.info, t->cap.colors - 1);
1182 ATTRCAPS(COST_BOOLCAP, COST_INTCAP, COST_STRCAP)
1187 #define CLEAR_BOOL(uix, info, cap_) t->cap.info = 0;
1188 #define CLEAR_INT(uix, info, cap_) t->cap.info = 0;
1189 #define CLEAR_STR(uix, info, cap_) t->cap.info = 0;
1190 #define CLEARCAPS(caplist) \
1191 do { caplist(CLEAR_BOOL, CLEAR_INT, CLEAR_STR) } while (0)
1193 /* Basic capabilities. */
1194 if (!t->cap.cr) t->cap.cr = "\r";
1195 if (!t->cap.nel) t->cap.nel = "\r\n";
1197 /* Attribute capabilities. */
1198 if (ops->cap.intcap(&t->tty, CAP_XMC) > 0)
1199 CLEARCAPS(ATTRCAPS);
1201 if (t->cap.smul && (t->cap.rmul || t->cap.sgr0))
1202 t->tty.acaps |= TTACF_ULINE;
1203 if (t->cap.bold && t->cap.sgr0)
1204 t->tty.acaps |= TTACF_BOLD;
1205 if (t->cap.dim && t->cap.sgr0)
1206 t->tty.acaps |= TTACF_DIM;
1207 if (t->cap.sitm && (t->cap.ritm || t->cap.sgr0))
1208 t->tty.acaps |= TTACF_ITAL;
1209 if (t->cap.rev && t->cap.sgr0)
1210 t->tty.acaps |= TTACF_INVV;
1212 if ((t->cap.setaf || t->cap.setab) && (t->cap.op || t->cap.sgr0)) {
1213 if (t->cap.setaf) t->tty.acaps |= TTACF_FG;
1214 if (t->cap.setab) t->tty.acaps |= TTACF_BG;
1215 t->tty.acaps |= TTACF_1BPC;
1216 if (t->cap.colors >= 16777216)
1217 t->tty.acaps |= TTACF_1BPC | TTACF_8BPC;
1218 else if (t->cap.colors >= 256)
1219 t->tty.acaps |= TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
1220 else if (t->cap.colors == 88)
1221 t->tty.acaps |= TTACF_1BPCBR | TTACF_4LPC | TTACF_8LGS;
1222 else if (t->cap.colors >= 16)
1223 t->tty.acaps |= TTACF_1BPCBR;
1224 if (ops->cap.boolcap(&t->tty, CAP_BCE)) t->tty.ocaps |= TTCF_BGER;
1225 env_colour_caps(&t->tty.acaps, 0);
1229 /* Motion capabilities. */
1230 if (ops->cap.boolcap(&t->tty, CAP_XHPA)) t->cap.hpa = 0;
1231 if (ops->cap.boolcap(&t->tty, CAP_XVPA)) t->cap.vpa = 0;
1232 if (!t->cap.cub1) t->cap.cub1 = "\b";
1233 if ((t->cap.cuf || t->cap.cuf1) &&
1234 (t->cap.cuu || t->cap.cuu1) &&
1235 (t->cap.cud || t->cap.cud1)) {
1236 t->tty.ocaps |= TTCF_RELMV;
1237 if (t->cap.vpa) t->tty.ocaps |= TTCF_ABSMV | TTCF_MIXMV;
1238 else if (t->cap.home) t->tty.ocaps |= TTCF_ABSMV;
1239 } else if (t->cap.cup ||
1240 (t->cap.hpa && t->cap.vpa) ||
1241 (t->cap.home && (t->cap.cuf || t->cap.cuf1)))
1242 t->tty.ocaps |= TTCF_ABSMV;
1244 /* Mode capabilities. */
1245 if (t->cap.smam && t->cap.rmam) t->tty.ocaps |= TTMF_AUTOM;
1246 if (t->cap.smcup && t->cap.rmcup) t->tty.ocaps |= TTMF_FSCRN;
1247 if (t->cap.smir && t->cap.rmir) t->tty.ocaps |= TTMF_INS;
1248 if (t->cap.smdc && t->cap.rmdc) t->tty.ocaps |= TTMF_DEL;
1249 if (t->cap.cnorm && t->cap.civis)
1250 { t->tty.ocaps |= TTMF_CVIS; t->tty.st.modes |= TTMF_CVIS; }
1251 if (ops->cap.boolcap(&t->tty, CAP_AM)) {
1252 t->tty.st.modes |= TTMF_AUTOM;
1253 if (ops->cap.boolcap(&t->tty, CAP_XENL)) t->tty.ocaps |= TTCF_MMARG;
1257 if (t->cap.ech) t->tty.ocaps |= TTCF_ERCH;
1258 if (t->cap.el1) t->tty.ocaps |= TTCF_ERBOL;
1259 if (t->cap.el) t->tty.ocaps |= TTCF_EREOL;
1260 if (t->cap.ed) t->tty.ocaps |= TTCF_EREOD;
1261 if (t->cap.clear || (t->cap.ed && t->cap.home)) t->tty.ocaps |= TTCF_ERDSP;
1263 /* Insertion and deletion. */
1264 if (t->cap.ich || t->cap.ich1) t->tty.ocaps |= TTCF_INSCH;
1265 if (t->cap.il || t->cap.il1) t->tty.ocaps |= TTCF_INSLN;
1266 if (t->cap.dch || t->cap.dch1) t->tty.ocaps |= TTCF_DELCH;
1267 if (t->cap.dl || t->cap.dl1) t->tty.ocaps |= TTCF_DELLN;
1271 { wd = ops->cap.intcap(&t->tty, CAP_WD); if (wd > 0) t->tty.wd = wd; }
1273 { ht = ops->cap.intcap(&t->tty, CAP_HT); if (ht > 0) t->tty.ht = ht; }
1281 /* --- @caps_padchars@ --- *
1283 * Arguments: @struct tty *tty@ = extended control block pointer
1284 * @unsigned delay@ = tenths of milliseconds required
1285 * @unsigned f@ = flags
1287 * Returns: The number of padding characters to send.
1289 * Use: Determine the number of padding characters to send to achieve
1290 * a delay of a given duration. If @CPF_FORCE@ is set, then
1291 * ignore the @pb@ and @xon@ capabilities.
1294 #define CPF_FORCE 1u
1295 static size_t caps_padchars(struct tty *tty, unsigned delay, unsigned f)
1297 struct tty_caps *t = (struct tty_caps *)tty;
1299 if (!(f&CPF_FORCE) && (t->tty.baud < t->cap.pb || t->cap.xon)) {
1300 /* We're not forced to pad, and the baud rate is sufficiently low or we
1301 * have flow control, then there's nothing to do.
1306 /* We're transmitting at R b/s, and we want to send N B of data so that
1307 * this takes D/10000 s. We're likely sending either seven bits with
1308 * parity or eight bits without, plus one stop bit, per character, so we
1309 * must send 9 N b, which will take 9 N/R s = D/10000 s. Rearranging
1314 * and we should round upwards.
1317 return ((unsigned long)t->tty.baud*delay + 89999/90000);
1321 #if defined(HAVE_TERMCAP) || defined(HAVE_TERMINFO)
1323 /* --- @caps_cost@ --- *
1325 * Arguments: @struct tty *tty@ = control block pointer
1326 * @unsigned npad@ = number of lines affected
1327 * @const char *ctrl@ = formatted control string
1329 * Returns: A linear `cost' for sending the capability.
1331 * Use: Determines the cost for a `termcap' or `terminfo' capability
1332 * by parsing a @tputs@-format string.
1335 static unsigned scan_delay(const char **p_inout, unsigned npad)
1337 const char *p = *p_inout;
1343 while (ISDIGIT(*p)) t = 10*t + (*p++ - '0');
1349 while (ISDIGIT(*p)) p++;
1354 case '*': p++; f |= f_padmul; break;
1355 case '/': p++; break;
1359 if (f&f_padmul) t *= npad;
1360 *p_inout = p; return (npad);
1365 static size_t caps_cost(struct tty *tty, unsigned npad, const char *ctrl)
1373 /* The string starts with a number, so it's a `termcap'-style string with
1374 * a leading millisecond count.
1377 t = scan_delay(&p, npad);
1380 /* No initial number. Search for `terminfo'-style %|$<NNN.N[*|/]|%
1386 if (*p != '$' || p[1] != '<' || (!ISDIGIT(p[2] && p[2] != '>')))
1389 p += 2; t += scan_delay(&p, npad);
1395 return (n + caps_padchars(tty, t, CPF_FORCE));
1400 /* Macros for formatting capabilities. The @...V@ macros evaluate the cap
1401 * name, while the unmarked macros interpret it as a slot name.
1403 #define PUT0V(npad, cap_) \
1404 CHECK(ops->cap.put0(&t->tty, (npad), (cap_)))
1405 #define PUT1IV(npad, cap_, i0) \
1406 CHECK(ops->cap.put1i(&t->tty, (npad), (cap_), (i0)))
1407 #define PUT2IV(npad, cap_, i0, i1) \
1408 CHECK(ops->cap.put2i(&t->tty, (npad), (cap_), (i0), (i1)))
1410 #define PUT0(npad, name) PUT0V(npad, t->cap.name)
1411 #define PUT1I(npad, name, i0) PUT1IV(npad, t->cap.name, i0)
1412 #define PUT2I(npad, name, i0, i1) PUT2IV(npad, t->cap.name, i0, i1)
1414 /* --- @caps_iterate@ --- *
1416 * Arguments: @struct tty_caps *t@ = extended control block pointer
1417 * @const char *cap1, *capn@ = capability strings for single and
1418 * multiple operations
1419 * @unsigned f@ = flags
1420 * @unsigned npad@ = number of lines affected
1421 * @unsigned n@ = number of operations to do
1423 * Returns: Zero on success, %$-1$% on error.
1425 * Use: Sends control codes to do some operation @n@ times.
1427 * The capability string @cap1@ should do the operation once,
1428 * while @capn@ should do it some number of times as requested
1429 * by an argument. The single-operation cap is likely better
1430 * for a single use. Either or both capabilities might be
1434 #define CIF_PADMUL 1u
1435 static int caps_iterate(struct tty_caps *t,
1436 const char *cap1, const char *capn,
1437 unsigned f, unsigned npad, unsigned n)
1439 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1443 if (cap1 && (n == 1 || !capn))
1444 while (n--) PUT0V(npad, cap1);
1446 max = npad && (f&CIF_PADMUL) ? INT_MAX/npad : INT_MAX;
1448 nn = n; if (nn > max) nn = max;
1449 PUT1IV(npad, capn, nn);
1458 /* --- @caps_setcolour@ --- *
1460 * Arguments: @struct tty_caps *t@ = extended control block pointer
1461 * @const char *cap@ = capability string to apply (typically
1462 * %|setaf|% or %|setab|%)
1463 * @uint32 spc, clr@ = the colour space and number
1465 * Returns: Zero on success, %$-1$% on error.
1467 * Use: Emit the correct control string to set the foreground or
1468 * background colour as indicated.
1471 static int caps_setcolour(struct tty_caps *t,
1472 const char *cap, uint32 spc, uint32 clr)
1474 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1478 case TTCSPC_1BPC: case TTCSPC_1BPCBR: PUT1IV(0, cap, clr); break;
1479 case TTCSPC_4LPC: case TTCSPC_6LPC: PUT1IV(0, cap, clr + 16); break;
1480 case TTCSPC_8LGS: PUT1IV(0, cap, clr + 80); break;
1481 case TTCSPC_24LGS: PUT1IV(0, cap, clr + 232); break;
1484 /* There's an unfortunate ambiguity in the %|setaf|% conventions. The
1485 * first eight colours should be dark shades of blue, but in fact
1486 * they're interpreted as the one-bit-per-channel basic colours by
1487 * common `terminfo' settings. Notice and kludge by adding a little
1488 * red. This will tinge the colour magenta, but all such colours are
1489 * so dark as to be effectively black anyway, so I doubt that this will
1492 if (spc == TTCSPC_8BPC && clr < 8) clr += 65536;
1493 PUT1IV(0, cap, clr); break;
1495 /* case TTCSPC_NONE: */
1496 default: rc = -1; goto end;
1503 /* --- @caps_setattr_internal@ --- *
1505 * Arguments: @struct tty_caps *t@ = extended control block pointer
1506 * @const struct tty_attr *a@ = attribute to set, already
1509 * Returns: Zero on success, %$-1$% on error.
1511 * Use: Internal version of @caps_setattr@. Assumes that @prepout@
1512 * has already been called, and that @flush@ will be called
1515 * This is split out from @caps_setattr@ because it's used
1516 * (e.g., by @caps_move@) to restore the capabilities after
1517 * moving the cursor on a terminal which doesn't advertise
1521 static int caps_setattr_internal(struct tty_caps *t,
1522 const struct tty_attr *a)
1524 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1529 /* Work out what, if anything, needs doing. */
1530 diff = a->f ^ t->tty.st.attr.f;
1532 /* Form a basic strategy.
1534 * In the general case, we're applying some attributes (say bold face) and
1535 * cancelling others (say italics). We likely have a big club for the
1536 * latter in the form of the %|sgr0|% capability, which cancels all
1537 * attributes and is likely fairly cheap. On the other hand, some -- but,
1538 * infuriatingly, not all -- attributes have a cap string for cancelling
1539 * them, which may or may not be present.
1541 * Hence, if we're holding over a lot of attributes from the previous
1542 * state, and cancelling just a small number, then it's likely better to
1543 * just cancel the individual attributes which need it, because otherwise
1544 * we'd have to reapply all of the ones we didn't actually mean to change.
1546 * Add up the costs of each approach and use the cheaper one. Of course,
1547 * if we're forced into one or the other, then we have no choice.
1549 * One point worth noting is that we don't make use of the general %|sgr|%
1550 * capability here. For one thing, %|sgr|% takes nine arguments -- so it
1551 * can't be used from `termcap'. But, more importantly, we don't know, in
1552 * any particular case, what it does to other attributes.
1556 /* First, add up the costs of cancelling the individual attributes which
1557 * are no longer wanted. If we find that we can't cancel one, then skip
1561 #define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
1562 #define ADDCOST(cap_) do { \
1563 if (t->cap.cap_) c += t->cap.cap_##_cost; \
1566 if (CLEARP(TTAF_LNMASK)) ADDCOST(rmul);
1567 if (CLEARP(TTAF_WTMASK)) goto sgr0;
1568 if (diff&~a->f&TTAF_INVV) goto sgr0;
1569 if (diff&~a->f&TTAF_ITAL) ADDCOST(ritm);
1570 if (CLEARP(TTAF_FGSPCMASK) || CLEARP(TTAF_BGSPCMASK)) ADDCOST(op);
1574 /* Now, add up the costs of reapplying all of the attributes which aren't
1575 * supposed to change. (Attributes which are changing don't count here
1576 * because we'll have to fiddle with them anyway.)
1579 if (!(diff&TTAF_LNMASK))
1580 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1581 case TTLN_ULINE: z += t->cap.smul_cost; break;
1583 if (!(diff&TTAF_WTMASK))
1584 switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1585 case TTWT_BOLD: z += t->cap.bold_cost; break;
1586 case TTWT_DIM: z += t->cap.dim_cost; break;
1589 if (m&TTAF_INVV) z += t->cap.rev_cost;
1590 if (m&TTAF_ITAL) z += t->cap.sitm_cost;
1591 #define COLOURCOST(G, g, cap_) do { \
1592 if (!(diff&TTAF_##G##SPCMASK) && \
1593 (a->f&TTAF_##G##SPCMASK) && a->g == t->tty.st.attr.g) \
1594 z += t->cap.cap_##_cost; \
1596 COLOURCOST(FG, fg, setaf);
1597 COLOURCOST(BG, bg, setab);
1600 if (z + t->cap.sgr0_cost < c) {
1602 /* We've decided to clear everything and start over. */
1604 PUT0(0, sgr0); diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
1609 if (diff&TTAF_LNMASK)
1610 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1611 case TTLN_NONE: PUT0(0, rmul); break;
1612 case TTLN_ULINE: PUT0(0, smul); break;
1613 /* case TTLN_UULINE: */
1614 /* case TTLN_STRIKE: */
1615 default: rc = -1; goto end;
1619 if (diff&TTAF_WTMASK)
1620 switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1621 /* case TTWT_MED: */
1622 case TTWT_BOLD: PUT0(0, bold); break;
1623 case TTWT_DIM: PUT0(0, dim); break;
1624 default: rc = -1; goto end;
1627 /* Other text effects. */
1628 if (diff&a->f&TTAF_INVV) PUT0(0, rev);
1629 if (diff&TTAF_ITAL) {
1630 if (a->f&TTAF_ITAL) PUT0(0, sitm);
1635 if (((diff&TTAF_FGSPCMASK) && !(a->f&TTAF_FGSPCMASK)) ||
1636 ((diff&TTAF_BGSPCMASK) && !(a->f&TTAF_BGSPCMASK))) {
1637 /* There's no capability string for resetting just the foreground
1638 * or background colours to the defaults, so deal with that here.
1642 diff = (diff&~(TTAF_FGSPCMASK | TTAF_BGSPCMASK)) |
1643 (a->f&(TTAF_FGSPCMASK | TTAF_BGSPCMASK));
1645 if ((diff&TTAF_FGSPCMASK) || a->fg != t->tty.st.attr.fg)
1646 CHECK(caps_setcolour(t, t->cap.setaf,
1647 (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
1648 if ((diff&TTAF_BGSPCMASK) || a->bg != t->tty.st.attr.bg)
1649 CHECK(caps_setcolour(t, t->cap.setab,
1650 (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
1655 t->tty.st.attr = *a; return (rc);
1660 /* --- @caps_setattr@ --- *
1662 * Arguments: @struct tty *tty@ = control block pointer
1663 * @const struct gprintf_ops *gops, void *go@ = output
1665 * @const struct tty_attr *a@ = attribute to set, already
1668 * Returns: Zero on success, %$-1$% on error.
1670 * Use: Arrange to display future characters with the display
1671 * attributes indicated by @a@.
1674 static int caps_setattr(struct tty *tty,
1675 const struct gprintf_ops *gops, void *go,
1676 const struct tty_attr *a)
1678 struct tty_caps *t = (struct tty_caps *)tty;
1679 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1682 /* Hand off to @caps_setattr_internal@. */
1683 ops->cap.prepout(&t->tty, gops, go);
1684 rc = caps_setattr_internal(t, a);
1685 if (ops->cap.flush(&t->tty)) rc = -1;
1689 /* --- @caps_setmodes@ --- *
1691 * Arguments: @struct tty *tty@ = control block pointer
1692 * @const struct gprintf_ops *gops, void *go@ = output
1694 * @uint32 modes_bic, modes_xor@ = masks to apply to the modes
1697 * Returns: Zero on success, %$-1$% on error.
1699 * Use: Set the requested terminal modes.
1702 static int caps_setmodes(struct tty *tty,
1703 const struct gprintf_ops *gops, void *go,
1704 uint32 modes_bic, uint32 modes_xor)
1706 struct tty_caps *t = (struct tty_caps *)tty;
1707 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1712 /* Figure out which modes to set. */
1713 modes = (t->tty.st.modes&~modes_bic) ^ modes_xor;
1714 diff = modes ^ t->tty.st.modes;
1716 /* Prepare output. */
1717 ops->cap.prepout(&t->tty, gops, go);
1719 /* Automatic margins. */
1720 if (diff&TTMF_AUTOM) {
1721 if (modes&TTMF_AUTOM) PUT0(0, smam);
1726 if (diff&TTMF_FSCRN) {
1727 if (modes&TTMF_FSCRN) PUT0(0, smcup);
1728 else PUT0(0, rmcup);
1731 /* Cursor visibility. */
1732 if (diff&TTMF_CVIS) {
1733 if (modes&TTMF_CVIS) PUT0(0, civis);
1734 else PUT0(0, cnorm);
1738 if (diff&TTMF_INS) {
1739 cap = modes&TTMF_INS ? t->cap.smir : t->cap.rmir;
1740 if (cap) PUT0V(0, cap);
1741 else if (!t->cap.ich) { rc = -1; goto end; }
1744 /* Delete characters. */
1745 if (diff&TTMF_DEL) {
1746 cap = modes&TTMF_DEL ? t->cap.smdc : t->cap.rmdc;
1747 if (cap) PUT0V(0, cap);
1748 else if (!t->cap.dch && !t->cap.dch1) { rc = -1; goto end; }
1754 if (ops->cap.flush(&t->tty)) rc = -1;
1755 t->tty.st.modes = modes; return (rc);
1758 /* --- @caps_move_relative@ --- *
1760 * Arguments: @struct tty_caps *t@ = extended control block pointer
1761 * @int delta@ = number of places to move
1762 * @const char *fw1, *fwn, *rv1, *rvn@ = capability strings for
1763 * to move by a single or multiple places, in the
1764 * forward (positive) or backwards (negative) direction
1766 * Returns: Zero on success, %$-1$% on error.
1768 * Use: Perform a relative motion, making the most of the supplied
1772 static int caps_move_relative(struct tty_caps *t,
1774 const char *fw1, const char *fwn,
1775 const char *rv1, const char *rvn)
1777 const char *mv1, *mvn;
1779 if (!delta) return (0);
1780 else if (delta > 0) { mv1 = fw1; mvn = fwn; }
1781 else { mv1 = rv1; mvn = rvn; delta = - delta; }
1782 return (caps_iterate(t, mv1, mvn, 0, 0, delta));
1785 /* --- @caps_move_absx@ --- *
1787 * Arguments: @struct tty_caps *t@ = extended control block pointer
1788 * @int x@ = column to move the cursor to
1790 * Returns: Zero on success, %$-1$% on error.
1792 * Use: Move the cursor to the given column in the current line.
1793 * This is possible even if we have only relative motion
1794 * because we can use the carriage return.
1797 static int caps_move_absx(struct tty_caps *t, int x)
1799 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1806 CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1813 /* --- @caps_move@ --- *
1815 * Arguments: @struct tty *tty@ = control block pointer
1816 * @const struct gprintf_ops *gops, void *go@ = output
1818 * @unsigned orig@ = origin
1819 * @int y, x@ = new cursor position
1821 * Returns: Zero on success, %$-1$% on error.
1823 * Use: Move the cursor.
1826 static int caps_move(struct tty *tty,
1827 const struct gprintf_ops *gops, void *go,
1828 unsigned orig, int y, int x)
1830 struct tty_caps *t = (struct tty_caps *)tty;
1831 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1832 struct tty_attr a, aa = TTY_ATTR_INIT;
1835 /* Check that the arguments are basically sensible. */
1836 if ((!(orig&TTOF_YCUR) && y < 0) || (!(orig&TTOF_XCUR) && x < 0))
1837 { rc = -1; goto end; }
1839 /* Prepare for output. */
1840 ops->cap.prepout(&t->tty, gops, go);
1842 /* If it's unsafe to move with insert mode on, then turn it off. */
1843 if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, rmir);
1845 /* If it's unsafe to move with attributes set then make a copy of them and
1849 if (!t->cap.msgr && a.f) {
1850 if (t->cap.sgr0) PUT0(0, sgr0);
1851 else CHECK(caps_setattr_internal(t, &aa));
1852 t->tty.st.attr.f = 0; t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
1855 /* Main dispatch. */
1858 /* Fully absolute movement.
1860 * Moving to the home position is likely easy and fast. Use general
1861 * cursor positioning if available. Otherwise, try to set the vertical
1862 * and horizontal positions separately. If all that fails, move to the
1863 * home position and use relative motion.
1866 if (t->cap.home && !x && !y)
1868 else if (t->cap.cup)
1869 PUT2I(1, cup, y, x);
1870 else if (t->cap.vpa) {
1872 CHECK(caps_move_absx(t, x));
1873 } else if (t->cap.home) {
1875 CHECK(caps_iterate(t, t->cap.cud1, t->cap.cud, 0, 1, y));
1876 CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1878 { rc = -1; goto end; }
1882 /* Fully relative movement. This is easy. */
1884 CHECK(caps_move_relative(t, y,
1885 t->cap.cud1, t->cap.cud,
1886 t->cap.cuu1, t->cap.cuu));
1887 CHECK(caps_move_relative(t, x,
1888 t->cap.cuf1, t->cap.cuf,
1889 t->cap.cub1, t->cap.cub));
1892 case TTOF_XHOME | TTOF_YCUR:
1893 /* Absolute horizontal movement, with relative vertical movement.
1895 * If we want to move to the start of the next line, this is a
1896 * `newline' operation. Otherwise, use two separate motions.
1899 if (x == 0 && y == 1)
1902 CHECK(caps_move_relative(t, y,
1903 t->cap.cud1, t->cap.cud,
1904 t->cap.cuu1, t->cap.cuu));
1905 CHECK(caps_move_absx(t, x));
1909 case TTOF_XCUR | TTOF_YHOME:
1910 /* Relative horizontal movement, with absolute vertical movement.
1912 * The only thing to try is two separate motions.
1916 CHECK(caps_move_relative(t, x,
1917 t->cap.cuf1, t->cap.cuf,
1918 t->cap.cub1, t->cap.cub));
1922 /* Anything else was an error. */
1928 /* If we turned off insert mode, we can turn it back on now. */
1929 if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, smir);
1931 /* And if we turned off any attributes, we can turn them back on. */
1932 if (!t->cap.msgr && a.f)
1933 CHECK(caps_setattr_internal(t, &a));
1938 if (ops->cap.flush(&t->tty)) rc = -1;
1942 /* --- @caps_repeat@ --- *
1944 * Arguments: @struct tty *tty@ = control block pointer
1945 * @const struct gprintf_ops *gops, void *go@ = output
1947 * @int ch@ = character to write
1948 * @unsigned n@ = number of copies
1950 * Returns: Zero on success, %$-1$% on error.
1952 * Use: Write @n@ copies of the character @ch@ to the terminal.
1955 static int caps_repeat(struct tty *tty,
1956 const struct gprintf_ops *gops, void *go,
1959 struct tty_caps *t = (struct tty_caps *)tty;
1960 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1964 /* Prepare for output. */
1965 ops->cap.prepout(&t->tty, gops, go);
1967 /* If there isn't a capability for this then do it the stupid way. */
1969 CHECK(stupid_repeat(tty, gops, go, ch, n));
1973 nn = n; if (nn > INT_MAX) nn = INT_MAX;
1974 PUT2I((nn + wd - 1)/wd, rep, ch, nn);
1982 if (ops->cap.flush(&t->tty)) rc = -1;
1986 /* --- @caps_erase@ --- *
1988 * Arguments: @struct tty *tty@ = control block pointer
1989 * @const struct gprintf_ops *gops, void *go@ = output
1991 * @unsigned f@ = flags
1993 * Returns: Zero on success, %$-1$% on error.
1995 * Use: Erase portions of the current line or the whole display.
1998 static int caps_erase(struct tty *tty,
1999 const struct gprintf_ops *gops, void *go,
2002 struct tty_caps *t = (struct tty_caps *)tty;
2003 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2006 /* Prepare for output. */
2007 ops->cap.prepout(&t->tty, gops, go);
2009 if (!(f&TTEF_DSP)) {
2010 /* Erase line. This is easy. */
2012 if (f&TTEF_BEGIN) PUT0(1, el1);
2013 if (f&TTEF_END) PUT0(1, el);
2015 /* Erase display. Note that `terminfo' lacks a capability for `erase
2016 * from start of display'.
2019 switch (f&(TTEF_BEGIN | TTEF_END)) {
2021 /* Nothing to do. */
2025 case TTEF_BEGIN | TTEF_END:
2026 /* Erase the whole display. If we have a `clear', then use it;
2027 * otherwise, synthesize it from `home' and `erase to end'.
2030 if (t->cap.clear) PUT0(t->tty.ht, clear);
2031 else { PUT0(1, home); PUT0(t->tty.ht, ed); }
2035 /* Erase to the end of the display. */
2037 PUT0(t->tty.ht, ed);
2041 /* Anything else is wrong. */
2051 if (ops->cap.flush(&t->tty)) rc = -1;
2055 /* --- @caps_erch@ --- *
2057 * Arguments: @struct tty *tty@ = control block pointer
2058 * @const struct gprintf_ops *gops, void *go@ = output
2060 * @unsigned n@ = number of characters to erase
2062 * Returns: Zero on success, %$-1$% on error.
2064 * Use: Erase a number of characters, starting from and including the
2065 * current cursor position.
2068 static int caps_erch(struct tty *tty,
2069 const struct gprintf_ops *gops, void *go,
2072 struct tty_caps *t = (struct tty_caps *)tty;
2073 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2076 /* Prepare for output. */
2077 ops->cap.prepout(&t->tty, gops, go);
2079 /* Produce the control sequence. */
2080 if (n) PUT1I(1, ech, n);
2085 if (ops->cap.flush(&t->tty)) rc = -1;
2089 /* --- @caps_ins@ --- *
2091 * Arguments: @struct tty *tty@ = control block pointer
2092 * @const struct gprintf_ops *gops, void *go@ = output
2094 * @unsigned f@ = flags
2095 * @unsigned n@ = number of items to insert
2097 * Returns: Zero on success, %$-1$% on error.
2099 * Use: Insert a number of blank characters or lines.
2102 static int caps_ins(struct tty *tty,
2103 const struct gprintf_ops *gops, void *go,
2104 unsigned f, unsigned n)
2106 struct tty_caps *t = (struct tty_caps *)tty;
2107 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2110 /* Prepare for output. */
2111 ops->cap.prepout(&t->tty, gops, go);
2113 /* Produce the control sequence. */
2115 CHECK(caps_iterate(t, t->cap.il1, t->cap.il, CIF_PADMUL, 1, n));
2117 CHECK(caps_iterate(t, t->cap.ich1, t->cap.ich, 0, 1, n));
2122 if (ops->cap.flush(&t->tty)) rc = -1;
2126 /* --- @caps_inch@ --- *
2128 * Arguments: @struct tty *tty@ = control block pointer
2129 * @const struct gprintf_ops *gops, void *go@ = output
2131 * @int ch@ = character to insert
2133 * Returns: Zero on success, %$-1$% on error.
2135 * Use: Insert a single character.
2138 static int caps_inch(struct tty *tty,
2139 const struct gprintf_ops *gops, void *go,
2142 struct tty_caps *t = (struct tty_caps *)tty;
2143 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2146 /* Prepare for output. */
2147 ops->cap.prepout(&t->tty, gops, go);
2149 /* If the terminal has an insert mode, but it's not turned on, then this
2150 * isn't going to work.
2152 if (t->cap.smir ? !(t->tty.st.modes&TTMF_INS) : !t->cap.ich)
2153 { rc = -1; goto end; }
2155 /* If there's an insert-one-character sequence, then send that. */
2156 if (t->cap.ich) PUT0(1, ich);
2158 /* Put the actual character. */
2159 CHECK(gops->putch(go, ch));
2161 /* Some terminals need post-insertion padding, which turns up here. */
2162 if (t->cap.ip) PUT0(1, ip);
2167 if (ops->cap.flush(&t->tty)) rc = -1;
2171 /* --- @caps_del@ --- *
2173 * Arguments: @struct tty *tty@ = control block pointer
2174 * @const struct gprintf_ops *gops, void *go@ = output
2176 * @unsigned f@ = flags
2177 * @unsigned n@ = number of items to delete
2179 * Returns: Zero on success, %$-1$% on error.
2181 * Use: Delete a number of characters or lines.
2184 static int caps_del(struct tty *tty,
2185 const struct gprintf_ops *gops, void *go,
2186 unsigned f, unsigned n)
2188 struct tty_caps *t = (struct tty_caps *)tty;
2189 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
2192 /* Prepare for output. */
2193 ops->cap.prepout(&t->tty, gops, go);
2195 /* Send the control sequence. */
2198 CHECK(caps_iterate(t, t->cap.dl1, t->cap.dl, CIF_PADMUL, 1, n));
2200 CHECK(caps_iterate(t, t->cap.dch1, t->cap.dch, 0, 1, n));
2206 if (ops->cap.flush(&t->tty)) rc = -1;
2217 /* The operation functions for terminal backends based on `terminfo'. */
2218 #define TTY_CAPOPS \
2219 caps_setattr, caps_setmodes, \
2220 caps_move, caps_repeat, \
2221 caps_erase, caps_erch, caps_ins, caps_inch, caps_del
2225 /*----- Termcap -----------------------------------------------------------*/
2229 /* So `termcap' is pretty bad, actually. It reads the terminal description
2230 * from a file into a caller-provided buffer without knowing how big the
2231 * buffer is. The @tgetstr@ function partially decodes a string capability
2232 * into another caller-provided buffer with no bounds checking.
2234 * And then there's the way that pieces of `termcap' communicate with each
2235 * other through global variables which the caller has to set for it.
2237 * Finally, while output can be redirected under application control, output
2238 * is strictly one-character-at-a-time, and there's no way to pass additional
2239 * context information to the character-output function, so we need yet more
2245 /* Additional state required by `termcap'. */
2246 struct tty_termcapslots {
2247 char capbuf[4096], *capcur; /* string caps, and cursor */
2249 struct tty_termcap { TTY_CAPSPFX; struct tty_termcapslots tc; };
2250 union tty_termcapu { struct tty_termcap tc; TTY_CAPSUSFX; };
2252 /* --- @termcap_boolcap@, @termcap_intcap@, @termcap_strcap@ --- *
2254 * Arguments: @struct tty *tty@ = control block pointer
2255 * @int uix, const char *info, const char *cap@ = names for the
2256 * requested capability (as Unibilium index, and
2257 * `terminfo' and `termcap' names
2259 * Returns: The requested capability value. On failure,
2260 * @termcap_boolcap@ returns false, @termcap_intcap@ returns
2261 * %$-1$%, and @termcap_strcap@ returns a null pointer.
2263 * Use: Return a requested boolean, integer, or string capability.
2266 static int termcap_boolcap(struct tty *tty,
2267 int uix, const char *info, const char *cap)
2271 p = tgetflag(cap); assert(p >= 0);
2275 static int termcap_intcap(struct tty *tty,
2276 int uix, const char *info, const char *cap)
2280 n = tgetnum(cap); assert(n >= -1);
2284 static const char *termcap_strcap(struct tty *tty,
2285 int uix, const char *info,
2288 struct tty_termcap *t = (struct tty_termcap *)tty;
2292 /* Try to detect overruns. This is gnarly because C and `termcap'. We're
2293 * not allowed to compare out-of-bounds pointers. The best I can manage is
2294 * to assume that @capcur@ is maintained within @capbuf@, and check that
2295 * the length of the new capability didn't push us over the limit. Things
2296 * are even harder because, e.g., `ncurses' doesn't actually make use of
2297 * our @capbuf@ at all.
2299 * (Of course, if it did, it's probably too late to expect anyhing good,
2300 * but maybe we can apply the emergency brakes before things get too bad.)
2302 n = t->tc.capcur - t->tc.capbuf;
2303 p = tgetstr(cap, &t->tc.capcur);
2304 assert(p != (const char *)-1);
2305 assert(!p || n + strlen(p) < sizeof(t->tc.capbuf));
2309 /* --- @termcap_put0@, @termcap_put1i@, @termcap_put2i@ --- *
2311 * Arguments: @struct tty *tty@ = control block pointer
2312 * @unsigned npad@ = number of lines affected, for padding
2313 * @const char *cap@ = capability string to write, or null
2314 * @int i0, i1@ = integer parameters
2316 * Returns: Zero on success, %$-1$% on error.
2318 * Use: Format a capability string and send it to the terminal.
2321 static int termcap_put0(struct tty *tty,
2322 unsigned npad, const char *cap)
2324 if (!cap) return (-1);
2325 return (tputs(cap, npad, caps_putch));
2328 static int termcap_put1i(struct tty *tty,
2329 unsigned npad, const char *cap, int i0)
2331 if (!cap) return (-1);
2332 return (tputs(tgoto(cap, -1, i0), npad, caps_putch) == OK ? 0 : -1);
2335 static int termcap_put2i(struct tty *tty,
2336 unsigned npad, const char *cap, int i0, int i1)
2338 if (!cap) return (-1);
2339 return (tputs(tgoto(cap, i1, i0), npad, caps_putch) == OK ? 0 : -1);
2342 /* --- @termcap_cost@ --- *
2344 * Arguments: @struct tty *tty@ = control block pointer
2345 * @unsigned npad@ = number of lines affected
2346 * @const char *cap@ = capability string to measure, or null
2347 * @int i0@ = argument
2349 * Returns: A linear cost for using the capability. This is meaningless
2353 static size_t termcap_cost(struct tty *tty,
2354 unsigned npad, const char *cap, int i0)
2355 { return (cap ? caps_cost(tty, npad, tgoto(cap, 0, i0)) : 0); }
2357 /* The `termcap' operations table. */
2358 static const union tty_capopsu termcap_ops = { {
2359 { caps_release, TTY_CAPOPS },
2360 { termcap_boolcap, termcap_intcap, termcap_strcap,
2361 caps_prepout, caps_flush,
2362 termcap_put0, termcap_put1i, termcap_put2i,
2366 /* --- @termcap_init@ --- *
2368 * Arguments: @FILE *fp@ = the output stream
2370 * Returns: A pointer to a fresh terminal control block, or null on
2373 * Use: Initialize a terminal for use through `termcap'.
2376 static struct tty *termcap_init(FILE *fp)
2378 union tty_termcapu *u = 0; struct tty *ret = 0;
2384 /* The `termcap' library makes use of global state, so we can only have one
2385 * at a time. Traditional `termcap' is actually less bad in this sense,
2386 * but the `ncurses' emulation is tangled up with its `terminfo' machinery,
2387 * which is backed by a large amount of global state. So check that there
2388 * aren't likely to be conflicts.
2390 if (caps_claim()) goto end;
2392 /* Get the terminal name and try to find the terminal description. */
2393 term = getenv("TERM"); if (!term) goto end;
2394 if (tgetent(termbuf, term) < 1) goto end;
2396 /* Set up a fresh control block. */
2398 u->tc.tc.capcur = u->tc.tc.capbuf;
2399 u->tty.ops = &termcap_ops.tty;
2400 common_init(&u->tty, fp, &spd);
2403 /* Set the `termcap' global variables. */
2404 t = tgetstr("bc", &u->tc.tc.capcur); BC = t ? t : "\b";
2405 UP = UNCONST(char, u->cap.cap.cuu1);
2406 PC = u->cap.cap.pad ? *u->cap.cap.pad : 0;
2410 ret = &u->tty; u = 0;
2412 xfree(u); global_lock = ret; return (ret);
2417 /*----- Terminfo ----------------------------------------------------------*/
2419 #ifdef HAVE_TERMINFO
2421 /* So `terminfo' is pretty bad too, actually. The good news is that
2422 * `termcap''s potential buffer overruns are gone. The bad news is that
2423 * `terminfo' goes all in on global state. Rather than being put in a
2424 * caller-provided buffer, the terminal description is read into global
2427 * Annoyingly, `terminfo' adopts the same `tputs' machinery as `termcap', and
2428 * therefore shares the one-character-at-a-time output and inability to pass
2429 * contextual information through to the ouptut function.
2431 * I'm not a fan of `terminfo' either.
2434 /* --- @terminfo_boolcap@, @terminfo_intcap@, @terminfo_strcap@ --- *
2436 * Arguments: @struct tty *tty@ = control block pointer
2437 * @int uix, const char *info, const char *cap@ = names for the
2438 * requested capability (as Unibilium index, and
2439 * `terminfo' and `termcap' names
2441 * Returns: The requested capability value. On failure,
2442 * @termcap_boolcap@ returns false, @termcap_intcap@ returns
2443 * %$-1$%, and @termcap_strcap@ returns a null pointer.
2445 * Use: Return a requested boolean, integer, or string capability.
2448 static int terminfo_boolcap(struct tty *tty,
2449 int uix, const char *info, const char *cap)
2453 p = tigetflag(info); assert(p >= 0);
2457 static int terminfo_intcap(struct tty *tty,
2458 int uix, const char *info, const char *cap)
2462 n = tigetnum(info); assert(n >= -1);
2466 static const char *terminfo_strcap(struct tty *tty,
2467 int uix, const char *info,
2472 p = tigetstr(info); assert(p != (const char *)-1);
2476 /* --- @terminfo_put0@, @terminfo_put1i@, @terminfo_put2i@ --- *
2478 * Arguments: @struct tty *tty@ = control block pointer
2479 * @unsigned npad@ = number of lines affected, for padding
2480 * @const char *cap@ = capability string to write, or null
2481 * @int i0, i1@ = integer parameters
2483 * Returns: Zero on success, %$-1$% on error.
2485 * Use: Format a capability string and send it to the terminal.
2488 static int terminfo_put0(struct tty *tty,
2489 unsigned npad, const char *cap)
2491 if (!cap) return (-1);
2492 return (tputs(cap, npad, caps_putch));
2495 static int terminfo_put1i(struct tty *tty,
2496 unsigned npad, const char *cap, int i0)
2498 if (!cap) return (-1);
2499 return (tputs(tparm(cap, i0), npad, caps_putch) == OK ? 0 : -1);
2502 static int terminfo_put2i(struct tty *tty,
2503 unsigned npad, const char *cap, int i0, int i1)
2505 if (!cap) return (-1);
2506 return (tputs(tparm(cap, i0, i1), npad, caps_putch) == OK ? 0 : -1);
2509 /* --- @terminfo_cost@ --- *
2511 * Arguments: @struct tty *tty@ = control block pointer
2512 * @unsigned npad@ = number of lines affected
2513 * @const char *cap@ = capability string to measure, or null
2514 * @int i0@ = argument
2516 * Returns: A linear cost for using the capability. This is meaningless
2520 static size_t terminfo_cost(struct tty *tty,
2521 unsigned npad, const char *cap, int i0)
2522 { return (cap ? caps_cost(tty, npad, tparm(cap, i0)) : 0); }
2524 /* The `terminfo' operations table. */
2525 static const union tty_capopsu terminfo_ops = { {
2526 { caps_release, TTY_CAPOPS },
2527 { terminfo_boolcap, terminfo_intcap, terminfo_strcap,
2528 caps_prepout, caps_flush,
2529 terminfo_put0, terminfo_put1i, terminfo_put2i,
2533 /* --- @terminfo_init@ --- *
2535 * Arguments: @FILE *fp@ = the output stream
2537 * Returns: A pointer to a fresh terminal control block, or null on
2540 * Use: Initialize a terminal for use through `terminfo'.
2543 static struct tty *terminfo_init(FILE *fp)
2545 union tty_capsu *u = 0; struct tty *ret = 0;
2548 /* The `termcap' library makes extensive use of global state, so we can
2549 * only have one at a time. Check that there aren't likely to be
2552 if (caps_claim()) goto end;
2554 /* Get the terminal description. */
2555 if (setupterm(0, fp ? fileno(fp) : -1, &err) != OK || err < 1) goto end;
2557 /* Set up a fresh control block. */
2559 u->tty.ops = &terminfo_ops.tty;
2560 common_init(&u->tty, fp, 0);
2564 ret = &u->tty; u = 0;
2566 xfree(u); global_lock = ret; return (ret);
2571 /*----- Unibilium ---------------------------------------------------------*/
2573 #ifdef HAVE_UNIBILIUM
2575 /* Unibilium is a `terminfo' library for the 21st century. It avoids all of
2576 * the mistakes of the `termcap' and `terminfo' interfaces. Honestly, it's a
2577 * bit too `modern' for my tastes: the type bureaucracy involved with
2578 * `unibi_var_t' seems unnecessarily heavyweight (but won't actually bother
2581 * It's not all sunshine and roses. The library makes a principled decision
2582 * to avoid actually messing with terminals directly at all, which seems
2583 * fair enough -- even laudable, maybe. But there's also no help provided to
2584 * generate padding characters, so we have to do that by ourselves, which
2585 * ends up being perhaps the most complicated part of this whole business.
2587 * Also, while the caller-provided output functions can at least be passed a
2588 * context pointer, they can't report errors, which means that we must leave
2589 * a pending error indication.
2591 * Oh, and the name is actually really difficult to type.
2594 /* Additional state required for Unibilium. */
2595 struct tty_unibislots {
2596 unibi_term *ut; /* the terminal description */
2597 unibi_var_t dy[26], st[26]; /* variables used by cap strings */
2598 const struct gprintf_ops *gops; void *go; /* output destination */
2599 char buf[BUFSZ]; size_t n; /* output buffer and length */
2600 int err; /* pending error indication */
2602 struct tty_unibilium { TTY_CAPSPFX; struct tty_unibislots u; };
2603 union tty_unibiliumu { struct tty_unibilium u; TTY_CAPSUSFX; };
2605 /* --- @termunibi_release@ --- *
2607 * Arguments: @struct tty *tty@ = control block pointer
2611 * Use: Release resources held by the control block.
2614 static void termunibi_release(struct tty *tty)
2616 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2618 unibi_destroy(t->u.ut);
2621 /* --- @termcap_boolcap@, @termcap_intcap@, @termcap_strcap@ --- *
2623 * Arguments: @struct tty *tty@ = control block pointer
2624 * @int uix, const char *info, const char *cap@ = names for the
2625 * requested capability (as Unibilium index, and
2626 * `terminfo' and `termcap' names
2628 * Returns: The requested capability value. On failure,
2629 * @termcap_boolcap@ returns false, @termcap_intcap@ returns
2630 * %$-1$%, and @termcap_strcap@ returns a null pointer.
2632 * Use: Return a requested boolean, integer, or string capability.
2635 static int termunibi_boolcap(struct tty *tty,
2636 int uix, const char *info, const char *cap)
2638 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2640 return (unibi_get_bool(t->u.ut, uix));
2643 static int termunibi_intcap(struct tty *tty,
2644 int uix, const char *info, const char *cap)
2646 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2648 return (unibi_get_num(t->u.ut, uix));
2651 static const char *termunibi_strcap(struct tty *tty,
2652 int uix, const char *info,
2655 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2657 return (unibi_get_str(t->u.ut, uix));
2660 /* --- @termunibi_prepout@ --- *
2662 * Arguments: @struct tty *tty@ = control block pointer (ignored)
2663 * @const struct gprintf_ops *gops, void *go@ = output
2668 * Use: Prepare output to the given destination. Check that the
2669 * output buffer is initially empty (i.e., that it was properly
2670 * flushed last time), and make a note of the output
2674 static void termunibi_prepout(struct tty *tty,
2675 const struct gprintf_ops *gops, void *go)
2677 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2679 assert(!t->u.n); t->u.gops = gops; t->u.go = go;
2682 /* Context for output operations. */
2683 struct termunibi_outctx {
2684 struct tty_unibilium *t; /* control block pointer */
2685 unsigned npad; /* number of lines, for padding */
2688 /* --- @termunibi_putm@ --- *
2690 * Arguments: @void *ctx@ = output context pointer
2691 * @const char *p, size_t sz@ = buffer to write
2695 * Use: Send the material in the buffer to the current output
2699 static void termunibi_putm(void *ctx, const char *p, size_t sz)
2701 struct termunibi_outctx *out = ctx;
2702 struct tty_unibilium *t = out->t;
2705 /* Find out how much space is left in the buffer. As for the common
2706 * `termcap'/`terminfo' machinery above, by policy, we leave the buffer
2707 * with at least one byte of space.
2709 n = BUFSZ - t->u.n; assert(n > 0);
2711 /* Save the output into the buffer. */
2713 /* There's enough space in the buffer for everything. Just store all of
2717 memcpy(t->u.buf + t->u.n, p, sz); t->u.n += sz;
2718 } else if (sz - n < THRESH) {
2719 /* We don't really have enough left over to be worth cycling the output
2720 * machinery again yet.
2723 memcpy(t->u.buf + t->u.n, p, n); p += n; sz -= n;
2724 if (t->u.gops->putm(t->u.go, t->u.buf, BUFSZ)) t->u.err = -1;
2725 memcpy(t->u.buf, p, sz); t->u.n = sz;
2727 /* There's enough that it's worth avoiding the copy. Flush whatever's in
2728 * the buffer, and inhale the entire new input in one go, leaving the
2732 if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) t->u.err = -1;
2734 if (t->u.gops->putm(t->u.go, p, sz)) t->u.err = -1;
2738 /* --- @termunibi_pad@ --- *
2740 * Arguments: @void *ctx@ = control block pointer
2741 * @size_t delay@ = tenths of milliseconds to pad
2742 * @int mulp@ = nonzero if padding is per line
2743 * @int forcep@ = nonzero if padding is unconditional
2744 * (regardless of, e.g., baud rate or flow control)
2748 * Use: Pad for a given duration.
2751 static void termunibi_pad(void *ctx, size_t delay, int mulp, int forcep)
2753 struct termunibi_outctx *out = ctx;
2754 struct tty_unibilium *t = out->t;
2760 /* Determine the actual delay. */
2761 if (mulp) d = (unsigned long)delay*out->npad;
2764 /* There's nothing to do unless (a) we're forced, or (b) the baud rate is
2765 * high enough and flow control isn't advertised.
2767 if (forcep || (t->tty.baud >= t->cap.pb && !t->cap.xon)) {
2770 /* There's no safe pad character to send, so we'll just have to wait
2773 * If output is buffered downstream of us then this isn't going to work
2774 * correctly. Honestly, that's a problem for the other output drivers
2775 * too, and it only comes into clear focus here.
2778 /* Flush the output. */
2780 if (t->u.gops->putm(t->u.go, t->u.buf, BUFSZ)) t->u.err = -1;
2783 if (t->tty.fpout) fflush(t->tty.fpout);
2785 /* Do nothing for a little while. */
2786 tv.tv_sec = d/10000; tv.tv_usec = 100*(d%10000);
2787 select(0, 0, 0, 0, &tv);
2789 /* There's a pad character, so figure out how many we need and send
2793 /* Determine the number of pad character and the pad value. */
2794 sz = caps_padchars(&t->tty, d, forcep ? CPF_FORCE : 0);
2795 pc = t->cap.pad ? *t->cap.pad : 0;
2797 /* Work out how much buffer space is available. Again, by policy, the
2798 * buffer is always left with at least one byte of capacity.
2800 n = BUFSZ - t->u.n; assert(n);
2802 /* The padding will fit in the buffer. */
2804 memset(t->u.buf + t->u.n, pc, sz); t->u.n += sz;
2805 } else if (sz - n < BUFSZ) {
2806 /* There's not enough to fill the buffer a second time. Do this in
2810 memset(t->u.buf + t->u.n, pc, n); sz -= n;
2811 if (t->u.gops->putm(t->u.go, t->u.buf, BUFSZ)) t->u.err = -1;
2812 memset(t->u.buf, pc, sz); t->u.n = sz;
2814 /* We'll fill the whole buffer at least once. Flush what we have
2815 * immediately and then do whole buffer-full sends.
2818 if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) t->u.err = -1;
2819 memset(t->u.buf, pc, BUFSZ);
2820 while (sz >= BUFSZ) {
2821 if (t->u.gops->putm(t->u.go, t->u.buf, BUFSZ)) t->u.err = -1;
2830 /* --- @termunibi_flush@ --- *
2832 * Arguments: @struct tty *tty@ = control block pointer (ignored)
2834 * Returns: Zero for success, %$-1$% if error pending.
2836 * Use: Flush the output buffer to the backend. If an error is
2837 * pending, clear it and return failure.
2840 static int termunibi_flush(struct tty *tty)
2842 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2846 if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) rc = -1;
2849 t->u.err = 0; return (rc);
2852 /* --- @termunibi_put0@, @termunibi_put1i@, @termunibi_put2i@ --- *
2854 * Arguments: @struct tty *tty@ = control block pointer
2855 * @unsigned npad@ = number of lines affected, for padding
2856 * @const char *cap@ = capability string to write, or null
2857 * @int i0, i1@ = integer parameters
2859 * Returns: Zero on success, %$-1$% on error.
2861 * Use: Format a capability string and send it to the terminal.
2864 static int termunibi_put0(struct tty *tty,
2865 unsigned npad, const char *cap)
2867 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2868 struct termunibi_outctx out;
2871 if (!cap) return (-1);
2872 out.t = t; out.npad = npad;
2873 unibi_format(t->u.dy, t->u.st, cap, arg,
2874 termunibi_putm, &out,
2875 termunibi_pad, &out);
2879 static int termunibi_put1i(struct tty *tty,
2880 unsigned npad, const char *cap, int i0)
2882 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2883 struct termunibi_outctx out;
2886 if (!cap) return (-1);
2887 arg[0] = unibi_var_from_num(i0);
2888 out.t = t; out.npad = npad;
2889 unibi_format(t->u.dy, t->u.st, cap, arg,
2890 termunibi_putm, &out,
2891 termunibi_pad, &out);
2895 static int termunibi_put2i(struct tty *tty,
2897 const char *cap, int i0, int i1)
2899 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2900 struct termunibi_outctx out;
2903 if (!cap) return (-1);
2904 arg[0] = unibi_var_from_num(i0);
2905 arg[1] = unibi_var_from_num(i1);
2906 out.t = t; out.npad = npad;
2907 unibi_format(t->u.dy, t->u.st, cap, arg,
2908 termunibi_putm, &out,
2909 termunibi_pad, &out);
2913 /* Context for cost counting. */
2914 struct termunibi_costctx {
2915 struct tty_unibilium *t; /* terminal control block */
2916 unsigned npad; /* number of lines affected */
2917 size_t n; /* accumulated cost, in chars */
2920 /* --- @termunibi_costputm@ --- *
2922 * Arguments: @void *ctx@ = output context pointer
2923 * @const char *p, size_t sz@ = buffer to write
2927 * Use: Count the number of characters written.
2930 static void termunibi_costputm(void *ctx, const char *p, size_t sz)
2931 { struct termunibi_costctx *c = ctx; c->n += sz; }
2933 /* --- @termunibi_costpad@ --- *
2935 * Arguments: @void *ctx@ = control block pointer
2936 * @size_t delay@ = tenths of milliseconds to pad
2937 * @int mulp@ = nonzero if padding is per line
2938 * @int forcep@ = nonzero if padding is unconditional
2939 * (regardless of, e.g., baud rate or flow control)
2943 * Use: Count the character equivalent of the requested delay.
2946 static void termunibi_costpad(void *ctx, size_t delay, int mulp, int forcep)
2948 struct termunibi_costctx *c = ctx;
2950 if (mulp) delay *= c->npad;
2951 c->n += caps_padchars(&c->t->tty, delay, CPF_FORCE);
2954 /* --- @termunibi_cost@ --- *
2956 * Arguments: @struct tty *tty@ = control block pointer
2957 * @unsigned npad@ = number of lines affected
2958 * @const char *cap@ = capability string to measure, or null
2959 * @int i0@ = argument
2961 * Returns: A linear cost for using the capability. This is meaningless
2965 static size_t termunibi_cost(struct tty *tty,
2966 unsigned npad, const char *cap, int i0)
2968 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2969 struct termunibi_costctx c;
2972 if (!cap) return (0);
2973 arg[0] = unibi_var_from_num(i0);
2974 c.t = t; c.npad = npad; c.n = 0;
2975 unibi_format(t->u.dy, t->u.st, cap, arg,
2976 termunibi_costputm, &c,
2977 termunibi_costpad, &c);
2981 /* The `terminfo' operations table. */
2982 static const union tty_capopsu termunibi_ops = { {
2983 { termunibi_release, TTY_CAPOPS },
2984 { termunibi_boolcap, termunibi_intcap, termunibi_strcap,
2985 termunibi_prepout, termunibi_flush,
2986 termunibi_put0, termunibi_put1i, termunibi_put2i,
2990 /* --- @termunibi_init@ --- *
2992 * Arguments: @FILE *fp@ = the output stream
2994 * Returns: A pointer to a fresh terminal control block, or null on
2997 * Use: Initialize a terminal for use through Unibilium.
3000 static struct tty *termunibi_init(FILE *fp)
3002 union tty_unibiliumu *u = 0; struct tty *ret = 0;
3005 /* Collect the terminal description. */
3006 ut = unibi_from_env(); if (!ut) goto end;
3008 /* Set up a fresh control block. */
3010 u->tty.ops = &termunibi_ops.tty;
3011 u->u.u.ut = ut; ut = 0;
3012 common_init(&u->tty, fp, 0);
3015 /* Initialize the buffer state. */
3016 u->u.u.n = 0; u->u.u.err = 0;
3019 ret = &u->tty; u = 0;
3021 xfree(u); if (ut) unibi_destroy(ut);
3027 /*----- ANSI terminals ----------------------------------------------------*/
3029 /* Here we dispense with external libraries and take a wild guess that the
3030 * terminal accepts some variant of ANSI control sequences.
3032 * Here's a quick reference of the control sequences that we use here.
3034 * * CUP: \33 [ Y ; X H `cursor position' [vt100]
3036 * * CUU/CUD/CUR/CUL: \33 [ N A/B/C/D `cursor up/down/right/left'
3038 * * DCH: \33 [ N P `delete character' [vt220]
3039 * (single char only in vt102?)
3041 * * DL: \33 [ N M `delete line' [vt220]
3042 * (single line only in vt102?)
3044 * * ECH: \33 [ N X `erase characters' [vt220]
3046 * * ED: \33 [ P J `erase in display'
3047 * P = 0 erase to end-of-screen [vt100]
3048 * P = 1 erase from start-of-screen [vt100]
3049 * P = 2 erase entire screen [vt100]
3051 * * EL: \33 [ P K `erase in line'
3052 * P = 0 erase to end-of-line [vt100]
3053 * P = 1 erase from start-of-line [vt100]
3054 * P = 2 erase entire line [vt100]
3056 * * HPA/VPA: \33 [ I G/d `horizontal/vertical position
3057 * absolute' [ecma48-4]
3059 * * ICH: \33 [ N @ `insert character' [vt220]
3060 * (single char only in vt102?)
3062 * * IL: \33 [ N L `insert line' [vt220]
3063 * (single line only in vt102?)
3065 * * SGR: \33 [ P ; ... m `select graphics rendition'
3066 * P = 0 cancel all attributes [vt100]
3067 * P = 1 bold [vt100]
3068 * P = 2 dim [ecma48-4]
3069 * P = 3 italics [ecma48-4]
3070 * P = 4 underline [vt100]
3071 * P = 7 inverse video [vt100]
3072 * P = 9 strikeout [ecma48-4]
3073 * P = 21 double underline [ecma48-4]
3074 * P = 22 cancal bold/dim [vt220]
3075 * P = 24 cancel underline [vt220]
3076 * P = 27 cancel inverse video [vt220]
3077 * P = 30 + 4 R + 2 G + B set 1BPC foreground [ecma48-4]
3078 * P = 38 : 2 : ? : R : G : B set foreground [iso8613-6]
3079 * P = 38 : 5 : N set foreground [iso8613-6, xterm]
3080 * P = 39 cancel foreground [ecma48-4]
3081 * P = 40--49 as above, for background
3082 * P = 90 + 4 R + 2 G + B set bright 1BPC foreground [xterm]
3083 * P = 100 + 4 R + 2 G + B set bright 1BPC background [xterm]
3085 * * SM/RM: \33 [ P ; ... h/l `set/reset modes'
3086 * M = 4 insert [vt220]
3088 * * SM, RM: \33 [ ? P ; ... h/l `set/reset private modes'
3089 * M = 7 auto right margin [vt100]
3090 * M = 25 visible cursor [vt220]
3091 * M = 1049 alternate screen [xterm]
3093 * * \33 [ P ; X ; Y t `window manipulation'
3094 * P = 22, X = 0 save title and icon [xterm]
3095 * P = 23, X = 0 restore title and icon [xterm]
3098 /* Additional state required for ANSI terminals. */
3099 struct tty_ansislots {
3101 #define TAF_CNCATTR 1u /* attributes can be cancelled */
3102 #define TAF_EDITN 2u /* insert/delete multiple */
3103 #define TAF_SEMI 4u /* semicolons in CSI 38 m colour */
3105 struct tty_ansi { TTY_BASEPFX; struct tty_ansislots ansi; };
3106 union tty_ansiu { struct tty_ansi ansi; TTY_BASEUSFX; };
3108 /* --- @ansi_release@ --- *
3110 * Arguments: @struct tty *tty@ = control block pointer
3114 * Use: Release resources held by the control block. (There's
3115 * actually nothing to do here.)
3118 static void ansi_release(struct tty *tty) { ; }
3120 /* Some simple macros we use throughout. */
3121 #define PUTCH(ch) CHECK(gops->putch(go, (ch)))
3122 #define PUTLIT(lit) CHECK(gops->putm(go, (lit), sizeof(lit) - 1))
3124 /* Shared macro for @ansi_setcolour@ and @ansi_setattr@. The output has the
3125 * form `ESC [ ... ; ... ; ... m', so there's a semicolon before each
3126 * parameter other than the first. The @TAF_SEMI@ flag here keeps track of
3127 * whether a semicolon is wanted before the next item, and this macro is
3128 * responsible for writing it when necessary.
3131 if (!(f&TAF_SEMI)) f |= TAF_SEMI; \
3135 /* --- @ansi_setcolour@ --- *
3137 * Arguments: @struct tty_caps *t@ = extended control block pointer
3138 * @unsigned *f_inout@ = flags (updated)
3139 * @const struct gprintf_ops *gops, void *go@ = output
3141 * @int norm, br@ = normal and bright base codes -- 30 and 90
3142 * for foreground, or 40 and 100 for background
3143 * @uint32 spc, clr@ = the colour space and number
3145 * Returns: Zero on success, %$-1$% on error.
3147 * Use: Emit the correct control string to set the foreground or
3148 * background colour as indicated.
3151 static int ansi_setcolour(struct tty_ansi *t, unsigned *f_inout,
3152 const struct gprintf_ops *gops, void *go,
3154 uint32 spc, uint32 clr)
3156 unsigned f = *f_inout;
3161 SEMI; CHECK(gprintf(gops, go, "%d", norm + 9));
3164 SEMI; CHECK(gprintf(gops, go, "%d", norm + clr));
3167 SEMI; CHECK(gprintf(gops, go, "%d", br + (clr&~TT1BPC_BRI)));
3169 case TTCSPC_4LPC: case TTCSPC_6LPC:
3171 if (t->ansi.f&TAF_SEMI)
3172 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 16));
3174 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 16));
3178 if (t->ansi.f&TAF_SEMI)
3179 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 80));
3181 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 80));
3185 if (t->ansi.f&TAF_SEMI)
3186 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 232));
3188 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 232));
3192 if (t->ansi.f&TAF_SEMI)
3193 CHECK(gprintf(gops, go, "%d;2;%d;%d;%d", norm + 8,
3194 TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
3196 CHECK(gprintf(gops, go, "%d:2::%d:%d:%d", norm + 8,
3197 TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
3205 *f_inout = f; return (rc);
3208 /* --- @ansi_setattr@ --- *
3210 * Arguments: @struct tty *tty@ = control block pointer
3211 * @const struct gprintf_ops *gops, void *go@ = output
3213 * @const struct tty_attr *a@ = attribute to set, already
3216 * Returns: Zero on success, %$-1$% on error.
3218 * Use: Arrange to display future characters with the display
3219 * attributes indicated by @a@.
3222 static int ansi_setattr(struct tty *tty,
3223 const struct gprintf_ops *gops, void *go,
3224 const struct tty_attr *a)
3226 struct tty_ansi *t = (struct tty_ansi *)tty;
3229 unsigned z, c, f = 0;
3231 /* Work out what, if anything, needs doing. Since we start by writing
3232 * `ESC [' unconditionally below, if there's actually no work to do at all,
3233 * we need to stop early.
3235 diff = a->f ^ t->tty.st.attr.f;
3236 if (!diff && a->fg == t->tty.st.attr.fg && a->bg == t->tty.st.attr.bg)
3237 { rc = 0; goto end; }
3239 /* We're committed. Write the start of the `SGR' sequence. */
3242 /* Form a basic strategy.
3244 * As for @caps_setattr@ above, we need to decide betwen cancelling
3245 * individual attributes or resetting the whole lot and reinstating the
3246 * attributes which were cancelled unnecessarily.
3249 /* Start by adding up the costs of cancelling individual attributes.
3250 * Early terminals can't do this, but we'll deal with that problem later.
3253 #define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
3254 if (CLEARP(TTAF_LNMASK)) c += 3;
3255 if (CLEARP(TTAF_WTMASK)) c += 3;
3256 if (diff&~a->f&TTAF_INVV) c += 3;
3257 if (diff&~a->f&TTAF_STRIKE) c += 3;
3258 if (diff&~a->f&TTAF_ITAL) c += 3;
3259 if (CLEARP(TTAF_FGSPCMASK)) c += 3;
3260 if (CLEARP(TTAF_BGSPCMASK)) c += 3;
3263 /* Count up the cost of cancelling everything and then putting back the
3264 * ones that we actually wanted.
3267 if (!(diff&TTAF_LNMASK))
3268 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
3269 case TTLN_ULINE: z += 2; break;
3270 case TTLN_UULINE: z += 3; break;
3272 if (!(diff&TTAF_WTMASK))
3273 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
3274 case TTWT_BOLD: z += 2; break;
3275 case TTWT_DIM: z += 2; break;
3278 if (m&TTAF_INVV) z += 2;
3279 if (m&TTAF_STRIKE) z += 2;
3280 if (m&TTAF_ITAL) z += 2;
3281 #define COLOURCOST(G, g) do { \
3282 if (!(diff&TTAF_##G##SPCMASK) && \
3283 (a->f&TTAF_##G##SPCMASK) && a->g == t->tty.st.attr.g) \
3284 switch ((a->f&TTAF_##G##SPCMASK) >> TTAF_##G##SPCSHIFT) { \
3285 case TTCSPC_1BPC: case TTCSPC_1BPCBR: z += 3; break; \
3286 case TTCSPC_4LPC: case TTCSPC_8LGS: z += 8; break; \
3287 case TTCSPC_6LPC: case TTCSPC_24LGS: z += 9; break; \
3288 case TTCSPC_8BPC: z += 16; break; \
3291 COLOURCOST(FG, fg); COLOURCOST(BG, bg);
3294 /* If cancelling everything is cheaper, or we can't cancel individual
3295 * attributes after all, then reset everything.
3297 if (z < c || (c && !(t->ansi.f&TAF_CNCATTR)))
3298 { SEMI; diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg = 0; }
3301 if (diff&TTAF_LNMASK)
3302 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
3303 case TTLN_NONE: SEMI; PUTLIT("24"); break;
3304 case TTLN_ULINE: SEMI; PUTCH('4'); break;
3305 case TTLN_UULINE: SEMI; PUTLIT("21"); break;
3306 default: rc = -1; goto end;
3310 if (diff&TTAF_WTMASK)
3311 switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
3312 case TTWT_MED: SEMI; PUTLIT("22"); break;
3313 case TTWT_BOLD: SEMI; PUTCH('1'); break;
3314 case TTWT_DIM: SEMI; PUTCH('2'); break;
3315 default: rc = -1; goto end;
3318 /* Other text effects. */
3320 { SEMI; if (a->f&TTAF_INVV) PUTCH('7'); else PUTLIT("27"); }
3321 if (diff&TTAF_STRIKE)
3322 { SEMI; if (a->f&TTAF_STRIKE) PUTCH('9'); else PUTLIT("29"); }
3324 { SEMI; if (a->f&TTAF_ITAL) PUTCH('3'); else PUTLIT("23"); }
3327 if (diff&TTAF_FGSPCMASK || a->fg != tty->st.attr.fg)
3328 CHECK(ansi_setcolour(t, &f, gops, go, 30, 90,
3329 (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
3330 if (diff&TTAF_BGSPCMASK || a->bg != tty->st.attr.bg)
3331 CHECK(ansi_setcolour(t, &f, gops, go, 40, 100,
3332 (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
3337 t->tty.st.attr = *a; return (rc);
3340 /* --- @ansi_setmodes@ --- *
3342 * Arguments: @struct tty *tty@ = control block pointer
3343 * @const struct gprintf_ops *gops, void *go@ = output
3345 * @uint32 modes_bic, modes_xor@ = masks to apply to the modes
3348 * Returns: Zero on success, %$-1$% on error.
3350 * Use: Set the requested terminal modes.
3353 static int ansi_setmodes(struct tty *tty,
3354 const struct gprintf_ops *gops, void *go,
3355 uint32 modes_bic, uint32 modes_xor)
3360 /* Figure out which modes to set. */
3361 modes = (tty->st.modes&~modes_bic) ^ modes_xor;
3362 diff = modes ^ tty->st.modes;
3365 if (diff&TTMF_AUTOM) {
3366 if (modes&TTMF_AUTOM) PUTLIT("\33[?7h");
3367 else PUTLIT("\33[?7l");
3371 if (diff&TTMF_FSCRN) {
3372 if (modes&TTMF_FSCRN) PUTLIT("\33[?1049h\33[22;0;0t");
3373 else PUTLIT("\33[?1049l\33[23;0;0t");
3376 /* Visible cursor. */
3377 if (diff&TTMF_CVIS) {
3378 if (modes&TTMF_CVIS) PUTLIT("\33[?25h");
3379 else PUTLIT("\33[?25l");
3383 if (diff&TTMF_INS) {
3384 if (modes&TTMF_INS) PUTLIT("\33[4h");
3385 else PUTLIT("\33[4l");
3391 tty->st.modes = modes;
3395 /* --- @ansi_move@ --- *
3397 * Arguments: @struct tty *tty@ = control block pointer
3398 * @const struct gprintf_ops *gops, void *go@ = output
3400 * @unsigned orig@ = origin
3401 * @int y, x@ = new cursor position
3403 * Returns: Zero on success, %$-1$% on error.
3405 * Use: Move the cursor.
3408 static int ansi_move(struct tty *tty,
3409 const struct gprintf_ops *gops, void *go,
3410 unsigned orig, int y, int x)
3412 static const char bs[4] = { '\b', '\b', '\b', '\b' };
3415 /* Check that the arguments are basically sensible. */
3416 if ((!(orig&TTOF_YCUR) && y < 0) || (!(orig&TTOF_XCUR) && x < 0))
3417 { rc = -1; goto end; }
3419 if (orig == TTORG_HOME) {
3420 /* Fully absolute positioning. We can omit the arguments for the topmost
3421 * row and/or leftmost column.
3425 if (!y) PUTLIT("\33[H");
3426 else CHECK(gprintf(gops, go, "\33[%dH", y + 1));
3428 if (!y) CHECK(gprintf(gops, go, "\33[;%dH", x + 1));
3429 else CHECK(gprintf(gops, go, "\33[%d,%dH", y + 1, x + 1));
3431 } else if (orig == (TTOF_XHOME | TTOF_YCUR) && x == 0 && y == 1) {
3432 /* Special case for starting a new line. */
3436 /* More complex motion. Handle the horizontal and vertical motions
3440 /* First, vertical motion. This is simpler. */
3441 if (!(orig&TTOF_YCUR)) CHECK(gprintf(gops, go, "\33[%dd", y + 1));
3442 else if (y == -1) PUTLIT("\33[A");
3443 else if (y < 0) CHECK(gprintf(gops, go, "\33[%dA", -y));
3444 else if (y == +1) PUTLIT("\33[B"); /* not %|^J|%! */
3445 else if (y > 1) CHECK(gprintf(gops, go, "\33[%dB", y));
3447 /* Next, horizontal motion. */
3448 if (!(orig&TTOF_XCUR)) {
3449 /* Absolute positioning. Use a carriage return if (a) we want the
3450 * leftmost column anyway, or (b) the terminal can't handle absolute
3451 * horizontal positioning.
3456 else if (tty->ocaps&TTCF_MIXMV)
3457 CHECK(gprintf(gops, go, "\33[%dG", x + 1));
3459 CHECK(gprintf(gops, go, "\r\33[%dC", x));
3461 /* Relative positioning. We can go back one space simply using the
3462 * backspace control sequence. The `CUB' sequence is at least four
3466 if (x == -1) PUTCH('\b');
3467 else if (-4 <= x && x < -1) CHECK(gops->putm(go, bs, -x));
3468 else if (x < 0) CHECK(gprintf(gops, go, "\33[%dD", -x));
3469 else if (x == +1) PUTLIT("\33[C");
3470 else if (x > 0) CHECK(gprintf(gops, go, "\33[%dC", x));
3480 /* --- @ansi_erase@ --- *
3482 * Arguments: @struct tty *tty@ = control block pointer
3483 * @const struct gprintf_ops *gops, void *go@ = output
3485 * @unsigned f@ = flags
3487 * Returns: Zero on success, %$-1$% on error.
3489 * Use: Erase portions of the current line or the whole display.
3492 static int ansi_erase(struct tty *tty,
3493 const struct gprintf_ops *gops, void *go,
3499 switch (f&(TTEF_BEGIN | TTEF_END)) {
3501 case TTEF_BEGIN: PUTLIT("\33[1J"); break;
3502 case TTEF_END: PUTLIT("\33[J"); break;
3503 case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2J"); break;
3506 switch (f&(TTEF_BEGIN | TTEF_END)) {
3508 case TTEF_BEGIN: PUTLIT("\33[1K"); break;
3509 case TTEF_END: PUTLIT("\33[K"); break;
3510 case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2K"); break;
3517 /* --- @ansi_erch@ --- *
3519 * Arguments: @struct tty *tty@ = control block pointer
3520 * @const struct gprintf_ops *gops, void *go@ = output
3522 * @unsigned n@ = number of characters to erase
3524 * Returns: Zero on success, %$-1$% on error.
3526 * Use: Erase a number of characters, starting from and including the
3527 * current cursor position.
3530 static int ansi_erch(struct tty *tty,
3531 const struct gprintf_ops *gops, void *go,
3536 if (n == 1) PUTLIT("\33[X");
3537 else if (n) CHECK(gprintf(gops, go, "\33[%uX", n));
3543 /* --- @caps_ins@ --- *
3545 * Arguments: @struct tty *tty@ = control block pointer
3546 * @const struct gprintf_ops *gops, void *go@ = output
3548 * @unsigned f@ = flags
3549 * @unsigned n@ = number of items to insert
3551 * Returns: Zero on success, %$-1$% on error.
3553 * Use: Insert a number of blank characters or lines.
3556 static int ansi_ins(struct tty *tty,
3557 const struct gprintf_ops *gops, void *go,
3558 unsigned f, unsigned n)
3563 if (n == 1) PUTLIT("\33[L");
3564 else if (n) CHECK(gprintf(gops, go, "\33[%uL", n));
3566 if (n == 1) PUTLIT("\33[@");
3567 else if (n) CHECK(gprintf(gops, go, "\33[%u@", n));
3574 /* --- @caps_inch@ --- *
3576 * Arguments: @struct tty *tty@ = control block pointer
3577 * @const struct gprintf_ops *gops, void *go@ = output
3579 * @int ch@ = character to insert
3581 * Returns: Zero on success, %$-1$% on error.
3583 * Use: Insert a single character.
3586 static int ansi_inch(struct tty *tty,
3587 const struct gprintf_ops *gops, void *go,
3590 /* There's actually nothing to do here except write the character. */
3591 if (!(tty->st.modes&TTMF_INS)) return (-1);
3592 else return (gops->putch(go, ch));
3595 /* --- @caps_del@ --- *
3597 * Arguments: @struct tty *tty@ = control block pointer
3598 * @const struct gprintf_ops *gops, void *go@ = output
3600 * @unsigned f@ = flags
3601 * @unsigned n@ = number of items to delete
3603 * Returns: Zero on success, %$-1$% on error.
3605 * Use: Delete a number of characters or lines.
3608 static int ansi_del(struct tty *tty,
3609 const struct gprintf_ops *gops, void *go,
3610 unsigned f, unsigned n)
3615 if (n == 1) PUTLIT("\33[M");
3616 else if (n) CHECK(gprintf(gops, go, "\33[%uM", n));
3618 if (n == 1) PUTLIT("\33[P");
3619 else if (n) CHECK(gprintf(gops, go, "\33[%uP", n));
3632 /* The ANSI operations table. */
3633 static const struct tty_ops ansi_ops = {
3635 ansi_setattr, ansi_setmodes,
3636 ansi_move, stupid_repeat,
3637 ansi_erase, ansi_erch, ansi_ins, ansi_inch, ansi_del
3640 /* --- @termcap_init@ --- *
3642 * Arguments: @FILE *fp@ = the output stream
3644 * Returns: A pointer to a fresh terminal control block, or null on
3647 * Use: Initialize a terminal for use with ANSI control sequences.
3650 static struct tty *ansi_init(FILE *fp)
3652 /* Useful collections of capabilities. */
3654 #define COLS_8 (TTACF_FG | TTACF_BG | TTACF_1BPC)
3655 #define COLS_16 (COLS_8 | TTACF_1BPCBR)
3656 #define COLS_88 (COLS_16 | TTACF_4LPC | TTACF_8LGS)
3657 #define COLS_256 (COLS_16 | TTACF_6LPC | TTACF_24LGS)
3658 #define COLS_16M (COLS_256 | TTACF_8BPC)
3660 #define EDIT_OPS (TTCF_ERCH | \
3661 TTCF_DELCH | TTCF_DELLN | \
3662 TTCF_INSCH | TTCF_INSLN)
3664 /* Table of user-settable flags. */
3665 static const struct flagmap {
3667 uint32 acaps, ocaps;
3670 { "dim", TTACF_DIM, 0, 0 },
3671 { "uuline", TTACF_UULINE, 0, 0 },
3672 { "strike", TTACF_STRIKE, 0, 0 },
3673 { "ital", TTACF_ITAL, 0, 0 },
3674 { "cvis", 0, TTMF_CVIS, 0 },
3675 { "fscrn", 0, TTMF_FSCRN, 0 },
3676 { "insmode", 0, TTMF_INS, 0 },
3677 { "hvpa" , 0, TTCF_MIXMV, 0 },
3678 { "edit", 0, EDIT_OPS, 0 },
3679 { "cncattr", 0, 0, TAF_CNCATTR },
3680 { "editn", 0, 0, TAF_EDITN },
3681 { "semi", 0, 0, TAF_SEMI },
3685 /* Tables of enumerated setting values. */
3686 static const struct kw { const char *name; uint32 val; }
3692 { "256", COLS_256 },
3693 { "16m", COLS_16M },
3697 /* Table of enumerated settings. */
3698 static const struct enummap {
3701 const struct kw *kw;
3703 { "colours", TTACF_CSPCMASK | TTACF_FG | TTACF_BG,
3708 /* Table of recognized terminal types. */
3709 static const struct termmap {
3711 unsigned acaps, ocaps, tf;
3714 #define VT100_ACAPS (TTACF_ULINE | TTACF_BOLD | TTACF_INVV)
3715 #define VT100_OCAPS (TTMF_AUTOM | \
3716 TTCF_RELMV | TTCF_ABSMV | \
3718 TTCF_ERBOL | TTCF_EREOL | \
3719 TTCF_ERBOD | TTCF_EREOD | TTCF_ERDSP)
3720 #define VT100_TF (0)
3722 #define VT102_ACAPS (VT100_ACAPS)
3723 #define VT102_OCAPS (VT100_OCAPS | \
3725 TTCF_INSCH | TTCF_INSLN | TTCF_DELCH | TTCF_DELLN)
3726 #define VT102_TF (VT100_TF)
3728 #define VT220_ACAPS (VT102_ACAPS)
3729 #define VT220_OCAPS (VT102_OCAPS | TTMF_CVIS | TTCF_ERCH)
3730 #define VT220_TF (VT102_TF | TAF_CNCATTR | TAF_EDITN)
3732 #define ECMA48_ACAPS (VT220_ACAPS | TTACF_DIM)
3733 #define ECMA48_OCAPS (VT220_OCAPS | TTCF_MIXMV)
3734 #define ECMA48_TF (VT220_TF)
3736 #define XTERM_ACAPS (ECMA48_ACAPS)
3737 #define XTERM_OCAPS (ECMA48_OCAPS | TTMF_FSCRN)
3738 #define XTERM_TF (ECMA48_TF)
3740 #define STRIKE TTACF_STRIKE
3741 #define ITAL TTACF_ITAL
3742 #define SEMI TAF_SEMI
3744 #define T(pat, base, cols, acaps, ocaps, tf) \
3746 base##_ACAPS | COLS_##cols | (acaps), \
3747 base##_OCAPS | (ocaps), base##_TF | (tf) }
3749 T("color_xterm", XTERM, 8, STRIKE | ITAL, 0, 0),
3751 T("gnome", XTERM, 16M, STRIKE | ITAL, 0, SEMI),
3752 /*T("gonme-*" XTERM, 16M, STRIKE | ITAL, 0, SEMI),*/
3754 T("linux", XTERM, 16, 0, 0, 0),
3756 T("putty", XTERM, 16M, 0, 0, SEMI),
3758 T("vt100*", VT100, NO, 0, 0, 0),
3759 T("vt102*", VT102, NO, 0, 0, 0),
3760 T("vt[2-5][0-9][0-9]*", VT220, NO, 0, 0, 0),
3762 T("vte", XTERM, 16M, STRIKE | ITAL, 0, SEMI),
3763 /*T("vte-*" XTERM, 16M, STRIKE | ITAL, 0, SEMI),*/
3765 T("win", XTERM, 16M, 0, 0, SEMI),
3767 T("xterm", XTERM, 16M, STRIKE | ITAL, 0, 0),
3768 T("xterm-color", XTERM, 8, STRIKE | ITAL, 0, 0),
3769 T("xterm-16color", XTERM, 16, STRIKE | ITAL, 0, 0),
3770 T("xterm-88color", XTERM, 88, STRIKE | ITAL, 0, SEMI),
3771 T("xterm-256color", XTERM, 256, STRIKE | ITAL, 0, SEMI),
3772 T("xterm-direct", XTERM, 16M, STRIKE | ITAL, 0, 0),
3773 T("xterm-*", XTERM, 16M, STRIKE | ITAL, 0, 0),
3775 /*T("*-color", XTERM, 16, 0, 0, 0),*/
3776 /*T("*-16color", XTERM, 16, 0, 0, 0),*/
3777 T("*-88color", XTERM, 88, 0, 0, SEMI),
3778 T("*-256color", XTERM, 256, 0, 0, SEMI),
3779 T("*-direct", XTERM, 16M, 0, 0, SEMI),
3781 T("*", XTERM, 16, 0, 0, 0),
3818 union tty_ansiu *u = 0; struct tty *ret = 0;
3819 const char *term, *config, *p, *l;
3820 const struct kw *kw;
3821 const struct enummap *em;
3822 const struct flagmap *fm;
3823 const struct termmap *tm;
3826 acaps = 0, ocaps = 0, tf = 0,
3827 acapset = 0, ocapset = 0, tfset = 0,
3831 /* Read the environment variables. */
3832 config = getenv("MLIB_TTY_ANSICONFIG");
3833 term = getenv("TERM");
3835 /* We're not going to touch Emacs's `dumb' terminal. */
3836 if (term && STRCMP(term, ==, "dumb")) goto end;
3838 /* Scan the user configuration first. We'll keep track of masks for the
3839 * capabilities that we've set so that we don't clobber them later.
3842 l = config + strlen(config);
3847 if (config >= l) goto done_config;
3848 else if (!ISSPACE(*config)) break;
3851 /* Find the length of the next space-sparated word. */
3852 for (p = config + 1; p < l && !ISSPACE(*p); p++);
3854 if (*config == '+' || *config == '-') {
3855 /* We've found a flag setting. */
3857 /* Track whether we're supposed to set or clear it. */
3858 if (*config == '+') f |= f_sense;
3860 config++; n = p - config;
3862 /* Search for the flag name. */
3863 for (fm = flagmap; fm->name; fm++)
3864 if (STRNCMP(config, ==, fm->name, n) && !fm->name[n])
3866 debug("unknown flag `%.*s'", (int)n, config); goto next_config;
3869 /* Found it. Check that we've not already seen this one. */
3870 if ((acapset&fm->acaps) || (ocapset&fm->ocaps) || (tfset&fm->tf)) {
3871 debug("duplicate setting for `%s'", fm->name);
3875 /* Apply the setting. */
3877 { acaps |= fm->acaps; ocaps |= fm->ocaps; tf |= fm->tf; }
3878 acapset |= fm->acaps; ocapset |= fm->ocaps; tfset |= fm->tf;
3880 /* Must be an assignment of an enumerated setting. */
3884 /* Find the `%|=|%' separator. */
3885 p = memchr(config, '=', n);
3887 debug("missing `=' in setting `%.*s'", (int)n, config);
3891 /* Search for the setting name. */
3893 for (em = enummap; em->name; em++)
3894 if (STRNCMP(config, ==, em->name, nn) && !em->name[nn])
3896 debug("unknown setting `%.*s'", (int)nn, config); goto next_config;
3899 /* Found it. Check that we've not already seen this one. */
3900 if (acapset&em->mask) {
3901 debug("duplicate setting for `%s'", em->name);
3905 /* Now search for the value. */
3906 p++; nn = n - nn - 1;
3907 for (kw = em->kw; kw->name; kw++)
3908 if (STRNCMP(p, ==, kw->name, nn) && !kw->name[nn])
3910 debug("unknown `%s' value `%.*s", em->name, (int)nn, p);
3914 /* Found that too. Apply the setting. */
3915 acaps |= kw->val; acapset |= em->mask;
3919 /* Move on to the next word. */
3925 /* Next, check our built-in table of terminal idiosyncrasies. */
3927 for (tm = termmap; tm->pat; tm++)
3928 if (str_match(tm->pat, term))
3932 acaps |= tm->acaps&~acapset;
3933 ocaps |= tm->ocaps&~ocapset;
3934 tf |= tm->tf&~tfset;
3937 /* If the user hasn't already set how we handle colour, then check the
3938 * environment for more general advice. Unlike `terminfo'-based backends,
3939 * we can handle colour upgrades as well as downgrades.
3941 if (!(acapset&TTACF_CSPCMASK)) env_colour_caps(&acaps, ECCF_SET);
3943 /* If we can handle background colours, then we can erase to background. */
3944 if (acaps&TTACF_BG) ocaps |= TTCF_BGER;
3946 /* Set up a fresh control block. */
3948 u->tty.ops = &ansi_ops;
3949 u->tty.acaps = acaps;
3950 u->tty.ocaps = ocaps;
3951 u->ansi.ansi.f = tf;
3952 u->tty.wd = 80; u->tty.ht = 25;
3953 u->tty.st.modes = TTMF_AUTOM | (u->tty.ocaps&TTMF_CVIS);
3954 u->tty.st.attr.f = 0; u->tty.st.attr.fg = u->tty.st.attr.bg = 0;
3955 common_init(&u->ansi.tty, fp, 0);
3958 ret = &u->tty; u = 0;
3960 xfree(u); return (ret);
3965 /*----- Backend selection -------------------------------------------------*/
3967 /* --- @tty_open@ --- *
3969 * Arguments: @FILE *fp@ = stream open on terminal, or null
3970 * @unsigned f@ = flags (@TTF_...@)
3971 * @const unsigned *backends@ = ordered list of backends to try
3973 * Returns: Pointer to terminal control block, or null on error.
3975 * Use: Open a terminal and return a @struct tty *@ terminal control
3978 * If @fp@ is provided, then it will be used for terminal
3979 * output. If @fp@ is @stdout@, then input (not currently
3980 * supported) will come from @stdin@; otherwise, input comes
3981 * from @fp@. If @fp@ is null and @TTF_OPEN@ is set, then
3982 * @tty_open@ will attempt to open a terminal for itself: if
3983 * @stdin@ is interactive then it used for input; if @stdout@ --
3984 * or, failing that, @stderr@ -- is interactive, then it is used
3985 * for output; otherwise %|/dev/tty|% is opened and used. If
3986 * @fp@ is null and @TTF_OPEN@ is not set, then a usable
3987 * terminal control block is still returned, but output cannot
3988 * be sent directly to the terminal -- since there isn't one.
3990 * If @TTF_BORROW@ is set, then the stream will not be closed by
3991 * @tty_close@. (This flag is ignored if @fp@ is null.)
3993 * If @beckends@ is provided, then it points to a vector of
3994 * @TTBK_...@ constants describing the backends to be tried in
3995 * order. The vector is terminated by @TTBK_END@; if this is
3996 * found, then a null pointer is returned.
3998 * A null control block pointer is valid for all @tty@
3999 * functions: most will just immediately report failure, but
4000 * there won't be any crashing.
4003 struct tty *tty_open(FILE *fp, unsigned f, const unsigned *backends)
4005 static const struct betab {
4006 const char *name; unsigned code;
4007 struct tty *(*init)(FILE */*fp*/);
4009 #ifdef HAVE_UNIBILIUM
4010 { "unibilium", TTBK_UNIBI, termunibi_init },
4012 #ifdef HAVE_TERMINFO
4013 { "terminfo", TTBK_TERMINFO, terminfo_init },
4016 { "termcap", TTBK_TERMCAP, termcap_init },
4018 { "ansi", TTBK_ANSI, ansi_init },
4022 const struct betab *bt;
4023 const char *config, *p, *l;
4024 struct tty *tty = 0;
4028 if (fp || !(f&TTF_OPEN))
4029 fpin = fp != stdout ? fp : isatty(STDIN_FILENO) ? stdin : 0;
4031 if (isatty(STDIN_FILENO)) fpin = stdin;
4033 if (isatty(STDOUT_FILENO)) { fp = stdout; f |= TTF_BORROW; }
4034 else if (isatty(STDERR_FILENO)) { fp = stderr; f |= TTF_BORROW; }
4036 fp = fopen("/dev/tty", "r+"); if (!fp) goto end;
4037 fpin = fp; f &= ~TTF_BORROW;
4041 config = getenv("MLIB_TTY_BACKENDS");
4043 l = config + strlen(config);
4046 if (config >= l) goto done_config;
4047 else if (!ISSPACE(*config)) break;
4050 for (p = config + 1; p < l && !ISSPACE(*p); p++);
4053 for (bt = betab; bt->name; bt++)
4054 if (STRNCMP(config, ==, bt->name, n) && !bt->name[n])
4056 debug("unknown backend `%.*s'", (int)n, config); goto next_config;
4058 tty = bt->init(fp); if (tty) goto found;
4059 debug("failed to initialize `%s'", bt->name);
4064 } else if (backends)
4066 for (bt = betab; bt->name; bt++)
4067 if (*backends == bt->code) goto found_bycode;
4068 debug("unknown backend code %u", *backends); goto next_code;
4070 tty = bt->init(fp); if (tty) goto found;
4071 debug("failed to initialize `%s'", bt->name);
4076 for (bt = betab; bt->name; bt++) {
4077 tty = bt->init(fp); if (tty) goto found;
4078 debug("failed to initialize `%s'", bt->name);
4081 debug("all backends failed"); goto end;
4083 debug("selected backend `%s'", bt->name);
4084 tty->fpin = fpin; tty->f = f; fp = 0;
4086 if (fp && !(f&TTF_BORROW)) fclose(fp);
4090 /* --- @tty_close@ --- *
4092 * Arguments: @struct tty *tty@ = control block pointer
4096 * Use: Closes a terminal, releasing the control block and any
4097 * resources it held. In particular, if the terminal was opened
4098 * without @TTF_BORROW@, then the output stream is closed.
4101 void tty_close(struct tty *tty)
4104 if (tty->fpout && !(tty->f&TTF_BORROW)) fclose(tty->fpout);
4105 tty->ops->release(tty); xfree(tty);
4109 /* --- @tty_resized@ --- *
4111 * Arguments: @struct tty *tty@ = control block pointer
4113 * Returns: Zero if the size hasn't changed, %$+1$% if the size has
4114 * changed, or %$-1$% on error.
4116 * Use: Update the terminal width and height. Call this after
4117 * receiving @SIGWINCH@, or otherwise periodically, to avoid
4121 int tty_resized(struct tty *tty)
4125 if (!tty || !tty->fpout) { errno = ENOTTY; return (-1); }
4126 else if (ioctl(fileno(tty->fpout), TIOCGWINSZ, &ws)) return (-1);
4127 else if (tty->wd == ws.ws_col && tty->ht == ws.ws_row) return (0);
4128 else { tty->wd = ws.ws_col; tty->ht = ws.ws_row; return (1); }
4131 /*----- Terminal operations -----------------------------------------------*/
4133 /* --- @tty_setattr@, @tty_setattrg@--- *
4135 * Arguments: @struct tty *tty@ = control block pointer
4136 * @const struct gprintf_ops *gops, void *go@ = output
4138 * @const struct tty_attr *a@ = pointer to attributes to set, or
4141 * Returns: Zero on success, %$-1$% on error.
4143 * Use: Set the indicated formatting attributes on following output.
4144 * If @a@ is null, then clear all attributes, just as if
4147 * If you only ever request attributes which are advertised in
4148 * the terminals capapbility masked, then you'll always get what
4149 * you asked for. Otherwise, the provided attributes will be
4150 * `clamped', i.e., modified so as to accommodate the terminal's
4151 * shortcomings. In simple cases, unsupported attributes may
4152 * just be dropped; but they can also be substituted, e.g.,
4153 * single underlining for double, or approximate colours for
4154 * unsupported colours.
4157 int tty_setattr(struct tty *tty, const struct tty_attr *a)
4161 if (!tty || !tty->fpout)
4164 clamp_attr(&aa, a, tty->acaps);
4165 return (tty->ops->setattr(tty, &file_printops, tty->fpout, &aa));
4169 int tty_setattrg(struct tty *tty,
4170 const struct gprintf_ops *gops, void *go,
4171 const struct tty_attr *a)
4178 clamp_attr(&aa, a, tty->acaps);
4179 return (tty->ops->setattr(tty, gops, go, &aa));
4183 /* --- @tty_setattrlist@, @tty_setattrlistg@ --- *
4185 * Arguments: @struct tty *tty@ = control block pointer
4186 * @const struct gprintf_ops *gops, void *go@ = output
4188 * @const struct tty_attrlist *aa@ = pointer to attribute list
4191 * Returns: Zero on success, %$-1$% on error.
4193 * Use: Search the list for an entry matching the terminal's
4194 * capabilities, i.e., @tty->acaps&a->cap_mask == a->cap_eq@.
4195 * The attributes in the first such entry are set, as if by
4198 * The list is terminated by an entry with @cap_mask == 0@ --
4199 * though it will be checked like any other before ending the
4200 * search. In particular, this means that an entry with
4201 * @cap_mask == cap_eq == 0@ is a `catch-all', and its
4202 * attributes will be set if no earlier matching entry could be
4203 * found, while an entry with @cap_mask == 0@ and @cap_eq != 0@
4204 * terminates the search without setting any attributes.
4207 int tty_setattrlist(struct tty *tty, const struct tty_attrlist *aa)
4209 if (!tty || !tty->fpout) return (-1);
4210 else return (tty_setattrlistg(tty, &file_printops, tty->fpout, aa));
4213 int tty_setattrlistg(struct tty *tty,
4214 const struct gprintf_ops *gops, void *go,
4215 const struct tty_attrlist *aa)
4217 if (!tty) return (-1);
4219 if ((tty->acaps&aa->cap_mask) == aa->cap_eq)
4220 return (tty->ops->setattr(tty, gops, go, &aa->attr));
4221 else if (!aa->cap_mask)
4225 /* --- @tty_setmodes@, @tty_setmodesg@ --- *
4227 * Arguments: @struct tty *tty@ = control block pointer
4228 * @const struct gprintf_ops *gops, void *go@ = output
4230 * @uint32 modes_bic, modes_xor@ = masks to apply to the modes
4233 * Returns: Zero on success, %$-1$% on error.
4235 * Use: Adjust the terminal display modes: specifically, the modes
4236 * are adjusted to be @(modes&~modes_bic) ^ modes_xor@.
4237 * Mode bits which aren't supported by the terminal are
4241 int tty_setmodes(struct tty *tty, uint32 modes_bic, uint32 modes_xor)
4243 if (!tty || !tty->fpout) return (-1);
4244 else return (tty->ops->setmodes(tty, &file_printops, tty->fpout,
4245 modes_bic, modes_xor));
4248 int tty_setmodesg(struct tty *tty,
4249 const struct gprintf_ops *gops, void *go,
4250 uint32 modes_bic, uint32 modes_xor)
4252 if (!tty) return (-1);
4253 else return (tty->ops->setmodes(tty, gops, go, modes_bic, modes_xor));
4256 /* --- @tty_move@, @tty_moveg@ --- *
4258 * Arguments: @struct tty *tty@ = control block pointer
4259 * @const struct gprintf_ops *gops, void *go@ = output
4261 * @unsigned orig@ = origin
4262 * @int y, x@ = new cursor position
4264 * Returns: Zero on success, %$-1$% on error.
4266 * Use: Move the cursor. Coordinates are numbered starting with 0.
4268 * The @y@ position is interpreted relative to the origin given
4269 * by @orig@: if @TTOF_YCUR@ is set, then the motion is relative
4270 * to the current cursor row; otherwise, it is relative to
4271 * the top `home' row. Similarly, the @x@ position is
4272 * interpreted relative to the current cursor column if
4273 * @TTOF_XCUR is set, otherwise relative to the leftmost
4276 * Not all terminals are capable of all kinds of motions:
4277 * @TTCF_ABSMV@ is set if absolute motion is possible, and
4278 * @TTCF_RELMV@ is set if relative motion is possible. The
4279 * @TTCF_MIXMV@ bit indicates that the combination of absolute-y
4280 * and relative-x motion is possible; note that the combination
4281 * of relative-y and absolute-x is always possible if relative
4282 * motion is possible at all.
4284 * The above notwithstanding, all terminals are assumed capable
4285 * of moving the cursor to the start of either the current line
4286 * @tty_move(tty, TTOF_YCUR | TTOF_XHOME, 0, 0)@, or of the next
4287 * line @tty_move(tty, TTOF_YCUR | TTOF_XHOME, +1, 0)@.
4290 int tty_move(struct tty *tty, unsigned orig, int y, int x)
4292 if (!tty || !tty->fpout) return (-1);
4293 else return (tty->ops->move(tty, &file_printops, tty->fpout, orig, y, x));
4296 int tty_moveg(struct tty *tty,
4297 const struct gprintf_ops *gops, void *go,
4298 unsigned orig, int y, int x)
4300 if (!tty) return (-1);
4301 else return (tty->ops->move(tty, gops, go, orig, y, x));
4304 /* --- @tty_repeat@, @tty_repeatg@ --- *
4306 * Arguments: @struct tty *tty@ = control block pointer
4307 * @const struct gprintf_ops *gops, void *go@ = output
4309 * @int ch@ = character to write
4310 * @unsigned n@ = number of copies
4312 * Returns: Zero on success, %$-1$% on error.
4314 * Use: Write @n@ copies of the character @ch@ to the terminal.
4315 * (Some terminals have a special control sequence for doing
4319 int tty_repeat(struct tty *tty, int ch, unsigned n)
4321 if (!tty || !tty->fpout) return (-1);
4322 else return (tty->ops->repeat(tty, &file_printops, tty->fpout, ch, n));
4325 int tty_repeatg(struct tty *tty,
4326 const struct gprintf_ops *gops, void *go,
4329 if (!tty) return (-1);
4330 else return (tty->ops->repeat(tty, gops, go, ch, n));
4333 /* --- @tty_erase@, @tty_eraseg@ --- *
4335 * Arguments: @struct tty *tty@ = control block pointer
4336 * @const struct gprintf_ops *gops, void *go@ = output
4338 * @unsigned f@ = flags
4340 * Returns: Zero on success, %$-1$% on error.
4342 * Use: Erase portions of the current line or the whole display.
4344 * If @TTEF_DSP@ is set, then the whole display is affected. If
4345 * @TTEF_BEGIN@ is set, then the display is erased starting from
4346 * the top left and ending at and including the cursor
4347 * position. If @TTEF_END@ is set, then the display is erased
4348 * starting from and including the cursor position, and ending
4349 * at the bottom right. If both flags are set, then,
4350 * additionally, the cursor is moved to its `home' position at
4353 * If @TTF_DSP@ is not set, then the current line is affected.
4354 * If @TTEF_BEGIN@ is set, then the line is erased starting from
4355 * the left and ending at and including the cursor position. If
4356 * @TTEF_END@ is set, then the line is erased starting from and
4357 * including the cursor position, and ending at the right hand
4360 * If the @TTCF_BGER@ capability is set, then the erased
4361 * positions take on the current background colour; otherwise,
4362 * they have the default background colour.
4365 int tty_erase(struct tty *tty, unsigned f)
4367 if (!tty || !tty->fpout) return (-1);
4368 else return (tty->ops->erase(tty, &file_printops, tty->fpout, f));
4371 int tty_eraseg(struct tty *tty,
4372 const struct gprintf_ops *gops, void *go,
4375 if (!tty) return (-1);
4376 else return (tty->ops->erase(tty, gops, go, f));
4379 /* --- @tty_erch@, @tty_erchg@ --- *
4381 * Arguments: @struct tty *tty@ = control block pointer
4382 * @const struct gprintf_ops *gops, void *go@ = output
4384 * @unsigned n@ = number of characters to erase
4386 * Returns: Zero on success, %$-1$% on error.
4388 * Use: Erase a number of characters, starting from and including the
4389 * current cursor position.
4391 * If the @TTCF_BGER@ capability is set, then the erased
4392 * positions take on the current background colour; otherwise,
4393 * they have the default background colour.
4396 int tty_erch(struct tty *tty, unsigned n)
4398 if (!tty || !tty->fpout) return (-1);
4399 else return (tty->ops->erch(tty, &file_printops, tty->fpout, n));
4402 int tty_erchg(struct tty *tty,
4403 const struct gprintf_ops *gops, void *go,
4406 if (!tty) return (-1);
4407 else return (tty->ops->erch(tty, gops, go, n));
4410 /* --- @tty_ins@, @tty_insg@ --- *
4412 * Arguments: @struct tty *tty@ = control block pointer
4413 * @const struct gprintf_ops *gops, void *go@ = output
4415 * @unsigned f@ = flags
4416 * @unsigned n@ = number of items to insert
4418 * Returns: Zero on success, %$-1$% on error.
4420 * Use: Insert a number of blank characters or lines.
4422 * If @TTIDF_LN@ is set, then insert @n@ blank lines above the
4423 * current line. The cursor must be at the far left of the
4426 * Otherwise, insert @n@ empty character spaces at the cursor
4430 int tty_ins(struct tty *tty, unsigned f, unsigned n)
4432 if (!tty || !tty->fpout) return (-1);
4433 else return (tty->ops->ins(tty, &file_printops, tty->fpout, f, n));
4436 int tty_insg(struct tty *tty,
4437 const struct gprintf_ops *gops, void *go,
4438 unsigned f, unsigned n)
4440 if (!tty) return (-1);
4441 else return (tty->ops->ins(tty, gops, go, f, n));
4444 /* --- @tty_inch@ --- *
4446 * Arguments: @struct tty *tty@ = control block pointer
4447 * @const struct gprintf_ops *gops, void *go@ = output
4449 * @int ch@ = character to insert
4451 * Returns: Zero on success, %$-1$% on error.
4453 * Use: Insert a single character.
4455 * If the @TTMF_INS@ mode is advertised, then insert mode must
4456 * be set before calling this function.
4459 int tty_inch(struct tty *tty, int ch)
4461 if (!tty || !tty->fpout) return (-1);
4462 else return (tty->ops->inch(tty, &file_printops, tty->fpout, ch));
4465 int tty_inchg(struct tty *tty,
4466 const struct gprintf_ops *gops, void *go,
4469 if (!tty) return (-1);
4470 else return (tty->ops->inch(tty, gops, go, ch));
4473 /* --- @tty_del@, @tty_delg@ --- *
4475 * Arguments: @struct tty *tty@ = control block pointer
4476 * @const struct gprintf_ops *gops, void *go@ = output
4478 * @unsigned f@ = flags
4479 * @unsigned n@ = number of items to delete
4481 * Returns: Zero on success, %$-1$% on error.
4483 * Use: Delete a number of characters or lines.
4485 * If @TTIDF_LN@ is set, then delete @n@ blank lines, starting
4486 * with the current line line. The cursor must be at the far
4489 * Otherwise, delete @n@ characters at the cursor position.
4492 int tty_del(struct tty *tty, unsigned f, unsigned n)
4494 if (!tty || !tty->fpout) return (-1);
4495 else return (tty->ops->del(tty, &file_printops, tty->fpout, f, n));
4498 int tty_delg(struct tty *tty,
4499 const struct gprintf_ops *gops, void *go,
4500 unsigned f, unsigned n)
4502 if (!tty) return (-1);
4503 else return (tty->ops->del(tty, gops, go, f, n));
4506 /* --- @tty_restore@, @tty_restoreg@ --- *
4508 * Arguments: @struct tty *tty@ = control block pointer
4509 * @const struct gprintf_ops *gops, void *go@ = output
4511 * @const struct tty_state *st@ = state to restore
4513 * Returns: Zero on success, %$-1$% on error.
4515 * Use: Restore the terminal modes and attributes to match a
4516 * state previously captured by copying @tty->st@.
4519 int tty_restore(struct tty *tty, const struct tty_state *st)
4521 if (!tty || !tty->fpout) return (-1);
4522 else return (tty_restoreg(tty, &file_printops, tty->fpout, st));
4525 int tty_restoreg(struct tty *tty,
4526 const struct gprintf_ops *gops, void *go,
4527 const struct tty_state *st)
4532 tty->ops->setmodes(tty, gops, go, MASK32, st->modes) ||
4533 tty->ops->setattr(tty, gops, go, &st->attr))
4534 { rc = -1; goto end; }
4540 /*----- That's all, folks -------------------------------------------------*/