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 +}