-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
#include <getopt.h>
+#include <limits.h>
#include <signal.h>
-#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include <pthread.h>
-#include <fcntl.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
#define LF '\n'
#define BS '\b'
#define DEL '\x7f'
-#ifndef __linux__
+#define EOT '\x4'
+#define ESC '\x1b'
+#if defined(CCDTR_IFLOW) && defined(CDSR_OFLOW)
#define CDTRDSR (CDTR_IFLOW | CDSR_OFLOW)
#endif
#define IXONXOFF (IXON | IXOFF)
+typedef struct {
+ char find;
+ char input;
+ int offset; /* can only be a 1 or a -1 */
+} Args;
+
+typedef struct {
+ ssize_t sizebm;
+ ssize_t sizesm;
+} Sizes;
+
static int gettermattr(int dv, void *strct);
-static int settermattr(int dv, void *strct);
-static void settermspd(unsigned int lispeed, unsigned int lospeed, void *strct);
+static int settermattr(int dv, const void *strct);
+static void settermspd(unsigned int lispeed, unsigned int lospeed, const void *strct);
static int openport();
-static unsigned int strtoui(const char *str, const char *msg, const unsigned int min);
-static int troptions(crlfopt *options, const char *str);
+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 unsigned int lsbmask(unsigned int n);
static inline int termchck(const void *term);
static void interchck();
static void cechck();
-static void sighandl(const int signo);
-static void die(const int code, const char *msg, ...);
static void *writeport(void *unused);
static void *readport(void *unused);
-static inline void replacechar(char *str, ssize_t size, const char find, const char repl);
-static inline ssize_t addchar(char *str, int *scratch, ssize_t size, const size_t bsize, const char find, const char inp, const int offset);
-static inline ssize_t rmchar(char *str, int *scratch, ssize_t size, const char find);
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 void sighandl(int signo);
+static void die(int code, const char *msg, ...);
#ifdef __linux__
static struct termios2 cntrl, origterm = {0}, newterm = {0};
static struct termios cntrl, origterm = {0}, newterm = {0};
#endif
-static const unsigned int uintmax = ~(unsigned int)0;
+static Args bsargs = {DEL, DEL, 0};
+static Args nocrargs = {CR, 0, 0};
+static Args nolfargs = {LF, '\0', 0}; /* null-terminator is for use inside `getcmd()` */
+static Args lfincrargs = {CR, LF, 1};
+static Args crinlfargs = {LF, CR, -1};
+static const unsigned int uintmax = ~(unsigned int)0; /* unsigned -1 to return on error */
static struct timespec wts;
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
-main(int argc, char **argv)
-{
- 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");
- break;
- }
- argv[i] = t;
- free(t);
- }
- }
-
- static struct option longopt[] = {
- {"device", required_argument, NULL, 'l'},
- {"speed", required_argument, NULL, 's'},
- {"rx-speed", required_argument, NULL, 'i'},
- {"echo", no_argument, NULL, 'h'},
- {"canonical", no_argument, 0, 'c'},
- {"odd", no_argument, NULL, 'o'},
- {"even", no_argument, NULL, 'e'},
- {"hardware-rtscts", no_argument, NULL, 'R'},
- {"hardware-dsrdtr", no_argument, NULL, 'r'},
- {"software", no_argument, NULL, 'X'},
- {"data", required_argument, NULL, 'D'},
- {"delay", required_argument, NULL, 'd'},
- {"min-chars", required_argument, NULL, 'm'},
- {"stop-bits", no_argument, NULL, 'S'},
- {"backspace", no_argument, NULL, 'b'},
- {"translation", required_argument, NULL, 't'},
- {"input-translation", required_argument, NULL, 'T'},
- {"verbose", no_argument, NULL, 'v'},
- {NULL, 0, NULL, 0}
- };
-
- unsigned int tui;
- int oind = 0, rxspdset = 0, devset = 0, c;
- while ((c = getopt_long(argc, argv, "bcd:D:ehi:l:m:oRrs:t:T:vX", longopt, &oind)) != -1) {
- switch (c) {
- case 0:
- break;
- case 'b':
- backspace = !backspace; break;
- case 'c':
- canonical = !canonical; break;
- case 'R':
- hard ^= 1; break;
- case 'r':
- hard ^= 2; break;
- case 'X':
- soft = !soft; break;
- case 'd':
- tui = strtoui(optarg, "invalid delay: %s\n", 0);
- if (tui == uintmax)
- goto ustusage;
- chardelay = tui;
- break;
- case 'D':
- if (strlen(optarg) != 1 || !(optarg[0] >= '5' && optarg[0] <= '8')) {
- fprintf(stderr, "invalid number of data bits: %s\n", optarg);
- goto ustusage;
- } else {
- datab = optarg[0];
- }
- break;
- case 'e':
- parity ^= 1; break;
- case 'o':
- parity ^= 2; break;
- case 'h':
- half = !half; break;
- case 'i':
- tui = strtoui(optarg, "invalid rx speed: %s\n", 1);
- if (tui == uintmax)
- goto ustusage;
- ispeed = tui;
- rxspdset = !rxspdset;
- break;
- case 's':
- tui = strtoui(optarg, "invalid speed: %s\n", 1);
- if (tui == uintmax)
- goto ustusage;
- ospeed = tui;
- break;
- case 'S':
- stopb = !stopb; break;
- case 'l':
- if (devset) {
- fprintf(stderr, "cannot specify multiple devices\n");
- goto ustusage;
- }
- if (strlen(optarg) > 10) {
- fprintf(stderr, "device name too long\n");
- goto ustusage;
- }
- if (strchr(optarg, '/')) {
- line[0] = '\0';
- strcpy(line, optarg);
- devset = 1;
- } else {
- line[0] = '\0';
- 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;
- minchars = tui;
- break;
- case 't':
- if (troptions(&tropts, optarg))
- goto ustusage;
- break;
- case 'T':
- if (troptions(&itropts, optarg))
- goto ustusage;
- break;
- case 'v':
- verbose = !verbose; break;
- default:
- ustusage:
- die(2, "usage: ust [--device|-l dev] [--speed|-s #|-#] [--rx-speed|-i #]\n"
- " [--data-bits|-D #] [--stop-bits|-S]"
- " [--even|-e] [--odd|-o]\n"
- " [--hardware-rtscts|-R]"
- " [--hardware-dsrdtr|-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");
- break;
- }
-
-
- }
- if (!rxspdset)
- ispeed = ospeed;
-
- if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))
- interactive = 1;
-
- signal(SIGHUP, sighandl);
- signal(SIGINT, sighandl);
- signal(SIGQUIT, sighandl);
- signal(SIGTERM, sighandl);
-
- 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);
- close(fd);
- return 0;
-
-}
-
int
gettermattr(int dv, void *strct)
{
}
int
-settermattr(int dv, void *strct)
+settermattr(int dv, const void *strct)
{
#ifdef __linux__
struct termios2 *optst = (struct termios2*)strct;
}
void
-settermspd(unsigned int lispeed, unsigned int lospeed, void *strct)
+settermspd(unsigned int lispeed, unsigned int lospeed, const void *strct)
{
#ifdef __linux__
struct termios2 *optst = (struct termios2*)strct;
optst->c_cflag |= BOTHER;
optst->c_ispeed = ispeed;
optst->c_ospeed = ospeed;
-
#else
struct termios *optst = (struct termios*)strct;
cfsetispeed(optst, ispeed);
int
openport()
{
+ if (verbose) fprintf(stderr, "opening \"%s\"\n", line);
+
fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("error opening device");
exit(1);
}
- if (verbose) fprintf(stderr, "opened \"%s\"\n", line);
+ if (verbose) fprintf(stderr, "checking if \"%s\" is a TTY\n", line);
if (!isatty(fd))
die(2, "device \"%s\" is not a TTY\n", line);
-
- if (verbose) fprintf(stderr, "\"%s\" is a TTY\n", line);
- int flags = fcntl(fd, F_GETFL, 0);
- flags &= ~(O_NONBLOCK | O_NDELAY); /* opened to check with non-blocking mode, now set to blocking */
-
- if (fcntl(fd, F_SETFL, flags) != 0)
+ 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 (gettermattr(fd, &cntrl) != 0)
+ if (gettermattr(fd, &cntrl) == -1)
die(1, "failed to get device attributes\n");
if (verbose) fprintf(stderr, "setting baudrate [RX:%u | TX:%u]\n", ispeed, ospeed);
settermspd(ispeed, ospeed, &cntrl);
- if (settermattr(fd, &cntrl) != 0)
+ if (settermattr(fd, &cntrl) == -1)
die(2,"failed to set baudrate [RX:%u | TX:%u]\n", ispeed, ospeed);
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 (parity == 2)
cntrl.c_cflag |= PARENB | PARODD;
- if (settermattr(fd, &cntrl) != 0)
+ if (settermattr(fd, &cntrl) == -1)
die(2, "failed to set parity [even: %d, odd: %d]\n", parity & 1, (parity & 2) >> 1);
if (verbose) {
if (hard == 1) {
cntrl.c_cflag |= CRTSCTS;
} else if (hard == 2) {
- #ifdef __linux__
- fprintf(stderr, "DTR/DSR flow control is not supported on Linux\nenabling this option does nothing!\n");
+ #ifndef CDTRDSR
+ 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
} else if (hard == 3) {
cntrl.c_cflag &= ~CLOCAL;
- #ifndef __linux__
+ #ifdef CCAR_OFLOW
cntrl.c_lflag |= CCAR_OFLOW;
#endif
}
- if (settermattr(fd, &cntrl) != 0)
+ if (settermattr(fd, &cntrl) == -1)
die(2, "failed to set flow control\n");
+ cntrl.c_iflag &= ~(ICRNL | INLCR);
+ cntrl.c_oflag &= ~(OCRNL | ONLRET | ONLCR);
+
if (setroptions())
die(2, "failed to set cr-lf translation options\n");
cntrl.c_cflag &= ~CSIZE;
cntrl.c_cflag |= db;
- if (settermattr(fd, &cntrl) != 0)
+ if (settermattr(fd, &cntrl) == -1)
die(2, "failed to set data bits [%c]\n", datab);
if (verbose) fprintf(stderr, "setting stop bits [%d]\n", stopb+1);
else
cntrl.c_cflag &= ~CSTOPB;
- if (settermattr(fd, &cntrl) != 0)
- die(1, "failed to set stop bits\n");
+ if (settermattr(fd, &cntrl) == -1)
+ die(1, "failed to set stop bits [%d]\n", stopb+1);
#ifdef __linux__
ioctl(fd, TCFLSH, TCIOFLUSH);
}
unsigned int
-strtoui(const char *str, const char *msg, const unsigned int min)
+strtoui(const char *str, const char *msg, unsigned int min)
{
long t;
char *endptr;
}
int
-troptions(crlfopt *options, const char *str)
+troptions(CRLFOpt *options, const char *str)
{
switch (str[0]) {
case 'l':
options->lfincr = !options->lfincr; return 0;
} else if (strcmp(str, "lf-to-cr") == 0) {
options->lftocr = !options->lftocr; return 0;
- } else
+ } 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
+ } 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
+ } else {
goto trerr;
+ }
break;
default:
trerr:
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)
{
struct termios *iterm = (struct termios*)term;
#define TERMIOS_STRUCT termios
#endif
- for (size_t i = 0; i < sizeof(struct TERMIOS_STRUCT); i++) {
+ for (size_t i = 0; i < sizeof(struct TERMIOS_STRUCT); i++)
if (((char*)iterm)[i] != 0)
return 1;
- }
+
return 0;
}
void *
writeport(void *unused)
{
+ Sizes msizes;
struct timespec ts;
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));
escape = 1;
}
- if (backspc != tbackspc)
- replacechar(writebuff, inln, backspc, tbackspc);
-
+ if (bsargs.find != bsargs.input)
+ replacechar(writebuff, inln, &bsargs);
if (tropts.nocr)
- inln = rmchar(writebuff, scratchw, inln, CR);
+ inln = rmchar(writebuff, inln, scratchw, &nocrargs);
if (tropts.nolf)
- inln = rmchar(writebuff, scratchw, inln, LF);
+ inln = rmchar(writebuff, inln, scratchw, &nolfargs);
if (tropts.lfincr)
- inln = addchar(writebuff, scratchw, inln, wbuffsz, CR, LF, 1);
+ 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 {
+ } else {
write(fd, writebuff, 1);
}
}
nanosleep(&ts, NULL);
}
-
- free(writebuff); free(scratchw);
- writebuff = NULL; scratchw = NULL;
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();
+ /* disable stdout buffering */
setvbuf(stdout, NULL, _IONBF, 0);
for (;;) {
ssize_t outln = read(fd, readbuff, rbuffsz - 1);
if (outln > 0) {
if (itropts.nocr)
- outln = rmchar(readbuff, scratchw, outln, CR);
+ outln = rmchar(readbuff, outln, scratchr, &nocrargs);
if (itropts.nolf)
- outln = rmchar(readbuff, scratchw, outln, LF);
+ outln = rmchar(readbuff, outln, scratchr, &nolfargs);
if (itropts.crinlf)
- outln = addchar(readbuff, scratchw, outln, rbuffsz, LF, CR, -1);
+ outln = addchar(readbuff, outln, scratchr, &msizes, &crinlfargs);
if (itropts.lfincr)
- outln = addchar(readbuff, scratchw, outln, rbuffsz, CR, LF, 1);
+ outln = addchar(readbuff, outln, scratchr, &msizes, &lfincrargs);
write(STDOUT_FILENO, readbuff, outln);
}
}
if (isatty(STDIN_FILENO))
settermattr(STDIN_FILENO, &origterm);
-
- free(readbuff); free(scratchr);
- readbuff = NULL; scratchr = NULL;
return NULL;
}
inline void __attribute__((hot))
-replacechar(char *str, ssize_t size, const char find, const char repl)
+replacechar(char *buff, ssize_t size, const Args *args)
{
- for (int i = 0; i < size; i++) {
- if (str[i] == find)
- str[i] = repl;
- }
+ for (int i = 0; i < size; i++)
+ if (buff[i] == args->find)
+ buff[i] = args->input;
}
-/* TODO: optimize the function and allow for offsets greater than 1 */
+
inline ssize_t __attribute__((hot))
-addchar(char *str, int *scratch, ssize_t size, const size_t bsize,const char find, const char inp, const int offset)
+addchar(char *buff, ssize_t size, int *scratch, const Sizes *sizes, const Args *args)
{
- /* turns any signed int into a signed 1
- int toff = offset & (~(uintmax >> 1) | 1)
- */
- int toff = offset;
- 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 (str[i] == find) {
+ if (buff[i] == args->find) {
scratch[c] = i;
c++;
}
+ if (c >= sizes->sizesm)
+ break;
}
if (!c)
return(size);
- if ((size + c) > bsize)
- c = bsize - size;
-
- if (scratch[0] == 0) {
- memmove(&str[0]+1, &str[0], size);
- scratch[0] = 1;
+ if (size == 1 && c == 1 && scratch[0] == 0) {
+ buff[to ^ 1] = args->input;
+ buff[to] = args->find;
+ return 2;
}
- for (int i = c; i > 0; i--) {
- for (int s = size; s >= scratch[i]; s--)
- str[s + toff] = str[s];
- str[scratch[i]] = inp;
+ if ((size + c) > sizes->sizebm)
+ c = sizes->sizebm - size;
+
+ for (int i = c - 1; i >= 0; i--) {
+ int t = scratch[i];
+ 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))
-rmchar(char *str, int *scratch, ssize_t size, const char find)
+rmchar(char *buff, ssize_t size, int *scratch, const Args *args)
{
int c = 0;
for (int i = 0; i < size; i++) {
- if (str[i] == find) {
+ if (buff[i] == args->find) {
scratch[c] = i;
c++;
}
}
if (!c)
return(size);
- for (int i = c; i > 0; i--) {
- for (int s = size; s >= scratch[i]; s--)
- str[s] = str[s + 1];
+ if (size == 1 && c == 1 && scratch[0] == 0)
+ return 0;
+
+ 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[VTIME] = chardelay;
newterm.c_cc[VINTR] = _POSIX_VDISABLE;
newterm.c_cc[VSUSP] = _POSIX_VDISABLE;
newterm.c_cc[VQUIT] = _POSIX_VDISABLE;
if (backspace)
- tbackspc = BS;
+ bsargs.input = BS;
else
- tbackspc = DEL;
+ bsargs.input = DEL;
- backspc = origterm.c_cc[VERASE];
+ bsargs.find = origterm.c_cc[VERASE];
if (settermattr(STDOUT_FILENO, &newterm) < 0)
die(1, "failed to set terminal attributes\n");
void
getcmd(int escape)
{
+ Sizes msizes;
+ msizes.sizesm = scratchwsz;
+ msizes.sizebm = wbuffsz;
+
char cmdchar;
char ttr[64];
unsigned int tspd;
- if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))
+ if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
interactive = 1;
interchck();
cmdchar = writebuff[1];
switch (cmdchar) {
- case '.':
- die(0,"\r\n[EOT]\r\n");
- break;
- case 'b':
- if (backspace)
- tbackspc = DEL;
- else
- tbackspc = BS;
- backspace = !backspace;
- break;
- case 'h':
- if (half)
- newterm.c_lflag &= ~ECHO;
- else
- newterm.c_lflag |= ECHO;
- half = !half;
- if (settermattr(STDOUT_FILENO, &newterm) < 0)
- die(1, "failed to set terminal attributes\n");
- break;
- case 'c':
- if (canonical)
- newterm.c_lflag &= ~ICANON;
- else
- newterm.c_lflag |= ICANON;
- canonical = !canonical;
- if (settermattr(STDOUT_FILENO, &newterm) < 0)
- die(1, "failed to set terminal attributes\n");
- break;
- case 'w':
- if (!half || !canonical) {
- newterm.c_lflag |= ECHO;
- newterm.c_lflag |= ICANON;
- settermattr(STDIN_FILENO, &newterm);
- }
- if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, LF, '\0');
- ssize_t frln;
- int wfd = open(ttr, O_RDONLY);
- if (fd == -1) {
- perror("error opening file");
- break;
- }
-
- while ((frln = read(wfd, writebuff, wbuffsz - 1)) > 0) {
- for (int i = 0; i <= frln; i++) {
- write(fd, &writebuff[i], 1);
- nanosleep(&wts, NULL);
- }
- }
- }
- goto finish;
- case 's':
- cechck();
- if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, LF, '\0');
- 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);
- }
- }
+ case EOT: /* FALLTHROUGH */
+ case '.':
+ die(0,"\n[EOT]\n");
+ break;
+ case 'b':
+ if (backspace)
+ bsargs.input = DEL;
+ else
+ bsargs.input = BS;
+ backspace ^= 1;
+ break;
+ case 'h':
+ if (half)
+ newterm.c_lflag &= ~ECHO;
+ else
+ newterm.c_lflag |= ECHO;
+ half ^= 1;
+ if (settermattr(STDOUT_FILENO, &newterm) < 0)
+ die(1, "failed to set terminal attributes\n");
+ break;
+ case 'c':
+ if (canonical)
+ newterm.c_lflag &= ~ICANON;
+ else
+ newterm.c_lflag |= ICANON;
+ 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, &nolfargs);
+ ssize_t frln;
+ int wfd = open(ttr, O_RDONLY);
+ if (wfd == -1) {
+ perror("error opening file");
+ goto finish;
}
- goto finish;
- case 'd':
- cechck();
- if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, LF, '\0');
- chardelay = strtoui(ttr, "invalid delay\n", 0);
- if (chardelay != uintmax) {
- wts.tv_sec = 0;
- wts.tv_nsec = chardelay;
+ 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);
}
}
- goto finish;
- case 't':
- cechck();
- fprintf(stderr, "additional output translation option: ");
- if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, LF, '\0');
- if(troptions(&tropts, ttr))
- goto finish;
- if (setroptions())
- fprintf(stderr, "could not set new options\n");
-
- }
- goto finish;
- case 'T':
- cechck();
- printf("additional input translation option: ");
- if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
- replacechar(ttr, 63, LF, '\0');
- if (troptions(&itropts, ttr))
- goto finish;
- if (setroptions())
- fprintf(stderr, "could not set new options\n");
- }
- finish:
- if (!half && interactive)
- newterm.c_lflag &= ~ECHO;
- if (!canonical && interactive)
- newterm.c_lflag &= ~ICANON;
- settermattr(STDIN_FILENO, &newterm);
- break;
- case 'p':
- int st;
- struct timespec ts;
- ts.tv_sec = spulsedelay;
- ts.tv_nsec = nspulsedelay;
-
- 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");
+ }
+ goto finish;
+ case 's':
+ cechck();
+ 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);
}
-
- nanosleep(&ts, NULL);
-
- st ^= TIOCM_DTR;
- if (ioctl(fd, TIOCMSET, &st) != 0) {
- fprintf(stderr, "failed to set DTR [negation]\n");
+ }
+ goto finish;
+ case 'd':
+ cechck();
+ 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");
+ }
+ goto finish;
+ case 'T':
+ cechck();
+ printf("additional input 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");
+ }
+ finish:
+ if (!half && interactive)
+ 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");
+ break;
+ case 'p':;
+ int st;
+ struct timespec ts;
+ ts.tv_sec = spulsedelay;
+ ts.tv_nsec = nspulsedelay;
+
+ if (ioctl(fd, TIOCMGET, &st) != 0) {
+ fprintf(stderr, "failed to get port status\n");
break;
- case BS:
- break;
- case DEL:
- break;
- default:
- fprintf(stderr, "not a valid command [%c]\n", cmdchar);
+ }
+ st ^= TIOCM_DTR;
+ 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");
+ break;
+ case BS: /* FALLTHROUGH */
+ case DEL:
+ case ESC:
+ break;
+ default:
+ fprintf(stderr, "not a valid command [%c]\n", cmdchar);
+ break;
}
return;
}
void
-sighandl(const int signo)
+sighandl(int signo)
{
die(128 + signo, "\nrecieved signal [%d], exiting\n", signo);
}
void
-die(const int code, const char *msg, ...)
+die(int code, const char *msg, ...)
{
va_list fpa;
if (fd != -1)
exit(code);
}
+
+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') {
+ asprintf(&t, "-s%s", argv[i] + 1);
+ if (!t) {
+ fprintf(stderr, "cannot convert -# to -s#\n");
+ break;
+ }
+ argv[i] = t;
+ }
+ }
+
+ static struct option longopt[] = {
+ {"line", required_argument, NULL, 'l'},
+ {"speed", required_argument, NULL, 's'},
+ {"rx-speed", required_argument, NULL, 'i'},
+ {"echo", no_argument, NULL, 'h'},
+ {"canonical", no_argument, NULL, 'c'},
+ {"odd", no_argument, NULL, 'o'},
+ {"even", no_argument, NULL, 'e'},
+ {"hardware-rts-cts", no_argument, NULL, 'R'},
+ {"hardware-dtr-dsr", no_argument, NULL, 'r'},
+ {"software", no_argument, NULL, 'X'},
+ {"data", required_argument, NULL, 'D'},
+ {"delay", required_argument, NULL, 'd'},
+ {"min-chars", required_argument, NULL, 'm'},
+ {"stop-bits", no_argument, NULL, 'S'},
+ {"backspace", no_argument, NULL, 'b'},
+ {"translation", required_argument, NULL, 't'},
+ {"input-translation", required_argument, NULL, 'T'},
+ {"verbose", no_argument, NULL, 'v'},
+ {NULL, 0, NULL, 0}
+ };
+
+ 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) {
+ switch (c) {
+ case 0:
+ break;
+ case 'b':
+ backspace ^= 1; break;
+ case 'c':
+ canonical ^= 1; break;
+ case 'R':
+ hard ^= 1; break;
+ case 'r':
+ hard ^= 2; break;
+ case 'X':
+ soft ^= 1; break;
+ case 'd':;
+ char* endptr;
+ long t;
+ t = strtol(optarg, &endptr, 10);
+ if (errno != 0 || *endptr != '\0' || t < 0) {
+ fprintf(stderr, "invalid character delay\n");
+ goto ustusage;
+ } else {
+ chardelay = t;
+ }
+ break;
+ case 'D':
+ if (strlen(optarg) != 1 || !(optarg[0] >= '5' && optarg[0] <= '8')) {
+ fprintf(stderr, "invalid number of data bits: %s\n", optarg);
+ goto ustusage;
+ } else {
+ datab = optarg[0];
+ }
+ break;
+ case 'e':
+ parity ^= 1; break;
+ case 'o':
+ 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 's':
+ tui = strtoui(optarg, "invalid speed: %s\n", 1);
+ if (tui == uintmax)
+ goto ustusage;
+ ospeed = tui;
+ break;
+ case 'S':
+ stopb ^= 1; break;
+ case 'l':
+ if (devset) {
+ fprintf(stderr, "cannot specify multiple devices\n");
+ goto ustusage;
+ }
+ if (strlen(optarg) > 10) {
+ fprintf(stderr, "device name too long\n");
+ goto ustusage;
+ }
+ if (strchr(optarg, '/')) {
+ strcpy(line, optarg);
+ devset = 1;
+ } else {
+ 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;
+ minchars = tui;
+ break;
+ case 't':
+ if (troptions(&tropts, optarg))
+ goto ustusage;
+ break;
+ case 'T':
+ if (troptions(&itropts, 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]\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 (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
+ interactive = 1;
+
+ signal(SIGHUP, sighandl);
+ signal(SIGINT, sighandl);
+ signal(SIGQUIT, sighandl);
+ signal(SIGTERM, sighandl);
+
+ 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);
+}