/* $NetBSD: tty.c,v 1.68 2018/12/02 16:58:13 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)tty.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: tty.c,v 1.68 2018/12/02 16:58:13 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * tty.c: tty interface stuff */ #include #include #include /* for abort */ #include #include /* for ffs */ #include /* for isatty */ #include "el.h" #include "fcns.h" #include "parse.h" typedef struct ttymodes_t { const char *m_name; unsigned int m_value; int m_type; } ttymodes_t; typedef struct ttymap_t { wint_t nch, och; /* Internal and termio rep of chars */ el_action_t bind[3]; /* emacs, vi, and vi-cmd */ } ttymap_t; static const ttyperm_t ttyperm = { { {"iflag:", ICRNL, (INLCR | IGNCR)}, {"oflag:", (OPOST | ONLCR), ONLRET}, {"cflag:", 0, 0}, {"lflag:", (ISIG | ICANON | ECHO | ECHOE | ECHOCTL | IEXTEN), (NOFLSH | ECHONL | EXTPROC | FLUSHO)}, {"chars:", 0, 0}, }, { {"iflag:", (INLCR | ICRNL), IGNCR}, {"oflag:", (OPOST | ONLCR), ONLRET}, {"cflag:", 0, 0}, {"lflag:", ISIG, (NOFLSH | ICANON | ECHO | ECHOK | ECHONL | EXTPROC | IEXTEN | FLUSHO)}, {"chars:", (C_SH(C_MIN) | C_SH(C_TIME) | C_SH(C_SWTCH) | C_SH(C_DSWTCH) | C_SH(C_SUSP) | C_SH(C_DSUSP) | C_SH(C_EOL) | C_SH(C_DISCARD) | C_SH(C_PGOFF) | C_SH(C_PAGE) | C_SH(C_STATUS)), 0} }, { {"iflag:", 0, IXON | IXOFF | INLCR | ICRNL}, {"oflag:", 0, 0}, {"cflag:", 0, 0}, {"lflag:", 0, ISIG | IEXTEN}, {"chars:", 0, 0}, } }; static const ttychar_t ttychar = { { CINTR, CQUIT, CERASE, CKILL, CEOF, CEOL, CEOL2, CSWTCH, CDSWTCH, CERASE2, CSTART, CSTOP, CWERASE, CSUSP, CDSUSP, CREPRINT, CDISCARD, CLNEXT, CSTATUS, CPAGE, CPGOFF, CKILL2, CBRK, CMIN, CTIME }, { CINTR, CQUIT, CERASE, CKILL, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, CERASE2, CSTART, CSTOP, _POSIX_VDISABLE, CSUSP, _POSIX_VDISABLE, _POSIX_VDISABLE, CDISCARD, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; static const ttymap_t tty_map[] = { #ifdef VERASE {C_ERASE, VERASE, {EM_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}}, #endif /* VERASE */ #ifdef VERASE2 {C_ERASE2, VERASE2, {EM_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}}, #endif /* VERASE2 */ #ifdef VKILL {C_KILL, VKILL, {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}}, #endif /* VKILL */ #ifdef VKILL2 {C_KILL2, VKILL2, {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}}, #endif /* VKILL2 */ #ifdef VEOF {C_EOF, VEOF, {EM_DELETE_OR_LIST, VI_LIST_OR_EOF, ED_UNASSIGNED}}, #endif /* VEOF */ #ifdef VWERASE {C_WERASE, VWERASE, {ED_DELETE_PREV_WORD, ED_DELETE_PREV_WORD, ED_PREV_WORD}}, #endif /* VWERASE */ #ifdef VREPRINT {C_REPRINT, VREPRINT, {ED_REDISPLAY, ED_INSERT, ED_REDISPLAY}}, #endif /* VREPRINT */ #ifdef VLNEXT {C_LNEXT, VLNEXT, {ED_QUOTED_INSERT, ED_QUOTED_INSERT, ED_UNASSIGNED}}, #endif /* VLNEXT */ {(wint_t)-1, (wint_t)-1, {ED_UNASSIGNED, ED_UNASSIGNED, ED_UNASSIGNED}} }; static const ttymodes_t ttymodes[] = { #ifdef IGNBRK {"ignbrk", IGNBRK, MD_INP}, #endif /* IGNBRK */ #ifdef BRKINT {"brkint", BRKINT, MD_INP}, #endif /* BRKINT */ #ifdef IGNPAR {"ignpar", IGNPAR, MD_INP}, #endif /* IGNPAR */ #ifdef PARMRK {"parmrk", PARMRK, MD_INP}, #endif /* PARMRK */ #ifdef INPCK {"inpck", INPCK, MD_INP}, #endif /* INPCK */ #ifdef ISTRIP {"istrip", ISTRIP, MD_INP}, #endif /* ISTRIP */ #ifdef INLCR {"inlcr", INLCR, MD_INP}, #endif /* INLCR */ #ifdef IGNCR {"igncr", IGNCR, MD_INP}, #endif /* IGNCR */ #ifdef ICRNL {"icrnl", ICRNL, MD_INP}, #endif /* ICRNL */ #ifdef IUCLC {"iuclc", IUCLC, MD_INP}, #endif /* IUCLC */ #ifdef IXON {"ixon", IXON, MD_INP}, #endif /* IXON */ #ifdef IXANY {"ixany", IXANY, MD_INP}, #endif /* IXANY */ #ifdef IXOFF {"ixoff", IXOFF, MD_INP}, #endif /* IXOFF */ #ifdef IMAXBEL {"imaxbel", IMAXBEL, MD_INP}, #endif /* IMAXBEL */ #ifdef OPOST {"opost", OPOST, MD_OUT}, #endif /* OPOST */ #ifdef OLCUC {"olcuc", OLCUC, MD_OUT}, #endif /* OLCUC */ #ifdef ONLCR {"onlcr", ONLCR, MD_OUT}, #endif /* ONLCR */ #ifdef OCRNL {"ocrnl", OCRNL, MD_OUT}, #endif /* OCRNL */ #ifdef ONOCR {"onocr", ONOCR, MD_OUT}, #endif /* ONOCR */ #ifdef ONOEOT {"onoeot", ONOEOT, MD_OUT}, #endif /* ONOEOT */ #ifdef ONLRET {"onlret", ONLRET, MD_OUT}, #endif /* ONLRET */ #ifdef OFILL {"ofill", OFILL, MD_OUT}, #endif /* OFILL */ #ifdef OFDEL {"ofdel", OFDEL, MD_OUT}, #endif /* OFDEL */ #ifdef NLDLY {"nldly", NLDLY, MD_OUT}, #endif /* NLDLY */ #ifdef CRDLY {"crdly", CRDLY, MD_OUT}, #endif /* CRDLY */ #ifdef TABDLY {"tabdly", TABDLY, MD_OUT}, #endif /* TABDLY */ #ifdef XTABS {"xtabs", XTABS, MD_OUT}, #endif /* XTABS */ #ifdef BSDLY {"bsdly", BSDLY, MD_OUT}, #endif /* BSDLY */ #ifdef VTDLY {"vtdly", VTDLY, MD_OUT}, #endif /* VTDLY */ #ifdef FFDLY {"ffdly", FFDLY, MD_OUT}, #endif /* FFDLY */ #ifdef PAGEOUT {"pageout", PAGEOUT, MD_OUT}, #endif /* PAGEOUT */ #ifdef WRAP {"wrap", WRAP, MD_OUT}, #endif /* WRAP */ #ifdef CIGNORE {"cignore", CIGNORE, MD_CTL}, #endif /* CBAUD */ #ifdef CBAUD {"cbaud", CBAUD, MD_CTL}, #endif /* CBAUD */ #ifdef CSTOPB {"cstopb", CSTOPB, MD_CTL}, #endif /* CSTOPB */ #ifdef CREAD {"cread", CREAD, MD_CTL}, #endif /* CREAD */ #ifdef PARENB {"parenb", PARENB, MD_CTL}, #endif /* PARENB */ #ifdef PARODD {"parodd", PARODD, MD_CTL}, #endif /* PARODD */ #ifdef HUPCL {"hupcl", HUPCL, MD_CTL}, #endif /* HUPCL */ #ifdef CLOCAL {"clocal", CLOCAL, MD_CTL}, #endif /* CLOCAL */ #ifdef LOBLK {"loblk", LOBLK, MD_CTL}, #endif /* LOBLK */ #ifdef CIBAUD {"cibaud", CIBAUD, MD_CTL}, #endif /* CIBAUD */ #ifdef CRTSCTS #ifdef CCTS_OFLOW {"ccts_oflow", CCTS_OFLOW, MD_CTL}, #else {"crtscts", CRTSCTS, MD_CTL}, #endif /* CCTS_OFLOW */ #endif /* CRTSCTS */ #ifdef CRTS_IFLOW {"crts_iflow", CRTS_IFLOW, MD_CTL}, #endif /* CRTS_IFLOW */ #ifdef CDTRCTS {"cdtrcts", CDTRCTS, MD_CTL}, #endif /* CDTRCTS */ #ifdef MDMBUF {"mdmbuf", MDMBUF, MD_CTL}, #endif /* MDMBUF */ #ifdef RCV1EN {"rcv1en", RCV1EN, MD_CTL}, #endif /* RCV1EN */ #ifdef XMT1EN {"xmt1en", XMT1EN, MD_CTL}, #endif /* XMT1EN */ #ifdef ISIG {"isig", ISIG, MD_LIN}, #endif /* ISIG */ #ifdef ICANON {"icanon", ICANON, MD_LIN}, #endif /* ICANON */ #ifdef XCASE {"xcase", XCASE, MD_LIN}, #endif /* XCASE */ #ifdef ECHO {"echo", ECHO, MD_LIN}, #endif /* ECHO */ #ifdef ECHOE {"echoe", ECHOE, MD_LIN}, #endif /* ECHOE */ #ifdef ECHOK {"echok", ECHOK, MD_LIN}, #endif /* ECHOK */ #ifdef ECHONL {"echonl", ECHONL, MD_LIN}, #endif /* ECHONL */ #ifdef NOFLSH {"noflsh", NOFLSH, MD_LIN}, #endif /* NOFLSH */ #ifdef TOSTOP {"tostop", TOSTOP, MD_LIN}, #endif /* TOSTOP */ #ifdef ECHOCTL {"echoctl", ECHOCTL, MD_LIN}, #endif /* ECHOCTL */ #ifdef ECHOPRT {"echoprt", ECHOPRT, MD_LIN}, #endif /* ECHOPRT */ #ifdef ECHOKE {"echoke", ECHOKE, MD_LIN}, #endif /* ECHOKE */ #ifdef DEFECHO {"defecho", DEFECHO, MD_LIN}, #endif /* DEFECHO */ #ifdef FLUSHO {"flusho", FLUSHO, MD_LIN}, #endif /* FLUSHO */ #ifdef PENDIN {"pendin", PENDIN, MD_LIN}, #endif /* PENDIN */ #ifdef IEXTEN {"iexten", IEXTEN, MD_LIN}, #endif /* IEXTEN */ #ifdef NOKERNINFO {"nokerninfo", NOKERNINFO, MD_LIN}, #endif /* NOKERNINFO */ #ifdef ALTWERASE {"altwerase", ALTWERASE, MD_LIN}, #endif /* ALTWERASE */ #ifdef EXTPROC {"extproc", EXTPROC, MD_LIN}, #endif /* EXTPROC */ #if defined(VINTR) {"intr", C_SH(C_INTR), MD_CHAR}, #endif /* VINTR */ #if defined(VQUIT) {"quit", C_SH(C_QUIT), MD_CHAR}, #endif /* VQUIT */ #if defined(VERASE) {"erase", C_SH(C_ERASE), MD_CHAR}, #endif /* VERASE */ #if defined(VKILL) {"kill", C_SH(C_KILL), MD_CHAR}, #endif /* VKILL */ #if defined(VEOF) {"eof", C_SH(C_EOF), MD_CHAR}, #endif /* VEOF */ #if defined(VEOL) {"eol", C_SH(C_EOL), MD_CHAR}, #endif /* VEOL */ #if defined(VEOL2) {"eol2", C_SH(C_EOL2), MD_CHAR}, #endif /* VEOL2 */ #if defined(VSWTCH) {"swtch", C_SH(C_SWTCH), MD_CHAR}, #endif /* VSWTCH */ #if defined(VDSWTCH) {"dswtch", C_SH(C_DSWTCH), MD_CHAR}, #endif /* VDSWTCH */ #if defined(VERASE2) {"erase2", C_SH(C_ERASE2), MD_CHAR}, #endif /* VERASE2 */ #if defined(VSTART) {"start", C_SH(C_START), MD_CHAR}, #endif /* VSTART */ #if defined(VSTOP) {"stop", C_SH(C_STOP), MD_CHAR}, #endif /* VSTOP */ #if defined(VWERASE) {"werase", C_SH(C_WERASE), MD_CHAR}, #endif /* VWERASE */ #if defined(VSUSP) {"susp", C_SH(C_SUSP), MD_CHAR}, #endif /* VSUSP */ #if defined(VDSUSP) {"dsusp", C_SH(C_DSUSP), MD_CHAR}, #endif /* VDSUSP */ #if defined(VREPRINT) {"reprint", C_SH(C_REPRINT), MD_CHAR}, #endif /* VREPRINT */ #if defined(VDISCARD) {"discard", C_SH(C_DISCARD), MD_CHAR}, #endif /* VDISCARD */ #if defined(VLNEXT) {"lnext", C_SH(C_LNEXT), MD_CHAR}, #endif /* VLNEXT */ #if defined(VSTATUS) {"status", C_SH(C_STATUS), MD_CHAR}, #endif /* VSTATUS */ #if defined(VPAGE) {"page", C_SH(C_PAGE), MD_CHAR}, #endif /* VPAGE */ #if defined(VPGOFF) {"pgoff", C_SH(C_PGOFF), MD_CHAR}, #endif /* VPGOFF */ #if defined(VKILL2) {"kill2", C_SH(C_KILL2), MD_CHAR}, #endif /* VKILL2 */ #if defined(VBRK) {"brk", C_SH(C_BRK), MD_CHAR}, #endif /* VBRK */ #if defined(VMIN) {"min", C_SH(C_MIN), MD_CHAR}, #endif /* VMIN */ #if defined(VTIME) {"time", C_SH(C_TIME), MD_CHAR}, #endif /* VTIME */ {NULL, 0, -1}, }; #define tty__gettabs(td) ((((td)->c_oflag & TAB3) == TAB3) ? 0 : 1) #define tty__geteightbit(td) (((td)->c_cflag & CSIZE) == CS8) #define tty__cooked_mode(td) ((td)->c_lflag & ICANON) static int tty_getty(EditLine *, struct termios *); static int tty_setty(EditLine *, int, const struct termios *); static int tty__getcharindex(int); static void tty__getchar(struct termios *, unsigned char *); static void tty__setchar(struct termios *, unsigned char *); static speed_t tty__getspeed(struct termios *); static int tty_setup(EditLine *); static void tty_setup_flags(EditLine *, struct termios *, int); #define t_qu t_ts /* tty_getty(): * Wrapper for tcgetattr to handle EINTR */ static int tty_getty(EditLine *el, struct termios *t) { int rv; while ((rv = tcgetattr(el->el_infd, t)) == -1 && errno == EINTR) continue; return rv; } /* tty_setty(): * Wrapper for tcsetattr to handle EINTR */ static int tty_setty(EditLine *el, int action, const struct termios *t) { int rv; while ((rv = tcsetattr(el->el_infd, action, t)) == -1 && errno == EINTR) continue; return rv; } /* tty_setup(): * Get the tty parameters and initialize the editing state */ static int tty_setup(EditLine *el) { int rst = (el->el_flags & NO_RESET) == 0; if (el->el_flags & EDIT_DISABLED) return 0; if (el->el_tty.t_initialized) return -1; if (!isatty(el->el_outfd)) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: isatty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ return -1; } if (tty_getty(el, &el->el_tty.t_or) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: tty_getty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ return -1; } el->el_tty.t_ts = el->el_tty.t_ex = el->el_tty.t_ed = el->el_tty.t_or; el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ex); el->el_tty.t_tabs = tty__gettabs(&el->el_tty.t_ex); el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ex); tty_setup_flags(el, &el->el_tty.t_ex, EX_IO); /* * Reset the tty chars to reasonable defaults * If they are disabled, then enable them. */ if (rst) { if (tty__cooked_mode(&el->el_tty.t_ts)) { tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]); /* * Don't affect CMIN and CTIME for the editor mode */ for (rst = 0; rst < C_NCC - 2; rst++) if (el->el_tty.t_c[TS_IO][rst] != el->el_tty.t_vdisable && el->el_tty.t_c[ED_IO][rst] != el->el_tty.t_vdisable) el->el_tty.t_c[ED_IO][rst] = el->el_tty.t_c[TS_IO][rst]; for (rst = 0; rst < C_NCC; rst++) if (el->el_tty.t_c[TS_IO][rst] != el->el_tty.t_vdisable) el->el_tty.t_c[EX_IO][rst] = el->el_tty.t_c[TS_IO][rst]; } tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ return -1; } } tty_setup_flags(el, &el->el_tty.t_ed, ED_IO); tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]); tty_bind_char(el, 1); el->el_tty.t_initialized = 1; return 0; } libedit_private int tty_init(EditLine *el) { el->el_tty.t_mode = EX_IO; el->el_tty.t_vdisable = _POSIX_VDISABLE; el->el_tty.t_initialized = 0; (void) memcpy(el->el_tty.t_t, ttyperm, sizeof(ttyperm_t)); (void) memcpy(el->el_tty.t_c, ttychar, sizeof(ttychar_t)); return tty_setup(el); } /* tty_end(): * Restore the tty to its original settings */ libedit_private void /*ARGSUSED*/ tty_end(EditLine *el, int how) { if (el->el_flags & EDIT_DISABLED) return; if (!el->el_tty.t_initialized) return; if (tty_setty(el, how, &el->el_tty.t_or) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ } } /* tty__getspeed(): * Get the tty speed */ static speed_t tty__getspeed(struct termios *td) { speed_t spd; if ((spd = cfgetispeed(td)) == 0) spd = cfgetospeed(td); return spd; } /* tty__getspeed(): * Return the index of the asked char in the c_cc array */ static int tty__getcharindex(int i) { switch (i) { #ifdef VINTR case C_INTR: return VINTR; #endif /* VINTR */ #ifdef VQUIT case C_QUIT: return VQUIT; #endif /* VQUIT */ #ifdef VERASE case C_ERASE: return VERASE; #endif /* VERASE */ #ifdef VKILL case C_KILL: return VKILL; #endif /* VKILL */ #ifdef VEOF case C_EOF: return VEOF; #endif /* VEOF */ #ifdef VEOL case C_EOL: return VEOL; #endif /* VEOL */ #ifdef VEOL2 case C_EOL2: return VEOL2; #endif /* VEOL2 */ #ifdef VSWTCH case C_SWTCH: return VSWTCH; #endif /* VSWTCH */ #ifdef VDSWTCH case C_DSWTCH: return VDSWTCH; #endif /* VDSWTCH */ #ifdef VERASE2 case C_ERASE2: return VERASE2; #endif /* VERASE2 */ #ifdef VSTART case C_START: return VSTART; #endif /* VSTART */ #ifdef VSTOP case C_STOP: return VSTOP; #endif /* VSTOP */ #ifdef VWERASE case C_WERASE: return VWERASE; #endif /* VWERASE */ #ifdef VSUSP case C_SUSP: return VSUSP; #endif /* VSUSP */ #ifdef VDSUSP case C_DSUSP: return VDSUSP; #endif /* VDSUSP */ #ifdef VREPRINT case C_REPRINT: return VREPRINT; #endif /* VREPRINT */ #ifdef VDISCARD case C_DISCARD: return VDISCARD; #endif /* VDISCARD */ #ifdef VLNEXT case C_LNEXT: return VLNEXT; #endif /* VLNEXT */ #ifdef VSTATUS case C_STATUS: return VSTATUS; #endif /* VSTATUS */ #ifdef VPAGE case C_PAGE: return VPAGE; #endif /* VPAGE */ #ifdef VPGOFF case C_PGOFF: return VPGOFF; #endif /* VPGOFF */ #ifdef VKILL2 case C_KILL2: return VKILL2; #endif /* KILL2 */ #ifdef VMIN case C_MIN: return VMIN; #endif /* VMIN */ #ifdef VTIME case C_TIME: return VTIME; #endif /* VTIME */ default: return -1; } } /* tty__getchar(): * Get the tty characters */ static void tty__getchar(struct termios *td, unsigned char *s) { #ifdef VINTR s[C_INTR] = td->c_cc[VINTR]; #endif /* VINTR */ #ifdef VQUIT s[C_QUIT] = td->c_cc[VQUIT]; #endif /* VQUIT */ #ifdef VERASE s[C_ERASE] = td->c_cc[VERASE]; #endif /* VERASE */ #ifdef VKILL s[C_KILL] = td->c_cc[VKILL]; #endif /* VKILL */ #ifdef VEOF s[C_EOF] = td->c_cc[VEOF]; #endif /* VEOF */ #ifdef VEOL s[C_EOL] = td->c_cc[VEOL]; #endif /* VEOL */ #ifdef VEOL2 s[C_EOL2] = td->c_cc[VEOL2]; #endif /* VEOL2 */ #ifdef VSWTCH s[C_SWTCH] = td->c_cc[VSWTCH]; #endif /* VSWTCH */ #ifdef VDSWTCH s[C_DSWTCH] = td->c_cc[VDSWTCH]; #endif /* VDSWTCH */ #ifdef VERASE2 s[C_ERASE2] = td->c_cc[VERASE2]; #endif /* VERASE2 */ #ifdef VSTART s[C_START] = td->c_cc[VSTART]; #endif /* VSTART */ #ifdef VSTOP s[C_STOP] = td->c_cc[VSTOP]; #endif /* VSTOP */ #ifdef VWERASE s[C_WERASE] = td->c_cc[VWERASE]; #endif /* VWERASE */ #ifdef VSUSP s[C_SUSP] = td->c_cc[VSUSP]; #endif /* VSUSP */ #ifdef VDSUSP s[C_DSUSP] = td->c_cc[VDSUSP]; #endif /* VDSUSP */ #ifdef VREPRINT s[C_REPRINT] = td->c_cc[VREPRINT]; #endif /* VREPRINT */ #ifdef VDISCARD s[C_DISCARD] = td->c_cc[VDISCARD]; #endif /* VDISCARD */ #ifdef VLNEXT s[C_LNEXT] = td->c_cc[VLNEXT]; #endif /* VLNEXT */ #ifdef VSTATUS s[C_STATUS] = td->c_cc[VSTATUS]; #endif /* VSTATUS */ #ifdef VPAGE s[C_PAGE] = td->c_cc[VPAGE]; #endif /* VPAGE */ #ifdef VPGOFF s[C_PGOFF] = td->c_cc[VPGOFF]; #endif /* VPGOFF */ #ifdef VKILL2 s[C_KILL2] = td->c_cc[VKILL2]; #endif /* KILL2 */ #ifdef VMIN s[C_MIN] = td->c_cc[VMIN]; #endif /* VMIN */ #ifdef VTIME s[C_TIME] = td->c_cc[VTIME]; #endif /* VTIME */ } /* tty__getchar */ /* tty__setchar(): * Set the tty characters */ static void tty__setchar(struct termios *td, unsigned char *s) { #ifdef VINTR td->c_cc[VINTR] = s[C_INTR]; #endif /* VINTR */ #ifdef VQUIT td->c_cc[VQUIT] = s[C_QUIT]; #endif /* VQUIT */ #ifdef VERASE td->c_cc[VERASE] = s[C_ERASE]; #endif /* VERASE */ #ifdef VKILL td->c_cc[VKILL] = s[C_KILL]; #endif /* VKILL */ #ifdef VEOF td->c_cc[VEOF] = s[C_EOF]; #endif /* VEOF */ #ifdef VEOL td->c_cc[VEOL] = s[C_EOL]; #endif /* VEOL */ #ifdef VEOL2 td->c_cc[VEOL2] = s[C_EOL2]; #endif /* VEOL2 */ #ifdef VSWTCH td->c_cc[VSWTCH] = s[C_SWTCH]; #endif /* VSWTCH */ #ifdef VDSWTCH td->c_cc[VDSWTCH] = s[C_DSWTCH]; #endif /* VDSWTCH */ #ifdef VERASE2 td->c_cc[VERASE2] = s[C_ERASE2]; #endif /* VERASE2 */ #ifdef VSTART td->c_cc[VSTART] = s[C_START]; #endif /* VSTART */ #ifdef VSTOP td->c_cc[VSTOP] = s[C_STOP]; #endif /* VSTOP */ #ifdef VWERASE td->c_cc[VWERASE] = s[C_WERASE]; #endif /* VWERASE */ #ifdef VSUSP td->c_cc[VSUSP] = s[C_SUSP]; #endif /* VSUSP */ #ifdef VDSUSP td->c_cc[VDSUSP] = s[C_DSUSP]; #endif /* VDSUSP */ #ifdef VREPRINT td->c_cc[VREPRINT] = s[C_REPRINT]; #endif /* VREPRINT */ #ifdef VDISCARD td->c_cc[VDISCARD] = s[C_DISCARD]; #endif /* VDISCARD */ #ifdef VLNEXT td->c_cc[VLNEXT] = s[C_LNEXT]; #endif /* VLNEXT */ #ifdef VSTATUS td->c_cc[VSTATUS] = s[C_STATUS]; #endif /* VSTATUS */ #ifdef VPAGE td->c_cc[VPAGE] = s[C_PAGE]; #endif /* VPAGE */ #ifdef VPGOFF td->c_cc[VPGOFF] = s[C_PGOFF]; #endif /* VPGOFF */ #ifdef VKILL2 td->c_cc[VKILL2] = s[C_KILL2]; #endif /* VKILL2 */ #ifdef VMIN td->c_cc[VMIN] = s[C_MIN]; #endif /* VMIN */ #ifdef VTIME td->c_cc[VTIME] = s[C_TIME]; #endif /* VTIME */ } /* tty__setchar */ /* tty_bind_char(): * Rebind the editline functions */ libedit_private void tty_bind_char(EditLine *el, int force) { unsigned char *t_n = el->el_tty.t_c[ED_IO]; unsigned char *t_o = el->el_tty.t_ed.c_cc; wchar_t new[2], old[2]; const ttymap_t *tp; el_action_t *map, *alt; const el_action_t *dmap, *dalt; new[1] = old[1] = '\0'; map = el->el_map.key; alt = el->el_map.alt; if (el->el_map.type == MAP_VI) { dmap = el->el_map.vii; dalt = el->el_map.vic; } else { dmap = el->el_map.emacs; dalt = NULL; } for (tp = tty_map; tp->nch != (wint_t)-1; tp++) { new[0] = (wchar_t)t_n[tp->nch]; old[0] = (wchar_t)t_o[tp->och]; if (new[0] == old[0] && !force) continue; /* Put the old default binding back, and set the new binding */ keymacro_clear(el, map, old); map[(unsigned char)old[0]] = dmap[(unsigned char)old[0]]; keymacro_clear(el, map, new); /* MAP_VI == 1, MAP_EMACS == 0... */ map[(unsigned char)new[0]] = tp->bind[el->el_map.type]; if (dalt) { keymacro_clear(el, alt, old); alt[(unsigned char)old[0]] = dalt[(unsigned char)old[0]]; keymacro_clear(el, alt, new); alt[(unsigned char)new[0]] = tp->bind[el->el_map.type + 1]; } } } static tcflag_t * tty__get_flag(struct termios *t, int kind) { switch (kind) { case MD_INP: return &t->c_iflag; case MD_OUT: return &t->c_oflag; case MD_CTL: return &t->c_cflag; case MD_LIN: return &t->c_lflag; default: abort(); /*NOTREACHED*/ } } static tcflag_t tty_update_flag(EditLine *el, tcflag_t f, int mode, int kind) { f &= ~el->el_tty.t_t[mode][kind].t_clrmask; f |= el->el_tty.t_t[mode][kind].t_setmask; return f; } static void tty_update_flags(EditLine *el, int kind) { tcflag_t *tt, *ed, *ex; tt = tty__get_flag(&el->el_tty.t_ts, kind); ed = tty__get_flag(&el->el_tty.t_ed, kind); ex = tty__get_flag(&el->el_tty.t_ex, kind); if (*tt != *ex && (kind != MD_CTL || *tt != *ed)) { *ed = tty_update_flag(el, *tt, ED_IO, kind); *ex = tty_update_flag(el, *tt, EX_IO, kind); } } static void tty_update_char(EditLine *el, int mode, int c) { if (!((el->el_tty.t_t[mode][MD_CHAR].t_setmask & C_SH(c))) && (el->el_tty.t_c[TS_IO][c] != el->el_tty.t_c[EX_IO][c])) el->el_tty.t_c[mode][c] = el->el_tty.t_c[TS_IO][c]; if (el->el_tty.t_t[mode][MD_CHAR].t_clrmask & C_SH(c)) el->el_tty.t_c[mode][c] = el->el_tty.t_vdisable; } /* tty_rawmode(): * Set terminal into 1 character at a time mode. */ libedit_private int tty_rawmode(EditLine *el) { if (el->el_tty.t_mode == ED_IO || el->el_tty.t_mode == QU_IO) return 0; if (el->el_flags & EDIT_DISABLED) return 0; if (tty_getty(el, &el->el_tty.t_ts) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: tty_getty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ return -1; } /* * We always keep up with the eight bit setting and the speed of the * tty. But we only believe changes that are made to cooked mode! */ el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ts); el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ts); if (tty__getspeed(&el->el_tty.t_ex) != el->el_tty.t_speed || tty__getspeed(&el->el_tty.t_ed) != el->el_tty.t_speed) { (void) cfsetispeed(&el->el_tty.t_ex, el->el_tty.t_speed); (void) cfsetospeed(&el->el_tty.t_ex, el->el_tty.t_speed); (void) cfsetispeed(&el->el_tty.t_ed, el->el_tty.t_speed); (void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed); } if (tty__cooked_mode(&el->el_tty.t_ts)) { int i; for (i = MD_INP; i <= MD_LIN; i++) tty_update_flags(el, i); if (tty__gettabs(&el->el_tty.t_ex) == 0) el->el_tty.t_tabs = 0; else el->el_tty.t_tabs = EL_CAN_TAB ? 1 : 0; tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]); /* * Check if the user made any changes. * If he did, then propagate the changes to the * edit and execute data structures. */ for (i = 0; i < C_NCC; i++) if (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]) break; if (i != C_NCC) { /* * Propagate changes only to the unlibedit_private * chars that have been modified just now. */ for (i = 0; i < C_NCC; i++) tty_update_char(el, ED_IO, i); tty_bind_char(el, 0); tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]); for (i = 0; i < C_NCC; i++) tty_update_char(el, EX_IO, i); tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); } } if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ return -1; } el->el_tty.t_mode = ED_IO; return 0; } /* tty_cookedmode(): * Set the tty back to normal mode */ libedit_private int tty_cookedmode(EditLine *el) { /* set tty in normal setup */ if (el->el_tty.t_mode == EX_IO) return 0; if (el->el_flags & EDIT_DISABLED) return 0; if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ return -1; } el->el_tty.t_mode = EX_IO; return 0; } /* tty_quotemode(): * Turn on quote mode */ libedit_private int tty_quotemode(EditLine *el) { if (el->el_tty.t_mode == QU_IO) return 0; el->el_tty.t_qu = el->el_tty.t_ed; tty_setup_flags(el, &el->el_tty.t_qu, QU_IO); if (tty_setty(el, TCSADRAIN, &el->el_tty.t_qu) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ return -1; } el->el_tty.t_mode = QU_IO; return 0; } /* tty_noquotemode(): * Turn off quote mode */ libedit_private int tty_noquotemode(EditLine *el) { if (el->el_tty.t_mode != QU_IO) return 0; if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ return -1; } el->el_tty.t_mode = ED_IO; return 0; } /* tty_stty(): * Stty builtin */ libedit_private int /*ARGSUSED*/ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const wchar_t **argv) { const ttymodes_t *m; char x; int aflag = 0; const wchar_t *s, *d; char name[EL_BUFSIZ]; struct termios *tios = &el->el_tty.t_ex; int z = EX_IO; if (argv == NULL) return -1; strncpy(name, ct_encode_string(*argv++, &el->el_scratch), sizeof(name)); name[sizeof(name) - 1] = '\0'; while (argv && *argv && argv[0][0] == '-' && argv[0][2] == '\0') switch (argv[0][1]) { case 'a': aflag++; argv++; break; case 'd': argv++; tios = &el->el_tty.t_ed; z = ED_IO; break; case 'x': argv++; tios = &el->el_tty.t_ex; z = EX_IO; break; case 'q': argv++; tios = &el->el_tty.t_ts; z = QU_IO; break; default: (void) fprintf(el->el_errfile, "%s: Unknown switch `%lc'.\n", name, (wint_t)argv[0][1]); return -1; } if (!argv || !*argv) { int i = -1; size_t len = 0, st = 0, cu; for (m = ttymodes; m->m_name; m++) { if (m->m_type != i) { (void) fprintf(el->el_outfile, "%s%s", i != -1 ? "\n" : "", el->el_tty.t_t[z][m->m_type].t_name); i = m->m_type; st = len = strlen(el->el_tty.t_t[z][m->m_type].t_name); } if (i != -1) { x = (el->el_tty.t_t[z][i].t_setmask & m->m_value) ? '+' : '\0'; if (el->el_tty.t_t[z][i].t_clrmask & m->m_value) x = '-'; } else { x = '\0'; } if (x != '\0' || aflag) { cu = strlen(m->m_name) + (x != '\0') + 1; if (len + cu >= (size_t)el->el_terminal.t_size.h) { (void) fprintf(el->el_outfile, "\n%*s", (int)st, ""); len = st + cu; } else len += cu; if (x != '\0') (void) fprintf(el->el_outfile, "%c%s ", x, m->m_name); else (void) fprintf(el->el_outfile, "%s ", m->m_name); } } (void) fprintf(el->el_outfile, "\n"); return 0; } while (argv && (s = *argv++)) { const wchar_t *p; switch (*s) { case '+': case '-': x = (char)*s++; break; default: x = '\0'; break; } d = s; p = wcschr(s, L'='); for (m = ttymodes; m->m_name; m++) if ((p ? strncmp(m->m_name, ct_encode_string(d, &el->el_scratch), (size_t)(p - d)) : strcmp(m->m_name, ct_encode_string(d, &el->el_scratch))) == 0 && (p == NULL || m->m_type == MD_CHAR)) break; if (!m->m_name) { (void) fprintf(el->el_errfile, "%s: Invalid argument `%ls'.\n", name, d); return -1; } if (p) { int c = ffs((int)m->m_value); int v = *++p ? parse__escape(&p) : el->el_tty.t_vdisable; assert(c != 0); c--; c = tty__getcharindex(c); assert(c != -1); tios->c_cc[c] = (cc_t)v; continue; } switch (x) { case '+': el->el_tty.t_t[z][m->m_type].t_setmask |= m->m_value; el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value; break; case '-': el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value; el->el_tty.t_t[z][m->m_type].t_clrmask |= m->m_value; break; default: el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value; el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value; break; } } tty_setup_flags(el, tios, z); if (el->el_tty.t_mode == z) { if (tty_setty(el, TCSADRAIN, tios) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, strerror(errno)); #endif /* DEBUG_TTY */ return -1; } } return 0; } #ifdef notyet /* tty_printchar(): * DEbugging routine to print the tty characters */ static void tty_printchar(EditLine *el, unsigned char *s) { ttyperm_t *m; int i; for (i = 0; i < C_NCC; i++) { for (m = el->el_tty.t_t; m->m_name; m++) if (m->m_type == MD_CHAR && C_SH(i) == m->m_value) break; if (m->m_name) (void) fprintf(el->el_errfile, "%s ^%c ", m->m_name, s[i] + 'A' - 1); if (i % 5 == 0) (void) fprintf(el->el_errfile, "\n"); } (void) fprintf(el->el_errfile, "\n"); } #endif /* notyet */ static void tty_setup_flags(EditLine *el, struct termios *tios, int mode) { int kind; for (kind = MD_INP; kind <= MD_LIN; kind++) { tcflag_t *f = tty__get_flag(tios, kind); *f = tty_update_flag(el, *f, mode, kind); } } libedit_private int tty_get_signal_character(EditLine *el, int sig) { #ifdef ECHOCTL tcflag_t *ed = tty__get_flag(&el->el_tty.t_ed, MD_INP); if ((*ed & ECHOCTL) == 0) return -1; #endif switch (sig) { #ifdef SIGINT case SIGINT: return el->el_tty.t_c[ED_IO][VINTR]; #endif #ifdef SIGQUIT case SIGQUIT: return el->el_tty.t_c[ED_IO][VQUIT]; #endif #ifdef SIGINFO case SIGINFO: return el->el_tty.t_c[ED_IO][VSTATUS]; #endif #ifdef SIGTSTP case SIGTSTP: return el->el_tty.t_c[ED_IO][VSUSP]; #endif default: return -1; } }