a9e463336b4ff7e53d84f468542950680a539aa9
1 /* see LICENSE for license details */
18 #include <asm/termbits.h>
19 #include <asm/ioctls.h>
20 #else /* including for termios2, which is needed for non-standard bauds */
31 #define IXONXOFF (IXON | IXOFF)
33 #if defined(CCDTR_IFLOW) && defined(CDSR_OFLOW)
34 #define CDTRDSR (CDTR_IFLOW | CDSR_OFLOW)
38 typedef struct termios2 term_struct
;
40 typedef struct termios term_struct
;
44 int offset
; /* can only be a 1 or a -1 */
63 /* including here to access CRLFOpt */
66 static int gettermattr(int dv
, term_struct
*optst
);
67 static int settermattr(int dv
, const term_struct
*optst
);
68 static void settermspd(unsigned int lispeed
, unsigned int lospeed
,
70 static int openport(void);
71 static int openparent(void);
72 static unsigned int strtoui(const char *str
, unsigned int min
);
73 static int chcktropts(CRLFOpt
*options
, const char *str
);
74 static int settropts(void);
75 static inline unsigned int lsbmask(unsigned int n
);
76 static inline int termvalid(const term_struct
*term
);
77 static void eninter(void);
78 static void enechocan(void);
79 static void *writeport(void *);
80 static void *readport(void *);
81 static void getcmd(int escape
);
82 static inline void replacechar(char *buff
, ssize_t size
, const Args
*args
);
83 static inline ssize_t
addchar(char *buff
, ssize_t size
, int *scratch
,
84 const Sizes
*msizes
, const Args
*args
);
85 static inline ssize_t
rmchar(char *buff
, ssize_t sread
, int *scratch
,
87 static void sighandl(int signo
);
88 static void die(int code
, const char *msg
, ...);
90 static term_struct cntrl
, /* device struct */
91 /* controlling terminal structs */
95 static Args bsargs
= {0, DEL
, DEL
};
96 static const Args nocrargs
= {0, CR
, 0},
97 /* null-terminator is for use inside `getcmd()` */
98 nolfargs
= {0, LF
, '\0'},
99 lfincrargs
= {1, CR
, LF
},
100 crinlfargs
= {0, LF
, CR
};
102 static struct timespec wts
;
103 /* unsigned -1 used to return an error */
104 static const unsigned int uintmax
= ~(unsigned int)0;
105 static int interactive
= 0;
106 static char *writebuff
= NULL
;
107 static char *readbuff
= NULL
;
108 static int *scratchr
= NULL
;
109 static int *scratchw
= NULL
;
115 gettermattr(int dv
, term_struct
*optst
)
118 return ioctl(dv
, TCGETS2
, optst
);
120 return tcgetattr(dv
, optst
);
125 settermattr(int dv
, const term_struct
*optst
)
128 return ioctl(dv
, TCSETS2
, optst
);
130 return tcsetattr(dv
, TCSANOW
, optst
);
135 settermspd(unsigned int lispeed
, unsigned int lospeed
, term_struct
*optst
)
138 optst
->c_cflag
&= ~CBAUD
;
139 optst
->c_cflag
|= BOTHER
;
140 optst
->c_ispeed
= lispeed
;
141 optst
->c_ospeed
= lospeed
;
143 cfsetispeed(optst
, lispeed
);
144 cfsetospeed(optst
, lospeed
);
152 struct flock fl
= { 0 };
155 if (pledge("stdio rpath wpath tty flock", NULL
) < 0) {
157 die(1, "pledge failed, exiting now\n");
161 if (verbose
) fprintf(stderr
, "opening \"%s\"\n", line
);
163 fd
= open(line
, (O_RDWR
| O_NOCTTY
| O_NDELAY
));
165 perror("error opening device");
169 if (verbose
) fprintf(stderr
, "checking if \"%s\" is a TTY\n", line
);
172 die(2, "device \"%s\" is not a TTY\n", line
);
175 flags
= fcntl(fd
, F_GETFL
, 0);
176 flags
&= ~(O_NONBLOCK
| O_NDELAY
);
178 if (fcntl(fd
, F_SETFL
, flags
) < 0) {
180 die(1, "exiting now\n");
184 if (gettermattr(fd
, &cntrl
) < 0)
185 die(1, "failed to get device attributes\n");
189 fl
.l_whence
= SEEK_SET
;
190 if (fcntl(fd
, F_SETLK
, &fl
) < 0) {
192 die(1, "failed to lock the device, exiting now\n");
199 if (verbose
) fprintf(stderr
, "setting baudrate [RX:%u | TX:%u]\n", ispeed
, ospeed
);
201 settermspd(ispeed
, ospeed
, &cntrl
);
202 if (settermattr(fd
, &cntrl
) < 0)
203 die(2,"failed to set baudrate [RX:%u | TX:%u]\n", ispeed
, ospeed
);
209 cntrl
.c_iflag
&= ~(ISTRIP
| BRKINT
);
210 cntrl
.c_cflag
&= ~(PARENB
| PARODD
);
212 if (verbose
) fprintf(stderr
, "setting parity [even: %d, odd: %d]\n",\
213 parity
& 1, (parity
& 2) >> 1);
216 cntrl
.c_cflag
|= PARENB
;
218 cntrl
.c_cflag
|= PARENB
| PARODD
;
220 if (settermattr(fd
, &cntrl
) < 0)
221 die(2, "failed to set parity [even: %d, odd: %d]\n",\
222 parity
& 1, (parity
& 2) >> 1);
228 /* it's so ugly and beautiful at the same time */
229 int t
= (((hard
& 1) << 1) & (hard
& 2)) >> 1;
230 fprintf(stderr
, "setting flow control [XON/XOFF: %d, RTS/CTS: %d, DTR/DSR: %d, DCD: %d]\n",\
231 soft
, t
^ ((hard
& 1) << 1) >> 1, t
^ (hard
& 2) >> 1, t
);
234 cntrl
.c_cflag
|= CLOCAL
;
235 cntrl
.c_iflag
&= ~IXONXOFF
;
238 cntrl
.c_iflag
|= IXONXOFF
;
241 cntrl
.c_cflag
|= CRTSCTS
;
242 } else if (hard
== 2) {
244 fprintf(stderr
, "DTR/DSR flow control is not supported on this platform\n"
245 "enabling this option does nothing!\n");
247 cntrl
.c_lflag
|= CDTRDSR
;
249 } else if (hard
== 3) {
250 cntrl
.c_cflag
&= ~CLOCAL
;
252 cntrl
.c_lflag
|= CCAR_OFLOW
;
256 if (settermattr(fd
, &cntrl
) < 0)
257 die(2, "failed to set flow control\n");
262 cntrl
.c_iflag
&= ~(ICRNL
| INLCR
);
263 cntrl
.c_oflag
&= ~(OCRNL
| ONLRET
| ONLCR
);
266 die(2, "failed to set cr-lf translation options\n");
271 if (verbose
) fprintf(stderr
, "setting data bits [%c]\n", datab
);
284 cntrl
.c_cflag
&= ~CSIZE
;
287 if (settermattr(fd
, &cntrl
) < 0)
288 die(2, "failed to set data bits [%c]\n", datab
);
293 if (verbose
) fprintf(stderr
, "setting stop bits [%d]\n", stopb
+1);
296 cntrl
.c_cflag
|= CSTOPB
;
298 cntrl
.c_cflag
&= ~CSTOPB
;
300 if (settermattr(fd
, &cntrl
) < 0)
301 die(1, "failed to set stop bits [%d]\n", stopb
+1);
303 cntrl
.c_cc
[VMIN
] = 0;
304 cntrl
.c_cc
[VTIME
] = 0;
306 /* flush both hardware buffers */
308 ioctl(fd
, TCFLSH
, TCIOFLUSH
);
310 tcflush(fd
, TCIOFLUSH
);
312 /* returning only for readability */
319 if (verbose
) fprintf(stderr
, "opening parent terminal\n");
321 pfd
= open("/dev/tty", O_RDWR
);
323 perror("error opening parent terminal fd");
324 die(1, "exiting now\n");
327 if (gettermattr(pfd
, &origterm
) < 0 )
328 die(1, "failed to get parent terminal attributes\n");
332 /* returning only for readability */
337 strtoui(const char *str
, unsigned int min
)
342 t
= strtol(str
, &endptr
, 10);
343 if (errno
!= 0 || *endptr
!= '\0' || t
< min
|| t
> uintmax
) {
347 return (unsigned int)t
;
351 chcktropts(CRLFOpt
*options
, const char *str
)
355 if (!strcmp(str
, "lf-in-cr")) {
356 options
->lfincr
^= 1; return 0;
357 } else if (!strcmp(str
, "lf-to-cr")) {
358 options
->lftocr
^= 1; return 0;
363 if (!strcmp(str
, "cr-in-lf")) {
364 options
->crinlf
^= 1; return 0;
365 } else if (!strcmp(str
, "cr-to-lf")) {
366 options
->crtolf
^= 1; return 0;
371 if (!strcmp(str
, "no-lf")) {
372 options
->nolf
^= 1; return 0;
373 } else if (!strcmp(str
, "no-cr")) {
374 options
->nocr
^= 1; return 0;
386 cntrl
.c_iflag
^= (ICRNL
& lsbmask(itropts
.crtolf
));
387 cntrl
.c_iflag
^= (INLCR
& lsbmask(itropts
.lftocr
));
388 cntrl
.c_oflag
^= (OCRNL
& lsbmask(tropts
.crtolf
));
389 cntrl
.c_oflag
^= (ONLRET
& lsbmask(tropts
.lftocr
));
390 cntrl
.c_oflag
^= (ONLCR
& lsbmask(tropts
.crinlf
));
392 return settermattr(fd
, &cntrl
);
396 lsbmask(unsigned int n
)
398 /* if n == 1, n fills with all 1s, 0 stays the same */
399 for (int i
= (sizeof(int) * CHAR_BIT
); i
> 0; i
--)
405 termvalid(const term_struct
*term
)
407 for (size_t i
= 0; i
< sizeof(term_struct
); i
++)
408 if (((char*)term
)[i
] != 0)
415 writeport(void *unused
)
420 ts
.tv_sec
= swritedelay
;
421 ts
.tv_nsec
= nswritedelay
;
423 wts
.tv_nsec
= chardelay
;
424 msizes
.sizesm
= scratchwsz
;
425 msizes
.sizebm
= wbuffsz
;
427 writebuff
= malloc(wbuffsz
* sizeof(char));
428 scratchw
= malloc(scratchwsz
* sizeof(int));
430 if (writebuff
== NULL
)
431 die(1, "buffer allocation failed\n");
432 if (scratchw
== NULL
)
433 die(1, "scratch buffer allocation failed\n");
438 ssize_t inln
= read(STDIN_FILENO
, writebuff
, wbuffsz
- 1);
445 if (writebuff
[0] == escapechar
) {
453 if (bsargs
.find
!= bsargs
.input
)
454 replacechar(writebuff
, inln
, &bsargs
);
456 inln
= rmchar(writebuff
, inln
, scratchw
, &nocrargs
);
458 inln
= rmchar(writebuff
, inln
, scratchw
, &nolfargs
);
460 inln
= addchar(writebuff
, inln
, scratchw
, &msizes
, &lfincrargs
);
463 for (int i
= 0; i
<= inln
; i
++) {
464 rwc
-= write(fd
, &writebuff
[i
], 1);
465 nanosleep(&wts
, NULL
);
469 rwc
-= write(fd
, writebuff
, inln
);
472 } else if (inln
== 0 && !interactive
) {
475 nanosleep(&ts
, NULL
);
485 readport(void *unused
)
490 ts
.tv_sec
= sreaddelay
;
491 ts
.tv_nsec
= nsreaddelay
;
492 msizes
.sizesm
= scratchrsz
;
493 msizes
.sizebm
= rbuffsz
;
495 readbuff
= malloc(rbuffsz
* sizeof(char));
496 scratchr
= malloc(scratchrsz
* sizeof(int));
498 if (readbuff
== NULL
)
499 die(1, "buffer allocation failed\n");
500 if (scratchr
== NULL
)
501 die(1, "scratch buffer allocation failed\n");
505 /* disable stdout buffering */
506 setvbuf(stdout
, NULL
, _IONBF
, 0);
509 ssize_t outln
= read(fd
, readbuff
, rbuffsz
- 1);
512 outln
= rmchar(readbuff
, outln
, scratchr
, &nocrargs
);
514 outln
= rmchar(readbuff
, outln
, scratchr
, &nolfargs
);
516 outln
= addchar(readbuff
, outln
, scratchr
, &msizes
, &crinlfargs
);
518 outln
= addchar(readbuff
, outln
, scratchr
, &msizes
, &lfincrargs
);
520 rwc
+= write(STDOUT_FILENO
, readbuff
, outln
);
522 nanosleep(&ts
, NULL
);
530 replacechar(char *buff
, ssize_t size
, const Args
*args
)
532 for (int i
= 0; i
< size
; i
++)
533 if (buff
[i
] == args
->find
)
534 buff
[i
] = args
->input
;
538 addchar(char *buff
, ssize_t size
, int *scratch
, const Sizes
*sizes
, const Args
*args
)
542 /* turns negative numbers into 1, positive into 0 */
543 to
= ((args
->offset
>> ((sizeof(int) * CHAR_BIT
) - 1)) & 1);
545 for (int i
= 0; i
< size
; i
++) {
546 if (buff
[i
] == args
->find
) {
550 if (c
>= sizes
->sizesm
)
555 if (size
== 1 && c
== 1) {
556 buff
[to
^ 1] = args
->input
;
557 buff
[to
] = args
->find
;
560 if ((size
+ c
) > sizes
->sizebm
)
561 c
= sizes
->sizebm
- size
;
563 for (int i
= c
- 1; i
>= 0; i
--) {
565 memmove(&buff
[t
] + 1, &buff
[t
], (size
+ c
) - t
);
566 buff
[t
+ (to
^ 1)] = args
->input
;
572 rmchar(char *buff
, ssize_t size
, int *scratch
, const Args
*args
)
575 for (int i
= 0; i
< size
; i
++) {
576 if (buff
[i
] == args
->find
) {
583 if (size
== 1 && c
== 1)
586 for (int i
= c
; i
>= 0; i
--) {
588 memmove(&buff
[t
], &buff
[t
] + 1, (size
+ c
) - t
);
601 newterm
.c_lflag
&= ~ECHO
;
603 newterm
.c_lflag
&= ~ICANON
;
606 newterm
.c_iflag
|= IXONXOFF
;
608 newterm
.c_iflag
|= INLCR
;
609 newterm
.c_cc
[VMIN
] = minchars
;
610 newterm
.c_cc
[VTIME
] = 0;
611 newterm
.c_cc
[VINTR
] = newterm
.c_cc
[VSUSP
]\
612 = newterm
.c_cc
[VQUIT
] = newterm
.c_cc
[VLNEXT
]\
613 = newterm
.c_cc
[VDISCARD
] = _POSIX_VDISABLE
;
616 newterm
.c_cc
[VDSUSP
] = _POSIX_VDISABLE
;
624 bsargs
.find
= origterm
.c_cc
[VERASE
];
626 if (settermattr(pfd
, &newterm
) < 0)
627 die(1, "failed to set terminal attributes\n");
635 if ((!half
|| !canonical
) && interactive
) {
636 newterm
.c_lflag
|= ECHO
;
637 newterm
.c_lflag
|= ICANON
;
638 if (settermattr(pfd
, &newterm
) < 0)
639 fprintf(stderr
, "failed to enable local echo and/or canonical mode\n");
652 msizes
.sizesm
= scratchwsz
;
653 msizes
.sizebm
= wbuffsz
;
655 if (isatty(STDIN_FILENO
))
661 cmdchar
= writebuff
[0];
663 cmdchar
= writebuff
[1];
666 case EOT
: /* FALLTHROUGH */
678 newterm
.c_lflag
&= ~ECHO
;
680 newterm
.c_lflag
|= ECHO
;
682 if (settermattr(pfd
, &newterm
) < 0)
683 die(1, "failed to set terminal attributes\n");
687 newterm
.c_lflag
&= ~ICANON
;
689 newterm
.c_lflag
|= ICANON
;
691 if (settermattr(pfd
, &newterm
) < 0)
692 die(1, "failed to set terminal attributes\n");
696 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
698 int wfd
= open(ttr
, O_RDONLY
);
699 replacechar(ttr
, 63, &nolfargs
);
701 perror("error opening file");
704 while ((frln
= read(wfd
, writebuff
, wbuffsz
- 1)) > 0) {
706 for (int i
= 0; i
<= frln
; i
++) {
708 frln
= rmchar(writebuff
, frln
, scratchw
, &nocrargs
);
710 frln
= rmchar(writebuff
, frln
, scratchw
, &nolfargs
);
712 frln
= addchar(writebuff
, frln
, scratchw
, &msizes
, &lfincrargs
);
714 rwc
-= write(fd
, &writebuff
[i
], 1);
715 nanosleep(&wts
, NULL
);
722 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
723 replacechar(ttr
, 63, &nolfargs
);
724 tspd
= strtoui(ttr
, 0);
725 if (tspd
!= uintmax
) {
726 ospeed
= ispeed
= tspd
;
727 settermspd(ispeed
, ospeed
, &cntrl
);
728 if (settermattr(fd
, &cntrl
) < 0)
729 fprintf(stderr
, "failed to set baudrate "
730 "[RX:%u | TX:%u]\n", ispeed
, ospeed
);
732 fprintf(stderr
, "invalid speed: %s\n", ttr
);
738 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
739 replacechar(ttr
, 63, &nolfargs
);
740 chardelay
= strtoui(ttr
, 0);
741 if (chardelay
!= uintmax
) {
743 wts
.tv_nsec
= chardelay
;
745 fprintf(stderr
, "invalid delay: %s\n", ttr
);
751 fprintf(stderr
, "additional output translation option: ");
752 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
753 replacechar(ttr
, 63, &nolfargs
);
754 if (chcktropts(&tropts
, ttr
)) {
755 fprintf(stderr
, "invalid output translation option: %s\n", optarg
);
759 fprintf(stderr
, "could not set new options\n");
764 printf("additional input translation option: ");
765 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
766 replacechar(ttr
, 63, &nolfargs
);
767 if (chcktropts(&itropts
, ttr
)) {
768 fprintf(stderr
, "invalid input translation option: %s\n", optarg
);
772 fprintf(stderr
, "could not set new options\n");
775 if (!half
&& interactive
)
776 newterm
.c_lflag
&= ~ECHO
;
777 if (!canonical
&& interactive
)
778 newterm
.c_lflag
&= ~ICANON
;
779 if (settermattr(pfd
, &newterm
) < 0)
780 fprintf(stderr
, "failed to restore echo and/or canonical mode\n");
785 ts
.tv_sec
= spulsedelay
;
786 ts
.tv_nsec
= nspulsedelay
;
788 if (ioctl(fd
, TIOCMGET
, &st
) < 0) {
789 fprintf(stderr
, "failed to get port status\n");
793 if (ioctl(fd
, TIOCMSET
, &st
) < 0) {
794 fprintf(stderr
, "failed to set DTR [assertion]\n");
797 nanosleep(&ts
, NULL
);
800 if (ioctl(fd
, TIOCMSET
, &st
) < 0)
801 fprintf(stderr
, "failed to set DTR [negation]\n");
803 case BS
: /* FALLTHROUGH */
808 fprintf(stderr
, "not a valid command [%c]\n", cmdchar
);
817 die(128 + signo
, "\nrecieved signal [%d], exiting\n", signo
);
821 die(int code
, const char *msg
, ...)
827 nanosleep(&wts
, NULL
);
831 if (termvalid(&origterm
))
832 settermattr(pfd
, &origterm
);
848 vfprintf(stderr
, msg
, fpa
);
855 main(int argc
, char *argv
[])
861 int oind
, rxspdset
, devset
, c
;
862 oind
= rxspdset
= devset
= 0;
864 for (int i
= 1; i
< argc
; i
++) {
865 if (argv
[i
][0] == '-' && argv
[i
][1] >= '0' && argv
[i
][1] <= '9') {
866 if (asprintf(&t
, "-s%s", argv
[i
] + 1) == -1) {
867 fprintf(stderr
, "%s: cannot convert -# to -s#\n", argv
[0]);
875 static struct option longopt
[] = {
876 {"line", required_argument
, NULL
, 'l'},
877 {"speed", required_argument
, NULL
, 's'},
878 {"rx-speed", required_argument
, NULL
, 'i'},
879 {"echo", no_argument
, NULL
, 'h'},
880 {"canonical", no_argument
, NULL
, 'c'},
881 {"odd", no_argument
, NULL
, 'o'},
882 {"even", no_argument
, NULL
, 'e'},
883 {"hardware-rts-cts", no_argument
, NULL
, 'R'},
884 {"hardware-dtr-dsr", no_argument
, NULL
, 'r'},
885 {"software", no_argument
, NULL
, 'X'},
886 {"data", required_argument
, NULL
, 'D'},
887 {"delay", required_argument
, NULL
, 'd'},
888 {"min-chars", required_argument
, NULL
, 'm'},
889 {"stop-bits", no_argument
, NULL
, 'S'},
890 {"backspace", no_argument
, NULL
, 'b'},
891 {"translation", required_argument
, NULL
, 't'},
892 {"input-translation", required_argument
, NULL
, 'T'},
893 {"verbose", no_argument
, NULL
, 'v'},
897 while ((c
= getopt_long(argc
, argv
, "D:T:d:i:l:m:s:t:RXbcehorv", longopt
, &oind
)) != -1) {
902 backspace
^= 1; break;
904 canonical
^= 1; break;
912 tl
= strtol(optarg
, &endptr
, 10);
913 if (errno
!= 0 || *endptr
!= '\0' || tl
< 0) {
914 fprintf(stderr
, "%s: invalid character delay: %s\n", argv
[0],optarg
);
921 if (optarg
[1] != '\0' || !(optarg
[0] >= '5' && optarg
[0] <= '8')) {
922 fprintf(stderr
, "%s: invalid number of data bits: %s\n", argv
[0], optarg
);
934 case 'i': /* FALLTHROUGH */
936 tui
= strtoui(optarg
, 1);
937 if (tui
== uintmax
) {
938 fprintf(stderr
, "%s: invalid speed: %s\n", argv
[0], optarg
);
942 ispeed
= tui
; rxspdset
= 1;
945 ispeed
= rxspdset
? ispeed
: ospeed
;
952 fprintf(stderr
, "%s: cannot specify multiple devices\n", argv
[0]);
955 if (strlen(optarg
) > 58) {
956 fprintf(stderr
, "%s: device name too long\n", argv
[0]);
959 if (strchr(optarg
, '/')) {
960 strcpy(line
, optarg
);
963 sprintf(line
, "/dev/%s", optarg
);
968 tui
= strtoui(optarg
, 1);
969 if (tui
== uintmax
) {
970 fprintf(stderr
, "%s: invalid number of characters: %s\n", argv
[0], optarg
);
976 if (chcktropts(&tropts
, optarg
)) {
977 fprintf(stderr
, "%s: invalid output translation option: %s\n", argv
[0], optarg
);
982 if (chcktropts(&itropts
, optarg
)) {
983 fprintf(stderr
, "%s: invalid input translation option: %s\n", argv
[0], optarg
);
991 die(2, "usage: %s [--line | -l line] [--speed | -s # | -#] [--rx-speed | -i #]\n"
992 " [--data-bits | -D #] [--stop-bits | -S]"
993 " [--even | -e] [--odd | -o]\n"
994 " [--hardware-rts-cts | -R]"
995 " [--hardware-dtr-dsr | -r] [--software | -X]\n"
996 " [--delay | -d #] [--min-chars | -m #]"
997 " [--canonical | -c] [--echo | -h]\n"
998 " [--translation | -t option]"
999 " [--input-translation | -T option]\n"
1000 " [--verbose | -v] [--backspace | -b]\n",
1009 if (isatty(STDIN_FILENO
))
1012 signal(SIGHUP
, sighandl
);
1013 signal(SIGINT
, sighandl
);
1014 signal(SIGQUIT
, sighandl
);
1015 signal(SIGTERM
, sighandl
);
1018 if (interactive
) pfd
= openparent();
1020 pthread_t readthread
, writethread
;
1021 pthread_create(&writethread
, NULL
, writeport
, NULL
);
1022 pthread_create(&readthread
, NULL
, readport
, NULL
);
1023 pthread_join(writethread
, NULL
);
1024 pthread_join(readthread
, NULL
);