]> datadissipation.net git - ust.git/commitdiff
interactive mode improvements
authorgit <redacted>
Sat, 11 Apr 2026 15:49:52 +0000 (11:49 -0400)
committergit <redacted>
Sat, 11 Apr 2026 15:49:52 +0000 (11:49 -0400)
Makefile
config.mk
ust.1
ust.c

index d30144b822c0f0bed9fb25f67c9de682e76dc14b..2335159f7bd5e7d19238c74e599b1d7f7b9672b6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
 include config.mk
 
+TARGET = ust
 SRC = ust.c
 OBJ = ${SRC:.c=.o}
 
index 31f86192e3fe7e4689fe074f3da5c1d3667aa6d5..33b391df4891cd966e08ef855768a200bd2bbcbb 100644 (file)
--- a/config.mk
+++ b/config.mk
@@ -1,6 +1,4 @@
-VERSION = 0.2.3
-
-TARGET = ust
+VERSION = 0.2.4
 
 PREFIX = /usr/local
 MANPREFIX = ${PREFIX}/share/man
diff --git a/ust.1 b/ust.1
index 9d7e694bf3ac68fca950c157f78d36b1dc8ec5d6..42a5a16fa4ff6ce4e6fa32013897f1e067e08862 100644 (file)
--- a/ust.1
+++ b/ust.1
@@ -97,7 +97,8 @@ Toggle translation options (from the line) for CR an LF, the options are:
 .Sh USAGE
 If the first character in the input buffer is the special character (tilde by default, can be changed in 
 .Ar config.h )
-it will be escaped and the next character may run a command.
+it will be escaped and the next character may run a command. The escape character and all subsequent characters
+do not get sent to the line.
 .Bl -tag -width ".Fl \&. No or Ar \&^D"
 .It Ar \&. No or Ar \&^D
 Drop the connection and exit.
diff --git a/ust.c b/ust.c
index 8be0ce784d782fbf5dda33fd0fd3c190e4d6024b..a9e463336b4ff7e53d84f468542950680a539aa9 100644 (file)
--- a/ust.c
+++ b/ust.c
@@ -17,7 +17,7 @@
 #ifdef __linux__
  #include <asm/termbits.h>
  #include <asm/ioctls.h>
-#else /* needed for termios2, which is itself needed for non-standard bauds */
+#else /* including for termios2, which is needed for non-standard bauds */
  #include <termios.h>
 #endif
 
 #endif
 
 #ifdef __linux__
- #define TERMIOS_STRUCT termios2
+ typedef struct termios2 term_struct;
 #else
- #define TERMIOS_STRUCT termios
+ typedef struct termios  term_struct;
 #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 {
@@ -63,48 +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 gettermattr(int dv, term_struct *optst);
+static int settermattr(int dv, const term_struct *optst);
+static void settermspd(unsigned int lispeed, unsigned int lospeed,
+                       term_struct *optst);
 static int openport(void);
+static int openparent(void);
 static unsigned int strtoui(const char *str, unsigned int min);
-static int troptions(CRLFOpt *options, const char *str);
-static int setroptions(void);
+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(void);
-static void cechck(void);
+static inline int termvalid(const term_struct *term);
+static void eninter(void);
+static void enechocan(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 term_struct 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, term_struct *optst)
 {
         #ifdef __linux__
          return ioctl(dv, TCGETS2, optst);
@@ -114,7 +122,7 @@ gettermattr(int dv, struct TERMIOS_STRUCT *optst)
 }
 
 int
-settermattr(int dv, const struct TERMIOS_STRUCT *optst)
+settermattr(int dv, const term_struct *optst)
 {
        #ifdef __linux__
         return ioctl(dv, TCSETS2, optst);
@@ -124,7 +132,7 @@ 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, term_struct *optst)
 {
        #ifdef __linux__
         optst->c_cflag &= ~CBAUD;
@@ -144,9 +152,9 @@ openport(void)
        struct flock fl = { 0 };
 
        #ifdef __OpenBSD__
-        if (pledge("stdio rpath wpath tty flock", NULL) == -1) {
+        if (pledge("stdio rpath wpath tty flock", NULL) < 0) {
                  perror("pledge");
-                 die(1, "failed to set up pledge, exiting now\n");
+                 die(1, "pledge failed, exiting now\n");
         }
        #endif
 
@@ -165,22 +173,21 @@ openport(void)
 
        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) {
+               if (fcntl(fd, F_SETFL, flags) < 0) {
                        perror("fcntl");
                        die(1, "exiting now\n");
                }
        }
 
-       if (gettermattr(fd, &cntrl) == -1)
+       if (gettermattr(fd, &cntrl) < 0)
                 die(1, "failed to get device attributes\n");
 
        if (exclusive) {
                fl.l_type = F_WRLCK;
                fl.l_whence = SEEK_SET;
-               if (fcntl(fd, F_SETLK, &fl) == -1) {
+               if (fcntl(fd, F_SETLK, &fl) < 0) {
                        perror("fcntl");
                        die(1, "failed to lock the device, exiting now\n");
                }
@@ -192,7 +199,7 @@ openport(void)
        if (verbose) fprintf(stderr, "setting baudrate [RX:%u | TX:%u]\n", ispeed, ospeed);
 
        settermspd(ispeed, ospeed, &cntrl);
-       if (settermattr(fd, &cntrl) == -1)
+       if (settermattr(fd, &cntrl) < 0)
                 die(2,"failed to set baudrate [RX:%u | TX:%u]\n",  ispeed, ospeed);
        
        /*
@@ -210,7 +217,7 @@ openport(void)
        if (parity == 2)
                cntrl.c_cflag |= PARENB | PARODD;
 
-       if (settermattr(fd, &cntrl) == -1)
+       if (settermattr(fd, &cntrl) < 0)
                die(2, "failed to set parity [even: %d, odd: %d]\n",\
                                parity & 1, (parity & 2) >> 1);
 
@@ -246,7 +253,7 @@ openport(void)
                #endif
        }
 
-       if (settermattr(fd, &cntrl) == -1)
+       if (settermattr(fd, &cntrl) < 0)
                die(2, "failed to set flow control\n");
 
        /*
@@ -255,7 +262,7 @@ openport(void)
        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");
 
        /*
@@ -277,7 +284,7 @@ openport(void)
        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);
        
        /*
@@ -290,10 +297,10 @@ openport(void)
        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 */
@@ -302,7 +309,28 @@ openport(void)
        #else
         tcflush(fd, TCIOFLUSH);
        #endif
-       return(fd);
+       /* returning only for readability */
+       return fd;
+}
+
+int
+openparent(void)
+{
+       if (verbose) fprintf(stderr, "opening parent terminal\n");
+
+       pfd = open("/dev/tty", O_RDWR);
+       if (pfd == -1) {
+               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
@@ -315,15 +343,12 @@ strtoui(const char *str, unsigned int min)
        if (errno != 0 || *endptr != '\0' || t < min || t > uintmax) {
                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':
@@ -356,7 +381,7 @@ troptions(CRLFOpt *options, const char *str)
 }
 
 int
-setroptions(void)
+settropts(void)
 {
        cntrl.c_iflag ^= (ICRNL  & lsbmask(itropts.crtolf));
        cntrl.c_iflag ^= (INLCR  & lsbmask(itropts.lftocr));
@@ -377,9 +402,9 @@ lsbmask(unsigned int n)
 }
 
 inline int
-termchck(const struct TERMIOS_STRUCT *term)
+termvalid(const term_struct *term)
 {
-       for (size_t i = 0; i < sizeof(struct TERMIOS_STRUCT); i++)
+       for (size_t i = 0; i < sizeof(term_struct); i++)
                if (((char*)term)[i] != 0)
                        return 1;
 
@@ -415,12 +440,14 @@ writeport(void *unused)
                        if (escape) {
                                getcmd(escape);
                                escape = 0;
+                               inln = 0;
                        }
                        if (writebuff[0] == escapechar) {
                                if (inln > 1)
-                                       getcmd(escape);
-                               else
-                                       escape = 1; 
+                                       getcmd(escape); 
+                               else 
+                                       escape = 1;
+                               inln = 0;
                        }
 
                        if (bsargs.find != bsargs.input)
@@ -439,7 +466,7 @@ writeport(void *unused)
                                }
 
                        } else {
-                               rwc -= write(fd, writebuff, 1);
+                               rwc -= write(fd, writebuff, inln);
                        }
 
                } else if (inln == 0 && !interactive) {
@@ -473,7 +500,7 @@ readport(void *unused)
        if (scratchr == NULL)
                die(1, "scratch buffer allocation failed\n");
        
-       interchck();
+       eninter();
 
        /* disable stdout buffering */
        setvbuf(stdout, NULL, _IONBF, 0);
@@ -564,13 +591,11 @@ rmchar(char *buff, ssize_t size, int *scratch, const Args *args)
 }
 
 void
-interchck(void)
+eninter(void)
 {
        if (interactive) {
-               if (gettermattr(STDIN_FILENO, &origterm) < 0 )
-                       die(1, "failed to get terminal attributes\n");
-
-               newterm = origterm;
+               if (pfd < 0)
+                       pfd = openparent();
 
                if (!canonical)
                        newterm.c_lflag &= ~ECHO;
@@ -598,20 +623,20 @@ interchck(void)
 
                bsargs.find = origterm.c_cc[VERASE];
                
-               if (settermattr(STDOUT_FILENO, &newterm) < 0)
+               if (settermattr(pfd, &newterm) < 0)
                        die(1, "failed to set terminal attributes\n");
        }
        return;
 }
 
 void
-cechck(void)
+enechocan(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 and/or canonical mode\n");
        }
        return;
 }
@@ -627,10 +652,10 @@ getcmd(int escape)
        msizes.sizesm = scratchwsz;
        msizes.sizebm = wbuffsz;
 
-       if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
+       if (isatty(STDIN_FILENO))
                interactive = 1;
 
-       interchck();
+       eninter();
 
        if (escape)
                cmdchar = writebuff[0];
@@ -654,7 +679,7 @@ getcmd(int escape)
                else
                        newterm.c_lflag |= ECHO;
                half ^= 1;
-               if (settermattr(STDOUT_FILENO, &newterm) < 0)
+               if (settermattr(pfd, &newterm) < 0)
                        die(1, "failed to set terminal attributes\n");
                break;
        case 'c':
@@ -663,11 +688,11 @@ getcmd(int escape)
                else
                        newterm.c_lflag |= ICANON;
                canonical ^= 1;
-               if (settermattr(STDOUT_FILENO, &newterm) < 0)
+               if (settermattr(pfd, &newterm) < 0)
                        die(1, "failed to set terminal attributes\n");
                break;
        case 'w':
-               cechck();
+               enechocan();
                if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
                        ssize_t frln;
                        int wfd = open(ttr, O_RDONLY);
@@ -693,14 +718,14 @@ getcmd(int escape)
                }
                goto finish;
        case 's':
-               cechck();
+               enechocan();
                if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
                        replacechar(ttr, 63, &nolfargs);
                        tspd = strtoui(ttr, 0);
                        if (tspd != uintmax) {
                                ospeed = ispeed = tspd;
                                settermspd(ispeed, ospeed, &cntrl);
-                               if (settermattr(fd, &cntrl) != 0)
+                               if (settermattr(fd, &cntrl) < 0)
                                        fprintf(stderr, "failed to set baudrate "
                                                        "[RX:%u | TX:%u]\n", ispeed, ospeed);
                        } else {
@@ -709,7 +734,7 @@ getcmd(int escape)
                }
                goto finish;
        case 'd':
-               cechck();
+               enechocan();
                if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
                        replacechar(ttr, 63, &nolfargs);
                        chardelay = strtoui(ttr, 0);
@@ -722,28 +747,28 @@ getcmd(int escape)
                }
                goto finish;
        case 't':
-               cechck();
+               enechocan();
                fprintf(stderr, "additional output translation option: ");
                if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
                        replacechar(ttr, 63, &nolfargs);
-                       if(troptions(&tropts, ttr)) {
+                       if (chcktropts(&tropts, ttr)) {
                                fprintf(stderr, "invalid output translation option: %s\n", optarg);
                                goto finish;
                        }
-                       if (setroptions())
+                       if (settropts())
                                fprintf(stderr, "could not set new options\n");         
                }
                goto finish;
        case 'T':
-               cechck();
+               enechocan();
                printf("additional input translation option: ");
                if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
                        replacechar(ttr, 63, &nolfargs);
-                       if (troptions(&itropts, ttr)) {
+                       if (chcktropts(&itropts, ttr)) {
                                fprintf(stderr, "invalid input translation option: %s\n", optarg);
                                goto finish;
                        }       
-                       if (setroptions())
+                       if (settropts())
                                fprintf(stderr, "could not set new options\n"); 
                }
                finish:
@@ -751,7 +776,7 @@ getcmd(int escape)
                        newterm.c_lflag &= ~ECHO;
                if (!canonical && interactive)
                        newterm.c_lflag &= ~ICANON;
-               if (settermattr(STDIN_FILENO, &newterm) == -1)
+               if (settermattr(pfd, &newterm) < 0)
                        fprintf(stderr, "failed to restore echo and/or canonical mode\n");
                break;
        case 'p':;
@@ -760,19 +785,19 @@ getcmd(int escape)
                ts.tv_sec = spulsedelay;
                ts.tv_nsec = nspulsedelay;
 
-               if (ioctl(fd, TIOCMGET, &st) != 0) {
+               if (ioctl(fd, TIOCMGET, &st) < 0) {
                        fprintf(stderr, "failed to get port status\n");
                        break;
                }
                st ^= TIOCM_DTR;
-               if (ioctl(fd, TIOCMSET, &st) != 0) {
+               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) 
+               if (ioctl(fd, TIOCMSET, &st) < 0) 
                        fprintf(stderr, "failed to set DTR [negation]\n");
                break;
        case BS: /* FALLTHROUGH */
@@ -803,6 +828,10 @@ die(int code, const char *msg, ...)
                if (t == rwc) break;
        }
 
+       if (termvalid(&origterm))
+               settermattr(pfd, &origterm);
+       if (pfd != -1)
+               close(pfd);
        if (fd != -1)
                close(fd);
 
@@ -815,9 +844,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);
@@ -829,8 +855,8 @@ int
 main(int argc, char *argv[])
 {
        long tl;
-       char *t;
-       char *endptr;
+       char *t = NULL;
+       char *endptr = NULL;
        unsigned int tui;
        int oind, rxspdset, devset, c;
        oind = rxspdset = devset = 0;
@@ -947,13 +973,13 @@ main(int argc, char *argv[])
                        minchars = tui;
                        break;
                case 't':
-                       if (troptions(&tropts, optarg)) {
+                       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)) {
+                       if (chcktropts(&itropts, optarg)) {
                                fprintf(stderr, "%s: invalid input translation option: %s\n", argv[0], optarg);
                                goto ustusage;
                        }
@@ -962,31 +988,25 @@ main(int argc, char *argv[])
                        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",
+                       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 (t)
+               free(t);
 
-       if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
+       if (isatty(STDIN_FILENO))
                interactive = 1;
 
        signal(SIGHUP, sighandl);
@@ -995,7 +1015,8 @@ main(int argc, char *argv[])
        signal(SIGTERM, sighandl);
 
        fd = openport();
-       
+       if (interactive) pfd = openparent();
+
        pthread_t readthread, writethread;
        pthread_create(&writethread, NULL, writeport, NULL);
        pthread_create(&readthread, NULL, readport, NULL);