DTR/DSR, DCD macro switches are now platform-agnostic.
[ust.git] / ust.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <stdarg.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <getopt.h>
7 #include <signal.h>
8 #include <errno.h>
9 #include <pthread.h>
10 #include <fcntl.h>
11 #include <sys/ioctl.h>
12
13 #ifdef __linux__
14 #include <asm/termbits.h>
15 #include <asm/ioctls.h>
16 #else
17 #include <termios.h>
18 #endif
19
20 #include "config.h"
21
22 #define CR '\r'
23 #define LF '\n'
24 #define BS '\b'
25 #define DEL '\x7f'
26 #if defined(CCDTR_IFLOW) && defined(CDSR_OFLOW)
27 #define CDTRDSR (CDTR_IFLOW | CDSR_OFLOW)
28 #endif
29 #define IXONXOFF (IXON | IXOFF)
30
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();
40 static void cechck();
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);
49
50 #ifdef __linux__
51 static struct termios2 cntrl, origterm = {0}, newterm = {0};
52 #else
53 static struct termios cntrl, origterm = {0}, newterm = {0};
54 #endif
55
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;
63 static int fd = -1;
64 static int interactive = 0;
65
66 int
67 main(int argc, char **argv)
68 {
69 for (int i = 1; i < argc; i++ ) {
70 if (argv[i][0] == '-' && argv[i][1] >= '0' && argv[i][1] <= '9') {
71 char *t = NULL;
72 /* glibc's `asprintf()` won't set the string to NULL on failure automatically */
73 asprintf(&t, "-s%s", argv[i] + 1);
74 if (!t) {
75 fprintf(stderr, "cannot convert -# to -s#\n");
76 break;
77 }
78 argv[i] = t;
79 free(t);
80 }
81 }
82
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'},
102 {NULL, 0, NULL, 0}
103 };
104
105 unsigned int tui;
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) {
108 switch (c) {
109 case 0:
110 break;
111 case 'b':
112 backspace = !backspace; break;
113 case 'c':
114 canonical = !canonical; break;
115 case 'R':
116 hard ^= 1; break;
117 case 'r':
118 hard ^= 2; break;
119 case 'X':
120 soft = !soft; break;
121 case 'd':
122 tui = strtoui(optarg, "invalid delay: %s\n", 0);
123 if (tui == uintmax)
124 goto ustusage;
125 chardelay = tui;
126 break;
127 case 'D':
128 if (strlen(optarg) != 1 || !(optarg[0] >= '5' && optarg[0] <= '8')) {
129 fprintf(stderr, "invalid number of data bits: %s\n", optarg);
130 goto ustusage;
131 } else {
132 datab = optarg[0];
133 }
134 break;
135 case 'e':
136 parity ^= 1; break;
137 case 'o':
138 parity ^= 2; break;
139 case 'h':
140 half = !half; break;
141 case 'i':
142 tui = strtoui(optarg, "invalid rx speed: %s\n", 1);
143 if (tui == uintmax)
144 goto ustusage;
145 ispeed = tui;
146 rxspdset = !rxspdset;
147 break;
148 case 's':
149 tui = strtoui(optarg, "invalid speed: %s\n", 1);
150 if (tui == uintmax)
151 goto ustusage;
152 ospeed = tui;
153 break;
154 case 'S':
155 stopb = !stopb; break;
156 case 'l':
157 if (devset) {
158 fprintf(stderr, "cannot specify multiple devices\n");
159 goto ustusage;
160 }
161 if (strlen(optarg) > 10) {
162 fprintf(stderr, "device name too long\n");
163 goto ustusage;
164 }
165 if (strchr(optarg, '/')) {
166 line[0] = '\0';
167 strcpy(line, optarg);
168 devset = 1;
169 } else {
170 line[0] = '\0';
171 sprintf(line, "/dev/%s", optarg);
172 devset = 1;
173 }
174 break;
175 case 'm':
176 tui = strtoui(optarg, "invalid number of characters: %s\n", 1);
177 if (tui == uintmax)
178 goto ustusage;
179 minchars = tui;
180 break;
181 case 't':
182 if (troptions(&tropts, optarg))
183 goto ustusage;
184 break;
185 case 'T':
186 if (troptions(&itropts, optarg))
187 goto ustusage;
188 break;
189 case 'v':
190 verbose = !verbose; break;
191 default:
192 ustusage:
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");
203 break;
204 }
205
206
207 }
208 if (!rxspdset)
209 ispeed = ospeed;
210
211 if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))
212 interactive = 1;
213
214 signal(SIGHUP, sighandl);
215 signal(SIGINT, sighandl);
216 signal(SIGQUIT, sighandl);
217 signal(SIGTERM, sighandl);
218
219 fd = openport();
220
221 pthread_t readthread, writethread;
222 pthread_create(&writethread, NULL, writeport, NULL);
223 pthread_create(&readthread, NULL, readport, NULL);
224
225 pthread_join(writethread, NULL);
226 pthread_join(readthread, NULL);
227 close(fd);
228 return 0;
229
230 }
231
232 int
233 gettermattr(int dv, void *strct)
234 {
235 #ifdef __linux__
236 struct termios2 *optst = (struct termios2*)strct;
237 return ioctl(dv, TCGETS2, optst);
238 #else
239 struct termios *optst = (struct termios*)strct;
240 return tcgetattr(dv, optst);
241 #endif
242 }
243
244 int
245 settermattr(int dv, void *strct)
246 {
247 #ifdef __linux__
248 struct termios2 *optst = (struct termios2*)strct;
249 return ioctl(dv, TCSETS2, optst);
250 #else
251 struct termios *optst = (struct termios*)strct;
252 return tcsetattr(dv, TCSANOW, optst);
253 #endif
254 }
255
256 void
257 settermspd(unsigned int lispeed, unsigned int lospeed, void *strct)
258 {
259 #ifdef __linux__
260 struct termios2 *optst = (struct termios2*)strct;
261
262 optst->c_cflag &= ~CBAUD;
263 optst->c_cflag |= BOTHER;
264 optst->c_ispeed = ispeed;
265 optst->c_ospeed = ospeed;
266
267 #else
268 struct termios *optst = (struct termios*)strct;
269 cfsetispeed(optst, ispeed);
270 cfsetospeed(optst, ospeed);
271 #endif
272 }
273
274 int
275 openport()
276 {
277 fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY);
278 if (fd == -1) {
279 perror("error opening device");
280 exit(1);
281 }
282
283 if (verbose) fprintf(stderr, "opened \"%s\"\n", line);
284
285 if (!isatty(fd))
286 die(2, "device \"%s\" is not a TTY\n", line);
287
288 if (verbose) fprintf(stderr, "\"%s\" is a TTY\n", line);
289
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 */
292
293 if (fcntl(fd, F_SETFL, flags) != 0)
294 die(1, "failed to set the device to blocking mode\n");
295
296 if (gettermattr(fd, &cntrl) != 0)
297 die(1, "failed to get device attributes\n");
298
299 if (verbose) fprintf(stderr, "setting baudrate [RX:%u | TX:%u]\n", ispeed, ospeed);
300
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);
304
305 cntrl.c_lflag = 0;
306 cntrl.c_iflag &= ~(ISTRIP | BRKINT);
307
308 cntrl.c_cflag &= ~(PARENB | PARODD);
309
310 if (verbose) fprintf(stderr, "setting parity [even: %d, odd: %d]\n", parity & 1, (parity & 2) >> 1);
311
312 if (parity == 1)
313 cntrl.c_cflag |= PARENB;
314 if (parity == 2)
315 cntrl.c_cflag |= PARENB | PARODD;
316
317 if (settermattr(fd, &cntrl) != 0)
318 die(2, "failed to set parity [even: %d, odd: %d]\n", parity & 1, (parity & 2) >> 1);
319
320 if (verbose) {
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);
325 }
326
327 cntrl.c_cflag |= CLOCAL;
328 cntrl.c_iflag &= ~IXONXOFF;
329
330 if (soft)
331 cntrl.c_iflag |= IXONXOFF;
332
333 if (hard == 1) {
334 cntrl.c_cflag |= CRTSCTS;
335 } else if (hard == 2) {
336 #ifndef CDTRDSR
337 fprintf(stderr, "DTR/DSR flow control is not supported on this platform\nenabling this option does nothing!\n");
338 #else
339 cntrl.c_lflag |= CDTRDSR;
340 #endif
341 } else if (hard == 3) {
342 cntrl.c_cflag &= ~CLOCAL;
343 #ifdef CCAR_OFLOW
344 cntrl.c_lflag |= CCAR_OFLOW;
345 #endif
346 }
347
348 if (settermattr(fd, &cntrl) != 0)
349 die(2, "failed to set flow control\n");
350
351 if (setroptions())
352 die(2, "failed to set cr-lf translation options\n");
353
354 if (verbose) fprintf(stderr, "setting data bits [%c]\n", datab);
355
356 tcflag_t db = CS8;
357 switch (datab) {
358 case '5':
359 db = CS5; break;
360 case '6':
361 db = CS6; break;
362 case '7':
363 db = CS7; break;
364 default:
365 break;
366 }
367 cntrl.c_cflag &= ~CSIZE;
368 cntrl.c_cflag |= db;
369
370 if (settermattr(fd, &cntrl) != 0)
371 die(2, "failed to set data bits [%c]\n", datab);
372
373 if (verbose) fprintf(stderr, "setting stop bits [%d]\n", stopb+1);
374
375 if (stopb)
376 cntrl.c_cflag |= CSTOPB;
377 else
378 cntrl.c_cflag &= ~CSTOPB;
379
380 if (settermattr(fd, &cntrl) != 0)
381 die(1, "failed to set stop bits\n");
382
383 #ifdef __linux__
384 ioctl(fd, TCFLSH, TCIOFLUSH);
385 #else
386 tcflush(fd, TCIOFLUSH);
387 #endif
388 return(fd);
389 }
390
391 unsigned int
392 strtoui(const char *str, const char *msg, const unsigned int min)
393 {
394 long t;
395 char *endptr;
396
397 t = strtol(str, &endptr, 10);
398 if (errno != 0 || *endptr != '\0' || t < min) {
399 fprintf(stderr, msg, str);
400 return uintmax;
401 }
402 /*
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
405 */
406 return (unsigned int)t;
407 /* termios's `speed_t` is a typedef for `unsigned int` */
408 }
409
410 int
411 troptions(crlfopt *options, const char *str)
412 {
413 switch (str[0]) {
414 case 'l':
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;
419 } else
420 goto trerr;
421 break;
422 case 'c':
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;
427 } else
428 goto trerr;
429 break;
430 case 'n':
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;
435 } else
436 goto trerr;
437 break;
438 default:
439 trerr:
440 fprintf(stderr, "invalid translation option: %s\n", str);
441 return 1;
442 }
443 }
444
445 int
446 setroptions()
447 {
448 if (itropts.crtolf)
449 cntrl.c_iflag |= ICRNL;
450 else
451 cntrl.c_iflag &= ~ICRNL;
452
453 if (itropts.lftocr)
454 cntrl.c_iflag |= INLCR;
455 else
456 cntrl.c_iflag &= ~INLCR;
457
458 if (tropts.crtolf)
459 cntrl.c_oflag |= OCRNL;
460 else
461 cntrl.c_oflag &= ~OCRNL;
462
463 if (tropts.lftocr)
464 cntrl.c_oflag |= ONLRET;
465 else
466 cntrl.c_oflag &= ~ONLRET;
467
468 if (tropts.crinlf)
469 cntrl.c_oflag |= ONLCR;
470 else
471 cntrl.c_oflag &= ~ONLCR;
472
473 return settermattr(fd, &cntrl);
474 }
475
476 inline int
477 termchck(const void *term)
478 {
479 #ifdef __linux__
480 struct termios2 *iterm = (struct termios2*)term;
481 #define TERMIOS_STRUCT termios2
482 #else
483 struct termios *iterm = (struct termios*)term;
484 #define TERMIOS_STRUCT termios
485 #endif
486 for (size_t i = 0; i < sizeof(struct TERMIOS_STRUCT); i++) {
487 if (((char*)iterm)[i] != 0)
488 return 1;
489 }
490 return 0;
491 }
492
493 void *
494 writeport(void *unused)
495 {
496 struct timespec ts;
497 ts.tv_sec = swritedelay;
498 ts.tv_nsec = nswritedelay;
499 wts.tv_sec = 0;
500 wts.tv_nsec = chardelay;
501 writebuff = malloc(wbuffsz * sizeof(char));
502 scratchw = malloc(scratchwsz * sizeof(int));
503
504 if (writebuff == NULL)
505 die(1, "buffer allocation failed\n");
506 if (scratchw == NULL)
507 die(1, "scratch buffer allocation failed\n");
508
509 int escape = 0;
510
511 for (;;) {
512 ssize_t inln = read(STDIN_FILENO, writebuff, wbuffsz - 1);
513 if (inln > 0) {
514 if (escape) {
515 getcmd(escape);
516 escape = 0;
517 }
518 if (writebuff[0] == escapechar) {
519 if (inln > 1)
520 getcmd(escape);
521 else
522 escape = 1;
523 }
524
525 if (backspc != tbackspc)
526 replacechar(writebuff, inln, backspc, tbackspc);
527
528 if (tropts.nocr)
529 inln = rmchar(writebuff, scratchw, inln, CR);
530 if (tropts.nolf)
531 inln = rmchar(writebuff, scratchw, inln, LF);
532 if (tropts.lfincr)
533 inln = addchar(writebuff, scratchw, inln, wbuffsz, CR, LF, 1);
534
535 if (inln > 1) {
536 for (int i = 0; i <= inln; i++) {
537 write(fd, &writebuff[i], 1);
538 nanosleep(&wts, NULL);
539 }
540 }
541 else {
542 write(fd, writebuff, 1);
543 }
544 }
545 nanosleep(&ts, NULL);
546 }
547
548 free(writebuff); free(scratchw);
549 writebuff = NULL; scratchw = NULL;
550 return NULL;
551 }
552
553 void *
554 readport(void *unused)
555 {
556 struct timespec ts;
557 ts.tv_sec = sreaddelay;
558 ts.tv_nsec = nsreaddelay;
559 readbuff = malloc(rbuffsz * sizeof(char));
560 scratchr = malloc(scratchrsz * sizeof(int));
561
562 if (readbuff == NULL)
563 die(1, "buffer allocation failed\n");
564 if (scratchr == NULL)
565 die(1, "scratch buffer allocation failed\n");
566
567 interchck();
568
569 setvbuf(stdout, NULL, _IONBF, 0);
570
571 for (;;) {
572 ssize_t outln = read(fd, readbuff, rbuffsz - 1);
573 if (outln > 0) {
574 if (itropts.nocr)
575 outln = rmchar(readbuff, scratchw, outln, CR);
576 if (itropts.nolf)
577 outln = rmchar(readbuff, scratchw, outln, LF);
578 if (itropts.crinlf)
579 outln = addchar(readbuff, scratchw, outln, rbuffsz, LF, CR, -1);
580 if (itropts.lfincr)
581 outln = addchar(readbuff, scratchw, outln, rbuffsz, CR, LF, 1);
582
583 write(STDOUT_FILENO, readbuff, outln);
584 }
585 nanosleep(&ts, NULL);
586 }
587 if (isatty(STDIN_FILENO))
588 settermattr(STDIN_FILENO, &origterm);
589
590 free(readbuff); free(scratchr);
591 readbuff = NULL; scratchr = NULL;
592 return NULL;
593 }
594
595 inline void __attribute__((hot))
596 replacechar(char *str, ssize_t size, const char find, const char repl)
597 {
598 for (int i = 0; i < size; i++) {
599 if (str[i] == find)
600 str[i] = repl;
601 }
602 }
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)
606 {
607 /* turns any signed int into a signed 1
608 int toff = offset & (~(uintmax >> 1) | 1)
609 */
610 int toff = offset;
611 int c = 0;
612 for (int i = 0; i < size; i++) {
613 if (str[i] == find) {
614 scratch[c] = i;
615 c++;
616 }
617 }
618 if (!c)
619 return(size);
620 if ((size + c) > bsize)
621 c = bsize - size;
622
623 if (scratch[0] == 0) {
624 memmove(&str[0]+1, &str[0], size);
625 scratch[0] = 1;
626 }
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;
631 }
632 return (size + c);
633 }
634
635 inline ssize_t __attribute__((hot))
636 rmchar(char *str, int *scratch, ssize_t size, const char find)
637 {
638 int c = 0;
639 for (int i = 0; i < size; i++) {
640 if (str[i] == find) {
641 scratch[c] = i;
642 c++;
643 }
644 }
645 if (!c)
646 return(size);
647 for (int i = c; i > 0; i--) {
648 for (int s = size; s >= scratch[i]; s--)
649 str[s] = str[s + 1];
650 }
651 return(size - c);
652 }
653
654 void
655 interchck()
656 {
657 if (interactive) {
658 if (gettermattr(STDIN_FILENO, &origterm) < 0 )
659 die(1, "failed to get terminal attributes\n");
660
661 newterm = origterm;
662
663 if (!canonical)
664 newterm.c_lflag &= ~ECHO;
665 if (!half)
666 newterm.c_lflag &= ~ICANON;
667
668 if (soft)
669 newterm.c_iflag |= IXONXOFF;
670
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;
677
678 if (backspace)
679 tbackspc = BS;
680 else
681 tbackspc = DEL;
682
683 backspc = origterm.c_cc[VERASE];
684
685 if (settermattr(STDOUT_FILENO, &newterm) < 0)
686 die(1, "failed to set terminal attributes\n");
687 }
688 return;
689 }
690
691 void
692 cechck()
693 {
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");
699 }
700 return;
701 }
702
703 void
704 getcmd(int escape)
705 {
706 char cmdchar;
707 char ttr[64];
708 unsigned int tspd;
709
710 if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))
711 interactive = 1;
712
713 interchck();
714
715 if (escape)
716 cmdchar = writebuff[0];
717 else
718 cmdchar = writebuff[1];
719
720 switch (cmdchar) {
721 case '.':
722 die(0,"\r\n[EOT]\r\n");
723 break;
724 case 'b':
725 if (backspace)
726 tbackspc = DEL;
727 else
728 tbackspc = BS;
729 backspace = !backspace;
730 break;
731 case 'h':
732 if (half)
733 newterm.c_lflag &= ~ECHO;
734 else
735 newterm.c_lflag |= ECHO;
736 half = !half;
737 if (settermattr(STDOUT_FILENO, &newterm) < 0)
738 die(1, "failed to set terminal attributes\n");
739 break;
740 case 'c':
741 if (canonical)
742 newterm.c_lflag &= ~ICANON;
743 else
744 newterm.c_lflag |= ICANON;
745 canonical = !canonical;
746 if (settermattr(STDOUT_FILENO, &newterm) < 0)
747 die(1, "failed to set terminal attributes\n");
748 break;
749 case 'w':
750 if (!half || !canonical) {
751 newterm.c_lflag |= ECHO;
752 newterm.c_lflag |= ICANON;
753 settermattr(STDIN_FILENO, &newterm);
754 }
755 if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
756 replacechar(ttr, 63, LF, '\0');
757 ssize_t frln;
758 int wfd = open(ttr, O_RDONLY);
759 if (fd == -1) {
760 perror("error opening file");
761 break;
762 }
763
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);
768 }
769 }
770 }
771 goto finish;
772 case 's':
773 cechck();
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);
782 }
783 }
784 }
785 goto finish;
786 case 'd':
787 cechck();
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) {
792 wts.tv_sec = 0;
793 wts.tv_nsec = chardelay;
794 }
795 }
796 goto finish;
797 case 't':
798 cechck();
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))
803 goto finish;
804 if (setroptions())
805 fprintf(stderr, "could not set new options\n");
806
807 }
808 goto finish;
809 case 'T':
810 cechck();
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))
815 goto finish;
816 if (setroptions())
817 fprintf(stderr, "could not set new options\n");
818 }
819 finish:
820 if (!half && interactive)
821 newterm.c_lflag &= ~ECHO;
822 if (!canonical && interactive)
823 newterm.c_lflag &= ~ICANON;
824 settermattr(STDIN_FILENO, &newterm);
825 break;
826 case 'p':;
827 int st;
828 struct timespec ts;
829 ts.tv_sec = spulsedelay;
830 ts.tv_nsec = nspulsedelay;
831
832 if (ioctl(fd, TIOCMGET, &st) != 0) {
833 fprintf(stderr, "failed to get port status\n");
834 break;
835 }
836 st ^= TIOCM_DTR;
837 if (ioctl(fd, TIOCMSET, &st) != 0) {
838 fprintf(stderr, "failed to set DTR [assertion]\n");
839 }
840
841 nanosleep(&ts, NULL);
842
843 st ^= TIOCM_DTR;
844 if (ioctl(fd, TIOCMSET, &st) != 0) {
845 fprintf(stderr, "failed to set DTR [negation]\n");
846 }
847 break;
848 case BS:
849 break;
850 case DEL:
851 break;
852 default:
853 fprintf(stderr, "not a valid command [%c]\n", cmdchar);
854 break;
855 }
856 return;
857 }
858
859 void
860 sighandl(const int signo)
861 {
862 die(128 + signo, "\nrecieved signal [%d], exiting\n", signo);
863 }
864
865 void
866 die(const int code, const char *msg, ...)
867 {
868 va_list fpa;
869 if (fd != -1)
870 close(fd);
871
872 if (writebuff)
873 free(writebuff);
874 if (readbuff)
875 free(readbuff);
876 if (scratchw)
877 free(scratchw);
878 if (scratchr)
879 free(scratchr);
880
881 if (termchck(&newterm))
882 settermattr(STDIN_FILENO, &origterm);
883
884 va_start(fpa, msg);
885 vfprintf(stderr, msg, fpa);
886 va_end(fpa);
887
888 exit(code);
889 }