📄 terminal.cc
字号:
/* * Copyright (c) 1988, 1990 Regents of the University of California. * All rights reserved. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. *//* * From: @(#)terminal.c 5.3 (Berkeley) 3/22/91 */char terminal_rcsid[] = "$Id: terminal.cc,v 1.25 1999/12/12 19:48:05 dholland Exp $";#include <arpa/telnet.h>#include <sys/types.h>#include <sys/time.h>#include <termios.h>#include <unistd.h>#include <signal.h>#include <errno.h>#include <stdio.h>#include "ring.h"#include "defines.h"#include "externs.h"#include "types.h"#include "proto.h"#include "terminal.h"static int TerminalWrite(const char *buf, int n);static int TerminalRead(char *buf, int n);ringbuf ttyoring, ttyiring;#ifndef VDISCARDcc_t termFlushChar;#endif#ifndef VLNEXTcc_t termLiteralNextChar;#endif#ifndef VSUSPcc_t termSuspChar;#endif#ifndef VWERASEcc_t termWerasChar;#endif#ifndef VREPRINTcc_t termRprntChar;#endif#ifndef VSTARTcc_t termStartChar;#endif#ifndef VSTOPcc_t termStopChar;#endif#ifndef VEOLcc_t termForw1Char;#endif#ifndef VEOL2cc_t termForw2Char;#endif#ifndef VSTATUScc_t termAytChar;#endif/* * initialize the terminal data structures. */void init_terminal(void) { if (ttyoring.init(2*BUFSIZ, ttysink, NULL) != 1) { exit(1); } if (ttyiring.init(BUFSIZ, NULL, ttysrc) != 1) { exit(1); } autoflush = TerminalAutoFlush();}/* * Send as much data as possible to the terminal. * if arg "drop" is nonzero, drop data on the floor instead. * * Return value: * -1: No useful work done, data waiting to go out. * 0: No data was waiting, so nothing was done. * 1: All waiting data was written out. * n: All data - n was written out. */int ttyflush(int drop) { datasink *s = NULL; if (drop) { TerminalFlushOutput(); s = ttyoring.setsink(nullsink); } int rv = ttyoring.flush(); if (s) ttyoring.setsink(s); return rv;}/* * These routines decides on what the mode should be (based on the values * of various global variables). */int getconnmode(void) { extern int linemode; int mode = 0;#ifdef KLUDGELINEMODE extern int kludgelinemode;#endif if (In3270) return(MODE_FLOW); if (my_want_state_is_dont(TELOPT_ECHO)) mode |= MODE_ECHO; if (localflow) mode |= MODE_FLOW; if (my_want_state_is_will(TELOPT_BINARY)) mode |= MODE_INBIN; if (his_want_state_is_will(TELOPT_BINARY)) mode |= MODE_OUTBIN;#ifdef KLUDGELINEMODE if (kludgelinemode) { if (my_want_state_is_dont(TELOPT_SGA)) { mode |= (MODE_TRAPSIG|MODE_EDIT); if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) { mode &= ~MODE_ECHO; } } return(mode); }#endif if (my_want_state_is_will(TELOPT_LINEMODE)) mode |= linemode; return(mode);}void setconnmode(int force) { int newmode; newmode = getconnmode()|(force?MODE_FORCE:0); TerminalNewMode(newmode);}void setcommandmode(void) { TerminalNewMode(-1);}/*********************/static int tout; /* Output file descriptor */static int tin; /* Input file descriptor */class ttysynk : public datasink { public: virtual int write(const char *buf, int len) { return TerminalWrite(buf, len); } virtual int writeurg(const char *buf, int len) { return TerminalWrite(buf, len); }};class ttysorc : public ringbuf::source { virtual int read(char *buf, int maxlen) { int l = TerminalRead(buf, maxlen); if (l<0 && errno==EWOULDBLOCK) l = 0; else if (l==0 && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) { /* EOF detection for line mode!!!! */ /* must be an EOF... */ *buf = termEofChar; l = 1; } return l; }};static ttysynk chan1;static ttysorc chan2;datasink *ttysink = &chan1;ringbuf::source *ttysrc = &chan2;struct termios old_tc;struct termios new_tc;#ifndef TCSANOW#if defined(TCSETS)#define TCSANOW TCSETS#define TCSADRAIN TCSETSW#define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)#elif defined(TCSETA)#define TCSANOW TCSETA#define TCSADRAIN TCSETAW#define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)#else#define TCSANOW TIOCSETA#define TCSADRAIN TIOCSETAW#define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)#endif#define tcsetattr(f, a, t) ioctl(f, a, (char *)t)#define cfgetospeed(ptr) ((ptr)->c_cflag&CBAUD)#ifdef CIBAUD#define cfgetispeed(ptr) (((ptr)->c_cflag&CIBAUD) >> IBSHIFT)#else#define cfgetispeed(ptr) cfgetospeed(ptr)#endif#endif /* no TCSANOW */static void susp(int sig);void tlink_init(void) {#ifdef SIGTSTP signal(SIGTSTP, susp);#endif tout = fileno(stdout); tin = fileno(stdin);}int tlink_getifd(void) { return tin;}int tlink_getofd(void) { return tout;}static int TerminalWrite(const char *buf, int n) { int r; do { r = write(tout, buf, n); } while (r<0 && errno==EINTR); if (r<0 && (errno==ENOBUFS || errno==EWOULDBLOCK)) r = 0; return r;}static int TerminalRead(char *buf, int n) { int r; do { r = read(tin, buf, n); } while (r<0 && errno==EINTR); return r;}#ifdef SIGTSTPstatic void susp(int /*sig*/) { if ((rlogin != _POSIX_VDISABLE) && rlogin_susp()) return; if (localchars) sendsusp();}#endif/* * TerminalNewMode - set up terminal to a specific mode. * MODE_ECHO: do local terminal echo * MODE_FLOW: do local flow control * MODE_TRAPSIG: do local mapping to TELNET IAC sequences * MODE_EDIT: do local line editing * * Command mode: * MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG * local echo * local editing * local xon/xoff * local signal mapping * * Linemode: * local/no editing * Both Linemode and Single Character mode: * local/remote echo * local/no xon/xoff * local/no signal mapping */void TerminalNewMode(int f){ static int prevmode = 0; struct termios tmp_tc; int onoff; int old; cc_t esc; globalmode = f&~MODE_FORCE; if (prevmode == f) return; /* * Write any outstanding data before switching modes * ttyflush() returns 0 only when there is no more data * left to write out, it returns -1 if it couldn't do * anything at all, otherwise it returns 1 + the number * of characters left to write. */ old = ttyflush(SYNCHing|flushout); if (old < 0 || old > 1) { tcgetattr(tin, &tmp_tc); do { /* * Wait for data to drain, then flush again. */ tcsetattr(tin, TCSADRAIN, &tmp_tc); old = ttyflush(SYNCHing|flushout); } while (old < 0 || old > 1); } old = prevmode; prevmode = f&~MODE_FORCE; tmp_tc = new_tc; if (f&MODE_ECHO) { tmp_tc.c_lflag |= ECHO; tmp_tc.c_oflag |= ONLCR; if (crlf) tmp_tc.c_iflag |= ICRNL; } else { tmp_tc.c_lflag &= ~ECHO; tmp_tc.c_oflag &= ~ONLCR; if (crlf) tmp_tc.c_iflag &= ~ICRNL; } if ((f&MODE_FLOW) == 0) { tmp_tc.c_iflag &= ~(IXANY|IXOFF|IXON); } else { tmp_tc.c_iflag |= IXANY|IXOFF|IXON; } if ((f&MODE_TRAPSIG) == 0) { tmp_tc.c_lflag &= ~ISIG; localchars = 0; } else { tmp_tc.c_lflag |= ISIG; localchars = 1; } if (f&MODE_EDIT) { tmp_tc.c_lflag |= ICANON; } else { tmp_tc.c_lflag &= ~ICANON; tmp_tc.c_iflag &= ~ICRNL; tmp_tc.c_cc[VMIN] = 1; tmp_tc.c_cc[VTIME] = 0; } if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {#ifdef VLNEXT tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE);#endif } if (f&MODE_SOFT_TAB) {#ifdef OXTABS tmp_tc.c_oflag |= OXTABS;#endif#ifdef TABDLY tmp_tc.c_oflag &= ~TABDLY; tmp_tc.c_oflag |= TAB3;#endif } else {#ifdef OXTABS tmp_tc.c_oflag &= ~OXTABS;#endif#ifdef TABDLY tmp_tc.c_oflag &= ~TABDLY;#endif } if (f&MODE_LIT_ECHO) {#ifdef ECHOCTL tmp_tc.c_lflag &= ~ECHOCTL;#endif } else {#ifdef ECHOCTL tmp_tc.c_lflag |= ECHOCTL;#endif } if (f == -1) { onoff = 0; } else { if (f & MODE_INBIN) { tmp_tc.c_iflag &= ~ISTRIP; } else { // Commented this out 5/97 so it works with 8-bit characters // ...and put it back 12/99 because it violates the RFC and // breaks SunOS. tmp_tc.c_iflag |= ISTRIP; } if (f & MODE_OUTBIN) { tmp_tc.c_cflag &= ~(CSIZE|PARENB); tmp_tc.c_cflag |= CS8; tmp_tc.c_oflag &= ~OPOST; } else { tmp_tc.c_cflag &= ~(CSIZE|PARENB); tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB); tmp_tc.c_oflag |= OPOST; } onoff = 1; } if (f != -1) {#ifdef SIGTSTP signal(SIGTSTP, susp);#endif /* SIGTSTP */#ifdef SIGINFO signal(SIGINFO, ayt);#endif SIGINFO#if defined(NOKERNINFO) tmp_tc.c_lflag |= NOKERNINFO;#endif /* * We don't want to process ^Y here. It's just another * character that we'll pass on to the back end. It has * to process it because it will be processed when the * user attempts to read it, not when we send it. */#ifdef VDSUSP tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);#endif /* * If the VEOL character is already set, then use VEOL2, * otherwise use VEOL. */ esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escapechar; if ((tmp_tc.c_cc[VEOL] != esc)#ifdef VEOL2 && (tmp_tc.c_cc[VEOL2] != esc)#endif ) { if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE)) tmp_tc.c_cc[VEOL] = esc;#ifdef VEOL2 else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE)) tmp_tc.c_cc[VEOL2] = esc;#endif } } else {#ifdef SIGINFO signal(SIGINFO, ayt_status);#endif SIGINFO#ifdef SIGTSTP signal(SIGTSTP, SIG_DFL);/* (void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1))); */#endif /* SIGTSTP */ tmp_tc = old_tc; } if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0) tcsetattr(tin, TCSANOW, &tmp_tc); ioctl(tin, FIONBIO, (char *)&onoff); ioctl(tout, FIONBIO, (char *)&onoff);#if defined(TN3270) if (noasynchtty == 0) { ioctl(tin, FIOASYNC, (char *)&onoff); }#endif /* defined(TN3270) */}#ifndef B19200#define B19200 B9600#endif#ifndef B38400#define B38400 B19200#endif#ifndef B57600#define B57600 B38400#endif#ifndef B115200#define B115200 B57600#endif/* * This code assumes that the values B0, B50, B75... * are in ascending order. They do not have to be * contiguous. */struct termspeeds { long speed; long value;} termspeeds[] = { { 0, B0 }, { 50, B50 }, { 75, B75 }, { 110, B110 }, { 134, B134 }, { 150, B150 }, { 200, B200 }, { 300, B300 }, { 600, B600 }, { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, { 4800, B4800 }, { 9600, B9600 }, { 19200, B19200 }, { 38400, B38400 }, { 57600, B57600 }, { 115200, B115200 }, { -1, B115200 }};void TerminalSpeeds(long *ispeed, long *ospeed) { register struct termspeeds *tp; register long in, out; out = cfgetospeed(&old_tc); in = cfgetispeed(&old_tc); if (in == 0) in = out; tp = termspeeds; while ((tp->speed != -1) && (tp->value < in)) tp++; *ispeed = tp->speed; tp = termspeeds; while ((tp->speed != -1) && (tp->value < out)) tp++; *ospeed = tp->speed;}int TerminalWindowSize(long *rows, long *cols) {#ifdef TIOCGWINSZ struct winsize ws; if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) { *rows = ws.ws_row; *cols = ws.ws_col; return 1; }#endif /* TIOCGWINSZ */ return 0;}/* * EmptyTerminal - called to make sure that the terminal buffer is * empty. Note that we consider the buffer to run all the way to the * kernel (thus the select). */void EmptyTerminal(void) { fd_set o; FD_ZERO(&o); if (TTYBYTES() == 0) { FD_SET(tout, &o); select(tout+1, NULL, &o, NULL, NULL); /* wait for TTLOWAT */ } else { while (TTYBYTES()) { ttyflush(0); FD_SET(tout, &o); select(tout+1, NULL, &o, NULL, NULL); /* wait for TTLOWAT */ } }}intTerminalAutoFlush(void){#if defined(LNOFLSH) int flush; ioctl(tin, TIOCLGET, (char *)&flush); return !(flush&LNOFLSH); /* if LNOFLSH, no autoflush */#else /* LNOFLSH */ return 1;#endif /* LNOFLSH */}/* * Flush output to the terminal */ voidTerminalFlushOutput(){#ifdef TIOCFLUSH (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);#else (void) ioctl(fileno(stdout), TCFLSH, (char *) 0);#endif} voidTerminalSaveState(){#ifndef USE_TERMIO ioctl(0, TIOCGETP, (char *)&ottyb); ioctl(0, TIOCGETC, (char *)&otc); ioctl(0, TIOCGLTC, (char *)&oltc); ioctl(0, TIOCLGET, (char *)&olmode); ntc = otc; nltc = oltc; nttyb = ottyb;#else /* USE_TERMIO */ tcgetattr(0, &old_tc); new_tc = old_tc;#ifndef VDISCARD termFlushChar = CONTROL('O');#endif#ifndef VWERASE termWerasChar = CONTROL('W');#endif#ifndef VREPRINT termRprntChar = CONTROL('R');#endif#ifndef VLNEXT termLiteralNextChar = CONTROL('V');#endif#ifndef VSTART termStartChar = CONTROL('Q');#endif#ifndef VSTOP termStopChar = CONTROL('S');#endif#ifndef VSTATUS termAytChar = CONTROL('T');#endif#endif /* USE_TERMIO */}void TerminalDefaultChars(void) {#ifndef USE_TERMIO ntc = otc; nltc = oltc; nttyb.sg_kill = ottyb.sg_kill; nttyb.sg_erase = ottyb.sg_erase;#else /* USE_TERMIO */ memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));#ifndef VDISCARD termFlushChar = CONTROL('O');#endif#ifndef VWERASE termWerasChar = CONTROL('W');#endif#ifndef VREPRINT termRprntChar = CONTROL('R');#endif#ifndef VLNEXT termLiteralNextChar = CONTROL('V');#endif#ifndef VSTART termStartChar = CONTROL('Q');#endif#ifndef VSTOP termStopChar = CONTROL('S');#endif#ifndef VSTATUS termAytChar = CONTROL('T');#endif#endif /* USE_TERMIO */}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -