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