Changed email address
    cmdedit API change
    optimizations for traceroute and md5sum
    added a new shared create_icmp_socket() function
		
	
		
			
				
	
	
		
			1377 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1377 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* vi: set sw=4 ts=4: */
 | |
| /* stty -- change and print terminal line settings
 | |
|    Copyright (C) 1990-1999 Free Software Foundation, Inc.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; either version 2, or (at your option)
 | |
|    any later version.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software Foundation,
 | |
|    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 | |
| 
 | |
| /* Usage: stty [-ag] [-F device] [setting...]
 | |
| 
 | |
|    Options:
 | |
|    -a Write all current settings to stdout in human-readable form.
 | |
|    -g Write all current settings to stdout in stty-readable form.
 | |
|    -F Open and use the specified device instead of stdin
 | |
| 
 | |
|    If no args are given, write to stdout the baud rate and settings that
 | |
|    have been changed from their defaults.  Mode reading and changes
 | |
|    are done on the specified device, or stdin if none was specified.
 | |
| 
 | |
|    David MacKenzie <djm@gnu.ai.mit.edu>
 | |
| 
 | |
|    Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
 | |
| 
 | |
|    */
 | |
| 
 | |
| //#define TEST
 | |
| 
 | |
| #include <termios.h>
 | |
| #include <sys/ioctl.h>
 | |
| #include <getopt.h>
 | |
| 
 | |
| #include <sys/param.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #ifndef STDIN_FILENO
 | |
| # define STDIN_FILENO 0
 | |
| #endif
 | |
| 
 | |
| #ifndef STDOUT_FILENO
 | |
| # define STDOUT_FILENO 1
 | |
| #endif
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <assert.h>
 | |
| #include <ctype.h>
 | |
| #include <errno.h>
 | |
| #include <limits.h>
 | |
| #include <memory.h>
 | |
| #include <fcntl.h>
 | |
| #include "busybox.h"
 | |
| 
 | |
| #define STREQ(a, b) (strcmp ((a), (b)) == 0)
 | |
| 
 | |
| 
 | |
| #ifndef _POSIX_VDISABLE
 | |
| # define _POSIX_VDISABLE ((unsigned char) 0)
 | |
| #endif
 | |
| 
 | |
| #define Control(c) ((c) & 0x1f)
 | |
| /* Canonical values for control characters. */
 | |
| #ifndef CINTR
 | |
| # define CINTR Control ('c')
 | |
| #endif
 | |
| #ifndef CQUIT
 | |
| # define CQUIT 28
 | |
| #endif
 | |
| #ifndef CERASE
 | |
| # define CERASE 127
 | |
| #endif
 | |
| #ifndef CKILL
 | |
| # define CKILL Control ('u')
 | |
| #endif
 | |
| #ifndef CEOF
 | |
| # define CEOF Control ('d')
 | |
| #endif
 | |
| #ifndef CEOL
 | |
| # define CEOL _POSIX_VDISABLE
 | |
| #endif
 | |
| #ifndef CSTART
 | |
| # define CSTART Control ('q')
 | |
| #endif
 | |
| #ifndef CSTOP
 | |
| # define CSTOP Control ('s')
 | |
| #endif
 | |
| #ifndef CSUSP
 | |
| # define CSUSP Control ('z')
 | |
| #endif
 | |
| #if defined(VEOL2) && !defined(CEOL2)
 | |
| # define CEOL2 _POSIX_VDISABLE
 | |
| #endif
 | |
| /* ISC renamed swtch to susp for termios, but we'll accept either name.  */
 | |
| #if defined(VSUSP) && !defined(VSWTCH)
 | |
| # define VSWTCH VSUSP
 | |
| # define CSWTCH CSUSP
 | |
| #endif
 | |
| #if defined(VSWTCH) && !defined(CSWTCH)
 | |
| # define CSWTCH _POSIX_VDISABLE
 | |
| #endif
 | |
| 
 | |
| /* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
 | |
|    So the default is to disable `swtch.'  */
 | |
| #if defined (__sparc__) && defined (__svr4__)
 | |
| # undef CSWTCH
 | |
| # define CSWTCH _POSIX_VDISABLE
 | |
| #endif
 | |
| 
 | |
| #if defined(VWERSE) && !defined (VWERASE)       /* AIX-3.2.5 */
 | |
| # define VWERASE VWERSE
 | |
| #endif
 | |
| #if defined(VDSUSP) && !defined (CDSUSP)
 | |
| # define CDSUSP Control ('y')
 | |
| #endif
 | |
| #if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
 | |
| # define VREPRINT VRPRNT
 | |
| #endif
 | |
| #if defined(VREPRINT) && !defined(CRPRNT)
 | |
| # define CRPRNT Control ('r')
 | |
| #endif
 | |
| #if defined(VWERASE) && !defined(CWERASE)
 | |
| # define CWERASE Control ('w')
 | |
| #endif
 | |
| #if defined(VLNEXT) && !defined(CLNEXT)
 | |
| # define CLNEXT Control ('v')
 | |
| #endif
 | |
| #if defined(VDISCARD) && !defined(VFLUSHO)
 | |
| # define VFLUSHO VDISCARD
 | |
| #endif
 | |
| #if defined(VFLUSH) && !defined(VFLUSHO)        /* Ultrix 4.2 */
 | |
| # define VFLUSHO VFLUSH
 | |
| #endif
 | |
| #if defined(CTLECH) && !defined(ECHOCTL)        /* Ultrix 4.3 */
 | |
| # define ECHOCTL CTLECH
 | |
| #endif
 | |
| #if defined(TCTLECH) && !defined(ECHOCTL)       /* Ultrix 4.2 */
 | |
| # define ECHOCTL TCTLECH
 | |
| #endif
 | |
| #if defined(CRTKIL) && !defined(ECHOKE)         /* Ultrix 4.2 and 4.3 */
 | |
| # define ECHOKE CRTKIL
 | |
| #endif
 | |
| #if defined(VFLUSHO) && !defined(CFLUSHO)
 | |
| # define CFLUSHO Control ('o')
 | |
| #endif
 | |
| #if defined(VSTATUS) && !defined(CSTATUS)
 | |
| # define CSTATUS Control ('t')
 | |
| #endif
 | |
| 
 | |
| /* Which speeds to set.  */
 | |
| enum speed_setting {
 | |
| 	input_speed, output_speed, both_speeds
 | |
| };
 | |
| 
 | |
| /* What to output and how.  */
 | |
| enum output_type {
 | |
| 	changed, all, recoverable       /* Default, -a, -g.  */
 | |
| };
 | |
| 
 | |
| /* Which member(s) of `struct termios' a mode uses.  */
 | |
| enum mode_type {
 | |
| 	control, input, output, local, combination
 | |
| };
 | |
| 
 | |
| 
 | |
| static const char evenp     [] = "evenp";
 | |
| static const char raw       [] = "raw";
 | |
| static const char stty_min  [] = "min";
 | |
| static const char stty_time [] = "time";
 | |
| static const char stty_swtch[] = "swtch";
 | |
| static const char stty_eol  [] = "eol";
 | |
| static const char stty_eof  [] = "eof";
 | |
| static const char parity    [] = "parity";
 | |
| static const char stty_oddp [] = "oddp";
 | |
| static const char stty_nl   [] = "nl";
 | |
| static const char stty_ek   [] = "ek";
 | |
| static const char stty_sane [] = "sane";
 | |
| static const char cbreak    [] = "cbreak";
 | |
| static const char stty_pass8[] = "pass8";
 | |
| static const char litout    [] = "litout";
 | |
| static const char cooked    [] = "cooked";
 | |
| static const char decctlq   [] = "decctlq";
 | |
| static const char stty_tabs [] = "tabs";
 | |
| static const char stty_lcase[] = "lcase";
 | |
| static const char stty_LCASE[] = "LCASE";
 | |
| static const char stty_crt  [] = "crt";
 | |
| static const char stty_dec  [] = "dec";
 | |
| 
 | |
| 
 | |
| /* Flags for `struct mode_info'. */
 | |
| #define SANE_SET 1              /* Set in `sane' mode.                  */
 | |
| #define SANE_UNSET 2            /* Unset in `sane' mode.                */
 | |
| #define REV 4                   /* Can be turned off by prepending `-'. */
 | |
| #define OMIT 8                  /* Don't display value.                 */
 | |
| 
 | |
| /* Each mode.  */
 | |
| struct mode_info {
 | |
| 	const char *name;       /* Name given on command line.           */
 | |
| 	enum mode_type type;    /* Which structure element to change.    */
 | |
| 	char flags;             /* Setting and display options.          */
 | |
| 	unsigned long bits;     /* Bits to set for this mode.            */
 | |
| 	unsigned long mask;     /* Other bits to turn off for this mode. */
 | |
| };
 | |
| 
 | |
| static const struct  mode_info mode_info[] = {
 | |
| 	{"parenb",   control,     REV,               PARENB,     0 },
 | |
| 	{"parodd",   control,     REV,               PARODD,     0 },
 | |
| 	{"cs5",      control,     0,                 CS5,     CSIZE},
 | |
| 	{"cs6",      control,     0,                 CS6,     CSIZE},
 | |
| 	{"cs7",      control,     0,                 CS7,     CSIZE},
 | |
| 	{"cs8",      control,     0,                 CS8,     CSIZE},
 | |
| 	{"hupcl",    control,     REV,               HUPCL,      0 },
 | |
| 	{"hup",      control,     REV        | OMIT, HUPCL,      0 },
 | |
| 	{"cstopb",   control,     REV,               CSTOPB,     0 },
 | |
| 	{"cread",    control,     SANE_SET   | REV,  CREAD,      0 },
 | |
| 	{"clocal",   control,     REV,               CLOCAL,     0 },
 | |
| #ifdef CRTSCTS
 | |
| 	{"crtscts",  control,     REV,               CRTSCTS,    0 },
 | |
| #endif
 | |
| 	{"ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 },
 | |
| 	{"brkint",   input,       SANE_SET   | REV,  BRKINT,     0 },
 | |
| 	{"ignpar",   input,       REV,               IGNPAR,     0 },
 | |
| 	{"parmrk",   input,       REV,               PARMRK,     0 },
 | |
| 	{"inpck",    input,       REV,               INPCK,      0 },
 | |
| 	{"istrip",   input,       REV,               ISTRIP,     0 },
 | |
| 	{"inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 },
 | |
| 	{"igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 },
 | |
| 	{"icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 },
 | |
| 	{"ixon",     input,       REV,               IXON,       0 },
 | |
| 	{"ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 },
 | |
| 	{"tandem",   input,       REV        | OMIT, IXOFF,      0 },
 | |
| #ifdef IUCLC
 | |
| 	{"iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 },
 | |
| #endif
 | |
| #ifdef IXANY
 | |
| 	{"ixany",    input,       SANE_UNSET | REV,  IXANY,      0 },
 | |
| #endif
 | |
| #ifdef IMAXBEL
 | |
| 	{"imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 },
 | |
| #endif
 | |
| 	{"opost",    output,      SANE_SET   | REV,  OPOST,      0 },
 | |
| #ifdef OLCUC
 | |
| 	{"olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 },
 | |
| #endif
 | |
| #ifdef OCRNL
 | |
| 	{"ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 },
 | |
| #endif
 | |
| #ifdef ONLCR
 | |
| 	{"onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 },
 | |
| #endif
 | |
| #ifdef ONOCR
 | |
| 	{"onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 },
 | |
| #endif
 | |
| #ifdef ONLRET
 | |
| 	{"onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 },
 | |
| #endif
 | |
| #ifdef OFILL
 | |
| 	{"ofill",    output,      SANE_UNSET | REV,  OFILL,      0 },
 | |
| #endif
 | |
| #ifdef OFDEL
 | |
| 	{"ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 },
 | |
| #endif
 | |
| #ifdef NLDLY
 | |
| 	{"nl1",      output,      SANE_UNSET,        NL1,     NLDLY},
 | |
| 	{"nl0",      output,      SANE_SET,          NL0,     NLDLY},
 | |
| #endif
 | |
| #ifdef CRDLY
 | |
| 	{"cr3",      output,      SANE_UNSET,        CR3,     CRDLY},
 | |
| 	{"cr2",      output,      SANE_UNSET,        CR2,     CRDLY},
 | |
| 	{"cr1",      output,      SANE_UNSET,        CR1,     CRDLY},
 | |
| 	{"cr0",      output,      SANE_SET,          CR0,     CRDLY},
 | |
| #endif
 | |
| 
 | |
| #ifdef TABDLY
 | |
| 	{"tab3",     output,      SANE_UNSET,        TAB3,   TABDLY},
 | |
| 	{"tab2",     output,      SANE_UNSET,        TAB2,   TABDLY},
 | |
| 	{"tab1",     output,      SANE_UNSET,        TAB1,   TABDLY},
 | |
| 	{"tab0",     output,      SANE_SET,          TAB0,   TABDLY},
 | |
| #else
 | |
| # ifdef OXTABS
 | |
| 	{"tab3",     output,      SANE_UNSET,        OXTABS,     0 },
 | |
| # endif
 | |
| #endif
 | |
| 
 | |
| #ifdef BSDLY
 | |
| 	{"bs1",      output,      SANE_UNSET,        BS1,     BSDLY},
 | |
| 	{"bs0",      output,      SANE_SET,          BS0,     BSDLY},
 | |
| #endif
 | |
| #ifdef VTDLY
 | |
| 	{"vt1",      output,      SANE_UNSET,        VT1,     VTDLY},
 | |
| 	{"vt0",      output,      SANE_SET,          VT0,     VTDLY},
 | |
| #endif
 | |
| #ifdef FFDLY
 | |
| 	{"ff1",      output,      SANE_UNSET,        FF1,     FFDLY},
 | |
| 	{"ff0",      output,      SANE_SET,          FF0,     FFDLY},
 | |
| #endif
 | |
| 	{"isig",     local,       SANE_SET   | REV,  ISIG,       0 },
 | |
| 	{"icanon",   local,       SANE_SET   | REV,  ICANON,     0 },
 | |
| #ifdef IEXTEN
 | |
| 	{"iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 },
 | |
| #endif
 | |
| 	{"echo",     local,       SANE_SET   | REV,  ECHO,       0 },
 | |
| 	{"echoe",    local,       SANE_SET   | REV,  ECHOE,      0 },
 | |
| 	{"crterase", local,       REV        | OMIT, ECHOE,      0 },
 | |
| 	{"echok",    local,       SANE_SET   | REV,  ECHOK,      0 },
 | |
| 	{"echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 },
 | |
| 	{"noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 },
 | |
| #ifdef XCASE
 | |
| 	{"xcase",    local,       SANE_UNSET | REV,  XCASE,      0 },
 | |
| #endif
 | |
| #ifdef TOSTOP
 | |
| 	{"tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 },
 | |
| #endif
 | |
| #ifdef ECHOPRT
 | |
| 	{"echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 },
 | |
| 	{"prterase", local,       REV | OMIT,        ECHOPRT,    0 },
 | |
| #endif
 | |
| #ifdef ECHOCTL
 | |
| 	{"echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 },
 | |
| 	{"ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 },
 | |
| #endif
 | |
| #ifdef ECHOKE
 | |
| 	{"echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 },
 | |
| 	{"crtkill",  local,       REV        | OMIT, ECHOKE,     0 },
 | |
| #endif
 | |
| 	{evenp,      combination, REV        | OMIT, 0,          0 },
 | |
| 	{parity,     combination, REV        | OMIT, 0,          0 },
 | |
| 	{stty_oddp,  combination, REV        | OMIT, 0,          0 },
 | |
| 	{stty_nl,    combination, REV        | OMIT, 0,          0 },
 | |
| 	{stty_ek,    combination, OMIT,              0,          0 },
 | |
| 	{stty_sane,  combination, OMIT,              0,          0 },
 | |
| 	{cooked,     combination, REV        | OMIT, 0,          0 },
 | |
| 	{raw,        combination, REV        | OMIT, 0,          0 },
 | |
| 	{stty_pass8, combination, REV        | OMIT, 0,          0 },
 | |
| 	{litout,     combination, REV        | OMIT, 0,          0 },
 | |
| 	{cbreak,     combination, REV        | OMIT, 0,          0 },
 | |
| #ifdef IXANY
 | |
| 	{decctlq,    combination, REV        | OMIT, 0,          0 },
 | |
| #endif
 | |
| #if defined (TABDLY) || defined (OXTABS)
 | |
| 	{stty_tabs,  combination, REV        | OMIT, 0,          0 },
 | |
| #endif
 | |
| #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
 | |
| 	{stty_lcase, combination, REV        | OMIT, 0,          0 },
 | |
| 	{stty_LCASE, combination, REV        | OMIT, 0,          0 },
 | |
| #endif
 | |
| 	{stty_crt,   combination, OMIT,              0,          0 },
 | |
| 	{stty_dec,   combination, OMIT,              0,          0 },
 | |
| };
 | |
| 
 | |
| static const int NUM_mode_info =
 | |
| 
 | |
| 	(sizeof(mode_info) / sizeof(struct mode_info));
 | |
| 
 | |
| /* Control character settings.  */
 | |
| struct control_info {
 | |
| 	const char *name;                       /* Name given on command line.  */
 | |
| 	unsigned char saneval;          /* Value to set for `stty sane'.  */
 | |
| 	int offset;                                     /* Offset in c_cc.  */
 | |
| };
 | |
| 
 | |
| /* Control characters. */
 | |
| 
 | |
| static const struct  control_info control_info[] = {
 | |
| 	{"intr",     CINTR,   VINTR},
 | |
| 	{"quit",     CQUIT,   VQUIT},
 | |
| 	{"erase",    CERASE,  VERASE},
 | |
| 	{"kill",     CKILL,   VKILL},
 | |
| 	{stty_eof,   CEOF,    VEOF},
 | |
| 	{stty_eol,   CEOL,    VEOL},
 | |
| #ifdef VEOL2
 | |
| 	{"eol2",     CEOL2,   VEOL2},
 | |
| #endif
 | |
| #ifdef VSWTCH
 | |
| 	{stty_swtch, CSWTCH,  VSWTCH},
 | |
| #endif
 | |
| 	{"start",    CSTART,  VSTART},
 | |
| 	{"stop",     CSTOP,   VSTOP},
 | |
| 	{"susp",     CSUSP,   VSUSP},
 | |
| #ifdef VDSUSP
 | |
| 	{"dsusp",    CDSUSP,  VDSUSP},
 | |
| #endif
 | |
| #ifdef VREPRINT
 | |
| 	{"rprnt",    CRPRNT,  VREPRINT},
 | |
| #endif
 | |
| #ifdef VWERASE
 | |
| 	{"werase",   CWERASE, VWERASE},
 | |
| #endif
 | |
| #ifdef VLNEXT
 | |
| 	{"lnext",    CLNEXT,  VLNEXT},
 | |
| #endif
 | |
| #ifdef VFLUSHO
 | |
| 	{"flush",    CFLUSHO, VFLUSHO},
 | |
| #endif
 | |
| #ifdef VSTATUS
 | |
| 	{"status",   CSTATUS, VSTATUS},
 | |
| #endif
 | |
| 	/* These must be last because of the display routines. */
 | |
| 	{stty_min,   1,       VMIN},
 | |
| 	{stty_time,  0,       VTIME},
 | |
| };
 | |
| 
 | |
| static const int NUM_control_info =
 | |
| 	(sizeof(control_info) / sizeof(struct control_info));
 | |
| 
 | |
| 
 | |
| static const char *  visible(unsigned int ch);
 | |
| static unsigned long baud_to_value(speed_t speed);
 | |
| static int           recover_mode(char *arg, struct termios *mode);
 | |
| static int           screen_columns(void);
 | |
| static int           set_mode(const struct mode_info *info,
 | |
| 					int reversed, struct termios *mode);
 | |
| static speed_t       string_to_baud(const char *arg);
 | |
| static tcflag_t*     mode_type_flag(enum mode_type type, struct termios *mode);
 | |
| static void          display_all(struct termios *mode, int fd,
 | |
| 					const char *device_name);
 | |
| static void          display_changed(struct termios *mode);
 | |
| static void          display_recoverable(struct termios *mode);
 | |
| static void          display_settings(enum output_type output_type,
 | |
| 					struct termios *mode, int fd,
 | |
| 					const char *device_name);
 | |
| static void          display_speed(struct termios *mode, int fancy);
 | |
| static void          display_window_size(int fancy, int fd,
 | |
| 					const char *device_name);
 | |
| static void          sane_mode(struct termios *mode);
 | |
| static void          set_control_char(const struct control_info *info,
 | |
| 					const char *arg, struct termios *mode);
 | |
| static void          set_speed(enum speed_setting type,
 | |
| 					const char *arg, struct termios *mode);
 | |
| static void          set_window_size(int rows, int cols, int fd,
 | |
| 					const char *device_name);
 | |
| 
 | |
| /* The width of the screen, for output wrapping. */
 | |
| static int max_col;
 | |
| 
 | |
| /* Current position, to know when to wrap. */
 | |
| static int current_col;
 | |
| 
 | |
| /* Print format string MESSAGE and optional args.
 | |
|    Wrap to next line first if it won't fit.
 | |
|    Print a space first unless MESSAGE will start a new line. */
 | |
| 
 | |
| static void wrapf(const char *message, ...)
 | |
| {
 | |
| 	va_list args;
 | |
| 	char buf[1024];                 /* Plenty long for our needs. */
 | |
| 	int buflen;
 | |
| 
 | |
| 	va_start(args, message);
 | |
| 	vsprintf(buf, message, args);
 | |
| 	va_end(args);
 | |
| 	buflen = strlen(buf);
 | |
| 	if (current_col + (current_col > 0) + buflen >= max_col) {
 | |
| 		putchar('\n');
 | |
| 		current_col = 0;
 | |
| 	}
 | |
| 	if (current_col > 0) {
 | |
| 		putchar(' ');
 | |
| 		current_col++;
 | |
| 	}
 | |
| 	fputs(buf, stdout);
 | |
| 	current_col += buflen;
 | |
| }
 | |
| 
 | |
| static const struct suffix_mult stty_suffixes[] = {
 | |
| 	{"b",  512 },
 | |
| 	{"k",  1024},
 | |
| 	{"B",  1024},
 | |
| 	{NULL, 0   }
 | |
| };
 | |
| 
 | |
| #ifndef TEST
 | |
| extern int stty_main(int argc, char **argv)
 | |
| #else
 | |
| extern int main(int argc, char **argv)
 | |
| #endif
 | |
| {
 | |
| 	struct termios mode;
 | |
| 	enum   output_type output_type;
 | |
| 	int    optc;
 | |
| 	int    require_set_attr;
 | |
| 	int    speed_was_set;
 | |
| 	int    verbose_output;
 | |
| 	int    recoverable_output;
 | |
| 	int    k;
 | |
| 	int    noargs = 1;
 | |
| 	char * file_name = NULL;
 | |
| 	int    fd;
 | |
| 	const char *device_name;
 | |
| 
 | |
| 	output_type = changed;
 | |
| 	verbose_output = 0;
 | |
| 	recoverable_output = 0;
 | |
| 
 | |
| 	/* Don't print error messages for unrecognized options.  */
 | |
| 	opterr = 0;
 | |
| 
 | |
| 	while ((optc = getopt(argc, argv, "agF:")) != -1) {
 | |
| 		switch (optc) {
 | |
| 		case 'a':
 | |
| 			verbose_output = 1;
 | |
| 			output_type = all;
 | |
| 			break;
 | |
| 
 | |
| 		case 'g':
 | |
| 			recoverable_output = 1;
 | |
| 			output_type = recoverable;
 | |
| 			break;
 | |
| 
 | |
| 		case 'F':
 | |
| 			if (file_name)
 | |
| 				error_msg_and_die("only one device may be specified");
 | |
| 			file_name = optarg;
 | |
| 			break;
 | |
| 
 | |
| 		default:                /* unrecognized option */
 | |
| 			noargs = 0;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (noargs == 0)
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	if (optind < argc)
 | |
| 		noargs = 0;
 | |
| 
 | |
| 	/* Specifying both -a and -g gets an error.  */
 | |
| 	if (verbose_output && recoverable_output)
 | |
| 		error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
 | |
| 
 | |
| 	/* Specifying any other arguments with -a or -g gets an error.  */
 | |
| 	if (!noargs && (verbose_output || recoverable_output))
 | |
| 		error_msg_and_die ("modes may not be set when specifying an output style");
 | |
| 
 | |
| 	/* FIXME: it'd be better not to open the file until we've verified
 | |
| 	   that all arguments are valid.  Otherwise, we could end up doing
 | |
| 	   only some of the requested operations and then failing, probably
 | |
| 	   leaving things in an undesirable state.  */
 | |
| 
 | |
| 	if (file_name) {
 | |
| 		int fdflags;
 | |
| 
 | |
| 		device_name = file_name;
 | |
| 		fd = open(device_name, O_RDONLY | O_NONBLOCK);
 | |
| 		if (fd < 0)
 | |
| 			perror_msg_and_die("%s", device_name);
 | |
| 		if ((fdflags = fcntl(fd, F_GETFL)) == -1
 | |
| 			|| fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
 | |
| 			perror_msg_and_die("%s: couldn't reset non-blocking mode",
 | |
| 							   device_name);
 | |
| 	} else {
 | |
| 		fd = 0;
 | |
| 		device_name = "standard input";
 | |
| 	}
 | |
| 
 | |
| 	/* Initialize to all zeroes so there is no risk memcmp will report a
 | |
| 	   spurious difference in an uninitialized portion of the structure.  */
 | |
| 	memset(&mode, 0, sizeof(mode));
 | |
| 	if (tcgetattr(fd, &mode))
 | |
| 		perror_msg_and_die("%s", device_name);
 | |
| 
 | |
| 	if (verbose_output || recoverable_output || noargs) {
 | |
| 		max_col = screen_columns();
 | |
| 		current_col = 0;
 | |
| 		display_settings(output_type, &mode, fd, device_name);
 | |
| 		return EXIT_SUCCESS;
 | |
| 	}
 | |
| 
 | |
| 	speed_was_set = 0;
 | |
| 	require_set_attr = 0;
 | |
| 	k = optind;
 | |
| 	while (k < argc) {
 | |
| 		int match_found = 0;
 | |
| 		int reversed = 0;
 | |
| 		int i;
 | |
| 
 | |
| 		if (argv[k][0] == '-') {
 | |
| 			++argv[k];
 | |
| 			reversed = 1;
 | |
| 		}
 | |
| 		for (i = 0; i < NUM_mode_info; ++i)
 | |
| 			if (STREQ(argv[k], mode_info[i].name)) {
 | |
| 				match_found = set_mode(&mode_info[i], reversed, &mode);
 | |
| 				require_set_attr = 1;
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 		if (match_found == 0 && reversed)
 | |
| 			error_msg_and_die("invalid argument `%s'", --argv[k]);
 | |
| 
 | |
| 		if (match_found == 0)
 | |
| 			for (i = 0; i < NUM_control_info; ++i)
 | |
| 				if (STREQ(argv[k], control_info[i].name)) {
 | |
| 					if (k == argc - 1)
 | |
| 					    error_msg_and_die("missing argument to `%s'", argv[k]);
 | |
| 					match_found = 1;
 | |
| 					++k;
 | |
| 					set_control_char(&control_info[i], argv[k], &mode);
 | |
| 					require_set_attr = 1;
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 		if (match_found == 0) {
 | |
| 			if (STREQ(argv[k], "ispeed")) {
 | |
| 				if (k == argc - 1)
 | |
| 				    error_msg_and_die("missing argument to `%s'", argv[k]);
 | |
| 				++k;
 | |
| 				set_speed(input_speed, argv[k], &mode);
 | |
| 				speed_was_set = 1;
 | |
| 				require_set_attr = 1;
 | |
| 			} else if (STREQ(argv[k], "ospeed")) {
 | |
| 				if (k == argc - 1)
 | |
| 				    error_msg_and_die("missing argument to `%s'", argv[k]);
 | |
| 				++k;
 | |
| 				set_speed(output_speed, argv[k], &mode);
 | |
| 				speed_was_set = 1;
 | |
| 				require_set_attr = 1;
 | |
| 			}
 | |
| #ifdef TIOCGWINSZ
 | |
| 			else if (STREQ(argv[k], "rows")) {
 | |
| 				if (k == argc - 1)
 | |
| 				    error_msg_and_die("missing argument to `%s'", argv[k]);
 | |
| 				++k;
 | |
| 				set_window_size((int) parse_number(argv[k], stty_suffixes),
 | |
| 								-1, fd, device_name);
 | |
| 			} else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
 | |
| 				if (k == argc - 1)
 | |
| 				    error_msg_and_die("missing argument to `%s'", argv[k]);
 | |
| 				++k;
 | |
| 				set_window_size(-1,
 | |
| 						(int) parse_number(argv[k], stty_suffixes),
 | |
| 						fd, device_name);
 | |
| 			} else if (STREQ(argv[k], "size")) {
 | |
| 				max_col = screen_columns();
 | |
| 				current_col = 0;
 | |
| 				display_window_size(0, fd, device_name);
 | |
| 			}
 | |
| #endif
 | |
| #ifdef HAVE_C_LINE
 | |
| 			else if (STREQ(argv[k], "line")) {
 | |
| 				if (k == argc - 1)
 | |
| 					error_msg_and_die("missing argument to `%s'", argv[k]);
 | |
| 				++k;
 | |
| 				mode.c_line = parse_number(argv[k], stty_suffixes);
 | |
| 				require_set_attr = 1;
 | |
| 			}
 | |
| #endif
 | |
| 			else if (STREQ(argv[k], "speed")) {
 | |
| 				max_col = screen_columns();
 | |
| 				display_speed(&mode, 0);
 | |
| 			} else if (recover_mode(argv[k], &mode) == 1)
 | |
| 				require_set_attr = 1;
 | |
| 			else if (string_to_baud(argv[k]) != (speed_t) - 1) {
 | |
| 				set_speed(both_speeds, argv[k], &mode);
 | |
| 				speed_was_set = 1;
 | |
| 				require_set_attr = 1;
 | |
| 			} else
 | |
| 				error_msg_and_die("invalid argument `%s'", argv[k]);
 | |
| 		}
 | |
| 		k++;
 | |
| 	}
 | |
| 
 | |
| 	if (require_set_attr) {
 | |
| 		struct termios new_mode;
 | |
| 
 | |
| 		if (tcsetattr(fd, TCSADRAIN, &mode))
 | |
| 			perror_msg_and_die("%s", device_name);
 | |
| 
 | |
| 		/* POSIX (according to Zlotnick's book) tcsetattr returns zero if
 | |
| 		   it performs *any* of the requested operations.  This means it
 | |
| 		   can report `success' when it has actually failed to perform
 | |
| 		   some proper subset of the requested operations.  To detect
 | |
| 		   this partial failure, get the current terminal attributes and
 | |
| 		   compare them to the requested ones.  */
 | |
| 
 | |
| 		/* Initialize to all zeroes so there is no risk memcmp will report a
 | |
| 		   spurious difference in an uninitialized portion of the structure.  */
 | |
| 		memset(&new_mode, 0, sizeof(new_mode));
 | |
| 		if (tcgetattr(fd, &new_mode))
 | |
| 			perror_msg_and_die("%s", device_name);
 | |
| 
 | |
| 		/* Normally, one shouldn't use memcmp to compare structures that
 | |
| 		   may have `holes' containing uninitialized data, but we have been
 | |
| 		   careful to initialize the storage of these two variables to all
 | |
| 		   zeroes.  One might think it more efficient simply to compare the
 | |
| 		   modified fields, but that would require enumerating those fields --
 | |
| 		   and not all systems have the same fields in this structure.  */
 | |
| 
 | |
| 		if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
 | |
| #ifdef CIBAUD
 | |
| 			/* SunOS 4.1.3 (at least) has the problem that after this sequence,
 | |
| 			   tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
 | |
| 			   sometimes (m1 != m2).  The only difference is in the four bits
 | |
| 			   of the c_cflag field corresponding to the baud rate.  To save
 | |
| 			   Sun users a little confusion, don't report an error if this
 | |
| 			   happens.  But suppress the error only if we haven't tried to
 | |
| 			   set the baud rate explicitly -- otherwise we'd never give an
 | |
| 			   error for a true failure to set the baud rate.  */
 | |
| 
 | |
| 			new_mode.c_cflag &= (~CIBAUD);
 | |
| 			if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
 | |
| #endif
 | |
| 				error_msg_and_die ("%s: unable to perform all requested operations",
 | |
| 					 device_name);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return EXIT_SUCCESS;
 | |
| }
 | |
| 
 | |
| /* Return 0 if not applied because not reversible; otherwise return 1.  */
 | |
| 
 | |
| static int
 | |
| set_mode(const struct mode_info *info, int reversed, struct termios *mode)
 | |
| {
 | |
| 	tcflag_t *bitsp;
 | |
| 
 | |
| 	if (reversed && (info->flags & REV) == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	bitsp = mode_type_flag(info->type, mode);
 | |
| 
 | |
| 	if (bitsp == NULL) {
 | |
| 		/* Combination mode. */
 | |
| 		if (info->name == evenp || info->name == parity) {
 | |
| 			if (reversed)
 | |
| 				mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
 | |
| 			else
 | |
| 				mode->c_cflag =
 | |
| 					(mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
 | |
| 		} else if (info->name == stty_oddp) {
 | |
| 			if (reversed)
 | |
| 				mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
 | |
| 			else
 | |
| 				mode->c_cflag =
 | |
| 					(mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
 | |
| 		} else if (info->name == stty_nl) {
 | |
| 			if (reversed) {
 | |
| 				mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
 | |
| 				mode->c_oflag = (mode->c_oflag
 | |
| #ifdef ONLCR
 | |
| 								 | ONLCR
 | |
| #endif
 | |
| 					)
 | |
| #ifdef OCRNL
 | |
| 					& ~OCRNL
 | |
| #endif
 | |
| #ifdef ONLRET
 | |
| 					& ~ONLRET
 | |
| #endif
 | |
| 					;
 | |
| 			} else {
 | |
| 				mode->c_iflag = mode->c_iflag & ~ICRNL;
 | |
| #ifdef ONLCR
 | |
| 				mode->c_oflag = mode->c_oflag & ~ONLCR;
 | |
| #endif
 | |
| 			}
 | |
| 		} else if (info->name == stty_ek) {
 | |
| 			mode->c_cc[VERASE] = CERASE;
 | |
| 			mode->c_cc[VKILL] = CKILL;
 | |
| 		} else if (info->name == stty_sane)
 | |
| 			sane_mode(mode);
 | |
| 		else if (info->name == cbreak) {
 | |
| 			if (reversed)
 | |
| 				mode->c_lflag |= ICANON;
 | |
| 			else
 | |
| 				mode->c_lflag &= ~ICANON;
 | |
| 		} else if (info->name == stty_pass8) {
 | |
| 			if (reversed) {
 | |
| 				mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
 | |
| 				mode->c_iflag |= ISTRIP;
 | |
| 			} else {
 | |
| 				mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
 | |
| 				mode->c_iflag &= ~ISTRIP;
 | |
| 			}
 | |
| 		} else if (info->name == litout) {
 | |
| 			if (reversed) {
 | |
| 				mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
 | |
| 				mode->c_iflag |= ISTRIP;
 | |
| 				mode->c_oflag |= OPOST;
 | |
| 			} else {
 | |
| 				mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
 | |
| 				mode->c_iflag &= ~ISTRIP;
 | |
| 				mode->c_oflag &= ~OPOST;
 | |
| 			}
 | |
| 		} else if (info->name == raw || info->name == cooked) {
 | |
| 			if ((info->name[0] == 'r' && reversed)
 | |
| 				|| (info->name[0] == 'c' && !reversed)) {
 | |
| 				/* Cooked mode. */
 | |
| 				mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
 | |
| 				mode->c_oflag |= OPOST;
 | |
| 				mode->c_lflag |= ISIG | ICANON;
 | |
| #if VMIN == VEOF
 | |
| 				mode->c_cc[VEOF] = CEOF;
 | |
| #endif
 | |
| #if VTIME == VEOL
 | |
| 				mode->c_cc[VEOL] = CEOL;
 | |
| #endif
 | |
| 			} else {
 | |
| 				/* Raw mode. */
 | |
| 				mode->c_iflag = 0;
 | |
| 				mode->c_oflag &= ~OPOST;
 | |
| 				mode->c_lflag &= ~(ISIG | ICANON
 | |
| #ifdef XCASE
 | |
| 								   | XCASE
 | |
| #endif
 | |
| 					);
 | |
| 				mode->c_cc[VMIN] = 1;
 | |
| 				mode->c_cc[VTIME] = 0;
 | |
| 			}
 | |
| 		}
 | |
| #ifdef IXANY
 | |
| 		else if (info->name == decctlq) {
 | |
| 			if (reversed)
 | |
| 				mode->c_iflag |= IXANY;
 | |
| 			else
 | |
| 				mode->c_iflag &= ~IXANY;
 | |
| 		}
 | |
| #endif
 | |
| #ifdef TABDLY
 | |
| 		else if (info->name == stty_tabs) {
 | |
| 			if (reversed)
 | |
| 				mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
 | |
| 			else
 | |
| 				mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
 | |
| 		}
 | |
| #else
 | |
| # ifdef OXTABS
 | |
| 		else if (info->name == stty_tabs) {
 | |
| 			if (reversed)
 | |
| 				mode->c_oflag = mode->c_oflag | OXTABS;
 | |
| 			else
 | |
| 				mode->c_oflag = mode->c_oflag & ~OXTABS;
 | |
| 		}
 | |
| # endif
 | |
| #endif
 | |
| #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
 | |
| 		else if (info->name == stty_lcase || info->name == stty_LCASE) {
 | |
| 			if (reversed) {
 | |
| 				mode->c_lflag &= ~XCASE;
 | |
| 				mode->c_iflag &= ~IUCLC;
 | |
| 				mode->c_oflag &= ~OLCUC;
 | |
| 			} else {
 | |
| 				mode->c_lflag |= XCASE;
 | |
| 				mode->c_iflag |= IUCLC;
 | |
| 				mode->c_oflag |= OLCUC;
 | |
| 			}
 | |
| 		}
 | |
| #endif
 | |
| 		else if (info->name == stty_crt)
 | |
| 			mode->c_lflag |= ECHOE
 | |
| #ifdef ECHOCTL
 | |
| 				| ECHOCTL
 | |
| #endif
 | |
| #ifdef ECHOKE
 | |
| 				| ECHOKE
 | |
| #endif
 | |
| 				;
 | |
| 		else if (info->name == stty_dec) {
 | |
| 			mode->c_cc[VINTR] = 3;  /* ^C */
 | |
| 			mode->c_cc[VERASE] = 127;       /* DEL */
 | |
| 			mode->c_cc[VKILL] = 21; /* ^U */
 | |
| 			mode->c_lflag |= ECHOE
 | |
| #ifdef ECHOCTL
 | |
| 				| ECHOCTL
 | |
| #endif
 | |
| #ifdef ECHOKE
 | |
| 				| ECHOKE
 | |
| #endif
 | |
| 				;
 | |
| #ifdef IXANY
 | |
| 			mode->c_iflag &= ~IXANY;
 | |
| #endif
 | |
| 		}
 | |
| 	} else if (reversed)
 | |
| 		*bitsp = *bitsp & ~info->mask & ~info->bits;
 | |
| 	else
 | |
| 		*bitsp = (*bitsp & ~info->mask) | info->bits;
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| set_control_char(const struct control_info *info, const char *arg,
 | |
| 				 struct termios *mode)
 | |
| {
 | |
| 	unsigned char value;
 | |
| 
 | |
| 	if (info->name == stty_min || info->name == stty_time)
 | |
| 		value = parse_number(arg, stty_suffixes);
 | |
| 	else if (arg[0] == '\0' || arg[1] == '\0')
 | |
| 		value = arg[0];
 | |
| 	else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
 | |
| 		value = _POSIX_VDISABLE;
 | |
| 	else if (arg[0] == '^' && arg[1] != '\0') {     /* Ignore any trailing junk. */
 | |
| 		if (arg[1] == '?')
 | |
| 			value = 127;
 | |
| 		else
 | |
| 			value = arg[1] & ~0140; /* Non-letters get weird results. */
 | |
| 	} else
 | |
| 		value = parse_number(arg, stty_suffixes);
 | |
| 	mode->c_cc[info->offset] = value;
 | |
| }
 | |
| 
 | |
| static void
 | |
| set_speed(enum speed_setting type, const char *arg, struct termios *mode)
 | |
| {
 | |
| 	speed_t baud;
 | |
| 
 | |
| 	baud = string_to_baud(arg);
 | |
| 	if (type == input_speed || type == both_speeds)
 | |
| 		cfsetispeed(mode, baud);
 | |
| 	if (type == output_speed || type == both_speeds)
 | |
| 		cfsetospeed(mode, baud);
 | |
| }
 | |
| 
 | |
| #ifdef TIOCGWINSZ
 | |
| 
 | |
| static int get_win_size(int fd, struct winsize *win)
 | |
| {
 | |
| 	int err = ioctl(fd, TIOCGWINSZ, (char *) win);
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static void
 | |
| set_window_size(int rows, int cols, int fd, const char *device_name)
 | |
| {
 | |
| 	struct winsize win;
 | |
| 
 | |
| 	if (get_win_size(fd, &win)) {
 | |
| 		if (errno != EINVAL)
 | |
| 			perror_msg_and_die("%s", device_name);
 | |
| 		memset(&win, 0, sizeof(win));
 | |
| 	}
 | |
| 
 | |
| 	if (rows >= 0)
 | |
| 		win.ws_row = rows;
 | |
| 	if (cols >= 0)
 | |
| 		win.ws_col = cols;
 | |
| 
 | |
| # ifdef TIOCSSIZE
 | |
| 	/* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
 | |
| 	   The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
 | |
| 	   This comment from sys/ttold.h describes Sun's twisted logic - a better
 | |
| 	   test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
 | |
| 	   At any rate, the problem is gone in Solaris 2.x. */
 | |
| 
 | |
| 	if (win.ws_row == 0 || win.ws_col == 0) {
 | |
| 		struct ttysize ttysz;
 | |
| 
 | |
| 		ttysz.ts_lines = win.ws_row;
 | |
| 		ttysz.ts_cols = win.ws_col;
 | |
| 
 | |
| 		win.ws_row = 1;
 | |
| 		win.ws_col = 1;
 | |
| 
 | |
| 		if (ioctl(fd, TIOCSWINSZ, (char *) &win))
 | |
| 			perror_msg_and_die("%s", device_name);
 | |
| 
 | |
| 		if (ioctl(fd, TIOCSSIZE, (char *) &ttysz))
 | |
| 			perror_msg_and_die("%s", device_name);
 | |
| 		return;
 | |
| 	}
 | |
| # endif
 | |
| 
 | |
| 	if (ioctl(fd, TIOCSWINSZ, (char *) &win))
 | |
| 		perror_msg_and_die("%s", device_name);
 | |
| }
 | |
| 
 | |
| static void display_window_size(int fancy, int fd, const char *device_name)
 | |
| {
 | |
| 	struct winsize win;
 | |
| 
 | |
| 	if (get_win_size(fd, &win)) {
 | |
| 		if (errno != EINVAL)
 | |
| 			perror_msg_and_die("%s", device_name);
 | |
| 		if (!fancy)
 | |
| 			perror_msg_and_die("%s: no size information for this device",
 | |
| 							   device_name);
 | |
| 	} else {
 | |
| 		wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
 | |
| 			  win.ws_row, win.ws_col);
 | |
| 		if (!fancy)
 | |
| 			current_col = 0;
 | |
| 	}
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int screen_columns(void)
 | |
| {
 | |
| #ifdef TIOCGWINSZ
 | |
| 	struct winsize win;
 | |
| 
 | |
| 	/* With Solaris 2.[123], this ioctl fails and errno is set to
 | |
| 	   EINVAL for telnet (but not rlogin) sessions.
 | |
| 	   On ISC 3.0, it fails for the console and the serial port
 | |
| 	   (but it works for ptys).
 | |
| 	   It can also fail on any system when stdout isn't a tty.
 | |
| 	   In case of any failure, just use the default.  */
 | |
| 	if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
 | |
| 		return win.ws_col;
 | |
| #endif
 | |
| 
 | |
| 	if (getenv("COLUMNS"))
 | |
| 		return atoi(getenv("COLUMNS"));
 | |
| 	return 80;
 | |
| }
 | |
| 
 | |
| static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
 | |
| {
 | |
| 	switch (type) {
 | |
| 	case control:
 | |
| 		return &mode->c_cflag;
 | |
| 
 | |
| 	case input:
 | |
| 		return &mode->c_iflag;
 | |
| 
 | |
| 	case output:
 | |
| 		return &mode->c_oflag;
 | |
| 
 | |
| 	case local:
 | |
| 		return &mode->c_lflag;
 | |
| 
 | |
| 	default:                                        /* combination: */
 | |
| 		return NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void
 | |
| display_settings(enum output_type output_type, struct termios *mode,
 | |
| 				 int fd, const char *device_name)
 | |
| {
 | |
| 	switch (output_type) {
 | |
| 	case changed:
 | |
| 		display_changed(mode);
 | |
| 		break;
 | |
| 
 | |
| 	case all:
 | |
| 		display_all(mode, fd, device_name);
 | |
| 		break;
 | |
| 
 | |
| 	case recoverable:
 | |
| 		display_recoverable(mode);
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void display_changed(struct termios *mode)
 | |
| {
 | |
| 	int i;
 | |
| 	int empty_line;
 | |
| 	tcflag_t *bitsp;
 | |
| 	unsigned long mask;
 | |
| 	enum mode_type prev_type = control;
 | |
| 
 | |
| 	display_speed(mode, 1);
 | |
| #ifdef HAVE_C_LINE
 | |
| 	wrapf("line = %d;", mode->c_line);
 | |
| #endif
 | |
| 	putchar('\n');
 | |
| 	current_col = 0;
 | |
| 
 | |
| 	empty_line = 1;
 | |
| 	for (i = 0; control_info[i].name != stty_min; ++i) {
 | |
| 		if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
 | |
| 			continue;
 | |
| 		/* If swtch is the same as susp, don't print both.  */
 | |
| #if VSWTCH == VSUSP
 | |
| 		if (control_info[i].name == stty_swtch)
 | |
| 			continue;
 | |
| #endif
 | |
| 		/* If eof uses the same slot as min, only print whichever applies.  */
 | |
| #if VEOF == VMIN
 | |
| 		if ((mode->c_lflag & ICANON) == 0
 | |
| 			&& (control_info[i].name == stty_eof
 | |
| 				|| control_info[i].name == stty_eol)) continue;
 | |
| #endif
 | |
| 
 | |
| 		empty_line = 0;
 | |
| 		wrapf("%s = %s;", control_info[i].name,
 | |
| 			  visible(mode->c_cc[control_info[i].offset]));
 | |
| 	}
 | |
| 	if ((mode->c_lflag & ICANON) == 0) {
 | |
| 		wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
 | |
| 			  (int) mode->c_cc[VTIME]);
 | |
| 	} else if (empty_line == 0)
 | |
| 		putchar('\n');
 | |
| 	current_col = 0;
 | |
| 
 | |
| 	empty_line = 1;
 | |
| 	for (i = 0; i < NUM_mode_info; ++i) {
 | |
| 		if (mode_info[i].flags & OMIT)
 | |
| 			continue;
 | |
| 		if (mode_info[i].type != prev_type) {
 | |
| 			if (empty_line == 0) {
 | |
| 				putchar('\n');
 | |
| 				current_col = 0;
 | |
| 				empty_line = 1;
 | |
| 			}
 | |
| 			prev_type = mode_info[i].type;
 | |
| 		}
 | |
| 
 | |
| 		bitsp = mode_type_flag(mode_info[i].type, mode);
 | |
| 		mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
 | |
| 		if ((*bitsp & mask) == mode_info[i].bits) {
 | |
| 			if (mode_info[i].flags & SANE_UNSET) {
 | |
| 				wrapf("%s", mode_info[i].name);
 | |
| 				empty_line = 0;
 | |
| 			}
 | |
| 		}
 | |
| 			else if ((mode_info[i].flags & (SANE_SET | REV)) ==
 | |
| 					 (SANE_SET | REV)) {
 | |
| 			wrapf("-%s", mode_info[i].name);
 | |
| 			empty_line = 0;
 | |
| 		}
 | |
| 	}
 | |
| 	if (empty_line == 0)
 | |
| 		putchar('\n');
 | |
| 	current_col = 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| display_all(struct termios *mode, int fd, const char *device_name)
 | |
| {
 | |
| 	int i;
 | |
| 	tcflag_t *bitsp;
 | |
| 	unsigned long mask;
 | |
| 	enum mode_type prev_type = control;
 | |
| 
 | |
| 	display_speed(mode, 1);
 | |
| #ifdef TIOCGWINSZ
 | |
| 	display_window_size(1, fd, device_name);
 | |
| #endif
 | |
| #ifdef HAVE_C_LINE
 | |
| 	wrapf("line = %d;", mode->c_line);
 | |
| #endif
 | |
| 	putchar('\n');
 | |
| 	current_col = 0;
 | |
| 
 | |
| 	for (i = 0; control_info[i].name != stty_min; ++i) {
 | |
| 		/* If swtch is the same as susp, don't print both.  */
 | |
| #if VSWTCH == VSUSP
 | |
| 		if (control_info[i].name == stty_swtch)
 | |
| 			continue;
 | |
| #endif
 | |
| 		/* If eof uses the same slot as min, only print whichever applies.  */
 | |
| #if VEOF == VMIN
 | |
| 		if ((mode->c_lflag & ICANON) == 0
 | |
| 			&& (control_info[i].name == stty_eof
 | |
| 				|| control_info[i].name == stty_eol)) continue;
 | |
| #endif
 | |
| 		wrapf("%s = %s;", control_info[i].name,
 | |
| 			  visible(mode->c_cc[control_info[i].offset]));
 | |
| 	}
 | |
| #if VEOF == VMIN
 | |
| 	if ((mode->c_lflag & ICANON) == 0)
 | |
| #endif
 | |
| 		wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
 | |
| 	if (current_col != 0)
 | |
| 		putchar('\n');
 | |
| 	current_col = 0;
 | |
| 
 | |
| 	for (i = 0; i < NUM_mode_info; ++i) {
 | |
| 		if (mode_info[i].flags & OMIT)
 | |
| 			continue;
 | |
| 		if (mode_info[i].type != prev_type) {
 | |
| 			putchar('\n');
 | |
| 			current_col = 0;
 | |
| 			prev_type = mode_info[i].type;
 | |
| 		}
 | |
| 
 | |
| 		bitsp = mode_type_flag(mode_info[i].type, mode);
 | |
| 		mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
 | |
| 		if ((*bitsp & mask) == mode_info[i].bits)
 | |
| 			wrapf("%s", mode_info[i].name);
 | |
| 		else if (mode_info[i].flags & REV)
 | |
| 			wrapf("-%s", mode_info[i].name);
 | |
| 	}
 | |
| 	putchar('\n');
 | |
| 	current_col = 0;
 | |
| }
 | |
| 
 | |
| static void display_speed(struct termios *mode, int fancy)
 | |
| {
 | |
| 	if (cfgetispeed(mode) == 0 || cfgetispeed(mode) == cfgetospeed(mode))
 | |
| 		wrapf(fancy ? "speed %lu baud;" : "%lu\n",
 | |
| 			  baud_to_value(cfgetospeed(mode)));
 | |
| 	else
 | |
| 		wrapf(fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n",
 | |
| 			  baud_to_value(cfgetispeed(mode)),
 | |
| 			  baud_to_value(cfgetospeed(mode)));
 | |
| 	if (!fancy)
 | |
| 		current_col = 0;
 | |
| }
 | |
| 
 | |
| static void display_recoverable(struct termios *mode)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	printf("%lx:%lx:%lx:%lx",
 | |
| 		   (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
 | |
| 		   (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
 | |
| 	for (i = 0; i < NCCS; ++i)
 | |
| 		printf(":%x", (unsigned int) mode->c_cc[i]);
 | |
| 	putchar('\n');
 | |
| }
 | |
| 
 | |
| static int recover_mode(char *arg, struct termios *mode)
 | |
| {
 | |
| 	int i, n;
 | |
| 	unsigned int chr;
 | |
| 	unsigned long iflag, oflag, cflag, lflag;
 | |
| 
 | |
| 	/* Scan into temporaries since it is too much trouble to figure out
 | |
| 	   the right format for `tcflag_t'.  */
 | |
| 	if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
 | |
| 			   &iflag, &oflag, &cflag, &lflag, &n) != 4)
 | |
| 		return 0;
 | |
| 	mode->c_iflag = iflag;
 | |
| 	mode->c_oflag = oflag;
 | |
| 	mode->c_cflag = cflag;
 | |
| 	mode->c_lflag = lflag;
 | |
| 	arg += n;
 | |
| 	for (i = 0; i < NCCS; ++i) {
 | |
| 		if (sscanf(arg, ":%x%n", &chr, &n) != 1)
 | |
| 			return 0;
 | |
| 		mode->c_cc[i] = chr;
 | |
| 		arg += n;
 | |
| 	}
 | |
| 
 | |
| 	/* Fail if there are too many fields.  */
 | |
| 	if (*arg != '\0')
 | |
| 		return 0;
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| struct speed_map {
 | |
| 	speed_t speed;                          /* Internal form. */
 | |
| 	unsigned long value;            /* Numeric value. */
 | |
| };
 | |
| 
 | |
| static const struct speed_map speeds[] = {
 | |
| 	{B0, 0},
 | |
| 	{B50, 50},
 | |
| 	{B75, 75},
 | |
| 	{B110, 110},
 | |
| 	{B134, 134},
 | |
| 	{B150, 150},
 | |
| 	{B200, 200},
 | |
| 	{B300, 300},
 | |
| 	{B600, 600},
 | |
| 	{B1200, 1200},
 | |
| 	{B1800, 1800},
 | |
| 	{B2400, 2400},
 | |
| 	{B4800, 4800},
 | |
| 	{B9600, 9600},
 | |
| 	{B19200, 19200},
 | |
| 	{B38400, 38400},
 | |
| #ifdef B57600
 | |
| 	{B57600, 57600},
 | |
| #endif
 | |
| #ifdef B115200
 | |
| 	{B115200, 115200},
 | |
| #endif
 | |
| #ifdef B230400
 | |
| 	{B230400, 230400},
 | |
| #endif
 | |
| #ifdef B460800
 | |
| 	{B460800, 460800},
 | |
| #endif
 | |
| };
 | |
| 
 | |
| static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map));
 | |
| 
 | |
| static speed_t string_to_baud(const char *arg)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < NUM_SPEEDS; ++i)
 | |
| 		if (parse_number(arg, 0) == speeds[i].value)
 | |
| 			return speeds[i].speed;
 | |
| 	return (speed_t) - 1;
 | |
| }
 | |
| 
 | |
| static unsigned long baud_to_value(speed_t speed)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < NUM_SPEEDS; ++i)
 | |
| 		if (speed == speeds[i].speed)
 | |
| 			return speeds[i].value;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void sane_mode(struct termios *mode)
 | |
| {
 | |
| 	int i;
 | |
| 	tcflag_t *bitsp;
 | |
| 
 | |
| 	for (i = 0; i < NUM_control_info; ++i) {
 | |
| #if VMIN == VEOF
 | |
| 		if (control_info[i].name == stty_min)
 | |
| 			break;
 | |
| #endif
 | |
| 		mode->c_cc[control_info[i].offset] = control_info[i].saneval;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < NUM_mode_info; ++i) {
 | |
| 		if (mode_info[i].flags & SANE_SET) {
 | |
| 			bitsp = mode_type_flag(mode_info[i].type, mode);
 | |
| 			*bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
 | |
| 		} else if (mode_info[i].flags & SANE_UNSET) {
 | |
| 			bitsp = mode_type_flag(mode_info[i].type, mode);
 | |
| 			*bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Return a string that is the printable representation of character CH.  */
 | |
| /* Adapted from `cat' by Torbjorn Granlund.  */
 | |
| 
 | |
| static const char *visible(unsigned int ch)
 | |
| {
 | |
| 	static char buf[10];
 | |
| 	char *bpout = buf;
 | |
| 
 | |
| 	if (ch == _POSIX_VDISABLE)
 | |
| 		return "<undef>";
 | |
| 
 | |
| 	if (ch >= 32) {
 | |
| 		if (ch < 127)
 | |
| 			*bpout++ = ch;
 | |
| 		else if (ch == 127) {
 | |
| 			*bpout++ = '^';
 | |
| 			*bpout++ = '?';
 | |
| 		} else {
 | |
| 			*bpout++ = 'M', *bpout++ = '-';
 | |
| 			if (ch >= 128 + 32) {
 | |
| 				if (ch < 128 + 127)
 | |
| 					*bpout++ = ch - 128;
 | |
| 				else {
 | |
| 					*bpout++ = '^';
 | |
| 					*bpout++ = '?';
 | |
| 				}
 | |
| 			} else {
 | |
| 				*bpout++ = '^';
 | |
| 				*bpout++ = ch - 128 + 64;
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		*bpout++ = '^';
 | |
| 		*bpout++ = ch + 64;
 | |
| 	}
 | |
| 	*bpout = '\0';
 | |
| 	return (const char *) buf;
 | |
| }
 | |
| 
 | |
| #ifdef TEST
 | |
| 
 | |
| const char *applet_name = "stty";
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /*
 | |
| Local Variables:
 | |
| c-file-style: "linux"
 | |
| c-basic-offset: 4
 | |
| tab-width: 4
 | |
| End:
 | |
| */
 |