formatting improvements
[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 #define ESC '\x1b'
27 #if defined(CCDTR_IFLOW) && defined(CDSR_OFLOW)
28 #define CDTRDSR (CDTR_IFLOW | CDSR_OFLOW)
29 #endif
30 #define IXONXOFF (IXON | IXOFF)
31
32 static int gettermattr(int dv, void *strct);
33 static int settermattr(int dv, void *strct);
34 static void settermspd(unsigned int lispeed, unsigned int lospeed, void *strct);
35 static int openport();
36 static unsigned int strtoui(const char *str, const char *msg, const unsigned int min);
37 static int troptions(crlfopt *options, const char *str);
38 static int setroptions();
39 static inline int termchck(const void *term);
40 static void interchck();
41 static void cechck();
42 static void sighandl(const int signo);
43 static void die(const int code, const char *msg, ...);
44 static void *writeport(void *unused);
45 static void *readport(void *unused);
46 static inline void replacechar(char *str, ssize_t size, const char find, const char repl);
47 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);
48 static inline ssize_t rmchar(char *str, int *scratch, ssize_t size, const char find);
49 static void getcmd(int escape);
50
51 #ifdef __linux__
52 static struct termios2 cntrl, origterm = {0}, newterm = {0};
53 #else
54 static struct termios cntrl, origterm = {0}, newterm = {0};
55 #endif
56
57 static const unsigned int uintmax = ~(unsigned int)0;
58 static struct timespec wts;
59 static char *writebuff = NULL;
60 static char *readbuff = NULL;
61 static int *scratchr = NULL;
62 static int *scratchw = NULL;
63 static char backspc = DEL,tbackspc = DEL;
64 static int fd = -1;
65 static int interactive = 0;
66
67 int
68 main(int argc, char **argv)
69 {
70 for (int i = 1; i < argc; i++ ) {
71 if (argv[i][0] == '-' && argv[i][1] >= '0' && argv[i][1] <= '9') {
72 char *t = NULL;
73 /* glibc's `asprintf()` won't set the string to NULL on failure automatically */
74 asprintf(&t, "-s%s", argv[i] + 1);
75 if (!t) {
76 fprintf(stderr, "cannot convert -# to -s#\n");
77 break;
78 }
79 argv[i] = t;
80 free(t);
81 }
82 }
83
84 static struct option longopt[] = {
85 {"line", required_argument, NULL, 'l'},
86 {"speed", required_argument, NULL, 's'},
87 {"rx-speed", required_argument, NULL, 'i'},
88 {"echo", no_argument, NULL, 'h'},
89 {"canonical", no_argument, NULL, 'c'},
90 {"odd", no_argument, NULL, 'o'},
91 {"even", no_argument, NULL, 'e'},
92 {"hardware-rts-cts", no_argument, NULL, 'R'},
93 {"hardware-dtr-dsr", no_argument, NULL, 'r'},
94 {"software", no_argument, NULL, 'X'},
95 {"data", required_argument, NULL, 'D'},
96 {"delay", required_argument, NULL, 'd'},
97 {"min-chars", required_argument, NULL, 'm'},
98 {"stop-bits", no_argument, NULL, 'S'},
99 {"backspace", no_argument, NULL, 'b'},
100 {"translation", required_argument, NULL, 't'},
101 {"input-translation", required_argument, NULL, 'T'},
102 {"verbose", no_argument, NULL, 'v'},
103 {NULL, 0, NULL, 0}
104 };
105
106 unsigned int tui;
107 int oind = 0, rxspdset = 0, devset = 0, c;
108 while ((c = getopt_long(argc, argv, "bcd:D:ehi:l:m:oRrs:t:T:vX", longopt, &oind)) != -1) {
109 switch (c) {
110 case 0:
111 break;
112 case 'b':
113 backspace ^= 1; break;
114 case 'c':
115 canonical ^= 1; break;
116 case 'R':
117 hard ^= 1; break;
118 case 'r':
119 hard ^= 2; break;
120 case 'X':
121 soft ^= 1; break;
122 case 'd':;
123 char* endptr;
124 long t;
125 t = strtol(optarg, &endptr, 10);
126 if (errno != 0 || *endptr != '\0' || t < 0) {
127 fprintf(stderr, "invalid character delay\n");
128 goto ustusage;
129 } else {
130 chardelay = t;
131 }
132 break;
133 case 'D':
134 if (strlen(optarg) != 1 || !(optarg[0] >= '5' && optarg[0] <= '8')) {
135 fprintf(stderr, "invalid number of data bits: %s\n", optarg);
136 goto ustusage;
137 } else {
138 datab = optarg[0];
139 }
140 break;
141 case 'e':
142 parity ^= 1; break;
143 case 'o':
144 parity ^= 2; break;
145 case 'h':
146 half ^= 1; break;
147 case 'i':
148 tui = strtoui(optarg, "invalid rx speed: %s\n", 1);
149 if (tui == uintmax)
150 goto ustusage;
151 ispeed = tui;
152 rxspdset = 1;
153 break;
154 case 's':
155 tui = strtoui(optarg, "invalid speed: %s\n", 1);
156 if (tui == uintmax)
157 goto ustusage;
158 ospeed = tui;
159 break;
160 case 'S':
161 stopb ^= 1; break;
162 case 'l':
163 if (devset) {
164 fprintf(stderr, "cannot specify multiple devices\n");
165 goto ustusage;
166 }
167 if (strlen(optarg) > 10) {
168 fprintf(stderr, "device name too long\n");
169 goto ustusage;
170 }
171 if (strchr(optarg, '/')) {
172 strcpy(line, optarg);
173 devset = 1;
174 } else {
175 sprintf(line, "/dev/%s", optarg);
176 devset = 1;
177 }
178 break;
179 case 'm':
180 tui = strtoui(optarg, "invalid number of characters: %s\n", 1);
181 if (tui == uintmax)
182 goto ustusage;
183 minchars = tui;
184 break;
185 case 't':
186 if (troptions(&tropts, optarg))
187 goto ustusage;
188 break;
189 case 'T':
190 if (troptions(&itropts, optarg))
191 goto ustusage;
192 break;
193 case 'v':
194 verbose ^= 1; break;
195 default:
196 ustusage:
197 die(2, "usage: %s [--line|-l line] [--speed|-s #|-#] [--rx-speed|-i #]\n"
198 " [--data-bits|-D #] [--stop-bits|-S]"
199 " [--even|-e] [--odd|-o]\n"
200 " [--hardware-rts-cts|-R]"
201 " [--hardware-dtr-dsr|-r] [--software|-X]\n"
202 " [--delay|-d #] [--min-chars|-m #]"
203 " [--canonical|-c] [--echo|-h]\n"
204 " [--translation|-t tropt]"
205 " [--input-translation|-T tropt]\n"
206 " [--verbose|-v] [--backspace|-b]\n",
207 argv[0]);
208 break;
209 }
210
211
212 }
213
214 if (!rxspdset)
215 ispeed = ospeed;
216
217 if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))
218 interactive = 1;
219
220 signal(SIGHUP, sighandl);
221 signal(SIGINT, sighandl);
222 signal(SIGQUIT, sighandl);
223 signal(SIGTERM, sighandl);
224
225 fd = openport();
226
227 pthread_t readthread, writethread;
228 pthread_create(&writethread, NULL, writeport, NULL);
229 pthread_create(&readthread, NULL, readport, NULL);
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 #else
267 struct termios *optst = (struct termios*)strct;
268 cfsetispeed(optst, ispeed);
269 cfsetospeed(optst, ospeed);
270 #endif
271 }
272
273 int
274 openport()
275 {
276 if (verbose) fprintf(stderr, "opening \"%s\"\n", line);
277
278 fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY);
279 if (fd == -1) {
280 perror("error opening device");
281 exit(1);
282 }
283
284 if (verbose) fprintf(stderr, "checking if \"%s\" is a TTY\n", line);
285
286 if (!isatty(fd))
287 die(2, "device \"%s\" is not a TTY\n", line);
288
289 int flags;
290 flags = fcntl(fd, F_GETFL, 0);
291 /* opened to check with non-blocking mode, now set to blocking */
292 flags &= ~(O_NONBLOCK | O_NDELAY);
293 if (fcntl(fd, F_SETFL, flags) == -1)
294 die(1, "failed to set the device to blocking mode\n");
295
296 if (gettermattr(fd, &cntrl) == -1)
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) == -1)
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 cntrl.c_cflag &= ~(PARENB | PARODD);
308
309 if (verbose) fprintf(stderr, "setting parity [even: %d, odd: %d]\n", parity & 1, (parity & 2) >> 1);
310
311 if (parity == 1)
312 cntrl.c_cflag |= PARENB;
313 if (parity == 2)
314 cntrl.c_cflag |= PARENB | PARODD;
315
316 if (settermattr(fd, &cntrl) == -1)
317 die(2, "failed to set parity [even: %d, odd: %d]\n", parity & 1, (parity & 2) >> 1);
318
319 if (verbose) {
320 /* it's so ugly and beautiful at the same time */
321 int t = (((hard & 1) << 1) & (hard & 2)) >> 1;
322 fprintf(stderr, "setting flow control [XON/XOFF: %d, RTS/CTS: %d, DTR/DSR: %d, DCD: %d]\n",\
323 soft, t ^ ((hard & 1) << 1) >> 1, t ^ (hard & 2) >> 1, t);
324 }
325
326 cntrl.c_cflag |= CLOCAL;
327 cntrl.c_iflag &= ~IXONXOFF;
328
329 if (soft)
330 cntrl.c_iflag |= IXONXOFF;
331
332 if (hard == 1) {
333 cntrl.c_cflag |= CRTSCTS;
334 } else if (hard == 2) {
335 #ifndef CDTRDSR
336 fprintf(stderr, "DTR/DSR flow control is not supported on this platform\n"
337 "enabling 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) == -1)
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) == -1)
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) == -1)
381 die(1, "failed to set stop bits [%d]\n", stopb+1);
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 }
422 break;
423 case 'c':
424 if (strcmp(str, "cr-in-lf") == 0) {
425 options->crinlf = !options->crinlf; return 0;
426 } else if (strcmp(str, "cr-to-lf") == 0) {
427 options->crtolf = !options->crtolf; return 0;
428 } else {
429 goto trerr;
430 }
431 break;
432 case 'n':
433 if (strcmp(str, "no-lf") == 0) {
434 options->nolf = !options->nolf; return 0;
435 } else if (strcmp(str, "no-cr") == 0) {
436 options->nocr = !options->nocr; return 0;
437 } else {
438 goto trerr;
439 }
440 break;
441 default:
442 trerr:
443 fprintf(stderr, "invalid translation option: %s\n", str);
444 return 1;
445 }
446 }
447
448 int
449 setroptions()
450 {
451 if (itropts.crtolf)
452 cntrl.c_iflag |= ICRNL;
453 else
454 cntrl.c_iflag &= ~ICRNL;
455
456 if (itropts.lftocr)
457 cntrl.c_iflag |= INLCR;
458 else
459 cntrl.c_iflag &= ~INLCR;
460
461 if (tropts.crtolf)
462 cntrl.c_oflag |= OCRNL;
463 else
464 cntrl.c_oflag &= ~OCRNL;
465
466 if (tropts.lftocr)
467 cntrl.c_oflag |= ONLRET;
468 else
469 cntrl.c_oflag &= ~ONLRET;
470
471 if (tropts.crinlf)
472 cntrl.c_oflag |= ONLCR;
473 else
474 cntrl.c_oflag &= ~ONLCR;
475
476 return settermattr(fd, &cntrl);
477 }
478
479 inline int
480 termchck(const void *term)
481 {
482 #ifdef __linux__
483 struct termios2 *iterm = (struct termios2*)term;
484 #define TERMIOS_STRUCT termios2
485 #else
486 struct termios *iterm = (struct termios*)term;
487 #define TERMIOS_STRUCT termios
488 #endif
489 for (size_t i = 0; i < sizeof(struct TERMIOS_STRUCT); i++)
490 if (((char*)iterm)[i] != 0)
491 return 1;
492
493 return 0;
494 }
495
496 void *
497 writeport(void *unused)
498 {
499 struct timespec ts;
500 ts.tv_sec = swritedelay;
501 ts.tv_nsec = nswritedelay;
502 wts.tv_sec = 0;
503 wts.tv_nsec = chardelay;
504 writebuff = malloc(wbuffsz * sizeof(char));
505 scratchw = malloc(scratchwsz * sizeof(int));
506
507 if (writebuff == NULL)
508 die(1, "buffer allocation failed\n");
509 if (scratchw == NULL)
510 die(1, "scratch buffer allocation failed\n");
511
512 int escape = 0;
513
514 for (;;) {
515 ssize_t inln = read(STDIN_FILENO, writebuff, wbuffsz - 1);
516 if (inln > 0) {
517 if (escape) {
518 getcmd(escape);
519 escape = 0;
520 }
521 if (writebuff[0] == escapechar) {
522 if (inln > 1)
523 getcmd(escape);
524 else
525 escape = 1;
526 }
527
528 if (backspc != tbackspc)
529 replacechar(writebuff, inln, backspc, tbackspc);
530
531 if (tropts.nocr)
532 inln = rmchar(writebuff, scratchw, inln, CR);
533 if (tropts.nolf)
534 inln = rmchar(writebuff, scratchw, inln, LF);
535 if (tropts.lfincr)
536 inln = addchar(writebuff, scratchw, inln, wbuffsz, CR, LF, 1);
537
538 if (inln > 1) {
539 for (int i = 0; i <= inln; i++) {
540 write(fd, &writebuff[i], 1);
541 nanosleep(&wts, NULL);
542 }
543 } else {
544 write(fd, writebuff, 1);
545 }
546 }
547 nanosleep(&ts, NULL);
548 }
549 return NULL;
550 }
551
552 void *
553 readport(void *unused)
554 {
555 struct timespec ts;
556 ts.tv_sec = sreaddelay;
557 ts.tv_nsec = nsreaddelay;
558 readbuff = malloc(rbuffsz * sizeof(char));
559 scratchr = malloc(scratchrsz * sizeof(int));
560
561 if (readbuff == NULL)
562 die(1, "buffer allocation failed\n");
563 if (scratchr == NULL)
564 die(1, "scratch buffer allocation failed\n");
565
566 interchck();
567
568 /* disable stdout buffering */
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 return NULL;
590 }
591
592 inline void __attribute__((hot))
593 replacechar(char *str, ssize_t size, const char find, const char repl)
594 {
595 for (int i = 0; i < size; i++)
596 if (str[i] == find)
597 str[i] = repl;
598 }
599
600 /* TODO: optimize the function and allow for offsets greater than 1 */
601 inline ssize_t __attribute__((hot))
602 addchar(char *str, int *scratch, ssize_t size, const size_t bsize,const char find, const char inp, const int offset)
603 {
604 int c = 0;
605 for (int i = 0; i < size; i++) {
606 if (str[i] == find) {
607 scratch[c] = i;
608 c++;
609 }
610 }
611 if (!c)
612 return(size);
613 if ((size + c) > bsize)
614 c = bsize - size;
615
616 if (scratch[0] == 0 && offset < 0) {
617 memmove(&str[0]+1, &str[0], size);
618 scratch[0] = 1;
619 }
620 for (int i = c; i > 0; i--) {
621 for (int s = size; s >= scratch[i]; s--)
622 str[s + offset] = str[s];
623 str[scratch[i]] = inp;
624 }
625 return (size + c);
626 }
627
628 inline ssize_t __attribute__((hot))
629 rmchar(char *str, int *scratch, ssize_t size, const char find)
630 {
631 int c = 0;
632 for (int i = 0; i < size; i++) {
633 if (str[i] == find) {
634 scratch[c] = i;
635 c++;
636 }
637 }
638 if (!c)
639 return(size);
640
641 for (int i = c; i > 0; i--)
642 for (int s = size; s >= scratch[i]; s--)
643 str[s] = str[s + 1];
644
645 return(size - c);
646 }
647
648 void
649 interchck()
650 {
651 if (interactive) {
652 if (gettermattr(STDIN_FILENO, &origterm) < 0 )
653 die(1, "failed to get terminal attributes\n");
654
655 newterm = origterm;
656
657 if (!canonical)
658 newterm.c_lflag &= ~ECHO;
659 if (!half)
660 newterm.c_lflag &= ~ICANON;
661
662 if (soft)
663 newterm.c_iflag |= IXONXOFF;
664
665 newterm.c_iflag |= INLCR;
666 newterm.c_cc[VMIN] = minchars;
667 newterm.c_cc[VINTR] = _POSIX_VDISABLE;
668 newterm.c_cc[VSUSP] = _POSIX_VDISABLE;
669 newterm.c_cc[VQUIT] = _POSIX_VDISABLE;
670
671 if (backspace)
672 tbackspc = BS;
673 else
674 tbackspc = DEL;
675
676 backspc = origterm.c_cc[VERASE];
677
678 if (settermattr(STDOUT_FILENO, &newterm) < 0)
679 die(1, "failed to set terminal attributes\n");
680 }
681 return;
682 }
683
684 void
685 cechck()
686 {
687 if ((!half || !canonical) && interactive) {
688 newterm.c_lflag |= ECHO;
689 newterm.c_lflag |= ICANON;
690 if (settermattr(STDIN_FILENO, &newterm) < 0)
691 fprintf(stderr, "failed to enable echo and/or canonical mode\n");
692 }
693 return;
694 }
695
696 void
697 getcmd(int escape)
698 {
699 char cmdchar;
700 char ttr[64];
701 unsigned int tspd;
702
703 if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))
704 interactive = 1;
705
706 interchck();
707
708 if (escape)
709 cmdchar = writebuff[0];
710 else
711 cmdchar = writebuff[1];
712
713 switch (cmdchar) {
714 case '.':
715 die(0,"\n[EOT]\n");
716 break;
717 case 'b':
718 if (backspace)
719 tbackspc = DEL;
720 else
721 tbackspc = BS;
722 backspace = !backspace;
723 break;
724 case 'h':
725 if (half)
726 newterm.c_lflag &= ~ECHO;
727 else
728 newterm.c_lflag |= ECHO;
729 half = !half;
730 if (settermattr(STDOUT_FILENO, &newterm) < 0)
731 die(1, "failed to set terminal attributes\n");
732 break;
733 case 'c':
734 if (canonical)
735 newterm.c_lflag &= ~ICANON;
736 else
737 newterm.c_lflag |= ICANON;
738 canonical = !canonical;
739 if (settermattr(STDOUT_FILENO, &newterm) < 0)
740 die(1, "failed to set terminal attributes\n");
741 break;
742 case 'w':
743 cechck();
744 if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
745 replacechar(ttr, 63, LF, '\0');
746 ssize_t frln;
747 int wfd = open(ttr, O_RDONLY);
748 if (fd == -1) {
749 perror("error opening file");
750 break;
751 }
752
753 while ((frln = read(wfd, writebuff, wbuffsz - 1)) > 0) {
754 for (int i = 0; i <= frln; i++) {
755 write(fd, &writebuff[i], 1);
756 nanosleep(&wts, NULL);
757 }
758 }
759 }
760 goto finish;
761 case 's':
762 cechck();
763 if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
764 replacechar(ttr, 63, LF, '\0');
765 tspd = strtoui(ttr, "invalid speed\n", 0);
766 if (tspd != uintmax) {
767 ospeed = ispeed = tspd;
768 settermspd(ispeed, ospeed, &cntrl);
769 if (settermattr(fd, &cntrl) != 0) {
770 fprintf(stderr, "failed to set baudrate [RX:%u | TX:%u]\n", ispeed, ospeed);
771 }
772 }
773 }
774 goto finish;
775 case 'd':
776 cechck();
777 if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
778 replacechar(ttr, 63, LF, '\0');
779 chardelay = strtoui(ttr, "invalid delay\n", 0);
780 if (chardelay != uintmax) {
781 wts.tv_sec = 0;
782 wts.tv_nsec = chardelay;
783 }
784 }
785 goto finish;
786 case 't':
787 cechck();
788 fprintf(stderr, "additional output translation option: ");
789 if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
790 replacechar(ttr, 63, LF, '\0');
791 if(troptions(&tropts, ttr))
792 goto finish;
793 if (setroptions())
794 fprintf(stderr, "could not set new options\n");
795
796 }
797 goto finish;
798 case 'T':
799 cechck();
800 printf("additional input translation option: ");
801 if (fgets(ttr, sizeof(ttr), stdin) != NULL) {
802 replacechar(ttr, 63, LF, '\0');
803 if (troptions(&itropts, ttr))
804 goto finish;
805 if (setroptions())
806 fprintf(stderr, "could not set new options\n");
807 }
808 finish:
809 if (!half && interactive)
810 newterm.c_lflag &= ~ECHO;
811 if (!canonical && interactive)
812 newterm.c_lflag &= ~ICANON;
813 settermattr(STDIN_FILENO, &newterm);
814 break;
815 case 'p':;
816 int st;
817 struct timespec ts;
818 ts.tv_sec = spulsedelay;
819 ts.tv_nsec = nspulsedelay;
820
821 if (ioctl(fd, TIOCMGET, &st) != 0) {
822 fprintf(stderr, "failed to get port status\n");
823 break;
824 }
825 st ^= TIOCM_DTR;
826 if (ioctl(fd, TIOCMSET, &st) != 0) {
827 fprintf(stderr, "failed to set DTR [assertion]\n");
828 break;
829 }
830
831 nanosleep(&ts, NULL);
832
833 st ^= TIOCM_DTR;
834 if (ioctl(fd, TIOCMSET, &st) != 0)
835 fprintf(stderr, "failed to set DTR [negation]\n");
836 break;
837 case BS:
838 case DEL:
839 case ESC:
840 break;
841 default:
842 fprintf(stderr, "not a valid command [%c]\n", cmdchar);
843 break;
844 }
845 return;
846 }
847
848 void
849 sighandl(const int signo)
850 {
851 die(128 + signo, "\nrecieved signal [%d], exiting\n", signo);
852 }
853
854 void
855 die(const int code, const char *msg, ...)
856 {
857 va_list fpa;
858 if (fd != -1)
859 close(fd);
860
861 if (writebuff)
862 free(writebuff);
863 if (readbuff)
864 free(readbuff);
865 if (scratchw)
866 free(scratchw);
867 if (scratchr)
868 free(scratchr);
869
870 if (termchck(&newterm))
871 settermattr(STDIN_FILENO, &origterm);
872
873 va_start(fpa, msg);
874 vfprintf(stderr, msg, fpa);
875 va_end(fpa);
876
877 exit(code);
878 }