dterm-nuc
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dterm.c Fri Apr 07 23:43:06 2017 +0300 1.3 @@ -0,0 +1,1045 @@ 1.4 +/* dterm.c Dumb terminal program 19/11/2004/dcs 1.5 + */ 1.6 + 1.7 +/* 1.8 + * Copyright 2007 Knossos Networks Ltd. 1.9 + * Copyright 2017 John Tsiombikas <nuclear@member.fsf.org> 1.10 + * 1.11 + * This program is free software; you can redistribute it and/or 1.12 + * modify it under the terms of the GNU General Public License version 2 1.13 + * as published by the Free Software Foundation. 1.14 + * 1.15 + * This program is distributed in the hope that it will be useful, 1.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 1.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.18 + * GNU General Public License 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License 1.21 + * along with this program; if not, write to the Free Software 1.22 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 1.23 + * 02110-1301, USA. 1.24 + */ 1.25 + 1.26 +#ifndef VERSION 1.27 +#define VERSION "unknown" 1.28 +#endif 1.29 +#define COPYRIGHT \ 1.30 +"dterm version " VERSION " Copyright 2007 Knossos Networks Ltd.\n" \ 1.31 +" Copyright 2017 John Tsiombikas <nuclear@member.fsf.org>\n" \ 1.32 +"\n" \ 1.33 +"This program is free software; you can redistribute it and/or\n" \ 1.34 +"modify it under the terms of the GNU General Public License version 2\n" \ 1.35 +"as published by the Free Software Foundation.\n" \ 1.36 +"\n" \ 1.37 +"This program is distributed in the hope that it will be useful,\n" \ 1.38 +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \ 1.39 +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \ 1.40 +"GNU General Public License for more details.\n" \ 1.41 +"\n" \ 1.42 +"You should have received a copy of the GNU General Public License\n" \ 1.43 +"along with this program; if not, write to the Free Software\n" \ 1.44 +"Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n" \ 1.45 +"02110-1301, USA.\n" 1.46 + 1.47 + 1.48 +#include <stdio.h> 1.49 +#include <ctype.h> 1.50 +#include <sys/types.h> 1.51 +#include <sys/signal.h> 1.52 +#include <sys/stat.h> 1.53 +#include <sys/wait.h> 1.54 +#include <fcntl.h> 1.55 +#include <termios.h> 1.56 +#include <string.h> 1.57 +#include <unistd.h> 1.58 +#include <stdlib.h> 1.59 +#include <time.h> 1.60 +#include <signal.h> 1.61 +#include <sys/select.h> 1.62 +#include <sys/stat.h> 1.63 +#include <sys/ioctl.h> 1.64 +#include <errno.h> 1.65 + 1.66 +#define FIXTTY if(istty) tcsetattr(0, TCSADRAIN, &savetio) 1.67 +#define DIEP(s) { FIXTTY; perror(s); exit(1); } 1.68 +#define DIE(s,m) { FIXTTY; fprintf(stderr, "%s: %s\n", s, m); exit(1); } 1.69 + 1.70 +/* 1.71 + Sigh 1.72 + */ 1.73 +#ifndef CCTS_OFLOW 1.74 +#ifdef CRTSCTS 1.75 +#define CCTS_OFLOW CRTSCTS 1.76 +#endif /* CRTSCTS */ 1.77 +#endif /* ! CCTS_OFLOW */ 1.78 + 1.79 + 1.80 +/* 1.81 + Valid speed settings. 1.82 + */ 1.83 +static struct { int s, c; } speeds[] = { 1.84 + { 50, B50 }, 1.85 + { 75, B75 }, 1.86 + { 110, B110 }, 1.87 + { 134, B134 }, 1.88 + { 150, B150 }, 1.89 + { 200, B200 }, 1.90 + { 300, B300 }, 1.91 + { 600, B600 }, 1.92 + { 1200, B1200 }, 1.93 + { 1800, B1800 }, 1.94 + { 2400, B2400 }, 1.95 + { 4800, B4800 }, 1.96 + { 9600, B9600 }, 1.97 + { 19200, B19200 }, 1.98 + { 38400, B38400 }, 1.99 +#ifdef B7200 1.100 + { 7200, B7200 }, 1.101 +#endif 1.102 +#ifdef B14400 1.103 + { 14400, B14400 }, 1.104 +#endif 1.105 +#ifdef B28800 1.106 + { 28800, B28800 }, 1.107 +#endif 1.108 +#ifdef B57600 1.109 + { 57600, B57600 }, 1.110 +#endif 1.111 +#ifdef B76800 1.112 + { 76800, B76800 }, 1.113 +#endif 1.114 +#ifdef B115200 1.115 + { 115200, B115200 }, 1.116 +#endif 1.117 +#ifdef B230400 1.118 + { 230400, B230400 }, 1.119 +#endif 1.120 +#ifdef B460800 1.121 + { 460800, B460800 }, 1.122 +#endif 1.123 +#ifdef B921600 1.124 + { 921600, B921600 }, 1.125 +#endif 1.126 + { 0, 0 }}; 1.127 + 1.128 + 1.129 +/* 1.130 + Save the terminal settings here 1.131 + */ 1.132 +static struct termios intio, savetio; 1.133 + 1.134 +static int fd = -1; /* Channel to port */ 1.135 +static int cmdchar = 035; /* Command character, default ^] */ 1.136 +static char *device = 0; /* Device, no default */ 1.137 +static int ispeed = B9600; /* Input speed, default 9600 bps */ 1.138 +static int ospeed = B9600; /* Output speed, default 9600 bps */ 1.139 +static int twostop = 0; /* Stop bits, if set to CSTOPB, use two */ 1.140 +static int parity = 0; /* Parity, -1 = mark, -2 = space 0=none */ 1.141 + /* PARENB enables parity, PARODD = odd */ 1.142 +static int bits = CS8; /* CS5-CS8 for 5-8 bits per character */ 1.143 +static int ctsflow = 0; /* CCTS_OFLOW enables CTS flow control */ 1.144 +static int xonflow = 0; /* IXON | IXOFF for in & out soft F/c */ 1.145 +static int modemcontrol = 0; /* Set to HUPCLto enable modem control */ 1.146 +static int markparity = 0; /* Mark parity -- set high bit on out */ 1.147 +static int backspace = 0; /* Backspace char, send on BS or DEL */ 1.148 +static int maplf = 0; /* If set, map LF to CR */ 1.149 +static int ignorecr = 0; /* Ignore carriage returns */ 1.150 +static int crlf = 0; /* Send CRLF on CR */ 1.151 +static int istty; /* Result of isatty() */ 1.152 +static char *connname = 0; /* Connection name found in config */ 1.153 +static int sendbreak = 0; /* Break requested */ 1.154 +static int modem = 0; /* Current modem status */ 1.155 +static int setmodem = 0; /* Set modem status to this */ 1.156 +static int delay = 0; /* Millisecond delay after each key */ 1.157 +static int linedelay = 0; /* Millisecond delay after each CR */ 1.158 +static int showspecial = 0; /* Show special chars as [xx] */ 1.159 + 1.160 +static void 1.161 +help() { 1.162 + fprintf(stderr, 1.163 +"dterm commands:\n" 1.164 +"^%c Enter command mode quit, q Exit\n" 1.165 +"show Display configuration help, h, ? Display this message\n" 1.166 +"!<command> Execute shell command version Display version\n" 1.167 +"@<filename> Get configuration from <filename>\n" 1.168 +"<device> Connect to <device> 300,1200,9600 Set speed\n" 1.169 +"5, 6, 7, 8 Set bits per character 1, 2 Set stop bits\n" 1.170 +"e, o, n, m, s Set parity" 1.171 +#ifdef CCTS_OFLOW 1.172 +" cts,nocts Enable / disable CTS\n" 1.173 +#else 1.174 +"\n" 1.175 +#endif 1.176 +"dtr, nodtr Raise / lower DTR b Send a 500ms break\n" 1.177 +"rts, norts Raise / lower RTS d, r Toggle DTR, RTS\n" 1.178 +"rx <filename> Receive file (XMODEM) sx <filename> Send file (XMODEM)\n" 1.179 +"rz Receive file (ZMODEM) sz <filename> Send file (ZMODEM)\n" 1.180 +"xon,noxon Enable / disable XON/XOFF flow control\n" 1.181 +"modem Hang up modem on exit, exit if modem hangs up\n" 1.182 +"nomodem Do not do modem control\n" 1.183 +"bs, nobs Enable / disable mapping of Delete to Backspace\n" 1.184 +"del, nodel Enable / disable mapping of Backspace to Delete\n" 1.185 +"maplf, nomaplf Enable / disable mapping of LF to CR\n" 1.186 +"igncr, noigncr Ignore / output carriage returns\n" 1.187 +"crlf, nocrlf Enable / disable sending LF after each CR\n" 1.188 +"delay=<n> Add delay of <n> ms after each charachter sent\n" 1.189 +"crwait=<n> Add delay of <n> ms after each line sent\n" 1.190 +"esc=<c> Set command mode character to Ctrl/<c>\n" 1.191 +"ctrl,hex,noctrl Show control characters as ^n (except tab, CR, LF)\n" 1.192 + 1.193 + 1.194 + 1.195 + , cmdchar + '@'); 1.196 +} 1.197 + 1.198 + 1.199 + 1.200 + 1.201 +/* 1.202 + Show current setup 1.203 + */ 1.204 +static void 1.205 +showsetup() { 1.206 + int i; 1.207 + fprintf(stderr, "Port: %s\n", device); 1.208 + fprintf(stderr, "Communications parameters: "); 1.209 + for(i = 0; speeds[i].s && speeds[i].c != ispeed; ) 1.210 + i++; 1.211 + fprintf(stderr, "%d", speeds[i].s); 1.212 + if(ospeed != ispeed) { 1.213 + for(i = 0; speeds[i].s && speeds[i].c != ospeed; ) 1.214 + i++; 1.215 + fprintf(stderr, "/%d", speeds[i].s); 1.216 + } 1.217 + if(bits == CS5) fprintf(stderr, " 5"); 1.218 + if(bits == CS6) fprintf(stderr, " 6"); 1.219 + if(bits == CS7) fprintf(stderr, " 7"); 1.220 + if(bits == CS8) fprintf(stderr, " 8"); 1.221 + if(parity == 0) fprintf(stderr, " n"); 1.222 + if(parity == PARENB) fprintf(stderr, " e"); 1.223 + if(parity == (PARENB|PARODD)) fprintf(stderr, " o"); 1.224 + if(parity == -1) fprintf(stderr, " m"); 1.225 + if(parity == -2) fprintf(stderr, " s"); 1.226 + if(twostop) fprintf(stderr, " 2\n"); 1.227 + else fprintf(stderr, " 1\n"); 1.228 + fprintf(stderr, "Flow/modem control:"); 1.229 + if(xonflow) fprintf(stderr, " xon"); 1.230 + if(ctsflow) fprintf(stderr, " cts"); 1.231 + if(modemcontrol)fprintf(stderr, " modem"); 1.232 + putc('\n', stderr); 1.233 + fprintf(stderr, "Character mappings:"); 1.234 + if(backspace == 8) fprintf(stderr, " bs"); 1.235 + if(backspace == 127) fprintf(stderr, " del"); 1.236 + if(maplf) fprintf(stderr, " maplf"); 1.237 + if(ignorecr) fprintf(stderr, " igncr"); 1.238 + if(crlf) fprintf(stderr, " crlf"); 1.239 + if(showspecial == 1) fprintf(stderr, " ctrl"); 1.240 + if(showspecial == 2) fprintf(stderr, " hex"); 1.241 + putc('\n', stderr); 1.242 + printf("Modem control: DTR: %s RTS: %s CTS: %s DSR: %s DCD: %s\n", 1.243 + setmodem & TIOCM_DTR ? "on" : "off", 1.244 + setmodem & TIOCM_RTS ? "on" : "off", 1.245 + modem & TIOCM_CTS ? "on" : "off", 1.246 + modem & TIOCM_DSR ? "on" : "off", 1.247 + modem & TIOCM_CD ? "on" : "off"); 1.248 + fprintf(stderr, "Escape character: ^%c\n", cmdchar + '@'); 1.249 + fflush(stderr); 1.250 +} 1.251 + 1.252 + 1.253 +/* 1.254 + Use the rzsz or lrzsz package to do file transfer 1.255 + Mode should be "rx", "rb" or "rz" to receive a file, "sx", "sb" or "sz" 1.256 + to send. "rz" & "rb" don't require a file name; the others do. 1.257 + */ 1.258 +static int 1.259 +rzsz(char *mode, char *file) { 1.260 + static char *loc[] = { "/usr/bin/", "/usr/local/bin/l", 1.261 + "/usr/bin/l", "/usr/local/bin/", 0 }; 1.262 + char path[128]; 1.263 + char *cmd; 1.264 + int i; 1.265 + struct stat sb; 1.266 + pid_t pid; 1.267 + 1.268 + /* 1.269 + Check file name 1.270 + */ 1.271 + if(*file == 0 && (mode[0] != 'r' || mode[1] == 'x')) { 1.272 + fprintf(stderr, "File name required for %c%c\n", 1.273 + mode[0], mode[1]); 1.274 + return -1; 1.275 + } 1.276 + 1.277 + /* 1.278 + Find the appropriate command 1.279 + */ 1.280 + for(i = 0; loc[i]; i++) { 1.281 + if(mode[1] == 'a') { 1.282 + sprintf(path, "%sascii-xfr", loc[i]); 1.283 + } else { 1.284 + sprintf(path, "%s%c%c", loc[i], mode[0], mode[1]); 1.285 + } 1.286 + if(!stat(path, &sb) && (sb.st_mode & S_IXUSR)) 1.287 + break; 1.288 + } 1.289 + if(!loc[i]) { 1.290 + fprintf(stderr, "RZ/SZ/ascii-xfr not installed\n"); 1.291 + return -1; 1.292 + } 1.293 + 1.294 + /* 1.295 + Get the command name 1.296 + */ 1.297 + cmd = strrchr(path, '/'); 1.298 + if(cmd) cmd++; 1.299 + else cmd = path; 1.300 + 1.301 + /* 1.302 + Fork subprocess. Set stdin & stdout to the serial line 1.303 + stderr remains on the console for progress messages 1.304 + */ 1.305 + pid = fork(); 1.306 + if(pid == 0) { 1.307 + dup2(fd, 0); 1.308 + dup2(fd, 1); 1.309 + if(mode[1] == 'a') { 1.310 + char delay_str[64]; 1.311 + sprintf(delay_str, "%d", linedelay); 1.312 + if(*file) execl(path, cmd, "-s", "-l", delay_str, file, NULL); 1.313 + else execl(path, cmd, "-r", NULL); 1.314 + } else { 1.315 + if(*file) execl(path, cmd, file, NULL); 1.316 + else execl(path, cmd, NULL); 1.317 + } 1.318 + _exit(127); 1.319 + } 1.320 + else if(pid == -1) { 1.321 + perror("fork"); 1.322 + return -1; 1.323 + } 1.324 + 1.325 + /* 1.326 + Wait for the process to complete and give a meaningful 1.327 + response 1.328 + */ 1.329 + signal(SIGINT, SIG_IGN); 1.330 + signal(SIGQUIT, SIG_IGN); 1.331 + signal(SIGTERM, SIG_IGN); 1.332 + while(waitpid(pid, &i, 0) != pid) {} 1.333 + signal(SIGINT, SIG_DFL); 1.334 + signal(SIGQUIT, SIG_DFL); 1.335 + signal(SIGTERM, SIG_DFL); 1.336 + 1.337 + if(WIFSIGNALED(i)) { 1.338 + fprintf(stderr, "Terminated on signal %d%s\n", WTERMSIG(i), 1.339 + WCOREDUMP(i) ? " (core dumped)" : ""); 1.340 + return -1; 1.341 + } 1.342 + if(WTERMSIG(i)) 1.343 + return -1; 1.344 + return 0; 1.345 +} 1.346 + 1.347 + 1.348 + 1.349 +static int readconfig(char *, char *); 1.350 + 1.351 +/* 1.352 + Process commands passed as strings, may be several commands on a line 1.353 + Allow ugliness like 8n1 -- treat as 8, n, 1 1.354 + */ 1.355 +static int 1.356 +setup(char *s, char *cffile, int cfline) { 1.357 + char *t, *d; 1.358 + int j,k; 1.359 + int ret = 0; 1.360 + struct stat sb; 1.361 + char ttybuf[128]; 1.362 + int justospeed = 0; 1.363 + 1.364 + while(*s) { 1.365 + /* 1.366 + Skip whitespace, commas 1.367 + */ 1.368 + while(isspace(*s) || *s == ',') 1.369 + s++; 1.370 + if(!*s) break; 1.371 + 1.372 + /* 1.373 + '?' = help 1.374 + */ 1.375 + if(*s == '?') { 1.376 + help(); 1.377 + return -4; 1.378 + } 1.379 + 1.380 + /* 1.381 + '!' = shell command 1.382 + */ 1.383 + if(*s == '!') { 1.384 + if((t = strchr(s, '\n'))) 1.385 + *t = 0; 1.386 + system(++s); 1.387 + break; 1.388 + } 1.389 + 1.390 + /* 1.391 + File transfer (sx, sz, rx, rz, sa, ra) 1.392 + Run rzsz or ascii-xfr to perform the actual file transfer 1.393 + Allow "sxfilename", "sx filename" or just "rz" 1.394 + */ 1.395 + if((s[0] == 's' || s[0] == 'r') && 1.396 + (s[1] == 'x' || s[1] == 'z' || s[1] == 'a')) { 1.397 + if((t = strchr(s, '\n'))) 1.398 + *t = 0; 1.399 + for(t = s + 2; isspace(*t); ) t++; 1.400 + return rzsz(s, t); 1.401 + } 1.402 + 1.403 + /* 1.404 + If a number, process it. 1.405 + If it's a valid speed, use that -- if nnn/mmm, set both to nnn 1.406 + for the first time, and flag things so mmm just sets ospeed 1.407 + 5-8 = data bits, 1,2 sets stops 1.408 + Anything else is error 1.409 + */ 1.410 + if(isdigit(*s)) { 1.411 + j = strtol(s, &s, 10); 1.412 + for(k = 1; speeds[k].s; k++) 1.413 + if(speeds[k].s == j) 1.414 + break; 1.415 + if(speeds[k].s) { 1.416 + ospeed = speeds[k].c; 1.417 + if(!justospeed) 1.418 + ispeed = ospeed; 1.419 + if(*s == '/') { 1.420 + s++; 1.421 + justospeed = 1; 1.422 + } 1.423 + else justospeed = 0; 1.424 + } 1.425 + else if(j == 5) bits = CS5; 1.426 + else if(j == 6) bits = CS6; 1.427 + else if(j == 7) bits = CS7; 1.428 + else if(j == 8) bits = CS8; 1.429 + else if(j == 1) twostop = 0; 1.430 + else if(j == 2) twostop = CSTOPB; 1.431 + else { 1.432 + if(cffile) 1.433 + fprintf(stderr, "in %s:%d: ", 1.434 + cffile, cfline); 1.435 + fprintf(stderr, "%d: invalid speed/bits\n", j); 1.436 + ret = -1; 1.437 + } 1.438 + } 1.439 + 1.440 + /* 1.441 + Otherwise, get the alpha-only keyword, see if it matches anything 1.442 + useful. 1.443 + */ 1.444 + else { 1.445 + t = s; 1.446 + while(isalpha(*t)) t++; 1.447 + j = *t; 1.448 + *t = 0; 1.449 + if( !strcasecmp(s, "xon")) 1.450 + xonflow = IXON | IXOFF; 1.451 + else if(!strcasecmp(s, "noxon")) 1.452 + xonflow = 0; 1.453 + 1.454 +#ifdef CCTS_OFLOW 1.455 + else if(!strcasecmp(s, "cts")) 1.456 + ctsflow = CCTS_OFLOW; 1.457 + else if(!strcasecmp(s, "nocts")) 1.458 + ctsflow = 0; 1.459 +#endif 1.460 + else if(!strcasecmp(s, "modem")) 1.461 + modemcontrol = HUPCL; 1.462 + else if(!strcasecmp(s, "nomodem")) 1.463 + modemcontrol = 0; 1.464 + else if(!strcasecmp(s, "E")) 1.465 + parity = PARENB; 1.466 + else if(!strcasecmp(s, "O")) 1.467 + parity = PARENB | PARODD; 1.468 + else if(!strcasecmp(s, "M")) 1.469 + parity = -1; 1.470 + else if(!strcasecmp(s, "S")) 1.471 + parity = -2; 1.472 + else if(!strcasecmp(s, "N")) 1.473 + parity = 0; 1.474 + else if(!strcasecmp(s, "q") || !strcasecmp(s, "quit")) 1.475 + ret = -3; 1.476 + else if(!strcasecmp(s, "h") || !strcasecmp(s, "help")) 1.477 + help(); 1.478 + else if(!strcasecmp(s, "del")) 1.479 + backspace = 127; 1.480 + else if(!strcasecmp(s, "bs")) 1.481 + backspace = 8; 1.482 + else if(!strcasecmp(s,"nobs") || !strcasecmp(s,"nodel")) 1.483 + backspace = 0; 1.484 + else if(!strcasecmp(s, "noesc")) 1.485 + cmdchar = 0; 1.486 + else if(!strcasecmp(s, "maplf")) 1.487 + maplf = 1; 1.488 + else if(!strcasecmp(s, "nomaplf")) 1.489 + maplf = 0; 1.490 + else if(!strcasecmp(s, "igncr")) 1.491 + ignorecr = 1; 1.492 + else if(!strcasecmp(s, "noigncr")) 1.493 + ignorecr = 0; 1.494 + else if(!strcasecmp(s, "crlf")) 1.495 + crlf = 1; 1.496 + else if(!strcasecmp(s, "nocrlf")) 1.497 + crlf = 0; 1.498 + else if(!strcasecmp(s, "b")) 1.499 + sendbreak = 1; 1.500 + else if(!strcasecmp(s, "d")) 1.501 + setmodem ^= TIOCM_DTR; 1.502 + else if(!strcasecmp(s, "r")) 1.503 + setmodem ^= TIOCM_RTS; 1.504 + else if(!strcasecmp(s, "dtr")) 1.505 + setmodem |= TIOCM_DTR; 1.506 + else if(!strcasecmp(s, "rts")) 1.507 + setmodem |= TIOCM_RTS; 1.508 + else if(!strcasecmp(s, "nodtr")) 1.509 + setmodem &= ~TIOCM_DTR; 1.510 + else if(!strcasecmp(s, "norts")) 1.511 + setmodem &= ~TIOCM_RTS; 1.512 + else if(!strcasecmp(s, "show")) 1.513 + showsetup(); 1.514 + else if(!strcasecmp(s, "version")) 1.515 + fputs(COPYRIGHT, stderr); 1.516 + else if(!strcasecmp(s, "ctrl")) 1.517 + showspecial = 1; 1.518 + else if(!strcasecmp(s, "hex")) 1.519 + showspecial = 2; 1.520 + else if(!strcasecmp(s, "noctrl") || 1.521 + !strcasecmp(s, "noctrl")) 1.522 + showspecial = 0; 1.523 + 1.524 + /* 1.525 + No? 1.526 + @<filename> includes a file 1.527 + !cmd Run a command 1.528 + esc=c sets escape char 1.529 + <device> select device 1.530 + */ 1.531 + else { 1.532 + *t = j; 1.533 + while(*t && !isspace(*t) && *t != ',') 1.534 + t++; 1.535 + j = *t; 1.536 + *t = 0; 1.537 + if(*s == '@') { 1.538 + k = readconfig(++s, 0); 1.539 + if(k == -2) { 1.540 + if(cffile) 1.541 + fprintf(stderr, 1.542 + "in %s:%d: ", 1.543 + cffile, cfline); 1.544 + fprintf(stderr, "%s: %s\n", 1.545 + s, strerror(errno)); 1.546 + ret = -1; 1.547 + } 1.548 + goto next; 1.549 + } 1.550 + if(!strncasecmp(s, "esc=", 4)) { 1.551 + cmdchar = s[4] & 0x1f; 1.552 + goto next; 1.553 + } 1.554 + else if(!strncasecmp(s, "delay=", 6)) { 1.555 + delay = atoi(s+6); 1.556 + goto next; 1.557 + } 1.558 + else if(!strncasecmp(s, "crwait=", 7)) { 1.559 + linedelay = atoi(s+7); 1.560 + goto next; 1.561 + } 1.562 + d = s; 1.563 + k = stat(d, &sb); 1.564 + if(k && *d != '/') { 1.565 + sprintf(ttybuf, "/dev/%.100s", d); 1.566 + d = ttybuf; 1.567 + k = stat(d, &sb); 1.568 + } 1.569 + if(!k) { 1.570 + if((sb.st_mode & S_IFMT) == S_IFCHR) { 1.571 + ret = 1; 1.572 + device = strdup(d); 1.573 + goto next; 1.574 + } 1.575 + } 1.576 + if(cffile) 1.577 + fprintf(stderr, "in %s:%d: ", 1.578 + cffile, cfline); 1.579 + fprintf(stderr, 1.580 + "%s: unrecognised keyword/device\n", s); 1.581 + ret = -1; 1.582 + } 1.583 + next: *t = j; 1.584 + s = t; 1.585 + } 1.586 + } 1.587 + return ret; 1.588 +} 1.589 + 1.590 + 1.591 +/* 1.592 + Read a config file 1.593 + Input lines can be lists of config words, or in the form 1.594 + name: words 1.595 + If name: form, only lines matching passed name are used 1.596 + */ 1.597 +static int 1.598 +readconfig(char *file, char *name) { 1.599 + char buf[1024]; 1.600 + FILE *f; 1.601 + char *s, *t; 1.602 + int lineno = 0; 1.603 + int ret = 0, r; 1.604 + 1.605 + /* 1.606 + ~/file = get file from $HOME/file 1.607 + */ 1.608 + if(*file == '~' && file[1] == '/' && (s = getenv("HOME"))) { 1.609 + snprintf(buf, sizeof(buf), "%s/%s", s, file+2); 1.610 + file = buf; 1.611 + } 1.612 + 1.613 + /* 1.614 + Return -2 if can't open 1.615 + */ 1.616 + f = fopen(file, "r"); 1.617 + if(!f) return -2; 1.618 + 1.619 + /* 1.620 + Read input, strip # commends 1.621 + Count lines 1.622 + Keep track of return code 1.623 + */ 1.624 + while(fgets(buf, sizeof(buf), f)) { 1.625 + lineno++; 1.626 + if((s = strchr(buf, '#'))) 1.627 + *s = 0; 1.628 + for(s = buf; isspace(*s);) 1.629 + s++; 1.630 + if(!*s) continue; 1.631 + for(t = s; *t && *t != ':' && *t != ',' && !isspace(*t); ) 1.632 + t++; 1.633 + if(*t == ':') { 1.634 + *t++ = 0; 1.635 + if(!name) 1.636 + continue; 1.637 + if(strcmp(name, s)) 1.638 + continue; 1.639 + s = t; 1.640 + connname = name; 1.641 + } 1.642 + 1.643 + r = setup(s, file, lineno); 1.644 + if(r < 0) 1.645 + ret = r; 1.646 + if(r > 0 && !ret) 1.647 + ret = 1; 1.648 + } 1.649 + 1.650 + fclose(f); 1.651 + return ret; 1.652 +} 1.653 + 1.654 + 1.655 +/* 1.656 + Sleep n milliseconds 1.657 + */ 1.658 +static void 1.659 +millisleep(int n) { 1.660 + struct timespec t; 1.661 + t.tv_sec = n / 1000; 1.662 + t.tv_nsec = (n % 1000) * 1000000; 1.663 + nanosleep(&t, 0); 1.664 +} 1.665 + 1.666 + 1.667 +/* 1.668 + Set up the port 1.669 + */ 1.670 +static int 1.671 +setupport(int fd) { 1.672 + int parityflags; 1.673 + int modemflags; 1.674 + int spaceparity = 0; 1.675 + struct termios tio; 1.676 + int m; 1.677 + 1.678 + /* 1.679 + See what to do with parity 1.680 + */ 1.681 + markparity = 0; 1.682 + parityflags = parity; 1.683 + if(parity == -1) { 1.684 + parityflags = 0; 1.685 + markparity = ISTRIP; 1.686 + } 1.687 + else if(parity == -2) { 1.688 + parityflags = 0; 1.689 + spaceparity = ISTRIP; 1.690 + } 1.691 + 1.692 + /* 1.693 + If no modem control, use local mode 1.694 + */ 1.695 + modemflags = ctsflow | modemcontrol; 1.696 + if(!modemflags) 1.697 + modemflags = CLOCAL; 1.698 + 1.699 + /* 1.700 + Set the speed and params 1.701 + */ 1.702 + tcgetattr(fd, &tio); 1.703 + tio.c_iflag = IGNBRK | IGNPAR | xonflow | spaceparity | markparity; 1.704 + tio.c_cflag = CREAD | HUPCL | modemflags | bits | parityflags; 1.705 + tio.c_oflag = 0; 1.706 + tio.c_lflag = 0; 1.707 + cfsetispeed(&tio, ispeed); 1.708 + cfsetospeed(&tio, ospeed); 1.709 + if(tcsetattr(fd, TCSAFLUSH, &tio) == -1) 1.710 + return -1; 1.711 + 1.712 + /* 1.713 + Set up the modem lines 1.714 + */ 1.715 + ioctl(fd, TIOCMGET, &modem); 1.716 + if((modem & (TIOCM_RTS | TIOCM_DTR)) != (setmodem & (TIOCM_RTS | 1.717 + TIOCM_DTR))) { 1.718 + m = setmodem & (TIOCM_RTS | TIOCM_DTR); 1.719 + ioctl(fd, TIOCMBIS, &m); 1.720 + m = (~setmodem) & (TIOCM_RTS | TIOCM_DTR); 1.721 + ioctl(fd, TIOCMBIC, &m); 1.722 + } 1.723 + 1.724 + /* 1.725 + Send a break if requested 1.726 + */ 1.727 + if(sendbreak) { 1.728 + ioctl(fd, TIOCSBRK, 0); 1.729 + millisleep(500); 1.730 + ioctl(fd, TIOCCBRK, 0); 1.731 + sendbreak = 0; 1.732 + } 1.733 + 1.734 + /* 1.735 + Get the current modem status 1.736 + */ 1.737 + ioctl(fd, TIOCMGET, &modem); 1.738 + setmodem = modem; 1.739 + 1.740 + return 0; 1.741 +} 1.742 + 1.743 + 1.744 +/* 1.745 + Set up the modem. This is a little tricky due to the need to 1.746 + ensure the modem does not block before we set it up. 1.747 + */ 1.748 +static int 1.749 +openport(char *device) { 1.750 + int fd; 1.751 + 1.752 + if((fd = open(device, O_RDWR|O_NONBLOCK, 0)) < 0) 1.753 + DIEP(device) 1.754 + if(setupport(fd) < 0) 1.755 + DIEP(device) 1.756 + millisleep(10); 1.757 + fcntl(fd, F_SETFL, 0); 1.758 + 1.759 + ioctl(fd, TIOCMGET, &modem); 1.760 + setmodem = modem; 1.761 + 1.762 + setenv("DTERM_PORT", device, 1); 1.763 + return fd; 1.764 +} 1.765 + 1.766 + 1.767 +/* 1.768 + Usage 1.769 + */ 1.770 +static void 1.771 +usage(char *this) { 1.772 + fprintf(stderr, "Usage: %s port/setup\n" 1.773 + " '%s help' for help\n", this, this); 1.774 + exit(1); 1.775 +} 1.776 + 1.777 + 1.778 +int 1.779 +main(int argc, char **argv) { 1.780 + int nfd; 1.781 + int i, j, c; 1.782 + unsigned char inbuf; 1.783 + char buf[256]; 1.784 + char cbuf[8]; 1.785 + char *s; 1.786 + int done; 1.787 + fd_set fds; 1.788 + struct timeval delay_tv, *readdelay; 1.789 + struct stat sb; 1.790 + 1.791 + /* 1.792 + Do the right things depending on whether stdin & stdout are TTYs 1.793 + If the input is a TTY, we need to put it into non-canonical mode 1.794 + (which we'll actually do later); otherwise, default to turning 1.795 + LFs from a file into CRs. (See [no]maplf setup command.) 1.796 + If the output is a TTY, default to passing CR transparently; 1.797 + otherwise default to ignoring CR so that output is logged to a 1.798 + files etc nicely. (See [no]igncr.) 1.799 + */ 1.800 + istty = isatty(0); 1.801 + if(istty) { 1.802 + tcgetattr(0, &savetio); 1.803 + maplf = 0; 1.804 + } 1.805 + else maplf = 1; 1.806 + if(isatty(1)) 1.807 + ignorecr = 0; 1.808 + else ignorecr = 1; 1.809 + 1.810 + /* 1.811 + Read default config, if no private .dtermrc, use /etc/dtermrc 1.812 + */ 1.813 + setmodem = modem = TIOCM_DTR | TIOCM_RTS; 1.814 + if(readconfig("~/.dtermrc", argv[1]) == -2) 1.815 + readconfig("/etc/dtermrc", argv[1]); 1.816 + 1.817 + /* 1.818 + Parse args 1.819 + If only arg is "help", exit 1.820 + */ 1.821 + i = 1; 1.822 + if(connname) i = 2; 1.823 + for(; i < argc; i++) 1.824 + if(setup(argv[i], 0, 0) < 0) 1.825 + usage(argv[0]); 1.826 + if(argc == 2 && !strcasecmp(argv[1], "help")) 1.827 + exit(0); 1.828 + 1.829 + /* 1.830 + If no device specified, have a crack at finding a default device 1.831 + Look for the first on-board device first; failing that look for 1.832 + the first USB device. For Linux & BSD these are: 1.833 + OS Onboard USB 1.834 + Linux ttyS0 ttyUSB0 1.835 + BSD ttyd0 ttyU0 1.836 + Note that this rather assumes that the existence of a device 1.837 + name in /dev indicates the actual existence of a device. Mostly 1.838 + this is the case on modern systems. Mostly. 1.839 + */ 1.840 + if(!device) { 1.841 + if( !stat("/dev/ttyS0", &sb)) device = "/dev/ttyS0"; 1.842 + else if(!stat("/dev/ttyd0", &sb)) device = "/dev/ttyd0"; 1.843 + else if(!stat("/dev/ttyUSB0", &sb)) device = "/dev/ttyUSB0"; 1.844 + else if(!stat("/dev/ttyU0", &sb)) device = "/dev/ttyU0"; 1.845 + else { 1.846 + fprintf(stderr, "Could not find default device\n"); 1.847 + usage(argv[0]); 1.848 + } 1.849 + } 1.850 + 1.851 + /* 1.852 + If the controlling TTY is in fact a TTY, set it up 1.853 + */ 1.854 + if(istty) { 1.855 + intio = savetio; 1.856 + intio.c_oflag = 0; 1.857 + intio.c_lflag = 0; 1.858 + intio.c_iflag = savetio.c_iflag & ~(INLCR|IGNCR|ICRNL); 1.859 + 1.860 + intio.c_cc[VEOF] = _POSIX_VDISABLE; 1.861 + intio.c_cc[VEOL] = _POSIX_VDISABLE; 1.862 + intio.c_cc[VEOL2] = _POSIX_VDISABLE; 1.863 + intio.c_cc[VERASE] = _POSIX_VDISABLE; 1.864 + intio.c_cc[VWERASE] = _POSIX_VDISABLE; 1.865 + intio.c_cc[VKILL] = _POSIX_VDISABLE; 1.866 + intio.c_cc[VREPRINT] = _POSIX_VDISABLE; 1.867 + intio.c_cc[VINTR] = _POSIX_VDISABLE; 1.868 + intio.c_cc[VQUIT] = _POSIX_VDISABLE; 1.869 + intio.c_cc[VSUSP] = _POSIX_VDISABLE; 1.870 +#ifdef VDSUSP 1.871 + intio.c_cc[VDSUSP] = _POSIX_VDISABLE; 1.872 +#endif 1.873 + intio.c_cc[VLNEXT] = _POSIX_VDISABLE; 1.874 + intio.c_cc[VDISCARD] = _POSIX_VDISABLE; 1.875 +#ifdef VSTATUS 1.876 + intio.c_cc[VSTATUS] = _POSIX_VDISABLE; 1.877 +#endif 1.878 + tcsetattr(0, TCSADRAIN, &intio); 1.879 + } 1.880 + 1.881 + /* 1.882 + Connect to serial port 1.883 + */ 1.884 + fd = openport(device); 1.885 + if(fd < 0) exit(1); 1.886 + 1.887 + /* 1.888 + Main loop 1.889 + */ 1.890 + readdelay = 0; 1.891 + done = 0; 1.892 + while(!done) { 1.893 + 1.894 + /* 1.895 + Set up the select() call 1.896 + If readdelay is not 0, we're waiting for things to go quiet so we 1.897 + can exit. 1.898 + Errors kill us, execpt for interrupted calls 1.899 + 0 return only happens if readdelay is set, so we exit then 1.900 + */ 1.901 + FD_ZERO(&fds); 1.902 + if(!readdelay) 1.903 + FD_SET(0, &fds); 1.904 + FD_SET(fd, &fds); 1.905 + i = select(fd + 1, &fds, 0,0, readdelay); 1.906 + if(i == -1 && errno != EINTR) 1.907 + DIEP("select"); 1.908 + if(i == 0 && readdelay) 1.909 + break; 1.910 + 1.911 + /* 1.912 + If input from line, read a full buffer in 1.913 + IgnoreCR means sucking CRs out of the buffer (yuck) 1.914 + If EOF (e.g. hangup), exit nicely. 1.915 + */ 1.916 + if(FD_ISSET(fd, &fds)) { 1.917 + i = read(fd, buf, sizeof(buf)); 1.918 + if(i < 0) 1.919 + DIEP(device); 1.920 + if(!i) break; 1.921 + if(showspecial) { 1.922 + s = buf; 1.923 + do { 1.924 + c = 0; 1.925 + for(j = 0; j < i; j++) { 1.926 + c = (unsigned char) s[j]; 1.927 + if(showspecial == 2 && ( 1.928 + c == '\t' || c > '~')) 1.929 + break; 1.930 + if(c == '\r' && ignorecr) 1.931 + break; 1.932 + if(( c < ' ' && c != '\t' 1.933 + && c != '\r' && c != '\n') 1.934 + || (c > '~' && c < 160)) 1.935 + break; 1.936 + } 1.937 + if(j) write(1, s, j); 1.938 + if(j >= i) 1.939 + break; 1.940 + if(c == '\r' && ignorecr) { 1.941 + /* Do nothing */ 1.942 + } 1.943 + else if(c < 32 && showspecial != 2) { 1.944 + cbuf[0] = '^'; 1.945 + cbuf[1] = c + '@'; 1.946 + write(1, cbuf, 2); 1.947 + } 1.948 + else if(c == 127 && showspecial != 2) 1.949 + write(1, "[DEL]", 5); 1.950 + else { 1.951 + sprintf(cbuf, "[%02x]", c); 1.952 + write(1, cbuf, 4); 1.953 + } 1.954 + j++; 1.955 + s += j; 1.956 + i -= j; 1.957 + } while(i > 0); 1.958 + } 1.959 + else { 1.960 + if(ignorecr) { 1.961 + j = 0; 1.962 + for(s = buf; s < buf + i; s++) 1.963 + if(*s != '\r') 1.964 + buf[j++] = *s; 1.965 + i = j; 1.966 + } 1.967 + write(1, buf, i); 1.968 + } 1.969 + } 1.970 + 1.971 + /* 1.972 + Input on stdin 1.973 + Read a character 1.974 + If EOF, set readdelay to 1 second 1.975 + */ 1.976 + if(FD_ISSET(0, &fds)) { 1.977 + if(read(0, &inbuf, 1) < 1) { 1.978 + delay_tv.tv_sec = 1; 1.979 + delay_tv.tv_usec = 0; 1.980 + readdelay = &delay_tv; 1.981 + continue; 1.982 + } 1.983 + /* 1.984 + If command character received, read commands 1.985 + */ 1.986 + if(inbuf == cmdchar && istty) { 1.987 + FIXTTY; 1.988 + putchar('\n'); 1.989 + for(;;) { 1.990 + fprintf(stderr, "dterm> "); 1.991 + if(!fgets(buf, sizeof(buf), stdin)) 1.992 + return 0; 1.993 + if((s = strchr(buf, '#'))) 1.994 + *s = 0; 1.995 + for(s = buf; *s; s++) 1.996 + if(!isspace(*s)) break; 1.997 + if(!*s) break; 1.998 + ioctl(fd, TIOCMGET, &modem); 1.999 + i = setup(buf, 0, 0); 1.1000 + if(i == -3) 1.1001 + return 0; 1.1002 + if(i == 1) { 1.1003 + nfd = openport(device); 1.1004 + if(nfd >= 0) { 1.1005 + close(fd); 1.1006 + fd = nfd; 1.1007 + } 1.1008 + } 1.1009 + else if(setupport(fd)) 1.1010 + fprintf(stderr, 1.1011 + "invalid parameters\n"); 1.1012 + } 1.1013 + if(istty) tcsetattr(0, TCSADRAIN, &intio); 1.1014 + } 1.1015 + /* 1.1016 + Otherwise do any processing on the character 1.1017 + Add dread high bit disease if mark parity 1.1018 + BS <-> DEL mapping 1.1019 + LF -> CR mapping for files 1.1020 + CR -> CRLF mapping for TTYs 1.1021 + */ 1.1022 + else { 1.1023 + if(markparity) 1.1024 + inbuf |= 0x80; 1.1025 + if(backspace && (inbuf == 8 || inbuf == 127)) 1.1026 + inbuf = backspace; 1.1027 + if(maplf && inbuf == '\n') 1.1028 + inbuf = '\r'; 1.1029 + write(fd, &inbuf, 1); 1.1030 + if(crlf && inbuf == '\r') { 1.1031 + inbuf = '\n'; 1.1032 + write(fd, &inbuf, 1); 1.1033 + } 1.1034 + if(linedelay && inbuf == '\r') 1.1035 + millisleep(linedelay); 1.1036 + else if(delay) 1.1037 + millisleep(delay); 1.1038 + } 1.1039 + } 1.1040 + 1.1041 + } 1.1042 + 1.1043 + /* 1.1044 + Fall out the bottom, cleaning up 1.1045 + */ 1.1046 + FIXTTY; 1.1047 + return 0; 1.1048 +}