+/* see LICENSE for license details */
+#include <sys/ioctl.h>
+
#include <errno.h>
#include <fcntl.h>
-#include <getopt.h>
+#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+
+#include <getopt.h>
#include <pthread.h>
-#include <sys/ioctl.h>
#ifdef __linux__
#include <asm/termbits.h>
#include <asm/ioctls.h>
-#else
+#else /* needed for termios2, which is itself needed for non-standard bauds */
#include <termios.h>
#endif
-#include "config.h"
-
#define CR '\r'
#define LF '\n'
#define BS '\b'
#define DEL '\x7f'
+#define EOT '\x4'
#define ESC '\x1b'
+
+#define IXONXOFF (IXON | IXOFF)
+
#if defined(CCDTR_IFLOW) && defined(CDSR_OFLOW)
#define CDTRDSR (CDTR_IFLOW | CDSR_OFLOW)
#endif
-#define IXONXOFF (IXON | IXOFF)
+
+#ifdef __linux__
+ #define TERMIOS_STRUCT termios2
+#else
+ #define TERMIOS_STRUCT termios
+#endif
typedef struct {
- ssize_t sizem;
char find;
char input;
- int offset;
+ int offset; /* can only be a 1 or a -1 */
} Args;
-static int gettermattr(int dv, void *strct);
-static int settermattr(int dv, const void *strct);
-static void settermspd(unsigned int lispeed, unsigned int lospeed, const void *strct);
+typedef struct {
+ ssize_t sizebm;
+ ssize_t sizesm;
+} Sizes;
+
+typedef struct {
+ int crinlf;
+ int crtolf;
+ int lfincr;
+ int lftocr;
+ int nocr;
+ int nolf;
+} CRLFOpt;
+
+/* 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 inline int termchck(const void *term);
+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 void getcmd(int escape);
-static inline void replacechar(char *buff, ssize_t sread, const Args *args);
-static inline ssize_t addchar(char *buff, ssize_t sread, int *scratch, const Args *args);
+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 void sighandl(int signo);
static void die(int code, const char *msg, ...);
-#ifdef __linux__
- static struct termios2 cntrl, origterm = {0}, newterm = {0};
-#else
- static struct termios cntrl, origterm = {0}, newterm = {0};
-#endif
+static struct TERMIOS_STRUCT cntrl, /* device struct */
+ origterm = { 0 }, /* controlling terminal structs */
+ 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 const unsigned int uintmax = ~(unsigned int)0;
static struct timespec wts;
+static const unsigned int uintmax = ~(unsigned int)0; /* unsigned -1 used to return an error */
+static int interactive = 0;
static char *writebuff = NULL;
static char *readbuff = NULL;
static int *scratchr = NULL;
static int *scratchw = NULL;
-static char backspc = DEL,tbackspc = DEL;
static int fd = -1;
-static int interactive = 0;
int
-gettermattr(int dv, void *strct)
+gettermattr(int dv, struct TERMIOS_STRUCT *optst)
{
#ifdef __linux__
- struct termios2 *optst = (struct termios2*)strct;
return ioctl(dv, TCGETS2, optst);
#else
- struct termios *optst = (struct termios*)strct;
return tcgetattr(dv, optst);
#endif
}
int
-settermattr(int dv, const void *strct)
+settermattr(int dv, const struct TERMIOS_STRUCT *optst)
{
#ifdef __linux__
- struct termios2 *optst = (struct termios2*)strct;
return ioctl(dv, TCSETS2, optst);
#else
- struct termios *optst = (struct termios*)strct;
return tcsetattr(dv, TCSANOW, optst);
#endif
}
void
-settermspd(unsigned int lispeed, unsigned int lospeed, const void *strct)
+settermspd(unsigned int lispeed, unsigned int lospeed, struct TERMIOS_STRUCT *optst)
{
#ifdef __linux__
- struct termios2 *optst = (struct termios2*)strct;
-
optst->c_cflag &= ~CBAUD;
optst->c_cflag |= BOTHER;
optst->c_ispeed = ispeed;
optst->c_ospeed = ospeed;
#else
- struct termios *optst = (struct termios*)strct;
cfsetispeed(optst, ispeed);
cfsetospeed(optst, ospeed);
#endif
int
openport()
{
+ int flags;
+ struct flock fl = { 0 };
+
if (verbose) fprintf(stderr, "opening \"%s\"\n", line);
- fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY);
+ fd = open(line, (O_RDWR | O_NOCTTY | O_NDELAY));
if (fd == -1) {
perror("error opening device");
exit(1);
if (!isatty(fd))
die(2, "device \"%s\" is not a TTY\n", line);
- int flags;
- flags = fcntl(fd, F_GETFL, 0);
- /* opened to check with non-blocking mode, now set to blocking */
- flags &= ~(O_NONBLOCK | O_NDELAY);
- if (fcntl(fd, F_SETFL, flags) == -1)
- die(1, "failed to set the device to blocking mode\n");
-
+ if (blocking) {
+ flags = fcntl(fd, F_GETFL, 0);
+ /* opened to check with non-blocking mode, now set to blocking */
+ flags &= ~(O_NONBLOCK | O_NDELAY);
+
+ if (fcntl(fd, F_SETFL, flags) == -1) {
+ 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 (exclusive) {
+ if (fcntl(fd, F_SETLK, &fl) == -1) {
+ 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);
-
+
settermspd(ispeed, ospeed, &cntrl);
if (settermattr(fd, &cntrl) == -1)
die(2,"failed to set baudrate [RX:%u | TX:%u]\n", ispeed, ospeed);
-
+
+ /*
+ * Parity
+ */
cntrl.c_lflag = 0;
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);
-
+ die(2, "failed to set parity [even: %d, odd: %d]\n",\
+ parity & 1, (parity & 2) >> 1);
+
+ /*
+ * Flow control
+ */
if (verbose) {
/* it's so ugly and beautiful at the same time */
int t = (((hard & 1) << 1) & (hard & 2)) >> 1;
if (settermattr(fd, &cntrl) == -1)
die(2, "failed to set flow control\n");
+ /*
+ * CR and LF
+ */
+ cntrl.c_iflag &= ~(ICRNL | INLCR);
+ cntrl.c_oflag &= ~(OCRNL | ONLRET | ONLCR);
+
if (setroptions())
die(2, "failed to set cr-lf translation options\n");
+ /*
+ * Data bits
+ */
if (verbose) fprintf(stderr, "setting data bits [%c]\n", datab);
tcflag_t db = CS8;
if (settermattr(fd, &cntrl) == -1)
die(2, "failed to set data bits [%c]\n", datab);
+ /*
+ * Stop bits
+ */
if (verbose) fprintf(stderr, "setting stop bits [%d]\n", stopb+1);
if (stopb)
if (settermattr(fd, &cntrl) == -1)
die(1, "failed to set stop bits [%d]\n", stopb+1);
+ cntrl.c_cc[VMIN] = 1;
+ cntrl.c_cc[VTIME] = 0;
+
+ /* flush both hardware buffers */
#ifdef __linux__
ioctl(fd, TCFLSH, TCIOFLUSH);
#else
char *endptr;
t = strtol(str, &endptr, 10);
- if (errno != 0 || *endptr != '\0' || t < min) {
+ if (errno != 0 || *endptr != '\0' || t < min || t > uintmax) {
fprintf(stderr, msg, str);
return uintmax;
}
* but almost all compilers will just truncate the value so it's OK
*/
return (unsigned int)t;
- /* termios's `speed_t` is a typedef for `unsigned int` */
}
int
troptions(CRLFOpt *options, const char *str)
{
switch (str[0]) {
- case 'l':
- if (strcmp(str, "lf-in-cr") == 0) {
- options->lfincr = !options->lfincr; return 0;
- } else if (strcmp(str, "lf-to-cr") == 0) {
- options->lftocr = !options->lftocr; return 0;
- } else {
- goto trerr;
- }
- break;
- case 'c':
- if (strcmp(str, "cr-in-lf") == 0) {
- options->crinlf = !options->crinlf; return 0;
- } else if (strcmp(str, "cr-to-lf") == 0) {
- options->crtolf = !options->crtolf; return 0;
- } else {
- goto trerr;
- }
- break;
- case 'n':
- if (strcmp(str, "no-lf") == 0) {
- options->nolf = !options->nolf; return 0;
- } else if (strcmp(str, "no-cr") == 0) {
- options->nocr = !options->nocr; return 0;
- } else {
- goto trerr;
- }
- break;
- default:
- trerr:
- fprintf(stderr, "invalid translation option: %s\n", str);
- return 1;
+ case 'l':
+ if (!strcmp(str, "lf-in-cr")) {
+ options->lfincr ^= 1; return 0;
+ } else if (!strcmp(str, "lf-to-cr")) {
+ options->lftocr ^= 1; return 0;
+ } else {
+ goto trerr;
+ }
+ break;
+ case 'c':
+ if (!strcmp(str, "cr-in-lf")) {
+ options->crinlf ^= 1; return 0;
+ } else if (!strcmp(str, "cr-to-lf")) {
+ options->crtolf ^= 1; return 0;
+ } else {
+ goto trerr;
+ }
+ break;
+ case 'n':
+ if (!strcmp(str, "no-lf")) {
+ options->nolf ^= 1; return 0;
+ } else if (!strcmp(str, "no-cr")) {
+ options->nocr ^= 1; return 0;
+ } else {
+ goto trerr;
+ }
+ break;
+ default:
+ trerr:
+ fprintf(stderr, "invalid translation option: %s\n", str);
+ return 1;
}
}
int
setroptions()
{
- if (itropts.crtolf)
- cntrl.c_iflag |= ICRNL;
- else
- cntrl.c_iflag &= ~ICRNL;
-
- if (itropts.lftocr)
- cntrl.c_iflag |= INLCR;
- else
- cntrl.c_iflag &= ~INLCR;
-
- if (tropts.crtolf)
- cntrl.c_oflag |= OCRNL;
- else
- cntrl.c_oflag &= ~OCRNL;
-
- if (tropts.lftocr)
- cntrl.c_oflag |= ONLRET;
- else
- cntrl.c_oflag &= ~ONLRET;
-
- if (tropts.crinlf)
- cntrl.c_oflag |= ONLCR;
- else
- cntrl.c_oflag &= ~ONLCR;
+ 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));
return settermattr(fd, &cntrl);
}
+inline unsigned int
+lsbmask(unsigned int n)
+{
+ /* if n == 1, n fills with all 1s, 0 stays the same */
+ for (int i = (sizeof(int) * CHAR_BIT); i > 0; i--)
+ n |= (n << 1);
+ return n;
+}
+
inline int
-termchck(const void *term)
+termchck(const struct TERMIOS_STRUCT *term)
{
- #ifdef __linux__
- struct termios2 *iterm = (struct termios2*)term;
- #define TERMIOS_STRUCT termios2
- #else
- struct termios *iterm = (struct termios*)term;
- #define TERMIOS_STRUCT termios
- #endif
for (size_t i = 0; i < sizeof(struct TERMIOS_STRUCT); i++)
- if (((char*)iterm)[i] != 0)
+ if (((char*)term)[i] != 0)
return 1;
return 0;
void *
writeport(void *unused)
{
- struct timespec ts;
+ Sizes msizes;
+ struct timespec timeout, ts;
+
+ timeout.tv_sec = stimeout;
+ timeout.tv_nsec = nstimeout;
ts.tv_sec = swritedelay;
ts.tv_nsec = nswritedelay;
wts.tv_sec = 0;
wts.tv_nsec = chardelay;
+ msizes.sizesm = scratchwsz;
+ msizes.sizebm = wbuffsz;
+
writebuff = malloc(wbuffsz * sizeof(char));
scratchw = malloc(scratchwsz * sizeof(int));
int escape = 0;
-
- Args bsargs, nocrargs, nolfargs, lfincrargs;
- bsargs.sizem = wbuffsz;
- nocrargs = nolfargs = lfincrargs = bsargs;
-
- bsargs.find=backspc; bsargs.input=tbackspc;
-
- nocrargs.find = CR;
- nolfargs.find = LF;
-
- lfincrargs.find = CR; lfincrargs.input = LF; lfincrargs.offset = 1;
-
for (;;) {
ssize_t inln = read(STDIN_FILENO, writebuff, wbuffsz - 1);
if (inln > 0) {
escape = 1;
}
- if (backspc != tbackspc)
+ 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, &lfincrargs);
+ inln = addchar(writebuff, inln, scratchw, &msizes, &lfincrargs);
if (inln > 1) {
for (int i = 0; i <= inln; i++) {
} else {
write(fd, writebuff, 1);
}
+ } else if (inln == 0 && !interactive) {
+ 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;
}
void *
readport(void *unused)
{
+ Sizes msizes;
struct timespec ts;
ts.tv_sec = sreaddelay;
ts.tv_nsec = nsreaddelay;
+ msizes.sizesm = scratchrsz;
+ msizes.sizebm = rbuffsz;
+
readbuff = malloc(rbuffsz * sizeof(char));
scratchr = malloc(scratchrsz * sizeof(int));
interchck();
- Args nocrargs, nolfargs, crinlfargs, lfincrargs;
- nocrargs.sizem = rbuffsz;
- nolfargs = crinlfargs = lfincrargs = nocrargs;
-
- nocrargs.find = CR;
- nolfargs.find = LF;
-
- lfincrargs.find = CR; lfincrargs.input = LF; lfincrargs.offset = 1;
- crinlfargs.find = LF; crinlfargs.input = CR; crinlfargs.offset = -1;
-
-
/* disable stdout buffering */
setvbuf(stdout, NULL, _IONBF, 0);
if (itropts.nolf)
outln = rmchar(readbuff, outln, scratchr, &nolfargs);
if (itropts.crinlf)
- outln = addchar(readbuff, outln, scratchr, &crinlfargs);
+ outln = addchar(readbuff, outln, scratchr, &msizes, &crinlfargs);
if (itropts.lfincr)
- outln = addchar(readbuff, outln, scratchr, &lfincrargs);
+ outln = addchar(readbuff, outln, scratchr, &msizes, &lfincrargs);
write(STDOUT_FILENO, readbuff, outln);
}
nanosleep(&ts, NULL);
}
- if (isatty(STDIN_FILENO))
- settermattr(STDIN_FILENO, &origterm);
+
+ /* unreachable */
return NULL;
}
buff[i] = args->input;
}
-/* TODO: optimize the function and allow for offsets greater than 1 */
inline ssize_t __attribute__((hot))
-addchar(char *buff, ssize_t size, int *scratch, const Args *args)
+addchar(char *buff, ssize_t size, int *scratch, const Sizes *sizes, const Args *args)
{
- int c = 0;
+ int to, c;
+ c = 0;
+ /* turns negative numbers into 1, positive into 0 */
+ to = ((args->offset >> ((sizeof(int) * CHAR_BIT) - 1)) & 1);
for (int i = 0; i < size; i++) {
if (buff[i] == args->find) {
scratch[c] = i;
c++;
}
+ if (c >= sizes->sizesm)
+ break;
}
if (!c)
return(size);
- if ((size + c) > args->sizem)
- c = args->sizem - size;
-
- if (scratch[0] == 0 && args->offset < 0) {
- memmove(&buff[0]+1, &buff[0], size);
- scratch[0] = 1;
+ if (size == 1 && c == 1) {
+ buff[to ^ 1] = args->input;
+ buff[to] = args->find;
+ return 2;
}
- for (int i = c; i > 0; i--) {
+ if ((size + c) > sizes->sizebm)
+ c = sizes->sizebm - size;
+
+ for (int i = c - 1; i >= 0; i--) {
int t = scratch[i];
- for (int s = size; s >= t;s--)
- buff[s + args->offset] = buff[s];
- buff[t] = args->input;
+ memmove(&buff[t] + 1, &buff[t], (size + c) - t);
+ buff[t + (to ^ 1)] = args->input;
}
+ printf("%s\n", buff);
return (size + c);
}
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;
}
if (!c)
return(size);
+ if (size == 1 && c == 1)
+ return 0;
- for (int i = c; i > 0; i--)
- for (int s = size; s >= scratch[i]; s--)
- buff[s] = buff[s + 1];
-
+ for (int i = c; i >= 0; i--) {
+ int t = scratch[i];
+ memmove(&buff[t], &buff[t] + 1, (size + c) - t);
+ }
return(size - c);
}
newterm.c_iflag |= INLCR;
newterm.c_cc[VMIN] = minchars;
- newterm.c_cc[VINTR] = _POSIX_VDISABLE;
- newterm.c_cc[VSUSP] = _POSIX_VDISABLE;
- newterm.c_cc[VQUIT] = _POSIX_VDISABLE;
+ 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;
+
+ #ifdef VDSUSP
+ newterm.c_cc[VDSUSP] = _POSIX_VDISABLE;
+ #endif
if (backspace)
- tbackspc = BS;
+ bsargs.input = BS;
else
- tbackspc = DEL;
-
- backspc = origterm.c_cc[VERASE];
+ bsargs.input = DEL;
+ bsargs.find = origterm.c_cc[VERASE];
+
if (settermattr(STDOUT_FILENO, &newterm) < 0)
die(1, "failed to set terminal attributes\n");
}
void
getcmd(int escape)
{
- Args rmlf;
+ Sizes msizes;
char cmdchar;
char ttr[64];
unsigned int tspd;
- rmlf.find = LF; rmlf.input = '\0';
+ msizes.sizesm = scratchwsz;
+ msizes.sizebm = wbuffsz;
- if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))
+ if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
interactive = 1;
interchck();
cmdchar = writebuff[1];
switch (cmdchar) {
+ case EOT: /* FALLTHROUGH */
case '.':
die(0,"\n[EOT]\n");
- break;
case 'b':
if (backspace)
- tbackspc = DEL;
+ bsargs.input = DEL;
else
- tbackspc = BS;
- backspace = !backspace;
+ bsargs.input = BS;
+ backspace ^= 1;
break;
case 'h':
if (half)
newterm.c_lflag &= ~ECHO;
else
newterm.c_lflag |= ECHO;
- half = !half;
+ half ^= 1;
if (settermattr(STDOUT_FILENO, &newterm) < 0)
die(1, "failed to set terminal attributes\n");
break;
newterm.c_lflag &= ~ICANON;
else
newterm.c_lflag |= ICANON;
- canonical = !canonical;
+ canonical ^= 1;
if (settermattr(STDOUT_FILENO, &newterm) < 0)
die(1, "failed to set terminal attributes\n");
break;
case 'w':
cechck();
if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, &rmlf);
+ replacechar(ttr, 63, &nolfargs);
ssize_t frln;
int wfd = open(ttr, O_RDONLY);
- if (fd == -1) {
+ if (wfd == -1) {
perror("error opening file");
- break;
+ 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);
+
write(fd, &writebuff[i], 1);
nanosleep(&wts, NULL);
}
case 's':
cechck();
if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, &rmlf);
+ replacechar(ttr, 63, &nolfargs);
tspd = strtoui(ttr, "invalid speed\n", 0);
if (tspd != uintmax) {
ospeed = ispeed = tspd;
case 'd':
cechck();
if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, &rmlf);
+ replacechar(ttr, 63, &nolfargs);
chardelay = strtoui(ttr, "invalid delay\n", 0);
if (chardelay != uintmax) {
wts.tv_sec = 0;
cechck();
fprintf(stderr, "additional output translation option: ");
if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, &rmlf);
+ replacechar(ttr, 63, &nolfargs);
if(troptions(&tropts, ttr))
goto finish;
if (setroptions())
cechck();
printf("additional input translation option: ");
if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, &rmlf);
+ replacechar(ttr, 63, &nolfargs);
if (troptions(&itropts, ttr))
goto finish;
if (setroptions())
int
main(int argc, char **argv)
{
+ 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') {
- char *t = NULL;
- /* glibc's `asprintf()` won't set the string to NULL on failure automatically */
asprintf(&t, "-s%s", argv[i] + 1);
if (!t) {
- fprintf(stderr, "cannot convert -# to -s#\n");
+ fprintf(stderr, "%s: cannot convert -# to -s#\n", argv[0]);
break;
}
argv[i] = t;
- free(t);
}
}
{"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'},
unsigned int tui;
int oind, rxspdset, devset, c;
oind = rxspdset = devset = 0;
- while ((c = getopt_long(argc, argv, "bcd:D:ehi:l:m:oRrs:t:T:vX", longopt, &oind)) != -1) {
+ while ((c = getopt_long(argc, argv, "D:F:T:d:f:i:l:m:s:t:RXbcehorv", longopt, &oind)) != -1) {
switch (c) {
case 0:
break;
hard ^= 2; break;
case 'X':
soft ^= 1; break;
- case 'd':;
+ 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, "invalid character delay\n");
+ fprintf(stderr, "%s: invalid character delay: %s\n", argv[0], optarg);
goto ustusage;
- } else {
+ } else if (c == 'd') {
chardelay = t;
+ } else if (c == 'f'){
+ nstimeout = t;
+ } else {
+ stimeout = t;
}
break;
case 'D':
if (strlen(optarg) != 1 || !(optarg[0] >= '5' && optarg[0] <= '8')) {
- fprintf(stderr, "invalid number of data bits: %s\n", optarg);
+ fprintf(stderr, "%s: invalid number of data bits: %s\n", argv[0], optarg);
goto ustusage;
} else {
datab = optarg[0];
stopb ^= 1; break;
case 'l':
if (devset) {
- fprintf(stderr, "cannot specify multiple devices\n");
+ fprintf(stderr, "%s: cannot specify multiple devices\n", argv[0]);
goto ustusage;
}
if (strlen(optarg) > 10) {
- fprintf(stderr, "device name too long\n");
+ fprintf(stderr, "%s: device name too long\n", argv[0]);
goto ustusage;
}
if (strchr(optarg, '/')) {
" [--hardware-dtr-dsr|-r] [--software|-X]\n"
" [--delay|-d #] [--min-chars|-m #]"
" [--canonical|-c] [--echo|-h]\n"
- " [--translation|-t tropt]"
- " [--input-translation|-T tropt]\n"
- " [--verbose|-v] [--backspace|-b]\n",
+ " [--translation|-t option]"
+ " [--input-translation|-T option]\n"
+ " [--verbose|-v] [--backspace|-b]"
+ " [--timeout-s|-F #] [--timeout-ns|-f #]",
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 (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))
+ if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
interactive = 1;
signal(SIGHUP, sighandl);
signal(SIGINT, sighandl);
signal(SIGQUIT, sighandl);
signal(SIGTERM, sighandl);
+ signal(SIGCHLD, SIG_DFL);
fd = openport();
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;
}