dterm-nuc

view dterm.c @ 0:ad098a33fd34

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