+int
+ttynew(char *line, char *cmd, char *out, char **args)
+{
+ int m, s;
+
+ if (out) {
+ term.mode |= MODE_PRINT;
+ iofd = (!strcmp(out, "-")) ?
+ 1 : open(out, O_WRONLY | O_CREAT, 0666);
+ if (iofd < 0) {
+ fprintf(stderr, "Error opening %s:%s\n",
+ out, strerror(errno));
+ }
+ }
+
+ if (line) {
+ if ((cmdfd = open(line, O_RDWR)) < 0)
+ die("open line '%s' failed: %s\n",
+ line, strerror(errno));
+ dup2(cmdfd, 0);
+ stty(args);
+ return cmdfd;
+ }
+
+ /* seems to work fine on linux, openbsd and freebsd */
+ if (openpty(&m, &s, NULL, NULL, NULL) < 0)
+ die("openpty failed: %s\n", strerror(errno));
+
+ switch (pid = fork()) {
+ case -1:
+ die("fork failed: %s\n", strerror(errno));
+ break;
+ case 0:
+ close(iofd);
+ setsid(); /* create a new process group */
+ dup2(s, 0);
+ dup2(s, 1);
+ dup2(s, 2);
+ if (ioctl(s, TIOCSCTTY, NULL) < 0)
+ die("ioctl TIOCSCTTY failed: %s\n", strerror(errno));
+ close(s);
+ close(m);
+#ifdef __OpenBSD__
+ if (pledge("stdio getpw proc exec", NULL) == -1)
+ die("pledge\n");
+#endif
+ execsh(cmd, args);
+ break;
+ default:
+#ifdef __OpenBSD__
+ if (pledge("stdio rpath tty proc", NULL) == -1)
+ die("pledge\n");
+#endif
+ close(s);
+ cmdfd = m;
+ signal(SIGCHLD, sigchld);
+ break;
+ }
+ return cmdfd;
+}
+
+size_t
+ttyread(void)
+{
+ static char buf[BUFSIZ];
+ static int buflen = 0;
+ int ret, written;
+
+ /* append read bytes to unprocessed bytes */
+ ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);
+
+ switch (ret) {
+ case 0:
+ exit(0);
+ case -1:
+ die("couldn't read from shell: %s\n", strerror(errno));
+ default:
+ buflen += ret;
+ written = twrite(buf, buflen, 0);
+ buflen -= written;
+ /* keep any incomplete UTF-8 byte sequence for the next call */
+ if (buflen > 0)
+ memmove(buf, buf + written, buflen);
+ return ret;
+ }
+}
+
+void
+ttywrite(const char *s, size_t n, int may_echo)
+{
+ const char *next;
+
+ if (may_echo && IS_SET(MODE_ECHO))
+ twrite(s, n, 1);
+
+ if (!IS_SET(MODE_CRLF)) {
+ ttywriteraw(s, n);
+ return;
+ }
+
+ /* This is similar to how the kernel handles ONLCR for ttys */
+ while (n > 0) {
+ if (*s == '\r') {
+ next = s + 1;
+ ttywriteraw("\r\n", 2);
+ } else {
+ next = memchr(s, '\r', n);
+ DEFAULT(next, s + n);
+ ttywriteraw(s, next - s);
+ }
+ n -= next - s;
+ s = next;
+ }
+}
+
+void
+ttywriteraw(const char *s, size_t n)
+{
+ fd_set wfd, rfd;
+ ssize_t r;
+ size_t lim = 256;
+
+ /*
+ * Remember that we are using a pty, which might be a modem line.
+ * Writing too much will clog the line. That's why we are doing this
+ * dance.
+ * FIXME: Migrate the world to Plan 9.
+ */
+ while (n > 0) {
+ FD_ZERO(&wfd);
+ FD_ZERO(&rfd);
+ FD_SET(cmdfd, &wfd);
+ FD_SET(cmdfd, &rfd);
+
+ /* Check if we can write. */
+ if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) {
+ if (errno == EINTR)
+ continue;
+ die("select failed: %s\n", strerror(errno));
+ }
+ if (FD_ISSET(cmdfd, &wfd)) {
+ /*
+ * Only write the bytes written by ttywrite() or the
+ * default of 256. This seems to be a reasonable value
+ * for a serial line. Bigger values might clog the I/O.
+ */
+ if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0)
+ goto write_error;
+ if (r < n) {
+ /*
+ * We weren't able to write out everything.
+ * This means the buffer is getting full
+ * again. Empty it.
+ */
+ if (n < lim)
+ lim = ttyread();
+ n -= r;
+ s += r;
+ } else {
+ /* All bytes have been written. */
+ break;
+ }
+ }
+ if (FD_ISSET(cmdfd, &rfd))
+ lim = ttyread();
+ }
+ return;
+
+write_error:
+ die("write error on tty: %s\n", strerror(errno));
+}
+
+void
+ttyresize(int tw, int th)
+{
+ struct winsize w;
+
+ w.ws_row = term.row;
+ w.ws_col = term.col;
+ w.ws_xpixel = tw;
+ w.ws_ypixel = th;
+ if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0)
+ fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno));
+}
+
+void
+ttyhangup()
+{
+ /* Send SIGHUP to shell */
+ kill(pid, SIGHUP);
+}
+
+int
+tattrset(int attr)
+{
+ int i, j;
+
+ for (i = 0; i < term.row-1; i++) {
+ for (j = 0; j < term.col-1; j++) {
+ if (term.line[i][j].mode & attr)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+tsetdirt(int top, int bot)
+{
+ int i;
+
+ LIMIT(top, 0, term.row-1);
+ LIMIT(bot, 0, term.row-1);
+
+ for (i = top; i <= bot; i++)
+ term.dirty[i] = 1;
+}
+
+void
+tsetdirtattr(int attr)
+{
+ int i, j;
+
+ for (i = 0; i < term.row-1; i++) {
+ for (j = 0; j < term.col-1; j++) {
+ if (term.line[i][j].mode & attr) {
+ tsetdirt(i, i);
+ break;
+ }
+ }
+ }
+}
+
+void
+tfulldirt(void)
+{
+ tsetdirt(0, term.row-1);
+}
+
+void
+tcursor(int mode)
+{
+ static TCursor c[2];
+ int alt = IS_SET(MODE_ALTSCREEN);
+
+ if (mode == CURSOR_SAVE) {
+ c[alt] = term.c;
+ } else if (mode == CURSOR_LOAD) {
+ term.c = c[alt];
+ tmoveto(c[alt].x, c[alt].y);
+ }
+}
+
+void
+treset(void)
+{
+ uint i;
+
+ term.c = (TCursor){{
+ .mode = ATTR_NULL,
+ .fg = defaultfg,
+ .bg = defaultbg
+ }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
+
+ memset(term.tabs, 0, term.col * sizeof(*term.tabs));
+ for (i = tabspaces; i < term.col; i += tabspaces)
+ term.tabs[i] = 1;
+ term.top = 0;
+ term.bot = term.row - 1;
+ term.mode = MODE_WRAP|MODE_UTF8;
+ memset(term.trantbl, CS_USA, sizeof(term.trantbl));
+ term.charset = 0;
+
+ for (i = 0; i < 2; i++) {
+ tmoveto(0, 0);
+ tcursor(CURSOR_SAVE);
+ tclearregion(0, 0, term.col-1, term.row-1);
+ tswapscreen();
+ }
+}
+
+void
+tnew(int col, int row)
+{
+ term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
+ tresize(col, row);
+ treset();
+}
+
+void
+tswapscreen(void)
+{
+ Line *tmp = term.line;
+
+ term.line = term.alt;
+ term.alt = tmp;
+ term.mode ^= MODE_ALTSCREEN;
+ tfulldirt();
+}
+
+void
+tscrolldown(int orig, int n)
+{
+ int i;
+ Line temp;
+
+ LIMIT(n, 0, term.bot-orig+1);
+
+ tsetdirt(orig, term.bot-n);
+ tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+
+ for (i = term.bot; i >= orig+n; i--) {
+ temp = term.line[i];
+ term.line[i] = term.line[i-n];
+ term.line[i-n] = temp;
+ }
+
+ selscroll(orig, n);
+}
+
+void
+tscrollup(int orig, int n)
+{
+ int i;
+ Line temp;
+
+ LIMIT(n, 0, term.bot-orig+1);
+
+ tclearregion(0, orig, term.col-1, orig+n-1);
+ tsetdirt(orig+n, term.bot);
+
+ for (i = orig; i <= term.bot-n; i++) {
+ temp = term.line[i];
+ term.line[i] = term.line[i+n];
+ term.line[i+n] = temp;
+ }
+
+ selscroll(orig, -n);
+}
+
+void
+selscroll(int orig, int n)
+{
+ if (sel.ob.x == -1)
+ return;
+
+ if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
+ selclear();
+ } else if (BETWEEN(sel.nb.y, orig, term.bot)) {
+ sel.ob.y += n;
+ sel.oe.y += n;
+ if (sel.ob.y < term.top || sel.ob.y > term.bot ||
+ sel.oe.y < term.top || sel.oe.y > term.bot) {
+ selclear();
+ } else {
+ selnormalize();
+ }
+ }
+}
+
+void
+tnewline(int first_col)
+{
+ int y = term.c.y;
+
+ if (y == term.bot) {
+ tscrollup(term.top, 1);
+ } else {
+ y++;
+ }
+ tmoveto(first_col ? 0 : term.c.x, y);
+}
+
+void
+csiparse(void)
+{
+ char *p = csiescseq.buf, *np;
+ long int v;
+
+ csiescseq.narg = 0;
+ if (*p == '?') {
+ csiescseq.priv = 1;
+ p++;
+ }
+
+ csiescseq.buf[csiescseq.len] = '\0';
+ while (p < csiescseq.buf+csiescseq.len) {
+ np = NULL;
+ v = strtol(p, &np, 10);
+ if (np == p)
+ v = 0;
+ if (v == LONG_MAX || v == LONG_MIN)
+ v = -1;
+ csiescseq.arg[csiescseq.narg++] = v;
+ p = np;
+ if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
+ break;
+ p++;
+ }
+ csiescseq.mode[0] = *p++;
+ csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0';
+}
+
+/* for absolute user moves, when decom is set */
+void
+tmoveato(int x, int y)
+{
+ tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0));
+}
+
+void
+tmoveto(int x, int y)
+{
+ int miny, maxy;
+
+ if (term.c.state & CURSOR_ORIGIN) {
+ miny = term.top;
+ maxy = term.bot;
+ } else {
+ miny = 0;
+ maxy = term.row - 1;
+ }
+ term.c.state &= ~CURSOR_WRAPNEXT;
+ term.c.x = LIMIT(x, 0, term.col-1);
+ term.c.y = LIMIT(y, miny, maxy);
+}
+
+void
+tsetchar(Rune u, Glyph *attr, int x, int y)
+{
+ static char *vt100_0[62] = { /* 0x41 - 0x7e */
+ "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
+ 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
+ "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */
+ "", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */
+ "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
+ "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
+ };
+
+ /*
+ * The table is proudly stolen from rxvt.
+ */
+ if (term.trantbl[term.charset] == CS_GRAPHIC0 &&
+ BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
+ utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
+
+ if (term.line[y][x].mode & ATTR_WIDE) {
+ if (x+1 < term.col) {
+ term.line[y][x+1].u = ' ';
+ term.line[y][x+1].mode &= ~ATTR_WDUMMY;
+ }
+ } else if (term.line[y][x].mode & ATTR_WDUMMY) {
+ term.line[y][x-1].u = ' ';
+ term.line[y][x-1].mode &= ~ATTR_WIDE;
+ }
+
+ term.dirty[y] = 1;
+ term.line[y][x] = *attr;
+ term.line[y][x].u = u;
+}
+
+void
+tclearregion(int x1, int y1, int x2, int y2)
+{
+ int x, y, temp;
+ Glyph *gp;
+
+ if (x1 > x2)
+ temp = x1, x1 = x2, x2 = temp;
+ if (y1 > y2)
+ temp = y1, y1 = y2, y2 = temp;
+
+ LIMIT(x1, 0, term.col-1);
+ LIMIT(x2, 0, term.col-1);
+ LIMIT(y1, 0, term.row-1);
+ LIMIT(y2, 0, term.row-1);
+
+ for (y = y1; y <= y2; y++) {
+ term.dirty[y] = 1;
+ for (x = x1; x <= x2; x++) {
+ gp = &term.line[y][x];
+ if (selected(x, y))
+ selclear();
+ gp->fg = term.c.attr.fg;
+ gp->bg = term.c.attr.bg;
+ gp->mode = 0;
+ gp->u = ' ';
+ }
+ }
+}
+
+void
+tdeletechar(int n)
+{
+ int dst, src, size;
+ Glyph *line;
+
+ LIMIT(n, 0, term.col - term.c.x);
+
+ dst = term.c.x;
+ src = term.c.x + n;
+ size = term.col - src;
+ line = term.line[term.c.y];
+
+ memmove(&line[dst], &line[src], size * sizeof(Glyph));
+ tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
+}
+
+void
+tinsertblank(int n)
+{
+ int dst, src, size;
+ Glyph *line;
+
+ LIMIT(n, 0, term.col - term.c.x);
+
+ dst = term.c.x + n;
+ src = term.c.x;
+ size = term.col - dst;
+ line = term.line[term.c.y];
+
+ memmove(&line[dst], &line[src], size * sizeof(Glyph));
+ tclearregion(src, term.c.y, dst - 1, term.c.y);
+}
+
+void
+tinsertblankline(int n)
+{
+ if (BETWEEN(term.c.y, term.top, term.bot))
+ tscrolldown(term.c.y, n);
+}
+
+void
+tdeleteline(int n)
+{
+ if (BETWEEN(term.c.y, term.top, term.bot))
+ tscrollup(term.c.y, n);
+}
+
+int32_t
+tdefcolor(int *attr, int *npar, int l)
+{
+ int32_t idx = -1;
+ uint r, g, b;
+
+ switch (attr[*npar + 1]) {
+ case 2: /* direct color in RGB space */
+ if (*npar + 4 >= l) {
+ fprintf(stderr,
+ "erresc(38): Incorrect number of parameters (%d)\n",
+ *npar);
+ break;
+ }
+ r = attr[*npar + 2];
+ g = attr[*npar + 3];
+ b = attr[*npar + 4];
+ *npar += 4;
+ if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255))
+ fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n",
+ r, g, b);
+ else
+ idx = TRUECOLOR(r, g, b);
+ break;
+ case 5: /* indexed color */
+ if (*npar + 2 >= l) {
+ fprintf(stderr,
+ "erresc(38): Incorrect number of parameters (%d)\n",
+ *npar);
+ break;
+ }
+ *npar += 2;
+ if (!BETWEEN(attr[*npar], 0, 255))
+ fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]);
+ else
+ idx = attr[*npar];
+ break;
+ case 0: /* implemented defined (only foreground) */
+ case 1: /* transparent */
+ case 3: /* direct color in CMY space */
+ case 4: /* direct color in CMYK space */
+ default:
+ fprintf(stderr,
+ "erresc(38): gfx attr %d unknown\n", attr[*npar]);
+ break;
+ }
+
+ return idx;
+}
+
+void
+tsetattr(int *attr, int l)
+{
+ int i;
+ int32_t idx;
+
+ for (i = 0; i < l; i++) {
+ switch (attr[i]) {
+ case 0:
+ term.c.attr.mode &= ~(
+ ATTR_BOLD |
+ ATTR_FAINT |
+ ATTR_ITALIC |
+ ATTR_UNDERLINE |
+ ATTR_BLINK |
+ ATTR_REVERSE |
+ ATTR_INVISIBLE |
+ ATTR_STRUCK );
+ term.c.attr.fg = defaultfg;
+ term.c.attr.bg = defaultbg;
+ break;
+ case 1:
+ term.c.attr.mode |= ATTR_BOLD;
+ break;
+ case 2:
+ term.c.attr.mode |= ATTR_FAINT;
+ break;
+ case 3:
+ term.c.attr.mode |= ATTR_ITALIC;
+ break;
+ case 4:
+ term.c.attr.mode |= ATTR_UNDERLINE;
+ break;
+ case 5: /* slow blink */
+ /* FALLTHROUGH */
+ case 6: /* rapid blink */
+ term.c.attr.mode |= ATTR_BLINK;
+ break;
+ case 7:
+ term.c.attr.mode |= ATTR_REVERSE;
+ break;
+ case 8:
+ term.c.attr.mode |= ATTR_INVISIBLE;
+ break;
+ case 9:
+ term.c.attr.mode |= ATTR_STRUCK;
+ break;
+ case 22:
+ term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT);
+ break;
+ case 23:
+ term.c.attr.mode &= ~ATTR_ITALIC;
+ break;
+ case 24:
+ term.c.attr.mode &= ~ATTR_UNDERLINE;
+ break;
+ case 25:
+ term.c.attr.mode &= ~ATTR_BLINK;
+ break;
+ case 27:
+ term.c.attr.mode &= ~ATTR_REVERSE;
+ break;
+ case 28:
+ term.c.attr.mode &= ~ATTR_INVISIBLE;
+ break;
+ case 29:
+ term.c.attr.mode &= ~ATTR_STRUCK;
+ break;
+ case 38:
+ if ((idx = tdefcolor(attr, &i, l)) >= 0)
+ term.c.attr.fg = idx;
+ break;
+ case 39:
+ term.c.attr.fg = defaultfg;
+ break;
+ case 48:
+ if ((idx = tdefcolor(attr, &i, l)) >= 0)
+ term.c.attr.bg = idx;
+ break;
+ case 49:
+ term.c.attr.bg = defaultbg;
+ break;
+ default:
+ if (BETWEEN(attr[i], 30, 37)) {
+ term.c.attr.fg = attr[i] - 30;
+ } else if (BETWEEN(attr[i], 40, 47)) {
+ term.c.attr.bg = attr[i] - 40;
+ } else if (BETWEEN(attr[i], 90, 97)) {
+ term.c.attr.fg = attr[i] - 90 + 8;
+ } else if (BETWEEN(attr[i], 100, 107)) {
+ term.c.attr.bg = attr[i] - 100 + 8;
+ } else {
+ fprintf(stderr,
+ "erresc(default): gfx attr %d unknown\n",
+ attr[i]);
+ csidump();
+ }
+ break;
+ }
+ }
+}
+
+void
+tsetscroll(int t, int b)
+{
+ int temp;
+
+ LIMIT(t, 0, term.row-1);
+ LIMIT(b, 0, term.row-1);
+ if (t > b) {
+ temp = t;
+ t = b;
+ b = temp;
+ }
+ term.top = t;
+ term.bot = b;
+}
+
+void
+tsetmode(int priv, int set, int *args, int narg)
+{
+ int alt, *lim;
+
+ for (lim = args + narg; args < lim; ++args) {
+ if (priv) {
+ switch (*args) {
+ case 1: /* DECCKM -- Cursor key */
+ xsetmode(set, MODE_APPCURSOR);
+ break;
+ case 5: /* DECSCNM -- Reverse video */
+ xsetmode(set, MODE_REVERSE);
+ break;
+ case 6: /* DECOM -- Origin */
+ MODBIT(term.c.state, set, CURSOR_ORIGIN);
+ tmoveato(0, 0);
+ break;
+ case 7: /* DECAWM -- Auto wrap */
+ MODBIT(term.mode, set, MODE_WRAP);
+ break;
+ case 0: /* Error (IGNORED) */
+ case 2: /* DECANM -- ANSI/VT52 (IGNORED) */
+ case 3: /* DECCOLM -- Column (IGNORED) */
+ case 4: /* DECSCLM -- Scroll (IGNORED) */
+ case 8: /* DECARM -- Auto repeat (IGNORED) */
+ case 18: /* DECPFF -- Printer feed (IGNORED) */
+ case 19: /* DECPEX -- Printer extent (IGNORED) */
+ case 42: /* DECNRCM -- National characters (IGNORED) */
+ case 12: /* att610 -- Start blinking cursor (IGNORED) */
+ break;
+ case 25: /* DECTCEM -- Text Cursor Enable Mode */
+ xsetmode(!set, MODE_HIDE);
+ break;
+ case 9: /* X10 mouse compatibility mode */
+ xsetpointermotion(0);
+ xsetmode(0, MODE_MOUSE);
+ xsetmode(set, MODE_MOUSEX10);
+ break;
+ case 1000: /* 1000: report button press */
+ xsetpointermotion(0);
+ xsetmode(0, MODE_MOUSE);
+ xsetmode(set, MODE_MOUSEBTN);
+ break;
+ case 1002: /* 1002: report motion on button press */
+ xsetpointermotion(0);
+ xsetmode(0, MODE_MOUSE);
+ xsetmode(set, MODE_MOUSEMOTION);
+ break;
+ case 1003: /* 1003: enable all mouse motions */
+ xsetpointermotion(set);
+ xsetmode(0, MODE_MOUSE);
+ xsetmode(set, MODE_MOUSEMANY);
+ break;
+ case 1004: /* 1004: send focus events to tty */
+ xsetmode(set, MODE_FOCUS);
+ break;
+ case 1006: /* 1006: extended reporting mode */
+ xsetmode(set, MODE_MOUSESGR);
+ break;
+ case 1034:
+ xsetmode(set, MODE_8BIT);
+ break;
+ case 1049: /* swap screen & set/restore cursor as xterm */
+ if (!allowaltscreen)
+ break;
+ tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
+ /* FALLTHROUGH */
+ case 47: /* swap screen */
+ case 1047:
+ if (!allowaltscreen)
+ break;
+ alt = IS_SET(MODE_ALTSCREEN);
+ if (alt) {
+ tclearregion(0, 0, term.col-1,
+ term.row-1);
+ }
+ if (set ^ alt) /* set is always 1 or 0 */
+ tswapscreen();
+ if (*args != 1049)
+ break;
+ /* FALLTHROUGH */
+ case 1048:
+ tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
+ break;
+ case 2004: /* 2004: bracketed paste mode */
+ xsetmode(set, MODE_BRCKTPASTE);
+ break;
+ /* Not implemented mouse modes. See comments there. */
+ case 1001: /* mouse highlight mode; can hang the
+ terminal by design when implemented. */
+ case 1005: /* UTF-8 mouse mode; will confuse
+ applications not supporting UTF-8
+ and luit. */
+ case 1015: /* urxvt mangled mouse mode; incompatible
+ and can be mistaken for other control
+ codes. */
+ break;
+ default:
+ fprintf(stderr,
+ "erresc: unknown private set/reset mode %d\n",
+ *args);
+ break;
+ }
+ } else {
+ switch (*args) {
+ case 0: /* Error (IGNORED) */
+ break;
+ case 2:
+ xsetmode(set, MODE_KBDLOCK);
+ break;
+ case 4: /* IRM -- Insertion-replacement */
+ MODBIT(term.mode, set, MODE_INSERT);
+ break;
+ case 12: /* SRM -- Send/Receive */
+ MODBIT(term.mode, !set, MODE_ECHO);
+ break;
+ case 20: /* LNM -- Linefeed/new line */
+ MODBIT(term.mode, set, MODE_CRLF);
+ break;
+ default:
+ fprintf(stderr,
+ "erresc: unknown set/reset mode %d\n",
+ *args);
+ break;
+ }
+ }
+ }
+}
+
+void
+csihandle(void)
+{
+ char buf[40];
+ int len;
+
+ switch (csiescseq.mode[0]) {