11 #include <sys/ioctl.h>
14 #include <asm/termbits.h>
15 #include <asm/ioctls.h>
26 #if defined(CCDTR_IFLOW) && defined(CDSR_OFLOW)
27 #define CDTRDSR (CDTR_IFLOW | CDSR_OFLOW)
29 #define IXONXOFF (IXON | IXOFF)
31 static int gettermattr(int dv
, void *strct
);
32 static int settermattr(int dv
, void *strct
);
33 static void settermspd(unsigned int lispeed
, unsigned int lospeed
, void *strct
);
34 static int openport();
35 static unsigned int strtoui(const char *str
, const char *msg
, const unsigned int min
);
36 static int troptions(crlfopt
*options
, const char *str
);
37 static int setroptions();
38 static inline int termchck(const void *term
);
39 static void interchck();
41 static void sighandl(const int signo
);
42 static void die(const int code
, const char *msg
, ...);
43 static void *writeport(void *unused
);
44 static void *readport(void *unused
);
45 static inline void replacechar(char *str
, ssize_t size
, const char find
, const char repl
);
46 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
);
47 static inline ssize_t
rmchar(char *str
, int *scratch
, ssize_t size
, const char find
);
48 static void getcmd(int escape
);
51 static struct termios2 cntrl
, origterm
= {0}, newterm
= {0};
53 static struct termios cntrl
, origterm
= {0}, newterm
= {0};
56 static const unsigned int uintmax
= ~(unsigned int)0;
57 static struct timespec wts
;
58 static char *writebuff
= NULL
;
59 static char *readbuff
= NULL
;
60 static int *scratchr
= NULL
;
61 static int *scratchw
= NULL
;
62 static char backspc
= DEL
,tbackspc
= DEL
;
64 static int interactive
= 0;
67 main(int argc
, char **argv
)
69 for (int i
= 1; i
< argc
; i
++ ) {
70 if (argv
[i
][0] == '-' && argv
[i
][1] >= '0' && argv
[i
][1] <= '9') {
72 /* glibc's `asprintf()` won't set the string to NULL on failure automatically */
73 asprintf(&t
, "-s%s", argv
[i
] + 1);
75 fprintf(stderr
, "cannot convert -# to -s#\n");
83 static struct option longopt
[] = {
84 {"device", required_argument
, NULL
, 'l'},
85 {"speed", required_argument
, NULL
, 's'},
86 {"rx-speed", required_argument
, NULL
, 'i'},
87 {"echo", no_argument
, NULL
, 'h'},
88 {"canonical", no_argument
, 0, 'c'},
89 {"odd", no_argument
, NULL
, 'o'},
90 {"even", no_argument
, NULL
, 'e'},
91 {"hardware-rtscts", no_argument
, NULL
, 'R'},
92 {"hardware-dsrdtr", no_argument
, NULL
, 'r'},
93 {"software", no_argument
, NULL
, 'X'},
94 {"data", required_argument
, NULL
, 'D'},
95 {"delay", required_argument
, NULL
, 'd'},
96 {"min-chars", required_argument
, NULL
, 'm'},
97 {"stop-bits", no_argument
, NULL
, 'S'},
98 {"backspace", no_argument
, NULL
, 'b'},
99 {"translation", required_argument
, NULL
, 't'},
100 {"input-translation", required_argument
, NULL
, 'T'},
101 {"verbose", no_argument
, NULL
, 'v'},
106 int oind
= 0, rxspdset
= 0, devset
= 0, c
;
107 while ((c
= getopt_long(argc
, argv
, "bcd:D:ehi:l:m:oRrs:t:T:vX", longopt
, &oind
)) != -1) {
112 backspace
= !backspace
; break;
114 canonical
= !canonical
; break;
122 tui
= strtoui(optarg
, "invalid delay: %s\n", 0);
128 if (strlen(optarg
) != 1 || !(optarg
[0] >= '5' && optarg
[0] <= '8')) {
129 fprintf(stderr
, "invalid number of data bits: %s\n", optarg
);
142 tui
= strtoui(optarg
, "invalid rx speed: %s\n", 1);
146 rxspdset
= !rxspdset
;
149 tui
= strtoui(optarg
, "invalid speed: %s\n", 1);
155 stopb
= !stopb
; break;
158 fprintf(stderr
, "cannot specify multiple devices\n");
161 if (strlen(optarg
) > 10) {
162 fprintf(stderr
, "device name too long\n");
165 if (strchr(optarg
, '/')) {
167 strcpy(line
, optarg
);
171 sprintf(line
, "/dev/%s", optarg
);
176 tui
= strtoui(optarg
, "invalid number of characters: %s\n", 1);
182 if (troptions(&tropts
, optarg
))
186 if (troptions(&itropts
, optarg
))
190 verbose
= !verbose
; break;
193 die(2, "usage: ust [--device|-l dev] [--speed|-s #|-#] [--rx-speed|-i #]\n"
194 " [--data-bits|-D #] [--stop-bits|-S]"
195 " [--even|-e] [--odd|-o]\n"
196 " [--hardware-rtscts|-R]"
197 " [--hardware-dsrdtr|-r] [--software|-X]\n"
198 " [--delay|-d #] [--min-chars|-m #]"
199 " [--canonical|-c] [--echo|-h]\n"
200 " [--translation|-t tropt]"
201 " [--input-translation|-T tropt]\n"
202 " [--verbose|-v] [--backspace|-b]\n");
211 if (isatty(STDIN_FILENO
) || isatty(STDOUT_FILENO
))
214 signal(SIGHUP
, sighandl
);
215 signal(SIGINT
, sighandl
);
216 signal(SIGQUIT
, sighandl
);
217 signal(SIGTERM
, sighandl
);
221 pthread_t readthread
, writethread
;
222 pthread_create(&writethread
, NULL
, writeport
, NULL
);
223 pthread_create(&readthread
, NULL
, readport
, NULL
);
225 pthread_join(writethread
, NULL
);
226 pthread_join(readthread
, NULL
);
233 gettermattr(int dv
, void *strct
)
236 struct termios2
*optst
= (struct termios2
*)strct
;
237 return ioctl(dv
, TCGETS2
, optst
);
239 struct termios
*optst
= (struct termios
*)strct
;
240 return tcgetattr(dv
, optst
);
245 settermattr(int dv
, void *strct
)
248 struct termios2
*optst
= (struct termios2
*)strct
;
249 return ioctl(dv
, TCSETS2
, optst
);
251 struct termios
*optst
= (struct termios
*)strct
;
252 return tcsetattr(dv
, TCSANOW
, optst
);
257 settermspd(unsigned int lispeed
, unsigned int lospeed
, void *strct
)
260 struct termios2
*optst
= (struct termios2
*)strct
;
262 optst
->c_cflag
&= ~CBAUD
;
263 optst
->c_cflag
|= BOTHER
;
264 optst
->c_ispeed
= ispeed
;
265 optst
->c_ospeed
= ospeed
;
268 struct termios
*optst
= (struct termios
*)strct
;
269 cfsetispeed(optst
, ispeed
);
270 cfsetospeed(optst
, ospeed
);
277 fd
= open(line
, O_RDWR
| O_NOCTTY
| O_NDELAY
);
279 perror("error opening device");
283 if (verbose
) fprintf(stderr
, "opened \"%s\"\n", line
);
286 die(2, "device \"%s\" is not a TTY\n", line
);
288 if (verbose
) fprintf(stderr
, "\"%s\" is a TTY\n", line
);
290 int flags
= fcntl(fd
, F_GETFL
, 0);
291 flags
&= ~(O_NONBLOCK
| O_NDELAY
); /* opened to check with non-blocking mode, now set to blocking */
293 if (fcntl(fd
, F_SETFL
, flags
) != 0)
294 die(1, "failed to set the device to blocking mode\n");
296 if (gettermattr(fd
, &cntrl
) != 0)
297 die(1, "failed to get device attributes\n");
299 if (verbose
) fprintf(stderr
, "setting baudrate [RX:%u | TX:%u]\n", ispeed
, ospeed
);
301 settermspd(ispeed
, ospeed
, &cntrl
);
302 if (settermattr(fd
, &cntrl
) != 0)
303 die(2,"failed to set baudrate [RX:%u | TX:%u]\n", ispeed
, ospeed
);
306 cntrl
.c_iflag
&= ~(ISTRIP
| BRKINT
);
308 cntrl
.c_cflag
&= ~(PARENB
| PARODD
);
310 if (verbose
) fprintf(stderr
, "setting parity [even: %d, odd: %d]\n", parity
& 1, (parity
& 2) >> 1);
313 cntrl
.c_cflag
|= PARENB
;
315 cntrl
.c_cflag
|= PARENB
| PARODD
;
317 if (settermattr(fd
, &cntrl
) != 0)
318 die(2, "failed to set parity [even: %d, odd: %d]\n", parity
& 1, (parity
& 2) >> 1);
321 /* it's so ugly and beautiful at the same time */
322 int t
= (((hard
& 1) << 1) & (hard
& 2)) >> 1;
323 fprintf(stderr
, "setting flow control [XON/XOFF: %d, RTS/CTS: %d, DTR/DSR: %d, DCD: %d]\n",\
324 soft
, t
^ ((hard
& 1) << 1) >> 1, t
^ (hard
& 2) >> 1, t
);
327 cntrl
.c_cflag
|= CLOCAL
;
328 cntrl
.c_iflag
&= ~IXONXOFF
;
331 cntrl
.c_iflag
|= IXONXOFF
;
334 cntrl
.c_cflag
|= CRTSCTS
;
335 } else if (hard
== 2) {
337 fprintf(stderr
, "DTR/DSR flow control is not supported on this platform\nenabling this option does nothing!\n");
339 cntrl
.c_lflag
|= CDTRDSR
;
341 } else if (hard
== 3) {
342 cntrl
.c_cflag
&= ~CLOCAL
;
344 cntrl
.c_lflag
|= CCAR_OFLOW
;
348 if (settermattr(fd
, &cntrl
) != 0)
349 die(2, "failed to set flow control\n");
352 die(2, "failed to set cr-lf translation options\n");
354 if (verbose
) fprintf(stderr
, "setting data bits [%c]\n", datab
);
367 cntrl
.c_cflag
&= ~CSIZE
;
370 if (settermattr(fd
, &cntrl
) != 0)
371 die(2, "failed to set data bits [%c]\n", datab
);
373 if (verbose
) fprintf(stderr
, "setting stop bits [%d]\n", stopb
+1);
376 cntrl
.c_cflag
|= CSTOPB
;
378 cntrl
.c_cflag
&= ~CSTOPB
;
380 if (settermattr(fd
, &cntrl
) != 0)
381 die(1, "failed to set stop bits\n");
384 ioctl(fd
, TCFLSH
, TCIOFLUSH
);
386 tcflush(fd
, TCIOFLUSH
);
392 strtoui(const char *str
, const char *msg
, const unsigned int min
)
397 t
= strtol(str
, &endptr
, 10);
398 if (errno
!= 0 || *endptr
!= '\0' || t
< min
) {
399 fprintf(stderr
, msg
, str
);
403 * conversion like this results in 'undefined behavior' according to C spec,
404 * but almost all compilers will just truncate the value so it's OK
406 return (unsigned int)t
;
407 /* termios's `speed_t` is a typedef for `unsigned int` */
411 troptions(crlfopt
*options
, const char *str
)
415 if (strcmp(str
, "lf-in-cr") == 0) {
416 options
->lfincr
= !options
->lfincr
; return 0;
417 } else if (strcmp(str
, "lf-to-cr") == 0) {
418 options
->lftocr
= !options
->lftocr
; return 0;
423 if (strcmp(str
, "cr-in-lf") == 0) {
424 options
->crinlf
= !options
->crinlf
; return 0;
425 } else if (strcmp(str
, "cr-to-lf") == 0) {
426 options
->crtolf
= !options
->crtolf
; return 0;
431 if (strcmp(str
, "no-lf") == 0) {
432 options
->nolf
= !options
->nolf
; return 0;
433 } else if (strcmp(str
, "no-cr") == 0) {
434 options
->nocr
= !options
->nocr
; return 0;
440 fprintf(stderr
, "invalid translation option: %s\n", str
);
449 cntrl
.c_iflag
|= ICRNL
;
451 cntrl
.c_iflag
&= ~ICRNL
;
454 cntrl
.c_iflag
|= INLCR
;
456 cntrl
.c_iflag
&= ~INLCR
;
459 cntrl
.c_oflag
|= OCRNL
;
461 cntrl
.c_oflag
&= ~OCRNL
;
464 cntrl
.c_oflag
|= ONLRET
;
466 cntrl
.c_oflag
&= ~ONLRET
;
469 cntrl
.c_oflag
|= ONLCR
;
471 cntrl
.c_oflag
&= ~ONLCR
;
473 return settermattr(fd
, &cntrl
);
477 termchck(const void *term
)
480 struct termios2
*iterm
= (struct termios2
*)term
;
481 #define TERMIOS_STRUCT termios2
483 struct termios
*iterm
= (struct termios
*)term
;
484 #define TERMIOS_STRUCT termios
486 for (size_t i
= 0; i
< sizeof(struct TERMIOS_STRUCT
); i
++) {
487 if (((char*)iterm
)[i
] != 0)
494 writeport(void *unused
)
497 ts
.tv_sec
= swritedelay
;
498 ts
.tv_nsec
= nswritedelay
;
500 wts
.tv_nsec
= chardelay
;
501 writebuff
= malloc(wbuffsz
* sizeof(char));
502 scratchw
= malloc(scratchwsz
* sizeof(int));
504 if (writebuff
== NULL
)
505 die(1, "buffer allocation failed\n");
506 if (scratchw
== NULL
)
507 die(1, "scratch buffer allocation failed\n");
512 ssize_t inln
= read(STDIN_FILENO
, writebuff
, wbuffsz
- 1);
518 if (writebuff
[0] == escapechar
) {
525 if (backspc
!= tbackspc
)
526 replacechar(writebuff
, inln
, backspc
, tbackspc
);
529 inln
= rmchar(writebuff
, scratchw
, inln
, CR
);
531 inln
= rmchar(writebuff
, scratchw
, inln
, LF
);
533 inln
= addchar(writebuff
, scratchw
, inln
, wbuffsz
, CR
, LF
, 1);
536 for (int i
= 0; i
<= inln
; i
++) {
537 write(fd
, &writebuff
[i
], 1);
538 nanosleep(&wts
, NULL
);
542 write(fd
, writebuff
, 1);
545 nanosleep(&ts
, NULL
);
548 free(writebuff
); free(scratchw
);
549 writebuff
= NULL
; scratchw
= NULL
;
554 readport(void *unused
)
557 ts
.tv_sec
= sreaddelay
;
558 ts
.tv_nsec
= nsreaddelay
;
559 readbuff
= malloc(rbuffsz
* sizeof(char));
560 scratchr
= malloc(scratchrsz
* sizeof(int));
562 if (readbuff
== NULL
)
563 die(1, "buffer allocation failed\n");
564 if (scratchr
== NULL
)
565 die(1, "scratch buffer allocation failed\n");
569 setvbuf(stdout
, NULL
, _IONBF
, 0);
572 ssize_t outln
= read(fd
, readbuff
, rbuffsz
- 1);
575 outln
= rmchar(readbuff
, scratchw
, outln
, CR
);
577 outln
= rmchar(readbuff
, scratchw
, outln
, LF
);
579 outln
= addchar(readbuff
, scratchw
, outln
, rbuffsz
, LF
, CR
, -1);
581 outln
= addchar(readbuff
, scratchw
, outln
, rbuffsz
, CR
, LF
, 1);
583 write(STDOUT_FILENO
, readbuff
, outln
);
585 nanosleep(&ts
, NULL
);
587 if (isatty(STDIN_FILENO
))
588 settermattr(STDIN_FILENO
, &origterm
);
590 free(readbuff
); free(scratchr
);
591 readbuff
= NULL
; scratchr
= NULL
;
595 inline void __attribute__((hot
))
596 replacechar(char *str
, ssize_t size
, const char find
, const char repl
)
598 for (int i
= 0; i
< size
; i
++) {
603 /* TODO: optimize the function and allow for offsets greater than 1 */
604 inline ssize_t
__attribute__((hot
))
605 addchar(char *str
, int *scratch
, ssize_t size
, const size_t bsize
,const char find
, const char inp
, const int offset
)
607 /* turns any signed int into a signed 1
608 int toff = offset & (~(uintmax >> 1) | 1)
612 for (int i
= 0; i
< size
; i
++) {
613 if (str
[i
] == find
) {
620 if ((size
+ c
) > bsize
)
623 if (scratch
[0] == 0) {
624 memmove(&str
[0]+1, &str
[0], size
);
627 for (int i
= c
; i
> 0; i
--) {
628 for (int s
= size
; s
>= scratch
[i
]; s
--)
629 str
[s
+ toff
] = str
[s
];
630 str
[scratch
[i
]] = inp
;
635 inline ssize_t
__attribute__((hot
))
636 rmchar(char *str
, int *scratch
, ssize_t size
, const char find
)
639 for (int i
= 0; i
< size
; i
++) {
640 if (str
[i
] == find
) {
647 for (int i
= c
; i
> 0; i
--) {
648 for (int s
= size
; s
>= scratch
[i
]; s
--)
658 if (gettermattr(STDIN_FILENO
, &origterm
) < 0 )
659 die(1, "failed to get terminal attributes\n");
664 newterm
.c_lflag
&= ~ECHO
;
666 newterm
.c_lflag
&= ~ICANON
;
669 newterm
.c_iflag
|= IXONXOFF
;
671 newterm
.c_iflag
|= INLCR
;
672 newterm
.c_cc
[VMIN
] = minchars
;
673 newterm
.c_cc
[VTIME
] = chardelay
;
674 newterm
.c_cc
[VINTR
] = _POSIX_VDISABLE
;
675 newterm
.c_cc
[VSUSP
] = _POSIX_VDISABLE
;
676 newterm
.c_cc
[VQUIT
] = _POSIX_VDISABLE
;
683 backspc
= origterm
.c_cc
[VERASE
];
685 if (settermattr(STDOUT_FILENO
, &newterm
) < 0)
686 die(1, "failed to set terminal attributes\n");
694 if ((!half
|| !canonical
) && interactive
) {
695 newterm
.c_lflag
|= ECHO
;
696 newterm
.c_lflag
|= ICANON
;
697 if (settermattr(STDIN_FILENO
, &newterm
) < 0)
698 fprintf(stderr
, "failed to enable echo and/or canonical mode\n");
710 if (isatty(STDIN_FILENO
) || isatty(STDOUT_FILENO
))
716 cmdchar
= writebuff
[0];
718 cmdchar
= writebuff
[1];
722 die(0,"\r\n[EOT]\r\n");
729 backspace
= !backspace
;
733 newterm
.c_lflag
&= ~ECHO
;
735 newterm
.c_lflag
|= ECHO
;
737 if (settermattr(STDOUT_FILENO
, &newterm
) < 0)
738 die(1, "failed to set terminal attributes\n");
742 newterm
.c_lflag
&= ~ICANON
;
744 newterm
.c_lflag
|= ICANON
;
745 canonical
= !canonical
;
746 if (settermattr(STDOUT_FILENO
, &newterm
) < 0)
747 die(1, "failed to set terminal attributes\n");
750 if (!half
|| !canonical
) {
751 newterm
.c_lflag
|= ECHO
;
752 newterm
.c_lflag
|= ICANON
;
753 settermattr(STDIN_FILENO
, &newterm
);
755 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
756 replacechar(ttr
, 63, LF
, '\0');
758 int wfd
= open(ttr
, O_RDONLY
);
760 perror("error opening file");
764 while ((frln
= read(wfd
, writebuff
, wbuffsz
- 1)) > 0) {
765 for (int i
= 0; i
<= frln
; i
++) {
766 write(fd
, &writebuff
[i
], 1);
767 nanosleep(&wts
, NULL
);
774 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
775 replacechar(ttr
, 63, LF
, '\0');
776 tspd
= strtoui(ttr
, "invalid speed\n", 0);
777 if (tspd
!= uintmax
) {
778 ospeed
= ispeed
= tspd
;
779 settermspd(ispeed
, ospeed
, &cntrl
);
780 if (settermattr(fd
, &cntrl
) != 0) {
781 fprintf(stderr
, "failed to set baudrate [RX:%u | TX:%u]\n", ispeed
, ospeed
);
788 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
789 replacechar(ttr
, 63, LF
, '\0');
790 chardelay
= strtoui(ttr
, "invalid delay\n", 0);
791 if (chardelay
!= uintmax
) {
793 wts
.tv_nsec
= chardelay
;
799 fprintf(stderr
, "additional output translation option: ");
800 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
801 replacechar(ttr
, 63, LF
, '\0');
802 if(troptions(&tropts
, ttr
))
805 fprintf(stderr
, "could not set new options\n");
811 printf("additional input translation option: ");
812 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
813 replacechar(ttr
, 63, LF
, '\0');
814 if (troptions(&itropts
, ttr
))
817 fprintf(stderr
, "could not set new options\n");
820 if (!half
&& interactive
)
821 newterm
.c_lflag
&= ~ECHO
;
822 if (!canonical
&& interactive
)
823 newterm
.c_lflag
&= ~ICANON
;
824 settermattr(STDIN_FILENO
, &newterm
);
829 ts
.tv_sec
= spulsedelay
;
830 ts
.tv_nsec
= nspulsedelay
;
832 if (ioctl(fd
, TIOCMGET
, &st
) != 0) {
833 fprintf(stderr
, "failed to get port status\n");
837 if (ioctl(fd
, TIOCMSET
, &st
) != 0) {
838 fprintf(stderr
, "failed to set DTR [assertion]\n");
841 nanosleep(&ts
, NULL
);
844 if (ioctl(fd
, TIOCMSET
, &st
) != 0) {
845 fprintf(stderr
, "failed to set DTR [negation]\n");
853 fprintf(stderr
, "not a valid command [%c]\n", cmdchar
);
860 sighandl(const int signo
)
862 die(128 + signo
, "\nrecieved signal [%d], exiting\n", signo
);
866 die(const int code
, const char *msg
, ...)
881 if (termchck(&newterm
))
882 settermattr(STDIN_FILENO
, &origterm
);
885 vfprintf(stderr
, msg
, fpa
);