]> datadissipation.net git - ust.git/commitdiff
changes in define macros and switches, improved piping support
authorgit <redacted>
Fri, 20 Feb 2026 19:14:53 +0000 (14:14 -0500)
committergit <redacted>
Fri, 20 Feb 2026 19:14:53 +0000 (14:14 -0500)
moved $TARGET to config.mk

Makefile
config.def.h
config.mk
ust.1
ust.c

index 068f0fa3914f81318f2f559ddc71ce9421099830..d30144b822c0f0bed9fb25f67c9de682e76dc14b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,5 @@
 include config.mk
 
-TARGET = ust
 SRC = ust.c
 OBJ = ${SRC:.c=.o}
 
@@ -19,8 +18,8 @@ install: all
        cp -f ${TARGET} ${PREFIX}/bin
        chmod 755 ${PREFIX}/bin/${TARGET}
        mkdir -p ${MANPREFIX}/man1
-       sed "s/VERSION/${VERSION}/" < ${TARGET}.1 > ${MANPREFIX}/man1/${TARGET}.1
-       chmod 644 ${MANPREFIX}/man1/ust.1
+       sed "s/VERSION/${VERSION}/" < ust.1 > ${MANPREFIX}/man1/${TARGET}.1
+       chmod 644 ${MANPREFIX}/man1/${TARGET}.1
 
 uninstall:
        rm -f ${PREFIX}/bin/${TARGET} ${MANPREFIX}/man1/${TARGET}.1
index 1e68dd1167d32395a42f306a021aed1ec65920ff..ddb69f9fd96411896205d1ba22a3396da000f46e 100644 (file)
@@ -28,6 +28,10 @@ const char escapechar = '~';
 /* print additional info to stderr */
 int verbose = 0; /*(0|1)*/
 
+/* exit timeout after piped ust reaches EOF */
+long stimeout = 0;
+long nstimeout = 1000;
+
 /*
  * device name
  *
index 5a1e32518195915c9f9cfecc8760b5df32586a89..36893ea88a9f8613c2cccfcb7a41b4ef75a3f08c 100644 (file)
--- a/config.mk
+++ b/config.mk
@@ -1,5 +1,7 @@
 VERSION = 0.2.1
 
+TARGET = ust
+
 PREFIX = /usr/local
 MANPREFIX = ${PREFIX}/share/man
 
diff --git a/ust.1 b/ust.1
index fd7c0e2256c3e3837bdcdc7da9618659b230ccac..dd7c132ed80b2061d3c040bba4eed148c290e798 100644 (file)
--- a/ust.1
+++ b/ust.1
@@ -17,6 +17,8 @@
 .Op Fl \-hardware\-dtr\-dsr|\-R
 .Op Fl \-software|\-X
 .Op Fl \-delay|\-d Ar #
+.Op Fl \-timeout\-ns|\-f Ar #
+.Op Fl \-timeout\-s|\-F Ar #
 .Op Fl \-min\-chars|\-m Ar #
 .Op Fl \-canonical|\-c
 .Op Fl \-echo|\-h
@@ -43,9 +45,9 @@ This way the user might set a default config for a specific device without any a
 .Bl -tag -width ".Fl \-line|\-l Ar line"
 .It Fl \-line|\-l Ar line
 The device name can be input in two formats:
-.Ar '/dev/cua00'
+.Ar /dev/cua00
 or
-.Ar `cua00` .
+.Ar cua00 .
 .It Fl \-speed|\-s Ar # | Ar \-#
 Baudrate set by this flag also applies to RX baud if that wasn't explicitly set.
 .It Fl \-rx\-speed|\-i Ar #
@@ -74,6 +76,17 @@ Toggle DTR/DSR flow control (setting both hardware flow control options will ena
 Toggle XON/XOFF software flow control (can be enabled with hardware flow control simultaneously).
 .It Fl \-delay|\-d Ar #
 Delay between sending multiple characters (when redirecting stdin or writing a file) to the line in nanoseconds.
+.It Fl \-timeout\-ns|\-f Ar #
+With stdin redirected,
+.Nm
+will exit upon reaching 
+.Ar EOF ,
+this option sets a delay (in nanoseconds) after
+.Ar EOF
+encounter and before program exit.
+.It Fl \-timeout\-s|\-F Ar #
+.Ar EOF
+exit delay (in seconds).
 .It Fl \-min\-chars|\-m Ar #
 The minimum of characters to input before they get sent to the line.
 .It Fl \-canonical|\-c
@@ -112,7 +125,7 @@ Set new speed (both TX and RX).
 .It Ar d
 Set new character delay.
 .It Ar b
-Toggle backspace character
+Toggle the backspace key character
 .It Ar t No | Ar T
 Toggle a translation option (output|input).
 .It Ar c
@@ -126,11 +139,11 @@ Send a DCD pulse.
 .Sh EXIT STATUS
 .Pp
 .Nm 
-exits 2 if some of the user\-set options were invalid and exits 1 on general errors.
+exits 2 if some of the user\-set options were invalid (errors caused by trying to aply unsupported options to hardware, eg., setting unsupported baud, are considered as such) and exits 1 on general errors.
 .Sh EXAMPLES
 .Ed
 .Pp
-Pipe the utility and write output to a file.
+Pipe the utility and write output to a file:
 .Bd -literal -offset indent
 $ printf "00,78,FFFF\\r45\\r" | ust \-19200 \-d 9000000 > wozmon.out
 $ cat wozmon.out
diff --git a/ust.c b/ust.c
index 4517e0c37770ba97095e6f2eb4fb5072005402d2..2aa9a240a208c6a152468b10a668070cc6389af7 100644 (file)
--- a/ust.c
+++ b/ust.c
 #define DEL '\x7f'
 #define EOT '\x4'
 #define ESC '\x1b'
+
+#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
-#define IXONXOFF (IXON | IXOFF)
 
 typedef struct {
        char find;
@@ -54,9 +62,9 @@ typedef struct{
 /* including here to access CRLFOpt */
 #include "config.h"
 
-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);
+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);
@@ -74,62 +82,51 @@ static inline ssize_t rmchar(char *buff, ssize_t sread, int *scratch, const 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, origterm = { 0 }, newterm = { 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 const Args nocrargs = {CR, 0, 0},
+                  nolfargs = {LF, '\0', 0}, /* null-terminator is for use inside `getcmd()` */
+                  lfincrargs = {CR, LF, 1},
+                  crinlfargs = {LF, CR, -1};
 static struct timespec wts;
+static const unsigned int uintmax = ~(unsigned int)0; /* unsigned -1 to return on error */
+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 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
@@ -177,13 +174,18 @@ openport()
                        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);
@@ -198,6 +200,10 @@ openport()
        if (settermattr(fd, &cntrl) == -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;
@@ -230,12 +236,18 @@ openport()
        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;
@@ -255,6 +267,9 @@ openport()
        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)
@@ -267,7 +282,8 @@ openport()
 
        cntrl.c_cc[VMIN] = 1;
        cntrl.c_cc[VTIME] = 0;
-
+       
+       /* flush both hardware buffers */
        #ifdef __linux__
         ioctl(fd, TCFLSH, TCIOFLUSH);
        #else
@@ -292,7 +308,6 @@ strtoui(const char *str, const char *msg, unsigned int min)
         * 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
@@ -357,15 +372,8 @@ lsbmask(unsigned int n)
 inline int
 termchck(const void *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;
@@ -375,8 +383,10 @@ void *
 writeport(void *unused)
 {
        Sizes msizes;
-       struct timespec ts;
+       struct timespec timeout, ts;
 
+       timeout.tv_sec = stimeout;
+       timeout.tv_nsec = nstimeout;
        ts.tv_sec = swritedelay;
        ts.tv_nsec = nswritedelay;
        wts.tv_sec = 0;
@@ -425,9 +435,14 @@ writeport(void *unused)
                        } 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, "\n[EOF]\n");
        return NULL;
 }
 
@@ -618,7 +633,6 @@ getcmd(int escape)
        case EOT: /* FALLTHROUGH */
        case '.':
                die(0,"\n[EOT]\n");
-               break;
        case 'b':
                if (backspace)
                        bsargs.input = DEL;
@@ -796,7 +810,7 @@ main(int argc, char **argv)
                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");
+                               fprintf(stderr, "%s: cannot convert -# to -s#\n", argv[0]);
                                break;
                        }
                        argv[i] = t;
@@ -817,6 +831,8 @@ 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'},
@@ -828,7 +844,7 @@ main(int argc, char **argv)
        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;
@@ -842,20 +858,26 @@ main(int argc, char **argv)
                        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];
@@ -884,11 +906,11 @@ main(int argc, char **argv)
                        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, '/')) {
@@ -926,7 +948,8 @@ main(int argc, char **argv)
                                        " [--canonical|-c] [--echo|-h]\n"
                                        "           [--translation|-t option]"
                                        " [--input-translation|-T option]\n"
-                                       "           [--verbose|-v] [--backspace|-b]\n",
+                                       "           [--verbose|-v] [--backspace|-b]"
+                                       " [--timeout-s|-F #] [--timeout-ns|-f #]",
                                        argv[0]);
                        break;
                }