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