dterm-nuc
view dterm.c @ 2:68e6bdd9c9df
minor copyright message adjustment
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 08 Apr 2017 00:00:47 +0300 |
parents | ad098a33fd34 |
children |
line source
1 /* dterm - Dumb terminal program
2 * Copyright 2007 Knossos Networks Ltd.
3 * Copyright 2017 John Tsiombikas <nuclear@member.fsf.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
20 #ifndef VERSION
21 #define VERSION "unknown"
22 #endif
23 #define COPYRIGHT \
24 "dterm version " VERSION "\n" \
25 "Copyright 2007 Knossos Networks Ltd.\n" \
26 "Copyright 2017 John Tsiombikas <nuclear@member.fsf.org>\n" \
27 "\n" \
28 "This program is free software; you can redistribute it and/or\n" \
29 "modify it under the terms of the GNU General Public License version 2\n" \
30 "as published by the Free Software Foundation.\n" \
31 "\n" \
32 "This program is distributed in the hope that it will be useful,\n" \
33 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
34 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \
35 "GNU General Public License for more details.\n" \
36 "\n" \
37 "You should have received a copy of the GNU General Public License\n" \
38 "along with this program; if not, write to the Free Software\n" \
39 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n" \
40 "02110-1301, USA.\n"
43 #include <stdio.h>
44 #include <ctype.h>
45 #include <sys/types.h>
46 #include <sys/signal.h>
47 #include <sys/stat.h>
48 #include <sys/wait.h>
49 #include <fcntl.h>
50 #include <termios.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #include <time.h>
55 #include <signal.h>
56 #include <sys/select.h>
57 #include <sys/stat.h>
58 #include <sys/ioctl.h>
59 #include <errno.h>
61 #define FIXTTY if(istty) tcsetattr(0, TCSADRAIN, &savetio)
62 #define DIEP(s) { FIXTTY; perror(s); exit(1); }
63 #define DIE(s,m) { FIXTTY; fprintf(stderr, "%s: %s\n", s, m); exit(1); }
65 /*
66 Sigh
67 */
68 #ifndef CCTS_OFLOW
69 #ifdef CRTSCTS
70 #define CCTS_OFLOW CRTSCTS
71 #endif /* CRTSCTS */
72 #endif /* ! CCTS_OFLOW */
75 /*
76 Valid speed settings.
77 */
78 static struct { int s, c; } speeds[] = {
79 { 50, B50 },
80 { 75, B75 },
81 { 110, B110 },
82 { 134, B134 },
83 { 150, B150 },
84 { 200, B200 },
85 { 300, B300 },
86 { 600, B600 },
87 { 1200, B1200 },
88 { 1800, B1800 },
89 { 2400, B2400 },
90 { 4800, B4800 },
91 { 9600, B9600 },
92 { 19200, B19200 },
93 { 38400, B38400 },
94 #ifdef B7200
95 { 7200, B7200 },
96 #endif
97 #ifdef B14400
98 { 14400, B14400 },
99 #endif
100 #ifdef B28800
101 { 28800, B28800 },
102 #endif
103 #ifdef B57600
104 { 57600, B57600 },
105 #endif
106 #ifdef B76800
107 { 76800, B76800 },
108 #endif
109 #ifdef B115200
110 { 115200, B115200 },
111 #endif
112 #ifdef B230400
113 { 230400, B230400 },
114 #endif
115 #ifdef B460800
116 { 460800, B460800 },
117 #endif
118 #ifdef B921600
119 { 921600, B921600 },
120 #endif
121 { 0, 0 }};
124 /*
125 Save the terminal settings here
126 */
127 static struct termios intio, savetio;
129 static int fd = -1; /* Channel to port */
130 static int cmdchar = 035; /* Command character, default ^] */
131 static char *device = 0; /* Device, no default */
132 static int ispeed = B9600; /* Input speed, default 9600 bps */
133 static int ospeed = B9600; /* Output speed, default 9600 bps */
134 static int twostop = 0; /* Stop bits, if set to CSTOPB, use two */
135 static int parity = 0; /* Parity, -1 = mark, -2 = space 0=none */
136 /* PARENB enables parity, PARODD = odd */
137 static int bits = CS8; /* CS5-CS8 for 5-8 bits per character */
138 static int ctsflow = 0; /* CCTS_OFLOW enables CTS flow control */
139 static int xonflow = 0; /* IXON | IXOFF for in & out soft F/c */
140 static int modemcontrol = 0; /* Set to HUPCLto enable modem control */
141 static int markparity = 0; /* Mark parity -- set high bit on out */
142 static int backspace = 0; /* Backspace char, send on BS or DEL */
143 static int maplf = 0; /* If set, map LF to CR */
144 static int ignorecr = 0; /* Ignore carriage returns */
145 static int crlf = 0; /* Send CRLF on CR */
146 static int istty; /* Result of isatty() */
147 static char *connname = 0; /* Connection name found in config */
148 static int sendbreak = 0; /* Break requested */
149 static int modem = 0; /* Current modem status */
150 static int setmodem = 0; /* Set modem status to this */
151 static int delay = 0; /* Millisecond delay after each key */
152 static int linedelay = 0; /* Millisecond delay after each CR */
153 static int showspecial = 0; /* Show special chars as [xx] */
155 static void
156 help() {
157 fprintf(stderr,
158 "dterm commands:\n"
159 "^%c Enter command mode quit, q Exit\n"
160 "show Display configuration help, h, ? Display this message\n"
161 "!<command> Execute shell command version Display version\n"
162 "@<filename> Get configuration from <filename>\n"
163 "<device> Connect to <device> 300,1200,9600 Set speed\n"
164 "5, 6, 7, 8 Set bits per character 1, 2 Set stop bits\n"
165 "e, o, n, m, s Set parity"
166 #ifdef CCTS_OFLOW
167 " cts,nocts Enable / disable CTS\n"
168 #else
169 "\n"
170 #endif
171 "dtr, nodtr Raise / lower DTR b Send a 500ms break\n"
172 "rts, norts Raise / lower RTS d, r Toggle DTR, RTS\n"
173 "rx <filename> Receive file (XMODEM) sx <filename> Send file (XMODEM)\n"
174 "rz Receive file (ZMODEM) sz <filename> Send file (ZMODEM)\n"
175 "xon,noxon Enable / disable XON/XOFF flow control\n"
176 "modem Hang up modem on exit, exit if modem hangs up\n"
177 "nomodem Do not do modem control\n"
178 "bs, nobs Enable / disable mapping of Delete to Backspace\n"
179 "del, nodel Enable / disable mapping of Backspace to Delete\n"
180 "maplf, nomaplf Enable / disable mapping of LF to CR\n"
181 "igncr, noigncr Ignore / output carriage returns\n"
182 "crlf, nocrlf Enable / disable sending LF after each CR\n"
183 "delay=<n> Add delay of <n> ms after each charachter sent\n"
184 "crwait=<n> Add delay of <n> ms after each line sent\n"
185 "esc=<c> Set command mode character to Ctrl/<c>\n"
186 "ctrl,hex,noctrl Show control characters as ^n (except tab, CR, LF)\n"
190 , cmdchar + '@');
191 }
196 /*
197 Show current setup
198 */
199 static void
200 showsetup() {
201 int i;
202 fprintf(stderr, "Port: %s\n", device);
203 fprintf(stderr, "Communications parameters: ");
204 for(i = 0; speeds[i].s && speeds[i].c != ispeed; )
205 i++;
206 fprintf(stderr, "%d", speeds[i].s);
207 if(ospeed != ispeed) {
208 for(i = 0; speeds[i].s && speeds[i].c != ospeed; )
209 i++;
210 fprintf(stderr, "/%d", speeds[i].s);
211 }
212 if(bits == CS5) fprintf(stderr, " 5");
213 if(bits == CS6) fprintf(stderr, " 6");
214 if(bits == CS7) fprintf(stderr, " 7");
215 if(bits == CS8) fprintf(stderr, " 8");
216 if(parity == 0) fprintf(stderr, " n");
217 if(parity == PARENB) fprintf(stderr, " e");
218 if(parity == (PARENB|PARODD)) fprintf(stderr, " o");
219 if(parity == -1) fprintf(stderr, " m");
220 if(parity == -2) fprintf(stderr, " s");
221 if(twostop) fprintf(stderr, " 2\n");
222 else fprintf(stderr, " 1\n");
223 fprintf(stderr, "Flow/modem control:");
224 if(xonflow) fprintf(stderr, " xon");
225 if(ctsflow) fprintf(stderr, " cts");
226 if(modemcontrol)fprintf(stderr, " modem");
227 putc('\n', stderr);
228 fprintf(stderr, "Character mappings:");
229 if(backspace == 8) fprintf(stderr, " bs");
230 if(backspace == 127) fprintf(stderr, " del");
231 if(maplf) fprintf(stderr, " maplf");
232 if(ignorecr) fprintf(stderr, " igncr");
233 if(crlf) fprintf(stderr, " crlf");
234 if(showspecial == 1) fprintf(stderr, " ctrl");
235 if(showspecial == 2) fprintf(stderr, " hex");
236 putc('\n', stderr);
237 printf("Modem control: DTR: %s RTS: %s CTS: %s DSR: %s DCD: %s\n",
238 setmodem & TIOCM_DTR ? "on" : "off",
239 setmodem & TIOCM_RTS ? "on" : "off",
240 modem & TIOCM_CTS ? "on" : "off",
241 modem & TIOCM_DSR ? "on" : "off",
242 modem & TIOCM_CD ? "on" : "off");
243 fprintf(stderr, "Escape character: ^%c\n", cmdchar + '@');
244 fflush(stderr);
245 }
248 /*
249 Use the rzsz or lrzsz package to do file transfer
250 Mode should be "rx", "rb" or "rz" to receive a file, "sx", "sb" or "sz"
251 to send. "rz" & "rb" don't require a file name; the others do.
252 */
253 static int
254 rzsz(char *mode, char *file) {
255 static char *loc[] = { "/usr/bin/", "/usr/local/bin/l",
256 "/usr/bin/l", "/usr/local/bin/", 0 };
257 char path[128];
258 char *cmd;
259 int i;
260 struct stat sb;
261 pid_t pid;
263 /*
264 Check file name
265 */
266 if(*file == 0 && (mode[0] != 'r' || mode[1] == 'x')) {
267 fprintf(stderr, "File name required for %c%c\n",
268 mode[0], mode[1]);
269 return -1;
270 }
272 /*
273 Find the appropriate command
274 */
275 for(i = 0; loc[i]; i++) {
276 if(mode[1] == 'a') {
277 sprintf(path, "%sascii-xfr", loc[i]);
278 } else {
279 sprintf(path, "%s%c%c", loc[i], mode[0], mode[1]);
280 }
281 if(!stat(path, &sb) && (sb.st_mode & S_IXUSR))
282 break;
283 }
284 if(!loc[i]) {
285 fprintf(stderr, "RZ/SZ/ascii-xfr not installed\n");
286 return -1;
287 }
289 /*
290 Get the command name
291 */
292 cmd = strrchr(path, '/');
293 if(cmd) cmd++;
294 else cmd = path;
296 /*
297 Fork subprocess. Set stdin & stdout to the serial line
298 stderr remains on the console for progress messages
299 */
300 pid = fork();
301 if(pid == 0) {
302 dup2(fd, 0);
303 dup2(fd, 1);
304 if(mode[1] == 'a') {
305 char delay_str[64];
306 sprintf(delay_str, "%d", linedelay);
307 if(*file) execl(path, cmd, "-s", "-l", delay_str, file, NULL);
308 else execl(path, cmd, "-r", NULL);
309 } else {
310 if(*file) execl(path, cmd, file, NULL);
311 else execl(path, cmd, NULL);
312 }
313 _exit(127);
314 }
315 else if(pid == -1) {
316 perror("fork");
317 return -1;
318 }
320 /*
321 Wait for the process to complete and give a meaningful
322 response
323 */
324 signal(SIGINT, SIG_IGN);
325 signal(SIGQUIT, SIG_IGN);
326 signal(SIGTERM, SIG_IGN);
327 while(waitpid(pid, &i, 0) != pid) {}
328 signal(SIGINT, SIG_DFL);
329 signal(SIGQUIT, SIG_DFL);
330 signal(SIGTERM, SIG_DFL);
332 if(WIFSIGNALED(i)) {
333 fprintf(stderr, "Terminated on signal %d%s\n", WTERMSIG(i),
334 WCOREDUMP(i) ? " (core dumped)" : "");
335 return -1;
336 }
337 if(WTERMSIG(i))
338 return -1;
339 return 0;
340 }
344 static int readconfig(char *, char *);
346 /*
347 Process commands passed as strings, may be several commands on a line
348 Allow ugliness like 8n1 -- treat as 8, n, 1
349 */
350 static int
351 setup(char *s, char *cffile, int cfline) {
352 char *t, *d;
353 int j,k;
354 int ret = 0;
355 struct stat sb;
356 char ttybuf[128];
357 int justospeed = 0;
359 while(*s) {
360 /*
361 Skip whitespace, commas
362 */
363 while(isspace(*s) || *s == ',')
364 s++;
365 if(!*s) break;
367 /*
368 '?' = help
369 */
370 if(*s == '?') {
371 help();
372 return -4;
373 }
375 /*
376 '!' = shell command
377 */
378 if(*s == '!') {
379 if((t = strchr(s, '\n')))
380 *t = 0;
381 system(++s);
382 break;
383 }
385 /*
386 File transfer (sx, sz, rx, rz, sa, ra)
387 Run rzsz or ascii-xfr to perform the actual file transfer
388 Allow "sxfilename", "sx filename" or just "rz"
389 */
390 if((s[0] == 's' || s[0] == 'r') &&
391 (s[1] == 'x' || s[1] == 'z' || s[1] == 'a')) {
392 if((t = strchr(s, '\n')))
393 *t = 0;
394 for(t = s + 2; isspace(*t); ) t++;
395 return rzsz(s, t);
396 }
398 /*
399 If a number, process it.
400 If it's a valid speed, use that -- if nnn/mmm, set both to nnn
401 for the first time, and flag things so mmm just sets ospeed
402 5-8 = data bits, 1,2 sets stops
403 Anything else is error
404 */
405 if(isdigit(*s)) {
406 j = strtol(s, &s, 10);
407 for(k = 1; speeds[k].s; k++)
408 if(speeds[k].s == j)
409 break;
410 if(speeds[k].s) {
411 ospeed = speeds[k].c;
412 if(!justospeed)
413 ispeed = ospeed;
414 if(*s == '/') {
415 s++;
416 justospeed = 1;
417 }
418 else justospeed = 0;
419 }
420 else if(j == 5) bits = CS5;
421 else if(j == 6) bits = CS6;
422 else if(j == 7) bits = CS7;
423 else if(j == 8) bits = CS8;
424 else if(j == 1) twostop = 0;
425 else if(j == 2) twostop = CSTOPB;
426 else {
427 if(cffile)
428 fprintf(stderr, "in %s:%d: ",
429 cffile, cfline);
430 fprintf(stderr, "%d: invalid speed/bits\n", j);
431 ret = -1;
432 }
433 }
435 /*
436 Otherwise, get the alpha-only keyword, see if it matches anything
437 useful.
438 */
439 else {
440 t = s;
441 while(isalpha(*t)) t++;
442 j = *t;
443 *t = 0;
444 if( !strcasecmp(s, "xon"))
445 xonflow = IXON | IXOFF;
446 else if(!strcasecmp(s, "noxon"))
447 xonflow = 0;
449 #ifdef CCTS_OFLOW
450 else if(!strcasecmp(s, "cts"))
451 ctsflow = CCTS_OFLOW;
452 else if(!strcasecmp(s, "nocts"))
453 ctsflow = 0;
454 #endif
455 else if(!strcasecmp(s, "modem"))
456 modemcontrol = HUPCL;
457 else if(!strcasecmp(s, "nomodem"))
458 modemcontrol = 0;
459 else if(!strcasecmp(s, "E"))
460 parity = PARENB;
461 else if(!strcasecmp(s, "O"))
462 parity = PARENB | PARODD;
463 else if(!strcasecmp(s, "M"))
464 parity = -1;
465 else if(!strcasecmp(s, "S"))
466 parity = -2;
467 else if(!strcasecmp(s, "N"))
468 parity = 0;
469 else if(!strcasecmp(s, "q") || !strcasecmp(s, "quit"))
470 ret = -3;
471 else if(!strcasecmp(s, "h") || !strcasecmp(s, "help"))
472 help();
473 else if(!strcasecmp(s, "del"))
474 backspace = 127;
475 else if(!strcasecmp(s, "bs"))
476 backspace = 8;
477 else if(!strcasecmp(s,"nobs") || !strcasecmp(s,"nodel"))
478 backspace = 0;
479 else if(!strcasecmp(s, "noesc"))
480 cmdchar = 0;
481 else if(!strcasecmp(s, "maplf"))
482 maplf = 1;
483 else if(!strcasecmp(s, "nomaplf"))
484 maplf = 0;
485 else if(!strcasecmp(s, "igncr"))
486 ignorecr = 1;
487 else if(!strcasecmp(s, "noigncr"))
488 ignorecr = 0;
489 else if(!strcasecmp(s, "crlf"))
490 crlf = 1;
491 else if(!strcasecmp(s, "nocrlf"))
492 crlf = 0;
493 else if(!strcasecmp(s, "b"))
494 sendbreak = 1;
495 else if(!strcasecmp(s, "d"))
496 setmodem ^= TIOCM_DTR;
497 else if(!strcasecmp(s, "r"))
498 setmodem ^= TIOCM_RTS;
499 else if(!strcasecmp(s, "dtr"))
500 setmodem |= TIOCM_DTR;
501 else if(!strcasecmp(s, "rts"))
502 setmodem |= TIOCM_RTS;
503 else if(!strcasecmp(s, "nodtr"))
504 setmodem &= ~TIOCM_DTR;
505 else if(!strcasecmp(s, "norts"))
506 setmodem &= ~TIOCM_RTS;
507 else if(!strcasecmp(s, "show"))
508 showsetup();
509 else if(!strcasecmp(s, "version"))
510 fputs(COPYRIGHT, stderr);
511 else if(!strcasecmp(s, "ctrl"))
512 showspecial = 1;
513 else if(!strcasecmp(s, "hex"))
514 showspecial = 2;
515 else if(!strcasecmp(s, "noctrl") ||
516 !strcasecmp(s, "noctrl"))
517 showspecial = 0;
519 /*
520 No?
521 @<filename> includes a file
522 !cmd Run a command
523 esc=c sets escape char
524 <device> select device
525 */
526 else {
527 *t = j;
528 while(*t && !isspace(*t) && *t != ',')
529 t++;
530 j = *t;
531 *t = 0;
532 if(*s == '@') {
533 k = readconfig(++s, 0);
534 if(k == -2) {
535 if(cffile)
536 fprintf(stderr,
537 "in %s:%d: ",
538 cffile, cfline);
539 fprintf(stderr, "%s: %s\n",
540 s, strerror(errno));
541 ret = -1;
542 }
543 goto next;
544 }
545 if(!strncasecmp(s, "esc=", 4)) {
546 cmdchar = s[4] & 0x1f;
547 goto next;
548 }
549 else if(!strncasecmp(s, "delay=", 6)) {
550 delay = atoi(s+6);
551 goto next;
552 }
553 else if(!strncasecmp(s, "crwait=", 7)) {
554 linedelay = atoi(s+7);
555 goto next;
556 }
557 d = s;
558 k = stat(d, &sb);
559 if(k && *d != '/') {
560 sprintf(ttybuf, "/dev/%.100s", d);
561 d = ttybuf;
562 k = stat(d, &sb);
563 }
564 if(!k) {
565 if((sb.st_mode & S_IFMT) == S_IFCHR) {
566 ret = 1;
567 device = strdup(d);
568 goto next;
569 }
570 }
571 if(cffile)
572 fprintf(stderr, "in %s:%d: ",
573 cffile, cfline);
574 fprintf(stderr,
575 "%s: unrecognised keyword/device\n", s);
576 ret = -1;
577 }
578 next: *t = j;
579 s = t;
580 }
581 }
582 return ret;
583 }
586 /*
587 Read a config file
588 Input lines can be lists of config words, or in the form
589 name: words
590 If name: form, only lines matching passed name are used
591 */
592 static int
593 readconfig(char *file, char *name) {
594 char buf[1024];
595 FILE *f;
596 char *s, *t;
597 int lineno = 0;
598 int ret = 0, r;
600 /*
601 ~/file = get file from $HOME/file
602 */
603 if(*file == '~' && file[1] == '/' && (s = getenv("HOME"))) {
604 snprintf(buf, sizeof(buf), "%s/%s", s, file+2);
605 file = buf;
606 }
608 /*
609 Return -2 if can't open
610 */
611 f = fopen(file, "r");
612 if(!f) return -2;
614 /*
615 Read input, strip # commends
616 Count lines
617 Keep track of return code
618 */
619 while(fgets(buf, sizeof(buf), f)) {
620 lineno++;
621 if((s = strchr(buf, '#')))
622 *s = 0;
623 for(s = buf; isspace(*s);)
624 s++;
625 if(!*s) continue;
626 for(t = s; *t && *t != ':' && *t != ',' && !isspace(*t); )
627 t++;
628 if(*t == ':') {
629 *t++ = 0;
630 if(!name)
631 continue;
632 if(strcmp(name, s))
633 continue;
634 s = t;
635 connname = name;
636 }
638 r = setup(s, file, lineno);
639 if(r < 0)
640 ret = r;
641 if(r > 0 && !ret)
642 ret = 1;
643 }
645 fclose(f);
646 return ret;
647 }
650 /*
651 Sleep n milliseconds
652 */
653 static void
654 millisleep(int n) {
655 struct timespec t;
656 t.tv_sec = n / 1000;
657 t.tv_nsec = (n % 1000) * 1000000;
658 nanosleep(&t, 0);
659 }
662 /*
663 Set up the port
664 */
665 static int
666 setupport(int fd) {
667 int parityflags;
668 int modemflags;
669 int spaceparity = 0;
670 struct termios tio;
671 int m;
673 /*
674 See what to do with parity
675 */
676 markparity = 0;
677 parityflags = parity;
678 if(parity == -1) {
679 parityflags = 0;
680 markparity = ISTRIP;
681 }
682 else if(parity == -2) {
683 parityflags = 0;
684 spaceparity = ISTRIP;
685 }
687 /*
688 If no modem control, use local mode
689 */
690 modemflags = ctsflow | modemcontrol;
691 if(!modemflags)
692 modemflags = CLOCAL;
694 /*
695 Set the speed and params
696 */
697 tcgetattr(fd, &tio);
698 tio.c_iflag = IGNBRK | IGNPAR | xonflow | spaceparity | markparity;
699 tio.c_cflag = CREAD | HUPCL | modemflags | bits | parityflags;
700 tio.c_oflag = 0;
701 tio.c_lflag = 0;
702 cfsetispeed(&tio, ispeed);
703 cfsetospeed(&tio, ospeed);
704 if(tcsetattr(fd, TCSAFLUSH, &tio) == -1)
705 return -1;
707 /*
708 Set up the modem lines
709 */
710 ioctl(fd, TIOCMGET, &modem);
711 if((modem & (TIOCM_RTS | TIOCM_DTR)) != (setmodem & (TIOCM_RTS |
712 TIOCM_DTR))) {
713 m = setmodem & (TIOCM_RTS | TIOCM_DTR);
714 ioctl(fd, TIOCMBIS, &m);
715 m = (~setmodem) & (TIOCM_RTS | TIOCM_DTR);
716 ioctl(fd, TIOCMBIC, &m);
717 }
719 /*
720 Send a break if requested
721 */
722 if(sendbreak) {
723 ioctl(fd, TIOCSBRK, 0);
724 millisleep(500);
725 ioctl(fd, TIOCCBRK, 0);
726 sendbreak = 0;
727 }
729 /*
730 Get the current modem status
731 */
732 ioctl(fd, TIOCMGET, &modem);
733 setmodem = modem;
735 return 0;
736 }
739 /*
740 Set up the modem. This is a little tricky due to the need to
741 ensure the modem does not block before we set it up.
742 */
743 static int
744 openport(char *device) {
745 int fd;
747 if((fd = open(device, O_RDWR|O_NONBLOCK, 0)) < 0)
748 DIEP(device)
749 if(setupport(fd) < 0)
750 DIEP(device)
751 millisleep(10);
752 fcntl(fd, F_SETFL, 0);
754 ioctl(fd, TIOCMGET, &modem);
755 setmodem = modem;
757 setenv("DTERM_PORT", device, 1);
758 return fd;
759 }
762 /*
763 Usage
764 */
765 static void
766 usage(char *this) {
767 fprintf(stderr, "Usage: %s port/setup\n"
768 " '%s help' for help\n", this, this);
769 exit(1);
770 }
773 int
774 main(int argc, char **argv) {
775 int nfd;
776 int i, j, c;
777 unsigned char inbuf;
778 char buf[256];
779 char cbuf[8];
780 char *s;
781 int done;
782 fd_set fds;
783 struct timeval delay_tv, *readdelay;
784 struct stat sb;
786 /*
787 Do the right things depending on whether stdin & stdout are TTYs
788 If the input is a TTY, we need to put it into non-canonical mode
789 (which we'll actually do later); otherwise, default to turning
790 LFs from a file into CRs. (See [no]maplf setup command.)
791 If the output is a TTY, default to passing CR transparently;
792 otherwise default to ignoring CR so that output is logged to a
793 files etc nicely. (See [no]igncr.)
794 */
795 istty = isatty(0);
796 if(istty) {
797 tcgetattr(0, &savetio);
798 maplf = 0;
799 }
800 else maplf = 1;
801 if(isatty(1))
802 ignorecr = 0;
803 else ignorecr = 1;
805 /*
806 Read default config, if no private .dtermrc, use /etc/dtermrc
807 */
808 setmodem = modem = TIOCM_DTR | TIOCM_RTS;
809 if(readconfig("~/.dtermrc", argv[1]) == -2)
810 readconfig("/etc/dtermrc", argv[1]);
812 /*
813 Parse args
814 If only arg is "help", exit
815 */
816 i = 1;
817 if(connname) i = 2;
818 for(; i < argc; i++)
819 if(setup(argv[i], 0, 0) < 0)
820 usage(argv[0]);
821 if(argc == 2 && !strcasecmp(argv[1], "help"))
822 exit(0);
824 /*
825 If no device specified, have a crack at finding a default device
826 Look for the first on-board device first; failing that look for
827 the first USB device. For Linux & BSD these are:
828 OS Onboard USB
829 Linux ttyS0 ttyUSB0
830 BSD ttyd0 ttyU0
831 Note that this rather assumes that the existence of a device
832 name in /dev indicates the actual existence of a device. Mostly
833 this is the case on modern systems. Mostly.
834 */
835 if(!device) {
836 if( !stat("/dev/ttyS0", &sb)) device = "/dev/ttyS0";
837 else if(!stat("/dev/ttyd0", &sb)) device = "/dev/ttyd0";
838 else if(!stat("/dev/ttyUSB0", &sb)) device = "/dev/ttyUSB0";
839 else if(!stat("/dev/ttyU0", &sb)) device = "/dev/ttyU0";
840 else {
841 fprintf(stderr, "Could not find default device\n");
842 usage(argv[0]);
843 }
844 }
846 /*
847 If the controlling TTY is in fact a TTY, set it up
848 */
849 if(istty) {
850 intio = savetio;
851 intio.c_oflag = 0;
852 intio.c_lflag = 0;
853 intio.c_iflag = savetio.c_iflag & ~(INLCR|IGNCR|ICRNL);
855 intio.c_cc[VEOF] = _POSIX_VDISABLE;
856 intio.c_cc[VEOL] = _POSIX_VDISABLE;
857 intio.c_cc[VEOL2] = _POSIX_VDISABLE;
858 intio.c_cc[VERASE] = _POSIX_VDISABLE;
859 intio.c_cc[VWERASE] = _POSIX_VDISABLE;
860 intio.c_cc[VKILL] = _POSIX_VDISABLE;
861 intio.c_cc[VREPRINT] = _POSIX_VDISABLE;
862 intio.c_cc[VINTR] = _POSIX_VDISABLE;
863 intio.c_cc[VQUIT] = _POSIX_VDISABLE;
864 intio.c_cc[VSUSP] = _POSIX_VDISABLE;
865 #ifdef VDSUSP
866 intio.c_cc[VDSUSP] = _POSIX_VDISABLE;
867 #endif
868 intio.c_cc[VLNEXT] = _POSIX_VDISABLE;
869 intio.c_cc[VDISCARD] = _POSIX_VDISABLE;
870 #ifdef VSTATUS
871 intio.c_cc[VSTATUS] = _POSIX_VDISABLE;
872 #endif
873 tcsetattr(0, TCSADRAIN, &intio);
874 }
876 /*
877 Connect to serial port
878 */
879 fd = openport(device);
880 if(fd < 0) exit(1);
882 /*
883 Main loop
884 */
885 readdelay = 0;
886 done = 0;
887 while(!done) {
889 /*
890 Set up the select() call
891 If readdelay is not 0, we're waiting for things to go quiet so we
892 can exit.
893 Errors kill us, execpt for interrupted calls
894 0 return only happens if readdelay is set, so we exit then
895 */
896 FD_ZERO(&fds);
897 if(!readdelay)
898 FD_SET(0, &fds);
899 FD_SET(fd, &fds);
900 i = select(fd + 1, &fds, 0,0, readdelay);
901 if(i == -1 && errno != EINTR)
902 DIEP("select");
903 if(i == 0 && readdelay)
904 break;
906 /*
907 If input from line, read a full buffer in
908 IgnoreCR means sucking CRs out of the buffer (yuck)
909 If EOF (e.g. hangup), exit nicely.
910 */
911 if(FD_ISSET(fd, &fds)) {
912 i = read(fd, buf, sizeof(buf));
913 if(i < 0)
914 DIEP(device);
915 if(!i) break;
916 if(showspecial) {
917 s = buf;
918 do {
919 c = 0;
920 for(j = 0; j < i; j++) {
921 c = (unsigned char) s[j];
922 if(showspecial == 2 && (
923 c == '\t' || c > '~'))
924 break;
925 if(c == '\r' && ignorecr)
926 break;
927 if(( c < ' ' && c != '\t'
928 && c != '\r' && c != '\n')
929 || (c > '~' && c < 160))
930 break;
931 }
932 if(j) write(1, s, j);
933 if(j >= i)
934 break;
935 if(c == '\r' && ignorecr) {
936 /* Do nothing */
937 }
938 else if(c < 32 && showspecial != 2) {
939 cbuf[0] = '^';
940 cbuf[1] = c + '@';
941 write(1, cbuf, 2);
942 }
943 else if(c == 127 && showspecial != 2)
944 write(1, "[DEL]", 5);
945 else {
946 sprintf(cbuf, "[%02x]", c);
947 write(1, cbuf, 4);
948 }
949 j++;
950 s += j;
951 i -= j;
952 } while(i > 0);
953 }
954 else {
955 if(ignorecr) {
956 j = 0;
957 for(s = buf; s < buf + i; s++)
958 if(*s != '\r')
959 buf[j++] = *s;
960 i = j;
961 }
962 write(1, buf, i);
963 }
964 }
966 /*
967 Input on stdin
968 Read a character
969 If EOF, set readdelay to 1 second
970 */
971 if(FD_ISSET(0, &fds)) {
972 if(read(0, &inbuf, 1) < 1) {
973 delay_tv.tv_sec = 1;
974 delay_tv.tv_usec = 0;
975 readdelay = &delay_tv;
976 continue;
977 }
978 /*
979 If command character received, read commands
980 */
981 if(inbuf == cmdchar && istty) {
982 FIXTTY;
983 putchar('\n');
984 for(;;) {
985 fprintf(stderr, "dterm> ");
986 if(!fgets(buf, sizeof(buf), stdin))
987 return 0;
988 if((s = strchr(buf, '#')))
989 *s = 0;
990 for(s = buf; *s; s++)
991 if(!isspace(*s)) break;
992 if(!*s) break;
993 ioctl(fd, TIOCMGET, &modem);
994 i = setup(buf, 0, 0);
995 if(i == -3)
996 return 0;
997 if(i == 1) {
998 nfd = openport(device);
999 if(nfd >= 0) {
1000 close(fd);
1001 fd = nfd;
1002 }
1003 }
1004 else if(setupport(fd))
1005 fprintf(stderr,
1006 "invalid parameters\n");
1007 }
1008 if(istty) tcsetattr(0, TCSADRAIN, &intio);
1009 }
1010 /*
1011 Otherwise do any processing on the character
1012 Add dread high bit disease if mark parity
1013 BS <-> DEL mapping
1014 LF -> CR mapping for files
1015 CR -> CRLF mapping for TTYs
1016 */
1017 else {
1018 if(markparity)
1019 inbuf |= 0x80;
1020 if(backspace && (inbuf == 8 || inbuf == 127))
1021 inbuf = backspace;
1022 if(maplf && inbuf == '\n')
1023 inbuf = '\r';
1024 write(fd, &inbuf, 1);
1025 if(crlf && inbuf == '\r') {
1026 inbuf = '\n';
1027 write(fd, &inbuf, 1);
1028 }
1029 if(linedelay && inbuf == '\r')
1030 millisleep(linedelay);
1031 else if(delay)
1032 millisleep(delay);
1033 }
1034 }
1036 }
1038 /*
1039 Fall out the bottom, cleaning up
1040 */
1041 FIXTTY;
1042 return 0;
1043 }