summaryrefslogtreecommitdiff
path: root/src/lib/libcmd/stty.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libcmd/stty.c')
-rw-r--r--src/lib/libcmd/stty.c971
1 files changed, 971 insertions, 0 deletions
diff --git a/src/lib/libcmd/stty.c b/src/lib/libcmd/stty.c
new file mode 100644
index 0000000..9afcd58
--- /dev/null
+++ b/src/lib/libcmd/stty.c
@@ -0,0 +1,971 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * stty.c
+ * Written by David Korn
+ * Tue Apr 4 10:46:00 EDT 1995
+ */
+
+static const char usage[] =
+"[-?@(#)$Id: stty (AT&T Research) 2010-04-01 $\n]"
+USAGE_LICENSE
+"[+NAME?stty - set or get terminal modes]"
+"[+DESCRIPTION?\bstty\b sets certain terminal I/O modes for the device "
+ "that is the current standard input; without arguments, it writes the "
+ "settings of certain modes to standard output.]"
+"[a:all?Writes to standard output all of the mode settings.]"
+"[f|F:fd|file?Use \afd\a as the terminal fd.]#[fd:=0]"
+"[g:save?Writes the current settings to standard output in a form that "
+ "can be used as an argument to another \bstty\b command. The \brows\b "
+ "and \bcolumns\b values are not included.]"
+"[t:terminal-group?Print the terminal group id of the device, -1 if "
+ "unknown.]"
+"\n"
+"\n[mode ...]\n"
+"\n"
+"[+EXTENDED DESCRIPTION?Modes are specified either as a single name or "
+ "as a name followed by a value. As indicated below, many of the mode "
+ "names can be preceded by a \b-\b to negate its meaning. Modes are "
+ "listed by group corresponding to field in the \btermios\b structure "
+ "defined in \b<termios.h>\b. Modes in the last group are implemented "
+ "using options in the previous groups. Note that many combinations of "
+ "modes make no sense, but no sanity checking is performed. The modes are "
+ "selected from the following:]"
+ "{\fabc\f}"
+"[+EXIT STATUS?]"
+ "{"
+ "[+0?All modes reported or set successfully.]"
+ "[+>0?Standard input not a terminaol or one or more modes "
+ "failed.]"
+ "}"
+"[+SEE ALSO?\btegetattr\b(2), \btcsetattr\b(2), \bioctl\b(2)]"
+;
+
+#include <cmd.h>
+#include <ccode.h>
+#include <ctype.h>
+#include <ast_tty.h>
+#if _sys_ioctl
+#include <sys/ioctl.h>
+#endif
+
+#define C(x) ERROR_catalog(x)
+
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE 0
+#endif
+
+#ifndef NCCS
+# ifdef NCC
+# define NCCS NCC
+# else
+# define NCCS elementsof(((struct termio*)0)->c_cc)
+# endif
+#endif
+
+/* command options */
+#define A_FLAG 1
+#define G_FLAG 2
+#define T_FLAG 4
+
+/* termios fields */
+#define C_FLAG 1
+#define C_LINE 2
+#define C_SPEED 3
+#define I_FLAG 4
+#define O_FLAG 5
+#define L_FLAG 6
+#define T_CHAR 7
+#define W_SIZE 8
+
+#define BIT 1
+#define BITS 2
+#define NUM 3
+#define CHAR 4
+#define SPEED 5
+#define SIZE 6
+#define MIXED 7
+#define SANE 8
+#define COOKED 9
+#define CASE 10
+#define TABS 11
+#define WIND 12
+
+#undef SS /* who co-opted this namespace? */
+
+#define IG 0x0001 /* ignore display */
+#define NL 0x0002 /* entry ends line of display */
+#define SS 0x0004 /* set in sane mode */
+#define US 0x0010 /* unset in sane mode */
+
+typedef struct tty_s
+{
+ const char name[8];
+ unsigned char type;
+ unsigned char field;
+ short flags;
+ unsigned long mask;
+ unsigned long val;
+ const char description[76];
+} Tty_t;
+
+static const Tty_t Ttable[] =
+{
+#ifdef CBAUD
+{ "ispeed", NUM, C_SPEED,0, CBAUD, 0, C("\an\a is the input baud rate") },
+{ "ospeed", NUM, C_SPEED,0, CBAUD, 0, C("\an\a is the output baud rate") },
+{ "speed", NUM, C_SPEED,IG, CBAUD },
+#endif
+{ "0", SPEED, C_FLAG, 0, B0 },
+{ "50", SPEED, C_FLAG, 0, B50 },
+{ "75", SPEED, C_FLAG, 0, B75 },
+{ "110", SPEED, C_FLAG, 0, B110 },
+{ "134", SPEED, C_FLAG, 0, B134 },
+{ "150", SPEED, C_FLAG, 0, B150 },
+{ "200", SPEED, C_FLAG, 0, B200 },
+{ "300", SPEED, C_FLAG, 0, B300 },
+{ "600", SPEED, C_FLAG, 0, B600 },
+{ "1200", SPEED, C_FLAG, 0, B1200 },
+{ "1800", SPEED, C_FLAG, 0, B1800 },
+{ "2400", SPEED, C_FLAG, 0, B2400 },
+{ "4800", SPEED, C_FLAG, 0, B4800 },
+{ "9600", SPEED, C_FLAG, 0, B9600 },
+{ "19200", SPEED, C_FLAG, 0, B19200 },
+{ "38400", SPEED, C_FLAG, 0, B38400 },
+
+#ifdef TIOCSWINSZ
+{ "rows", WIND, W_SIZE, IG, 0, 24, C("\an\a is the number of lines for display") },
+{ "cols", WIND, W_SIZE, IG, 1, 80, C("\an\a is the number of columns for display") },
+{ "columns", WIND, W_SIZE, IG, 1, 80, C("Same as \bcols\b") },
+#endif
+{ "intr", CHAR, T_CHAR, SS, VINTR, 'C', C("Send an interrupt signal") },
+{ "quit", CHAR, T_CHAR, SS, VQUIT, '|', C("Send a quit signal") },
+{ "erase", CHAR, T_CHAR, SS, VERASE, 'H', C("Erase the last character entered") },
+{ "kill", CHAR, T_CHAR, NL|SS, VKILL, 'U', C("Erase the current line") },
+{ "eof", CHAR, T_CHAR, SS, VEOF, 'D', C("Send an end of file") },
+#ifdef VEOL2
+{ "eol2", CHAR, T_CHAR, US, VEOL2, _POSIX_VDISABLE, C("Alternate character to end the line") },
+#endif /* VEOL2 */
+#ifdef VSWTCH
+{ "swtch", CHAR, T_CHAR, US, VSWTCH, _POSIX_VDISABLE, C("Switch to a different shell layer") },
+#endif /* VSWTCH */
+{ "eol", CHAR, T_CHAR, NL|US, VEOL, _POSIX_VDISABLE, C("End the line") },
+#ifdef VSTART
+{ "start", CHAR, T_CHAR, SS, VSTART, 'Q', C("Restart the output after stopping it") },
+#endif /* VSTART */
+#ifdef VSTOP
+{ "stop", CHAR, T_CHAR, SS, VSTOP, 'S', C("Stop the output") },
+#endif /* VSTOP */
+#ifdef VDSUSP
+{ "dsusp", CHAR, T_CHAR, SS, VDSUSP, 'Y', C("Send a terminal stop signal after flushing the input") },
+#endif /* VDSUSP */
+#ifdef VSUSP
+{ "susp", CHAR, T_CHAR, NL|SS, VSUSP, 'Z', C("Send a terminal stop signal") },
+#endif /* VSUSP */
+#ifdef VREPRINT
+{ "rprnt", CHAR, T_CHAR, SS, VREPRINT, 'R', C("Redraw the current line") },
+#endif /* VREPRINT */
+#ifdef VDISCARD
+{ "flush", CHAR, T_CHAR, SS, VDISCARD, 'O', C("Discard output") },
+#endif /* VDISCARD */
+#ifdef VWERASE
+{ "werase", CHAR, T_CHAR, SS, VWERASE, 'W', C("Erase the last word entered") },
+#endif /* VWERASE */
+#ifdef VLNEXT
+{ "lnext", CHAR, T_CHAR, NL|SS, VLNEXT, 'V', C("Enter the next input character literally") },
+#endif /* VLNEXT */
+
+#if _mem_c_line_termios
+{ "line", NUM, C_LINE, 0, 0, 0, C("Line discipline number") },
+#endif
+{ "min", NUM, T_CHAR, 0, VMIN, 0, C("Mininmum number of characters to read in raw mode") },
+{ "time", NUM, T_CHAR, 0, VTIME, 0, C("Number of .1 second intervals with raw mode") },
+
+{ "parenb", BIT, C_FLAG, 0, PARENB, PARENB, C("Enable (disable) parity generation and detection") },
+{ "parodd", BIT, C_FLAG, 0, PARODD, PARODD, C("Use odd (even) parity") },
+#ifdef PAREXT
+{ "parext", BIT, C_FLAG, 0, PAREXT, PAREXT },
+#endif /* PAREXT */
+#ifdef CREAD
+{ "cread", BIT, C_FLAG, SS, CREAD, CREAD, C("Enable (disable) input") },
+#endif /* CREAD */
+{ "cs5", SIZE, C_FLAG, 0, CSIZE, CS5 , C("Char size 5") },
+{ "cs6", SIZE, C_FLAG, 0, CSIZE, CS6 , C("Char size 6") },
+{ "cs7", SIZE, C_FLAG, 0, CSIZE, CS7 , C("Char size 7") },
+{ "cs8", SIZE, C_FLAG, 0, CSIZE, CS8 , C("Char size 8") },
+{ "hupcl", BIT, C_FLAG, 0, HUPCL, HUPCL, C("Hangup (do not hangup) connection on last close") },
+{ "hup", BIT, C_FLAG, IG, HUPCL, HUPCL, C("Same as \bhupcl\b") },
+{ "cstopb", BIT, C_FLAG, 0, CSTOPB, CSTOPB, C("Use two (one) stop bits") },
+#ifdef CRTSCTS
+{ "crtscts", BIT, C_FLAG, 0, CRTSCTS, CRTSCTS, C("Enable (disable) RTS/CTS handshaking") },
+#endif /* CRTSCTS */
+{ "clocal", BIT, C_FLAG, NL, CLOCAL, CLOCAL, C("Disable (enable) modem control signals") },
+
+{ "ignbrk", BIT, I_FLAG, US, IGNBRK, IGNBRK, C("Ignore (do not ignore) break characters") },
+{ "brkint", BIT, I_FLAG, SS, BRKINT, BRKINT, C("Generate (do not generate) INTR signal on break") },
+{ "ignpar", BIT, I_FLAG, 0, IGNPAR, IGNPAR, C("Ignore (do not ignore) characters with parity errors") },
+{ "parmrk", BIT, I_FLAG, 0, PARMRK, PARMRK, C("Mark (do not mark) parity errors") },
+{ "inpck", BIT, I_FLAG, 0, INPCK, INPCK, C("Enable (disable) input parity checking") },
+{ "istrip", BIT, I_FLAG, 0, ISTRIP, ISTRIP, C("Clear (do not clear) high bit of input characters") },
+{ "inlcr", BIT, I_FLAG, US, INLCR, INLCR, C("Translate (do not translate) carriage return to newline") },
+{ "igncr", BIT, I_FLAG, US, IGNCR, IGNCR, C("Ignore (do not ignore) carriage return") },
+#ifdef IUCLC
+{ "iuclc", BIT, I_FLAG, US, IUCLC, IUCLC, C("Map (do not map) upper-case to lower case") },
+#endif /* IUCLC */
+{ "ixon", BIT, I_FLAG, 0, IXON, IXON, C("Enable (disable) XON/XOFF flow control. \bstop\b character stops output") },
+#ifdef IXANY
+{ "ixany", BIT, I_FLAG, US, IXANY, IXANY, C("Any character (only start character) can restart output.") },
+{ "decctlq", BIT, I_FLAG, IG, IXANY, 0, C("Same as \b-ixany\b") },
+#endif /* IXANY */
+{ "ixoff", BIT, I_FLAG, US, IXOFF, IXOFF, C("Disable (enable) XON/XOFF flow control") },
+#ifdef IMAXBEL
+{ "imaxbel", BIT, I_FLAG, SS, IMAXBEL, IMAXBEL, C("Beep (do not beep) if a character arrives with full input buffer") },
+#endif /* IMAXBEL */
+{ "icrnl", BIT, I_FLAG, NL|SS, ICRNL, ICRNL, C("Translate (do not translate) carriage return to newline") },
+
+{ "isig", BIT, L_FLAG, SS, ISIG, ISIG, C("Enable (disable) \bintr\b, \bquit\b, and \bsusp\b special characters") },
+{ "icanon", BIT, L_FLAG, SS, ICANON, ICANON, C("Enable (disable) \berase\b, \bkill\b, \bwerase\b, and \brprnt\b special characters") },
+{ "icannon", BIT, L_FLAG, SS, ICANON, ICANON },
+#ifdef IEXTEN
+{ "iexten", BIT, L_FLAG, SS, IEXTEN, IEXTEN, C("Enable (disable) non-POSIX special characters") },
+#endif /* IEXTEN */
+{ "echo", BIT, L_FLAG, SS, ECHO|ECHONL, ECHO|ECHONL, C("Echo (do not echo) input characters") },
+{ "echoe", BIT, L_FLAG, SS, ECHOE, ECHOE, C("Echo (do not echo) erase characters as backspace-space-backspace") },
+{ "echok", BIT, L_FLAG, SS, ECHOK, ECHOK, C("Echo (do not echo) a newline after a kill character") },
+#ifdef ECHOKE
+{ "echoke", BIT, L_FLAG, SS, ECHOKE, ECHOKE, C("Echo (do not echo) a newline after a kill character") },
+#endif
+{ "lfkc", BIT, L_FLAG, IG, ECHOK, ECHOK, C("Same as \bechok\b (\b-echok\b); obsolete") },
+{ "echonl", BIT, L_FLAG, SS, ECHONL, ECHONL,"Echo (do not echo) newline even if not echoing other character" },
+#ifdef ECHOCTL
+{ "echoctl", BIT, L_FLAG, SS, ECHOCTL, ECHOCTL, C("Echo (do not echo) control characters as \b^\b\ac\a") },
+#else
+#define ECHOCTL 0
+#endif /* ECHOCTL */
+#ifdef ECHOPRT
+{ "echoprt", BIT, L_FLAG, US, ECHOPRT, ECHOPRT, C("Echo (do not echo) erased characters backward, between '\\' and '/'") },
+#else
+#define ECHOPRT 0
+#endif /* ECHOPRT */
+#ifdef XCASE
+{ "xcase", BIT, L_FLAG, US, XCASE, XCASE, C("Enable (disable) \bicanon\b uppercase as lowercase with '\\' prefix") },
+#endif /* XCASE */
+#ifdef DEFECHO
+{ "defecho", BIT, L_FLAG, 0, DEFECHO, DEFECHO },
+#endif /* DEFECHO */
+#ifdef FLUSHO
+{ "flusho", BIT, L_FLAG, 0, FLUSHO, FLUSHO, C("Discard (do not discard) written data. Cleared by subsequent input") },
+#endif /* FLUSHO */
+#ifdef PENDIN
+{ "pendin", BIT, L_FLAG, 0, PENDIN, PENDIN, C("Redisplay pending input at next read and then automatically clear \bpendin\b") },
+#endif /* PENDIN */
+{ "noflsh", BIT, L_FLAG, US, NOFLSH, NOFLSH, C("Disable (enable) flushing after \bintr\b and \bquit\b special characters") },
+#ifdef TOSTOP
+{ "tostop", BIT, L_FLAG, NL|US, TOSTOP, TOSTOP, C("Stop (do not stop) background jobs that try to write to the terminal") },
+#endif /* TOSTOP */
+#ifdef OLCUC
+{ "olcuc", BIT, O_FLAG, US, OLCUC, OLCUC, C("Translate (do not translate) lowercase characters to uppercase") },
+#endif /* OLCUC */
+#ifdef ONLCR
+{ "onlcr", BIT, O_FLAG, SS, ONLCR, ONLCR, C("Translate (do not translate) newline to carriage return-newline") },
+#endif /* ONLCR */
+#ifdef ONLRET
+{ "onlret", BIT, O_FLAG, US, ONLRET, ONLRET, C("Newline performs (does not perform) a carriage return") },
+#endif /* ONLRET */
+#ifdef OCRNL
+{ "ocrnl", BIT, O_FLAG, US, OCRNL, OCRNL, C("Translate (do not translate) carriage return to newline") },
+#endif /* OCRNL */
+#ifdef ONOCR
+{ "onocr", BIT, O_FLAG, US, ONOCR, ONOCR, C("Do not (do) print carriage returns in the first column") },
+#endif /* ONOCR */
+#ifdef OFILL
+{ "ofill", BIT, O_FLAG, US, OFILL, OFILL, C("Use fill characters (use timing) for delays") },
+#endif /* OFILL */
+#ifdef OFDEL
+{ "ofdel", BIT, O_FLAG, US, OFDEL, OFDEL, C("Use DEL (NUL) as fill characters for delays") },
+#endif /* OFDEL */
+{ "opost", BIT, O_FLAG, SS, OPOST, OPOST, C(" Postprocess (do not postprocess) output") },
+#ifdef CRDLY
+{ "cr0", BITS, O_FLAG, IG|SS, CRDLY, CR0 },
+{ "cr1", BITS, O_FLAG, US, CRDLY, CR1 },
+{ "cr2", BITS, O_FLAG, US, CRDLY, CR2 },
+{ "cr3", BITS, O_FLAG, US, CRDLY, CR3 },
+#endif
+#ifdef NLDLY
+{ "nl0", BITS, O_FLAG, IG|US, NLDLY, NL0 },
+{ "nl1", BITS, O_FLAG, US, NLDLY, NL1 },
+#endif
+#ifdef TABDLY
+{ "tabs", TABS, O_FLAG, IG, TABDLY, TAB3, C("Preserve (expand to spaces) tabs") },
+#ifdef TAB0
+{ "tab0", BITS, O_FLAG, IG|SS, TABDLY, TAB0 },
+#endif
+#ifdef TAB1
+{ "tab1", BITS, O_FLAG, US, TABDLY, TAB1 },
+#endif
+#ifdef TAB2
+{ "tab2", BITS, O_FLAG, US, TABDLY, TAB2 },
+#endif
+{ "tab3", BITS, O_FLAG, US, TABDLY, TAB3 },
+#endif
+#ifdef BSDLY
+{ "bs0", BITS, O_FLAG, IG|SS, BSDLY, BS0 },
+{ "bs1", BITS, O_FLAG, US, BSDLY, BS1 },
+#endif
+#ifdef VTDLY
+{ "vt0", BITS, O_FLAG, IG|SS, VTDLY, VT0 },
+{ "vt1", BITS, O_FLAG, US, VTDLY, VT1 },
+#endif
+#ifdef FFDLY
+{ "ff0", BITS, O_FLAG, IG|SS, FFDLY, FF0 },
+{ "ff1", BITS, O_FLAG, US, FFDLY, FF1 },
+#endif
+{ "", MIXED, O_FLAG, NL|IG },
+
+{ "evenp", MIXED, C_FLAG, IG, PARENB, 0, C("Same as \bparenb -parodd cs7\b") },
+{ "oddp", MIXED, C_FLAG, IG, PARODD, 0, C("Same as \bparenb parodd cs7\b") },
+{ "parity", MIXED, C_FLAG, IG, 0, 0, C("Same as parenb \b-parodd cs7\b") },
+{ "ek", MIXED, C_FLAG, IG, 0, 0, C("Reset the \berase\b and \bkill\b special characters to their default values") },
+{ "sane", SANE, C_FLAG, IG, 0, 0, C("Reset all modes to some reasonable values") },
+{ "cooked", COOKED, C_FLAG, IG, 0, 0, C("Disable raw input and output") },
+{ "raw", COOKED, C_FLAG, IG, 0, 0, C("Enable raw input and output") },
+{ "lcase", CASE, C_FLAG, IG, 0 , 0, C("Set \bxcase\b, \biuclc\b, and \bolcuc\b") },
+{ "LCASE", CASE, C_FLAG, IG, 0 , 0, C("Same as \blcase\b") }
+};
+
+#if CC_NATIVE == CC_ASCII
+#define cntl(x) (((x)=='?')?0177:((x)&037))
+#else
+#define cntl(x) (((x)=='?')?ccmapc(0177,CC_ASCII,CC_NATIVE):ccmapc(ccmapc(x,CC_NATIVE,CC_ASCII)&037,CC_ASCII,CC_NATIVE))
+#endif
+
+static void sane(register struct termios *sp)
+{
+ register const Tty_t* tp;
+
+ for (tp = Ttable; tp < &Ttable[elementsof(Ttable)]; tp++)
+ if (tp->flags & (SS|US))
+ switch (tp->type)
+ {
+ case BIT:
+ case BITS:
+ switch (tp->field)
+ {
+ case C_FLAG:
+ if (tp->flags & SS)
+ sp->c_cflag |= tp->mask;
+ else
+ sp->c_cflag &= ~tp->mask;
+ break;
+ case I_FLAG:
+ if (tp->flags & SS)
+ sp->c_iflag |= tp->mask;
+ else
+ sp->c_iflag &= ~tp->mask;
+ break;
+ case O_FLAG:
+ if (tp->flags & SS)
+ sp->c_oflag |= tp->mask;
+ else
+ sp->c_oflag &= ~tp->mask;
+ break;
+ case L_FLAG:
+ if (tp->flags & SS)
+ sp->c_lflag |= tp->mask;
+ else
+ sp->c_lflag &= ~tp->mask;
+ break;
+ }
+ break;
+ case CHAR:
+ sp->c_cc[tp->mask] = cntl(tp->val);
+ break;
+ }
+}
+
+static int gin(char *arg,struct termios *sp)
+{
+ register int i;
+ if(*arg++ != ':')
+ return(0);
+ sp->c_iflag = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ sp->c_oflag = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ sp->c_cflag = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ sp->c_lflag = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ for(i=0;i< NCCS; i++)
+ {
+ sp->c_cc[i] = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ }
+#if _mem_c_line_termios
+ sp->c_line =
+#endif
+ strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ i = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ cfsetispeed(sp, i);
+ i = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ cfsetospeed(sp, i);
+ if(*arg)
+ return(0);
+ return(1);
+}
+
+static void gout(struct termios *sp)
+{
+ register int i;
+ sfprintf(sfstdout,":%x",sp->c_iflag);
+ sfprintf(sfstdout,":%x",sp->c_oflag);
+ sfprintf(sfstdout,":%x",sp->c_cflag);
+ sfprintf(sfstdout,":%x",sp->c_lflag);
+ for(i=0;i< NCCS; i++)
+ sfprintf(sfstdout,":%x",sp->c_cc[i]);
+#if _mem_c_line_termios
+ sfprintf(sfstdout,":%x", sp->c_line);
+#else
+ sfprintf(sfstdout,":%x", 0);
+#endif
+ sfprintf(sfstdout,":%x",cfgetispeed(sp));
+ sfprintf(sfstdout,":%x",cfgetospeed(sp));
+ sfprintf(sfstdout,":\n");
+}
+
+static void output(struct termios *sp, int flags)
+{
+ const Tty_t *tp;
+ struct termios tty;
+ register int delim = ' ';
+ register int i,off,off2;
+ char schar[2];
+ unsigned int ispeed = cfgetispeed(sp);
+ unsigned int ospeed = cfgetospeed(sp);
+ if(flags&G_FLAG)
+ {
+ gout(sp);
+ return;
+ }
+ tty = *sp;
+ sane(&tty);
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ tp= &Ttable[i];
+ if(tp->flags&IG)
+ {
+ if(tp->flags&NL)
+ sfputc(sfstdout,'\n');
+ continue;
+ }
+ switch(tp->type)
+ {
+ case BIT:
+ case BITS:
+ off = off2 = 1;
+ switch(tp->field)
+ {
+ case C_FLAG:
+ if(sp->c_cflag&tp->mask)
+ off = 0;
+ if(tty.c_cflag&tp->mask)
+ off2 = 0;
+ break;
+ case I_FLAG:
+ if(sp->c_iflag&tp->mask)
+ off = 0;
+ if(tty.c_iflag&tp->mask)
+ off2 = 0;
+ break;
+ case O_FLAG:
+ if((sp->c_oflag&tp->mask)==tp->val)
+ off = 0;
+ if(tty.c_oflag&tp->mask)
+ off2 = 0;
+ break;
+ case L_FLAG:
+ if(sp->c_lflag&tp->mask)
+ off = 0;
+ if(tty.c_lflag&tp->mask)
+ off2 = 0;
+ }
+ if(tp->flags&NL)
+ delim = '\n';
+ if(!flags && off==off2)
+ continue;
+ if(!off)
+ sfprintf(sfstdout,"%s%c",tp->name,delim);
+ else if(tp->type==BIT)
+ sfprintf(sfstdout,"-%s%c",tp->name,delim);
+ delim = ' ';
+ break;
+
+ case CHAR:
+ off = sp->c_cc[tp->mask];
+ if(tp->flags&NL)
+ delim = '\n';
+ if(!flags && off==(unsigned char)tty.c_cc[tp->mask])
+ continue;
+ if(off==_POSIX_VDISABLE)
+ sfprintf(sfstdout,"%s = <undef>;%c",tp->name,delim);
+ else if(isprint(off&0xff))
+ sfprintf(sfstdout,"%s = %c;%c",tp->name,off,delim);
+ else
+#if CC_NATIVE == CC_ASCII
+ sfprintf(sfstdout,"%s = ^%c;%c",tp->name,off==0177?'?':(off^0100),delim);
+#else
+ {
+ off = ccmapc(off, CC_NATIVE, CC_ASCII);
+ sfprintf(sfstdout,"%s = ^%c;%c",tp->name,off==0177?'?':ccmapc(off^0100,CC_ASCII,CC_NATIVE),delim);
+ }
+#endif
+ delim = ' ';
+ break;
+ case SIZE:
+ if((sp->c_cflag&CSIZE)!=tp->mask)
+ continue;
+ if(flags || (sp->c_cflag&CSIZE) != (tty.c_cflag&CSIZE))
+ sfprintf(sfstdout,"%s ",tp->name);
+ break;
+ case SPEED:
+ if(tp->mask==ispeed)
+ {
+ if(ispeed!=ospeed)
+ schar[0]='i';
+ else
+ schar[0]=0;
+ }
+ else if(tp->mask==ospeed)
+ schar[0]='o';
+ else
+ continue;
+ schar[1] = 0;
+#ifdef TIOCSWINSZ
+ {
+ struct winsize win;
+ off = ioctl(0,TIOCGWINSZ,&win);
+ if(off>=0)
+ sfprintf(sfstdout,"%sspeed %s baud; rows %d; columns %d;\n",schar,tp->name,win.ws_row,win.ws_col);
+ }
+ if(off<0)
+#endif
+ sfprintf(sfstdout,"%sspeed %s baud;\n",schar,tp->name);
+ }
+ }
+ if(delim=='\n')
+ sfputc(sfstdout,'\n');
+}
+
+static const Tty_t *lookup(const char *name)
+{
+ register int i;
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(strcmp(Ttable[i].name,name)==0)
+ return(&Ttable[i]);
+ }
+ return(0);
+
+}
+
+static const Tty_t *getspeed(unsigned long val)
+{
+ register int i;
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].type==SPEED && Ttable[i].mask==val)
+ return(&Ttable[i]);
+ }
+ return(0);
+}
+
+static int gettchar(register const char *cp)
+{
+ if(*cp==0)
+ return(-1);
+ if(cp[1]==0)
+ return((unsigned)cp[0]);
+ if(*cp=='^' && cp[1] && cp[2]==0)
+ {
+ switch(cp[1])
+ {
+ case '-':
+ return(-1);
+ default:
+ return(cntl(cp[1]));
+ }
+ }
+ if(streq(cp,"undef") || streq(cp,"<undef>"))
+ return(-1);
+ return(*((unsigned char*)cp));
+}
+
+static void set(char *argv[], struct termios *sp)
+{
+ const Tty_t *tp;
+ register int c,off;
+ char *cp;
+ char *ep;
+ while(cp = *argv++)
+ {
+ off = 0;
+ if(*cp=='-')
+ {
+ cp++;
+ off=1;
+ }
+ if(!(tp=lookup(cp)) || (off && (tp->type!=BIT) && (tp->type!=TABS)))
+ error(ERROR_exit(1),"%s: unknown mode",cp);
+ switch(tp->type)
+ {
+ case CHAR:
+ if(off)
+ error(ERROR_exit(1),"%s: unknown mode",cp);
+ if(!*argv)
+ error(ERROR_exit(1),"missing argument to %s",cp);
+ c = gettchar(*argv++);
+ if(c>=0)
+ sp->c_cc[tp->mask] = c;
+ else
+ sp->c_cc[tp->mask] = _POSIX_VDISABLE;
+ break;
+ case BIT: case BITS:
+ switch(tp->field)
+ {
+ case C_FLAG:
+ if(off)
+ sp->c_cflag &= ~tp->mask;
+ else
+ sp->c_cflag |= tp->mask;
+ break;
+ case I_FLAG:
+ if(off)
+ sp->c_iflag &= ~tp->mask;
+ else
+ sp->c_iflag |= tp->mask;
+ break;
+ case O_FLAG:
+ sp->c_oflag &= ~tp->mask;
+ sp->c_oflag |= tp->val;
+ break;
+ case L_FLAG:
+ if(off)
+ sp->c_lflag &= ~tp->mask;
+ else
+ sp->c_lflag |= tp->mask;
+ break;
+ }
+ break;
+ case TABS:
+ sp->c_oflag &= ~tp->mask;
+ if(off)
+ sp->c_oflag |= tp->val;
+ break;
+#ifdef TIOCSWINSZ
+ case WIND:
+ {
+ struct winsize win;
+ int n;
+ if(ioctl(0,TIOCGWINSZ,&win)<0)
+ error(ERROR_system(1),"cannot set %s",tp->name);
+ if(!(cp= *argv))
+ {
+ sfprintf(sfstdout,"%d\n",tp->mask?win.ws_col:win.ws_row);
+ break;
+ }
+ argv++;
+ n=strtol(cp,&cp,10);
+ if(*cp)
+ error(ERROR_system(1),"%d: invalid number of %s",argv[-1],tp->name);
+ if(tp->mask)
+ win.ws_col = n;
+ else
+ win.ws_row = n;
+ if(ioctl(0,TIOCSWINSZ,&win)<0)
+ error(ERROR_system(1),"cannot set %s",tp->name);
+ break;
+ }
+#endif
+ case NUM:
+ cp = *argv;
+ if (!cp)
+ {
+ if (tp->field == C_SPEED)
+ {
+ if (tp = getspeed(*tp->name == 'i' ? cfgetispeed(sp) : cfgetospeed(sp)))
+ sfprintf(sfstdout, "%s\n", tp->name);
+ break;
+ }
+ error(ERROR_exit(1), "%s: missing numeric argument", tp->name);
+ }
+ argv++;
+ c = (int)strtol(cp, &ep, 10);
+ if (*ep)
+ error(ERROR_exit(1), "%s: %s: numeric argument expected", tp->name, cp);
+ switch (tp->field)
+ {
+#if _mem_c_line_termios
+ case C_LINE:
+ sp->c_line = c;
+ break;
+#endif
+ case C_SPEED:
+ if(getspeed(c))
+ {
+ if (*tp->name != 'o')
+ cfsetispeed(sp, c);
+ if (*tp->name != 'i')
+ cfsetospeed(sp, c);
+ }
+ else
+ error(ERROR_exit(1), "%s: %s: invalid speed", tp->name, cp);
+ break;
+ case T_CHAR:
+ sp->c_cc[tp->mask] = c;
+ break;
+ }
+ break;
+ case SPEED:
+ cfsetospeed(sp, tp->mask);
+ cfsetispeed(sp, tp->mask);
+ break;
+ case SIZE:
+ sp->c_cflag &= ~CSIZE;
+ sp->c_cflag |= tp->mask;
+ break;
+ case SANE:
+ sane(sp);
+ break;
+#if defined(OLCUC) && defined(IUCLC)
+ case CASE:
+ if(off)
+ {
+ sp->c_iflag |= IUCLC;
+ sp->c_oflag |= OLCUC;
+ }
+ else
+ {
+ sp->c_iflag &= ~IUCLC;
+ sp->c_oflag &= ~OLCUC;
+ }
+ break;
+#endif /* OLCUC && IUCLC */
+ }
+ }
+}
+
+
+static void listchars(Sfio_t *sp,int type)
+{
+ int i,c;
+ c = (type==CHAR?'c':'n');
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].type==type && *Ttable[i].description)
+ sfprintf(sp,"[+%s \a%c\a?%s.]",Ttable[i].name,c,Ttable[i].description);
+ }
+}
+
+static void listgroup(Sfio_t *sp,int type, const char *description)
+{
+ int i;
+ sfprintf(sp,"[+");
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].type==type)
+ sfprintf(sp,"%s ",Ttable[i].name);
+ }
+ sfprintf(sp,"?%s.]",description);
+}
+
+static void listmask(Sfio_t *sp,unsigned int mask,const char *description)
+{
+ int i;
+ sfprintf(sp,"[+");
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].mask==mask && Ttable[i].type==BITS)
+ sfprintf(sp,"%s ",Ttable[i].name);
+ }
+ sfprintf(sp,"?%s.]",description);
+}
+
+static void listfields(Sfio_t *sp,int field)
+{
+ int i;
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].field==field && Ttable[i].type==BIT && *Ttable[i].description)
+ sfprintf(sp,"[+%s (-%s)?%s.]",Ttable[i].name,Ttable[i].name,Ttable[i].description);
+ }
+}
+
+static void listmode(Sfio_t *sp,const char *name)
+{
+ sfprintf(sp,"[+%s?%s.]",name,lookup(name)->description);
+}
+
+static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
+{
+ NoP(op);
+ NoP(s);
+ NoP(dp);
+ sfprintf(sp,"[+Control Modes.]{");
+ listfields(sp,C_FLAG);
+ listgroup(sp,SPEED,"Attempt to set input and output baud rate to number given. A value of \b0\b causes immediate hangup");
+ listchars(sp,NUM);
+ listgroup(sp,SIZE,"Number of bits in a character");
+ sfprintf(sp,"}[+Input Modes.]{");
+ listfields(sp,I_FLAG);
+ sfprintf(sp,"}[+Output Modes.]{");
+ listfields(sp,O_FLAG);
+#ifdef CRDLY
+ listmask(sp,CRDLY,"Carriage return delay style");
+#endif
+#ifdef NLDLY
+ listmask(sp,NLDLY,"Newline delay style");
+#endif
+#ifdef TABDLY
+ listmask(sp,TABDLY,"Horizontal tab delay style");
+#endif
+#ifdef BSDLY
+ listmask(sp,BSDLY,"Backspace delay style");
+#endif
+#ifdef FFDLY
+ listmask(sp,FFDLY,"Form feed delay style");
+#endif
+#ifdef VTDLY
+ listmask(sp,VTDLY,"Vertical tab delay style");
+#endif
+ sfprintf(sp,"}[+Local Modes.]{");
+ listfields(sp,L_FLAG);
+ sfprintf(sp,"}[+Control Assignments.?If \ac\a is \bundef\b or an empty "
+ "string then the control assignment is disabled.]{");
+ listchars(sp,WIND);
+ listchars(sp,CHAR);
+ sfprintf(sp,"}[+Combination Modes.]{");
+ listmode(sp,"ek");
+ listmode(sp,"evenp");
+ listmode(sp,"lcase");
+ listmode(sp,"oddp");
+ listmode(sp,"parity");
+ listmode(sp,"sane");
+ listmode(sp,"tabs");
+ listmode(sp,"LCASE");
+ sfputc(sp,'}');
+ return(1);
+}
+
+#ifndef _lib_tcgetpgrp
+# ifdef TIOCGPGRP
+ static int _i_;
+# define tcgetpgrp(a) (ioctl(a, TIOCGPGRP, &_i_)>=0?_i_:-1)
+# else
+# define tcgetpgrp(a) (-1)
+# endif /* TIOCGPGRP */
+#endif /* _lib_tcgetpgrp */
+
+int
+b_stty(int argc, char** argv, Shbltin_t* context)
+{
+ struct termios tty;
+ register int n;
+ register int flags = 0;
+ int fd = 0;
+ const Tty_t* tp;
+ Optdisc_t disc;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_INTERACTIVE);
+ memset(&disc, 0, sizeof(disc));
+ disc.version = OPT_VERSION;
+ disc.infof = infof;
+ opt_info.disc = &disc;
+ for (;;)
+ {
+ switch (n = optget(argv, usage))
+ {
+ case 'f':
+ fd = (int)opt_info.num;
+ continue;
+ case 'a':
+ case 'g':
+ case 't':
+ if (!opt_info.offset || !argv[opt_info.index][opt_info.offset])
+ {
+ switch (n)
+ {
+ case 'a':
+ flags |= A_FLAG;
+ break;
+ case 'g':
+ flags |= G_FLAG;
+ break;
+ case 't':
+ flags |= T_FLAG;
+ break;
+ }
+ continue;
+ }
+ /*FALLTHROUGH*/
+ case ':':
+ if (!opt_info.offset)
+ error(2, "%s", opt_info.arg);
+ else if (!(tp = lookup(argv[opt_info.index]+1)) || (tp->type != BIT && tp->type != TABS))
+ error(ERROR_exit(1), "%s: unknown mode", argv[opt_info.index]);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || (flags && *argv) || (flags&(flags-1)))
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (tcgetattr(fd, &tty) < 0)
+ error(ERROR_system(1), "not a tty");
+ if (flags & T_FLAG)
+ sfprintf(sfstdout, "%d\n", tcgetpgrp(0));
+ else if (*argv)
+ {
+ if (!argv[1] && **argv == ':')
+ gin(*argv, &tty);
+ else
+ set(argv, &tty);
+ if (tcsetattr(0, TCSANOW, &tty) < 0)
+ error(ERROR_system(1), "cannot set tty");
+ }
+ else
+ output(&tty, flags);
+ return error_info.errors;
+}