[comp.os.minix] Improved term.c

brucee@runx.ips.oz (Bruce Evans) (10/12/88)

I rewrote term.c to remove the following limitations.

(1) It only worked for a "modem" on /dev/tty1. The new code is almost
    device independent and allows any file.

(2) The default settings were wired in. I wanted the current settings
    to be used. This allows a stty < /dev/ttyx in /etc/rc to override
    inconvenient defaults.

(3) Not all usable baud rates were supported. I needed to test TTY at
    faster rates. This version only gives 19200 because of limitations
    in the interface to TTY.

(4) Part of the escape sequence was transmitted to the external device.
    My solution requires the escape sequence to be returned in a single
    read and it is best if it is multi-character. '5' on the numeric
    keypad works well, but it would need to be changed for a terminal.

(5) If the external device was not on line, term would hang forever
    waiting to write to it (with my TTY driver), since the the test
    for the escape sequence was in the same process. My solution was
    to introduce yet another process.

(6) An extra newline was printed on exit.

(7) Parity was not stripped. I wanted to keep 8 bits no parity for the
    line settings for zmodem, but the external system gave garbage high
    bits.

I improved the argument processing code and made other small changes so
the executable is slightlly smaller despite the additions. This is
important since it will fork twice. I use chmem =3000.

Bugs: the ioctls should check the return value. This bug is also in stty.

#! /bin/sh
# Contents:  term.c
# Wrapped by sys@besplex on Wed Oct 12 20:33:21 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'term.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'term.c'\"
else
echo shar: Extracting \"'term.c'\" \(7740 characters\)
sed "s/^X//" >'term.c' <<'END_OF_FILE'
X/* term - terminal simulator		Author: Andy Tanenbaum */
X
X/* This program allows the user to turn a MINIX system into a dumb
X * terminal to communicate with a remote computer through one of the ttys.
X * It forks into two processes.  The parent sits in a tight loop copying
X * from stdin to the tty.  The child sits in a tight loop copying from
X * the tty to stdout.
X *
X * 2 Sept 88 BDE: Massive changes to make current settings the default,
X * allow any file as the "tty", support fancy baud rates and remove
X * references to and dependencies on modems and keyboards, so (e.g.)
X * a local login on /dev/tty1 can do an external login on /dev/tty2.
X *
X * 3 Sept 88 BDE: Split parent again to main process copies from stdin to a
X * pipe which is copied to the tty.  This stops a blocked write to the
X * tty from hanging the program.
X *
X * 11 Oct 88 BDE: Cleaned up baud rates and parity stripping.
X *
X * Example usage:
X *	term			: baud, bits/char, parity from /dev/tty1
X *	term 9600 7 even	: 9600 baud, 7 bits/char, even parity
X *	term odd 300 7		:  300 baud, 7 bits/char, odd parity
X *	term /dev/tty2		: use /dev/tty2 rather than /dev/tty1
X *				: Any argument starting with "/" is
X *				: taken as the communication device.
X */
X
X#include <sgtty.h>
X#include <signal.h>
X
X#define MAXARGS  3		/* maximum number of uart params */
X#define CHUNK 1024		/* how much to read at once */
X#define NULL     0
X
X/* Hack some new baud rates for Minix. Minix uses a divide-by-100 encoding. */
X#define B200     2
X#define B600     6
X#define B1800   18
X#define B3600   36
X#define B7200   72
X#define B19200 192
X#define EXTA   192
X/* We can't handle some standard (slow) V7 speeds and speeds above 25500 since
X * since the speed is packed into a char :-(. Trap them with an illegal value.
X */
X#define B50      0
X#define B75      0
X#define B134     0
X#define EXTB     0
X#define B38400   0
X#define B57600   0
X#define B115200  0
X
Xint commfd;			/* open file no. for comm device */
Xint readpid;			/* pid of child reading commfd */
Xstruct sgttyb sgcommfd;		/* saved terminal parameters for commfd */
Xstruct sgttyb sgstdin;		/* saved terminal parameters for stdin */
Xint writepid;			/* pid of child writing commfd */
X
Xchar endseq[] = "\033[G";	/* sequence to leave simulator */
X				/* keypad '5', and must arrive in 1 piece */
Xstruct param_s
X{
X  char *pattern;
X  int value;
X  char type;
X#define BAD      0
X#define BITS     1
X#define NOSTRIP  2
X#define PARITY   3
X#define SPEED    4
X}
X  params[] =
X{
X	"5", BITS5, BITS,
X	"6", BITS6, BITS,
X	"7", BITS7, BITS,
X	"8", BITS8, BITS,
X
X	"even", EVENP, PARITY,
X	"odd", ODDP, PARITY,
X	"nostrip", 0, NOSTRIP,
X
X	"50", B50, SPEED,
X	"75", B75, SPEED,
X	"110", B110, SPEED,
X	"134", B134, SPEED,
X	"200", B200, SPEED,
X	"300", B300, SPEED,
X	"600", B600, SPEED,
X	"1200", B1200, SPEED,
X	"1800", B1800, SPEED,
X	"2400", B2400, SPEED,
X	"3600", B3600, SPEED,
X	"4800", B4800, SPEED,
X	"7200", B7200, SPEED,
X	"9600", B9600, SPEED,
X	"19200", B19200, SPEED,
X	"EXTA", EXTA, SPEED,
X	"EXTB", EXTB, SPEED,
X	"38400", B38400, SPEED,
X	"57600", B57600, SPEED,
X	"115200", B115200, SPEED,
X	"", 0, BAD,		/* BAD type to end list */	
X};
Xunsigned char strip_parity = 1;	/* nonzero to strip high bits before output */
X
Xint quit();			/* forward declare signal handler */
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X  char *commdev = NULL;
X  int i;
X  int pipefd[2];
X
X  sync();
X  for (i = 1; i < argc; ++i)
X	if ( argv[i][0] == '/') {
X		if (commdev != NULL)
X			error("Too may communication devices", "");
X		commdev = argv[i];
X	}
X  if (commdev == NULL) {
X	i = MAXARGS + 1;
X	commdev = "/dev/tty1";
X  }
X  else
X	i = MAXARGS + 2;
X  if (argc > i)
X	error("Usage: term [baudrate] [data_bits] [parity]", "");
X  commfd = open(commdev, 2);
X  if (commfd < 0) error("Can't open ", commdev);
X
X  /* Save state of both devices before altering either (may be identical!). */
X  ioctl(0, TIOCGETP, &sgstdin);
X  ioctl(commfd, TIOCGETP, &sgcommfd);
X  set_mode(0, -1, -1, -1, &sgstdin);	/* RAW mode on stdin, others current */
X  set_uart(argc, argv);
X
X  /* Main body of the terminal simulator. */
X  signal(SIGINT, quit);
X  signal(SIGPIPE, quit);
X  if (pipe(pipefd) < 0)
X	error("Can't create pipe", "");
X  switch((writepid = fork()))
X  {
X  case -1:
X	error("Can't create process to write to comm device", "");
X  case 0:
X	/* piped stdin to tty */
X	close(pipefd[1]);
X	copy(pipefd[0], "piped stdin", commfd, commdev, "");
X  }
X  close(pipefd[0]);
X  switch((readpid = fork()))
X  {
X  case -1:
X	error("Can't create process to read from comm device", "");
X  case 0:
X	/* tty to stdout */
X	copy(commfd, commdev, 1, "stdout", "");
X  }  
X  /* stdin to pipe */
X  copy(0, "stdin", pipefd[1], "redirect stdin", endseq);
X}
X
X
Xset_uart(argc, argv)
Xint argc;
Xchar *argv[];
X{
X/* Set up the UART parameters. */
X
X  int i, j, bits, nbits, parity, nparities, speed, nspeeds;
X  char *arg;
X  register struct param_s *param;
X
X  /* Examine all the parameters and check for validity. */
X  nspeeds = nparities = nbits = 0;
X  speed = parity = bits = -1;	/* -1 means use current value */
X  for (i = 1; i < argc; ++i) {
X	if ((arg = argv[i])[0] == '/') continue;
X
X	/* Check parameter for legality. */
X	for (j = 0, param = &params[0];
X	     param->type != BAD && strcmp(arg, param->pattern) != 0;
X	     ++j, ++param)
X		;
X	switch (param->type)
X	{
X	case BAD:
X		error("Invalid parameter: ", arg);
X	case BITS:
X		bits = param->value;
X		if (++nbits > 1) error("Too many character sizes", "");
X		break;
X	case PARITY:
X		parity = param->value;
X		if (++nparities > 1) error("Too many parities", "");
X		break;
X	case SPEED:
X		speed = param->value;
X		if (speed == 0)
X			error("Invalid speed: ", arg);
X		if (++nspeeds > 1) error("Too many speeds", "");
X		break;
X	case NOSTRIP:
X		strip_parity = 0;
X		break;
X	}
X  }
X  set_mode(commfd, speed, parity, bits, &sgcommfd);
X}
X
X
Xset_mode(fd, speed, parity, bits, sgsavep)
Xint speed;
Xint parity;
Xint bits;
Xstruct sgttyb *sgsavep;
X{
X  /* Set open file fd to RAW mode with the given other modes.
X   * If fd is not a tty, this may do nothing but connecting ordinary files
X   * as ttys may have some use.
X   */
X
X  struct sgttyb sgtty;
X
X  sgtty = *sgsavep;
X  if (speed == -1) speed = sgtty.sg_ispeed;
X  if (parity == -1) parity = sgtty.sg_flags & (EVENP | ODDP);
X  if (bits == -1) bits = sgtty.sg_flags & BITS8; /* BITS8 is actually a mask */
X  sgtty.sg_ispeed = speed;
X  sgtty.sg_ospeed = speed;
X  sgtty.sg_flags = RAW | parity | bits;
X  ioctl(fd, TIOCSETP, &sgtty);
X}
X
X
Xcopy(in, inname, out, outname, end)
Xint in;
Xchar *inname;
Xint out;
Xchar *outname;
Xchar *end;
X{
X/* Copy from one open file to another. If the 'end' sequence is not "", and
X * precisely matches the input, terminate the copy and various children.
X * The end sequence is best provided by keyboard input from one of the
X * special keys which always produces chars in a bunch. RAW mode almost
X * guarantees exactly one keystroke's worth of input at a time.
X */
X
X  static char buf[CHUNK];
X  char *bufend;
X  register char *bufp;
X  int count;
X  int len;
X
X  len = strlen(end);
X  while (1) {
X	if ( (count = read(in, buf, CHUNK)) <= 0) {
X		write2sn("Can't read from ", inname);
X		quit();
X	}
X	if (count == len && strncmp(buf, end, count) == 0)
X		quit();
X	if (strip_parity)
X		for (bufp = buf, bufend = bufp + count;
X		     bufp < bufend; ++bufp )
X			*bufp &= 0x7F;
X	if (write(out, buf, count) != count) {
X		write2sn("Can't write to ", outname);
X		quit();
X	}
X  }
X}
X
X
Xerror(s1, s2)
Xchar *s1;
Xchar *s2;
X{
X  write2sn(s1, s2);
X  exit(1);
X}
X
X
Xnicequit()
X{
X  exit(0);
X}
X
X
Xquit()
X{
X  ioctl(commfd, TIOCSETP, &sgcommfd);
X  ioctl(0, TIOCSETP, &sgstdin);
X  signal(SIGINT, nicequit);	/* if not caught, sh prints extra newline */
X  kill(0, SIGINT);
X  nicequit();
X}
X
X
Xwrite2sn(s1, s2)
X{
X  write(1, s1, strlen(s1));
X  write(1, s2, strlen(s2));
X  write(1, "\r\n", 2);
X}
END_OF_FILE
if test 7740 -ne `wc -c <'term.c'`; then
    echo shar: \"'term.c'\" unpacked with wrong size!
fi
# end of 'term.c'
fi
echo shar: End of shell archive.
exit 0

brucee@runx.ips.oz (Bruce Evans) (10/12/88)

Sorry, I forgot my signature last time.

Another matter. There was a question about fakfp.s (fake floating point)
along with an incorrect list of items in it. The fake functions call a
message printing routine, eventually in trp.s. I noticed that the kernel
also links in trp.s. This is not nice - it wastes space and brings up the
possibility of the kernel calling itself with an int 0x20. There must be
routine(s) which need to be annulled better (like exit() already is).
Probably not floating point :-).

Bruce Evans
Internet: brucee@runx.ips.oz.au    UUCP: uunet!runx.ips.oz.au!brucee