tty.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,414 行 · 第 1/4 页

C
2,414
字号
/* * Copyright (c) 1997-1998 University of Utah and the Flux Group. * All rights reserved. *  * This file is part of the Flux OSKit.  The OSKit is free software, also known * as "open source;" you can redistribute it and/or modify it under the terms * of the GNU General Public License (GPL), version 2, as published by the Free * Software Foundation (FSF).  To explore alternate licensing terms, contact * the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271. *  * The OSKit is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE.  See the GPL for more details.  You should have * received a copy of the GPL along with the OSKit; see the file COPYING.  If * not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA. *//*- * Copyright (c) 1982, 1986, 1990, 1991, 1993 *	The Regents of the University of California.  All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * *	@(#)tty.c	8.8 (Berkeley) 1/21/94 * $\Id: tty.c,v 1.46.2.3 1996/01/03 17:59:21 davidg Exp $ *//*- * TODO: *	o Fix races for sending the start char in ttyflush(). *	o Handle inter-byte timeout for "MIN > 0, TIME > 0" in ttyselect(). *	  With luck, there will be MIN chars before select() returns(). *	o Handle CLOCAL consistently for ptys.  Perhaps disallow setting it. *	o Don't allow input in TS_ZOMBIE case.  It would be visible through *	  FIONREAD. *	o Do the new sio locking stuff here and use it to avoid special *	  case for EXTPROC? *	o Lock PENDIN too? *	o Move EXTPROC and/or PENDIN to t_state? *	o Wrap most of ttioctl in spltty/splx. *	o Implement TIOCNOTTY or remove it from <sys/ioctl.h>. *	o Send STOP if IXOFF is toggled off while TS_TBLOCK is set. *	o Don't allow certain termios flags to affect disciplines other *	  than TTYDISC.  Cancel their effects before switch disciplines *	  and ignore them if they are set while we are in another *	  discipline. *	o Handle c_ispeed = 0 to c_ispeed = c_ospeed conversion here instead *	  of in drivers and fix drivers that write to tp->t_termios. *	o Check for TS_CARR_ON being set while everything is closed and not *	  waiting for carrier.  TS_CARR_ON isn't cleared if nothing is open, *	  so it would live until the next open even if carrier drops. *	o Restore TS_WOPEN since it is useful in pstat.  It must be cleared *	  only when _all_ openers leave open(). */#include "snp.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/ioctl.h>#include <sys/proc.h>#define	TTYDEFCHARS#include <sys/tty.h>#undef	TTYDEFCHARS#include <sys/file.h>#include <sys/conf.h>#include <sys/dkstat.h>#include <sys/uio.h>#include <sys/kernel.h>#include <sys/vnode.h>#include <sys/syslog.h>#include <sys/signalvar.h>#include <sys/resourcevar.h>#include <sys/malloc.h>#if NSNP > 0#include <sys/snoop.h>#endif#include <vm/vm.h>static int	proc_compare __P((struct proc *p1, struct proc *p2));static int	ttnread __P((struct tty *tp));static void	ttyecho __P((int c, struct tty *tp));static int	ttyoutput __P((int c, register struct tty *tp));static void	ttypend __P((struct tty *tp));static void	ttyretype __P((struct tty *tp));static void	ttyrub __P((int c, struct tty *tp));static void	ttyrubo __P((struct tty *tp, int cnt));static void	ttyunblock __P((struct tty *tp));/* * Table with character classes and parity. The 8th bit indicates parity, * the 7th bit indicates the character is an alphameric or underscore (for * ALTWERASE), and the low 6 bits indicate delay type.  If the low 6 bits * are 0 then the character needs no special processing on output; classes * other than 0 might be translated or (not currently) require delays. */#define	E	0x00	/* Even parity. */#define	O	0x80	/* Odd parity. */#define	PARITY(c)	(char_type[c] & O)#define	ALPHA	0x40	/* Alpha or underscore. */#define	ISALPHA(c)	(char_type[(c) & TTY_CHARMASK] & ALPHA)#define	CCLASSMASK	0x3f#define	CCLASS(c)	(char_type[c] & CCLASSMASK)#define	BS	BACKSPACE#define	CC	CONTROL#define	CR	RETURN#define	NA	ORDINARY | ALPHA#define	NL	NEWLINE#define	NO	ORDINARY#define	TB	TAB#define	VT	VTABchar const char_type[] = {	E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC,	/* nul - bel */	O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */	O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */	E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */	O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */	E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */	E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */	O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */	O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */	E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */	E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */	O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */	E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */	O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */	O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */	E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */	/*	 * Meta chars; should be settable per character set;	 * for now, treat them all as normal characters.	 */	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,	NA,   NA,   NA,   NA,   NA,   NA,   NA,   NA,};#undef	BS#undef	CC#undef	CR#undef	NA#undef	NL#undef	NO#undef	TB#undef	VT/* Macros to clear/set/test flags. */#define	SET(t, f)	(t) |= (f)#define	CLR(t, f)	(t) &= ~(f)#define	ISSET(t, f)	((t) & (f))/* * Input control starts when we would not be able to fit the maximum * contents of the ping-pong buffers and finishes when we would be able * to fit that much plus 1/8 more. */#define	I_HIGH_WATER	(TTYHOG - 2 * 256)	/* XXX */#define	I_LOW_WATER	((TTYHOG - 2 * 256) * 7 / 8)	/* XXX */#undef MAX_INPUT		/* XXX wrong in <sys/syslimits.h> */#define	MAX_INPUT	TTYHOG/* * Initial open of tty, or (re)entry to standard tty line discipline. */intttyopen(device, tp)	dev_t device;	register struct tty *tp;{	int s;	s = spltty();	tp->t_dev = device;	if (!ISSET(tp->t_state, TS_ISOPEN)) {		SET(tp->t_state, TS_ISOPEN);		if (ISSET(tp->t_cflag, CLOCAL))			SET(tp->t_state, TS_CONNECTED);		bzero(&tp->t_winsize, sizeof(tp->t_winsize));	}	/*	 * Initialize or restore a cblock allocation policy suitable for	 * the standard line discipline.	 */	clist_alloc_cblocks(&tp->t_canq, TTYHOG, 512);	clist_alloc_cblocks(&tp->t_outq, TTMAXHIWAT + OBUFSIZ + 100,			    TTMAXHIWAT + OBUFSIZ + 100);	clist_alloc_cblocks(&tp->t_rawq, TTYHOG, TTYHOG);	splx(s);	return (0);}/* * Handle close() on a tty line: flush and set to initial state, * bumping generation number so that pending read/write calls * can detect recycling of the tty. * XXX our caller should have done `spltty(); l_close(); ttyclose();' * and l_close() should have flushed, but we repeat the spltty() and * the flush in case there are buggy callers. */intttyclose(tp)	register struct tty *tp;{	int s;	s = spltty();	if (constty == tp)		constty = NULL;	ttyflush(tp, FREAD | FWRITE);	clist_free_cblocks(&tp->t_canq);	clist_free_cblocks(&tp->t_outq);	clist_free_cblocks(&tp->t_rawq);#if NSNP > 0	if (ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL)		snpdown((struct snoop *)tp->t_sc);#endif	tp->t_gen++;	tp->t_line = TTYDISC;	tp->t_pgrp = NULL;	tp->t_session = NULL;	tp->t_state = 0;	splx(s);	return (0);}#define	FLUSHQ(q) {							\	if ((q)->c_cc)							\		ndflush(q, (q)->c_cc);					\}/* Is 'c' a line delimiter ("break" character)? */#define	TTBREAKC(c)							\	((c) == '\n' || (((c) == cc[VEOF] ||				\	(c) == cc[VEOL] || (c) == cc[VEOL2]) && (c) != _POSIX_VDISABLE))/* * Process input of a single character received on a tty. */intttyinput(c, tp)	register int c;	register struct tty *tp;{	register tcflag_t iflag, lflag;	register cc_t *cc;	int i, err;	/*	 * If input is pending take it first.	 */	lflag = tp->t_lflag;	if (ISSET(lflag, PENDIN))		ttypend(tp);	/*	 * Gather stats.	 */	if (ISSET(lflag, ICANON)) {		++tk_cancc;		++tp->t_cancc;	} else {		++tk_rawcc;		++tp->t_rawcc;	}	++tk_nin;	/*	 * Block further input iff:	 * current input > threshold AND input is available to user program	 * AND input flow control is enabled and not yet invoked.	 * The 3 is slop for PARMRK.	 */	iflag = tp->t_iflag;	if (tp->t_rawq.c_cc + tp->t_canq.c_cc > I_HIGH_WATER - 3 &&	    (!ISSET(lflag, ICANON) || tp->t_canq.c_cc != 0) &&	    (ISSET(tp->t_cflag, CRTS_IFLOW) || ISSET(iflag, IXOFF)) &&	    !ISSET(tp->t_state, TS_TBLOCK))		ttyblock(tp);	/* Handle exceptional conditions (break, parity, framing). */	cc = tp->t_cc;	err = (ISSET(c, TTY_ERRORMASK));	if (err) {		CLR(c, TTY_ERRORMASK);		if (ISSET(err, TTY_BI)) { /* Break. */			if (ISSET(iflag, IGNBRK))				return (0);			else if (ISSET(iflag, BRKINT) &&			    ISSET(lflag, ISIG) &&			    (cc[VINTR] != _POSIX_VDISABLE))				c = cc[VINTR];			else if (ISSET(iflag, PARMRK))				goto parmrk;		} else if ((ISSET(err, TTY_PE) && ISSET(iflag, INPCK))			|| ISSET(err, TTY_FE)) {			if (ISSET(iflag, IGNPAR))				return (0);			else if (ISSET(iflag, PARMRK)) {parmrk:				if (tp->t_rawq.c_cc + tp->t_canq.c_cc >				    MAX_INPUT - 3)					goto input_overflow;				(void)putc(0377 | TTY_QUOTE, &tp->t_rawq);				(void)putc(0 | TTY_QUOTE, &tp->t_rawq);				(void)putc(c | TTY_QUOTE, &tp->t_rawq);				goto endcase;			} else				c = 0;		}	}	if (!ISSET(tp->t_state, TS_TYPEN) && ISSET(iflag, ISTRIP))		CLR(c, 0x80);	if (!ISSET(lflag, EXTPROC)) {		/*		 * Check for literal nexting very first		 */		if (ISSET(tp->t_state, TS_LNCH)) {			SET(c, TTY_QUOTE);			CLR(tp->t_state, TS_LNCH);		}		/*		 * Scan for special characters.  This code		 * is really just a big case statement with		 * non-constant cases.  The bottom of the		 * case statement is labeled ``endcase'', so goto		 * it after a case match, or similar.		 */		/*		 * Control chars which aren't controlled		 * by ICANON, ISIG, or IXON.		 */		if (ISSET(lflag, IEXTEN)) {			if (CCEQ(cc[VLNEXT], c)) {				if (ISSET(lflag, ECHO)) {					if (ISSET(lflag, ECHOE)) {						(void)ttyoutput('^', tp);						(void)ttyoutput('\b', tp);					} else						ttyecho(c, tp);				}				SET(tp->t_state, TS_LNCH);				goto endcase;			}			if (CCEQ(cc[VDISCARD], c)) {				if (ISSET(lflag, FLUSHO))					CLR(tp->t_lflag, FLUSHO);				else {					ttyflush(tp, FWRITE);					ttyecho(c, tp);					if (tp->t_rawq.c_cc + tp->t_canq.c_cc)						ttyretype(tp);					SET(tp->t_lflag, FLUSHO);				}				goto startoutput;			}		}		/*		 * Signals.		 */		if (ISSET(lflag, ISIG)) {			if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) {				if (!ISSET(lflag, NOFLSH))					ttyflush(tp, FREAD | FWRITE);				ttyecho(c, tp);				pgsignal(tp->t_pgrp,				    CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1);				goto endcase;			}			if (CCEQ(cc[VSUSP], c)) {				if (!ISSET(lflag, NOFLSH))					ttyflush(tp, FREAD);				ttyecho(c, tp);				pgsignal(tp->t_pgrp, SIGTSTP, 1);				goto endcase;			}		}		/*		 * Handle start/stop characters.		 */		if (ISSET(iflag, IXON)) {			if (CCEQ(cc[VSTOP], c)) {				if (!ISSET(tp->t_state, TS_TTSTOP)) {					SET(tp->t_state, TS_TTSTOP);#ifdef sun4c						/* XXX */					(*tp->t_stop)(tp, 0);#else					(*cdevsw[major(tp->t_dev)].d_stop)(tp,					   0);#endif					return (0);				}				if (!CCEQ(cc[VSTART], c))					return (0);				/*				 * if VSTART == VSTOP then toggle				 */				goto endcase;			}			if (CCEQ(cc[VSTART], c))				goto restartoutput;		}		/*		 * IGNCR, ICRNL, & INLCR		 */		if (c == '\r') {			if (ISSET(iflag, IGNCR))				return (0);			else if (ISSET(iflag, ICRNL))				c = '\n';		} else if (c == '\n' && ISSET(iflag, INLCR))			c = '\r';	}	if (!ISSET(tp->t_lflag, EXTPROC) && ISSET(lflag, ICANON)) {		/*		 * From here on down canonical mode character		 * processing takes place.		 */		/*		 * erase (^H / ^?)		 */		if (CCEQ(cc[VERASE], c)) {			if (tp->t_rawq.c_cc)				ttyrub(unputc(&tp->t_rawq), tp);			goto endcase;		}		/*		 * kill (^U)		 */		if (CCEQ(cc[VKILL], c)) {			if (ISSET(lflag, ECHOKE) &&			    tp->t_rawq.c_cc == tp->t_rocount &&			    !ISSET(lflag, ECHOPRT))				while (tp->t_rawq.c_cc)					ttyrub(unputc(&tp->t_rawq), tp);			else {				ttyecho(c, tp);				if (ISSET(lflag, ECHOK) ||				    ISSET(lflag, ECHOKE))					ttyecho('\n', tp);				FLUSHQ(&tp->t_rawq);				tp->t_rocount = 0;			}			CLR(tp->t_state, TS_LOCAL);			goto endcase;		}		/*		 * word erase (^W)		 */		if (CCEQ(cc[VWERASE], c)) {			int ctype;			/*			 * erase whitespace			 */			while ((c = unputc(&tp->t_rawq)) == ' ' || c == '\t')				ttyrub(c, tp);			if (c == -1)				goto endcase;			/*			 * erase last char of word and remember the			 * next chars type (for ALTWERASE)			 */			ttyrub(c, tp);			c = unputc(&tp->t_rawq);			if (c == -1)				goto endcase;			if (c == ' ' || c == '\t') {				(void)putc(c, &tp->t_rawq);				goto endcase;			}			ctype = ISALPHA(c);			/*			 * erase rest of word			 */			do {				ttyrub(c, tp);				c = unputc(&tp->t_rawq);				if (c == -1)					goto endcase;			} while (c != ' ' && c != '\t' &&			    (!ISSET(lflag, ALTWERASE) || ISALPHA(c) == ctype));			(void)putc(c, &tp->t_rawq);			goto endcase;		}		/*		 * reprint line (^R)		 */		if (CCEQ(cc[VREPRINT], c)) {			ttyretype(tp);			goto endcase;		}		/*		 * ^T - kernel info and generate SIGINFO		 */		if (CCEQ(cc[VSTATUS], c)) {			if (ISSET(lflag, ISIG))				pgsignal(tp->t_pgrp, SIGINFO, 1);			if (!ISSET(lflag, NOKERNINFO))				ttyinfo(tp);			goto endcase;		}	}	/*	 * Check for input buffer overflow	 */	if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= MAX_INPUT) {input_overflow:		if (ISSET(iflag, IMAXBEL)) {			if (tp->t_outq.c_cc < tp->t_hiwat)				(void)ttyoutput(CTRL('g'), tp);		}		goto endcase;	}	if (   c == 0377 && ISSET(iflag, PARMRK) && !ISSET(iflag, ISTRIP)	     && ISSET(iflag, IGNBRK|IGNPAR) != (IGNBRK|IGNPAR))		(void)putc(0377 | TTY_QUOTE, &tp->t_rawq);	/*	 * Put data char in q for user and	 * wakeup on seeing a line delimiter.	 */	if (putc(c, &tp->t_rawq) >= 0) {		if (!ISSET(lflag, ICANON)) {			ttwakeup(tp);			ttyecho(c, tp);			goto endcase;		}		if (TTBREAKC(c)) {			tp->t_rocount = 0;			catq(&tp->t_rawq, &tp->t_canq);			ttwakeup(tp);		} else if (tp->t_rocount++ == 0)			tp->t_rocol = tp->t_column;		if (ISSET(tp->t_state, TS_ERASE)) {			/*			 * end of prterase \.../			 */			CLR(tp->t_state, TS_ERASE);			(void)ttyoutput('/', tp);		}		i = tp->t_column;		ttyecho(c, tp);		if (CCEQ(cc[VEOF], c) && ISSET(lflag, ECHO)) {			/*			 * Place the cursor over the '^' of the ^D.			 */			i = imin(2, tp->t_column - i);			while (i > 0) {				(void)ttyoutput('\b', tp);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?