#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  Makefile hsh.c hsh.manl test.hshrc tmp.hdr
# Wrapped by danj1@ihspa on Tue Jun 23 19:04:55 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive."'
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
  echo shar: Extracting \"'Makefile'\" \(70 characters\)
  sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XCFLAGS= -g
XCC= cc
X
Xall:	hsh
X
Xhsh:	hsh.o
X	$(CC) $(CFLAGS) -o hsh hsh.o
END_OF_FILE
  if test 70 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
  fi
  # end of 'Makefile'
fi
if test -f 'hsh.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hsh.c'\"
else
  echo shar: Extracting \"'hsh.c'\" \(15147 characters\)
  sed "s/^X//" >'hsh.c' <<'END_OF_FILE'
X/* 
X * hsh - a telnet like program for escaping shift- and ctrl-key
X * making a one-finger person able to use a normal terminal.
X * 
X * Ole Bj|rn Hessen, 11 feb 1988.
X * Revision: 28 Sep 1988.
X *
X * This program is public domain.
X *
X*/
X
X#include <strings.h>
X#include <stdio.h>
X#include <sys/ioctl.h>
X#include <sys/ttydev.h>
X#include <signal.h>
X#include <sys/file.h>
X#include <sys/time.h>
X#include <utmp.h>
X#include <pwd.h>
X
X#define DEFAULT_SHELL "/bin/csh"
X#define DEF_SHIFT_KEY '\''
X#define DEF_CTRL_KEY '^'
X#define TERM_BUFF 100   
X#define SHELL_BUFF 100
X#define CNULL (char *)0
X#define BUFFSIZ 200		/* std*'s BUFSIZ is unessecary large */
X
XFILE *pty, *tty;		/* FILE pointers to the pty and tty */
X				/* devices used for user communication */
X
Xint errno;			/* returned error number */
Xchar *sys_errlist[];		/* vector of strings of error descriptors */
Xint sys_nerr;			/* max entries + 1 */
X
Xtypedef enum { TRUE, FALSE } BOOL;
XBOOL in_err;
X
Xchar *program;
Xchar shell[SHELL_BUFF];
X
Xstatic struct sgttyb sg,sgold;	/* structures for controlling the terminal */
Xstatic struct tchars tcold;	/* device drivers. */
Xstatic struct ltchars ltold;
Xstatic int ldiscold;
Xstatic int lmold;
X
Xstatic char ttynam[BUFFSIZ];	/* name of tty. */
X
Xstatic int subshell_pid;	/* pid of subshell */
X
Xstatic char namepty[BUFFSIZ];	/* name of pty allocated */
Xstatic char nametty[BUFFSIZ];	/* and the tty. */
X
Xint shift_key; /* the key used for escaping the shift character */
Xint ctrl_key; /* the key used for escaping the control character */
X
X
X
Xvoid start_subshell();	
X	/* Start a subshell on the tty */
X
Xvoid alloc_pty_tty();
X	/* Allocate a pair of pty and tty used for conversation with user */
Xvoid de_alloc_pty_tty();
X	/* Deallocate the pty/tty pair */
X
Xvoid do_mplx();
X	/* Read (duplexes) pty connection 'forever'.  */
X      
Xvoid err();
X	/* Aborts connection and delete entry from the utmp file. Sends */
X	/* SIGHUP to the process family. */
X
Xvoid ferr();
X	/* write error to controlling tty */
X
Xvoid setup_for_mplx();
X	/* setup both tty and stdin for read write. */
X
Xvoid raw_mode();
X	/* Sets tty in raw mode. */
X
Xvoid save_tty_setup();
X	/* Save the tty setup. */
X
Xvoid reset_tty();
X	/* Reset tty as saved by the two previous commands */
X
Xvoid clean_up();
X	/* reset world back to normal */
X
Xvoid change_shift();
X	/* Change entries in the shift table. */
X
Xvoid process_input();		/* process input using the escape convention */
Xvoid process_args();		/* process any command args */
Xint grateful_dead();		/* die silent and graceful! */
Xvoid read_hshrc();		/* read the .hshrc file. */
Xchar *getenv();
Xchar *sprintf();
X
X
Xmain(argc, argv)
X      int argc;
X      char **argv;
X{
X
X    (void) strncpy(shell, DEFAULT_SHELL, SHELL_BUFF); /* Initialize */
X    shift_key = '\'';
X    ctrl_key = '^';
X
X    read_hshrc();		/* read any defaults */
X    process_args(argc, argv);
X    in_err = FALSE;
X    
X    subshell_pid = 0;		/* subshell is not initialized */
X    save_tty_setup();		/* save tty setup */
X    alloc_pty_tty();		/* allocate pty and tty */
X    setup_for_mplx();		/* setup tty/pty and stdin */
X    start_subshell();		/* start subshell */
X    do_mplx();			/* multiplex between stdin/tty */
X    clean_up();
X    reset_tty();		/* return without destroying tty setup */
X    err("do_mplx returned..");
X    /*NOTREACHED*/
X}
X
Xstatic int shift_table[] =
X{
X	'!',
X	'"',
X	'#',
X	'$',
X	'%',
X	'&',
X	'*',
X	'(',
X	')',
X	'*',
X	'?',
X	';',
X	'_',
X	':',
X	'/',
X	'=',
X	'!',
X	'"',
X	'#',
X	'$',
X	'%',
X	'&',
X	'/',
X	'(',
X	')',
X	':',
X	';',
X	'>',
X	'=',
X	'>',
X	'?',
X	'`',
X	'A',
X	'B',
X	'C',
X	'D',
X	'E',
X	'F',
X	'G',
X	'H',
X	'I',
X	'J',
X	'K',
X	'L',
X	'M',
X	'N',
X	'O',
X	'P',
X	'Q',
X	'R',
X	'S',
X	'T',
X	'U',
X	'V',
X	'W',
X	'X',
X	'Y',
X	'Z',
X	'[',
X	'\\',
X	']',
X	'~',
X	'_',
X	'`',
X	'A',
X	'B',
X	'C',
X	'D',
X	'E',
X	'F',
X	'G',
X	'H',
X	'I',
X	'J',
X	'K',
X	'L',
X	'M',
X	'N',
X	'O',
X	'P',
X	'Q',
X	'R',
X	'S',
X	'T',
X	'U',
X	'V',
X	'W',
X	'Z',
X	'Y',
X	'Z',
X	'[',
X	'\\',
X	']',
X	'~'
X};
X
X				/* changes entry in the shift table */
X				/* arg is a string ala "4$5%" */
Xvoid change_shift(new)
X      char *new;
X{
X    register int c, p;
X    p = 0;
X    while (c = new[p++]) {
X        if (c >= 33 && c < 127)
X	    shift_table[c-33] = new[p];
X        p++;	
X    }
X}	
X
X
Xvoid setup_for_mplx()
X{
X
X    int sig;
X	/* Set non-buffering i/o from pty and stdin. */
X    
X    setbuf(pty, CNULL);
X    setbuf(stdin, CNULL);
X    setbuf(stdout, CNULL);
X    setbuf(stderr, CNULL);
X
X	/* setup for raw mode. */
X    raw_mode();
X
X	/* catch child's death */
X    for (sig = 0; sig < 32; sig++) (void) signal(sig, grateful_dead);
X    
X
X}
X
Xvoid save_tty_setup()
X{    
X    if (ioctl(0, TIOCGETD, (char *)&ldiscold) == -1) perror(program);
X    if (ioctl(0, TIOCLGET, (char *)&lmold)    == -1) perror(program);
X    if (ioctl(0, TIOCGETP, (char *)&sgold)    == -1) perror(program);
X    if (ioctl(0, TIOCGETC, (char *)&tcold)    == -1) perror(program);
X    if (ioctl(0, TIOCGLTC, (char *)&ltold)    == -1) perror(program);
X}
X
Xvoid reset_tty()
X{
X    if (ioctl(0, TIOCSETD, &ldiscold) == -1) ferr("ioctl");
X    if (ioctl(0, TIOCLSET, &lmold)    == -1) ferr("ioctl");
X    if (ioctl(0, TIOCSETP, &sgold)    == -1) ferr("ioctl");
X    if (ioctl(0, TIOCSETC, &tcold)    == -1) ferr("ioctl");
X    if (ioctl(0, TIOCSLTC, &ltold)    == -1) ferr("ioctl");
X}
X
Xvoid raw_mode()
X{
X
X    if (ioctl(0, TIOCGETP, &sg) == -1) perror(program);
X    sg.sg_flags = (RAW);
X    if (ioctl(0, TIOCSETP, &sg) == -1) perror(program);
X				/* set raw mode */
X}
X
Xint grateful_dead(ignore)
X      int ignore;
X      
X{
X
X#ifdef lint
X	for(;ignore;);
X#endif lint
X    if (subshell_pid)
X	(void) kill(subshell_pid, SIGHUP);
X    clean_up();
X    (void)printf("\n");
X    exit(0);
X}
X   
Xvoid clean_up()
X{
X
X    (void) fflush(stdout);
X    (void) fflush(stderr);
X    setlinebuf(stdin);
X    setlinebuf(stdout);
X    setlinebuf(stderr);
X    de_alloc_pty_tty();		/* free them */
X    reset_tty();
X}
X
X
Xvoid start_subshell()
X{
X
X    	/* Now create the subshell process environment. */
X    
X    if ((subshell_pid = fork()) == 0) {
X
X				/* clear seteuid bit. */
X	if (setuid(getuid()) < 0) {
X	    ferr("setuid error");
X	    exit(1);
X	}
X
X	/* Set new controling tty. */
X
X	if (setpgrp(0,0) < 0) ferr("setpgrp");
X	if (close(0) < 0) ferr("cty close error");
X	if (open(nametty, O_RDWR, 0) < 0) ferr("cty open");
X	if (dup2(0, 1) < 0) ferr("dup 1");
X	if (dup2(0, 2) < 0) ferr("dup 2");
X
X	if (close(fileno(pty)) < 0) ferr("close pty");
X	if (close(fileno(tty)) < 0) ferr("close tty");
X
X	setlinebuf(stdin);
X	setlinebuf(stdout);
X	setlinebuf(stderr);
X
X	reset_tty();			/* reset tty to same as parent */
X					/* process */
X	/* Now start the subshell process */
X	execl(shell, shell, 0);	    
X	
X	ferr("execl");
X	exit(1);
X    }
X
X	/* the subshell process take care of the tty, so close it. */
X
X    (void) close(fileno(tty));
X
X}
X
X	/* duplex between stdin and pty */
X
Xvoid do_mplx()
X{
X    int readmask;		/* masked used by select */
X    int tmpmask;		/* select returns with files ready for input */
X    register int status; 	/* select returns with status information */
X
X    static char buffer[BUFFSIZ];
X    int rbytes, wbytes;
X    int ptyinmask;		/* these are masks used by select. */
X
X#define stdinmask (1 << 0)
X    int width = 32;		/* This is the size of bits in an integer */
X				/* used by select. (count of bits to check) */
X
X    ptyinmask = (1 << fileno(pty));
X    readmask = (stdinmask | ptyinmask);
X
X    for (;;) {
X				/* selecting input from stding and pty */
X	tmpmask = readmask;
X	status = select(width, &tmpmask, (int*)0, (int*)0, (struct timeval*)0);
X	
X	if (status == 0) {	/* ttyd uses no timeout */
X	    (void) err("timout...\n");
X
X	} else if (status < 0) {
X	    err("select");
X
X	} else {
X
X		/* Read pty, write to stdin */
X
X	    if (tmpmask & ptyinmask) {
X		rbytes = read(fileno(pty), buffer, BUFFSIZ);
X		if (rbytes <= 0)
X		    (void) grateful_dead(-1);
X		wbytes = write(fileno(stdout), buffer, rbytes);
X		if (wbytes <= 0)
X		    err("write error (stdout)");
X		if (wbytes < rbytes)
X		    (void) fprintf(stderr, "%s:Lost input from pty\r\n",
X				   program);
X	    }
X
X		/* Read from stdin, write to pty  */
X
X	    if (tmpmask & stdinmask) {
X		rbytes = read(fileno(stdin), buffer, BUFFSIZ);
X		if (rbytes <= 0)
X		    err("Can't read from stdin");
X		process_input(buffer, rbytes, fileno(pty));
X	    }
X	}
X    }
X}
X
X
X	
X	/* Err aborts the subshell if possible*/
X
Xvoid err(errmsg)
Xchar *errmsg;
X{
X
X    int error;
X    
X    error = errno;		/* save errno in case of errors in err. */
X    in_err = TRUE;
X
X    if (error > 0 && error < sys_nerr)
X	
X	(void) fprintf(stderr, "%s: -- %s -- %s\r\n",
X		       program, errmsg,sys_errlist[error]);
X    else {
X				/* else print it's number */
X	(void) fprintf(stderr, "%s: -- %s -- %d\r\n",
X		       program, errmsg,error);
X    }
X    if (subshell_pid)
X	(void) kill(subshell_pid, SIGHUP);
X    clean_up();
X    exit(1);
X}
X
Xvoid ferr(errmsg)
Xchar *errmsg;
X{
X
X    int error;
X    FILE *fd;
X    
X    error = errno;		/* save errno in case of errors in err. */
X    in_err = TRUE;
X    if ((fd = fopen("/dev/tty", "w")) != NULL) {
X	if (error > 0 && error < sys_nerr)
X	    (void) fprintf(fd, "%s: -- %s -- %s\r\n",
X			   program, errmsg,sys_errlist[error]);
X	else {
X	    /* else print it's number */
X	    (void) fprintf(fd, "%s: -- %s -- %d\r\n", program, errmsg,error);
X	}
X    } else {
X	errno = error;
X	err(errmsg);
X    }
X    exit(1);
X}
X
X
X    /* Try allocate a pty-tty device. Return winning with the files in FILE */
X    /* pointers *pty and *tty. Saves name of tty in ttynam buffer.	    */
X
Xvoid alloc_pty_tty()
X{
X    /* pty's and tty's goes together in pairs with name: */
X    /* /dev/ttyAB and /dev/ptyAB where A may be one of (pqr) */
X    /* and B one of (0123456789abcdef). */
X
X    int A='p', B='0';
X    int ok;
X
X    for (ok = 0; !ok;) {
X	for (;;) {			/* try to open next pty for read */
X					/* Open for both read and write. */
X	    (void) sprintf(namepty, "/dev/pty%c%c", A, B);
X	    pty = fopen(namepty, "r+");
X	    if (pty != NULL)
X		break;			/* found one, now try open the tty */
X	    if (B++ == '9')
X		B = 'a';
X	    if (B > 'f') {
X		B = '0';
X		if (A++ == 'r') {
X		    (void) fprintf(stderr, "%s: No more ptys/ttys\n", program);
X		    exit(1);
X		}
X	    }
X	}
X	(void) sprintf(nametty, "/dev/tty%c%c", A, B);
X	if ((tty = fopen(nametty, "r+")) != NULL)
X	    ok = 1;
X	else {				/* coudn't open the tty so */
X	    (void) close(fileno(pty));	/* release the opened pty  */
X	}
X
X		/* save name of tty for further */
X		/* references for reset_utmp_wtmp() */
X	
X	(void) sprintf(ttynam, "tty%c%c", A, B);
X    }
X    (void) chown(nametty, getuid(), getgid());
X    (void) chmod(nametty, 0622);
X}
X
Xvoid de_alloc_pty_tty()
X{
X
X    (void) close(fileno(pty));
X    (void) close(fileno(tty));
X    (void) chown(nametty, 0, 0);
X    (void) chmod(nametty, 0666);
X}
X
X
X/*
X	Writes input (from tty) onto outfile, the input is parsed for
X	shift- and ctrl-character. The next character is modified
X	accordingly.
X*/
Xvoid process_input(buffer, length, outfile)
X      char *buffer;
X      int length;
X      int outfile;
X{
X
X    register char c;
X    static int last_char = 0, last_shift = 0;
X    char outbuf[BUFFSIZ], *obuf;
X
X    for (obuf = outbuf; length--; buffer++) {
X	/* for each input character */
X	c = *buffer;
X	
X	if (last_char == ctrl_key) {
X	    /* and char may be controllified */
X	    if ((c != ctrl_key && c >= '@') || c == ' ') {
X		*obuf++ = c & 31; /* controllify! */
X	    } else {
X		/* if can't do that, send the ctrl-char and the new one */
X		*obuf++ = ctrl_key;
X		if (c != ctrl_key)
X		    *obuf++ = c;
X	    }
X	    last_char = 0;
X	    continue;
X	}
X
X	if (last_char == shift_key) {
X	    /* and char may be shifted */
X	    if (c >= 33 && c < 127 && c != shift_key && !last_shift) {
X		*obuf++ = shift_table[ c - 33];
X	    } else {
X		/* if can't do that, check conflict (twice shift char) */
X		if (last_shift && c == shift_key) {
X		    /* 3 times shift char */
X		    if (c >= 33 && c < 127)
X			*obuf++ = shift_table[ c - 33];
X		    else
X			*obuf++ = shift_key;
X		    last_char = last_shift = 0;
X		    continue;
X		}
X		
X		/* no conflicts */
X		if (shift_key == c) {
X		    last_shift = 1;
X		    continue;
X		}
X		*obuf++ = shift_key;
X		*obuf++ = c;
X	    }
X	    last_shift = last_char = 0;
X	    continue;
X	}
X
X	last_char = c;
X	if (last_char == shift_key || last_char == ctrl_key)
X	    continue;		/* Don't do any thing with this character */
X
X	*obuf++ = last_char;
X    }
X
X    if (obuf != outbuf)		/* then got something to write */
X	write(outfile, outbuf, obuf - outbuf);
X
X}
X
X
Xvoid usage()
X{
X    (void)fprintf(stderr,
X"usage: [-exec <program>] [-ctrl <ctrl-key>]\n\
X       [-shift <shift-key>] [-alter <shift-table>] \n");
X    exit(1);
X}
X
Xvoid process_args(argc, argv)
X      int argc;
X      char **argv;
X{
X    char *strncpy();
X    program = *argv++;
X    while (--argc) {
X	if (strcmp(*argv, "-exec") == 0) {
X	    if (!--argc) usage();
X	    (void)strncpy(shell, *++argv, SHELL_BUFF);
X	} else if (strcmp(*argv, "-ctrl") == 0) {
X	    if (!--argc) usage();
X	    ctrl_key = **++argv;
X	} else if (strcmp(*argv, "-shift") == 0) {
X	    if (!--argc) usage();
X	    shift_key = **++argv;
X	} else if (strcmp(*argv, "-alter") == 0) {
X	    if (!--argc) usage();
X	    change_shift(*++argv);
X	} else {
X	    usage();
X	}
X	argv++;
X    }
X}
X	    
Xchar this_term[TERM_BUFF];
Xchar term[TERM_BUFF];
Xchar alternate[129];
X
X/* Input an item delimited by : and newline */
Xint in_item(fd, buf, len)
X      FILE *fd;
X      char *buf;
X      int *len;
X{
X    register int c, i;
X    for (i = 0; i < *len; i++) {
X	c = getc(fd);
X	if (c == EOF || c == '\n' || c == ':') {
X	    *len = i;
X	    return c;
X	}
X	if (c == '\r') continue;
X	if (c == '\\') c = getc(fd);
X	*buf++ = c;
X    }
X    while (!(c == EOF || c == '\n' || c == ':')) c = getc(fd);
X    return c;
X}
X
X/* Input a line divided by ":" fields. \ is an escape character */
Xint input_line(fd)
X      FILE *fd;
X{
X    int len;
X    char buf[1];
X
X    len = TERM_BUFF - 1;
X    if (in_item(fd, term, &len) == -1) return -1;
X    term[len] = 0;
X
X    len = 1;
X    if (in_item(fd, buf, &len) == -1) return -1;
X    shift_key = (len == 0) ? DEF_SHIFT_KEY : *buf;
X
X    len = 1;
X    if (in_item(fd, buf, &len) == -1) return -1;
X    ctrl_key = (len == 0) ? DEF_CTRL_KEY : *buf;
X
X    len = SHELL_BUFF - 1;
X    if (in_item(fd, shell, &len) == -1) return -1;
X    shell[len] = 0;
X
X    len = 128;
X    (void) in_item(fd, alternate, &len); /* ignore eventual EOF */
X    alternate[len] = 0;
X    return 0;
X}
X
X/* Read an input line until a term equal getenv("TERM") is seen */
Xvoid read_hshrc()
X{
X    FILE *rc;
X    char *t, *sprintf();
X    struct passwd *pw, *getpwuid();
X    char buf[100];
X
X    if ((t = getenv("TERM")) == NULL)
X	*this_term = 0;
X    else
X	(void) strcpy(this_term, t);	/* live dangerous */
X
X    if ((t = getenv("HOME")) == NULL) {
X	if ((pw = getpwuid(getuid())) == NULL) {
X	    (void) fprintf(stderr, "You don't exists, go away\n");
X	    exit(1);
X	}
X	t = pw->pw_dir;
X    }
X    (void) sprintf(buf, "%s/.hshrc", t);
X    if ((rc = fopen(buf, "r")) == NULL) return;
X    
X    while (1) {
X	if (input_line(rc) == -1) {
X	    (void) close(fileno(rc));
X	    (void) strcpy(shell, DEFAULT_SHELL);
X	    return;
X	}
X	if (*term == 0 || strcmp(term, this_term) == 0) break;
X    }
X    change_shift(alternate);
X    if (*shell == 0) (void) strcpy(shell, DEFAULT_SHELL);
X}
END_OF_FILE
  if test 15147 -ne `wc -c <'hsh.c'`; then
    echo shar: \"'hsh.c'\" unpacked with wrong size!
  fi
  # end of 'hsh.c'
fi
if test -f 'hsh.manl' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hsh.manl'\"
else
  echo shar: Extracting \"'hsh.manl'\" \(6257 characters\)
  sed "s/^X//" >'hsh.manl' <<'END_OF_FILE'
X.\" No copyright, no rights reserved. Everybody is free to use this 
X.\" program. Ole Bj|rn Hessen obh@ifi.uio.no, 1988. 	(\chardef |=\oe)
X.\" 
X.\"
X.\" @(#)hsh.l 1.32 88/03/04 SMI; from UCB 6.2 8/7/85
X.TH HSH L "29 September 1988"
X.SH NAME
X.B  hsh 
X- enables persons with only one finger to use normal terminals.
X.SH SYNOPSIS
X.B hsh
X.RB "[\|" \- exec
X.IR program "\|]
X.RB "[\|" \- ctrl
X.IR ctrl-key "\|]
X.RB "[\|" \- shift
X.IR shift-key "\|]
X.BR
X.RB "[\|" \- alter
X.IR shift-table "\|]
X.SH DESCRIPTION
X.IX hsh "" "\fLhsh\fR \(em enables one-fingers to use an ordinary tty"
X.LP
X.B hsh
Xstarts up a 
X.IR program
Xand acts like a filter between the user
Xand the program. Input from the user is sent to the program and output
Xfrom the program is sent to the terminal. See telnet(1).
X.LP
XIf the user input a special 
X.IR shift-key
Xthen the next
Xcharacter is used to get an upcased or shifted version of the character.
XThis character is then sent to the program.
X.LP
XIf the user type a special
X.IR ctrl-key
Xthen the next character is 
X.IR controllified ,
Xthat is, if you type "a" after the 
X.IR ctrl-key ,
X.B hsh
Xwill send the program the character "^A" (ascii 1).
X.LP
XThus, a person with only one finger may be able to type 
X.BR upcase
Xletters without using the
X.I caps-lock
Xkey. She may also type a
X.B control-character
Xwithout holding down the
X.IR control-key .
X.LP
X.SS "Algorithm"
X.LP
X.B  hsh
Xstarts reading an
X.I init
Xfile. This file defines the terminal's specific
X.IR shift-key ,
X.IR control-key ,
X.IR shift-table
Xand it's
X.I program
X.RB ( shell ) .
XAfter that, hsh processes the arguments, saves the tty state, allocates a
Xnew tty. Then hsh initializes the new tty and starts a 
Xprogram
Xunder the new tty. hsh thereby acts like a telnet program: reading 
Xoutput from the program, sending it to the tty and reading 
Xinput from the user's tty and sending it to the program after
Xescaping the
X.IR ctrl-key
Xand the
X.IR shift-key .
XIf hsh catches any signals, hsh dies.
X.LP
X.SH OPTIONS
X.TP
X.BI \-exec " program"
X.B hsh
Xuses
X.I program
Xas shell instead the usual
X.IR "/bin/csh" .
X.TP
X.BI \-ctrl " key"
X.B hsh
Xuses
X.I key
Xas escape character for controllifying the next character. If the next 
Xcharacter is a space, hsh will send ^@ to the 
X.BR program .
XIf the next character doesn't equal the 
X.IR control-key
Xand is larger than '?', hsh will clear all but the first 5 bit in the 
Xcharacter thus,  
X.IR "'controllifying'"
Xthe character.
X.TP
X.BI \-shift " key"
X.B hsh 
Xuses the next
X.I key
Xas the escape character for 
X.I shifting
Xthe next character, usually this means
Xshifting
X.B lowcase
Xletters to
X.BR upcase ,
Xor 
X.I shifting
Xa digit with the matching character printed on the top of the digit
Xon the keyboard. See entry about the 
X.IR \-alter
Xswitch. 
X.TP
X\ 
XMostly, people will use a 
X.IR shift-key
Xthat may also be
Xused to access an upcase or "shifted" character. In order to
Xdo this, hsh treat the 
X.IR shift-key
X(if pressed twice) rather special.
XThat is: 3 times of 
X.IR shift-key
Xmakes the shifted version of 
X.IR shift-key ;
Xotherwise 2 times 
X.IR shift-key
Xand another key makes up for one 
X.IR shift-key
Xand the last key. This seems to me a bit silly, but I haven't got any
Xbetter ideas...
X.TP
X.BI \-alter " shift-table"
X.B hsh
Xuses the
X.IR shift-table
Xto modify hsh current understanding of keybord layout. On my keybord
Xright now, the "!" character is printed on top of the digit "1" and 
Xon top of "2", the character ``@'' is printed. So, if I push down "1"
Xafter using the
X.IR shift-key ,
XI'll want the computer to understand that I really wanted the character "!".
X.B hsh 
Xexpects this 
X.IR shift-table
Xto be a sequence of 2 character entries. The second character defines
Xwhat 
X.B hsh
Xwill send to
X.IR program
Xif you push down the first character after using the
X.IR shift-key.
X.TP
X.RS 4
X.IR ex : 
X.BR
X1!2@3#4$5%6^7&8*9(0)-_=+|\`~
X.BR
X.RE
X.TP
X\ 
XRemember that this table modifies 
X.BR hsh ' s
Xinternal table, so it's not necessary to define "A" as upcase of "a".
XYou will probably want to set this table in the init file.
X.SH INITFILE
X.SS "Name of initfile:"
X.IR $HOME/.hshrc ;
Xif no environment variable $HOME is defined:
X.IR  <home-directory>/.hshrc , 
Xsee getpwuid(3).
X.SS "Format of initfile:"
X.RS 4
XTERM:shift-key:ctrl-key:SHELL:shift-table
X.RE
X.RS 4
XTERM:shift-key:ctrl-key:SHELL:shift-table
X.RE
X.RS 4
Xetc...
X.RE
X.RS 4
X:shift-key:ctrl-key:SHELL:shift-table
X.RE
X.LP
X.B hsh 
Xgets the name of the terminal type from the 
Xenvironment variable 
X.B $TERM
Xand if there is an entry in the 
X.RB "$HOME/.hshrc"
Xfile for this terminal, 
X.B hsh
Xwill use this entry to set 
X.IR shift-key ,
X.IR ctrl-key ,
X.I program
Xand
X.IR shift-table .
XThe character ":" is used as delimiter. To escape this character or
Xa newline (which ends the record), you can use "\\". If the 
X.B TERM
Xfield is empty, this field is used as default entry.
X.TP
X.IR ex :
X.PP
X.RS 4
Xxterms:':;:/bin/csh:1!2@3#4$5%6^7&8*9(0)\-_=+|\\`~{[}]'";\:,<.>/?
X.RE
X.RS 4
Xtdv2230:':^:/bin/csh:1!2"3#4$5%6&7/8(9)0=+?@`^~'*\-_.\:,;|\\{[}]<>
X.RE
X.RS 4
X:':^:/bin/csh:
X.RE
X.SH DEFAULTS
XProgram  : /bin/csh
X.br
Xshift-key: "'"
X.br
Xctrl-key : "^"
X.br
X.SH INSTALLATION
XFind out which terminals you have and the matching 
X.B $TERM
Xenvironment
Xnames.  Foreach terminal, decide which terminal key you want to use as
X.IR shift-key
Xand which key you want as 
X.IR ctrl-key .
XMake the 
X.IR shift-table :
XType key and it's upcase/shifted version. Make an initfile with 
Xdifferent entries for each type of terminals.
X.PP
XBeware that
X.B hsh
Xshould be installed as suid root because of tty and pty allocation.
XIt's not necessary, but some people don't want other people writing
Xdirectly on their ttys.
X.SH FILES
X$HOME/.hshrc ,
X.br 
X/dev/[tp]ty[p-r][0-9a-f] ,
X.br 
X/dev/tty
X.br
X/bin/csh
X.SH ENVIRONMENT
X$HOME
X.br
X$TERM
X.SH SEE ALSO
X.B
Xtelnet(1), sh(1), csh(1), write(1)
X.SH AUTHOR
XOle Bj\(*frn Hessen,
X.br
Xobh@ifi.uio.no 					(Ole Bjoern Hessen)
X.SH COPYRIGHT
XNone
X.SH BUGS
X.B hsh
Xis only tested on the BSD4.3 flavor of Unix\(tm. I'm sorry
Xbut I don't have access to other systems.
X.br
X.B hsh
Xdepends to much on the Ascii character set.
X.br
XThe use of the 
X.IR shift-key \- shift-key
Xis a bit hairy; I'm open for any suggestion on this point.
X.B 
XHaven't found the others yet..., please mail me if you find someone.
END_OF_FILE
  if test 6257 -ne `wc -c <'hsh.manl'`; then
    echo shar: \"'hsh.manl'\" unpacked with wrong size!
  fi
  # end of 'hsh.manl'
fi
if test -f 'test.hshrc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test.hshrc'\"
else
  echo shar: Extracting \"'test.hshrc'\" \(279 characters\)
  sed "s/^X//" >'test.hshrc' <<'END_OF_FILE'
Xxterms:':;:/bin/csh:1!2@3#4$5%6^7&8*9(0)\-_=+|\\`~{[}]'";\:,<.>/?
Xtdv2230:':^:/bin/csh:1!2"3#4$5%6&7/8(9)0=+?@`^~'*\-_.\:,;|\\{[}]<>
Xta2230:':^:/bin/csh:1!2"3#4$5%6&7/8(9)0=+?@`^~'*\-_.\:,;|\\{[}]<>
Xvt100:':^:/bin/csh:1!2"3#4$5%6&7/8(9)0=+?@`^~'*\-_.\:,;|\\{[}]<>
X:':^:/bin/csh:
END_OF_FILE
  if test 279 -ne `wc -c <'test.hshrc'`; then
    echo shar: \"'test.hshrc'\" unpacked with wrong size!
  fi
  # end of 'test.hshrc'
fi
if test -f 'tmp.hdr' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tmp.hdr'\"
else
  echo shar: Extracting \"'tmp.hdr'\" \(528 characters\)
  sed "s/^X//" >'tmp.hdr' <<'END_OF_FILE'
XHi,
X
XI posted your article tonight about using control/shift keys
Xwith only one hand.  Here is an article written in C to help
Xyou.  The program was written by Ole Bjorn Hessen.
X
X                                Bill McGarry
X                                (203) 337-1518
X
XUUCP:       {oliveb, philabs, decvax, yale}!bunker!wtm
XINTERNET:   wtm@bunker.shel.isc-br.com
XBITNET:     l-hcap@ndsuvm1.bitnet
XFidonet:    The Handicap News BBS (141/420)   1-203-337-1607
X            (300/1200/2400 baud, 24 hours)
XCompuserve: 73170,1064
X
END_OF_FILE
  if test 528 -ne `wc -c <'tmp.hdr'`; then
    echo shar: \"'tmp.hdr'\" unpacked with wrong size!
  fi
  # end of 'tmp.hdr'
fi
echo shar: End of archive.
exit 0
