int main(void) { unsigned long v; speed_t s; for (v = 0 ; v < 1000000; v++) { s = tty_value_to_baud(v); if (s == (speed_t) -1) { continue; } printf("v = %lu -- s = %0lo\n", v, (unsigned long) s); } printf("-------------------------------\n"); for (s = 0 ; s < 010017+1; s++) { v = tty_baud_to_value(s); if (!v) { continue; } printf("v = %lu -- s = %0lo\n", v, (unsigned long) s); } return 0; }
static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode) { static const uint8_t tcflag_offsets[] ALIGN1 = { offsetof(struct termios, c_cflag), /* control */ offsetof(struct termios, c_iflag), /* input */ offsetof(struct termios, c_oflag), /* output */ offsetof(struct termios, c_lflag) /* local */ }; if (type <= local) { return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]); } return NULL; } static void set_speed_or_die(enum speed_setting type, const char *arg, struct termios *mode) { speed_t baud; baud = tty_value_to_baud(xatou(arg)); if (type != output_speed) { /* either input or both */ cfsetispeed(mode, baud); } if (type != input_speed) { /* either output or both */ cfsetospeed(mode, baud); } }
static void set_speed_or_die(enum speed_setting type, const char * const arg, struct termios * const mode) { speed_t baud; baud = tty_value_to_baud(xatou(arg)); if (type != output_speed) { /* either input or both */ cfsetispeed(mode, baud); } if (type != input_speed) { /* either output or both */ cfsetospeed(mode, baud); } }
static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode) { static const uint8_t tcflag_offsets[] ALIGN1 = { offsetof(struct termios, c_cflag), /* control */ offsetof(struct termios, c_iflag), /* input */ offsetof(struct termios, c_oflag), /* output */ offsetof(struct termios, c_lflag) /* local */ }; if (type <= local) { return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]); } return NULL; } /* 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. * This structure should be kept as small as humanly possible. */ struct mode_info { const uint8_t type; /* Which structure element to change */ const uint8_t flags; /* Setting and display options */ /* only these values are ever used, so... */ #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100 const uint8_t mask; #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000 const uint16_t mask; #else const tcflag_t mask; /* Other bits to turn off for this mode */ #endif /* was using short here, but ppc32 was unhappy */ const tcflag_t bits; /* Bits to set for this mode */ }; enum { /* Must match mode_name[] and mode_info[] order! */ IDX_evenp = 0, IDX_parity, IDX_oddp, IDX_nl, IDX_ek, IDX_sane, IDX_cooked, IDX_raw, IDX_pass8, IDX_litout, IDX_cbreak, IDX_crt, IDX_dec, #if IXANY IDX_decctlq, #endif #if TABDLY || OXTABS IDX_tabs, #endif #if XCASE && IUCLC && OLCUC IDX_lcase, IDX_LCASE, #endif }; #define MI_ENTRY(N,T,F,B,M) N "\0" /* Mode names given on command line */ static const char mode_name[] = MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) MI_ENTRY("ek", combination, OMIT, 0, 0 ) MI_ENTRY("sane", combination, OMIT, 0, 0 ) MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) MI_ENTRY("crt", combination, OMIT, 0, 0 ) MI_ENTRY("dec", combination, OMIT, 0, 0 ) #if IXANY MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) #endif #if TABDLY || OXTABS MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) #endif #if XCASE && IUCLC && OLCUC MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) #endif MI_ENTRY("parenb", control, REV, PARENB, 0 ) MI_ENTRY("parodd", control, REV, PARODD, 0 ) MI_ENTRY("cs5", control, 0, CS5, CSIZE) MI_ENTRY("cs6", control, 0, CS6, CSIZE) MI_ENTRY("cs7", control, 0, CS7, CSIZE) MI_ENTRY("cs8", control, 0, CS8, CSIZE) MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) #if CRTSCTS MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) #endif MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) MI_ENTRY("inpck", input, REV, INPCK, 0 ) MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) MI_ENTRY("ixon", input, REV, IXON, 0 ) MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) #if IUCLC MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) #endif #if IXANY MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) #endif #if IMAXBEL MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) #endif #if IUTF8 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) #endif MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) #if OLCUC MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) #endif #if OCRNL MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) #endif #if ONLCR MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) #endif #if ONOCR MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) #endif #if ONLRET MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) #endif #if OFILL MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) #endif #if OFDEL MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) #endif #if NLDLY MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) #endif #if CRDLY MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) #endif #if TABDLY MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) # if TAB2 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) # endif # if TAB1 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) # endif MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) #else # if OXTABS MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) # endif #endif #if BSDLY MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) #endif #if VTDLY MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) #endif #if FFDLY MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) #endif MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) #if IEXTEN MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) #endif MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) #if XCASE MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) #endif #if TOSTOP MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) #endif #if ECHOPRT MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) #endif #if ECHOCTL MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) #endif #if ECHOKE MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) #endif ; #undef MI_ENTRY #define MI_ENTRY(N,T,F,B,M) { T, F, M, B }, static const struct mode_info mode_info[] = { /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) MI_ENTRY("ek", combination, OMIT, 0, 0 ) MI_ENTRY("sane", combination, OMIT, 0, 0 ) MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) MI_ENTRY("crt", combination, OMIT, 0, 0 ) MI_ENTRY("dec", combination, OMIT, 0, 0 ) #if IXANY MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) #endif #if TABDLY || OXTABS MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) #endif #if XCASE && IUCLC && OLCUC MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) #endif MI_ENTRY("parenb", control, REV, PARENB, 0 ) MI_ENTRY("parodd", control, REV, PARODD, 0 ) MI_ENTRY("cs5", control, 0, CS5, CSIZE) MI_ENTRY("cs6", control, 0, CS6, CSIZE) MI_ENTRY("cs7", control, 0, CS7, CSIZE) MI_ENTRY("cs8", control, 0, CS8, CSIZE) MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) #if CRTSCTS MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) #endif MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) MI_ENTRY("inpck", input, REV, INPCK, 0 ) MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) MI_ENTRY("ixon", input, REV, IXON, 0 ) MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) #if IUCLC MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) #endif #if IXANY MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) #endif #if IMAXBEL MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) #endif #if IUTF8 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) #endif MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) #if OLCUC MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) #endif #if OCRNL MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) #endif #if ONLCR MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) #endif #if ONOCR MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) #endif #if ONLRET MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) #endif #if OFILL MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) #endif #if OFDEL MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) #endif #if NLDLY MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) #endif #if CRDLY MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) #endif #if TABDLY MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) # if TAB2 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) # endif # if TAB1 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) # endif MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) #else # if OXTABS MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) # endif #endif #if BSDLY MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) #endif #if VTDLY MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) #endif #if FFDLY MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) #endif MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) #if IEXTEN MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) #endif MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) #if XCASE MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) #endif #if TOSTOP MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) #endif #if ECHOPRT MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) #endif #if ECHOCTL MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) #endif #if ECHOKE MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) #endif }; enum { NUM_mode_info = ARRAY_SIZE(mode_info) }; /* Control characters */ struct control_info { const uint8_t saneval; /* Value to set for 'stty sane' */ const uint8_t offset; /* Offset in c_cc */ }; enum { /* Must match control_name[] and control_info[] order! */ CIDX_intr = 0, CIDX_quit, CIDX_erase, CIDX_kill, CIDX_eof, CIDX_eol, #if VEOL2 CIDX_eol2, #endif #if VSWTCH CIDX_swtch, #endif CIDX_start, CIDX_stop, CIDX_susp, #if VDSUSP CIDX_dsusp, #endif #if VREPRINT CIDX_rprnt, #endif #if VWERASE CIDX_werase, #endif #if VLNEXT CIDX_lnext, #endif #if VFLUSHO CIDX_flush, #endif #if VSTATUS CIDX_status, #endif CIDX_min, CIDX_time, }; #define CI_ENTRY(n,s,o) n "\0" /* Name given on command line */ static const char control_name[] = CI_ENTRY("intr", CINTR, VINTR ) CI_ENTRY("quit", CQUIT, VQUIT ) CI_ENTRY("erase", CERASE, VERASE ) CI_ENTRY("kill", CKILL, VKILL ) CI_ENTRY("eof", CEOF, VEOF ) CI_ENTRY("eol", CEOL, VEOL ) #if VEOL2 CI_ENTRY("eol2", CEOL2, VEOL2 ) #endif #if VSWTCH CI_ENTRY("swtch", CSWTCH, VSWTCH ) #endif CI_ENTRY("start", CSTART, VSTART ) CI_ENTRY("stop", CSTOP, VSTOP ) CI_ENTRY("susp", CSUSP, VSUSP ) #if VDSUSP CI_ENTRY("dsusp", CDSUSP, VDSUSP ) #endif #if VREPRINT CI_ENTRY("rprnt", CRPRNT, VREPRINT) #endif #if VWERASE CI_ENTRY("werase", CWERASE, VWERASE ) #endif #if VLNEXT CI_ENTRY("lnext", CLNEXT, VLNEXT ) #endif #if VFLUSHO CI_ENTRY("flush", CFLUSHO, VFLUSHO ) #endif #if VSTATUS CI_ENTRY("status", CSTATUS, VSTATUS ) #endif /* These must be last because of the display routines */ CI_ENTRY("min", 1, VMIN ) CI_ENTRY("time", 0, VTIME ) ; #undef CI_ENTRY #define CI_ENTRY(n,s,o) { s, o }, static const struct control_info control_info[] = { /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */ CI_ENTRY("intr", CINTR, VINTR ) CI_ENTRY("quit", CQUIT, VQUIT ) CI_ENTRY("erase", CERASE, VERASE ) CI_ENTRY("kill", CKILL, VKILL ) CI_ENTRY("eof", CEOF, VEOF ) CI_ENTRY("eol", CEOL, VEOL ) #if VEOL2 CI_ENTRY("eol2", CEOL2, VEOL2 ) #endif #if VSWTCH CI_ENTRY("swtch", CSWTCH, VSWTCH ) #endif CI_ENTRY("start", CSTART, VSTART ) CI_ENTRY("stop", CSTOP, VSTOP ) CI_ENTRY("susp", CSUSP, VSUSP ) #if VDSUSP CI_ENTRY("dsusp", CDSUSP, VDSUSP ) #endif #if VREPRINT CI_ENTRY("rprnt", CRPRNT, VREPRINT) #endif #if VWERASE CI_ENTRY("werase", CWERASE, VWERASE ) #endif #if VLNEXT CI_ENTRY("lnext", CLNEXT, VLNEXT ) #endif #if VFLUSHO CI_ENTRY("flush", CFLUSHO, VFLUSHO ) #endif #if VSTATUS CI_ENTRY("status", CSTATUS, VSTATUS ) #endif /* These must be last because of the display routines */ CI_ENTRY("min", 1, VMIN ) CI_ENTRY("time", 0, VTIME ) }; enum { NUM_control_info = ARRAY_SIZE(control_info) }; struct globals { const char *device_name; /* The width of the screen, for output wrapping */ unsigned max_col; /* Current position, to know when to wrap */ unsigned current_col; char buf[10]; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ G.device_name = bb_msg_standard_input; \ G.max_col = 80; \ } while (0) static void set_speed_or_die(enum speed_setting type, const char *arg, struct termios *mode) { speed_t baud; baud = tty_value_to_baud(xatou(arg)); if (type != output_speed) { /* either input or both */ cfsetispeed(mode, baud); } if (type != input_speed) { /* either output or both */ cfsetospeed(mode, baud); } }
int stty_main(int argc UNUSED_PARAM, char **argv) { struct termios mode; void (*output_func)(const struct termios *, int); const char *file_name = NULL; int display_all = 0; int stty_state; int k; INIT_G(); stty_state = STTY_noargs; output_func = do_display; /* First pass: only parse/verify command line params */ k = 0; while (argv[++k]) { const struct mode_info *mp; const struct control_info *cp; const char *arg = argv[k]; const char *argnext = argv[k+1]; int param; if (arg[0] == '-') { int i; mp = find_mode(arg+1); if (mp) { if (!(mp->flags & REV)) goto invalid_argument; stty_state &= ~STTY_noargs; continue; } /* It is an option - parse it */ i = 0; while (arg[++i]) { switch (arg[i]) { case 'a': stty_state |= STTY_verbose_output; output_func = do_display; display_all = 1; break; case 'g': stty_state |= STTY_recoverable_output; output_func = display_recoverable; break; case 'F': if (file_name) bb_error_msg_and_die("only one device may be specified"); file_name = &arg[i+1]; /* "-Fdevice" ? */ if (!file_name[0]) { /* nope, "-F device" */ int p = k+1; /* argv[p] is argnext */ file_name = argnext; if (!file_name) bb_error_msg_and_die(bb_msg_requires_arg, "-F"); /* remove -F param from arg[vc] */ while (argv[p]) { argv[p] = argv[p+1]; ++p; } } goto end_option; default: goto invalid_argument; } } end_option: continue; } mp = find_mode(arg); if (mp) { stty_state &= ~STTY_noargs; continue; } cp = find_control(arg); if (cp) { if (!argnext) bb_error_msg_and_die(bb_msg_requires_arg, arg); /* called for the side effect of xfunc death only */ set_control_char_or_die(cp, argnext, &mode); stty_state &= ~STTY_noargs; ++k; continue; } param = find_param(arg); if (param & param_need_arg) { if (!argnext) bb_error_msg_and_die(bb_msg_requires_arg, arg); ++k; } switch (param) { #ifdef __linux__ case param_line: # ifndef TIOCGWINSZ xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); break; # endif /* else fall-through */ #endif #ifdef TIOCGWINSZ case param_rows: case param_cols: case param_columns: xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); break; case param_size: #endif case param_speed: break; case param_ispeed: /* called for the side effect of xfunc death only */ set_speed_or_die(input_speed, argnext, &mode); break; case param_ospeed: /* called for the side effect of xfunc death only */ set_speed_or_die(output_speed, argnext, &mode); break; default: if (recover_mode(arg, &mode) == 1) break; if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; invalid_argument: bb_error_msg_and_die("invalid argument '%s'", arg); } stty_state &= ~STTY_noargs; } /* Specifying both -a and -g is an error */ if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == (STTY_verbose_output | STTY_recoverable_output) ) { bb_error_msg_and_die("-a and -g are mutually exclusive"); } /* Specifying -a or -g with non-options is an error */ if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) && !(stty_state & STTY_noargs) ) { bb_error_msg_and_die("modes may not be set when -a or -g is used"); } /* Now it is safe to start doing things */ if (file_name) { G.device_name = file_name; xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); ndelay_off(STDIN_FILENO); } /* 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(STDIN_FILENO, &mode)) perror_on_device_and_die("%s"); if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) { G.max_col = get_terminal_width(STDOUT_FILENO); output_func(&mode, display_all); return EXIT_SUCCESS; } /* Second pass: perform actions */ k = 0; while (argv[++k]) { const struct mode_info *mp; const struct control_info *cp; const char *arg = argv[k]; const char *argnext = argv[k+1]; int param; if (arg[0] == '-') { mp = find_mode(arg+1); if (mp) { set_mode(mp, 1 /* reversed */, &mode); stty_state |= STTY_require_set_attr; } /* It is an option - already parsed. Skip it */ continue; } mp = find_mode(arg); if (mp) { set_mode(mp, 0 /* non-reversed */, &mode); stty_state |= STTY_require_set_attr; continue; } cp = find_control(arg); if (cp) { ++k; set_control_char_or_die(cp, argnext, &mode); stty_state |= STTY_require_set_attr; continue; } param = find_param(arg); if (param & param_need_arg) { ++k; } switch (param) { #ifdef __linux__ case param_line: mode.c_line = xatoul_sfx(argnext, stty_suffixes); stty_state |= STTY_require_set_attr; break; #endif #ifdef TIOCGWINSZ case param_cols: case param_columns: set_window_size(-1, xatoul_sfx(argnext, stty_suffixes)); break; case param_size: display_window_size(0); break; case param_rows: set_window_size(xatoul_sfx(argnext, stty_suffixes), -1); break; #endif case param_speed: display_speed(&mode, 0); break; case param_ispeed: set_speed_or_die(input_speed, argnext, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); break; case param_ospeed: set_speed_or_die(output_speed, argnext, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); break; default: if (recover_mode(arg, &mode) == 1) stty_state |= STTY_require_set_attr; else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{ set_speed_or_die(both_speeds, arg, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); } /* else - impossible (caught in the first pass): bb_error_msg_and_die("invalid argument '%s'", arg); */ } } if (stty_state & STTY_require_set_attr) { struct termios new_mode; if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) perror_on_device_and_die("%s"); /* 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(STDIN_FILENO, &new_mode)) perror_on_device_and_die("%s"); if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { /* * I think the below chunk is not necessary on Linux. * If you are deleting it, also delete STTY_speed_was_set bit - * it is only ever checked here. */ #if 0 /* was "if 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 ((stty_state & STTY_speed_was_set) || memcmp(&mode, &new_mode, sizeof(mode)) != 0) #endif perror_on_device_and_die("%s: cannot perform all requested operations"); } } return EXIT_SUCCESS; }
int slattach_main(int argc ATTRIBUTE_UNUSED, char **argv) { /* Line discipline code table */ static const char proto_names[] ALIGN1 = "slip\0" /* 0 */ "cslip\0" /* 1 */ "slip6\0" /* 2 */ "cslip6\0" /* 3 */ "adaptive\0" /* 8 */ ; int i, encap, opt; struct termios state; const char *proto = "cslip"; const char *extcmd; /* Command to execute after hangup */ const char *baud_str; int baud_code = -1; /* Line baud rate (system code) */ enum { OPT_p_proto = 1 << 0, OPT_s_baud = 1 << 1, OPT_c_extcmd = 1 << 2, OPT_e_quit = 1 << 3, OPT_h_watch = 1 << 4, OPT_m_nonraw = 1 << 5, OPT_L_local = 1 << 6, OPT_F_noflow = 1 << 7 }; INIT_G(); /* Parse command line options */ opt = getopt32(argv, "p:s:c:ehmLF", &proto, &baud_str, &extcmd); /*argc -= optind;*/ argv += optind; if (!*argv) bb_show_usage(); encap = index_in_strings(proto_names, proto); if (encap < 0) invarg(proto, "protocol"); if (encap > 3) encap = 8; /* We want to know if the baud rate is valid before we start touching the ttys */ if (opt & OPT_s_baud) { baud_code = tty_value_to_baud(xatoi(baud_str)); if (baud_code < 0) invarg(baud_str, "baud rate"); } /* Trap signals in order to restore tty states upon exit */ if (!(opt & OPT_e_quit)) { bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGQUIT) + (1 << SIGTERM) , sig_handler); } /* Open tty */ handle = open(*argv, O_RDWR | O_NDELAY); if (handle < 0) { char *buf = concat_path_file("/dev", *argv); handle = xopen(buf, O_RDWR | O_NDELAY); /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */ free(buf); } /* Save current tty state */ save_state(); /* Configure tty */ memcpy(&state, &saved_state, sizeof(state)); if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */ memset(&state.c_cc, 0, sizeof(state.c_cc)); state.c_cc[VMIN] = 1; state.c_iflag = IGNBRK | IGNPAR; state.c_oflag = 0; state.c_lflag = 0; state.c_cflag = CS8 | HUPCL | CREAD | ((opt & OPT_L_local) ? CLOCAL : 0) | ((opt & OPT_F_noflow) ? 0 : CRTSCTS); } if (opt & OPT_s_baud) { cfsetispeed(&state, baud_code); cfsetospeed(&state, baud_code); } set_state(&state, encap); /* Exit now if option -e was passed */ if (opt & OPT_e_quit) return 0; /* If we're not requested to watch, just keep descriptor open * until we are killed */ if (!(opt & OPT_h_watch)) while (1) sleep(24*60*60); /* Watch line for hangup */ while (1) { if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR)) goto no_carrier; sleep(15); } no_carrier: /* Execute command on hangup */ if (opt & OPT_c_extcmd) system(extcmd); /* Restore states and exit */ restore_state_and_exit(EXIT_SUCCESS); }
int microcom_main(int argc UNUSED_PARAM, char **argv) { int sfd; int nfd; struct pollfd pfd[2]; struct termios tio0, tiosfd, tio; char *device_lock_file; enum { OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@ OPT_s = 1 << 1, // baudrate OPT_d = 1 << 2, // wait for device response, ms OPT_t = 1 << 3, // timeout, ms }; speed_t speed = 9600; int delay = -1; int timeout = -1; unsigned opts; // fetch options opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout); // argc -= optind; argv += optind; // try to create lock file in /var/lock device_lock_file = (char *)bb_basename(argv[0]); device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file); sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644); if (sfd < 0) { // device already locked -> bail out if (errno == EEXIST) bb_perror_msg_and_die("can't create %s", device_lock_file); // can't create lock -> don't care if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); device_lock_file = NULL; } else { // %4d to make concurrent mgetty (if any) happy. // Mgetty treats 4-bytes lock files as binary, // not text, PID. Making 5+ char file. Brrr... fdprintf(sfd, "%4d\n", getpid()); close(sfd); } // setup signals bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGTERM) + (1 << SIGPIPE) , signal_handler); // error exit code if we fail to open the device signalled = 1; // open device sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK); if (sfd < 0) goto done; fcntl(sfd, F_SETFL, 0); // put device to "raw mode" xget1(sfd, &tio, &tiosfd); // set device speed cfsetspeed(&tio, tty_value_to_baud(speed)); if (xset1(sfd, &tio, argv[0])) goto done; // put stdin to "raw mode" (if stdin is a TTY), // handle one character at a time if (isatty(STDIN_FILENO)) { xget1(STDIN_FILENO, &tio, &tio0); if (xset1(STDIN_FILENO, &tio, "stdin")) goto done; } // main loop: check with poll(), then read/write bytes across pfd[0].fd = sfd; pfd[0].events = POLLIN; pfd[1].fd = STDIN_FILENO; pfd[1].events = POLLIN; signalled = 0; nfd = 2; while (!signalled && safe_poll(pfd, nfd, timeout) > 0) { if (nfd > 1 && pfd[1].revents) { char c; // read from stdin -> write to device if (safe_read(STDIN_FILENO, &c, 1) < 1) { // don't poll stdin anymore if we got EOF/error nfd--; goto skip_write; } // do we need special processing? if (!(opts & OPT_X)) { // ^@ sends Break if (VINTR == c) { tcsendbreak(sfd, 0); goto skip_write; } // ^X exits if (24 == c) break; } write(sfd, &c, 1); if (delay >= 0) safe_poll(pfd, 1, delay); skip_write: ; } if (pfd[0].revents) { #define iobuf bb_common_bufsiz1 ssize_t len; // read from device -> write to stdout len = safe_read(sfd, iobuf, sizeof(iobuf)); if (len > 0) full_write(STDOUT_FILENO, iobuf, len); else { // EOF/error -> bail out signalled = SIGHUP; break; } } } // restore device mode tcsetattr(sfd, TCSAFLUSH, &tiosfd); if (isatty(STDIN_FILENO)) tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); done: if (device_lock_file) unlink(device_lock_file); return signalled; }
int microcom_main(int argc, char **argv) { struct pollfd pfd[2]; #define sfd (pfd[1].fd) char *device_lock_file = NULL; const char *s; const char *opt_s = "9600"; unsigned speed; int len; int exitcode = 1; struct termios tio0, tiosfd, tio; getopt32(argv, "s:", &opt_s); argc -= optind; argv += optind; if (!argv[0]) bb_show_usage(); speed = xatou(opt_s); // try to create lock file in /var/lock s = bb_basename(argv[0]); if (!s[0]) { errno = ENODEV; bb_perror_msg_and_die("can't lock device"); } device_lock_file = xasprintf("/var/lock/LCK..%s", s); sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644); if (sfd < 0) { if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); device_lock_file = NULL; if (errno == EEXIST) bb_perror_msg_and_die("can't lock device"); // We don't abort on other errors: /var/lock can be // non-writable or non-existent } else { // %4d to make mgetty happy. It treats 4-bytes lock files as binary, // not text, PID. Making 5+ char file. Brrr... s = xasprintf("%4d\n", getpid()); write(sfd, s, strlen(s)); if (ENABLE_FEATURE_CLEAN_UP) free((char*)s); close(sfd); } // open device sfd = open(argv[0], O_RDWR); if (sfd < 0) { bb_perror_msg("can't open device"); goto unlock_and_exit; } // put stdin to "raw mode", handle one character at a time tcgetattr(STDIN_FILENO, &tio0); tio = tio0; tio.c_lflag &= ~(ICANON|ECHO); tio.c_iflag &= ~(IXON|ICRNL); tio.c_oflag &= ~(ONLCR); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSANOW, &tio)) { bb_perror_msg("can't tcsetattr for %s", "stdin"); goto unlock_and_exit; } /* same thing for modem (plus: set baud rate) - TODO: make CLI option */ tcgetattr(sfd, &tiosfd); tio = tiosfd; tio.c_lflag &= ~(ICANON|ECHO); tio.c_iflag &= ~(IXON|ICRNL); tio.c_oflag &= ~(ONLCR); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; cfsetispeed(&tio, tty_value_to_baud(speed)); cfsetospeed(&tio, tty_value_to_baud(speed)); if (tcsetattr(sfd, TCSANOW, &tio)) { bb_perror_msg("can't tcsetattr for %s", "device"); goto unlock_and_exit; } // disable SIGINT signal(SIGINT, SIG_IGN); // drain stdin tcflush(STDIN_FILENO, TCIFLUSH); printf("connected to '%s' (%d bps), exit with ctrl-X...\r\n", argv[0], speed); // main loop: check with poll(), then read/write bytes across pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; /*pfd[1].fd = sfd;*/ pfd[1].events = POLLIN; while (1) { int i; safe_poll(pfd, 2, -1); for (i = 0; i < 2; ++i) { if (pfd[i].revents & POLLIN) { len = read(pfd[i].fd, bb_common_bufsiz1, COMMON_BUFSIZE); if (len > 0) { if (!i && 24 == bb_common_bufsiz1[0]) goto done; // ^X exits write(pfd[1-i].fd, bb_common_bufsiz1, len); } } } } done: tcsetattr(sfd, TCSANOW, &tiosfd); tcsetattr(STDIN_FILENO, TCSANOW, &tio0); tcflush(STDIN_FILENO, TCIFLUSH); if (ENABLE_FEATURE_CLEAN_UP) close(sfd); exitcode = 0; unlock_and_exit: // delete lock file if (device_lock_file) { unlink(device_lock_file); if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); } return exitcode; }