X-Git-Url: https://git.datadissipation.net/ust/blobdiff_plain/600e7dfa6d742f6300d27ce7f10f29fa0e434d7e..HEAD:/ust.c diff --git a/ust.c b/ust.c index 8ad6dd1..4f00a4c 100644 --- a/ust.c +++ b/ust.c @@ -1,7 +1,7 @@ +/* see LICENSE for license details */ #include #include -#include #include #include #include @@ -10,13 +10,14 @@ #include #include +#include #include #include #ifdef __linux__ #include #include -#else /* needed for termios2, which is itself needed for non-standard bauds */ +#else /* including for termios2, which is needed for non-standard bauds */ #include #endif @@ -29,20 +30,20 @@ #define IXONXOFF (IXON | IXOFF) -#ifdef __linux__ - #define TERMIOS_STRUCT termios2 -#else - #define TERMIOS_STRUCT termios -#endif - #if defined(CCDTR_IFLOW) && defined(CDSR_OFLOW) #define CDTRDSR (CDTR_IFLOW | CDSR_OFLOW) #endif +#ifdef __linux__ + typedef struct termios2 TermStruct; +#else + typedef struct termios TermStruct; +#endif + typedef struct { + int offset; /* can only be a 1 or a -1 */ char find; char input; - int offset; /* can only be a 1 or a -1 */ } Args; typedef struct { @@ -62,47 +63,56 @@ typedef struct { /* including here to access CRLFOpt */ #include "config.h" -static int gettermattr(int dv, struct TERMIOS_STRUCT *optst); -static int settermattr(int dv, const struct TERMIOS_STRUCT *optst); -static void settermspd(unsigned int lispeed, unsigned int lospeed, struct TERMIOS_STRUCT *optst); -static int openport(); -static unsigned int strtoui(const char *str, const char *msg, unsigned int min); -static int troptions(CRLFOpt *options, const char *str); -static int setroptions(); +static int gettermattr(int dv, TermStruct *optst); +static int settermattr(int dv, const TermStruct *optst); +static void settermspd(unsigned int lispeed, unsigned int lospeed, + TermStruct *optst); +static int openport(void); +static int openparent(void); +static unsigned int strtoui(const char *str, unsigned int min); +static int chcktropts(CRLFOpt *options, const char *str); +static int settropts(void); static inline unsigned int lsbmask(unsigned int n); -static inline int termchck(const struct TERMIOS_STRUCT *term); -static void interchck(); -static void cechck(); -static void *writeport(void *unused); -static void *readport(void *unused); +static inline int termvalid(const TermStruct *term); +static void eninter(void); +static void enusrecho(void); +static void *writeport(void *); +static void *readport(void *); static void getcmd(int escape); static inline void replacechar(char *buff, ssize_t size, const Args *args); -static inline ssize_t addchar(char *buff, ssize_t size, int *scratch, const Sizes *msizes, const Args *args); -static inline ssize_t rmchar(char *buff, ssize_t sread, int *scratch, const Args *args); +static inline ssize_t addchar(char *buff, ssize_t size, int *scratch, + const Sizes *msizes, const Args *args); +static inline ssize_t rmchar(char *buff, ssize_t sread, int *scratch, + const Args *args); static void sighandl(int signo); static void die(int code, const char *msg, ...); -static struct TERMIOS_STRUCT cntrl, /* device struct */ - origterm = { 0 }, /* controlling terminal structs */ - newterm = { 0 }; +static TermStruct cntrl, /* device struct */ + /* controlling terminal structs */ + origterm = { 0 }, + newterm = { 0 }; -static Args bsargs = {DEL, DEL, 0}; -static const Args nocrargs = {CR, 0, 0}, - nolfargs = {LF, '\0', 0}, /* null-terminator is for use inside `getcmd()` */ - lfincrargs = {CR, LF, 1}, - crinlfargs = {LF, CR, 0}; +static Args bsargs = {0, DEL, DEL}; +static const Args nocrargs = {0, CR, 0}, + /* null-terminator is for use inside `getcmd()` */ + nolfargs = {0, LF, '\0'}, + lfincrargs = {1, CR, LF}, + crinlfargs = {0, LF, CR}; static struct timespec wts; -static const unsigned int uintmax = ~(unsigned int)0; /* unsigned -1 used to return an error */ +/* unsigned -1 used to return an error */ +static const unsigned int uintmax = ~(unsigned int)0; static int interactive = 0; static char *writebuff = NULL; static char *readbuff = NULL; static int *scratchr = NULL; static int *scratchw = NULL; static int fd = -1; +static int pfd = -1; +static long rwc = 0; int -gettermattr(int dv, struct TERMIOS_STRUCT *optst) +gettermattr(int dv, TermStruct *optst) { #ifdef __linux__ return ioctl(dv, TCGETS2, optst); @@ -112,7 +122,7 @@ gettermattr(int dv, struct TERMIOS_STRUCT *optst) } int -settermattr(int dv, const struct TERMIOS_STRUCT *optst) +settermattr(int dv, const TermStruct *optst) { #ifdef __linux__ return ioctl(dv, TCSETS2, optst); @@ -122,29 +132,29 @@ settermattr(int dv, const struct TERMIOS_STRUCT *optst) } void -settermspd(unsigned int lispeed, unsigned int lospeed, struct TERMIOS_STRUCT *optst) +settermspd(unsigned int lispeed, unsigned int lospeed, TermStruct *optst) { #ifdef __linux__ optst->c_cflag &= ~CBAUD; optst->c_cflag |= BOTHER; - optst->c_ispeed = ispeed; - optst->c_ospeed = ospeed; + optst->c_ispeed = lispeed; + optst->c_ospeed = lospeed; #else - cfsetispeed(optst, ispeed); - cfsetospeed(optst, ospeed); + (void)cfsetispeed(optst, lispeed); + (void)cfsetospeed(optst, lospeed); #endif } int -openport() +openport(void) { int flags; struct flock fl = { 0 }; - + if (verbose) fprintf(stderr, "opening \"%s\"\n", line); - fd = open(line, (O_RDWR | O_NOCTTY | O_NDELAY)); - if (fd == -1) { + fd = open(line, (O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY)); + if (fd < 0) { perror("error opening device"); exit(1); } @@ -154,38 +164,38 @@ openport() if (!isatty(fd)) die(2, "device \"%s\" is not a TTY\n", line); - flags = fcntl(fd, F_GETFL, 0); - /* opened to check with non-blocking mode, now set to blocking */ - flags &= ~(O_NONBLOCK | O_NDELAY); + if (blocking) { + flags = fcntl(fd, F_GETFL, 0); + flags &= ~(O_NONBLOCK | O_NDELAY); - if (fcntl(fd, F_SETFL, flags) == -1) { - perror("fcntl"); - die(1, "exiting now\n"); + if (fcntl(fd, F_SETFL, flags) < 0) { + perror("fcntl"); + die(1, "exiting now\n"); + } } - - if (gettermattr(fd, &cntrl) == -1) - die(1, "failed to get device attributes\n"); - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; + if (gettermattr(fd, &cntrl) < 0) + die(1, "failed to get device attributes\n"); if (exclusive) { - if (fcntl(fd, F_SETLK, &fl) == -1) { - perror("fcntl"); - die(1, "failed to lock the device, exiting now\n"); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + if (fcntl(fd, F_SETLK, &fl) < 0) { + perror("fcntl"); + die(1, "failed to lock the device, exiting now\n"); } } /* * Baud */ - if (verbose) fprintf(stderr, "setting baudrate [RX:%u | TX:%u]\n", ispeed, ospeed); + if (verbose) fprintf(stderr, "setting baudrate [RX:%u | TX:%u]\n", + ispeed, ospeed); settermspd(ispeed, ospeed, &cntrl); - if (settermattr(fd, &cntrl) == -1) - die(2,"failed to set baudrate [RX:%u | TX:%u]\n", ispeed, ospeed); + if (settermattr(fd, &cntrl) < 0) + die(2,"failed to set baudrate [RX:%u | TX:%u]\n", + ispeed, ospeed); /* * Parity @@ -194,17 +204,17 @@ openport() cntrl.c_iflag &= ~(ISTRIP | BRKINT); cntrl.c_cflag &= ~(PARENB | PARODD); - if (verbose) fprintf(stderr, "setting parity [even: %d, odd: %d]\n",\ - parity & 1, (parity & 2) >> 1); + if (verbose) fprintf(stderr, "setting parity [even: %d, odd: %d]\n", + parity & 1, (parity & 2) >> 1); if (parity == 1) cntrl.c_cflag |= PARENB; if (parity == 2) cntrl.c_cflag |= PARENB | PARODD; - if (settermattr(fd, &cntrl) == -1) - die(2, "failed to set parity [even: %d, odd: %d]\n",\ - parity & 1, (parity & 2) >> 1); + if (settermattr(fd, &cntrl) < 0) + die(2, "failed to set parity [even: %d, odd: %d]\n", + parity & 1, (parity & 2) >> 1); /* * Flow control @@ -212,8 +222,10 @@ openport() if (verbose) { /* it's so ugly and beautiful at the same time */ int t = (((hard & 1) << 1) & (hard & 2)) >> 1; - fprintf(stderr, "setting flow control [XON/XOFF: %d, RTS/CTS: %d, DTR/DSR: %d, DCD: %d]\n",\ - soft, t ^ ((hard & 1) << 1) >> 1, t ^ (hard & 2) >> 1, t); + fprintf(stderr, "setting flow control " + "[XON/XOFF: %d, RTS/CTS: %d, DTR/DSR: %d, DCD: %d]\n", + soft, t ^ ((hard & 1) << 1) >> 1, + t ^ (hard & 2) >> 1, t); } cntrl.c_cflag |= CLOCAL; @@ -226,8 +238,9 @@ openport() cntrl.c_cflag |= CRTSCTS; } else if (hard == 2) { #ifndef CDTRDSR - fprintf(stderr, "DTR/DSR flow control is not supported on this platform\n" - "enabling this option does nothing!\n"); + fprintf(stderr, "DTR/DSR " + "flow control is not supported on this platform\n" + "enabling this option does nothing!\n"); #else cntrl.c_lflag |= CDTRDSR; #endif @@ -238,7 +251,7 @@ openport() #endif } - if (settermattr(fd, &cntrl) == -1) + if (settermattr(fd, &cntrl) < 0) die(2, "failed to set flow control\n"); /* @@ -247,7 +260,7 @@ openport() cntrl.c_iflag &= ~(ICRNL | INLCR); cntrl.c_oflag &= ~(OCRNL | ONLRET | ONLCR); - if (setroptions()) + if (settropts()) die(2, "failed to set cr-lf translation options\n"); /* @@ -269,7 +282,7 @@ openport() cntrl.c_cflag &= ~CSIZE; cntrl.c_cflag |= db; - if (settermattr(fd, &cntrl) == -1) + if (settermattr(fd, &cntrl) < 0) die(2, "failed to set data bits [%c]\n", datab); /* @@ -282,10 +295,10 @@ openport() else cntrl.c_cflag &= ~CSTOPB; - if (settermattr(fd, &cntrl) == -1) + if (settermattr(fd, &cntrl) < 0) die(1, "failed to set stop bits [%d]\n", stopb+1); - cntrl.c_cc[VMIN] = 1; + cntrl.c_cc[VMIN] = 0; cntrl.c_cc[VTIME] = 0; /* flush both hardware buffers */ @@ -294,73 +307,85 @@ openport() #else tcflush(fd, TCIOFLUSH); #endif - return(fd); + /* returning only for readability */ + return fd; } -unsigned int -strtoui(const char *str, const char *msg, unsigned int min) +int +openparent(void) { + if (verbose) fprintf(stderr, "opening parent terminal\n"); + + pfd = open("/dev/tty", O_RDWR); + if (pfd < 0) { + perror("error opening parent terminal fd"); + die(1, "exiting now\n"); + } + + if (gettermattr(pfd, &origterm) < 0 ) + die(1, "failed to get parent terminal attributes\n"); + + newterm = origterm; + + /* returning only for readability */ + return pfd; +} + +unsigned int +strtoui(const char *str, unsigned int min) +{ long t; char *endptr; t = strtol(str, &endptr, 10); if (errno != 0 || *endptr != '\0' || t < min || t > uintmax) { - fprintf(stderr, msg, str); return uintmax; } - /* - * conversion like this results in 'undefined behavior' according to C spec, - * but almost all compilers will just truncate the value so it's OK - */ + return (unsigned int)t; } int -troptions(CRLFOpt *options, const char *str) +chcktropts(CRLFOpt *options, const char *str) { switch (str[0]) { case 'l': if (!strcmp(str, "lf-in-cr")) { - options->lfincr ^= 1; return 0; + options->lfincr ^= 1; return 0; } else if (!strcmp(str, "lf-to-cr")) { - options->lftocr ^= 1; return 0; + options->lftocr ^= 1; return 0; } else { - goto trerr; + return 1; } - break; case 'c': if (!strcmp(str, "cr-in-lf")) { - options->crinlf ^= 1; return 0; + options->crinlf ^= 1; return 0; } else if (!strcmp(str, "cr-to-lf")) { - options->crtolf ^= 1; return 0; + options->crtolf ^= 1; return 0; } else { - goto trerr; + return 1; } - break; case 'n': if (!strcmp(str, "no-lf")) { - options->nolf ^= 1; return 0; + options->nolf ^= 1; return 0; } else if (!strcmp(str, "no-cr")) { - options->nocr ^= 1; return 0; + options->nocr ^= 1; return 0; } else { - goto trerr; + return 1; } - break; default: - trerr: - fprintf(stderr, "invalid translation option: %s\n", str); return 1; } } int -setroptions() +settropts(void) { - cntrl.c_iflag ^= (ICRNL & lsbmask(itropts.crtolf)); - cntrl.c_iflag ^= (INLCR & lsbmask(itropts.lftocr)); - cntrl.c_oflag ^= (OCRNL & lsbmask(tropts.crtolf)); + cntrl.c_iflag ^= (ICRNL & lsbmask(itropts.crtolf)); + cntrl.c_iflag ^= (INLCR & lsbmask(itropts.lftocr)); + cntrl.c_oflag ^= (OCRNL & lsbmask(tropts.crtolf)); cntrl.c_oflag ^= (ONLRET & lsbmask(tropts.lftocr)); - cntrl.c_oflag ^= (ONLCR & lsbmask(tropts.crinlf)); + cntrl.c_oflag ^= (ONLCR & lsbmask(tropts.crinlf)); return settermattr(fd, &cntrl); } @@ -375,11 +400,11 @@ lsbmask(unsigned int n) } inline int -termchck(const struct TERMIOS_STRUCT *term) +termvalid(const TermStruct *term) { - for (size_t i = 0; i < sizeof(struct TERMIOS_STRUCT); i++) - if (((char*)term)[i] != 0) - return 1; + for (size_t i = 0; i < sizeof(TermStruct); i++) + if (((char*)term)[i] != 0) + return 1; return 0; } @@ -388,10 +413,9 @@ void * writeport(void *unused) { Sizes msizes; - struct timespec timeout, ts; + struct timespec ts; + int escape = 0; - timeout.tv_sec = stimeout; - timeout.tv_nsec = nstimeout; ts.tv_sec = swritedelay; ts.tv_nsec = nswritedelay; wts.tv_sec = 0; @@ -406,48 +430,52 @@ writeport(void *unused) die(1, "buffer allocation failed\n"); if (scratchw == NULL) die(1, "scratch buffer allocation failed\n"); - - int escape = 0; for (;;) { ssize_t inln = read(STDIN_FILENO, writebuff, wbuffsz - 1); - if (inln > 0) { - if (escape) { - getcmd(escape); - escape = 0; - } - if (writebuff[0] == escapechar) { - if (inln > 1) - getcmd(escape); - else - escape = 1; - } + if (inln > 0) { + if (escape) { + getcmd(escape); + escape = 0; + inln = 0; + } + if (writebuff[0] == escapechar) { + if (inln > 1) + getcmd(escape); + else + escape = 1; + inln = 0; + } + + if (bsargs.find != bsargs.input) + replacechar(writebuff, inln, &bsargs); + if (tropts.nocr) + inln = rmchar(writebuff, inln, scratchw, &nocrargs); + if (tropts.nolf) + inln = rmchar(writebuff, inln, scratchw, &nolfargs); + if (tropts.lfincr) + inln = addchar(writebuff, inln, scratchw, &msizes, + &lfincrargs); + + if (inln > 1) { + for (int i = 0; i <= inln; i++) { + rwc -= write(fd, &writebuff[i], 1); + nanosleep(&wts, NULL); + } + + } else { + rwc -= write(fd, writebuff, inln); + } - if (bsargs.find != bsargs.input) - replacechar(writebuff, inln, &bsargs); - if (tropts.nocr) - inln = rmchar(writebuff, inln, scratchw, &nocrargs); - if (tropts.nolf) - inln = rmchar(writebuff, inln, scratchw, &nolfargs); - if (tropts.lfincr) - inln = addchar(writebuff, inln, scratchw, &msizes, &lfincrargs); - - if (inln > 1) { - for (int i = 0; i <= inln; i++) { - write(fd, &writebuff[i], 1); - nanosleep(&wts, NULL); - } - } else { - write(fd, writebuff, 1); - } } else if (inln == 0 && !interactive) { - break; + break; } nanosleep(&ts, NULL); } - /* ust kills itself upon receiving a signal so no fancy `nanosleep()` features needed */ - nanosleep(&timeout, NULL); + die(0, "[EOF]\n"); + + /* unreachable */ return NULL; } @@ -456,6 +484,7 @@ readport(void *unused) { Sizes msizes; struct timespec ts; + ts.tv_sec = sreaddelay; ts.tv_nsec = nsreaddelay; msizes.sizesm = scratchrsz; @@ -469,7 +498,7 @@ readport(void *unused) if (scratchr == NULL) die(1, "scratch buffer allocation failed\n"); - interchck(); + eninter(); /* disable stdout buffering */ setvbuf(stdout, NULL, _IONBF, 0); @@ -477,32 +506,37 @@ readport(void *unused) for (;;) { ssize_t outln = read(fd, readbuff, rbuffsz - 1); if (outln > 0) { - if (itropts.nocr) - outln = rmchar(readbuff, outln, scratchr, &nocrargs); - if (itropts.nolf) - outln = rmchar(readbuff, outln, scratchr, &nolfargs); - if (itropts.crinlf) - outln = addchar(readbuff, outln, scratchr, &msizes, &crinlfargs); - if (itropts.lfincr) - outln = addchar(readbuff, outln, scratchr, &msizes, &lfincrargs); - - write(STDOUT_FILENO, readbuff, outln); + if (itropts.nocr) + outln = rmchar(readbuff, outln, scratchr, &nocrargs); + if (itropts.nolf) + outln = rmchar(readbuff, outln, scratchr, &nolfargs); + if (itropts.crinlf) + outln = addchar(readbuff, outln, scratchr, &msizes, + &crinlfargs); + if (itropts.lfincr) + outln = addchar(readbuff, outln, scratchr, &msizes, + &lfincrargs); + + rwc += write(STDOUT_FILENO, readbuff, outln); } nanosleep(&ts, NULL); } + + /* unreachable */ return NULL; } -inline void __attribute__((hot)) +inline void replacechar(char *buff, ssize_t size, const Args *args) { for (int i = 0; i < size; i++) if (buff[i] == args->find) - buff[i] = args->input; + buff[i] = args->input; } -inline ssize_t __attribute__((hot)) -addchar(char *buff, ssize_t size, int *scratch, const Sizes *sizes, const Args *args) +inline ssize_t +addchar(char *buff, ssize_t size, int *scratch, const Sizes *sizes, + const Args *args) { int to, c; c = 0; @@ -511,15 +545,15 @@ addchar(char *buff, ssize_t size, int *scratch, const Sizes *sizes, const Args * for (int i = 0; i < size; i++) { if (buff[i] == args->find) { - scratch[c] = i; - c++; + scratch[c] = i; + c++; } if (c >= sizes->sizesm) - break; + break; } if (!c) return(size); - if (size == 1 && c == 1 && scratch[0] == 0) { + if (size == 1 && c == 1) { buff[to ^ 1] = args->input; buff[to] = args->find; return 2; @@ -532,23 +566,22 @@ addchar(char *buff, ssize_t size, int *scratch, const Sizes *sizes, const Args * memmove(&buff[t] + 1, &buff[t], (size + c) - t); buff[t + (to ^ 1)] = args->input; } - printf("%s\n", buff); return (size + c); } -inline ssize_t __attribute__((hot)) +inline ssize_t rmchar(char *buff, ssize_t size, int *scratch, const Args *args) { int c = 0; for (int i = 0; i < size; i++) { if (buff[i] == args->find) { - scratch[c] = i; - c++; + scratch[c] = i; + c++; } } if (!c) return(size); - if (size == 1 && c == 1 && scratch[0] == 0) + if (size == 1 && c == 1) return 0; for (int i = c; i >= 0; i--) { @@ -559,54 +592,50 @@ rmchar(char *buff, ssize_t size, int *scratch, const Args *args) } void -interchck() +eninter(void) { if (interactive) { - if (gettermattr(STDIN_FILENO, &origterm) < 0 ) - die(1, "failed to get terminal attributes\n"); - - newterm = origterm; - if (!canonical) - newterm.c_lflag &= ~ECHO; + newterm.c_lflag &= ~ECHO; if (!half) - newterm.c_lflag &= ~ICANON; + newterm.c_lflag &= ~ICANON; if (soft) - newterm.c_iflag |= IXONXOFF; + newterm.c_iflag |= IXONXOFF; newterm.c_iflag |= INLCR; newterm.c_cc[VMIN] = minchars; newterm.c_cc[VTIME] = 0; - newterm.c_cc[VINTR] = newterm.c_cc[VSUSP]\ - = newterm.c_cc[VQUIT] = newterm.c_cc[VLNEXT]\ - = newterm.c_cc[VDISCARD] = _POSIX_VDISABLE; + newterm.c_cc[VINTR] = newterm.c_cc[VSUSP] + = newterm.c_cc[VQUIT] = newterm.c_cc[VLNEXT] + = newterm.c_cc[VDISCARD] = _POSIX_VDISABLE; #ifdef VDSUSP newterm.c_cc[VDSUSP] = _POSIX_VDISABLE; #endif if (backspace) - bsargs.input = BS; + bsargs.input = BS; else - bsargs.input = DEL; + bsargs.input = DEL; bsargs.find = origterm.c_cc[VERASE]; - if (settermattr(STDOUT_FILENO, &newterm) < 0) - die(1, "failed to set terminal attributes\n"); + if (settermattr(pfd, &newterm) < 0) + die(1, "failed to set terminal attributes\n"); } return; } void -cechck() +enusrecho(void) { if ((!half || !canonical) && interactive) { newterm.c_lflag |= ECHO; newterm.c_lflag |= ICANON; - if (settermattr(STDIN_FILENO, &newterm) < 0) - fprintf(stderr, "failed to enable echo and/or canonical mode\n"); + if (settermattr(pfd, &newterm) < 0) + fprintf(stderr, + "failed to enable local echo for user input\n"); } return; } @@ -615,6 +644,7 @@ void getcmd(int escape) { Sizes msizes; + CRLFOpt *toptptr; char cmdchar; char ttr[64]; unsigned int tspd; @@ -622,11 +652,6 @@ getcmd(int escape) msizes.sizesm = scratchwsz; msizes.sizebm = wbuffsz; - if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) - interactive = 1; - - interchck(); - if (escape) cmdchar = writebuff[0]; else @@ -638,107 +663,114 @@ getcmd(int escape) die(0,"\n[EOT]\n"); case 'b': if (backspace) - bsargs.input = DEL; + bsargs.input = DEL; else - bsargs.input = BS; + bsargs.input = BS; backspace ^= 1; break; case 'h': if (half) - newterm.c_lflag &= ~ECHO; + newterm.c_lflag &= ~ECHO; + else + newterm.c_lflag |= ECHO; + if (settermattr(pfd, &newterm) < 0) + die(1, "failed to set terminal attributes\n"); else - newterm.c_lflag |= ECHO; - half ^= 1; - if (settermattr(STDOUT_FILENO, &newterm) < 0) - die(1, "failed to set terminal attributes\n"); + half ^= 1; break; case 'c': if (canonical) - newterm.c_lflag &= ~ICANON; + newterm.c_lflag &= ~ICANON; + else + newterm.c_lflag |= ICANON; + if (settermattr(pfd, &newterm) < 0) + die(1, "failed to set terminal attributes\n"); else - newterm.c_lflag |= ICANON; - canonical ^= 1; - if (settermattr(STDOUT_FILENO, &newterm) < 0) - die(1, "failed to set terminal attributes\n"); + canonical ^= 1; break; case 'w': - cechck(); + enusrecho(); if (fgets(ttr, sizeof(ttr), stdin) != NULL) { - replacechar(ttr, 63, &nolfargs); - ssize_t frln; - int wfd = open(ttr, O_RDONLY); - if (wfd == -1) { - perror("error opening file"); - goto finish; - } - while ((frln = read(wfd, writebuff, wbuffsz - 1)) > 0) { - for (int i = 0; i <= frln; i++) { - if (tropts.nocr) - frln = rmchar(writebuff, frln, scratchw, &nocrargs); - if (tropts.nolf) - frln = rmchar(writebuff, frln, scratchw, &nolfargs); - if (tropts.lfincr) - frln = addchar(writebuff, frln, scratchw, &msizes, &lfincrargs); + ssize_t frln; + int wfd = open(ttr, O_RDONLY); + replacechar(ttr, 63, &nolfargs); + if (wfd < -1) { + perror("error opening file"); + goto finish; + } + while ((frln = read(wfd, writebuff, wbuffsz - 1)) > 0) { + rwc += frln; + for (int i = 0; i <= frln; i++) { + if (tropts.nocr) + frln = rmchar(writebuff, frln, scratchw, + &nocrargs); + if (tropts.nolf) + frln = rmchar(writebuff, frln, scratchw, + &nolfargs); + if (tropts.lfincr) + frln = addchar(writebuff, frln, scratchw, + &msizes, &lfincrargs); - write(fd, &writebuff[i], 1); - nanosleep(&wts, NULL); - } - } + rwc -= write(fd, &writebuff[i], 1); + nanosleep(&wts, NULL); + } + } } goto finish; case 's': - cechck(); + enusrecho(); if (fgets(ttr, sizeof(ttr), stdin) != NULL) { - replacechar(ttr, 63, &nolfargs); - tspd = strtoui(ttr, "invalid speed\n", 0); - if (tspd != uintmax) { - ospeed = ispeed = tspd; - settermspd(ispeed, ospeed, &cntrl); - if (settermattr(fd, &cntrl) != 0) - fprintf(stderr, "failed to set baudrate " - "[RX:%u | TX:%u]\n", ispeed, ospeed); - } + replacechar(ttr, 63, &nolfargs); + tspd = strtoui(ttr, 0); + if (tspd != uintmax) { + ospeed = ispeed = tspd; + settermspd(ispeed, ospeed, &cntrl); + if (settermattr(fd, &cntrl) < 0) + fprintf(stderr, "failed to set baudrate " + "[RX:%u | TX:%u]\n", ispeed, ospeed); + } else { + fprintf(stderr, "invalid speed: %s\n", ttr); + } } goto finish; case 'd': - cechck(); + enusrecho(); if (fgets(ttr, sizeof(ttr), stdin) != NULL) { - replacechar(ttr, 63, &nolfargs); - chardelay = strtoui(ttr, "invalid delay\n", 0); - if (chardelay != uintmax) { - wts.tv_sec = 0; - wts.tv_nsec = chardelay; - } - } - goto finish; - case 't': - cechck(); - fprintf(stderr, "additional output translation option: "); - if (fgets(ttr, sizeof(ttr), stdin) != NULL) { - replacechar(ttr, 63, &nolfargs); - if(troptions(&tropts, ttr)) - goto finish; - if (setroptions()) - fprintf(stderr, "could not set new options\n"); + replacechar(ttr, 63, &nolfargs); + chardelay = strtoui(ttr, 0); + if (chardelay != uintmax) { + wts.tv_sec = 0; + wts.tv_nsec = chardelay; + } else { + fprintf(stderr, "invalid delay: %s\n", ttr); + } } goto finish; + case 't': /* FALLTHROUGH */ case 'T': - cechck(); - printf("additional input translation option: "); + enusrecho(); + toptptr = (cmdchar == 't') ? &tropts : &itropts; + fprintf(stderr, "additional translation option: "); if (fgets(ttr, sizeof(ttr), stdin) != NULL) { - replacechar(ttr, 63, &nolfargs); - if (troptions(&itropts, ttr)) - goto finish; - if (setroptions()) - fprintf(stderr, "could not set new options\n"); + replacechar(ttr, 63, &nolfargs); + if (chcktropts(toptptr, ttr)) { + fprintf(stderr, + "invalid translation option: %s\n", + optarg); + + goto finish; + } + if (settropts()) + fprintf(stderr, "could not set new options\n"); } finish: if (!half && interactive) - newterm.c_lflag &= ~ECHO; + newterm.c_lflag &= ~ECHO; if (!canonical && interactive) - newterm.c_lflag &= ~ICANON; - if (settermattr(STDIN_FILENO, &newterm) == -1) - fprintf(stderr, "failed to restore echo and/or canonical mode\n"); + newterm.c_lflag &= ~ICANON; + if (settermattr(pfd, &newterm) < 0) + fprintf(stderr, + "failed to restore echo and/or canonical mode\n"); break; case 'p':; int st; @@ -746,20 +778,20 @@ getcmd(int escape) ts.tv_sec = spulsedelay; ts.tv_nsec = nspulsedelay; - if (ioctl(fd, TIOCMGET, &st) != 0) { - fprintf(stderr, "failed to get port status\n"); - break; + if (ioctl(fd, TIOCMGET, &st) < 0) { + fprintf(stderr, "failed to get port status\n"); + break; } st ^= TIOCM_DTR; - if (ioctl(fd, TIOCMSET, &st) != 0) { - fprintf(stderr, "failed to set DTR [assertion]\n"); - break; + if (ioctl(fd, TIOCMSET, &st) < 0) { + fprintf(stderr, "failed to set DTR [assertion]\n"); + break; } nanosleep(&ts, NULL); st ^= TIOCM_DTR; - if (ioctl(fd, TIOCMSET, &st) != 0) - fprintf(stderr, "failed to set DTR [negation]\n"); + if (ioctl(fd, TIOCMSET, &st) < 0) + fprintf(stderr, "failed to set DTR [negation]\n"); break; case BS: /* FALLTHROUGH */ case DEL: @@ -782,7 +814,19 @@ void die(int code, const char *msg, ...) { va_list fpa; - if (fd != -1) + + if (termvalid(&origterm)) + settermattr(pfd, &origterm); + + while (rwc > 0) { + int t = rwc; + nanosleep(&wts, NULL); + if (t == rwc) break; + } + + if (pfd < 0) + close(pfd); + if (fd < 0) close(fd); if (writebuff) @@ -794,9 +838,6 @@ die(int code, const char *msg, ...) if (scratchr) free(scratchr); - if (termchck(&newterm)) - settermattr(STDIN_FILENO, &origterm); - va_start(fpa, msg); vfprintf(stderr, msg, fpa); va_end(fpa); @@ -805,18 +846,27 @@ die(int code, const char *msg, ...) } int -main(int argc, char **argv) +main(int argc, char *argv[]) { + long tl; char *t = NULL; - /* glibc's `asprintf()` won't set the string to NULL on failure automatically */ - for (int i = 1; i < argc; i++ ) { - if (argv[i][0] == '-' && argv[i][1] >= '0' && argv[i][1] <= '9') { - asprintf(&t, "-s%s", argv[i] + 1); - if (!t) { - fprintf(stderr, "%s: cannot convert -# to -s#\n", argv[0]); - break; - } - argv[i] = t; + char *endptr = NULL; + unsigned int tui; + int oind, rxspdset, devset, c; + oind = rxspdset = devset = 0; + + for (int i = 1; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] >= '0' && + argv[i][1] <= '9') { + if (asprintf(&t, "-s%s", argv[i] + 1) == -1) { + fprintf(stderr, + "%s: cannot convert -# to -s#\n", + argv[0]); + + break; + } else { + argv[i] = t; + } } } @@ -834,8 +884,6 @@ main(int argc, char **argv) {"data", required_argument, NULL, 'D'}, {"delay", required_argument, NULL, 'd'}, {"min-chars", required_argument, NULL, 'm'}, - {"timeout-ns", required_argument, NULL, 'f'}, - {"timeout-s", required_argument, NULL, 'F'}, {"stop-bits", no_argument, NULL, 'S'}, {"backspace", no_argument, NULL, 'b'}, {"translation", required_argument, NULL, 't'}, @@ -844,12 +892,10 @@ main(int argc, char **argv) {NULL, 0, NULL, 0} }; - unsigned int tui; - int oind, rxspdset, devset, c; - oind = rxspdset = devset = 0; - while ((c = getopt_long(argc, argv, "D:F:T:d:f:i:l:m:s:t:RXbcehorv", longopt, &oind)) != -1) { + while ((c = getopt_long(argc, argv, + "D:T:d:i:l:m:s:t:RXbcehorv", longopt, &oind)) >= 0) { switch (c) { - case 0: + case 0: break; case 'b': backspace ^= 1; break; @@ -861,29 +907,28 @@ main(int argc, char **argv) hard ^= 2; break; case 'X': soft ^= 1; break; - case 'd': /* FALLTHROUGH */ - case 'f': - case 'F':; - char* endptr; - long t; - t = strtol(optarg, &endptr, 10); - if (errno != 0 || *endptr != '\0' || t < 0) { - fprintf(stderr, "%s: invalid character delay: %s\n", argv[0], optarg); - goto ustusage; - } else if (c == 'd') { - chardelay = t; - } else if (c == 'f'){ - nstimeout = t; + case 'd': + tl = strtol(optarg, &endptr, 10); + if (errno != 0 || *endptr != '\0' || tl < 0) { + fprintf(stderr, + "%s: invalid character delay: %s\n", + argv[0], optarg); + + goto ustusage; } else { - stimeout = t; + chardelay = tl; } break; case 'D': - if (strlen(optarg) != 1 || !(optarg[0] >= '5' && optarg[0] <= '8')) { - fprintf(stderr, "%s: invalid number of data bits: %s\n", argv[0], optarg); - goto ustusage; + if (optarg[1] != '\0' || + !(optarg[0] >= '5' && optarg[0] <= '8')) { + fprintf(stderr, + "%s: invalid number of data bits: %s\n", + argv[0], optarg); + + goto ustusage; } else { - datab = optarg[0]; + datab = optarg[0]; } break; case 'e': @@ -892,95 +937,121 @@ main(int argc, char **argv) parity ^= 2; break; case 'h': half ^= 1; break; - case 'i': - tui = strtoui(optarg, "invalid rx speed: %s\n", 1); - if (tui == uintmax) - goto ustusage; - ispeed = tui; - rxspdset = 1; - break; + case 'i': /* FALLTHROUGH */ case 's': - tui = strtoui(optarg, "invalid speed: %s\n", 1); - if (tui == uintmax) - goto ustusage; - ospeed = tui; + tui = strtoui(optarg, 1); + if (tui == uintmax) { + fprintf(stderr, + "%s: invalid speed: %s\n", + argv[0], optarg); + + goto ustusage; + } + if (c == 'i') { + ispeed = tui; rxspdset = 1; + } else { + ospeed = tui; + ispeed = rxspdset ? ispeed : ospeed; + } break; case 'S': stopb ^= 1; break; case 'l': if (devset) { - fprintf(stderr, "%s: cannot specify multiple devices\n", argv[0]); - goto ustusage; + fprintf(stderr, + "%s: cannot specify multiple devices\n", + argv[0]); + + goto ustusage; } - if (strlen(optarg) > 10) { - fprintf(stderr, "%s: device name too long\n", argv[0]); - goto ustusage; + if (strlen(optarg) > 58) { + fprintf(stderr, "%s: device name too long\n", argv[0]); + goto ustusage; } if (strchr(optarg, '/')) { - strcpy(line, optarg); - devset = 1; + strcpy(line, optarg); + devset = 1; } else { - sprintf(line, "/dev/%s", optarg); - devset = 1; + sprintf(line, "/dev/%s", optarg); + devset = 1; } break; case 'm': - tui = strtoui(optarg, "invalid number of characters: %s\n", 1); - if (tui == uintmax) - goto ustusage; + tui = strtoui(optarg, 1); + if (tui == uintmax) { + fprintf(stderr, + "%s: invalid number of characters: %s\n", + argv[0], optarg); + + goto ustusage; + } minchars = tui; break; case 't': - if (troptions(&tropts, optarg)) - goto ustusage; + if (chcktropts(&tropts, optarg)) { + fprintf(stderr, + "%s: invalid output translation option: %s\n", + argv[0], optarg); + + goto ustusage; + } break; case 'T': - if (troptions(&itropts, optarg)) - goto ustusage; + if (chcktropts(&itropts, optarg)) { + fprintf(stderr, + "%s: invalid input translation option: %s\n", + argv[0], optarg); + + goto ustusage; + } break; case 'v': verbose ^= 1; break; default: ustusage: - die(2, "usage: %s [--line|-l line] [--speed|-s #|-#] [--rx-speed|-i #]\n" - " [--data-bits|-D #] [--stop-bits|-S]" - " [--even|-e] [--odd|-o]\n" - " [--hardware-rts-cts|-R]" - " [--hardware-dtr-dsr|-r] [--software|-X]\n" - " [--delay|-d #] [--min-chars|-m #]" - " [--canonical|-c] [--echo|-h]\n" - " [--translation|-t option]" - " [--input-translation|-T option]\n" - " [--verbose|-v] [--backspace|-b]" - " [--timeout-s|-F #] [--timeout-ns|-f #]", - argv[0]); + die(2, "usage: %s [--line | -l line] " + "[--speed | -s # | -#] [--rx-speed | -i #]\n" + " [--data-bits | -D #] " + "[--stop-bits | -S] [--even | -e] [--odd | -o]\n" + " [--hardware-rts-cts | -R] " + "[--hardware-dtr-dsr | -r] [--software | -X]\n" + " [--min-chars | -m #] [--echo | -h] " + "[--canonical | -c] [--delay | -d #]\n" + " [--translation | -t option] " + "[--input-translation | -T option]\n" + " [--verbose | -v] " + "[--backspace | -b]\n", argv[0]); break; } } - /* - * not the best practice, but in order for this `free()` - * to not get executed ust needs to be killed, - * which means the OS *should* free the memory - */ - free(t); - if (!rxspdset) - ispeed = ospeed; + if (t) + free(t); - if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) + if (isatty(STDIN_FILENO)) interactive = 1; signal(SIGHUP, sighandl); signal(SIGINT, sighandl); signal(SIGQUIT, sighandl); signal(SIGTERM, sighandl); - signal(SIGCHLD, SIG_DFL); fd = openport(); - + if (interactive) pfd = openparent(); + + #ifdef __OpenBSD__ + if (pledge("stdio rpath tty", NULL) < 0) { + perror("pledge"); + die(1, "pledge failed, exiting now\n"); + } + #endif + pthread_t readthread, writethread; pthread_create(&writethread, NULL, writeport, NULL); pthread_create(&readthread, NULL, readport, NULL); pthread_join(writethread, NULL); pthread_join(readthread, NULL); + + /* unreachable */ + return 0; }