diff --git a/bin/stty/modes.c b/bin/stty/modes.c
index ce39083d7d63..3818677ddc4e 100644
--- a/bin/stty/modes.c
+++ b/bin/stty/modes.c
@@ -1,245 +1,247 @@
 /*-
  * Copyright (c) 1991, 1993, 1994
  *	The 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. 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.
  */
 
 #ifndef lint
 #if 0
 static char sccsid[] = "@(#)modes.c	8.3 (Berkeley) 4/2/94";
 #endif
 #endif /* not lint */
 #include <sys/cdefs.h>
 #include <sys/types.h>
 #include <string.h>
 #include "stty.h"
 
 int msearch(char ***, struct info *);
 
 struct modes {
 	const char *name;
 	long set;
 	long unset;
 };
 
 /*
  * The code in optlist() depends on minus options following regular
  * options, i.e. "foo" must immediately precede "-foo".
  */
 static const struct modes cmodes[] = {
 	{ "cs5",	CS5, CSIZE },
 	{ "cs6",	CS6, CSIZE },
 	{ "cs7",	CS7, CSIZE },
 	{ "cs8",	CS8, CSIZE },
 	{ "cstopb",	CSTOPB, 0 },
 	{ "-cstopb",	0, CSTOPB },
 	{ "cread",	CREAD, 0 },
 	{ "-cread",	0, CREAD },
 	{ "parenb",	PARENB, 0 },
 	{ "-parenb",	0, PARENB },
 	{ "parodd",	PARODD, 0 },
 	{ "-parodd",	0, PARODD },
 	{ "parity",	PARENB | CS7, PARODD | CSIZE },
 	{ "-parity",	CS8, PARODD | PARENB | CSIZE },
 	{ "evenp",	PARENB | CS7, PARODD | CSIZE },
 	{ "-evenp",	CS8, PARODD | PARENB | CSIZE },
 	{ "oddp",	PARENB | CS7 | PARODD, CSIZE },
 	{ "-oddp",	CS8, PARODD | PARENB | CSIZE },
 	{ "pass8",	CS8, PARODD | PARENB | CSIZE },
 	{ "-pass8",	PARENB | CS7, PARODD | CSIZE },
 	{ "hupcl",	HUPCL, 0 },
 	{ "-hupcl",	0, HUPCL },
 	{ "hup",	HUPCL, 0 },
 	{ "-hup",	0, HUPCL },
 	{ "clocal",	CLOCAL, 0 },
 	{ "-clocal",	0, CLOCAL },
 	{ "crtscts",	CRTSCTS, 0 },
 	{ "-crtscts",	0, CRTSCTS },
 	{ "ctsflow",	CCTS_OFLOW, 0 },
 	{ "-ctsflow",	0, CCTS_OFLOW },
 	{ "dsrflow",	CDSR_OFLOW, 0 },
 	{ "-dsrflow",	0, CDSR_OFLOW },
 	{ "dtrflow",	CDTR_IFLOW, 0 },
 	{ "-dtrflow",	0, CDTR_IFLOW },
 	{ "rtsflow",	CRTS_IFLOW, 0 },
 	{ "-rtsflow",	0, CRTS_IFLOW },
 	{ "mdmbuf",	MDMBUF, 0 },
 	{ "-mdmbuf",	0, MDMBUF },
 	{ "rtsdtr",	0, CNO_RTSDTR },
 	{ "-rtsdtr",	CNO_RTSDTR, 0 },
 	{ NULL,		0, 0 },
 };
 
 static const struct modes imodes[] = {
 	{ "ignbrk",	IGNBRK, 0 },
 	{ "-ignbrk",	0, IGNBRK },
 	{ "brkint",	BRKINT, 0 },
 	{ "-brkint",	0, BRKINT },
 	{ "ignpar",	IGNPAR, 0 },
 	{ "-ignpar",	0, IGNPAR },
 	{ "parmrk",	PARMRK, 0 },
 	{ "-parmrk",	0, PARMRK },
 	{ "inpck",	INPCK, 0 },
 	{ "-inpck",	0, INPCK },
 	{ "istrip",	ISTRIP, 0 },
 	{ "-istrip",	0, ISTRIP },
 	{ "inlcr",	INLCR, 0 },
 	{ "-inlcr",	0, INLCR },
 	{ "igncr",	IGNCR, 0 },
 	{ "-igncr",	0, IGNCR },
 	{ "icrnl",	ICRNL, 0 },
 	{ "-icrnl",	0, ICRNL },
 	{ "ixon",	IXON, 0 },
 	{ "-ixon",	0, IXON },
 	{ "flow",	IXON, 0 },
 	{ "-flow",	0, IXON },
 	{ "ixoff",	IXOFF, 0 },
 	{ "-ixoff",	0, IXOFF },
 	{ "tandem",	IXOFF, 0 },
 	{ "-tandem",	0, IXOFF },
 	{ "ixany",	IXANY, 0 },
 	{ "-ixany",	0, IXANY },
 	{ "decctlq",	0, IXANY },
 	{ "-decctlq",	IXANY, 0 },
 	{ "imaxbel",	IMAXBEL, 0 },
 	{ "-imaxbel",	0, IMAXBEL },
+	{ "iutf8",	IUTF8, 0 },
+	{ "-iutf8",	0, IUTF8 },
 	{ NULL,		0, 0 },
 };
 
 static const struct modes lmodes[] = {
 	{ "echo",	ECHO, 0 },
 	{ "-echo",	0, ECHO },
 	{ "echoe",	ECHOE, 0 },
 	{ "-echoe",	0, ECHOE },
 	{ "crterase",	ECHOE, 0 },
 	{ "-crterase",	0, ECHOE },
 	{ "crtbs",	ECHOE, 0 },	/* crtbs not supported, close enough */
 	{ "-crtbs",	0, ECHOE },
 	{ "echok",	ECHOK, 0 },
 	{ "-echok",	0, ECHOK },
 	{ "echoke",	ECHOKE, 0 },
 	{ "-echoke",	0, ECHOKE },
 	{ "crtkill",	ECHOKE, 0 },
 	{ "-crtkill",	0, ECHOKE },
 	{ "altwerase",	ALTWERASE, 0 },
 	{ "-altwerase",	0, ALTWERASE },
 	{ "iexten",	IEXTEN, 0 },
 	{ "-iexten",	0, IEXTEN },
 	{ "echonl",	ECHONL, 0 },
 	{ "-echonl",	0, ECHONL },
 	{ "echoctl",	ECHOCTL, 0 },
 	{ "-echoctl",	0, ECHOCTL },
 	{ "ctlecho",	ECHOCTL, 0 },
 	{ "-ctlecho",	0, ECHOCTL },
 	{ "echoprt",	ECHOPRT, 0 },
 	{ "-echoprt",	0, ECHOPRT },
 	{ "prterase",	ECHOPRT, 0 },
 	{ "-prterase",	0, ECHOPRT },
 	{ "isig",	ISIG, 0 },
 	{ "-isig",	0, ISIG },
 	{ "icanon",	ICANON, 0 },
 	{ "-icanon",	0, ICANON },
 	{ "noflsh",	NOFLSH, 0 },
 	{ "-noflsh",	0, NOFLSH },
 	{ "tostop",	TOSTOP, 0 },
 	{ "-tostop",	0, TOSTOP },
 	{ "flusho",	FLUSHO, 0 },
 	{ "-flusho",	0, FLUSHO },
 	{ "pendin",	PENDIN, 0 },
 	{ "-pendin",	0, PENDIN },
 	{ "crt",	ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
 	{ "-crt",	ECHOK, ECHOE|ECHOKE|ECHOCTL },
 	{ "newcrt",	ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
 	{ "-newcrt",	ECHOK, ECHOE|ECHOKE|ECHOCTL },
 	{ "nokerninfo",	NOKERNINFO, 0 },
 	{ "-nokerninfo",0, NOKERNINFO },
 	{ "kerninfo",	0, NOKERNINFO },
 	{ "-kerninfo",	NOKERNINFO, 0 },
 	{ NULL,		0, 0 },
 };
 
 static const struct modes omodes[] = {
 	{ "opost",	OPOST, 0 },
 	{ "-opost",	0, OPOST },
 	{ "litout",	0, OPOST },
 	{ "-litout",	OPOST, 0 },
 	{ "onlcr",	ONLCR, 0 },
 	{ "-onlcr",	0, ONLCR },
 	{ "ocrnl",	OCRNL, 0 },
 	{ "-ocrnl",	0, OCRNL },
 	{ "tabs",	TAB0, TABDLY },		/* "preserve" tabs */
 	{ "-tabs",	TAB3, TABDLY },
 	{ "oxtabs",	TAB3, TABDLY },
 	{ "-oxtabs",	TAB0, TABDLY },
 	{ "tab0",	TAB0, TABDLY },
 	{ "tab3",	TAB3, TABDLY },
 	{ "onocr",	ONOCR, 0 },
 	{ "-onocr",	0, ONOCR },
 	{ "onlret",	ONLRET, 0 },
 	{ "-onlret",	0, ONLRET },
 	{ NULL,		0, 0 },
 };
 
 #define	CHK(s)	(*name == s[0] && !strcmp(name, s))
 
 int
 msearch(char ***argvp, struct info *ip)
 {
 	const struct modes *mp;
 	char *name;
 
 	name = **argvp;
 
 	for (mp = cmodes; mp->name; ++mp)
 		if (CHK(mp->name)) {
 			ip->t.c_cflag &= ~mp->unset;
 			ip->t.c_cflag |= mp->set;
 			ip->set = 1;
 			return (1);
 		}
 	for (mp = imodes; mp->name; ++mp)
 		if (CHK(mp->name)) {
 			ip->t.c_iflag &= ~mp->unset;
 			ip->t.c_iflag |= mp->set;
 			ip->set = 1;
 			return (1);
 		}
 	for (mp = lmodes; mp->name; ++mp)
 		if (CHK(mp->name)) {
 			ip->t.c_lflag &= ~mp->unset;
 			ip->t.c_lflag |= mp->set;
 			ip->set = 1;
 			return (1);
 		}
 	for (mp = omodes; mp->name; ++mp)
 		if (CHK(mp->name)) {
 			ip->t.c_oflag &= ~mp->unset;
 			ip->t.c_oflag |= mp->set;
 			ip->set = 1;
 			return (1);
 		}
 	return (0);
 }
diff --git a/bin/stty/print.c b/bin/stty/print.c
index 07a3c2058474..33472bbccea1 100644
--- a/bin/stty/print.c
+++ b/bin/stty/print.c
@@ -1,285 +1,286 @@
 /*-
  * Copyright (c) 1991, 1993, 1994
  *	The 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. 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.
  */
 
 #ifndef lint
 #if 0
 static char sccsid[] = "@(#)print.c	8.6 (Berkeley) 4/16/94";
 #endif
 #endif /* not lint */
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
 #include <stddef.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "stty.h"
 #include "extern.h"
 
 static void  binit(const char *);
 static void  bput(const char *);
 static const char *ccval(struct cchar *, int);
 
 void
 print(struct termios *tp, struct winsize *wp, int ldisc, enum FMT fmt)
 {
 	struct cchar *p;
 	long tmp;
 	u_char *cc;
 	int cnt, ispeed, ospeed;
 	char buf1[100], buf2[100];
 
 	cnt = 0;
 
 	/* Line discipline. */
 	if (ldisc != TTYDISC) {
 		switch(ldisc) {
 		case SLIPDISC:
 			cnt += printf("slip disc; ");
 			break;
 		case PPPDISC:
 			cnt += printf("ppp disc; ");
 			break;
 		default:
 			cnt += printf("#%d disc; ", ldisc);
 			break;
 		}
 	}
 
 	/* Line speed. */
 	ispeed = cfgetispeed(tp);
 	ospeed = cfgetospeed(tp);
 	if (ispeed != ospeed)
 		cnt +=
 		    printf("ispeed %d baud; ospeed %d baud;", ispeed, ospeed);
 	else
 		cnt += printf("speed %d baud;", ispeed);
 	if (fmt >= BSD)
 		cnt += printf(" %d rows; %d columns;", wp->ws_row, wp->ws_col);
 	if (cnt)
 		(void)printf("\n");
 
 #define	on(f)	((tmp & (f)) != 0)
 #define put(n, f, d) \
 	if (fmt >= BSD || on(f) != (d)) \
 		bput((n) + on(f));
 
 	/* "local" flags */
 	tmp = tp->c_lflag;
 	binit("lflags");
 	put("-icanon", ICANON, 1);
 	put("-isig", ISIG, 1);
 	put("-iexten", IEXTEN, 1);
 	put("-echo", ECHO, 1);
 	put("-echoe", ECHOE, 0);
 	put("-echok", ECHOK, 0);
 	put("-echoke", ECHOKE, 0);
 	put("-echonl", ECHONL, 0);
 	put("-echoctl", ECHOCTL, 0);
 	put("-echoprt", ECHOPRT, 0);
 	put("-altwerase", ALTWERASE, 0);
 	put("-noflsh", NOFLSH, 0);
 	put("-tostop", TOSTOP, 0);
 	put("-flusho", FLUSHO, 0);
 	put("-pendin", PENDIN, 0);
 	put("-nokerninfo", NOKERNINFO, 0);
 	put("-extproc", EXTPROC, 0);
 
 	/* input flags */
 	tmp = tp->c_iflag;
 	binit("iflags");
 	put("-istrip", ISTRIP, 0);
 	put("-icrnl", ICRNL, 1);
 	put("-inlcr", INLCR, 0);
 	put("-igncr", IGNCR, 0);
 	put("-ixon", IXON, 1);
 	put("-ixoff", IXOFF, 0);
 	put("-ixany", IXANY, 1);
 	put("-imaxbel", IMAXBEL, 1);
 	put("-ignbrk", IGNBRK, 0);
 	put("-brkint", BRKINT, 1);
 	put("-inpck", INPCK, 0);
 	put("-ignpar", IGNPAR, 0);
 	put("-parmrk", PARMRK, 0);
+	put("-iutf8", IUTF8, 1);
 
 	/* output flags */
 	tmp = tp->c_oflag;
 	binit("oflags");
 	put("-opost", OPOST, 1);
 	put("-onlcr", ONLCR, 1);
 	put("-ocrnl", OCRNL, 0);
 	switch(tmp&TABDLY) {
 	case TAB0:
 		bput("tab0");
 		break;
 	case TAB3:
 		bput("tab3");
 		break;
 	}
 	put("-onocr", ONOCR, 0);
 	put("-onlret", ONLRET, 0);
 
 	/* control flags (hardware state) */
 	tmp = tp->c_cflag;
 	binit("cflags");
 	put("-cread", CREAD, 1);
 	switch(tmp&CSIZE) {
 	case CS5:
 		bput("cs5");
 		break;
 	case CS6:
 		bput("cs6");
 		break;
 	case CS7:
 		bput("cs7");
 		break;
 	case CS8:
 		bput("cs8");
 		break;
 	}
 	bput("-parenb" + on(PARENB));
 	put("-parodd", PARODD, 0);
 	put("-hupcl", HUPCL, 1);
 	put("-clocal", CLOCAL, 0);
 	put("-cstopb", CSTOPB, 0);
 	switch(tmp & (CCTS_OFLOW | CRTS_IFLOW)) {
 	case CCTS_OFLOW:
 		bput("ctsflow");
 		break;
 	case CRTS_IFLOW:
 		bput("rtsflow");
 		break;
 	default:
 		put("-crtscts", CCTS_OFLOW | CRTS_IFLOW, 0);
 		break;
 	}
 	put("-dsrflow", CDSR_OFLOW, 0);
 	put("-dtrflow", CDTR_IFLOW, 0);
 	put("-mdmbuf", MDMBUF, 0);	/* XXX mdmbuf ==  dtrflow */
 	if (on(CNO_RTSDTR))
 		bput("-rtsdtr");
 	else {
 		if (fmt >= BSD)
 			bput("rtsdtr");
 	}
 
 	/* special control characters */
 	cc = tp->c_cc;
 	if (fmt == POSIX) {
 		binit("cchars");
 		for (p = cchars1; p->name; ++p) {
 			(void)snprintf(buf1, sizeof(buf1), "%s = %s;",
 			    p->name, ccval(p, cc[p->sub]));
 			bput(buf1);
 		}
 		binit(NULL);
 	} else {
 		binit(NULL);
 		for (p = cchars1, cnt = 0; p->name; ++p) {
 			if (fmt != BSD && cc[p->sub] == p->def)
 				continue;
 #define	WD	"%-8s"
 			(void)snprintf(buf1 + cnt * 8, sizeof(buf1) - cnt * 8,
 			    WD, p->name);
 			(void)snprintf(buf2 + cnt * 8, sizeof(buf2) - cnt * 8,
 			    WD, ccval(p, cc[p->sub]));
 			if (++cnt == LINELENGTH / 8) {
 				cnt = 0;
 				(void)printf("%s\n", buf1);
 				(void)printf("%s\n", buf2);
 			}
 		}
 		if (cnt) {
 			(void)printf("%s\n", buf1);
 			(void)printf("%s\n", buf2);
 		}
 	}
 }
 
 static int col;
 static const char *label;
 
 static void
 binit(const char *lb)
 {
 
 	if (col) {
 		(void)printf("\n");
 		col = 0;
 	}
 	label = lb;
 }
 
 static void
 bput(const char *s)
 {
 
 	if (col == 0) {
 		col = printf("%s: %s", label, s);
 		return;
 	}
 	if ((col + strlen(s)) > LINELENGTH) {
 		(void)printf("\n\t");
 		col = printf("%s", s) + 8;
 		return;
 	}
 	col += printf(" %s", s);
 }
 
 static const char *
 ccval(struct cchar *p, int c)
 {
 	static char buf[5];
 	char *bp;
 
 	if (p->sub == VMIN || p->sub == VTIME) {
 		(void)snprintf(buf, sizeof(buf), "%d", c);
 		return (buf);
 	}
 	if (c == _POSIX_VDISABLE)
 		return ("<undef>");
 	bp = buf;
 	if (c & 0200) {
 		*bp++ = 'M';
 		*bp++ = '-';
 		c &= 0177;
 	}
 	if (c == 0177) {
 		*bp++ = '^';
 		*bp++ = '?';
 	}
 	else if (c < 040) {
 		*bp++ = '^';
 		*bp++ = c + '@';
 	}
 	else
 		*bp++ = c;
 	*bp = '\0';
 	return (buf);
 }
diff --git a/bin/stty/stty.1 b/bin/stty/stty.1
index 9e9405928c67..c572a657a997 100644
--- a/bin/stty/stty.1
+++ b/bin/stty/stty.1
@@ -1,644 +1,647 @@
 .\"-
 .\" Copyright (c) 1990, 1993, 1994
 .\"	The Regents of the University of California.  All rights reserved.
 .\"
 .\" This code is derived from software contributed to Berkeley by
 .\" the Institute of Electrical and Electronics Engineers, 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. 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.
 .\"
 .\"     @(#)stty.1	8.4 (Berkeley) 4/18/94
 .\"
 .Dd September 27, 2022
 .Dt STTY 1
 .Os
 .Sh NAME
 .Nm stty
 .Nd set the options for a terminal device interface
 .Sh SYNOPSIS
 .Nm
 .Op Fl a | e | g
 .Op Fl f Ar file
 .Op Ar arguments
 .Sh DESCRIPTION
 The
 .Nm
 utility sets or reports on terminal
 characteristics for the device that is its standard input.
 If no options or arguments are specified, it reports the settings of a subset
 of characteristics as well as additional ones if they differ from their
 default values.
 Otherwise it modifies
 the terminal state according to the specified arguments.
 Some combinations of arguments are mutually
 exclusive on some terminal types.
 .Pp
 The following options are available:
 .Bl -tag -width indent
 .It Fl a
 Display all the current settings for the terminal to standard output
 as per
 .St -p1003.2 .
 .It Fl e
 Display all the current settings for the terminal to standard output
 in the traditional
 .Bx
 ``all'' and ``everything'' formats.
 .It Fl f
 Open and use the terminal named by
 .Ar file
 rather than using standard input.
 The file is opened
 using the
 .Dv O_NONBLOCK
 flag of
 .Fn open ,
 making it possible to
 set or display settings on a terminal that might otherwise
 block on the open.
 .It Fl g
 Display all the current settings for the terminal to standard output
 in a form that may be used as an argument to a subsequent invocation of
 .Nm
 to restore the current terminal state as per
 .St -p1003.2 .
 .El
 .Pp
 The following arguments are available to set the terminal
 characteristics:
 .Ss Control Modes:
 Control mode flags affect hardware characteristics associated with the
 terminal.
 This corresponds to the c_cflag in the termios structure.
 .Bl -tag -width Fl
 .It Cm parenb Pq Fl parenb
 Enable (disable) parity generation
 and detection.
 .It Cm parodd Pq Fl parodd
 Select odd (even) parity.
 .It Cm cs5 cs6 cs7 cs8
 Select character size, if possible.
 .It Ar number
 Set terminal baud rate to the
 number given, if possible.
 If the
 baud rate is set to zero, modem
 control is no longer
 asserted.
 .It Cm ispeed Ar number
 Set terminal input baud rate to the
 number given, if possible.
 If the
 input baud rate is set to zero, the
 input baud rate is set to the
 value of the output baud
 rate.
 .It Cm ospeed Ar number
 Set terminal output baud rate to
 the number given, if possible.
 If
 the output baud rate is set to
 zero, modem control is
 no longer asserted.
 .It Cm speed Ar number
 This sets both
 .Cm ispeed
 and
 .Cm ospeed
 to
 .Ar number .
 .It Cm hupcl Pq Fl hupcl
 Stop asserting modem control
 (do not stop asserting modem control) on last close.
 .It Cm hup Pq Fl hup
 Same as hupcl
 .Pq Fl hupcl .
 .It Cm cstopb Pq Fl cstopb
 Use two (one) stop bits per character.
 .It Cm cread Pq Fl cread
 Enable (disable) the receiver.
 .It Cm clocal Pq Fl clocal
 Assume a line without (with) modem
 control.
 .It Cm crtscts Pq Fl crtscts
 Enable (disable) RTS/CTS flow control.
 .It Cm rtsdtr Pq Fl rtsdtr
 Enable (disable) asserting RTS/DTR on open.
 .El
 .Ss Input Modes:
 This corresponds to the c_iflag in the termios structure.
 .Bl -tag -width Fl
 .It Cm ignbrk Pq Fl ignbrk
 Ignore (do not ignore) break on
 input.
 .It Cm brkint Pq Fl brkint
 Signal (do not signal)
 .Dv INTR
 on
 break.
 .It Cm ignpar Pq Fl ignpar
 Ignore (do not ignore) characters with parity
 errors.
 .It Cm parmrk Pq Fl parmrk
 Mark (do not mark) characters with parity errors.
 .It Cm inpck Pq Fl inpck
 Enable (disable) input parity
 checking.
 .It Cm istrip Pq Fl istrip
 Strip (do not strip) input characters
 to seven bits.
 .It Cm inlcr Pq Fl inlcr
 Map (do not map)
 .Dv NL
 to
 .Dv CR
 on input.
 .It Cm igncr Pq Fl igncr
 Ignore (do not ignore)
 .Dv CR
 on input.
 .It Cm icrnl Pq Fl icrnl
 Map (do not map)
 .Dv CR
 to
 .Dv NL
 on input.
 .It Cm ixon Pq Fl ixon
 Enable (disable)
 .Dv START/STOP
 output
 control.
 Output from the system is
 stopped when the system receives
 .Dv STOP
 and started when the system
 receives
 .Dv START ,
 or if
 .Cm ixany
 is set, any character restarts output.
 .It Cm ixoff Pq Fl ixoff
 Request that the system send (not
 send)
 .Dv START/STOP
 characters when
 the input queue is nearly
 empty/full.
 .It Cm ixany Pq Fl ixany
 Allow any character (allow only
 .Dv START )
 to restart output.
 .It Cm imaxbel Pq Fl imaxbel
 The system imposes a limit of
 .Dv MAX_INPUT
 (currently 255) characters in the input queue.
 If
 .Cm imaxbel
 is set and the input queue limit has been reached,
 subsequent input causes the system to send an ASCII BEL
 character to the output queue (the terminal beeps at you).
 Otherwise,
 if
 .Cm imaxbel
 is unset and the input queue is full, the next input character causes
 the entire input and output queues to be discarded.
+.It Cm iutf8 Pq Fl iutf8
+Assume that input characters are UTF-8 encoded. Setting this flag
+causes backspace to properly delete multibyte characters in canonical mode.
 .El
 .Ss Output Modes:
 This corresponds to the c_oflag of the termios structure.
 .Bl -tag -width Fl
 .It Cm opost Pq Fl opost
 Post-process output (do not
 post-process output; ignore all other
 output modes).
 .It Cm onlcr Pq Fl onlcr
 Map (do not map)
 .Dv NL
 to
 .Dv CR-NL
 on output.
 .It Cm ocrnl Pq Fl ocrnl
 Map (do not map)
 .Dv CR
 to
 .Dv NL
 on output.
 .It Cm tab0 tab3
 Select tab expansion policy.
 .Cm tab0
 disables tab expansion, while
 .Cm tab3
 enables it.
 .It Cm onocr Pq Fl onocr
 Do not (do) output CRs at column zero.
 .It Cm onlret Pq Fl onlret
 On the terminal NL performs (does not perform) the CR function.
 .El
 .Ss Local Modes:
 Local mode flags (lflags) affect various and sundry characteristics of terminal
 processing.
 Historically the term "local" pertained to new job control features
 implemented by Jim Kulp on a
 .Tn Pdp 11/70
 at
 .Tn IIASA .
 Later the driver ran on the first
 .Tn VAX
 at Evans Hall, UC Berkeley, where the job control details
 were greatly modified but the structure definitions and names
 remained essentially unchanged.
 The second interpretation of the 'l' in lflag
 is ``line discipline flag'' which corresponds to the
 .Ar c_lflag
 of the
 .Ar termios
 structure.
 .Bl -tag -width Fl
 .It Cm isig Pq Fl isig
 Enable (disable) the checking of
 characters against the special control
 characters
 .Dv INTR , QUIT ,
 and
 .Dv SUSP .
 .It Cm icanon Pq Fl icanon
 Enable (disable) canonical input
 .Pf ( Dv ERASE
 and
 .Dv KILL
 processing).
 .It Cm iexten Pq Fl iexten
 Enable (disable) any implementation
 defined special control characters
 not currently controlled by icanon,
 isig, or ixon.
 .It Cm echo Pq Fl echo
 Echo back (do not echo back) every
 character typed.
 .It Cm echoe Pq Fl echoe
 The
 .Dv ERASE
 character shall (shall
 not) visually erase the last character
 in the current line from the
 display, if possible.
 .It Cm echok Pq Fl echok
 Echo (do not echo)
 .Dv NL
 after
 .Dv KILL
 character.
 .It Cm echoke Pq Fl echoke
 The
 .Dv KILL
 character shall (shall
 not) visually erase the
 current line from the
 display, if possible.
 .It Cm echonl Pq Fl echonl
 Echo (do not echo)
 .Dv NL ,
 even if echo
 is disabled.
 .It Cm echoctl Pq Fl echoctl
 If
 .Cm echoctl
 is set, echo control characters as ^X.
 Otherwise control characters
 echo as themselves.
 .It Cm echoprt Pq Fl echoprt
 For printing terminals.
 If set, echo erased characters backwards within ``\\''
 and ``/''.
 Otherwise, disable this feature.
 .It Cm noflsh Pq Fl noflsh
 Disable (enable) flush after
 .Dv INTR , QUIT , SUSP .
 .It Cm tostop Pq Fl tostop
 Send (do not send)
 .Dv SIGTTOU
 for background output.
 This causes background jobs to stop if they attempt
 terminal output.
 .It Cm altwerase Pq Fl altwerase
 Use (do not use) an alternate word erase algorithm when processing
 .Dv WERASE
 characters.
 This alternate algorithm considers sequences of
 alphanumeric/underscores as words.
 It also skips the first preceding character in its classification
 (as a convenience since the one preceding character could have been
 erased with simply an
 .Dv ERASE
 character.)
 .It Cm mdmbuf Pq Fl mdmbuf
 If set, flow control output based on condition of Carrier Detect.
 Otherwise
 writes return an error if Carrier Detect is low (and Carrier is not being
 ignored with the
 .Dv CLOCAL
 flag.)
 .It Cm flusho Pq Fl flusho
 Indicates output is (is not) being discarded.
 .It Cm pendin Pq Fl pendin
 Indicates input is (is not) pending after a switch from non-canonical
 to canonical mode and will be re-input when a read becomes pending
 or more input arrives.
 .El
 .Ss Control Characters:
 .Bl -tag -width Fl
 .It Ar control-character Ar string
 Set
 .Ar control-character
 to
 .Ar string .
 If string is a single character,
 the control character is set to
 that character.
 If string is the
 two character sequence "^-" or the
 string "undef" the control character
 is disabled (i.e., set to
 .Pf { Dv _POSIX_VDISABLE Ns } . )
 .Pp
 Recognized control-characters:
 .Bd -ragged -offset indent
 .Bl -column character Subscript
 .It control- Ta \& Ta \&
 .It character Ta Subscript Ta Description
 .It _________ Ta _________ Ta _______________
 .It eof Ta Tn VEOF Ta EOF No character
 .It eol Ta Tn VEOL Ta EOL No character
 .It eol2 Ta Tn VEOL2 Ta EOL2 No character
 .It erase Ta Tn VERASE Ta ERASE No character
 .It erase2 Ta Tn VERASE2 Ta ERASE2 No character
 .It werase Ta Tn VWERASE Ta WERASE No character
 .It intr Ta Tn VINTR Ta INTR No character
 .It kill Ta Tn VKILL Ta KILL No character
 .It quit Ta Tn VQUIT Ta QUIT No character
 .It susp Ta Tn VSUSP Ta SUSP No character
 .It start Ta Tn VSTART Ta START No character
 .It stop Ta Tn VSTOP Ta STOP No character
 .It dsusp Ta Tn VDSUSP Ta DSUSP No character
 .It lnext Ta Tn VLNEXT Ta LNEXT No character
 .It reprint Ta Tn VREPRINT Ta REPRINT No character
 .It status Ta Tn VSTATUS Ta STATUS No character
 .El
 .Ed
 .It Cm min Ar number
 .It Cm time Ar number
 Set the value of min or time to
 number.
 .Dv MIN
 and
 .Dv TIME
 are used in
 Non-Canonical mode input processing
 (-icanon).
 .El
 .Ss Combination Modes:
 .Bl -tag -width Fl
 .It Ar saved settings
 Set the current terminal
 characteristics to the saved settings
 produced by the
 .Fl g
 option.
 .It Cm evenp No or Cm parity
 Enable parenb and cs7; disable
 parodd.
 .It Cm oddp
 Enable parenb, cs7, and parodd.
 .It Fl parity , evenp , oddp
 Disable parenb, and set cs8.
 .It Cm \&nl Pq Fl \&nl
 Enable (disable) icrnl.
 In addition
 -nl unsets inlcr and igncr.
 .It Cm ek
 Reset
 .Dv ERASE ,
 .Dv ERASE2 ,
 and
 .Dv KILL
 characters
 back to system defaults.
 .It Cm sane
 Resets all modes to reasonable values for interactive terminal use.
 .It Cm tty
 Set the line discipline to the standard terminal line discipline
 .Dv TTYDISC .
 .It Cm crt Pq Fl crt
 Set (disable) all modes suitable for a CRT display device.
 .It Cm kerninfo Pq Fl kerninfo
 Enable (disable) the system generated status line associated with
 processing a
 .Dv STATUS
 character (usually set to ^T).
 The status line consists of the
 system load average, the current command name, its process ID, the
 event the process is waiting on (or the status of the process), the user
 and system times, percent cpu, and current memory usage.
 .Pp
 If the
 .Xr sysctl 8
 variable
 .Va kern.tty_info_kstacks
 is set to a non-zero value, the status message also includes the kernel program
 stack of the foreground thread.
 .It Cm columns Ar number
 The terminal size is recorded as having
 .Ar number
 columns.
 .It Cm cols Ar number
 is an alias for
 .Cm columns .
 .It Cm rows Ar number
 The terminal size is recorded as having
 .Ar number
 rows.
 .It Cm dec
 Set modes suitable for users of Digital Equipment Corporation systems
 .Dv ( ERASE ,
 .Dv KILL ,
 and
 .Dv INTR
 characters are set to ^?, ^U, and ^C;
 .Dv ixany
 is disabled, and
 .Dv crt
 is enabled.)
 .It Cm extproc Pq Fl extproc
 If set, this flag indicates that some amount of terminal processing is being
 performed by either the terminal hardware or by the remote side connected
 to a pty.
 .It Cm raw Pq Fl raw
 If set, change the modes of the terminal so that no input or output processing
 is performed.
 If unset, change the modes of the terminal to some reasonable
 state that performs input and output processing.
 Note that since the
 terminal driver no longer has a single
 .Dv RAW
 bit, it is not possible to intuit what flags were set prior to setting
 .Cm raw .
 This means that unsetting
 .Cm raw
 may not put back all the setting that were previously in effect.
 To set the terminal into a raw state and then accurately restore it, the following
 shell code is recommended:
 .Bd -literal
 save_state=$(stty -g)
 stty raw
 \&...
 stty "$save_state"
 .Ed
 .It Cm size
 The size of the terminal is printed as two numbers on a single line,
 first rows, then columns.
 .El
 .Ss Compatibility Modes:
 These modes remain for compatibility with the previous version of
 the
 .Nm
 command.
 .Bl -tag -width Fl
 .It Cm all
 Reports all the terminal modes as with
 .Cm stty Fl a
 except that the control characters are printed in a columnar format.
 .It Cm everything
 Same as
 .Cm all .
 .It Cm cooked
 Same as
 .Cm sane .
 .It Cm cbreak
 If set, enables
 .Cm brkint , ixon , imaxbel , opost ,
 .Cm isig , iexten ,
 and
 .Fl icanon .
 If unset, same as
 .Cm sane .
 .It Cm new
 Same as
 .Cm tty .
 .It Cm old
 Same as
 .Cm tty .
 .It Cm newcrt Pq Fl newcrt
 Same as
 .Cm crt .
 .It Cm pass8
 The converse of
 .Cm parity .
 .It Cm tandem Pq Fl tandem
 Same as
 .Cm ixoff .
 .It Cm decctlq Pq Fl decctlq
 The converse of
 .Cm ixany .
 .It Cm crterase Pq Fl crterase
 Same as
 .Cm echoe .
 .It Cm crtbs Pq Fl crtbs
 Same as
 .Cm echoe .
 .It Cm crtkill Pq Fl crtkill
 Same as
 .Cm echoke .
 .It Cm ctlecho Pq Fl ctlecho
 Same as
 .Cm echoctl .
 .It Cm prterase Pq Fl prterase
 Same as
 .Cm echoprt .
 .It Cm litout Pq Fl litout
 The converse of
 .Cm opost .
 .It Cm oxtabs Pq Fl oxtabs
 Expand (do not expand) tabs to spaces on output.
 .It Cm tabs Pq Fl tabs
 The converse of
 .Cm oxtabs .
 .It Cm brk Ar value
 Same as the control character
 .Cm eol .
 .It Cm flush Ar value
 Same as the control character
 .Cm discard .
 .It Cm rprnt Ar value
 Same as the control character
 .Cm reprint .
 .El
 .Sh INTERACTION WITH JOB CONTROL
 Modifications to the terminal settings are treated by job control
 (see
 .Xr termios 4 )
 same as writes.
 When the
 .Nm
 utility is executing in a background process group,
 such attempts result in the kernel sending the
 .Dv SIGTTOU
 signal and stopping the process until its group is returned
 to foreground.
 The non-blocking open of the terminal device with the
 .Fl f
 option to
 .Nm
 does not affect the behavior.
 If it is desirable to modify the settings from the background,
 .Xr sh 1
 users might utilize the following idiom:
 .Bd -literal
 (trap '' TTOU; stty -f /dev/tty sane)
 .Ed
 .Pp
 Note that changing terminal settings for a running foreground
 job that is not prepared for it might cause inconsistencies.
 .Sh EXIT STATUS
 .Ex -std
 .Sh SEE ALSO
 .Xr resizewin 1 ,
 .Xr termios 4 ,
 .Xr pstat 8
 .Sh STANDARDS
 The
 .Nm
 utility is expected to be
 .St -p1003.2
 compatible.
 The flags
 .Fl e
 and
 .Fl f
 are
 extensions to the standard.
 .Sh HISTORY
 A
 .Nm
 command appeared in
 .At v2 .
diff --git a/share/man/man4/termios.4 b/share/man/man4/termios.4
index d3a5c8fa582d..80015025345e 100644
--- a/share/man/man4/termios.4
+++ b/share/man/man4/termios.4
@@ -1,1597 +1,1599 @@
 .\" Copyright (c) 1991, 1992, 1993
 .\"	The 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. 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.
 .\"
 .\"	@(#)termios.4	8.4 (Berkeley) 4/19/94
 .\"
 .Dd June 28, 2020
 .Dt TERMIOS 4
 .Os
 .Sh NAME
 .Nm termios
 .Nd general terminal line discipline
 .Sh SYNOPSIS
 .In termios.h
 .Sh DESCRIPTION
 This describes a general terminal line discipline that is
 supported on tty asynchronous communication ports.
 .Ss Opening a Terminal Device File
 When a terminal file is opened, it normally causes the process to wait
 until a connection is established.
 For most hardware, the presence
 of a connection is indicated by the assertion of the hardware
 .Dv CARRIER
 line.
 If the termios structure associated with the terminal file has the
 .Dv CLOCAL
 flag set in the cflag, or if the
 .Dv O_NONBLOCK
 flag is set
 in the
 .Xr open 2
 call, then the open will succeed even without
 a connection being present.
 In practice, applications
 seldom open these files; they are opened by special programs, such
 as
 .Xr getty 8 ,
 and become
 an application's standard input, output, and error files.
 .Ss Job Control in a Nutshell
 Every process is associated with a particular process group and session.
 The grouping is hierarchical: every member of a particular process group is a
 member of the same session.
 This structuring is used in managing groups
 of related processes for purposes of
 .\" .Gw "job control" ;
 .Em "job control" ;
 that is, the
 ability from the keyboard (or from program control) to simultaneously
 stop or restart
 a complex command (a command composed of one or more related
 processes).
 The grouping into process groups allows delivering
 of signals that stop or start the group as a whole, along with
 arbitrating which process group has access to the single controlling
 terminal.
 The grouping at a higher layer into sessions is to restrict
 the job control related signals and system calls to within processes
 resulting from a particular instance of a
 .Dq login .
 Typically, a session
 is created when a user logs in, and the login terminal is setup
 to be the controlling terminal; all processes spawned from that
 login shell are in the same session, and inherit the controlling
 terminal.
 .Pp
 A job control shell
 operating interactively (that is, reading commands from a terminal)
 normally groups related processes together by placing them into the
 same process group.
 A set of processes in the same process group
 is collectively referred to as a
 .Dq job .
 When the foreground process
 group of the terminal is the same as the process group of a particular
 job, that job is said to be in the
 .Dq foreground .
 When the process group of the terminal is different from the process group of
 a job (but is still the controlling terminal), that job is said
 to be in the
 .Dq background .
 Normally the
 shell reads a command and starts the job that implements that
 command.
 If the command is to be started in the foreground (typical), it
 sets the process group of the terminal to the process group
 of the started job, waits for the job to complete, and then
 sets the process group of the terminal back to its own process
 group (it puts itself into the foreground).
 If the job is to
 be started in the background (as denoted by the shell operator "&"),
 it never changes the process group of the terminal and does not
 wait for the job to complete (that is, it immediately attempts to read the next
 command).
 If the job is started in the foreground, the user may
 type a key (usually
 .Ql \&^Z )
 which generates the terminal stop signal
 .Pq Dv SIGTSTP
 and has the effect of stopping the entire job.
 The shell will notice that the job stopped, and will resume running after
 placing itself in the foreground.
 The shell also has commands for placing stopped jobs in the background,
 and for placing stopped or background jobs into the foreground.
 .Ss Orphaned Process Groups
 An orphaned process group is a process group that has no process
 whose parent is in a different process group, yet is in the same
 session.
 Conceptually it means a process group that does not have
 a parent that could do anything if it were to be stopped.
 For example,
 the initial login shell is typically in an orphaned process group.
 Orphaned process groups are immune to keyboard generated stop
 signals and job control signals resulting from reads or writes to the
 controlling terminal.
 .Ss The Controlling Terminal
 A terminal may belong to a process as its controlling terminal.
 Each
 process of a session that has a controlling terminal has the same
 controlling terminal.
 A terminal may be the controlling terminal for at
 most one session.
 The controlling terminal for a session is allocated by
 the session leader by issuing the
 .Dv TIOCSCTTY
 ioctl.
 A controlling terminal
 is never acquired by merely opening a terminal device file.
 When a controlling terminal becomes
 associated with a session, its foreground process group is set to
 the process group of the session leader.
 .Pp
 The controlling terminal is inherited by a child process during a
 .Xr fork 2
 function call.
 A process relinquishes its controlling terminal when it
 creates a new session with the
 .Xr setsid 2
 function; other processes
 remaining in the old session that had this terminal as their controlling
 terminal continue to have it.
 A process does not relinquish its
 controlling terminal simply by closing all of its file descriptors
 associated with the controlling terminal if other processes continue to
 have it open.
 .Pp
 When a controlling process terminates, the controlling terminal is
 disassociated from the current session, allowing it to be acquired by a
 new session leader.
 Subsequent access to the terminal by other processes
 in the earlier session will be denied, with attempts to access the
 terminal treated as if modem disconnect had been sensed.
 .Ss Terminal Access Control
 If a process is in the foreground process group of its controlling
 terminal, read operations are allowed.
 Any attempts by a process
 in a background process group to read from its controlling terminal
 causes a
 .Dv SIGTTIN
 signal to be sent to
 the process's group
 unless one of the
 following special cases apply: if the reading process is ignoring or
 blocking the
 .Dv SIGTTIN
 signal, or if the process group of the reading
 process is orphaned, the
 .Xr read 2
 returns -1 with
 .Va errno
 set to
 .Er EIO
 and no
 signal is sent.
 The default action of the
 .Dv SIGTTIN
 signal is to stop the
 process to which it is sent.
 .Pp
 If a process is in the foreground process group of its controlling
 terminal, write operations are allowed.
 Attempts by a process in a background process group to write to its
 controlling terminal will cause the process group to be sent a
 .Dv SIGTTOU
 signal unless one of the following special cases apply: if
 .Dv TOSTOP
 is not
 set, or if
 .Dv TOSTOP
 is set and the process is ignoring or blocking the
 .Dv SIGTTOU
 signal, the process is allowed to write to the terminal and the
 .Dv SIGTTOU
 signal is not sent.
 If
 .Dv TOSTOP
 is set, and the process group of
 the writing process is orphaned, and the writing process is not ignoring
 or blocking
 .Dv SIGTTOU ,
 the
 .Xr write 2
 returns -1 with
 errno set to
 .Er EIO
 and no signal is sent.
 .Pp
 Certain calls that set terminal parameters are treated in the same
 fashion as write, except that
 .Dv TOSTOP
 is ignored; that is, the effect is
 identical to that of terminal writes when
 .Dv TOSTOP
 is set.
 .Ss Input Processing and Reading Data
 A terminal device associated with a terminal device file may operate in
 full-duplex mode, so that data may arrive even while output is occurring.
 Each terminal device file has associated with it an input queue, into
 which incoming data is stored by the system before being read by a
 process.
 The system imposes a limit,
 .Pf \&{ Dv MAX_INPUT Ns \&} ,
 on the number of
 bytes that may be stored in the input queue.
 The behavior of the system
 when this limit is exceeded depends on the setting of the
 .Dv IMAXBEL
 flag in the termios
 .Fa c_iflag .
 If this flag is set, the terminal
 is sent an
 .Tn ASCII
 .Dv BEL
 character each time a character is received
 while the input queue is full.
 Otherwise, the input queue is flushed upon receiving the character.
 .Pp
 Two general kinds of input processing are available, determined by
 whether the terminal device file is in canonical mode or noncanonical
 mode.
 Additionally,
 input characters are processed according to the
 .Fa c_iflag
 and
 .Fa c_lflag
 fields.
 Such processing can include echoing, which
 in general means transmitting input characters immediately back to the
 terminal when they are received from the terminal.
 This is useful for terminals that can operate in full-duplex mode.
 .Pp
 The manner in which data is provided to a process reading from a terminal
 device file is dependent on whether the terminal device file is in
 canonical or noncanonical mode.
 .Pp
 Another dependency is whether the
 .Dv O_NONBLOCK
 flag is set by
 .Xr open 2
 or
 .Xr fcntl 2 .
 If the
 .Dv O_NONBLOCK
 flag is clear, then the read request is
 blocked until data is available or a signal has been received.
 If the
 .Dv O_NONBLOCK
 flag is set, then the read request is completed, without
 blocking, in one of three ways:
 .Bl -enum -offset indent
 .It
 If there is enough data available to satisfy the entire request,
 and the read completes successfully the number of
 bytes read is returned.
 .It
 If there is not enough data available to satisfy the entire
 request, and the read completes successfully, having read as
 much data as possible, the number of bytes read is returned.
 .It
 If there is no data available, the read returns -1, with
 errno set to
 .Er EAGAIN .
 .El
 .Pp
 When data is available depends on whether the input processing mode is
 canonical or noncanonical.
 .Ss Canonical Mode Input Processing
 In canonical mode input processing, terminal input is processed in units
 of lines.
 A line is delimited by a newline
 .Ql \&\en
 character, an end-of-file
 .Pq Dv EOF
 character, or an end-of-line
 .Pq Dv EOL
 character.
 See the
 .Sx "Special Characters"
 section for
 more information on
 .Dv EOF
 and
 .Dv EOL .
 This means that a read request will
 not return until an entire line has been typed, or a signal has been
 received.
 Also, no matter how many bytes are requested in the read call,
 at most one line is returned.
 It is not, however, necessary to
 read a whole line at once; any number of bytes, even one, may be
 requested in a read without losing information.
 .Pp
 .Pf \&{ Dv MAX_CANON Ns \&}
 is a limit on the
 number of bytes in a line.
 The behavior of the system when this limit is
 exceeded is the same as when the input queue limit
 .Pf \&{ Dv MAX_INPUT Ns \&} ,
 is exceeded.
 .Pp
 Erase and kill processing occur when either of two special characters,
 the
 .Dv ERASE
 and
 .Dv KILL
 characters (see the
 .Sx "Special Characters"
 section), is received.
 This processing affects data in the input queue that has not yet been
 delimited by a newline
 .Dv NL ,
 .Dv EOF ,
 or
 .Dv EOL
 character.
 This un-delimited
 data makes up the current line.
 The
 .Dv ERASE
 character deletes the last
 character in the current line, if there is any.
 The
 .Dv KILL
 character
 deletes all data in the current line, if there is any.
 The
 .Dv ERASE
 and
 .Dv KILL
 characters have no effect if there is no data in the current line.
 The
 .Dv ERASE
 and
 .Dv KILL
 characters themselves are not placed in the input
 queue.
 .Ss Noncanonical Mode Input Processing
 In noncanonical mode input processing, input bytes are not assembled into
 lines, and erase and kill processing does not occur.
 The values of the
 .Dv VMIN
 and
 .Dv VTIME
 members of the
 .Fa c_cc
 array are used to determine how to
 process the bytes received.
 .Pp
 .Dv MIN
 represents the minimum number of bytes that should be received when
 the
 .Xr read 2
 function successfully returns.
 .Dv TIME
 is a timer of 0.1 second
 granularity that is used to time out bursty and short term data
 transmissions.
 If
 .Dv MIN
 is greater than
 .Dv \&{ Dv MAX_INPUT Ns \&} ,
 the response to the
 request is undefined.
 The four possible values for
 .Dv MIN
 and
 .Dv TIME
 and
 their interactions are described below.
 .Ss "Case A: MIN > 0, TIME > 0"
 In this case
 .Dv TIME
 serves as an inter-byte timer and is activated after
 the first byte is received.
 Since it is an inter-byte timer, it is reset
 after a byte is received.
 The interaction between
 .Dv MIN
 and
 .Dv TIME
 is as
 follows: as soon as one byte is received, the inter-byte timer is
 started.
 If
 .Dv MIN
 bytes are received before the inter-byte timer expires
 (remember that the timer is reset upon receipt of each byte), the read is
 satisfied.
 If the timer expires before
 .Dv MIN
 bytes are received, the
 characters received to that point are returned to the user.
 Note that if
 .Dv TIME
 expires at least one byte is returned because the timer would
 not have been enabled unless a byte was received.
 In this case
 .Pf \&( Dv MIN
 > 0,
 .Dv TIME
 > 0) the read blocks until the
 .Dv MIN
 and
 .Dv TIME
 mechanisms are
 activated by the receipt of the first byte, or a signal is received.
 If data is in the buffer at the time of the
 .Fn read ,
 the result is as
 if data had been received immediately after the
 .Fn read .
 .Ss "Case B: MIN > 0, TIME = 0"
 In this case, since the value of
 .Dv TIME
 is zero, the timer plays no role
 and only
 .Dv MIN
 is significant.
 A pending read is not satisfied until
 .Dv MIN
 bytes are received (i.e., the pending read blocks until
 .Dv MIN
 bytes
 are received), or a signal is received.
 A program that uses this case to read record-based terminal
 .Dv I/O
 may block indefinitely in the read
 operation.
 .Ss "Case C: MIN = 0, TIME > 0"
 In this case, since
 .Dv MIN
 = 0,
 .Dv TIME
 no longer represents an inter-byte
 timer.
 It now serves as a read timer that is activated as soon as the
 read function is processed.
 A read is satisfied as soon as a single
 byte is received or the read timer expires.
 Note that in this case if the timer expires, no bytes are returned.
 If the timer does not
 expire, the only way the read can be satisfied is if a byte is received.
 In this case the read will not block indefinitely waiting for a byte; if
 no byte is received within
 .Dv TIME Ns *0.1
 seconds after the read is initiated,
 the read returns a value of zero, having read no data.
 If data is
 in the buffer at the time of the read, the timer is started as if
 data had been received immediately after the read.
 .Ss Case D: MIN = 0, TIME = 0
 The minimum of either the number of bytes requested or the number of
 bytes currently available is returned without waiting for more
 bytes to be input.
 If no characters are available, read returns a
 value of zero, having read no data.
 .Ss Writing Data and Output Processing
 When a process writes one or more bytes to a terminal device file, they
 are processed according to the
 .Fa c_oflag
 field (see the
 .Sx "Output Modes"
 section).
 The
 implementation may provide a buffering mechanism; as such, when a call to
 .Fn write
 completes, all of the bytes written have been scheduled for
 transmission to the device, but the transmission will not necessarily
 have been completed.
 .\" See also .Sx "6.4.2" for the effects of
 .\" .Dv O_NONBLOCK
 .\" on write.
 .Ss Special Characters
 Certain characters have special functions on input or output or both.
 These functions are summarized as follows:
 .Bl -tag -width indent
 .It Dv INTR
 Special character on input and is recognized if the
 .Dv ISIG
 flag (see the
 .Sx "Local Modes"
 section) is enabled.
 Generates a
 .Dv SIGINT
 signal which is sent to all processes in the foreground
 process group for which the terminal is the controlling
 terminal.
 If
 .Dv ISIG
 is set, the
 .Dv INTR
 character is
 discarded when processed.
 .It Dv QUIT
 Special character on input and is recognized if the
 .Dv ISIG
 flag is enabled.
 Generates a
 .Dv SIGQUIT
 signal which is
 sent to all processes in the foreground process group
 for which the terminal is the controlling terminal.
 If
 .Dv ISIG
 is set, the
 .Dv QUIT
 character is discarded when
 processed.
 .It Dv ERASE
 Special character on input and is recognized if the
 .Dv ICANON
 flag is set.
 Erases the last character in the
 current line; see
 .Sx "Canonical Mode Input Processing" .
 It does not erase beyond
 the start of a line, as delimited by an
 .Dv NL ,
 .Dv EOF ,
 or
 .Dv EOL
 character.
 If
 .Dv ICANON
 is set, the
 .Dv ERASE
 character is
 discarded when processed.
 .It Dv KILL
 Special character on input and is recognized if the
 .Dv ICANON
 flag is set.
 Deletes the entire line, as
 delimited by a
 .Dv NL ,
 .Dv EOF ,
 or
 .Dv EOL
 character.
 If
 .Dv ICANON
 is set, the
 .Dv KILL
 character is discarded when processed.
 .It Dv EOF
 Special character on input and is recognized if the
 .Dv ICANON
 flag is set.
 When received, all the bytes
 waiting to be read are immediately passed to the
 process, without waiting for a newline, and the
 .Dv EOF
 is discarded.
 Thus, if there are no bytes waiting (that is, the
 .Dv EOF
 occurred at the beginning of a line), a byte
 count of zero is returned from the
 .Fn read ,
 representing an end-of-file indication.
 If
 .Dv ICANON
 is
 set, the
 .Dv EOF
 character is discarded when processed.
 .It Dv NL
 Special character on input and is recognized if the
 .Dv ICANON
 flag is set.
 It is the line delimiter
 .Ql \&\en .
 .It Dv EOL
 Special character on input and is recognized if the
 .Dv ICANON
 flag is set.
 Is an additional line delimiter, like
 .Dv NL .
 .It Dv SUSP
 If the
 .Dv ISIG
 flag is enabled, receipt of the
 .Dv SUSP
 character causes a
 .Dv SIGTSTP
 signal to be sent to all processes in the
 foreground process group for which the terminal is the
 controlling terminal, and the
 .Dv SUSP
 character is
 discarded when processed.
 .It Dv STOP
 Special character on both input and output and is
 recognized if the
 .Dv IXON
 (output control) or
 .Dv IXOFF
 (input
 control) flag is set.
 Can be used to temporarily suspend output.
 It is useful with fast terminals to
 prevent output from disappearing before it can be read.
 If
 .Dv IXON
 is set, the
 .Dv STOP
 character is discarded when
 processed.
 .It Dv START
 Special character on both input and output and is
 recognized if the
 .Dv IXON
 (output control) or
 .Dv IXOFF
 (input
 control) flag is set.
 Can be used to resume output that has been suspended by a
 .Dv STOP
 character.
 If
 .Dv IXON
 is set, the
 .Dv START
 character is discarded when processed.
 .It Dv CR
 Special character on input and is recognized if the
 .Dv ICANON
 flag is set; it is the
 .Ql \&\er ,
 as denoted in the
 .Tn \&C
 Standard {2}.
 When
 .Dv ICANON
 and
 .Dv ICRNL
 are set and
 .Dv IGNCR
 is not set, this character is translated into a
 .Dv NL ,
 and
 has the same effect as a
 .Dv NL
 character.
 .El
 .Pp
 The following special characters are extensions defined by this
 system and are not a part of
 .St -p1003.1
 termios.
 .Bl -tag -width indent
 .It Dv EOL2
 Secondary
 .Dv EOL
 character.
 Same function as
 .Dv EOL .
 .It Dv WERASE
 Special character on input and is recognized if the
 .Dv ICANON
 flag is set.
 Erases the last word in the current line according to one of two algorithms.
 If the
 .Dv ALTWERASE
 flag is not set, first any preceding whitespace is
 erased, and then the maximal sequence of non-whitespace
 characters.
 If
 .Dv ALTWERASE
 is set, first any preceding
 whitespace is erased, and then the maximal sequence
 of alphabetic/underscores or non alphabetic/underscores.
 As a special case in this second algorithm, the first previous
 non-whitespace character is skipped in determining
 whether the preceding word is a sequence of
 alphabetic/underscores.
 This sounds confusing but turns out to be quite practical.
 .It Dv REPRINT
 Special character on input and is recognized if the
 .Dv ICANON
 flag is set.
 Causes the current input edit line to be retyped.
 .It Dv DSUSP
 Has similar actions to the
 .Dv SUSP
 character, except that
 the
 .Dv SIGTSTP
 signal is delivered when one of the processes
 in the foreground process group issues a
 .Fn read
 to the
 controlling terminal.
 .It Dv LNEXT
 Special character on input and is recognized if the
 .Dv IEXTEN
 flag is set.
 Receipt of this character causes the next character to be taken literally.
 .It Dv DISCARD
 Special character on input and is recognized if the
 .Dv IEXTEN
 flag is set.
 Receipt of this character toggles the flushing of terminal output.
 .It Dv STATUS
 Special character on input and is recognized if the
 .Dv ICANON
 flag is set.
 Receipt of this character causes a
 .Dv SIGINFO
 signal to be sent to the foreground process group of the
 terminal.
 Also, if the
 .Dv NOKERNINFO
 flag is not set, it
 causes the kernel to write a status message to the terminal
 that displays the current load average, the name of the
 command in the foreground, its process ID, the symbolic
 wait channel, the number of user and system seconds used,
 the percentage of cpu the process is getting, and the resident
 set size of the process.
 .Pp
 In case the
 .Xr sysctl 8
 variable
 .Va kern.tty_info_kstacks
 is set to a non-zero value, the running thread's kernel stack is
 written to the terminal (e.g., for debugging purposes).
 .El
 .Pp
 The
 .Dv NL
 and
 .Dv CR
 characters cannot be changed.
 The values for all the remaining characters can be set and are
 described later in the document under
 Special Control Characters.
 .Pp
 Special
 character functions associated with changeable special control characters
 can be disabled individually by setting their value to
 .Dv {_POSIX_VDISABLE} ;
 see
 .Sx "Special Control Characters" .
 .Pp
 If two or more special characters have the same value, the function
 performed when that character is received is undefined.
 .Ss Modem Disconnect
 If a modem disconnect is detected by the terminal interface for a
 controlling terminal, and if
 .Dv CLOCAL
 is not set in the
 .Fa c_cflag
 field for
 the terminal, the
 .Dv SIGHUP
 signal is sent to the controlling
 process associated with the terminal.
 Unless other arrangements have
 been made, this causes the controlling process to terminate.
 Any subsequent call to the
 .Fn read
 function returns the value zero,
 indicating end of file.
 Thus, processes that read a terminal
 file and test for end-of-file can terminate appropriately after a
 disconnect.
 .\" If the
 .\" .Er EIO
 .\" condition specified in 6.1.1.4 that applies
 .\" when the implementation supports job control also exists, it is
 .\" unspecified whether the
 .\" .Dv EOF
 .\" condition or the
 .\" .Pf [ Dv EIO
 .\" ] is returned.
 Any
 subsequent
 .Fn write
 to the terminal device returns -1, with
 .Va errno
 set to
 .Er EIO ,
 until the device is closed.
 .Sh General Terminal Interface
 .Ss Closing a Terminal Device File
 The last process to close a terminal device file causes any output
 to be sent to the device and any input to be discarded.
 Then, if
 .Dv HUPCL
 is set in the control modes, and the communications port supports a
 disconnect function, the terminal device performs a disconnect.
 .Ss Parameters That Can Be Set
 Routines that need to control certain terminal
 .Tn I/O
 characteristics
 do so by using the termios structure as defined in the header
 .In termios.h .
 This structure contains minimally four scalar elements of bit flags
 and one array of special characters.
 The scalar flag elements are named:
 .Fa c_iflag ,
 .Fa c_oflag ,
 .Fa c_cflag ,
 and
 .Fa c_lflag .
 The character array is named
 .Fa c_cc ,
 and its maximum index is
 .Dv NCCS .
 .Ss Input Modes
 Values of the
 .Fa c_iflag
 field describe the basic
 terminal input control, and are composed of
 following masks:
 .Pp
 .Bl -tag -width IMAXBEL -offset indent -compact
 .It Dv IGNBRK
 /* ignore BREAK condition */
 .It Dv BRKINT
 /* map BREAK to SIGINTR */
 .It Dv IGNPAR
 /* ignore (discard) parity errors */
 .It Dv PARMRK
 /* mark parity and framing errors */
 .It Dv INPCK
 /* enable checking of parity errors */
 .It Dv ISTRIP
 /* strip 8th bit off chars */
 .It Dv INLCR
 /* map NL into CR */
 .It Dv IGNCR
 /* ignore CR */
 .It Dv ICRNL
 /* map CR to NL (ala CRMOD) */
 .It Dv IXON
 /* enable output flow control */
 .It Dv IXOFF
 /* enable input flow control */
 .It Dv IXANY
 /* any char will restart after stop */
 .It Dv IMAXBEL
 /* ring bell on input queue full */
+.It Dv IUTF8
+/* assume input is UTF-8 encoded */
 .El
 .Pp
 In the context of asynchronous serial data transmission, a break
 condition is defined as a sequence of zero-valued bits that continues for
 more than the time to send one byte.
 The entire sequence of zero-valued
 bits is interpreted as a single break condition, even if it continues for
 a time equivalent to more than one byte.
 In contexts other than
 asynchronous serial data transmission the definition of a break condition
 is implementation defined.
 .Pp
 If
 .Dv IGNBRK
 is set, a break condition detected on input is ignored, that
 is, not put on the input queue and therefore not read by any process.
 If
 .Dv IGNBRK
 is not set and
 .Dv BRKINT
 is set, the break condition flushes the
 input and output queues and if the terminal is the controlling terminal
 of a foreground process group, the break condition generates a
 single
 .Dv SIGINT
 signal to that foreground process group.
 If neither
 .Dv IGNBRK
 nor
 .Dv BRKINT
 is set, a break condition is read as a single
 .Ql \&\e0 ,
 or if
 .Dv PARMRK
 is set, as
 .Ql \&\e377 ,
 .Ql \&\e0 ,
 .Ql \&\e0 .
 .Pp
 If
 .Dv IGNPAR
 is set, a byte with a framing or parity error (other than
 break) is ignored.
 .Pp
 If
 .Dv PARMRK
 is set, and
 .Dv IGNPAR
 is not set, a byte with a framing or parity
 error (other than break) is given to the application as the
 three-character sequence
 .Ql \&\e377 ,
 .Ql \&\e0 ,
 X, where
 .Ql \&\e377 ,
 .Ql \&\e0
 is a two-character
 flag preceding each sequence and X is the data of the character received
 in error.
 To avoid ambiguity in this case, if
 .Dv ISTRIP
 is not set, a valid
 character of
 .Ql \&\e377
 is given to the application as
 .Ql \&\e377 ,
 .Ql \&\e377 .
 If
 neither
 .Dv PARMRK
 nor
 .Dv IGNPAR
 is set, a framing or parity error (other than
 break) is given to the application as a single character
 .Ql \&\e0 .
 .Pp
 If
 .Dv INPCK
 is set, input parity checking is enabled.
 If
 .Dv INPCK
 is not set,
 input parity checking is disabled, allowing output parity generation
 without input parity errors.
 Note that whether input parity checking is
 enabled or disabled is independent of whether parity detection is enabled
 or disabled (see
 .Sx "Control Modes" ) .
 If parity detection is enabled but input
 parity checking is disabled, the hardware to which the terminal is
 connected recognizes the parity bit, but the terminal special file
 does not check whether this bit is set correctly or not.
 .Pp
 If
 .Dv ISTRIP
 is set, valid input bytes are first stripped to seven bits,
 otherwise all eight bits are processed.
 .Pp
 If
 .Dv INLCR
 is set, a received
 .Dv NL
 character is translated into a
 .Dv CR
 character.
 If
 .Dv IGNCR
 is set, a received
 .Dv CR
 character is ignored (not
 read).
 If
 .Dv IGNCR
 is not set and
 .Dv ICRNL
 is set, a received
 .Dv CR
 character is
 translated into a
 .Dv NL
 character.
 .Pp
 If
 .Dv IXON
 is set, start/stop output control is enabled.
 A received
 .Dv STOP
 character suspends output and a received
 .Dv START
 character
 restarts output.
 If
 .Dv IXANY
 is also set, then any character may
 restart output.
 When
 .Dv IXON
 is set,
 .Dv START
 and
 .Dv STOP
 characters are not
 read, but merely perform flow control functions.
 When
 .Dv IXON
 is not set,
 the
 .Dv START
 and
 .Dv STOP
 characters are read.
 .Pp
 If
 .Dv IXOFF
 is set, start/stop input control is enabled.
 The system shall transmit one or more
 .Dv STOP
 characters, which are intended to cause the
 terminal device to stop transmitting data, as needed to prevent the input
 queue from overflowing and causing the undefined behavior described in
 .Sx "Input Processing and Reading Data" ,
 and shall transmit one or more
 .Dv START
 characters, which are
 intended to cause the terminal device to resume transmitting data, as
 soon as the device can continue transmitting data without risk of
 overflowing the input queue.
 The precise conditions under which
 .Dv STOP
 and
 .Dv START
 characters are transmitted are implementation defined.
 .Pp
 If
 .Dv IMAXBEL
 is set and the input queue is full, subsequent input shall cause an
 .Tn ASCII
 .Dv BEL
 character to be transmitted to
 the output queue.
 .Pp
 The initial input control value after
 .Fn open
 is implementation defined.
 .Ss Output Modes
 Values of the
 .Fa c_oflag
 field describe the basic terminal output control,
 and are composed of the following masks:
 .Pp
 .Bl -tag -width ONOEOT -offset indent -compact
 .It Dv OPOST
 /* enable following output processing */
 .It Dv ONLCR
 /* map NL to CR-NL (ala
 .Dv CRMOD )
 */
 .It Dv OCRNL
 /* map CR to NL */
 .It Dv TABDLY
 /* tab delay mask */
 .It Dv TAB0
 /* no tab delay and expansion */
 .It Dv TAB3
 /* expand tabs to spaces */
 .It Dv ONOEOT
 /* discard
 .Dv EOT Ns 's
 .Ql \&^D
 on output) */
 .It Dv ONOCR
 /* do not transmit CRs on column 0 */
 .It Dv ONLRET
 /* on the terminal NL performs the CR function */
 .El
 .Pp
 If
 .Dv OPOST
 is set, the remaining flag masks are interpreted as follows;
 otherwise characters are transmitted without change.
 .Pp
 If
 .Dv ONLCR
 is set, newlines are translated to carriage return, linefeeds.
 .Pp
 If
 .Dv OCRNL
 is set, carriage returns are translated to newlines.
 .Pp
 The
 .Dv TABDLY
 bits specify the tab delay.
 The
 .Fa c_oflag
 is masked with
 .Dv TABDLY
 and compared with the
 values
 .Dv TAB0
 or
 .Dv TAB3 .
 If
 .Dv TAB3
 is set, tabs are expanded to the appropriate number of
 spaces (assuming 8 column tab stops).
 .Pp
 If
 .Dv ONOEOT
 is set,
 .Tn ASCII
 .Dv EOT Ns 's
 are discarded on output.
 .Pp
 If
 .Dv ONOCR
 is set, no CR character is transmitted when at column 0 (first position).
 .Pp
 If
 .Dv ONLRET
 is set, the NL character is assumed to do the carriage-return function;
 the column pointer will be set to 0.
 .Ss Control Modes
 Values of the
 .Fa c_cflag
 field describe the basic
 terminal hardware control, and are composed of the
 following masks.
 Not all values
 specified are supported by all hardware.
 .Pp
 .Bl -tag -width CRTSXIFLOW -offset indent -compact
 .It Dv CSIZE
 /* character size mask */
 .It Dv CS5
 /* 5 bits (pseudo) */
 .It Dv CS6
 /* 6 bits */
 .It Dv CS7
 /* 7 bits */
 .It Dv CS8
 /* 8 bits */
 .It Dv CSTOPB
 /* send 2 stop bits */
 .It Dv CREAD
 /* enable receiver */
 .It Dv PARENB
 /* parity enable */
 .It Dv PARODD
 /* odd parity, else even */
 .It Dv HUPCL
 /* hang up on last close */
 .It Dv CLOCAL
 /* ignore modem status lines */
 .It Dv CCTS_OFLOW
 /*
 .Dv CTS
 flow control of output */
 .It Dv CRTSCTS
 /* same as
 .Dv CCTS_OFLOW
 */
 .It Dv CRTS_IFLOW
 /* RTS flow control of input */
 .It Dv MDMBUF
 /* flow control output via Carrier */
 .It Dv CNO_RTSDTR
 /* Do not assert RTS or DTR automatically */
 .El
 .Pp
 The
 .Dv CSIZE
 bits specify the byte size in bits for both transmission and
 reception.
 The
 .Fa c_cflag
 is masked with
 .Dv CSIZE
 and compared with the
 values
 .Dv CS5 ,
 .Dv CS6 ,
 .Dv CS7 ,
 or
 .Dv CS8 .
 This size does not include the parity bit, if any.
 If
 .Dv CSTOPB
 is set, two stop bits are used, otherwise one stop bit.
 For example, at 110 baud, two stop bits are normally used.
 .Pp
 If
 .Dv CREAD
 is set, the receiver is enabled.
 Otherwise, no character is received.
 Not all hardware supports this bit.
 In fact, this flag is pretty silly and if it were not part of the
 .Nm
 specification
 it would be omitted.
 .Pp
 If
 .Dv PARENB
 is set, parity generation and detection are enabled and a parity
 bit is added to each character.
 If parity is enabled,
 .Dv PARODD
 specifies
 odd parity if set, otherwise even parity is used.
 .Pp
 If
 .Dv HUPCL
 is set, the modem control lines for the port are lowered
 when the last process with the port open closes the port or the process
 terminates.
 The modem connection is broken.
 .Pp
 If
 .Dv CLOCAL
 is set, a connection does not depend on the state of the modem
 status lines.
 If
 .Dv CLOCAL
 is clear, the modem status lines are
 monitored.
 .Pp
 Under normal circumstances, a call to the
 .Fn open
 function waits for
 the modem connection to complete.
 However, if the
 .Dv O_NONBLOCK
 flag is set
 or if
 .Dv CLOCAL
 has been set, the
 .Fn open
 function returns
 immediately without waiting for the connection.
 .Pp
 The
 .Dv CCTS_OFLOW
 .Pf ( Dv CRTSCTS )
 flag is currently unused.
 .Pp
 If
 .Dv MDMBUF
 is set then output flow control is controlled by the state
 of Carrier Detect.
 .Pp
 If
 .Dv CNO_RTSDTR
 is set then the RTS and DTR lines will not be asserted when the device
 is opened.
 As a result, this flag is only useful on initial-state devices.
 .Pp
 If the object for which the control modes are set is not an asynchronous
 serial connection, some of the modes may be ignored; for example, if an
 attempt is made to set the baud rate on a network connection to a
 terminal on another host, the baud rate may or may not be set on the
 connection between that terminal and the machine it is directly connected
 to.
 .Ss Local Modes
 Values of the
 .Fa c_lflag
 field describe the control of
 various functions, and are composed of the following
 masks.
 .Pp
 .Bl -tag -width NOKERNINFO -offset indent -compact
 .It Dv ECHOKE
 /* visual erase for line kill */
 .It Dv ECHOE
 /* visually erase chars */
 .It Dv ECHO
 /* enable echoing */
 .It Dv ECHONL
 /* echo
 .Dv NL
 even if
 .Dv ECHO
 is off */
 .It Dv ECHOPRT
 /* visual erase mode for hardcopy */
 .It Dv ECHOCTL
 /* echo control chars as ^(Char) */
 .It Dv ISIG
 /* enable signals
 .Dv INTR ,
 .Dv QUIT ,
 .Dv [D]SUSP
 */
 .It Dv ICANON
 /* canonicalize input lines */
 .It Dv ALTWERASE
 /* use alternate
 .Dv WERASE
 algorithm */
 .It Dv IEXTEN
 /* enable
 .Dv DISCARD
 and
 .Dv LNEXT
 */
 .It Dv EXTPROC
 /* external processing */
 .It Dv TOSTOP
 /* stop background jobs from output */
 .It Dv FLUSHO
 /* output being flushed (state) */
 .It Dv NOKERNINFO
 /* no kernel output from
 .Dv VSTATUS
 */
 .It Dv PENDIN
 /* XXX retype pending input (state) */
 .It Dv NOFLSH
 /* don't flush after interrupt */
 .El
 .Pp
 If
 .Dv ECHO
 is set, input characters are echoed back to the terminal.
 If
 .Dv ECHO
 is not set, input characters are not echoed.
 .Pp
 If
 .Dv ECHOE
 and
 .Dv ICANON
 are set, the
 .Dv ERASE
 character causes the terminal
 to erase the last character in the current line from the display, if
 possible.
 If there is no character to erase, an implementation may echo
 an indication that this was the case or do nothing.
 .Pp
 If
 .Dv ECHOK
 and
 .Dv ICANON
 are set, the
 .Dv KILL
 character causes
 the current line to be discarded and the system echoes the
 .Ql \&\en
 character after the
 .Dv KILL
 character.
 .Pp
 If
 .Dv ECHOKE
 and
 .Dv ICANON
 are set, the
 .Dv KILL
 character causes
 the current line to be discarded and the system causes
 the terminal
 to erase the line from the display.
 .Pp
 If
 .Dv ECHOPRT
 and
 .Dv ICANON
 are set, the system assumes
 that the display is a printing device and prints a
 backslash and the erased characters when processing
 .Dv ERASE
 characters, followed by a forward slash.
 .Pp
 If
 .Dv ECHOCTL
 is set, the system echoes control characters
 in a visible fashion using a caret followed by the control character.
 .Pp
 If
 .Dv ALTWERASE
 is set, the system uses an alternative algorithm
 for determining what constitutes a word when processing
 .Dv WERASE
 characters (see
 .Dv WERASE ) .
 .Pp
 If
 .Dv ECHONL
 and
 .Dv ICANON
 are set, the
 .Ql \&\en
 character echoes even if
 .Dv ECHO
 is not set.
 .Pp
 If
 .Dv ICANON
 is set, canonical processing is enabled.
 This enables the
 erase and kill edit functions, and the assembly of input characters into
 lines delimited by
 .Dv NL ,
 .Dv EOF ,
 and
 .Dv EOL ,
 as described in
 .Sx "Canonical Mode Input Processing" .
 .Pp
 If
 .Dv ICANON
 is not set, read requests are satisfied directly from the input
 queue.
 A read is not satisfied until at least
 .Dv MIN
 bytes have been
 received or the timeout value
 .Dv TIME
 expired between bytes.
 The time value
 represents tenths of seconds.
 See
 .Sx "Noncanonical Mode Input Processing"
 for more details.
 .Pp
 If
 .Dv ISIG
 is set, each input character is checked against the special
 control characters
 .Dv INTR ,
 .Dv QUIT ,
 and
 .Dv SUSP
 (job control only).
 If an input
 character matches one of these control characters, the function
 associated with that character is performed.
 If
 .Dv ISIG
 is not set, no
 checking is done.
 Thus these special input functions are possible only
 if
 .Dv ISIG
 is set.
 .Pp
 If
 .Dv IEXTEN
 is set, implementation-defined functions are recognized
 from the input data.
 How
 .Dv IEXTEN
 being set
 interacts with
 .Dv ICANON ,
 .Dv ISIG ,
 .Dv IXON ,
 or
 .Dv IXOFF
 is implementation defined.
 If
 .Dv IEXTEN
 is not set, then
 implementation-defined functions are not recognized, and the
 corresponding input characters are not processed as described for
 .Dv ICANON ,
 .Dv ISIG ,
 .Dv IXON ,
 and
 .Dv IXOFF .
 .Pp
 If
 .Dv NOFLSH
 is set, the normal flush of the input and output queues
 associated with the
 .Dv INTR ,
 .Dv QUIT ,
 and
 .Dv SUSP
 characters
 are not be done.
 .Pp
 If
 .Dv TOSTOP
 is set, the signal
 .Dv SIGTTOU
 is sent to the process group of a process that tries to write to
 its controlling terminal if it is not in the foreground process group for
 that terminal.
 This signal, by default, stops the members of the process group.
 Otherwise, the output generated by that process is output to the
 current output stream.
 Processes that are blocking or ignoring
 .Dv SIGTTOU
 signals are excepted and allowed to produce output and the
 .Dv SIGTTOU
 signal
 is not sent.
 .Pp
 If
 .Dv NOKERNINFO
 is set, the kernel does not produce a status message
 when processing
 .Dv STATUS
 characters (see
 .Dv STATUS ) .
 .Ss Special Control Characters
 The special control characters values are defined by the array
 .Fa c_cc .
 This table lists the array index, the corresponding special character,
 and the system default value.
 For an accurate list of
 the system defaults, consult the header file
 .In sys/ttydefaults.h .
 .Pp
 .Bl -column "Index Name" "Special Character" -offset indent -compact
 .It Em "Index Name	Special Character	Default Value"
 .It Dv VEOF Ta EOF Ta \&^D
 .It Dv VEOL Ta EOL Ta _POSIX_VDISABLE
 .It Dv VEOL2 Ta EOL2 Ta _POSIX_VDISABLE
 .It Dv VERASE Ta ERASE Ta \&^? Ql \&\e177
 .It Dv VWERASE Ta WERASE Ta \&^W
 .It Dv VKILL Ta KILL Ta \&^U
 .It Dv VREPRINT Ta REPRINT Ta \&^R
 .It Dv VINTR Ta INTR Ta \&^C
 .It Dv VQUIT Ta QUIT Ta \&^\e\e Ql \&\e34
 .It Dv VSUSP Ta SUSP Ta \&^Z
 .It Dv VDSUSP Ta DSUSP Ta \&^Y
 .It Dv VSTART Ta START Ta \&^Q
 .It Dv VSTOP Ta STOP Ta \&^S
 .It Dv VLNEXT Ta LNEXT Ta \&^V
 .It Dv VDISCARD Ta DISCARD Ta \&^O
 .It Dv VMIN Ta --- Ta \&1
 .It Dv VTIME Ta --- Ta \&0
 .It Dv VSTATUS Ta STATUS Ta \&^T
 .El
 .Pp
 If the
 value of one of the changeable special control characters (see
 .Sx "Special Characters" )
 is
 .Dv {_POSIX_VDISABLE} ,
 that function is disabled; that is, no input
 data is recognized as the disabled special character.
 If
 .Dv ICANON
 is
 not set, the value of
 .Dv {_POSIX_VDISABLE}
 has no special meaning for the
 .Dv VMIN
 and
 .Dv VTIME
 entries of the
 .Fa c_cc
 array.
 .Pp
 The initial values of the flags and control characters
 after
 .Fn open
 is set according to
 the values in the header
 .In sys/ttydefaults.h .
 .Sh SEE ALSO
 .Xr stty 1 ,
 .Xr tcgetsid 3 ,
 .Xr tcgetwinsize 3 ,
 .Xr tcsendbreak 3 ,
 .Xr tcsetattr 3 ,
 .Xr tcsetsid 3 ,
 .Xr tty 4 ,
 .Xr stack 9
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index 134c1dfba98b..620233947410 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -1,2449 +1,2449 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
  *
  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
  * All rights reserved.
  *
  * Portions of this software were developed under sponsorship from Snow
  * B.V., the Netherlands.
  *
  * 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.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <sys/cdefs.h>
 #include "opt_capsicum.h"
 #include "opt_printf.h"
 
 #include <sys/param.h>
 #include <sys/capsicum.h>
 #include <sys/conf.h>
 #include <sys/cons.h>
 #include <sys/fcntl.h>
 #include <sys/file.h>
 #include <sys/filedesc.h>
 #include <sys/filio.h>
 #ifdef COMPAT_43TTY
 #include <sys/ioctl_compat.h>
 #endif /* COMPAT_43TTY */
 #include <sys/kernel.h>
 #include <sys/limits.h>
 #include <sys/malloc.h>
 #include <sys/mount.h>
 #include <sys/poll.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
 #include <sys/serial.h>
 #include <sys/signal.h>
 #include <sys/stat.h>
 #include <sys/sx.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 #include <sys/tty.h>
 #include <sys/ttycom.h>
 #define TTYDEFCHARS
 #include <sys/ttydefaults.h>
 #undef TTYDEFCHARS
 #include <sys/ucred.h>
 #include <sys/vnode.h>
 
 #include <fs/devfs/devfs.h>
 
 #include <machine/stdarg.h>
 
 static MALLOC_DEFINE(M_TTY, "tty", "tty device");
 
 static void tty_rel_free(struct tty *tp);
 
 static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list);
 static struct sx tty_list_sx;
 SX_SYSINIT(tty_list, &tty_list_sx, "tty list");
 static unsigned int tty_list_count = 0;
 
 /* Character device of /dev/console. */
 static struct cdev	*dev_console;
 static const char	*dev_console_filename;
 
 /*
  * Flags that are supported and stored by this implementation.
  */
-#define TTYSUP_IFLAG	(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|\
-			INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|IMAXBEL)
+#define TTYSUP_IFLAG	(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|\
+			IGNCR|ICRNL|IXON|IXOFF|IXANY|IMAXBEL|IUTF8)
 #define TTYSUP_OFLAG	(OPOST|ONLCR|TAB3|ONOEOT|OCRNL|ONOCR|ONLRET)
 #define TTYSUP_LFLAG	(ECHOKE|ECHOE|ECHOK|ECHO|ECHONL|ECHOPRT|\
 			ECHOCTL|ISIG|ICANON|ALTWERASE|IEXTEN|TOSTOP|\
 			FLUSHO|NOKERNINFO|NOFLSH)
 #define TTYSUP_CFLAG	(CIGNORE|CSIZE|CSTOPB|CREAD|PARENB|PARODD|\
 			HUPCL|CLOCAL|CCTS_OFLOW|CRTS_IFLOW|CDTR_IFLOW|\
 			CDSR_OFLOW|CCAR_OFLOW|CNO_RTSDTR)
 
 #define	TTY_CALLOUT(tp,d) (dev2unit(d) & TTYUNIT_CALLOUT)
 
 static int  tty_drainwait = 5 * 60;
 SYSCTL_INT(_kern, OID_AUTO, tty_drainwait, CTLFLAG_RWTUN,
     &tty_drainwait, 0, "Default output drain timeout in seconds");
 
 /*
  * Set TTY buffer sizes.
  */
 
 #define	TTYBUF_MAX	65536
 
 #ifdef PRINTF_BUFR_SIZE
 #define	TTY_PRBUF_SIZE	PRINTF_BUFR_SIZE
 #else
 #define	TTY_PRBUF_SIZE	256
 #endif
 
 /*
  * Allocate buffer space if necessary, and set low watermarks, based on speed.
  * Note that the ttyxxxq_setsize() functions may drop and then reacquire the tty
  * lock during memory allocation.  They will return ENXIO if the tty disappears
  * while unlocked.
  */
 static int
 tty_watermarks(struct tty *tp)
 {
 	size_t bs = 0;
 	int error;
 
 	/* Provide an input buffer for 2 seconds of data. */
 	if (tp->t_termios.c_cflag & CREAD)
 		bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX);
 	error = ttyinq_setsize(&tp->t_inq, tp, bs);
 	if (error != 0)
 		return (error);
 
 	/* Set low watermark at 10% (when 90% is available). */
 	tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10;
 
 	/* Provide an output buffer for 2 seconds of data. */
 	bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX);
 	error = ttyoutq_setsize(&tp->t_outq, tp, bs);
 	if (error != 0)
 		return (error);
 
 	/* Set low watermark at 10% (when 90% is available). */
 	tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10;
 
 	return (0);
 }
 
 static int
 tty_drain(struct tty *tp, int leaving)
 {
 	sbintime_t timeout_at;
 	size_t bytes;
 	int error;
 
 	if (ttyhook_hashook(tp, getc_inject))
 		/* buffer is inaccessible */
 		return (0);
 
 	/*
 	 * For close(), use the recent historic timeout of "1 second without
 	 * making progress".  For tcdrain(), use t_drainwait as the timeout,
 	 * with zero meaning "no timeout" which gives POSIX behavior.
 	 */
 	if (leaving)
 		timeout_at = getsbinuptime() + SBT_1S;
 	else if (tp->t_drainwait != 0)
 		timeout_at = getsbinuptime() + SBT_1S * tp->t_drainwait;
 	else
 		timeout_at = 0;
 
 	/*
 	 * Poll the output buffer and the hardware for completion, at 10 Hz.
 	 * Polling is required for devices which are not able to signal an
 	 * interrupt when the transmitter becomes idle (most USB serial devs).
 	 * The unusual structure of this loop ensures we check for busy one more
 	 * time after tty_timedwait() returns EWOULDBLOCK, so that success has
 	 * higher priority than timeout if the IO completed in the last 100mS.
 	 */
 	error = 0;
 	bytes = ttyoutq_bytesused(&tp->t_outq);
 	for (;;) {
 		if (ttyoutq_bytesused(&tp->t_outq) == 0 && !ttydevsw_busy(tp))
 			return (0);
 		if (error != 0)
 			return (error);
 		ttydevsw_outwakeup(tp);
 		error = tty_timedwait(tp, &tp->t_outwait, hz / 10);
 		if (error != 0 && error != EWOULDBLOCK)
 			return (error);
 		else if (timeout_at == 0 || getsbinuptime() < timeout_at)
 			error = 0;
 		else if (leaving && ttyoutq_bytesused(&tp->t_outq) < bytes) {
 			/* In close, making progress, grant an extra second. */
 			error = 0;
 			timeout_at += SBT_1S;
 			bytes = ttyoutq_bytesused(&tp->t_outq);
 		}
 	}
 }
 
 /*
  * Though ttydev_enter() and ttydev_leave() seem to be related, they
  * don't have to be used together. ttydev_enter() is used by the cdev
  * operations to prevent an actual operation from being processed when
  * the TTY has been abandoned. ttydev_leave() is used by ttydev_open()
  * and ttydev_close() to determine whether per-TTY data should be
  * deallocated.
  */
 
 static __inline int
 ttydev_enter(struct tty *tp)
 {
 
 	tty_lock(tp);
 
 	if (tty_gone(tp) || !tty_opened(tp)) {
 		/* Device is already gone. */
 		tty_unlock(tp);
 		return (ENXIO);
 	}
 
 	return (0);
 }
 
 static void
 ttydev_leave(struct tty *tp)
 {
 
 	tty_assert_locked(tp);
 
 	if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) {
 		/* Device is still opened somewhere. */
 		tty_unlock(tp);
 		return;
 	}
 
 	tp->t_flags |= TF_OPENCLOSE;
 
 	/* Remove console TTY. */
 	constty_clear(tp);
 
 	/* Drain any output. */
 	if (!tty_gone(tp))
 		tty_drain(tp, 1);
 
 	ttydisc_close(tp);
 
 	/* Free i/o queues now since they might be large. */
 	ttyinq_free(&tp->t_inq);
 	tp->t_inlow = 0;
 	ttyoutq_free(&tp->t_outq);
 	tp->t_outlow = 0;
 
 	knlist_clear(&tp->t_inpoll.si_note, 1);
 	knlist_clear(&tp->t_outpoll.si_note, 1);
 
 	if (!tty_gone(tp))
 		ttydevsw_close(tp);
 
 	tp->t_flags &= ~TF_OPENCLOSE;
 	cv_broadcast(&tp->t_dcdwait);
 	tty_rel_free(tp);
 }
 
 /*
  * Operations that are exposed through the character device in /dev.
  */
 static int
 ttydev_open(struct cdev *dev, int oflags, int devtype __unused,
     struct thread *td)
 {
 	struct tty *tp;
 	int error;
 
 	tp = dev->si_drv1;
 	error = 0;
 	tty_lock(tp);
 	if (tty_gone(tp)) {
 		/* Device is already gone. */
 		tty_unlock(tp);
 		return (ENXIO);
 	}
 
 	/*
 	 * Block when other processes are currently opening or closing
 	 * the TTY.
 	 */
 	while (tp->t_flags & TF_OPENCLOSE) {
 		error = tty_wait(tp, &tp->t_dcdwait);
 		if (error != 0) {
 			tty_unlock(tp);
 			return (error);
 		}
 	}
 	tp->t_flags |= TF_OPENCLOSE;
 
 	/*
 	 * Make sure the "tty" and "cua" device cannot be opened at the
 	 * same time.  The console is a "tty" device.
 	 */
 	if (TTY_CALLOUT(tp, dev)) {
 		if (tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) {
 			error = EBUSY;
 			goto done;
 		}
 	} else {
 		if (tp->t_flags & TF_OPENED_OUT) {
 			error = EBUSY;
 			goto done;
 		}
 	}
 
 	if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) {
 		error = EBUSY;
 		goto done;
 	}
 
 	if (!tty_opened(tp)) {
 		/* Set proper termios flags. */
 		if (TTY_CALLOUT(tp, dev))
 			tp->t_termios = tp->t_termios_init_out;
 		else
 			tp->t_termios = tp->t_termios_init_in;
 		ttydevsw_param(tp, &tp->t_termios);
 		/* Prevent modem control on callout devices and /dev/console. */
 		if (TTY_CALLOUT(tp, dev) || dev == dev_console)
 			tp->t_termios.c_cflag |= CLOCAL;
 
 		if ((tp->t_termios.c_cflag & CNO_RTSDTR) == 0)
 			ttydevsw_modem(tp, SER_DTR|SER_RTS, 0);
 
 		error = ttydevsw_open(tp);
 		if (error != 0)
 			goto done;
 
 		ttydisc_open(tp);
 		error = tty_watermarks(tp);
 		if (error != 0)
 			goto done;
 	}
 
 	/* Wait for Carrier Detect. */
 	if ((oflags & O_NONBLOCK) == 0 &&
 	    (tp->t_termios.c_cflag & CLOCAL) == 0) {
 		while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0) {
 			error = tty_wait(tp, &tp->t_dcdwait);
 			if (error != 0)
 				goto done;
 		}
 	}
 
 	if (dev == dev_console)
 		tp->t_flags |= TF_OPENED_CONS;
 	else if (TTY_CALLOUT(tp, dev))
 		tp->t_flags |= TF_OPENED_OUT;
 	else
 		tp->t_flags |= TF_OPENED_IN;
 	MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 ||
 	    (tp->t_flags & TF_OPENED_OUT) == 0);
 
 done:	tp->t_flags &= ~TF_OPENCLOSE;
 	cv_broadcast(&tp->t_dcdwait);
 	ttydev_leave(tp);
 
 	return (error);
 }
 
 static int
 ttydev_close(struct cdev *dev, int fflag, int devtype __unused,
     struct thread *td __unused)
 {
 	struct tty *tp = dev->si_drv1;
 
 	tty_lock(tp);
 
 	/*
 	 * Don't actually close the device if it is being used as the
 	 * console.
 	 */
 	MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 ||
 	    (tp->t_flags & TF_OPENED_OUT) == 0);
 	if (dev == dev_console)
 		tp->t_flags &= ~TF_OPENED_CONS;
 	else
 		tp->t_flags &= ~(TF_OPENED_IN|TF_OPENED_OUT);
 
 	if (tp->t_flags & TF_OPENED) {
 		tty_unlock(tp);
 		return (0);
 	}
 
 	/* If revoking, flush output now to avoid draining it later. */
 	if (fflag & FREVOKE)
 		tty_flush(tp, FWRITE);
 
 	tp->t_flags &= ~TF_EXCLUDE;
 
 	/* Properly wake up threads that are stuck - revoke(). */
 	tp->t_revokecnt++;
 	tty_wakeup(tp, FREAD|FWRITE);
 	cv_broadcast(&tp->t_bgwait);
 	cv_broadcast(&tp->t_dcdwait);
 
 	ttydev_leave(tp);
 
 	return (0);
 }
 
 static __inline int
 tty_is_ctty(struct tty *tp, struct proc *p)
 {
 
 	tty_assert_locked(tp);
 
 	return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT);
 }
 
 int
 tty_wait_background(struct tty *tp, struct thread *td, int sig)
 {
 	struct proc *p;
 	struct pgrp *pg;
 	ksiginfo_t ksi;
 	int error;
 
 	MPASS(sig == SIGTTIN || sig == SIGTTOU);
 	tty_assert_locked(tp);
 
 	p = td->td_proc;
 	for (;;) {
 		pg = p->p_pgrp;
 		PGRP_LOCK(pg);
 		PROC_LOCK(p);
 
 		/*
 		 * pg may no longer be our process group.
 		 * Re-check after locking.
 		 */
 		if (p->p_pgrp != pg) {
 			PROC_UNLOCK(p);
 			PGRP_UNLOCK(pg);
 			continue;
 		}
 
 		/*
 		 * The process should only sleep, when:
 		 * - This terminal is the controlling terminal
 		 * - Its process group is not the foreground process
 		 *   group
 		 * - The parent process isn't waiting for the child to
 		 *   exit
 		 * - the signal to send to the process isn't masked
 		 */
 		if (!tty_is_ctty(tp, p) || p->p_pgrp == tp->t_pgrp) {
 			/* Allow the action to happen. */
 			PROC_UNLOCK(p);
 			PGRP_UNLOCK(pg);
 			return (0);
 		}
 
 		if (SIGISMEMBER(p->p_sigacts->ps_sigignore, sig) ||
 		    SIGISMEMBER(td->td_sigmask, sig)) {
 			/* Only allow them in write()/ioctl(). */
 			PROC_UNLOCK(p);
 			PGRP_UNLOCK(pg);
 			return (sig == SIGTTOU ? 0 : EIO);
 		}
 
 		if ((p->p_flag & P_PPWAIT) != 0 ||
 		    (pg->pg_flags & PGRP_ORPHANED) != 0) {
 			/* Don't allow the action to happen. */
 			PROC_UNLOCK(p);
 			PGRP_UNLOCK(pg);
 			return (EIO);
 		}
 		PROC_UNLOCK(p);
 
 		/*
 		 * Send the signal and sleep until we're the new
 		 * foreground process group.
 		 */
 		if (sig != 0) {
 			ksiginfo_init(&ksi);
 			ksi.ksi_code = SI_KERNEL;
 			ksi.ksi_signo = sig;
 			sig = 0;
 		}
 
 		pgsignal(pg, ksi.ksi_signo, 1, &ksi);
 		PGRP_UNLOCK(pg);
 
 		error = tty_wait(tp, &tp->t_bgwait);
 		if (error)
 			return (error);
 	}
 }
 
 static int
 ttydev_read(struct cdev *dev, struct uio *uio, int ioflag)
 {
 	struct tty *tp = dev->si_drv1;
 	int error;
 
 	error = ttydev_enter(tp);
 	if (error)
 		goto done;
 	error = ttydisc_read(tp, uio, ioflag);
 	tty_unlock(tp);
 
 	/*
 	 * The read() call should not throw an error when the device is
 	 * being destroyed. Silently convert it to an EOF.
 	 */
 done:	if (error == ENXIO)
 		error = 0;
 	return (error);
 }
 
 static int
 ttydev_write(struct cdev *dev, struct uio *uio, int ioflag)
 {
 	struct tty *tp = dev->si_drv1;
 	int defer, error;
 
 	error = ttydev_enter(tp);
 	if (error)
 		return (error);
 
 	if (tp->t_termios.c_lflag & TOSTOP) {
 		error = tty_wait_background(tp, curthread, SIGTTOU);
 		if (error)
 			goto done;
 	}
 
 	if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT) {
 		/* Allow non-blocking writes to bypass serialization. */
 		error = ttydisc_write(tp, uio, ioflag);
 	} else {
 		/* Serialize write() calls. */
 		while (tp->t_flags & TF_BUSY_OUT) {
 			error = tty_wait(tp, &tp->t_outserwait);
 			if (error)
 				goto done;
 		}
 
 		tp->t_flags |= TF_BUSY_OUT;
 		defer = sigdeferstop(SIGDEFERSTOP_ERESTART);
 		error = ttydisc_write(tp, uio, ioflag);
 		sigallowstop(defer);
 		tp->t_flags &= ~TF_BUSY_OUT;
 		cv_signal(&tp->t_outserwait);
 	}
 
 done:	tty_unlock(tp);
 	return (error);
 }
 
 static int
 ttydev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
     struct thread *td)
 {
 	struct tty *tp = dev->si_drv1;
 	int error;
 
 	error = ttydev_enter(tp);
 	if (error)
 		return (error);
 
 	switch (cmd) {
 	case TIOCCBRK:
 	case TIOCCONS:
 	case TIOCDRAIN:
 	case TIOCEXCL:
 	case TIOCFLUSH:
 	case TIOCNXCL:
 	case TIOCSBRK:
 	case TIOCSCTTY:
 	case TIOCSETA:
 	case TIOCSETAF:
 	case TIOCSETAW:
 	case TIOCSPGRP:
 	case TIOCSTART:
 	case TIOCSTAT:
 	case TIOCSTI:
 	case TIOCSTOP:
 	case TIOCSWINSZ:
 #if 0
 	case TIOCSDRAINWAIT:
 	case TIOCSETD:
 #endif
 #ifdef COMPAT_43TTY
 	case  TIOCLBIC:
 	case  TIOCLBIS:
 	case  TIOCLSET:
 	case  TIOCSETC:
 	case OTIOCSETD:
 	case  TIOCSETN:
 	case  TIOCSETP:
 	case  TIOCSLTC:
 #endif /* COMPAT_43TTY */
 		/*
 		 * If the ioctl() causes the TTY to be modified, let it
 		 * wait in the background.
 		 */
 		error = tty_wait_background(tp, curthread, SIGTTOU);
 		if (error)
 			goto done;
 	}
 
 	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
 		struct termios *old = &tp->t_termios;
 		struct termios *new = (struct termios *)data;
 		struct termios *lock = TTY_CALLOUT(tp, dev) ?
 		    &tp->t_termios_lock_out : &tp->t_termios_lock_in;
 		int cc;
 
 		/*
 		 * Lock state devices.  Just overwrite the values of the
 		 * commands that are currently in use.
 		 */
 		new->c_iflag = (old->c_iflag & lock->c_iflag) |
 		    (new->c_iflag & ~lock->c_iflag);
 		new->c_oflag = (old->c_oflag & lock->c_oflag) |
 		    (new->c_oflag & ~lock->c_oflag);
 		new->c_cflag = (old->c_cflag & lock->c_cflag) |
 		    (new->c_cflag & ~lock->c_cflag);
 		new->c_lflag = (old->c_lflag & lock->c_lflag) |
 		    (new->c_lflag & ~lock->c_lflag);
 		for (cc = 0; cc < NCCS; ++cc)
 			if (lock->c_cc[cc])
 				new->c_cc[cc] = old->c_cc[cc];
 		if (lock->c_ispeed)
 			new->c_ispeed = old->c_ispeed;
 		if (lock->c_ospeed)
 			new->c_ospeed = old->c_ospeed;
 	}
 
 	error = tty_ioctl(tp, cmd, data, fflag, td);
 done:	tty_unlock(tp);
 
 	return (error);
 }
 
 static int
 ttydev_poll(struct cdev *dev, int events, struct thread *td)
 {
 	struct tty *tp = dev->si_drv1;
 	int error, revents = 0;
 
 	error = ttydev_enter(tp);
 	if (error)
 		return ((events & (POLLIN|POLLRDNORM)) | POLLHUP);
 
 	if (events & (POLLIN|POLLRDNORM)) {
 		/* See if we can read something. */
 		if (ttydisc_read_poll(tp) > 0)
 			revents |= events & (POLLIN|POLLRDNORM);
 	}
 
 	if (tp->t_flags & TF_ZOMBIE) {
 		/* Hangup flag on zombie state. */
 		revents |= POLLHUP;
 	} else if (events & (POLLOUT|POLLWRNORM)) {
 		/* See if we can write something. */
 		if (ttydisc_write_poll(tp) > 0)
 			revents |= events & (POLLOUT|POLLWRNORM);
 	}
 
 	if (revents == 0) {
 		if (events & (POLLIN|POLLRDNORM))
 			selrecord(td, &tp->t_inpoll);
 		if (events & (POLLOUT|POLLWRNORM))
 			selrecord(td, &tp->t_outpoll);
 	}
 
 	tty_unlock(tp);
 
 	return (revents);
 }
 
 static int
 ttydev_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
     int nprot, vm_memattr_t *memattr)
 {
 	struct tty *tp = dev->si_drv1;
 	int error;
 
 	/* Handle mmap() through the driver. */
 
 	error = ttydev_enter(tp);
 	if (error)
 		return (-1);
 	error = ttydevsw_mmap(tp, offset, paddr, nprot, memattr);
 	tty_unlock(tp);
 
 	return (error);
 }
 
 /*
  * kqueue support.
  */
 
 static void
 tty_kqops_read_detach(struct knote *kn)
 {
 	struct tty *tp = kn->kn_hook;
 
 	knlist_remove(&tp->t_inpoll.si_note, kn, 0);
 }
 
 static int
 tty_kqops_read_event(struct knote *kn, long hint __unused)
 {
 	struct tty *tp = kn->kn_hook;
 
 	tty_assert_locked(tp);
 
 	if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) {
 		kn->kn_flags |= EV_EOF;
 		return (1);
 	} else {
 		kn->kn_data = ttydisc_read_poll(tp);
 		return (kn->kn_data > 0);
 	}
 }
 
 static void
 tty_kqops_write_detach(struct knote *kn)
 {
 	struct tty *tp = kn->kn_hook;
 
 	knlist_remove(&tp->t_outpoll.si_note, kn, 0);
 }
 
 static int
 tty_kqops_write_event(struct knote *kn, long hint __unused)
 {
 	struct tty *tp = kn->kn_hook;
 
 	tty_assert_locked(tp);
 
 	if (tty_gone(tp)) {
 		kn->kn_flags |= EV_EOF;
 		return (1);
 	} else {
 		kn->kn_data = ttydisc_write_poll(tp);
 		return (kn->kn_data > 0);
 	}
 }
 
 static struct filterops tty_kqops_read = {
 	.f_isfd = 1,
 	.f_detach = tty_kqops_read_detach,
 	.f_event = tty_kqops_read_event,
 };
 
 static struct filterops tty_kqops_write = {
 	.f_isfd = 1,
 	.f_detach = tty_kqops_write_detach,
 	.f_event = tty_kqops_write_event,
 };
 
 static int
 ttydev_kqfilter(struct cdev *dev, struct knote *kn)
 {
 	struct tty *tp = dev->si_drv1;
 	int error;
 
 	error = ttydev_enter(tp);
 	if (error)
 		return (error);
 
 	switch (kn->kn_filter) {
 	case EVFILT_READ:
 		kn->kn_hook = tp;
 		kn->kn_fop = &tty_kqops_read;
 		knlist_add(&tp->t_inpoll.si_note, kn, 1);
 		break;
 	case EVFILT_WRITE:
 		kn->kn_hook = tp;
 		kn->kn_fop = &tty_kqops_write;
 		knlist_add(&tp->t_outpoll.si_note, kn, 1);
 		break;
 	default:
 		error = EINVAL;
 		break;
 	}
 
 	tty_unlock(tp);
 	return (error);
 }
 
 static struct cdevsw ttydev_cdevsw = {
 	.d_version	= D_VERSION,
 	.d_open		= ttydev_open,
 	.d_close	= ttydev_close,
 	.d_read		= ttydev_read,
 	.d_write	= ttydev_write,
 	.d_ioctl	= ttydev_ioctl,
 	.d_kqfilter	= ttydev_kqfilter,
 	.d_poll		= ttydev_poll,
 	.d_mmap		= ttydev_mmap,
 	.d_name		= "ttydev",
 	.d_flags	= D_TTY,
 };
 
 /*
  * Init/lock-state devices
  */
 
 static int
 ttyil_open(struct cdev *dev, int oflags __unused, int devtype __unused,
     struct thread *td)
 {
 	struct tty *tp;
 	int error;
 
 	tp = dev->si_drv1;
 	error = 0;
 	tty_lock(tp);
 	if (tty_gone(tp))
 		error = ENODEV;
 	tty_unlock(tp);
 
 	return (error);
 }
 
 static int
 ttyil_close(struct cdev *dev __unused, int flag __unused, int mode __unused,
     struct thread *td __unused)
 {
 
 	return (0);
 }
 
 static int
 ttyil_rdwr(struct cdev *dev __unused, struct uio *uio __unused,
     int ioflag __unused)
 {
 
 	return (ENODEV);
 }
 
 static int
 ttyil_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
     struct thread *td)
 {
 	struct tty *tp = dev->si_drv1;
 	int error;
 
 	tty_lock(tp);
 	if (tty_gone(tp)) {
 		error = ENODEV;
 		goto done;
 	}
 
 	error = ttydevsw_cioctl(tp, dev2unit(dev), cmd, data, td);
 	if (error != ENOIOCTL)
 		goto done;
 	error = 0;
 
 	switch (cmd) {
 	case TIOCGETA:
 		/* Obtain terminal flags through tcgetattr(). */
 		*(struct termios*)data = *(struct termios*)dev->si_drv2;
 		break;
 	case TIOCSETA:
 		/* Set terminal flags through tcsetattr(). */
 		error = priv_check(td, PRIV_TTY_SETA);
 		if (error)
 			break;
 		*(struct termios*)dev->si_drv2 = *(struct termios*)data;
 		break;
 	case TIOCGETD:
 		*(int *)data = TTYDISC;
 		break;
 	case TIOCGWINSZ:
 		bzero(data, sizeof(struct winsize));
 		break;
 	default:
 		error = ENOTTY;
 	}
 
 done:	tty_unlock(tp);
 	return (error);
 }
 
 static struct cdevsw ttyil_cdevsw = {
 	.d_version	= D_VERSION,
 	.d_open		= ttyil_open,
 	.d_close	= ttyil_close,
 	.d_read		= ttyil_rdwr,
 	.d_write	= ttyil_rdwr,
 	.d_ioctl	= ttyil_ioctl,
 	.d_name		= "ttyil",
 	.d_flags	= D_TTY,
 };
 
 static void
 tty_init_termios(struct tty *tp)
 {
 	struct termios *t = &tp->t_termios_init_in;
 
 	t->c_cflag = TTYDEF_CFLAG;
 	t->c_iflag = TTYDEF_IFLAG;
 	t->c_lflag = TTYDEF_LFLAG;
 	t->c_oflag = TTYDEF_OFLAG;
 	t->c_ispeed = TTYDEF_SPEED;
 	t->c_ospeed = TTYDEF_SPEED;
 	memcpy(&t->c_cc, ttydefchars, sizeof ttydefchars);
 
 	tp->t_termios_init_out = *t;
 }
 
 void
 tty_init_console(struct tty *tp, speed_t s)
 {
 	struct termios *ti = &tp->t_termios_init_in;
 	struct termios *to = &tp->t_termios_init_out;
 
 	if (s != 0) {
 		ti->c_ispeed = ti->c_ospeed = s;
 		to->c_ispeed = to->c_ospeed = s;
 	}
 
 	ti->c_cflag |= CLOCAL;
 	to->c_cflag |= CLOCAL;
 }
 
 /*
  * Standard device routine implementations, mostly meant for
  * pseudo-terminal device drivers. When a driver creates a new terminal
  * device class, missing routines are patched.
  */
 
 static int
 ttydevsw_defopen(struct tty *tp __unused)
 {
 
 	return (0);
 }
 
 static void
 ttydevsw_defclose(struct tty *tp __unused)
 {
 
 }
 
 static void
 ttydevsw_defoutwakeup(struct tty *tp __unused)
 {
 
 	panic("Terminal device has output, while not implemented");
 }
 
 static void
 ttydevsw_definwakeup(struct tty *tp __unused)
 {
 
 }
 
 static int
 ttydevsw_defioctl(struct tty *tp __unused, u_long cmd __unused,
     caddr_t data __unused, struct thread *td __unused)
 {
 
 	return (ENOIOCTL);
 }
 
 static int
 ttydevsw_defcioctl(struct tty *tp __unused, int unit __unused,
     u_long cmd __unused, caddr_t data __unused, struct thread *td __unused)
 {
 
 	return (ENOIOCTL);
 }
 
 static int
 ttydevsw_defparam(struct tty *tp __unused, struct termios *t)
 {
 
 	/*
 	 * Allow the baud rate to be adjusted for pseudo-devices, but at
 	 * least restrict it to 115200 to prevent excessive buffer
 	 * usage.  Also disallow 0, to prevent foot shooting.
 	 */
 	if (t->c_ispeed < B50)
 		t->c_ispeed = B50;
 	else if (t->c_ispeed > B115200)
 		t->c_ispeed = B115200;
 	if (t->c_ospeed < B50)
 		t->c_ospeed = B50;
 	else if (t->c_ospeed > B115200)
 		t->c_ospeed = B115200;
 	t->c_cflag |= CREAD;
 
 	return (0);
 }
 
 static int
 ttydevsw_defmodem(struct tty *tp __unused, int sigon __unused,
     int sigoff __unused)
 {
 
 	/* Simulate a carrier to make the TTY layer happy. */
 	return (SER_DCD);
 }
 
 static int
 ttydevsw_defmmap(struct tty *tp __unused, vm_ooffset_t offset __unused,
     vm_paddr_t *paddr __unused, int nprot __unused,
     vm_memattr_t *memattr __unused)
 {
 
 	return (-1);
 }
 
 static void
 ttydevsw_defpktnotify(struct tty *tp __unused, char event __unused)
 {
 
 }
 
 static void
 ttydevsw_deffree(void *softc __unused)
 {
 
 	panic("Terminal device freed without a free-handler");
 }
 
 static bool
 ttydevsw_defbusy(struct tty *tp __unused)
 {
 
 	return (FALSE);
 }
 
 /*
  * TTY allocation and deallocation. TTY devices can be deallocated when
  * the driver doesn't use it anymore, when the TTY isn't a session's
  * controlling TTY and when the device node isn't opened through devfs.
  */
 
 struct tty *
 tty_alloc(struct ttydevsw *tsw, void *sc)
 {
 
 	return (tty_alloc_mutex(tsw, sc, NULL));
 }
 
 struct tty *
 tty_alloc_mutex(struct ttydevsw *tsw, void *sc, struct mtx *mutex)
 {
 	struct tty *tp;
 
 	/* Make sure the driver defines all routines. */
 #define PATCH_FUNC(x) do {				\
 	if (tsw->tsw_ ## x == NULL)			\
 		tsw->tsw_ ## x = ttydevsw_def ## x;	\
 } while (0)
 	PATCH_FUNC(open);
 	PATCH_FUNC(close);
 	PATCH_FUNC(outwakeup);
 	PATCH_FUNC(inwakeup);
 	PATCH_FUNC(ioctl);
 	PATCH_FUNC(cioctl);
 	PATCH_FUNC(param);
 	PATCH_FUNC(modem);
 	PATCH_FUNC(mmap);
 	PATCH_FUNC(pktnotify);
 	PATCH_FUNC(free);
 	PATCH_FUNC(busy);
 #undef PATCH_FUNC
 
 	tp = malloc(sizeof(struct tty) + TTY_PRBUF_SIZE, M_TTY,
 	    M_WAITOK | M_ZERO);
 	tp->t_prbufsz = TTY_PRBUF_SIZE;
 	tp->t_devsw = tsw;
 	tp->t_devswsoftc = sc;
 	tp->t_flags = tsw->tsw_flags;
 	tp->t_drainwait = tty_drainwait;
 
 	tty_init_termios(tp);
 
 	cv_init(&tp->t_inwait, "ttyin");
 	cv_init(&tp->t_outwait, "ttyout");
 	cv_init(&tp->t_outserwait, "ttyosr");
 	cv_init(&tp->t_bgwait, "ttybg");
 	cv_init(&tp->t_dcdwait, "ttydcd");
 
 	/* Allow drivers to use a custom mutex to lock the TTY. */
 	if (mutex != NULL) {
 		tp->t_mtx = mutex;
 	} else {
 		tp->t_mtx = &tp->t_mtxobj;
 		mtx_init(&tp->t_mtxobj, "ttymtx", NULL, MTX_DEF);
 	}
 
 	knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_mtx);
 	knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx);
 
 	return (tp);
 }
 
 static void
 tty_dealloc(void *arg)
 {
 	struct tty *tp = arg;
 
 	/*
 	 * ttyydev_leave() usually frees the i/o queues earlier, but it is
 	 * not always called between queue allocation and here.  The queues
 	 * may be allocated by ioctls on a pty control device without the
 	 * corresponding pty slave device ever being open, or after it is
 	 * closed.
 	 */
 	ttyinq_free(&tp->t_inq);
 	ttyoutq_free(&tp->t_outq);
 	seldrain(&tp->t_inpoll);
 	seldrain(&tp->t_outpoll);
 	knlist_destroy(&tp->t_inpoll.si_note);
 	knlist_destroy(&tp->t_outpoll.si_note);
 
 	cv_destroy(&tp->t_inwait);
 	cv_destroy(&tp->t_outwait);
 	cv_destroy(&tp->t_bgwait);
 	cv_destroy(&tp->t_dcdwait);
 	cv_destroy(&tp->t_outserwait);
 
 	if (tp->t_mtx == &tp->t_mtxobj)
 		mtx_destroy(&tp->t_mtxobj);
 	ttydevsw_free(tp);
 	free(tp, M_TTY);
 }
 
 static void
 tty_rel_free(struct tty *tp)
 {
 	struct cdev *dev;
 
 	tty_assert_locked(tp);
 
 #define	TF_ACTIVITY	(TF_GONE|TF_OPENED|TF_HOOK|TF_OPENCLOSE)
 	if (tp->t_sessioncnt != 0 || (tp->t_flags & TF_ACTIVITY) != TF_GONE) {
 		/* TTY is still in use. */
 		tty_unlock(tp);
 		return;
 	}
 
 	/* Stop asynchronous I/O. */
 	funsetown(&tp->t_sigio);
 
 	/* TTY can be deallocated. */
 	dev = tp->t_dev;
 	tp->t_dev = NULL;
 	tty_unlock(tp);
 
 	if (dev != NULL) {
 		sx_xlock(&tty_list_sx);
 		TAILQ_REMOVE(&tty_list, tp, t_list);
 		tty_list_count--;
 		sx_xunlock(&tty_list_sx);
 		destroy_dev_sched_cb(dev, tty_dealloc, tp);
 	}
 }
 
 void
 tty_rel_pgrp(struct tty *tp, struct pgrp *pg)
 {
 
 	MPASS(tp->t_sessioncnt > 0);
 	tty_assert_locked(tp);
 
 	if (tp->t_pgrp == pg)
 		tp->t_pgrp = NULL;
 
 	tty_unlock(tp);
 }
 
 void
 tty_rel_sess(struct tty *tp, struct session *sess)
 {
 
 	MPASS(tp->t_sessioncnt > 0);
 
 	/* Current session has left. */
 	if (tp->t_session == sess) {
 		tp->t_session = NULL;
 		MPASS(tp->t_pgrp == NULL);
 	}
 	tp->t_sessioncnt--;
 	tty_rel_free(tp);
 }
 
 void
 tty_rel_gone(struct tty *tp)
 {
 
 	tty_assert_locked(tp);
 	MPASS(!tty_gone(tp));
 
 	/* Simulate carrier removal. */
 	ttydisc_modem(tp, 0);
 
 	/* Wake up all blocked threads. */
 	tty_wakeup(tp, FREAD|FWRITE);
 	cv_broadcast(&tp->t_bgwait);
 	cv_broadcast(&tp->t_dcdwait);
 
 	tp->t_flags |= TF_GONE;
 	tty_rel_free(tp);
 }
 
 static int
 tty_drop_ctty(struct tty *tp, struct proc *p)
 {
 	struct session *session;
 	struct vnode *vp;
 
 	/*
 	 * This looks terrible, but it's generally safe as long as the tty
 	 * hasn't gone away while we had the lock dropped.  All of our sanity
 	 * checking that this operation is OK happens after we've picked it back
 	 * up, so other state changes are generally not fatal and the potential
 	 * for this particular operation to happen out-of-order in a
 	 * multithreaded scenario is likely a non-issue.
 	 */
 	tty_unlock(tp);
 	sx_xlock(&proctree_lock);
 	tty_lock(tp);
 	if (tty_gone(tp)) {
 		sx_xunlock(&proctree_lock);
 		return (ENODEV);
 	}
 
 	/*
 	 * If the session doesn't have a controlling TTY, or if we weren't
 	 * invoked on the controlling TTY, we'll return ENOIOCTL as we've
 	 * historically done.
 	 */
 	session = p->p_session;
 	if (session->s_ttyp == NULL || session->s_ttyp != tp) {
 		sx_xunlock(&proctree_lock);
 		return (ENOTTY);
 	}
 
 	if (!SESS_LEADER(p)) {
 		sx_xunlock(&proctree_lock);
 		return (EPERM);
 	}
 
 	PROC_LOCK(p);
 	SESS_LOCK(session);
 	vp = session->s_ttyvp;
 	session->s_ttyp = NULL;
 	session->s_ttyvp = NULL;
 	session->s_ttydp = NULL;
 	SESS_UNLOCK(session);
 
 	tp->t_sessioncnt--;
 	p->p_flag &= ~P_CONTROLT;
 	PROC_UNLOCK(p);
 	sx_xunlock(&proctree_lock);
 
 	/*
 	 * If we did have a vnode, release our reference.  Ordinarily we manage
 	 * these at the devfs layer, but we can't necessarily know that we were
 	 * invoked on the vnode referenced in the session (i.e. the vnode we
 	 * hold a reference to).  We explicitly don't check VBAD/VIRF_DOOMED here
 	 * to avoid a vnode leak -- in circumstances elsewhere where we'd hit a
 	 * VIRF_DOOMED vnode, release has been deferred until the controlling TTY
 	 * is either changed or released.
 	 */
 	if (vp != NULL)
 		devfs_ctty_unref(vp);
 	return (0);
 }
 
 /*
  * Exposing information about current TTY's through sysctl
  */
 
 static void
 tty_to_xtty(struct tty *tp, struct xtty *xt)
 {
 
 	tty_assert_locked(tp);
 
 	xt->xt_size = sizeof(struct xtty);
 	xt->xt_insize = ttyinq_getsize(&tp->t_inq);
 	xt->xt_incc = ttyinq_bytescanonicalized(&tp->t_inq);
 	xt->xt_inlc = ttyinq_bytesline(&tp->t_inq);
 	xt->xt_inlow = tp->t_inlow;
 	xt->xt_outsize = ttyoutq_getsize(&tp->t_outq);
 	xt->xt_outcc = ttyoutq_bytesused(&tp->t_outq);
 	xt->xt_outlow = tp->t_outlow;
 	xt->xt_column = tp->t_column;
 	xt->xt_pgid = tp->t_pgrp ? tp->t_pgrp->pg_id : 0;
 	xt->xt_sid = tp->t_session ? tp->t_session->s_sid : 0;
 	xt->xt_flags = tp->t_flags;
 	xt->xt_dev = tp->t_dev ? dev2udev(tp->t_dev) : (uint32_t)NODEV;
 }
 
 static int
 sysctl_kern_ttys(SYSCTL_HANDLER_ARGS)
 {
 	unsigned long lsize;
 	struct xtty *xtlist, *xt;
 	struct tty *tp;
 	int error;
 
 	sx_slock(&tty_list_sx);
 	lsize = tty_list_count * sizeof(struct xtty);
 	if (lsize == 0) {
 		sx_sunlock(&tty_list_sx);
 		return (0);
 	}
 
 	xtlist = xt = malloc(lsize, M_TTY, M_WAITOK);
 
 	TAILQ_FOREACH(tp, &tty_list, t_list) {
 		tty_lock(tp);
 		tty_to_xtty(tp, xt);
 		tty_unlock(tp);
 		xt++;
 	}
 	sx_sunlock(&tty_list_sx);
 
 	error = SYSCTL_OUT(req, xtlist, lsize);
 	free(xtlist, M_TTY);
 	return (error);
 }
 
 SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD|CTLFLAG_MPSAFE,
 	0, 0, sysctl_kern_ttys, "S,xtty", "List of TTYs");
 
 /*
  * Device node creation. Device has been set up, now we can expose it to
  * the user.
  */
 
 int
 tty_makedevf(struct tty *tp, struct ucred *cred, int flags,
     const char *fmt, ...)
 {
 	va_list ap;
 	struct make_dev_args args;
 	struct cdev *dev, *init, *lock, *cua, *cinit, *clock;
 	const char *prefix = "tty";
 	char name[SPECNAMELEN - 3]; /* for "tty" and "cua". */
 	uid_t uid;
 	gid_t gid;
 	mode_t mode;
 	int error;
 
 	/* Remove "tty" prefix from devices like PTY's. */
 	if (tp->t_flags & TF_NOPREFIX)
 		prefix = "";
 
 	va_start(ap, fmt);
 	vsnrprintf(name, sizeof name, 32, fmt, ap);
 	va_end(ap);
 
 	if (cred == NULL) {
 		/* System device. */
 		uid = UID_ROOT;
 		gid = GID_WHEEL;
 		mode = S_IRUSR|S_IWUSR;
 	} else {
 		/* User device. */
 		uid = cred->cr_ruid;
 		gid = GID_TTY;
 		mode = S_IRUSR|S_IWUSR|S_IWGRP;
 	}
 
 	flags = flags & TTYMK_CLONING ? MAKEDEV_REF : 0;
 	flags |= MAKEDEV_CHECKNAME;
 
 	/* Master call-in device. */
 	make_dev_args_init(&args);
 	args.mda_flags = flags;
 	args.mda_devsw = &ttydev_cdevsw;
 	args.mda_cr = cred;
 	args.mda_uid = uid;
 	args.mda_gid = gid;
 	args.mda_mode = mode;
 	args.mda_si_drv1 = tp;
 	error = make_dev_s(&args, &dev, "%s%s", prefix, name);
 	if (error != 0)
 		return (error);
 	tp->t_dev = dev;
 
 	init = lock = cua = cinit = clock = NULL;
 
 	/* Slave call-in devices. */
 	if (tp->t_flags & TF_INITLOCK) {
 		args.mda_devsw = &ttyil_cdevsw;
 		args.mda_unit = TTYUNIT_INIT;
 		args.mda_si_drv1 = tp;
 		args.mda_si_drv2 = &tp->t_termios_init_in;
 		error = make_dev_s(&args, &init, "%s%s.init", prefix, name);
 		if (error != 0)
 			goto fail;
 		dev_depends(dev, init);
 
 		args.mda_unit = TTYUNIT_LOCK;
 		args.mda_si_drv2 = &tp->t_termios_lock_in;
 		error = make_dev_s(&args, &lock, "%s%s.lock", prefix, name);
 		if (error != 0)
 			goto fail;
 		dev_depends(dev, lock);
 	}
 
 	/* Call-out devices. */
 	if (tp->t_flags & TF_CALLOUT) {
 		make_dev_args_init(&args);
 		args.mda_flags = flags;
 		args.mda_devsw = &ttydev_cdevsw;
 		args.mda_cr = cred;
 		args.mda_uid = UID_UUCP;
 		args.mda_gid = GID_DIALER;
 		args.mda_mode = 0660;
 		args.mda_unit = TTYUNIT_CALLOUT;
 		args.mda_si_drv1 = tp;
 		error = make_dev_s(&args, &cua, "cua%s", name);
 		if (error != 0)
 			goto fail;
 		dev_depends(dev, cua);
 
 		/* Slave call-out devices. */
 		if (tp->t_flags & TF_INITLOCK) {
 			args.mda_devsw = &ttyil_cdevsw;
 			args.mda_unit = TTYUNIT_CALLOUT | TTYUNIT_INIT;
 			args.mda_si_drv2 = &tp->t_termios_init_out;
 			error = make_dev_s(&args, &cinit, "cua%s.init", name);
 			if (error != 0)
 				goto fail;
 			dev_depends(dev, cinit);
 
 			args.mda_unit = TTYUNIT_CALLOUT | TTYUNIT_LOCK;
 			args.mda_si_drv2 = &tp->t_termios_lock_out;
 			error = make_dev_s(&args, &clock, "cua%s.lock", name);
 			if (error != 0)
 				goto fail;
 			dev_depends(dev, clock);
 		}
 	}
 
 	sx_xlock(&tty_list_sx);
 	TAILQ_INSERT_TAIL(&tty_list, tp, t_list);
 	tty_list_count++;
 	sx_xunlock(&tty_list_sx);
 
 	return (0);
 
 fail:
 	destroy_dev(dev);
 	if (init)
 		destroy_dev(init);
 	if (lock)
 		destroy_dev(lock);
 	if (cinit)
 		destroy_dev(cinit);
 	if (clock)
 		destroy_dev(clock);
 
 	return (error);
 }
 
 /*
  * Signalling processes.
  */
 
 void
 tty_signal_sessleader(struct tty *tp, int sig)
 {
 	struct proc *p;
 	struct session *s;
 
 	tty_assert_locked(tp);
 	MPASS(sig >= 1 && sig < NSIG);
 
 	/* Make signals start output again. */
 	tp->t_flags &= ~TF_STOPPED;
 	tp->t_termios.c_lflag &= ~FLUSHO;
 
 	/*
 	 * Load s_leader exactly once to avoid race where s_leader is
 	 * set to NULL by a concurrent invocation of killjobc() by the
 	 * session leader.  Note that we are not holding t_session's
 	 * lock for the read.
 	 */
 	if ((s = tp->t_session) != NULL &&
 	    (p = atomic_load_ptr(&s->s_leader)) != NULL) {
 		PROC_LOCK(p);
 		kern_psignal(p, sig);
 		PROC_UNLOCK(p);
 	}
 }
 
 void
 tty_signal_pgrp(struct tty *tp, int sig)
 {
 	ksiginfo_t ksi;
 
 	tty_assert_locked(tp);
 	MPASS(sig >= 1 && sig < NSIG);
 
 	/* Make signals start output again. */
 	tp->t_flags &= ~TF_STOPPED;
 	tp->t_termios.c_lflag &= ~FLUSHO;
 
 	if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO))
 		tty_info(tp);
 	if (tp->t_pgrp != NULL) {
 		ksiginfo_init(&ksi);
 		ksi.ksi_signo = sig;
 		ksi.ksi_code = SI_KERNEL;
 		PGRP_LOCK(tp->t_pgrp);
 		pgsignal(tp->t_pgrp, sig, 1, &ksi);
 		PGRP_UNLOCK(tp->t_pgrp);
 	}
 }
 
 void
 tty_wakeup(struct tty *tp, int flags)
 {
 
 	if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL)
 		pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL));
 
 	if (flags & FWRITE) {
 		cv_broadcast(&tp->t_outwait);
 		selwakeup(&tp->t_outpoll);
 		KNOTE_LOCKED(&tp->t_outpoll.si_note, 0);
 	}
 	if (flags & FREAD) {
 		cv_broadcast(&tp->t_inwait);
 		selwakeup(&tp->t_inpoll);
 		KNOTE_LOCKED(&tp->t_inpoll.si_note, 0);
 	}
 }
 
 int
 tty_wait(struct tty *tp, struct cv *cv)
 {
 	int error;
 	int revokecnt = tp->t_revokecnt;
 
 	tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED);
 	MPASS(!tty_gone(tp));
 
 	error = cv_wait_sig(cv, tp->t_mtx);
 
 	/* Bail out when the device slipped away. */
 	if (tty_gone(tp))
 		return (ENXIO);
 
 	/* Restart the system call when we may have been revoked. */
 	if (tp->t_revokecnt != revokecnt)
 		return (ERESTART);
 
 	return (error);
 }
 
 int
 tty_timedwait(struct tty *tp, struct cv *cv, int hz)
 {
 	int error;
 	int revokecnt = tp->t_revokecnt;
 
 	tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED);
 	MPASS(!tty_gone(tp));
 
 	error = cv_timedwait_sig(cv, tp->t_mtx, hz);
 
 	/* Bail out when the device slipped away. */
 	if (tty_gone(tp))
 		return (ENXIO);
 
 	/* Restart the system call when we may have been revoked. */
 	if (tp->t_revokecnt != revokecnt)
 		return (ERESTART);
 
 	return (error);
 }
 
 void
 tty_flush(struct tty *tp, int flags)
 {
 
 	if (flags & FWRITE) {
 		tp->t_flags &= ~TF_HIWAT_OUT;
 		ttyoutq_flush(&tp->t_outq);
 		tty_wakeup(tp, FWRITE);
 		if (!tty_gone(tp)) {
 			ttydevsw_outwakeup(tp);
 			ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE);
 		}
 	}
 	if (flags & FREAD) {
 		tty_hiwat_in_unblock(tp);
 		ttyinq_flush(&tp->t_inq);
 		tty_wakeup(tp, FREAD);
 		if (!tty_gone(tp)) {
 			ttydevsw_inwakeup(tp);
 			ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD);
 		}
 	}
 }
 
 void
 tty_set_winsize(struct tty *tp, const struct winsize *wsz)
 {
 
 	if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0)
 		return;
 	tp->t_winsize = *wsz;
 	tty_signal_pgrp(tp, SIGWINCH);
 }
 
 static int
 tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag,
     struct thread *td)
 {
 	int error;
 
 	switch (cmd) {
 	/*
 	 * Modem commands.
 	 * The SER_* and TIOCM_* flags are the same, but one bit
 	 * shifted. I don't know why.
 	 */
 	case TIOCSDTR:
 		ttydevsw_modem(tp, SER_DTR, 0);
 		return (0);
 	case TIOCCDTR:
 		ttydevsw_modem(tp, 0, SER_DTR);
 		return (0);
 	case TIOCMSET: {
 		int bits = *(int *)data;
 		ttydevsw_modem(tp,
 		    (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1,
 		    ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1);
 		return (0);
 	}
 	case TIOCMBIS: {
 		int bits = *(int *)data;
 		ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0);
 		return (0);
 	}
 	case TIOCMBIC: {
 		int bits = *(int *)data;
 		ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1);
 		return (0);
 	}
 	case TIOCMGET:
 		*(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1);
 		return (0);
 
 	case FIOASYNC:
 		if (*(int *)data)
 			tp->t_flags |= TF_ASYNC;
 		else
 			tp->t_flags &= ~TF_ASYNC;
 		return (0);
 	case FIONBIO:
 		/* This device supports non-blocking operation. */
 		return (0);
 	case FIONREAD:
 		*(int *)data = ttyinq_bytescanonicalized(&tp->t_inq);
 		return (0);
 	case FIONWRITE:
 	case TIOCOUTQ:
 		*(int *)data = ttyoutq_bytesused(&tp->t_outq);
 		return (0);
 	case FIOSETOWN:
 		if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc))
 			/* Not allowed to set ownership. */
 			return (ENOTTY);
 
 		/* Temporarily unlock the TTY to set ownership. */
 		tty_unlock(tp);
 		error = fsetown(*(int *)data, &tp->t_sigio);
 		tty_lock(tp);
 		return (error);
 	case FIOGETOWN:
 		if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc))
 			/* Not allowed to set ownership. */
 			return (ENOTTY);
 
 		/* Get ownership. */
 		*(int *)data = fgetown(&tp->t_sigio);
 		return (0);
 	case TIOCGETA:
 		/* Obtain terminal flags through tcgetattr(). */
 		*(struct termios*)data = tp->t_termios;
 		return (0);
 	case TIOCSETA:
 	case TIOCSETAW:
 	case TIOCSETAF: {
 		struct termios *t = data;
 
 		/*
 		 * Who makes up these funny rules? According to POSIX,
 		 * input baud rate is set equal to the output baud rate
 		 * when zero.
 		 */
 		if (t->c_ispeed == 0)
 			t->c_ispeed = t->c_ospeed;
 
 		/* Discard any unsupported bits. */
 		t->c_iflag &= TTYSUP_IFLAG;
 		t->c_oflag &= TTYSUP_OFLAG;
 		t->c_lflag &= TTYSUP_LFLAG;
 		t->c_cflag &= TTYSUP_CFLAG;
 
 		/* Set terminal flags through tcsetattr(). */
 		if (cmd == TIOCSETAW || cmd == TIOCSETAF) {
 			error = tty_drain(tp, 0);
 			if (error)
 				return (error);
 			if (cmd == TIOCSETAF)
 				tty_flush(tp, FREAD);
 		}
 
 		/*
 		 * Only call param() when the flags really change.
 		 */
 		if ((t->c_cflag & CIGNORE) == 0 &&
 		    (tp->t_termios.c_cflag != t->c_cflag ||
 		    ((tp->t_termios.c_iflag ^ t->c_iflag) &
 		    (IXON|IXOFF|IXANY)) ||
 		    tp->t_termios.c_ispeed != t->c_ispeed ||
 		    tp->t_termios.c_ospeed != t->c_ospeed)) {
 			error = ttydevsw_param(tp, t);
 			if (error)
 				return (error);
 
 			/* XXX: CLOCAL? */
 
 			tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE;
 			tp->t_termios.c_ispeed = t->c_ispeed;
 			tp->t_termios.c_ospeed = t->c_ospeed;
 
 			/* Baud rate has changed - update watermarks. */
 			error = tty_watermarks(tp);
 			if (error)
 				return (error);
 		}
 
 		/* Copy new non-device driver parameters. */
 		tp->t_termios.c_iflag = t->c_iflag;
 		tp->t_termios.c_oflag = t->c_oflag;
 		tp->t_termios.c_lflag = t->c_lflag;
 		memcpy(&tp->t_termios.c_cc, t->c_cc, sizeof t->c_cc);
 
 		ttydisc_optimize(tp);
 
 		if ((t->c_lflag & ICANON) == 0) {
 			/*
 			 * When in non-canonical mode, wake up all
 			 * readers. Canonicalize any partial input. VMIN
 			 * and VTIME could also be adjusted.
 			 */
 			ttyinq_canonicalize(&tp->t_inq);
 			tty_wakeup(tp, FREAD);
 		}
 
 		/*
 		 * For packet mode: notify the PTY consumer that VSTOP
 		 * and VSTART may have been changed.
 		 */
 		if (tp->t_termios.c_iflag & IXON &&
 		    tp->t_termios.c_cc[VSTOP] == CTRL('S') &&
 		    tp->t_termios.c_cc[VSTART] == CTRL('Q'))
 			ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP);
 		else
 			ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP);
 		return (0);
 	}
 	case TIOCGETD:
 		/* For compatibility - we only support TTYDISC. */
 		*(int *)data = TTYDISC;
 		return (0);
 	case TIOCGPGRP:
 		if (!tty_is_ctty(tp, td->td_proc))
 			return (ENOTTY);
 
 		if (tp->t_pgrp != NULL)
 			*(int *)data = tp->t_pgrp->pg_id;
 		else
 			*(int *)data = NO_PID;
 		return (0);
 	case TIOCGSID:
 		if (!tty_is_ctty(tp, td->td_proc))
 			return (ENOTTY);
 
 		MPASS(tp->t_session);
 		*(int *)data = tp->t_session->s_sid;
 		return (0);
 	case TIOCNOTTY:
 		return (tty_drop_ctty(tp, td->td_proc));
 	case TIOCSCTTY: {
 		struct proc *p = td->td_proc;
 
 		/* XXX: This looks awful. */
 		tty_unlock(tp);
 		sx_xlock(&proctree_lock);
 		tty_lock(tp);
 
 		if (!SESS_LEADER(p)) {
 			/* Only the session leader may do this. */
 			sx_xunlock(&proctree_lock);
 			return (EPERM);
 		}
 
 		if (tp->t_session != NULL && tp->t_session == p->p_session) {
 			/* This is already our controlling TTY. */
 			sx_xunlock(&proctree_lock);
 			return (0);
 		}
 
 		if (p->p_session->s_ttyp != NULL ||
 		    (tp->t_session != NULL && tp->t_session->s_ttyvp != NULL &&
 		    tp->t_session->s_ttyvp->v_type != VBAD)) {
 			/*
 			 * There is already a relation between a TTY and
 			 * a session, or the caller is not the session
 			 * leader.
 			 *
 			 * Allow the TTY to be stolen when the vnode is
 			 * invalid, but the reference to the TTY is
 			 * still active.  This allows immediate reuse of
 			 * TTYs of which the session leader has been
 			 * killed or the TTY revoked.
 			 */
 			sx_xunlock(&proctree_lock);
 			return (EPERM);
 		}
 
 		/* Connect the session to the TTY. */
 		tp->t_session = p->p_session;
 		tp->t_session->s_ttyp = tp;
 		tp->t_sessioncnt++;
 
 		/* Assign foreground process group. */
 		tp->t_pgrp = p->p_pgrp;
 		PROC_LOCK(p);
 		p->p_flag |= P_CONTROLT;
 		PROC_UNLOCK(p);
 
 		sx_xunlock(&proctree_lock);
 		return (0);
 	}
 	case TIOCSPGRP: {
 		struct pgrp *pg;
 
 		/*
 		 * XXX: Temporarily unlock the TTY to locate the process
 		 * group. This code would be lot nicer if we would ever
 		 * decompose proctree_lock.
 		 */
 		tty_unlock(tp);
 		sx_slock(&proctree_lock);
 		pg = pgfind(*(int *)data);
 		if (pg != NULL)
 			PGRP_UNLOCK(pg);
 		if (pg == NULL || pg->pg_session != td->td_proc->p_session) {
 			sx_sunlock(&proctree_lock);
 			tty_lock(tp);
 			return (EPERM);
 		}
 		tty_lock(tp);
 
 		/*
 		 * Determine if this TTY is the controlling TTY after
 		 * relocking the TTY.
 		 */
 		if (!tty_is_ctty(tp, td->td_proc)) {
 			sx_sunlock(&proctree_lock);
 			return (ENOTTY);
 		}
 		tp->t_pgrp = pg;
 		sx_sunlock(&proctree_lock);
 
 		/* Wake up the background process groups. */
 		cv_broadcast(&tp->t_bgwait);
 		return (0);
 	}
 	case TIOCFLUSH: {
 		int flags = *(int *)data;
 
 		if (flags == 0)
 			flags = (FREAD|FWRITE);
 		else
 			flags &= (FREAD|FWRITE);
 		tty_flush(tp, flags);
 		return (0);
 	}
 	case TIOCDRAIN:
 		/* Drain TTY output. */
 		return tty_drain(tp, 0);
 	case TIOCGDRAINWAIT:
 		*(int *)data = tp->t_drainwait;
 		return (0);
 	case TIOCSDRAINWAIT:
 		error = priv_check(td, PRIV_TTY_DRAINWAIT);
 		if (error == 0)
 			tp->t_drainwait = *(int *)data;
 		return (error);
 	case TIOCCONS:
 		/* Set terminal as console TTY. */
 		if (*(int *)data) {
 			error = priv_check(td, PRIV_TTY_CONSOLE);
 			if (error)
 				return (error);
 			error = constty_set(tp);
 		} else {
 			error = constty_clear(tp);
 		}
 		return (error);
 	case TIOCGWINSZ:
 		/* Obtain window size. */
 		*(struct winsize*)data = tp->t_winsize;
 		return (0);
 	case TIOCSWINSZ:
 		/* Set window size. */
 		tty_set_winsize(tp, data);
 		return (0);
 	case TIOCEXCL:
 		tp->t_flags |= TF_EXCLUDE;
 		return (0);
 	case TIOCNXCL:
 		tp->t_flags &= ~TF_EXCLUDE;
 		return (0);
 	case TIOCSTOP:
 		tp->t_flags |= TF_STOPPED;
 		ttydevsw_pktnotify(tp, TIOCPKT_STOP);
 		return (0);
 	case TIOCSTART:
 		tp->t_flags &= ~TF_STOPPED;
 		tp->t_termios.c_lflag &= ~FLUSHO;
 		ttydevsw_outwakeup(tp);
 		ttydevsw_pktnotify(tp, TIOCPKT_START);
 		return (0);
 	case TIOCSTAT:
 		tty_info(tp);
 		return (0);
 	case TIOCSTI:
 		if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI))
 			return (EPERM);
 		if (!tty_is_ctty(tp, td->td_proc) &&
 		    priv_check(td, PRIV_TTY_STI))
 			return (EACCES);
 		ttydisc_rint(tp, *(char *)data, 0);
 		ttydisc_rint_done(tp);
 		return (0);
 	}
 
 #ifdef COMPAT_43TTY
 	return tty_ioctl_compat(tp, cmd, data, fflag, td);
 #else /* !COMPAT_43TTY */
 	return (ENOIOCTL);
 #endif /* COMPAT_43TTY */
 }
 
 int
 tty_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, struct thread *td)
 {
 	int error;
 
 	tty_assert_locked(tp);
 
 	if (tty_gone(tp))
 		return (ENXIO);
 
 	error = ttydevsw_ioctl(tp, cmd, data, td);
 	if (error == ENOIOCTL)
 		error = tty_generic_ioctl(tp, cmd, data, fflag, td);
 
 	return (error);
 }
 
 dev_t
 tty_udev(struct tty *tp)
 {
 
 	if (tp->t_dev)
 		return (dev2udev(tp->t_dev));
 	else
 		return (NODEV);
 }
 
 int
 tty_checkoutq(struct tty *tp)
 {
 
 	/* 256 bytes should be enough to print a log message. */
 	return (ttyoutq_bytesleft(&tp->t_outq) >= 256);
 }
 
 void
 tty_hiwat_in_block(struct tty *tp)
 {
 
 	if ((tp->t_flags & TF_HIWAT_IN) == 0 &&
 	    tp->t_termios.c_iflag & IXOFF &&
 	    tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) {
 		/*
 		 * Input flow control. Only enter the high watermark when we
 		 * can successfully store the VSTOP character.
 		 */
 		if (ttyoutq_write_nofrag(&tp->t_outq,
 		    &tp->t_termios.c_cc[VSTOP], 1) == 0)
 			tp->t_flags |= TF_HIWAT_IN;
 	} else {
 		/* No input flow control. */
 		tp->t_flags |= TF_HIWAT_IN;
 	}
 }
 
 void
 tty_hiwat_in_unblock(struct tty *tp)
 {
 
 	if (tp->t_flags & TF_HIWAT_IN &&
 	    tp->t_termios.c_iflag & IXOFF &&
 	    tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) {
 		/*
 		 * Input flow control. Only leave the high watermark when we
 		 * can successfully store the VSTART character.
 		 */
 		if (ttyoutq_write_nofrag(&tp->t_outq,
 		    &tp->t_termios.c_cc[VSTART], 1) == 0)
 			tp->t_flags &= ~TF_HIWAT_IN;
 	} else {
 		/* No input flow control. */
 		tp->t_flags &= ~TF_HIWAT_IN;
 	}
 
 	if (!tty_gone(tp))
 		ttydevsw_inwakeup(tp);
 }
 
 /*
  * TTY hooks interface.
  */
 
 static int
 ttyhook_defrint(struct tty *tp, char c, int flags)
 {
 
 	if (ttyhook_rint_bypass(tp, &c, 1) != 1)
 		return (-1);
 
 	return (0);
 }
 
 int
 ttyhook_register(struct tty **rtp, struct proc *p, int fd, struct ttyhook *th,
     void *softc)
 {
 	struct tty *tp;
 	struct file *fp;
 	struct cdev *dev;
 	struct cdevsw *cdp;
 	struct filedesc *fdp;
 	cap_rights_t rights;
 	int error, ref;
 
 	/* Validate the file descriptor. */
 	/*
 	 * XXX this code inspects a file descriptor from a different process,
 	 * but there is no dedicated routine to do it in fd code, making the
 	 * ordeal highly questionable.
 	 */
 	fdp = p->p_fd;
 	FILEDESC_SLOCK(fdp);
 	error = fget_cap_noref(fdp, fd, cap_rights_init_one(&rights, CAP_TTYHOOK),
 	    &fp, NULL);
 	if (error == 0 && !fhold(fp))
 		error = EBADF;
 	FILEDESC_SUNLOCK(fdp);
 	if (error != 0)
 		return (error);
 	if (fp->f_ops == &badfileops) {
 		error = EBADF;
 		goto done1;
 	}
 
 	/*
 	 * Make sure the vnode is bound to a character device.
 	 * Unlocked check for the vnode type is ok there, because we
 	 * only shall prevent calling devvn_refthread on the file that
 	 * never has been opened over a character device.
 	 */
 	if (fp->f_type != DTYPE_VNODE || fp->f_vnode->v_type != VCHR) {
 		error = EINVAL;
 		goto done1;
 	}
 
 	/* Make sure it is a TTY. */
 	cdp = devvn_refthread(fp->f_vnode, &dev, &ref);
 	if (cdp == NULL) {
 		error = ENXIO;
 		goto done1;
 	}
 	if (dev != fp->f_data) {
 		error = ENXIO;
 		goto done2;
 	}
 	if (cdp != &ttydev_cdevsw) {
 		error = ENOTTY;
 		goto done2;
 	}
 	tp = dev->si_drv1;
 
 	/* Try to attach the hook to the TTY. */
 	error = EBUSY;
 	tty_lock(tp);
 	MPASS((tp->t_hook == NULL) == ((tp->t_flags & TF_HOOK) == 0));
 	if (tp->t_flags & TF_HOOK)
 		goto done3;
 
 	tp->t_flags |= TF_HOOK;
 	tp->t_hook = th;
 	tp->t_hooksoftc = softc;
 	*rtp = tp;
 	error = 0;
 
 	/* Maybe we can switch into bypass mode now. */
 	ttydisc_optimize(tp);
 
 	/* Silently convert rint() calls to rint_bypass() when possible. */
 	if (!ttyhook_hashook(tp, rint) && ttyhook_hashook(tp, rint_bypass))
 		th->th_rint = ttyhook_defrint;
 
 done3:	tty_unlock(tp);
 done2:	dev_relthread(dev, ref);
 done1:	fdrop(fp, curthread);
 	return (error);
 }
 
 void
 ttyhook_unregister(struct tty *tp)
 {
 
 	tty_assert_locked(tp);
 	MPASS(tp->t_flags & TF_HOOK);
 
 	/* Disconnect the hook. */
 	tp->t_flags &= ~TF_HOOK;
 	tp->t_hook = NULL;
 
 	/* Maybe we need to leave bypass mode. */
 	ttydisc_optimize(tp);
 
 	/* Maybe deallocate the TTY as well. */
 	tty_rel_free(tp);
 }
 
 /*
  * /dev/console handling.
  */
 
 static int
 ttyconsdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
 {
 	struct tty *tp;
 
 	/* System has no console device. */
 	if (dev_console_filename == NULL)
 		return (ENXIO);
 
 	/* Look up corresponding TTY by device name. */
 	sx_slock(&tty_list_sx);
 	TAILQ_FOREACH(tp, &tty_list, t_list) {
 		if (strcmp(dev_console_filename, tty_devname(tp)) == 0) {
 			dev_console->si_drv1 = tp;
 			break;
 		}
 	}
 	sx_sunlock(&tty_list_sx);
 
 	/* System console has no TTY associated. */
 	if (dev_console->si_drv1 == NULL)
 		return (ENXIO);
 
 	return (ttydev_open(dev, oflags, devtype, td));
 }
 
 static int
 ttyconsdev_write(struct cdev *dev, struct uio *uio, int ioflag)
 {
 
 	log_console(uio);
 
 	return (ttydev_write(dev, uio, ioflag));
 }
 
 /*
  * /dev/console is a little different than normal TTY's.  When opened,
  * it determines which TTY to use.  When data gets written to it, it
  * will be logged in the kernel message buffer.
  */
 static struct cdevsw ttyconsdev_cdevsw = {
 	.d_version	= D_VERSION,
 	.d_open		= ttyconsdev_open,
 	.d_close	= ttydev_close,
 	.d_read		= ttydev_read,
 	.d_write	= ttyconsdev_write,
 	.d_ioctl	= ttydev_ioctl,
 	.d_kqfilter	= ttydev_kqfilter,
 	.d_poll		= ttydev_poll,
 	.d_mmap		= ttydev_mmap,
 	.d_name		= "ttyconsdev",
 	.d_flags	= D_TTY,
 };
 
 static void
 ttyconsdev_init(void *unused __unused)
 {
 
 	dev_console = make_dev_credf(MAKEDEV_ETERNAL, &ttyconsdev_cdevsw, 0,
 	    NULL, UID_ROOT, GID_WHEEL, 0600, "console");
 }
 
 SYSINIT(tty, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyconsdev_init, NULL);
 
 void
 ttyconsdev_select(const char *name)
 {
 
 	dev_console_filename = name;
 }
 
 /*
  * Debugging routines.
  */
 
 #include "opt_ddb.h"
 #ifdef DDB
 #include <ddb/ddb.h>
 #include <ddb/db_sym.h>
 
 static const struct {
 	int flag;
 	char val;
 } ttystates[] = {
 #if 0
 	{ TF_NOPREFIX,		'N' },
 #endif
 	{ TF_INITLOCK,		'I' },
 	{ TF_CALLOUT,		'C' },
 
 	/* Keep these together -> 'Oi' and 'Oo'. */
 	{ TF_OPENED,		'O' },
 	{ TF_OPENED_IN,		'i' },
 	{ TF_OPENED_OUT,	'o' },
 	{ TF_OPENED_CONS,	'c' },
 
 	{ TF_GONE,		'G' },
 	{ TF_OPENCLOSE,		'B' },
 	{ TF_ASYNC,		'Y' },
 	{ TF_LITERAL,		'L' },
 
 	/* Keep these together -> 'Hi' and 'Ho'. */
 	{ TF_HIWAT,		'H' },
 	{ TF_HIWAT_IN,		'i' },
 	{ TF_HIWAT_OUT,		'o' },
 
 	{ TF_STOPPED,		'S' },
 	{ TF_EXCLUDE,		'X' },
 	{ TF_BYPASS,		'l' },
 	{ TF_ZOMBIE,		'Z' },
 	{ TF_HOOK,		's' },
 
 	/* Keep these together -> 'bi' and 'bo'. */
 	{ TF_BUSY,		'b' },
 	{ TF_BUSY_IN,		'i' },
 	{ TF_BUSY_OUT,		'o' },
 
 	{ 0,			'\0'},
 };
 
 #define	TTY_FLAG_BITS \
 	"\20\1NOPREFIX\2INITLOCK\3CALLOUT\4OPENED_IN" \
 	"\5OPENED_OUT\6OPENED_CONS\7GONE\10OPENCLOSE" \
 	"\11ASYNC\12LITERAL\13HIWAT_IN\14HIWAT_OUT" \
 	"\15STOPPED\16EXCLUDE\17BYPASS\20ZOMBIE" \
 	"\21HOOK\22BUSY_IN\23BUSY_OUT"
 
 #define DB_PRINTSYM(name, addr) \
 	db_printf("%s  " #name ": ", sep); \
 	db_printsym((db_addr_t) addr, DB_STGY_ANY); \
 	db_printf("\n");
 
 static void
 _db_show_devsw(const char *sep, const struct ttydevsw *tsw)
 {
 
 	db_printf("%sdevsw: ", sep);
 	db_printsym((db_addr_t)tsw, DB_STGY_ANY);
 	db_printf(" (%p)\n", tsw);
 	DB_PRINTSYM(open, tsw->tsw_open);
 	DB_PRINTSYM(close, tsw->tsw_close);
 	DB_PRINTSYM(outwakeup, tsw->tsw_outwakeup);
 	DB_PRINTSYM(inwakeup, tsw->tsw_inwakeup);
 	DB_PRINTSYM(ioctl, tsw->tsw_ioctl);
 	DB_PRINTSYM(param, tsw->tsw_param);
 	DB_PRINTSYM(modem, tsw->tsw_modem);
 	DB_PRINTSYM(mmap, tsw->tsw_mmap);
 	DB_PRINTSYM(pktnotify, tsw->tsw_pktnotify);
 	DB_PRINTSYM(free, tsw->tsw_free);
 }
 
 static void
 _db_show_hooks(const char *sep, const struct ttyhook *th)
 {
 
 	db_printf("%shook: ", sep);
 	db_printsym((db_addr_t)th, DB_STGY_ANY);
 	db_printf(" (%p)\n", th);
 	if (th == NULL)
 		return;
 	DB_PRINTSYM(rint, th->th_rint);
 	DB_PRINTSYM(rint_bypass, th->th_rint_bypass);
 	DB_PRINTSYM(rint_done, th->th_rint_done);
 	DB_PRINTSYM(rint_poll, th->th_rint_poll);
 	DB_PRINTSYM(getc_inject, th->th_getc_inject);
 	DB_PRINTSYM(getc_capture, th->th_getc_capture);
 	DB_PRINTSYM(getc_poll, th->th_getc_poll);
 	DB_PRINTSYM(close, th->th_close);
 }
 
 static void
 _db_show_termios(const char *name, const struct termios *t)
 {
 
 	db_printf("%s: iflag 0x%x oflag 0x%x cflag 0x%x "
 	    "lflag 0x%x ispeed %u ospeed %u\n", name,
 	    t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag,
 	    t->c_ispeed, t->c_ospeed);
 }
 
 /* DDB command to show TTY statistics. */
 DB_SHOW_COMMAND(tty, db_show_tty)
 {
 	struct tty *tp;
 
 	if (!have_addr) {
 		db_printf("usage: show tty <addr>\n");
 		return;
 	}
 	tp = (struct tty *)addr;
 
 	db_printf("%p: %s\n", tp, tty_devname(tp));
 	db_printf("\tmtx: %p\n", tp->t_mtx);
 	db_printf("\tflags: 0x%b\n", tp->t_flags, TTY_FLAG_BITS);
 	db_printf("\trevokecnt: %u\n", tp->t_revokecnt);
 
 	/* Buffering mechanisms. */
 	db_printf("\tinq: %p begin %u linestart %u reprint %u end %u "
 	    "nblocks %u quota %u\n", &tp->t_inq, tp->t_inq.ti_begin,
 	    tp->t_inq.ti_linestart, tp->t_inq.ti_reprint, tp->t_inq.ti_end,
 	    tp->t_inq.ti_nblocks, tp->t_inq.ti_quota);
 	db_printf("\toutq: %p begin %u end %u nblocks %u quota %u\n",
 	    &tp->t_outq, tp->t_outq.to_begin, tp->t_outq.to_end,
 	    tp->t_outq.to_nblocks, tp->t_outq.to_quota);
 	db_printf("\tinlow: %zu\n", tp->t_inlow);
 	db_printf("\toutlow: %zu\n", tp->t_outlow);
 	_db_show_termios("\ttermios", &tp->t_termios);
 	db_printf("\twinsize: row %u col %u xpixel %u ypixel %u\n",
 	    tp->t_winsize.ws_row, tp->t_winsize.ws_col,
 	    tp->t_winsize.ws_xpixel, tp->t_winsize.ws_ypixel);
 	db_printf("\tcolumn: %u\n", tp->t_column);
 	db_printf("\twritepos: %u\n", tp->t_writepos);
 	db_printf("\tcompatflags: 0x%x\n", tp->t_compatflags);
 
 	/* Init/lock-state devices. */
 	_db_show_termios("\ttermios_init_in", &tp->t_termios_init_in);
 	_db_show_termios("\ttermios_init_out", &tp->t_termios_init_out);
 	_db_show_termios("\ttermios_lock_in", &tp->t_termios_lock_in);
 	_db_show_termios("\ttermios_lock_out", &tp->t_termios_lock_out);
 
 	/* Hooks */
 	_db_show_devsw("\t", tp->t_devsw);
 	_db_show_hooks("\t", tp->t_hook);
 
 	/* Process info. */
 	db_printf("\tpgrp: %p gid %d\n", tp->t_pgrp,
 	    tp->t_pgrp ? tp->t_pgrp->pg_id : 0);
 	db_printf("\tsession: %p", tp->t_session);
 	if (tp->t_session != NULL)
 	    db_printf(" count %u leader %p tty %p sid %d login %s",
 		tp->t_session->s_count, tp->t_session->s_leader,
 		tp->t_session->s_ttyp, tp->t_session->s_sid,
 		tp->t_session->s_login);
 	db_printf("\n");
 	db_printf("\tsessioncnt: %u\n", tp->t_sessioncnt);
 	db_printf("\tdevswsoftc: %p\n", tp->t_devswsoftc);
 	db_printf("\thooksoftc: %p\n", tp->t_hooksoftc);
 	db_printf("\tdev: %p\n", tp->t_dev);
 }
 
 /* DDB command to list TTYs. */
 DB_SHOW_ALL_COMMAND(ttys, db_show_all_ttys)
 {
 	struct tty *tp;
 	size_t isiz, osiz;
 	int i, j;
 
 	/* Make the output look like `pstat -t'. */
 	db_printf("PTR        ");
 #if defined(__LP64__)
 	db_printf("        ");
 #endif
 	db_printf("      LINE   INQ  CAN  LIN  LOW  OUTQ  USE  LOW   "
 	    "COL  SESS  PGID STATE\n");
 
 	TAILQ_FOREACH(tp, &tty_list, t_list) {
 		isiz = tp->t_inq.ti_nblocks * TTYINQ_DATASIZE;
 		osiz = tp->t_outq.to_nblocks * TTYOUTQ_DATASIZE;
 
 		db_printf("%p %10s %5zu %4u %4u %4zu %5zu %4u %4zu %5u %5d "
 		    "%5d ", tp, tty_devname(tp), isiz,
 		    tp->t_inq.ti_linestart - tp->t_inq.ti_begin,
 		    tp->t_inq.ti_end - tp->t_inq.ti_linestart,
 		    isiz - tp->t_inlow, osiz,
 		    tp->t_outq.to_end - tp->t_outq.to_begin,
 		    osiz - tp->t_outlow, MIN(tp->t_column, 99999),
 		    tp->t_session ? tp->t_session->s_sid : 0,
 		    tp->t_pgrp ? tp->t_pgrp->pg_id : 0);
 
 		/* Flag bits. */
 		for (i = j = 0; ttystates[i].flag; i++)
 			if (tp->t_flags & ttystates[i].flag) {
 				db_printf("%c", ttystates[i].val);
 				j++;
 			}
 		if (j == 0)
 			db_printf("-");
 		db_printf("\n");
 	}
 }
 #endif /* DDB */
diff --git a/sys/sys/_termios.h b/sys/sys/_termios.h
index 327ffcb2f98b..823752732ee2 100644
--- a/sys/sys/_termios.h
+++ b/sys/sys/_termios.h
@@ -1,237 +1,238 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (c) 1988, 1989, 1993, 1994
  *	The 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. 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.
  *
  *	@(#)termios.h	8.3 (Berkeley) 3/28/94
  */
 
 #ifndef _SYS__TERMIOS_H_
 #define	_SYS__TERMIOS_H_
 
 /*
  * Special Control Characters
  *
  * Index into c_cc[] character array.
  *
  *	Name	     Subscript	Enabled by
  */
 #define	VEOF		0	/* ICANON */
 #define	VEOL		1	/* ICANON */
 #if __BSD_VISIBLE
 #define	VEOL2		2	/* ICANON together with IEXTEN */
 #endif
 #define	VERASE		3	/* ICANON */
 #if __BSD_VISIBLE
 #define	VWERASE 	4	/* ICANON together with IEXTEN */
 #endif
 #define	VKILL		5	/* ICANON */
 #if __BSD_VISIBLE
 #define	VREPRINT 	6	/* ICANON together with IEXTEN */
 #define	VERASE2 	7	/* ICANON */
 #endif
 /*			7	   ex-spare 1 */
 #define	VINTR		8	/* ISIG */
 #define	VQUIT		9	/* ISIG */
 #define	VSUSP		10	/* ISIG */
 #if __BSD_VISIBLE
 #define	VDSUSP		11	/* ISIG together with IEXTEN */
 #endif
 #define	VSTART		12	/* IXON, IXOFF */
 #define	VSTOP		13	/* IXON, IXOFF */
 #if __BSD_VISIBLE
 #define	VLNEXT		14	/* IEXTEN */
 #define	VDISCARD	15	/* IEXTEN */
 #endif
 #define	VMIN		16	/* !ICANON */
 #define	VTIME		17	/* !ICANON */
 #if __BSD_VISIBLE
 #define	VSTATUS		18	/* ICANON together with IEXTEN */
 /*			19	   spare 2 */
 #endif
 #define	NCCS		20
 
 #define	_POSIX_VDISABLE	0xff
 
 /*
  * Input flags - software input processing
  */
 #define	IGNBRK		0x00000001	/* ignore BREAK condition */
 #define	BRKINT		0x00000002	/* map BREAK to SIGINTR */
 #define	IGNPAR		0x00000004	/* ignore (discard) parity errors */
 #define	PARMRK		0x00000008	/* mark parity and framing errors */
 #define	INPCK		0x00000010	/* enable checking of parity errors */
 #define	ISTRIP		0x00000020	/* strip 8th bit off chars */
 #define	INLCR		0x00000040	/* map NL into CR */
 #define	IGNCR		0x00000080	/* ignore CR */
 #define	ICRNL		0x00000100	/* map CR to NL (ala CRMOD) */
 #define	IXON		0x00000200	/* enable output flow control */
 #define	IXOFF		0x00000400	/* enable input flow control */
 #if __XSI_VISIBLE || __POSIX_VISIBLE >= 200809
 #define	IXANY		0x00000800	/* any char will restart after stop */
 #endif
 #if __BSD_VISIBLE
 #define	IMAXBEL		0x00002000	/* ring bell on input queue full */
+#define IUTF8		0x00004000	/* assume input is utf-8 encoded */
 #endif
 
 /*
  * Output flags - software output processing
  */
 #define	OPOST		0x00000001	/* enable following output processing */
 #if __XSI_VISIBLE
 #define	ONLCR		0x00000002	/* map NL to CR-NL (ala CRMOD) */
 #endif
 #if __BSD_VISIBLE
 #define	TABDLY		0x00000004	/* tab delay mask */
 #define	    TAB0	    0x00000000	    /* no tab delay and expansion */
 #define	    TAB3	    0x00000004	    /* expand tabs to spaces */
 #define	ONOEOT		0x00000008	/* discard EOT's (^D) on output) */
 #endif
 #if __XSI_VISIBLE
 #define	OCRNL		0x00000010	/* map CR to NL on output */
 #define	ONOCR		0x00000020	/* no CR output at column 0 */
 #define	ONLRET		0x00000040	/* NL performs CR function */
 #endif
 
 /*
  * Control flags - hardware control of terminal
  */
 #if __BSD_VISIBLE
 #define	CIGNORE		0x00000001	/* ignore control flags */
 #endif
 #define	CSIZE		0x00000300	/* character size mask */
 #define	    CS5		    0x00000000	    /* 5 bits (pseudo) */
 #define	    CS6		    0x00000100	    /* 6 bits */
 #define	    CS7		    0x00000200	    /* 7 bits */
 #define	    CS8		    0x00000300	    /* 8 bits */
 #define	CSTOPB		0x00000400	/* send 2 stop bits */
 #define	CREAD		0x00000800	/* enable receiver */
 #define	PARENB		0x00001000	/* parity enable */
 #define	PARODD		0x00002000	/* odd parity, else even */
 #define	HUPCL		0x00004000	/* hang up on last close */
 #define	CLOCAL		0x00008000	/* ignore modem status lines */
 #if __BSD_VISIBLE
 #define	CCTS_OFLOW	0x00010000	/* CTS flow control of output */
 #define	CRTSCTS		(CCTS_OFLOW | CRTS_IFLOW)
 #define	CRTS_IFLOW	0x00020000	/* RTS flow control of input */
 #define	CDTR_IFLOW	0x00040000	/* DTR flow control of input */
 #define	CDSR_OFLOW	0x00080000	/* DSR flow control of output */
 #define	CCAR_OFLOW	0x00100000	/* DCD flow control of output */
 #define	CNO_RTSDTR	0x00200000	/* Do not assert RTS or DTR automatically */
 #endif
 
 /*
  * "Local" flags - dumping ground for other state
  *
  * Warning: some flags in this structure begin with
  * the letter "I" and look like they belong in the
  * input flag.
  */
 
 #if __BSD_VISIBLE
 #define	ECHOKE		0x00000001	/* visual erase for line kill */
 #endif
 #define	ECHOE		0x00000002	/* visually erase chars */
 #define	ECHOK		0x00000004	/* echo NL after line kill */
 #define	ECHO		0x00000008	/* enable echoing */
 #define	ECHONL		0x00000010	/* echo NL even if ECHO is off */
 #if __BSD_VISIBLE
 #define	ECHOPRT		0x00000020	/* visual erase mode for hardcopy */
 #define	ECHOCTL  	0x00000040	/* echo control chars as ^(Char) */
 #endif
 #define	ISIG		0x00000080	/* enable signals INTR, QUIT, [D]SUSP */
 #define	ICANON		0x00000100	/* canonicalize input lines */
 #if __BSD_VISIBLE
 #define	ALTWERASE	0x00000200	/* use alternate WERASE algorithm */
 #endif
 #define	IEXTEN		0x00000400	/* enable DISCARD and LNEXT */
 #define	EXTPROC         0x00000800      /* external processing */
 #define	TOSTOP		0x00400000	/* stop background jobs from output */
 #if __BSD_VISIBLE
 #define	FLUSHO		0x00800000	/* output being flushed (state) */
 #define	NOKERNINFO	0x02000000	/* no kernel output from VSTATUS */
 #define	PENDIN		0x20000000	/* XXX retype pending input (state) */
 #endif
 #define	NOFLSH		0x80000000	/* don't flush after interrupt */
 
 /*
  * Standard speeds
  */
 #define	B0	0
 #define	B50	50
 #define	B75	75
 #define	B110	110
 #define	B134	134
 #define	B150	150
 #define	B200	200
 #define	B300	300
 #define	B600	600
 #define	B1200	1200
 #define	B1800	1800
 #define	B2400	2400
 #define	B4800	4800
 #define	B9600	9600
 #define	B19200	19200
 #define	B38400	38400
 #if __BSD_VISIBLE
 #define	B7200	7200
 #define	B14400	14400
 #define	B28800	28800
 #define	B57600	57600
 #define	B76800	76800
 #define	B115200	115200
 #define	B230400	230400
 #define	B460800	460800
 #define B500000 500000
 #define	B921600	921600
 #define B1000000	1000000U
 #define B1500000	1500000U
 #define B2000000	2000000U
 #define B2500000	2500000U
 #define B3000000	3000000U
 #define B3500000	3500000U
 #define B4000000	4000000U
 #define	EXTA	19200
 #define	EXTB	38400
 #endif
 
 typedef unsigned int	tcflag_t;
 typedef unsigned char	cc_t;
 typedef unsigned int	speed_t;
 
 struct termios {
 	tcflag_t	c_iflag;	/* input flags */
 	tcflag_t	c_oflag;	/* output flags */
 	tcflag_t	c_cflag;	/* control flags */
 	tcflag_t	c_lflag;	/* local flags */
 	cc_t		c_cc[NCCS];	/* control chars */
 	speed_t		c_ispeed;	/* input speed */
 	speed_t		c_ospeed;	/* output speed */
 };
 
 #endif /* !_SYS__TERMIOS_H_ */