[comp.sys.sequent] tsh - a modification to csh that adds filec

nagel@paris.ics.uci.edu (Mark Nagel) (09/24/88)

OK, it turns out that this was retrieved from the net at some point
in the past, although I don't know when.  I'm going to post it here
even though I imagine it would be better off posted somewhere else.
Most modern csh's have filec, though, so I guess maybe it is just
Sequent that is distributing 4.2BSD when they say it will be 4.3BSD.

Sorry this is so long...

Mark

#---- cut here ----
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  INSTALL MANIFEST makebuiltins newbgetc.c newshell.1 sh.c
#   sh.hist.c tenex.c
# Wrapped by root@bonnie.ics.uci.edu on Fri Sep 23 13:37:16 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'INSTALL' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'INSTALL'\"
else
echo shar: Extracting \"'INSTALL'\" \(1910 characters\)
sed "s/^X//" >'INSTALL' <<'END_OF_FILE'
XInstalling the new C shell...
X
X1. Adjust the define BUILTINS in tenex.c, if you wish, to whatever
X   directory you want to place the builtins.  We use /usr/local/lib/builtins.
X
X2. If you change it, adjust DIR in the shell script "makebuiltins".
X
X3. Run "makebuiltins".
X
X4. In sh.lex.c replace the bgetc routine with the contents
X   of newbgetc.c
X
X5. Replace sh.c and sh.hist.c with the ones in this distribution.
X
X6. To makefile:
X
X   a. Add tenex.o to OBJS.
X
X   b. If you have not yet installed the new UCB directory code
X      add "-I." to the CFLAGS and dir14.o to OBJS.
X      The -I. will let cc find <dir.h> in the current directory.
X      4.2 Folks should change <dir.h> to <sys/dir.h>
X
X   c. Add -ltermcap to LIBES.
X
X   d. Xstr does not understand static initialized arrays.  So you must
X      either have a special make rule for tenex.c -> tenex.o *without*
X      the xstr processing or remove the xstr stuff entirly.
X      (I've removed it entirly from ours -- "make clean" the first time,
X      of course, if you choose to do this.)
X
X7. make
X
X8. Move ./csh wherever you like.
X
X9. Enjoy.
X
XNotes:
X
X   If you rename csh to something else (like newcsh) and use it as your
X   login shell you'll have to fix login.c to know that newcsh wants the
X   newtty driver.  If you don't, you'll just get a dumb message
X   "Switching to new tty driver".  Alternately, You can just delete this
X   message (from sh.c).  We (HP Labs) have simply replaced /bin/csh with
X   this one as everyones standard login shell.
X
X   The files dir.h and dir14.c support the old 14 character UNIX
X   directories.  Change the makefile if you have the Berkeley arbitrary
X   length file names.
X
XSee newshell.1 man file for changes made.
X
XIf you make any fixes or changes, please mail them back to
X
X		kg@HP-Labs		(CSNET)
X		kg.HP-Labs@Rand-Relay	(ARPANET)
X		ucbvax!hplabs!kg	(UUCP)
X
XComments and suggestions always welcome.
X
X				-Ken Greer
END_OF_FILE
if test 1910 -ne `wc -c <'INSTALL'`; then
    echo shar: \"'INSTALL'\" unpacked with wrong size!
fi
# end of 'INSTALL'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(362 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X INSTALL                    1	
X MANIFEST                   1	This shipping list
X makebuiltins               1	
X newbgetc.c                 1	
X newshell.1                 1	
X sh.c                       1	
X sh.hist.c                  1	
X tenex.c                    1	
END_OF_FILE
if test 362 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'makebuiltins' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makebuiltins'\"
else
echo shar: Extracting \"'makebuiltins'\" \(432 characters\)
sed "s/^X//" >'makebuiltins' <<'END_OF_FILE'
X#! /bin/sh -x
X# Make a fake builtins directory.
XDIR=/usr/local/lib/builtins
Xif [ ! -d $DIR ]
Xthen
X	mkdir $DIR
Xfi
Xcd $DIR
Xtee @ alias alloc bg case cd chdir dirs eval exec exit fg foreach glob \
X  hashstat history if jobs kill limit login logout newgrp nice nohup \
X  notify onintr popd pushd rehash repeat set setenv source stop suspend \
X  switch time umask unalias unhash unlimit unset unsetenv wait while < /dev/null
Xchmod 555 *
END_OF_FILE
if test 432 -ne `wc -c <'makebuiltins'`; then
    echo shar: \"'makebuiltins'\" unpacked with wrong size!
fi
chmod +x 'makebuiltins'
# end of 'makebuiltins'
fi
if test -f 'newbgetc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'newbgetc.c'\"
else
echo shar: Extracting \"'newbgetc.c'\" \(1483 characters\)
sed "s/^X//" >'newbgetc.c' <<'END_OF_FILE'
Xbgetc()
X{
X	register int buf, off, c;
X	char ttyline[BUFSIZ];
X	register int numleft = 0, roomleft;
X
X#ifdef TELL
X	if (cantell) {
X		if (fseekp < fbobp || fseekp > feobp) {
X			fbobp = feobp = fseekp;
X			lseek(SHIN, fseekp, 0);
X		}
X		if (fseekp == feobp) {
X			fbobp = feobp;
X			do
X			    c = read(SHIN, fbuf[0], BUFSIZ);
X			while (c < 0 && errno == EINTR);
X			if (c <= 0)
X				return (-1);
X			feobp += c;
X		}
X		c = fbuf[0][fseekp - fbobp];
X		fseekp++;
X		return (c);
X	}
X#endif
Xagain:
X	buf = (int) fseekp / BUFSIZ;
X	if (buf >= fblocks) {
X		register char **nfbuf = (char **) calloc(fblocks+2, sizeof (char **));
X
X		if (fbuf) {
X			blkcpy(nfbuf, fbuf);
X			xfree((char *)fbuf);
X		}
X		fbuf = nfbuf;
X		fbuf[fblocks] = calloc(BUFSIZ, sizeof (char));
X		fblocks++;
X		goto again;
X	}
X	if (fseekp >= feobp) {
X		buf = (int) feobp / BUFSIZ;
X		off = (int) feobp % BUFSIZ;
X		roomleft = BUFSIZ - off;
X		do
X		    if (intty)			/* then use tenex routine */
X		    {
X			c = numleft ? numleft : tenex(ttyline, BUFSIZ);
X			if (c > roomleft)	/* No room in this buffer? */
X			{
X			    /* start with fresh buffer */
X			    feobp = fseekp = fblocks * BUFSIZ;
X			    numleft = c;
X			    goto again;
X			}
X			if (c > 0)
X			    copy (fbuf[buf] + off, ttyline, c);
X			numleft = 0;
X		    }
X		    else
X			c = read(SHIN, fbuf[buf] + off, roomleft);
X		while (c < 0 && errno == EINTR);
X		if (c <= 0)
X			return (-1);
X		feobp += c;
X		if (!intty)
X		    goto again;
X	}
X	c = fbuf[buf][(int) fseekp % BUFSIZ];
X	fseekp++;
X	return (c);
X}
END_OF_FILE
if test 1483 -ne `wc -c <'newbgetc.c'`; then
    echo shar: \"'newbgetc.c'\" unpacked with wrong size!
fi
# end of 'newbgetc.c'
fi
if test -f 'newshell.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'newshell.1'\"
else
echo shar: Extracting \"'newshell.1'\" \(6659 characters\)
sed "s/^X//" >'newshell.1' <<'END_OF_FILE'
X.TH NEWSHELL 1 LOCAL
X.SH NAME
Xnewshell \- New C shell with tenex-like file name and command completion
X.SH DESCRIPTION
XWe have installed
Xan enhanced version of the Berkeley UNIX C shell
X.I csh (1).
XIt behaves exactly like the C shell,
Xexcept for the added utilities of:
X.PP
X.in +6
X.ti -3
X1) Interactive file name, command, and user name recognition and
Xcompletion.
X.sp
X.ti -3
X2) Command/File/Directory/User list in the middle of a typed command.
X.sp
X.ti -3
X3) Optional timer for automatic logout after selected idle time.
X.sp
X.ti -3
X4) Terminal mode sanity checking.
X.sp
X.ti -3
X5) Saving history between logouts.
X.in -6
X.PP
XA description of these features follows.
XFor information on the other standard 
X.I csh
Xfeatures, please see "man csh".
X.SH "1. FILE NAME COMPLETION"
XIn typing file names as arguments to commands,
Xit is no longer necessary to type a complete name,
Xonly a unique abbreviation is necessary.
XWhen you type an ESCAPE to
X.I csh
Xit will complete the file name for you,
Xechoing the full name on the terminal.
X.PP
XIf the file name prefix you typed matches no file name, the terminal
Xbell is enunciated.
XThe file name may be partially completed if the prefix matches several
Xlonger file names.  If this is the case, the name is extended up to
Xthe ambiguous deviation, and the bell is enunciated.
X.PP
X.I Example
X.PP
XIn the following example, assume the plus character ``+''
Xis where the user typed the ESCAPE key.
XAssume the current directory contained the files:
X.sp
X.nf
X   DSC.OLD    bin        cmd       lib        memos
X   DSC.NEW    chaosnet   cmtest    mail       netnews
X   bench      class      dev       mbox       new
X.fi
X.sp
XThe command:
X.sp
X		% vi ch+
X.sp
Xwould cause 
X.I csh
Xto complete the command with the name \fIchaosnet\fR.  If instead, the
Xuser had typed:
X.sp
X		% vi D+
X.sp
X.I csh
Xwould have extended the name to \fIDSC.\fR and enunciated the terminal bell, 
Xindicating partial completion.
X.PP
XFile name completion works equally well when other directories are addressed.
XIn addition,
Xthe tilde (~) convention for home directories is understood in this context.
XThus,
X.sp
X		% cd ~speech/data/fr+
X.sp
Xdoes what one might expect.  This may also be used to expand login names only.
XThus,
X.sp
X		% cd ~sy+
X.sp
Xdoes a 
X.I cd
Xto the 
X.I synthesis
Xdirectory.
X.SH "2. FILE/DIRECTORY LIST"
XAt any point in typing a command, you may request "what files are available"
Xor "what files match my current specification".
XThus, when you have typed, perhaps:
X.sp
X		% cd ~speech/data/bench/fritz/
X.sp
Xyou may wish to know what files or subdirectories exist
X(in ~speech/data/bench/fritz),
Xwithout, of course, aborting the command you are typing.
XTyping the character Control-D at this point, will list the files available.
XThe files are listed in multicolumn format, sorted column-wise.
XDirectories and executable files are indicated with a trailing `/' and `*',
Xrespectively.
XOnce printed, the command is re-echoed for you to complete.
X.PP
XAdditionally, one may want to know which files match a prefix, the current file
Xspecification so far.
XIf the user had typed:
X.sp
X		% cd ~speech/data/bench/fr
X.sp
Xfollowed by a control-D, all files and subdirectories whose prefix was
X``fr'' in the directory ~speech/data/bench would be printed.
XNotice that the example before was simply
Xa degenerate case of this with a null trailing file name. 
X(The null string is a prefix of all strings.)
XNotice also, that
Xa trailing slash is required to pass to a \fInew\fR sub-directory for 
Xboth file name completion and listing.
X.PP
XNotice, the degenerate case
X.sp
X		% ~^D
X.sp
Xwill print a full list of login names on the current system.
X.SH "3. COMMAND NAME RECOGNITION"
XCommand name recognition and completion
Xworks in the same manner as file name recognition
Xand completion above.
XThe current value of the environment variable \fBPATH\fR is used
Xin searching for the command.
XFor example
X.sp
X		% newa+
X.sp
Xmight expand to
X.sp
X		% newaliases
X.sp
XAlso,
X.sp
X		% new^D
X.sp
Xwould list all commands (along PATH) that begin with "new".
X.PP
XAs an option, if the shell variable \fIlistpathnum\fR is set, then
Xa number indicating the
Xindex in PATH is printed next to each command on a ^D listing.
X.SH "4. AUTO-LOGOUT"
XA new shell variable has been added called \fIautologout\fB.
XIf the terminal remains idle (no character input) at the shell's
Xtop level for the number of minutes greater than the autologout
Xvalue, you are automatically logged off.
XThis feature was added primarily for security reasons as people
Xoften forget to log off when they leave, permitting anyone to walk by
Xand peruse your mail, or private files.
X.PP
XThe autologout feature is temporarily disabled while a command is executing.
XThe initial value of \fIautologout\fB is 0.
XIf unset or set to 0, autologout is entirely disabled.
X.PP
X.SH "5. SANITY"
XThe shell will now restore your terminal to a sane mode if it appears to
Xreturn from some command in raw, cbreak, or noecho mode.
X.PP
X.SH "6. SAVING HISTORY"
XThe shell now has the facility to save your history list between login session.
XIf the shell variable \fBsavehist\fR is set to a number
Xthen that number of commands from your history list is saved.
XFor example, placing the line
X.PP
X	set history=50 savehist=50
X.PP
Xin your .cshrc file maintains a history list of length 50 and 
Xsaves the entire list when
Xyou logout, to be retored when you login next.
XThe commands are saved in the file \fB.history\fR in your login directory.
X.SH "F.Y.I."
XThis shell uses neither raw or cbreak mode.
XIt works by (temporarily) setting the "additional" tty break character to ESC.
XThere is no overhead usually associated by programs which run in
Xraw or cbreak mode.
X.PP
XIf you select ESC as your default additonal break character, then
Xyou will be able to do recognition on typeahead. 
X.PP
XThe "vb" (visible bell) is used in place of the terminal bell if
Xit exists in the termcap entry for your terminal.
X.SH FILES
X/usr/local/lib/builtins/* - fake list of builtin commands
X.sp
X/etc/termcap - to discover vb (visible bell)
X.SH SEE ALSO
Xcsh (1)
X.SH BUGS
XA control-D on a blank line (a degenerate case of the List command)
Xlogs you out, of course.  (But try <space><control-d>, which lists all
Xcommands on the system along PATH).
X.PP
XIt would be nice if you could change the command characters to something else
Xand not just be stuck with ESC and ^D.
X.PP
XTyping \fIimmediately\fR after hitting ESC
Xbefore recognition expansion completes
Xwill result in character juxtaposition or loss.
X.PP
XYour terminal type is examined the first time you attempt recognition only.
X.SH AUTHORS
XKen Greer, HP Labs; 
XMike Ellis, Fairchild, added command name recognition/completion.
END_OF_FILE
if test 6659 -ne `wc -c <'newshell.1'`; then
    echo shar: \"'newshell.1'\" unpacked with wrong size!
fi
# end of 'newshell.1'
fi
if test -f 'sh.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sh.c'\"
else
echo shar: Extracting \"'sh.c'\" \(20442 characters\)
sed "s/^X//" >'sh.c' <<'END_OF_FILE'
Xstatic char *RCSid =
X"$Header: /usr/local/src/cmd/tcsh/sh.c,v 1.4 83/10/05 22:26:25 kg Exp $";
Xstatic	char *sccsid = "@(#)sh.c 4.9 6/6/82";
X
X#include "sh.h"
X#include <sys/ioctl.h>
X/*
X * C Shell
X *
X * Bill Joy, UC Berkeley, California, USA
X * October 1978, May 1980
X *
X * Jim Kulp, IIASA, Laxenburg, Austria
X * April 1980
X *
X * Filename recognition added:
X * Ken Greer, Ind. Consultant, Palo Alto CA
X * October 1983.
X */
X
Xchar	*pathlist[] =	{ ".", "/usr/ucb", "/bin", "/usr/bin", 0 };
Xchar	*dumphist[] =	{ "history", "-h", 0, 0 };
Xchar	*loadhist[] =	{ "source", "-h", "~/.history", 0 };
Xchar	HIST = '!';
Xchar	HISTSUB = '^';
Xbool	nofile;
Xbool	reenter;
Xbool	nverbose;
Xbool	nexececho;
Xbool	quitit;
Xbool	fast;
Xbool	prompt = 1;
Xbool	enterhist = 0;
X
X#define DEFAULT_AUTOLOGOUT	"60"	/* 1 Hour Alarm default */
X
Xint     phup ();
X
Xauto_logout ()
X{
X    printf ("auto-logout\n");
X    close (SHIN);
X    child++;
X    goodbye ();
X}
X
Xmain(c, av)
X	int c;
X	char **av;
X{
X	register char **v, *cp;
X	register int f;
X
X	settimes();			/* Immed. estab. timing base */
X	v = av;
X	if (eq(v[0], "a.out"))		/* A.out's are quittable */
X		quitit = 1;
X	uid = getuid();
X	loginsh = **v == '-';
X	if (loginsh)
X		time(&chktim);
X
X	/*
X	 * Move the descriptors to safe places.
X	 * The variable didfds is 0 while we have only FSH* to work with.
X	 * When didfds is true, we have 0,1,2 and prefer to use these.
X	 */
X	initdesc();
X
X	/*
X	 * Initialize the shell variables.
X	 * ARGV and PROMPT are initialized later.
X	 * STATUS is also munged in several places.
X	 * CHILD is munged when forking/waiting
X	 */
X
X	set("autologout", DEFAULT_AUTOLOGOUT);
X	sigset (SIGALRM, auto_logout);
X	set("status", "0");
X	dinit(cp = getenv("HOME"));	/* dinit thinks that HOME == cwd in a
X					 * login shell */
X	if (cp == NOSTR)
X		fast++;			/* No home -> can't read scripts */
X	else
X		set("home", savestr(cp));
X	/*
X	 * Grab other useful things from the environment.
X	 * Should we grab everything??
X	 */
X	if ((cp = getenv("USER")) != NOSTR)
X		set("user", savestr(cp));
X	if ((cp = getenv("TERM")) != NOSTR)
X		set("term", savestr(cp));
X	/*
X	 * Re-initialize path if set in environment
X	 */
X	if ((cp = getenv("PATH")) == NOSTR)
X		set1("path", saveblk(pathlist), &shvhed);
X	else {
X		register unsigned i = 0;
X		register char *dp;
X		register char **pv;
X
X		for (dp = cp; *dp; dp++)
X			if (*dp == ':')
X				i++;
X		pv = (char **)calloc(i+2, sizeof (char **));
X		for (dp = cp, i = 0; ;)
X			if (*dp == ':') {
X				*dp = 0;
X				pv[i++] = savestr(*cp ? cp : ".");
X				*dp++ = ':';
X				cp = dp;
X			} else if (*dp++ == 0) {
X				pv[i++] = savestr(*cp ? cp : ".");
X				break;
X			}
X		pv[i] = 0;
X		set1("path", pv, &shvhed);
X	}
X	set("shell", SHELLPATH);
X
X	doldol = putn(getpid());		/* For $$ */
X	shtemp = strspl("/tmp/sh", doldol);	/* For << */
X
X	/*
X	 * Record the interrupt states from the parent process.
X	 * If the parent is non-interruptible our hand must be forced
X	 * or we (and our children) won't be either.
X	 * Our children inherit termination from our parent.
X	 * We catch it only if we are the login shell.
X	 */
X	parintr = signal(SIGINT, SIG_IGN);	/* parents interruptibility */
X	sigset(SIGINT, parintr);			/* ... restore */
X	parterm = signal(SIGTERM, SIG_IGN);	/* parents terminability */
X	signal(SIGTERM, parterm);			/* ... restore */
X	if (loginsh) {
X		signal(SIGHUP, phup);		/* exit processing on HUP */
X		signal(SIGXCPU, phup);		/* ...and on XCPU */
X		signal(SIGXFSZ, phup);		/* ...and on XFSZ */
X	}
X
X	/*
X	 * Process the arguments.
X	 *
X	 * Note that processing of -v/-x is actually delayed till after
X	 * script processing.
X	 *
X	 * We set the first character of our name to be '-' if we are
X	 * a shell running interruptible commands.  Many programs which
X	 * examine ps'es use this to filter such shells out.
X	 */
X	c--, v++;
X	while (c > 0 && (cp = v[0])[0] == '-') {
X		do switch (*cp++) {
X
X		case 0:			/* -	Interruptible, no prompt */
X			prompt = 0;
X			setintr++;
X			nofile++;
X			break;
X
X		case 'c':		/* -c	Command input from arg */
X			if (c == 1)
X				exit(0);
X			c--, v++;
X			arginp = v[0];
X			prompt = 0;
X			nofile++;
X			break;
X
X		case 'e':		/* -e	Exit on any error */
X			exiterr++;
X			break;
X
X		case 'f':		/* -f	Fast start */
X			fast++;
X			break;
X
X		case 'i':		/* -i	Interactive, even if !intty */
X			intact++;
X			nofile++;
X			break;
X
X		case 'n':		/* -n	Don't execute */
X			noexec++;
X			break;
X
X		case 'q':		/* -q	(Undoc'd) ... die on quit */
X			quitit = 1;
X			break;
X
X		case 's':		/* -s	Read from std input */
X			nofile++;
X			break;
X
X		case 't':		/* -t	Read one line from input */
X			onelflg = 2;
X			prompt = 0;
X			nofile++;
X			break;
X
X		case 'v':		/* -v	Echo hist expanded input */
X			nverbose = 1;			/* ... later */
X			break;
X
X		case 'x':		/* -x	Echo just before execution */
X			nexececho = 1;			/* ... later */
X			break;
X
X		case 'V':		/* -V	Echo hist expanded input */
X			setNS("verbose");		/* NOW! */
X			break;
X
X		case 'X':		/* -X	Echo just before execution */
X			setNS("echo");			/* NOW! */
X			break;
X
X		} while (*cp);
X		v++, c--;
X	}
X
X	if (quitit)			/* With all due haste, for debugging */
X		signal(SIGQUIT, SIG_DFL);
X
X	/*
X	 * Unless prevented by -, -c, -i, -s, or -t, if there
X	 * are remaining arguments the first of them is the name
X	 * of a shell file from which to read commands.
X	 */
X	if (nofile == 0 && c > 0) {
X		nofile = open(v[0], 0);
X		if (nofile < 0) {
X			child++;		/* So this ... */
X			Perror(v[0]);		/* ... doesn't return */
X		}
X		file = v[0];
X		SHIN = dmove(nofile, FSHIN);	/* Replace FSHIN */
X		prompt = 0;
X		c--, v++;
X	}
X	/*
X	 * Consider input a tty if it really is or we are interactive.
X	 */
X	intty = intact || isatty(SHIN);
X	/*
X	 * Decide whether we should play with signals or not.
X	 * If we are explicitly told (via -i, or -) or we are a login
X	 * shell (arg0 starts with -) or the input and output are both
X	 * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
X	 * Note that in only the login shell is it likely that parent
X	 * may have set signals to be ignored
X	 */
X	if (loginsh || intact || intty && isatty(SHOUT))
X		setintr = 1;
X#ifdef TELL
X	settell();
X#endif
X	/*
X	 * Save the remaining arguments in argv.
X	 */
X	setq("argv", v, &shvhed);
X
X	/*
X	 * Set up the prompt.
X	 */
X	if (prompt)
X		set("prompt", uid == 0 ? "# " : "% ");
X
X	/*
X	 * If we are an interactive shell, then start fiddling
X	 * with the signals; this is a tricky game.
X	 */
X	shpgrp = getpgrp(0);
X	opgrp = tpgrp = -1;
X	oldisc = -1;
X	if (setintr) {
X		**av = '-';
X		if (!quitit)		/* Wary! */
X			signal(SIGQUIT, SIG_IGN);
X		sigset(SIGINT, pintr);
X		sighold(SIGINT);
X		signal(SIGTERM, SIG_IGN);
X		if (quitit == 0 && arginp == 0) {
X			signal(SIGTSTP, SIG_IGN);
X			signal(SIGTTIN, SIG_IGN);
X			signal(SIGTTOU, SIG_IGN);
X			/*
X			 * Wait till in foreground, in case someone
X			 * stupidly runs
X			 *	csh &
X			 * dont want to try to grab away the tty.
X			 */
X			if (isatty(FSHDIAG))
X				f = FSHDIAG;
X			else if (isatty(FSHOUT))
X				f = FSHOUT;
X			else if (isatty(OLDSTD))
X				f = OLDSTD;
X			else
X				f = -1;
Xretry:
X			if (ioctl(f, TIOCGPGRP, &tpgrp) == 0 && tpgrp != -1) {
X				int ldisc;
X				if (tpgrp != shpgrp) {
X					int old = sigsys(SIGTTIN, SIG_DFL);
X					kill(0, SIGTTIN);
X					sigsys(SIGTTIN, old);
X					goto retry;
X				}
X				if (ioctl(f, TIOCGETD, &oldisc) != 0) 
X					goto notty;
X				if (oldisc != NTTYDISC) {
X#ifdef DEBUG
X					printf("Switching to new tty driver...\n");
X#endif DEBUG
X					ldisc = NTTYDISC;
X					ioctl(f, TIOCSETD, &ldisc);
X				} else
X					oldisc = -1;
X				opgrp = shpgrp;
X				shpgrp = getpid();
X				tpgrp = shpgrp;
X				ioctl(f, TIOCSPGRP, &shpgrp);
X				setpgrp(0, shpgrp);
X				dcopy(f, FSHTTY);
X				ioctl(FSHTTY, FIOCLEX, 0);
X			} else {
Xnotty:
X  printf("Warning: no access to tty; thus no job control in this shell...\n");
X				tpgrp = -1;
X			}
X		}
X	}
X	if (setintr == 0 && parintr == SIG_DFL)
X		setintr++;
X	sigset(SIGCHLD, pchild);		/* while signals not ready */
X
X	/*
X	 * Set an exit here in case of an interrupt or error reading
X	 * the shell start-up scripts.
X	 */
X	setexit();
X	haderr = 0;		/* In case second time through */
X	if (!fast && reenter == 0) {
X		reenter++;
X		/* Will have value("home") here because set fast if don't */
X		srccat(value("home"), "/.cshrc");
X		if (!fast && !arginp && !onelflg)
X			dohash();
X		dosource(loadhist);
X		if (loginsh) {
X			srccat(value("home"), "/.login");
X		}
X	}
X
X	/*
X	 * Now are ready for the -v and -x flags
X	 */
X	if (nverbose)
X		setNS("verbose");
X	if (nexececho)
X		setNS("echo");
X
X	/*
X	 * All the rest of the world is inside this call.
X	 * The argument to process indicates whether it should
X	 * catch "error unwinds".  Thus if we are a interactive shell
X	 * our call here will never return by being blown past on an error.
X	 */
X	process(setintr);
X
X	/*
X	 * Mop-up.
X	 */
X	if (loginsh) {
X		printf("logout\n");
X		close(SHIN);
X		child++;
X		goodbye();
X	}
X	rechist();
X	exitstat();
X}
X
Xuntty()
X{
X
X	if (tpgrp > 0) {
X		setpgrp(0, opgrp);
X		ioctl(FSHTTY, TIOCSPGRP, &opgrp);
X		if (oldisc != -1 && oldisc != NTTYDISC) {
X#ifdef DEBUG
X			printf("\nReverting to old tty driver...\n");
X#endif DEBUG
X			ioctl(FSHTTY, TIOCSETD, &oldisc);
X		}
X	}
X}
X
Ximportpath(cp)
Xchar *cp;
X{
X	register int i = 0;
X	register char *dp;
X	register char **pv;
X	int c;
X	static char dot[2] = {'.', 0};
X
X	for (dp = cp; *dp; dp++)
X		if (*dp == ':')
X			i++;
X	/*
X	 * i+2 where i is the number of colons in the path.
X	 * There are i+1 directories in the path plus we need
X	 * room for a zero terminator.
X	 */
X	pv = (char **) calloc(i+2, sizeof (char **));
X	dp = cp;
X	i = 0;
X	if (*dp)
X	for (;;) {
X		if ((c = *dp) == ':' || c == 0) {
X			*dp = 0;
X			pv[i++] = savestr(*cp ? cp : dot);
X			if (c) {
X				cp = dp + 1;
X				*dp = ':';
X			} else
X				break;
X		}
X		dp++;
X	}
X	pv[i] = 0;
X	set1("path", pv, &shvhed);
X}
X
X/*
X * Source to the file which is the catenation of the argument names.
X */
Xsrccat(cp, dp)
X	char *cp, *dp;
X{
X	register char *ep = strspl(cp, dp);
X	register int unit = dmove(open(ep, 0), -1);
X
X	/* ioctl(unit, FIOCLEX, NULL); */
X	xfree(ep);
X#ifdef DONT_SOURCE_IF_I_DONT_OWN_IT
X	srcunit(unit, 1, 0);
X#else
X	srcunit(unit, 0, 0);
X#endif
X}
X
X/*
X * Source to a unit.  If onlyown it must be our file or our group or
X * we don't chance it.	This occurs on ".cshrc"s and the like.
X */
Xsrcunit(unit, onlyown, hflg)
X	register int unit;
X	bool onlyown;
X	bool hflg;
X{
X	/* We have to push down a lot of state here */
X	/* All this could go into a structure */
X	int oSHIN = -1, oldintty = intty;
X	struct whyle *oldwhyl = whyles;
X	char *ogointr = gointr, *oarginp = arginp;
X	char *oevalp = evalp, **oevalvec = evalvec;
X	int oonelflg = onelflg;
X	bool oenterhist = enterhist;
X	char OHIST = HIST;
X#ifdef TELL
X	bool otell = cantell;
X#endif
X	struct Bin saveB;
X
X	/* The (few) real local variables */
X	jmp_buf oldexit;
X	int reenter;
X
X	if (unit < 0)
X		return;
X	if (didfds)
X		donefds();
X	if (onlyown) {
X		struct stat stb;
X
X		if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_gid !
X= getgid())) {
X			close(unit);
X			return;
X		}
X	}
X
X	/*
X	 * There is a critical section here while we are pushing down the
X	 * input stream since we have stuff in different structures.
X	 * If we weren't careful an interrupt could corrupt SHIN's Bin
X	 * structure and kill the shell.
X	 *
X	 * We could avoid the critical region by grouping all the stuff
X	 * in a single structure and pointing at it to move it all at
X	 * once.  This is less efficient globally on many variable references
X	 * however.
X	 */
X	getexit(oldexit);
X	reenter = 0;
X	if (setintr)
X		sighold(SIGINT);
X	setexit();
X	reenter++;
X	if (reenter == 1) {
X		/* Setup the new values of the state stuff saved above */
X		copy((char *)&saveB, (char *)&B, sizeof saveB);
X		fbuf = (char **) 0;
X		fseekp = feobp = fblocks = 0;
X		oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
X		intty = isatty(SHIN), whyles = 0, gointr = 0;
X		evalvec = 0; evalp = 0;
X		enterhist = hflg;
X		if (enterhist)
X			HIST = '\0';
X		/*
X		 * Now if we are allowing commands to be interrupted,
X		 * we let ourselves be interrupted.
X		 */
X		if (setintr)
X			sigrelse(SIGINT);
X#ifdef TELL
X		settell();
X#endif
X		process(0);		/* 0 -> blow away on errors */
X	}
X	if (setintr)
X		sigrelse(SIGINT);
X	if (oSHIN >= 0) {
X		register int i;
X
X		/* We made it to the new state... free up its storage */
X		/* This code could get run twice but xfree doesn't care */
X		for (i = 0; i < fblocks; i++)
X			xfree(fbuf[i]);
X		xfree((char *)fbuf);
X
X		/* Reset input arena */
X		copy((char *)&B, (char *)&saveB, sizeof B);
X
X		close(SHIN), SHIN = oSHIN;
X		arginp = oarginp, onelflg = oonelflg;
X		evalp = oevalp, evalvec = oevalvec;
X		intty = oldintty, whyles = oldwhyl, gointr = ogointr;
X		if (enterhist)
X			HIST = OHIST;
X		enterhist = oenterhist;
X#ifdef TELL
X		cantell = otell;
X#endif
X	}
X
X	resexit(oldexit);
X	/*
X	 * If process reset() (effectively an unwind) then
X	 * we must also unwind.
X	 */
X	if (reenter >= 2)
X		error(NOSTR);
X}
X
Xrechist()
X{
X	char buf[BUFSIZ];
X	int fp, ftmp, oldidfds;
X
X	if (!fast) {
X		if (value("savehist")[0] == '\0')
X			return;
X		strcpy(buf, value("home"));
X		strcat(buf, "/.history");
X		fp = creat(buf, 0777);
X		if (fp == -1)
X			return;
X		oldidfds = didfds;
X		didfds = 0;
X		ftmp = SHOUT;
X		SHOUT = fp;
X		strcpy(buf, value("savehist"));
X		dumphist[2] = buf;
X		dohist(dumphist);
X		close(fp);
X		SHOUT = ftmp;
X		didfds = oldidfds;
X	}
X}
X
Xgoodbye()
X{
X	if (loginsh) {
X		signal(SIGQUIT, SIG_IGN);
X		sigset(SIGINT, SIG_IGN);
X		signal(SIGTERM, SIG_IGN);
X		setintr = 0;		/* No interrupts after "logout" */
X		if (adrof("home"))
X			srccat(value("home"), "/.logout");
X	}
X	rechist();
X	exitstat();
X}
X
Xexitstat()
X{
X
X	/*
X	 * Note that if STATUS is corrupted (i.e. getn bombs)
X	 * then error will exit directly because we poke child here.
X	 * Otherwise we might continue unwarrantedly (sic).
X	 */
X	child++;
X	exit(getn(value("status")));
X}
X
X/*
X * in the event of a HUP we want to save the history
X */
Xphup()
X{
X	rechist();
X	exit(1);
X}
X
Xchar	*jobargv[2] = { "jobs", 0 };
X/*
X * Catch an interrupt, e.g. during lexical input.
X * If we are an interactive shell, we reset the interrupt catch
X * immediately.  In any case we drain the shell output,
X * and finally go through the normal error mechanism, which
X * gets a chance to make the shell go away.
X */
Xpintr()
X{
X	pintr1(1);
X}
X
Xpintr1(wantnl)
X	bool wantnl;
X{
X	register char **v;
X
X	if (setintr) {
X		sigrelse(SIGINT);
X		if (pjobs) {
X			pjobs = 0;
X			printf("\n");
X			dojobs(jobargv);
X			bferr("Interrupted");
X		}
X	}
X	if (setintr)
X		sighold(SIGINT);
X	sigrelse(SIGCHLD);
X	draino();
X
X	/*
X	 * If we have an active "onintr" then we search for the label.
X	 * Note that if one does "onintr -" then we shan't be interruptible
X	 * so we needn't worry about that here.
X	 */
X	if (gointr) {
X		search(ZGOTO, 0, gointr);
X		timflg = 0;
X		if (v = pargv)
X			pargv = 0, blkfree(v);
X		if (v = gargv)
X			gargv = 0, blkfree(v);
X		reset();
X	} else if (intty && wantnl)
X		printf("\n");		/* Some like this, others don't */
X	error(NOSTR);
X}
X
X/*
X * Process is the main driving routine for the shell.
X * It runs all command processing, except for those within { ... }
X * in expressions (which is run by a routine evalav in sh.exp.c which
X * is a stripped down process), and `...` evaluation which is run
X * also by a subset of this code in sh.glob.c in the routine backeval.
X *
X * The code here is a little strange because part of it is interruptible
X * and hence freeing of structures appears to occur when none is necessary
X * if this is ignored.
X *
X * Note that if catch is not set then we will unwind on any error.
X * If an end-of-file occurs, we return.
X */
Xprocess(catch)
X	bool catch;
X{
X	register char *cp;
X	jmp_buf osetexit;
X	struct command *t;
X
X	getexit(osetexit);
X	for (;;) {
X		pendjob();
X		paraml.next = paraml.prev = &paraml;
X		paraml.word = "";
X		t = 0;
X		setexit();
X		justpr = enterhist;	/* execute if not entering history */
X
X		/*
X		 * Interruptible during interactive reads
X		 */
X		if (setintr)
X			sigrelse(SIGINT);
X
X		/*
X		 * For the sake of reset()
X		 */
X		freelex(&paraml), freesyn(t), t = 0;
X
X		if (haderr) {
X			if (!catch) {
X				/* unwind */
X				doneinp = 0;
X				resexit(osetexit);
X				reset();
X			}
X			haderr = 0;
X			/*
X			 * Every error is eventually caught here or
X			 * the shell dies.  It is at this
X			 * point that we clean up any left-over open
X			 * files, by closing all but a fixed number
X			 * of pre-defined files.  Thus routines don't
X			 * have to worry about leaving files open due
X			 * to deeper errors... they will get closed here.
X			 */
X			closem();
X			continue;
X		}
X		if (doneinp) {
X			doneinp = 0;
X			break;
X		}
X		if (chkstop)
X			chkstop--;
X		if (neednote)
X			pnote();
X		if (intty && evalvec == 0) {
X			mailchk();
X			/*
X			 * If we are at the end of the input buffer
X			 * then we are going to read fresh stuff.
X			 * Otherwise, we are rereading input and don't
X			 * need or want to prompt.
X			 */
X			if (fseekp == feobp)
X				printprompt ();
X			flush();
X			if (cp = value("autologout"))
X			    alarm (atoi (cp) * 60);	/* Autologout ON */
X		}
X		err = 0;
X
X		/*
X		 * Echo not only on VERBOSE, but also with history expansion.
X		 * If there is a lexical error then we forego history echo.
X		 */
X		if (lex(&paraml) && !err && intty ||
X		    adrof("verbose")) {
X			haderr = 1;
X			prlex(&paraml);
X			haderr = 0;
X		}
X		alarm (0);				/* Autologout OFF */
X
X		/*
X		 * The parser may lose space if interrupted.
X		 */
X		if (setintr)
X			sighold(SIGINT);
X
X		/*
X		 * Save input text on the history list if 
X		 * reading in old history, or it
X		 * is from the terminal at the top level and not
X		 * in a loop.
X		 */
X		if (enterhist || catch && intty && !whyles)
X			savehist(&paraml);
X
X		/*
X		 * Print lexical error messages, except when sourcing
X		 * history lists.
X		 */
X		if (!enterhist && err)
X			error(err);
X
X		/*
X		 * If had a history command :p modifier then
X		 * this is as far as we should go
X		 */
X		if (justpr)
X			reset();
X
X		alias(&paraml);
X
X		/*
X		 * Parse the words of the input into a parse tree.
X		 */
X		t = syntax(paraml.next, &paraml, 0);
X		if (err)
X			error(err);
X
X		/*
X		 * Execute the parse tree
X		 */
X		execute(t, tpgrp);
X
X		/*
X		 * Made it!
X		 */
X		freelex(&paraml), freesyn(t);
X	}
X	resexit(osetexit);
X}
X
Xdosource(t)
X	register char **t;
X{
X	register char *f;
X	register int u;
X	bool hflg = 0;
X	char buf[BUFSIZ];
X
X	t++;
X	if (*t && eq(*t, "-h")) {
X		t++;
X		hflg++;
X	}
X	strcpy(buf, *t);
X	f = globone(buf);
X	u = dmove(open(f, 0), -1);
X	xfree(f);
X	if (u < 0 && !hflg)
X		Perror(f);
X	srcunit(u, 0, hflg);
X}
X
X/*
X * Check for mail.
X * If we are a login shell, then we don't want to tell
X * about any mail file unless its been modified
X * after the time we started.
X * This prevents us from telling the user things he already
X * knows, since the login program insists on saying
X * "You have mail."
X */
Xmailchk()
X{
X	register struct varent *v;
X	register char **vp;
X	time_t t;
X	int intvl, cnt;
X	struct stat stb;
X	bool new;
X
X	v = adrof("mail");
X	if (v == 0)
X		return;
X	time(&t);
X	vp = v->vec;
X	cnt = blklen(vp);
X	intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
X	if (intvl < 1)
X		intvl = 1;
X	if (chktim + intvl > t)
X		return;
X	for (; *vp; vp++) {
X		if (stat(*vp, &stb) < 0)
X			continue;
X		new = stb.st_mtime > time0;
X		if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
X		    (stb.st_atime < chktim && stb.st_mtime < chktim) ||
X		    loginsh && !new)
X			continue;
X		if (cnt == 1)
X			printf("You have %smail.\n", new ? "new " : "");
X		else
X			printf("%s in %s.\n", new ? "New mail" : "Mail", *vp);
X	}
X	chktim = t;
X}
X
X#include <pwd.h>
X/*
X * Extract a home directory from the password file
X * The argument points to a buffer where the name of the
X * user whose home directory is sought is currently.
X * We write the home directory of the user back there.
X */
Xgethdir(home)
X	char *home;
X{
X	register struct passwd *pp = getpwnam(home);
X
X	if (pp == 0)
X		return (1);
X	strcpy(home, pp->pw_dir);
X	return (0);
X}
X
X/*
X * Move the initial descriptors to their eventual
X * resting places, closin all other units.
X */
Xinitdesc()
X{
X
X	didcch = 0;			/* Havent closed for child */
X	didfds = 0;			/* 0, 1, 2 aren't set up */
X	SHIN = dcopy(0, FSHIN);
X	SHOUT = dcopy(1, FSHOUT);
X	SHDIAG = dcopy(2, FSHDIAG);
X	OLDSTD = dcopy(SHIN, FOLDSTD);
X	closem();
X}
X
Xexit(i)
X	int i;
X{
X
X	untty();
X#ifdef PROF
X	IEH3exit(i);
X#else
X	_exit(i);
X#endif
X}
X
Xprintprompt ()
X{
X    register char *cp;
X    if (!whyles)
X	for (cp = value ("prompt"); *cp; cp++)
X	    if (*cp == HIST)
X		printf ("%d", eventno + 1);
X	    else
X	    {
X		if (*cp == '\\' && cp[1] == HIST)
X		    cp++;
X		putchar (*cp | QUOTE);
X	    }
X    else
X    /* 
X     * Prompt for forward reading loop
X     * body content.
X     */
X	printf ("? ");
X    flush ();
X}
END_OF_FILE
if test 20442 -ne `wc -c <'sh.c'`; then
    echo shar: \"'sh.c'\" unpacked with wrong size!
fi
# end of 'sh.c'
fi
if test -f 'sh.hist.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sh.hist.c'\"
else
echo shar: Extracting \"'sh.hist.c'\" \(1991 characters\)
sed "s/^X//" >'sh.hist.c' <<'END_OF_FILE'
Xstatic	char *sccsid = "@(#)sh.hist.c 4.3 11/19/81";
X
X#include "sh.h"
X
X/*
X * C shell
X */
X
Xsavehist(sp)
X	struct wordent *sp;
X{
X	register struct Hist *hp, *np;
X	int histlen;
X	register char *cp;
X
X	cp = value("history");
X	if (*cp == 0)
X		histlen = 0;
X	else {
X		while (*cp && digit(*cp))
X			cp++;
X		/* avoid a looping snafu */
X		if (*cp)
X			set("history", "10");
X		histlen = getn(value("history"));
X	}
X	/* throw away null lines */
X	if (sp->next->word[0] == '\n')
X		return;
X	for (hp = &Histlist; np = hp->Hnext;)
X		if (eventno - np->Href >= histlen || histlen == 0)
X			hp->Hnext = np->Hnext, hfree(np);
X		else
X			hp = np;
X	enthist(++eventno, sp, 1);
X}
X
Xstruct Hist *
Xenthist(event, lp, docopy)
X	int event;
X	register struct wordent *lp;
X	bool docopy;
X{
X	register struct Hist *np;
X
X	np = (struct Hist *) calloc(1, sizeof *np);
X	np->Hnum = np->Href = event;
X	if (docopy)
X		copylex(&np->Hlex, lp);
X	else {
X		np->Hlex.next = lp->next;
X		lp->next->prev = &np->Hlex;
X		np->Hlex.prev = lp->prev;
X		lp->prev->next = &np->Hlex;
X	}
X	np->Hnext = Histlist.Hnext;
X	Histlist.Hnext = np;
X	return (np);
X}
X
Xhfree(hp)
X	register struct Hist *hp;
X{
X
X	freelex(&hp->Hlex);
X	xfree((char *)hp);
X}
X
Xdohist(vp)
X	char **vp;
X{
X	int n, rflg = 0, hflg = 0;
X	if (getn(value("history")) == 0)
X		return;
X	if (setintr)
X		sigrelse(SIGINT);
X	vp++;
X	while (*vp[0] == '-') {
X		if (*vp && eq(*vp, "-h")) {
X			hflg++;
X			vp++;
X		}
X		if (*vp && eq(*vp, "-r")) {
X			rflg++;
X			vp++;
X		}
X	}
X	if (*vp)
X		n = getn(*vp);
X	else {
X		n = getn(value("history"));
X	}
X	dohist1(Histlist.Hnext, &n, rflg, hflg);
X}
X
Xdohist1(hp, np, rflg, hflg)
X	struct Hist *hp;
X	int *np, rflg, hflg;
X{
X	bool print = (*np) > 0;
Xtop:
X	if (hp == 0)
X		return;
X	(*np)--;
X	hp->Href++;
X	if (rflg == 0) {
X		dohist1(hp->Hnext, np, rflg, hflg);
X		if (print)
X			phist(hp, hflg);
X		return;
X	}
X	if (*np >= 0)
X		phist(hp, hflg);
X	hp = hp->Hnext;
X	goto top;
X}
X
Xphist(hp, hflg)
X	register struct Hist *hp;
X	int hflg;
X{
X
X	if (hflg == 0)
X		printf("%6d\t", hp->Hnum);
X	prlex(&hp->Hlex);
X}
X
END_OF_FILE
if test 1991 -ne `wc -c <'sh.hist.c'`; then
    echo shar: \"'sh.hist.c'\" unpacked with wrong size!
fi
# end of 'sh.hist.c'
fi
if test -f 'tenex.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tenex.c'\"
else
echo shar: Extracting \"'tenex.c'\" \(19004 characters\)
sed "s/^X//" >'tenex.c' <<'END_OF_FILE'
Xstatic char *RCSid = 
X"$Header: /usr/local/src/cmd/tcsh/tenex.c,v 1.9 83/10/05 21:56:27 kg Exp $";
X
X/*
X * Tenex style file name recognition, .. and more.
X * History:
X *	Author: Ken Greer, Sept. 1975, CMU.
X *	Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
X *
X *	Search and recognition of command names (in addition to file names)
X *	by Mike Ellis, Fairchild A.I. Labs, Sept 1983.
X *
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sgtty.h>
X#ifndef	UCI
X#include <dir.h>
X#else	UCI
X#include <ndir.h>
X#endif	UCI
X#include <signal.h>
X#include <pwd.h>
X/* Don't include stdio.h!  Csh doesn't like it!! */
X#ifdef TEST
X#include <stdio.h>
X#include "dir.h"
X#define flush()		fflush(stdout)
X#endif
X
X#define TRUE		1
X#define FALSE		0
X#define ON		1
X#define OFF		0
X#define FILSIZ		512		/* Max reasonable file name length */
X#define ESC		'\033'
X#define equal(a, b)	(strcmp(a, b) == 0)
X#define is_set(var)	adrof(var)
X#define BUILTINS	"/usr/local/lib/builtins/" /* fake builtin bin */
X
Xextern short SHIN, SHOUT;
Xextern char *getenv ();
Xextern putchar ();
X
Xtypedef enum {LIST, RECOGNIZE} COMMAND;
X
Xstatic char
X    *BELL = "\07";
X
Xstatic
Xsetup_tty (on)
X{
X    static struct tchars  tchars;	/* INT, QUIT, XON, XOFF, EOF, BRK */
X    static char save_t_brkc = -1;	/* Save user's break character */
X
X    sigignore (SIGINT);
X    if (on)
X    {
X	struct sgttyb sgtty;
X
X	ioctl (SHIN, TIOCGETC, &tchars);	/* Get current break character*/
X	save_t_brkc = tchars.t_brkc;		/* Current break char, if any */
X	if (save_t_brkc != ESC)			/* If it's not already ESCAPE */
X	{
X	    tchars.t_brkc = ESC;		/* Set break char to ESCAPE */
X	    ioctl (SHIN, TIOCSETC, &tchars);
X	}
X
X	/*
X	 * This is a useful feature in it's own right...
X	 * The shell makes sure that the tty is not in some weird state
X	 * and fixes it if it is.  But it should be noted that the
X	 * tenex routine will not work correctly in CBREAK or RAW mode
X	 * so this code below is, therefore, mandatory.
X	 */
X	ioctl (SHIN, TIOCGETP, &sgtty);
X#ifndef	UCI
X	if ((sgtty.sg_flags & (RAW | CBREAK)) ||
X	   ((sgtty.sg_flags & ECHO) == 0))	/* not manditory, but nice */
X#else	UCI
X	if (sgtty.sg_flags & (RAW | CBREAK))
X#endif	UCI
X	{
X	    sgtty.sg_flags &= ~(RAW | CBREAK);
X#ifndef	UCI
X	    sgtty.sg_flags |= ECHO;
X#endif	UCI
X	    ioctl (SHIN, TIOCSETP, &sgtty);
X	}
X    }
X    else
X    {
X	/*
X	 * Reset break character to what user had when invoked
X	 * (providing it is different from current one)
X	 */
X	if (save_t_brkc != tchars.t_brkc)
X	{
X	    tchars.t_brkc = save_t_brkc;
X	    ioctl (SHIN, TIOCSETC, &tchars);
X	}
X    }
X    sigrelse (SIGINT);
X}
X
Xstatic
Xtermchars ()
X{
X    extern char *tgetstr ();
X    char bp[1024];
X    static char area[256];
X    static int been_here = 0;
X    char *ap = area;
X    register char *s;
X
X    if (been_here)
X	return;
X    been_here = TRUE;
X
X    if (tgetent (bp, getenv ("TERM")) != 1)
X        return;
X    if (s = tgetstr ("vb", &ap))		/* Visible Bell */
X	BELL = s;
X    return;
X}
X
X/*
X * Move back to beginning of current line
X */
Xstatic
Xback_to_col_1 ()
X{
X    struct sgttyb tty, tty_normal;
X    sigignore (SIGINT);
X    ioctl (SHIN, TIOCGETP, &tty);
X    tty_normal = tty;
X    tty.sg_flags &= ~CRMOD;
X    ioctl (SHIN, TIOCSETN, &tty);
X    (void) write (SHOUT, "\r", 1);
X    ioctl (SHIN, TIOCSETN, &tty_normal);
X    sigrelse (SIGINT);
X}
X
X/*
X * Push string contents back into tty queue
X */
Xstatic
Xpushback (string)
Xchar  *string;
X{
X    register char  *p;
X    struct sgttyb   tty, tty_normal;
X
X    sigignore (SIGINT);
X    ioctl (SHOUT, TIOCGETP, &tty);
X    tty_normal = tty;
X    tty.sg_flags &= ~ECHO;
X    ioctl (SHOUT, TIOCSETN, &tty);
X
X    for (p = string; *p; p++)
X	ioctl (SHOUT, TIOCSTI, p);
X    ioctl (SHOUT, TIOCSETN, &tty_normal);
X    sigrelse (SIGINT);
X}
X
X/*
X * Concatonate src onto tail of des.
X * Des is a string whose maximum length is count.
X * Always null terminate.
X */
Xcatn (des, src, count)
Xregister char *des, *src;
Xregister count;
X{
X    while (--count >= 0 && *des)
X	des++;
X    while (--count >= 0)
X	if ((*des++ = *src++) == 0)
X	    return;
X    *des = '\0';
X}
X
Xstatic
Xmax (a, b)
X{
X    if (a > b)
X	return (a);
X    return (b);
X}
X
X/*
X * like strncpy but always leave room for trailing \0
X * and always null terminate.
X */
Xcopyn (des, src, count)
Xregister char *des, *src;
Xregister count;
X{
X    while (--count >= 0)
X	if ((*des++ = *src++) == 0)
X	    return;
X    *des = '\0';
X}
X
X/*
X * For qsort()
X */
Xstatic
Xfcompare (file1, file2)
Xchar  **file1, **file2;
X{
X    return (strcmp (*file1, *file2));
X}
X
Xstatic char
Xfiletype (dir, file)
Xchar *dir, *file;
X{
X    if (dir)
X    {
X	char path[512];
X	struct stat statb;
X	strcpy (path, dir);
X	catn (path, file, sizeof path);
X	if (stat (path, &statb) >= 0)
X	{
X	    if (statb.st_mode & S_IFDIR)
X		return ('/');
X	    if (statb.st_mode & 0111)
X		return ('*');
X	}
X    }
X    return (' ');
X}
X
X/*
X * Print sorted down columns
X */
Xstatic
Xprint_by_column (dir, items, count, looking_for_command)
Xregister char *dir, *items[];
X{
X    register int i, rows, r, c, maxwidth = 0, columns;
X    for (i = 0; i < count; i++)
X	maxwidth = max (maxwidth, strlen (items[i]));
X    maxwidth += looking_for_command ? 1:2;	/* for the file tag and space */
X    columns = 80 / maxwidth;
X    rows = (count + (columns - 1)) / columns;
X    for (r = 0; r < rows; r++)
X    {
X	for (c = 0; c < columns; c++)
X	{
X	    i = c * rows + r;
X	    if (i < count)
X	    {
X		register int w;
X		printf("%s", items[i]);
X		w = strlen (items[i]);
X		/* Print filename followed by '/' or '*' or ' ' */
X		if (!looking_for_command)
X			putchar (filetype (dir, items[i])), w++;
X		if (c < (columns - 1))			/* Not last column? */
X		    for (; w < maxwidth; w++)
X			putchar (' ');
X	    }
X	}
X	printf ("\n");
X    }
X}
X
X/*
X * expand "old" file name with possible tilde usage
X *		~person/mumble
X * expands to
X *		home_directory_of_person/mumble
X * into string "new".
X */
X
Xchar *
Xtilde (new, old)
Xchar *new, *old;
X{
X    extern struct passwd *getpwuid (), *getpwnam ();
X
X    register char *o, *p;
X    register struct passwd *pw;
X    static char person[40] = {0};
X
X    if (old[0] != '~')
X    {
X	strcpy (new, old);
X	return (new);
X    }
X
X    for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++);
X    *p = '\0';
X
X    if (person[0] == '\0')			/* then use current uid */
X	pw = getpwuid (getuid ());
X    else
X	pw = getpwnam (person);
X
X    if (pw == NULL)
X	return (NULL);
X
X    strcpy (new, pw -> pw_dir);
X    (void) strcat (new, o);
X    return (new);
X}
X
X/*
X * Cause pending line to be printed
X */
Xstatic
Xretype ()
X{
X    int     pending_input = LPENDIN;
X    ioctl (SHOUT, TIOCLBIS, &pending_input);
X}
X
Xstatic
Xbeep ()
X{
X    (void) write (SHOUT, BELL, strlen(BELL));
X}
X
X
X/*
X * parse full path in file into 2 parts: directory and file names
X * Should leave final slash (/) at end of dir.
X */
Xstatic
Xextract_dir_and_name (path, dir, name)
Xchar   *path, *dir, *name;
X{
X    extern char *rindex ();
X    register char  *p;
X    p = rindex (path, '/');
X    if (p == NULL)
X    {
X	copyn (name, path, MAXNAMLEN);
X	dir[0] = '\0';
X    }
X    else
X    {
X	p++;
X	copyn (name, p, MAXNAMLEN);
X	copyn (dir, path, p - path);
X    }
X}
X
X
Xchar *
Xgetentry (dir_fd, looking_for_lognames)
XDIR *dir_fd;
X{
X    if (looking_for_lognames)			/* Is it login names we want? */
X    {
X	extern struct passwd *getpwent ();
X	register struct passwd *pw;
X	if ((pw = getpwent ()) == NULL)
X	    return (NULL);
X	return (pw -> pw_name);
X    }
X    else					/* It's a dir entry we want */
X    {
X	register struct direct *dirp;
X	if (dirp = readdir (dir_fd))
X	    return (dirp -> d_name);
X	return (NULL);
X    }
X}
X
Xstatic
Xfree_items (items)
Xregister char **items;
X{
X    register int i;
X    for (i = 0; items[i]; i++)
X	free (items[i]);
X    free (items);
X}
X
X#define FREE_ITEMS(items)\
X{\
X    sighold (SIGINT);\
X    free_items (items);\
X    items = NULL;\
X    sigrelse (SIGINT);\
X}
X
X#define FREE_DIR(fd)\
X{\
X    sighold (SIGINT);\
X    closedir (fd);\
X    fd = NULL;\
X    sigrelse (SIGINT);\
X}
X
Xstatic int  dirctr;		/* -1 0 1 2 ... */
Xstatic char dirflag[5];		/*  ' nn\0' - dir #s -  . 1 2 ... */
X
X/*
X * Strip next directory from path; return ptr to next unstripped directory.
X */
X 
Xchar *extract_dir_from_path (path, dir)
Xchar *path, dir[];
X{
X    register char *d = dir;
X
X    while (*path && (*path == ' ' || *path == ':')) path++;
X    while (*path && (*path != ' ' && *path != ':')) *(d++) = *(path++);
X    while (*path && (*path == ' ' || *path == ':')) path++;
X
X    ++dirctr;
X    if (*dir == '.')
X        strcpy (dirflag, " .");
X    else
X    {
X        dirflag[0] = ' ';
X	if (dirctr <= 9)
X	{
X		dirflag[1] = '0' + dirctr;
X		dirflag[2] = '\0';
X	}
X	else
X	{
X		dirflag[1] = '0' + dirctr / 10;
X		dirflag[2] = '0' + dirctr % 10;
X		dirflag[3] = '\0';
X	}
X    }
X    *(d++) = '/';
X    *d = 0;
X
X    return path;
X}
X
X/*
X * Perform a RECOGNIZE or LIST command on string "word".
X */
Xstatic
Xsearch (word, wp, command, routine, max_word_length, looking_for_command)
Xchar   *word,
X       *wp;			/* original end-of-word */
XCOMMAND command;
Xint (*routine) ();
X{
X#   define MAXITEMS 2048
X    register numitems,
X	    name_length,		/* Length of prefix (file name) */
X	    looking_for_lognames;	/* True if looking for login names */
X    int	    showpathn;			/* True if we want path number */
X    struct stat
X	    dot_statb,			/* Stat buffer for "." */
X	    curdir_statb;		/* Stat buffer for current directory */
X    int	    dot_scan,			/* True if scanning "." */
X	    dot_got;			/* True if have scanned dot already */
X    char    tilded_dir[FILSIZ + 1],	/* dir after ~ expansion */
X	    dir[FILSIZ + 1],		/* /x/y/z/ part in /x/y/z/f */
X            name[MAXNAMLEN + 1],	/* f part in /d/d/d/f */
X            extended_name[MAXNAMLEN+1],	/* the recognized (extended) name */
X            *entry,			/* single directory entry or logname */
X	    *path;			/* hacked PATH environment variable */
X    static DIR 
X	    *dir_fd = NULL;
X    static char
X           **items = NULL;		/* file names when doing a LIST */
X
X    if (items != NULL)
X	FREE_ITEMS (items);
X    if (dir_fd != NULL)
X	FREE_DIR (dir_fd);
X
X    looking_for_lognames = (*word == '~') && (index (word, '/') == NULL);
X    looking_for_command &= (*word != '~') && (index (word, '/') == NULL);
X
X    if (looking_for_command)
X    {
X        copyn (name, word, MAXNAMLEN);
X        if ((path = getenv ("PATH")) == NULL)
X	    path = "";
X	/* setup builtins as 1st to search before PATH */
X	copyn (dir, BUILTINS, sizeof dir);
X
X	dirctr = -1;		/* BUILTINS -1 */
X	dirflag[0] = 0;
X    }
X    numitems = 0;
X
X    dot_got = FALSE;
X    stat (".", &dot_statb);
X
Xcmdloop:	/* One loop per directory in PATH, if looking_for_command */
X
X    if (looking_for_lognames)			/* Looking for login names? */
X    {
X	setpwent ();				/* Open passwd file */
X	copyn (name, &word[1], MAXNAMLEN);	/* name sans ~ */
X    }
X    else
X    {						/* Open directory */
X        if (!looking_for_command)
X	    extract_dir_and_name (word, dir, name);
X	if ((tilde (tilded_dir, dir) == 0) ||	/* expand ~user/... stuff */
X	    
X	   ((dir_fd = opendir (*tilded_dir ? tilded_dir : ".")) == NULL))
X	{
X	    if (looking_for_command)
X	        goto try_next_path;
X	    else
X		return (0);
X	}
X	dot_scan = FALSE;
X	if (looking_for_command)
X	{
X	    /*
X	     * Are we searching "."?
X	     */
X	    fstat (dir_fd->dd_fd, &curdir_statb);
X	    if (curdir_statb.st_dev == dot_statb.st_dev &&
X	        curdir_statb.st_ino == dot_statb.st_ino)
X	    {
X	        if (dot_got)			/* Second time in PATH? */
X			goto try_next_path;
X		dot_scan = TRUE;
X		dot_got = TRUE;
X	    }
X	}
X    }
X
X    name_length = strlen (name);
X    showpathn = looking_for_command && is_set("listpathnum");
X
X    while (entry = getentry (dir_fd, looking_for_lognames))
X    {
X	if (!is_prefix (name, entry))
X	    continue;
X
X	/*
X	 * Don't match . files on null prefix match
X	 */
X	if (name_length == 0 && entry[0] == '.' && !looking_for_lognames)
X	    continue;
X
X	/*
X	 * Skip non-executables if looking for commands:
X	 * Only done for directory "." for speed.
X	 * (Benchmarked with and without:
X	 * With filetype check, a full search took 10 seconds.
X	 * Without filetype check, a full search took 1 second.)
X	 *                                   -Ken Greer
X         */
X	if (looking_for_command && dot_scan && filetype (dir, entry) != '*')
X	    continue;
X
X	if (command == LIST)		/* LIST command */
X	{
X	    extern char *malloc ();
X	    register int length;
X	    if (numitems >= MAXITEMS)
X	    {
X		printf ("\nYikes!! Too many %s!!\n",
X		    looking_for_lognames ? "names in password file":"files");
X		break;
X	    }
X	    if (items == NULL)
X	    {
X		items = (char **) calloc (sizeof (items[1]), MAXITEMS + 1);
X		if (items == NULL)
X		    break;
X	    }
X	    length = strlen(entry) + 1;
X	    if (showpathn)
X		length += strlen(dirflag);
X	    if ((items[numitems] = malloc (length)) == NULL)
X	    {
X		printf ("out of mem\n");
X		break;
X	    }
X	    copyn (items[numitems], entry, MAXNAMLEN);
X	    if (showpathn)
X	        catn (items[numitems], dirflag, MAXNAMLEN);
X	    numitems++;
X	}
X	else					/* RECOGNIZE command */
X	    if (recognize (extended_name, entry, name_length, ++numitems))
X		break;
X    }
X
X    if (looking_for_lognames)
X	endpwent ();
X    else
X	FREE_DIR (dir_fd);
X
Xtry_next_path:
X    if (looking_for_command && *path &&
X    	(path = extract_dir_from_path (path, dir), dir)) 
X    	goto cmdloop;
X    
X    if (command == RECOGNIZE && numitems > 0)
X    {
X	if (looking_for_lognames)
X	    copyn (word, "~", 1);
X	else if (looking_for_command)
X	    word[0] = 0;
X	else
X	    copyn (word, dir, max_word_length);		/* put back dir part */
X	catn (word, extended_name, max_word_length);	/* add extended name */
X	while (*wp) (*routine) (*wp++);
X	return (numitems);
X    }
X
X    if (command == LIST)
X    {
X	qsort (items, numitems, sizeof (items[1]), fcompare);
X	print_by_column (looking_for_lognames ? NULL:tilded_dir, items,
X			 numitems, looking_for_command);
X	if (items != NULL)
X	    FREE_ITEMS (items);
X    }
X    return (0);
X}
X
X/*
X * Object: extend what user typed up to an ambiguity.
X * Algorithm:
X * On first match, copy full entry (assume it'll be the only match) 
X * On subsequent matches, shorten extended_name to the first
X * character mismatch between extended_name and entry.
X * If we shorten it back to the prefix length, stop searching.
X */
Xrecognize (extended_name, entry, name_length, numitems)
Xchar *extended_name, *entry;
X{
X    if (numitems == 1)				/* 1st match */
X	copyn (extended_name, entry, MAXNAMLEN);
X    else					/* 2nd and subsequent matches */
X    {
X	register char *x, *ent;
X	register int len = 0;
X	for (x = extended_name, ent = entry; *x && *x == *ent++; x++, len++);
X	*x = '\0';				/* Shorten at 1st char diff */
X	if (len == name_length)			/* Ambiguous to prefix? */
X	    return (-1);			/* So stop now and save time */
X    }
X    return (0);
X}
X
X/*
X * return true if check items initial chars in template
X * This differs from PWB imatch in that if check is null
X * it items anything
X */
Xstatic
Xis_prefix (check, template)
Xchar   *check,
X       *template;
X{
X    register char  *check_char,
X                   *template_char;
X
X    check_char = check;
X    template_char = template;
X    do
X	if (*check_char == 0)
X	    return (TRUE);
X    while (*check_char++ == *template_char++);
X    return (FALSE);
X}
X
Xstarting_a_command (wordstart, inputline)
Xregister char *wordstart, *inputline;
X{
X    static char
X	    cmdstart[] = ";&(|`",
X	    cmdalive[] = " \t'\"";
X    while (--wordstart >= inputline)
X    {
X	if (index (cmdstart, *wordstart))
X	    break;
X	if (!index (cmdalive, *wordstart))
X	    return (FALSE);
X    }
X    if (wordstart > inputline && *wordstart == '&')	/* Look for >& */
X    {
X	while (wordstart > inputline &&
X			(*--wordstart == ' ' || *wordstart == '\t'));
X	if (*wordstart == '>')
X		return (FALSE);
X    }
X    return (TRUE);
X}
X
Xtenematch (inputline, inputline_size, num_read, command, command_routine)
Xchar   *inputline;		/* match string prefix */
Xint     inputline_size;		/* max size of string */
Xint	num_read;		/* # actually in inputline */
XCOMMAND command;		/* LIST or RECOGNIZE */
Xint	(*command_routine) ();	/* either append char or display char */
X
X{
X    static char 
X	    delims[] = " '\"\t;&<>()|^%";
X    char word [FILSIZ + 1];
X    register char *str_end, *word_start, *cmd_start, *wp;
X    int space_left;
X    int is_a_cmd;		/* UNIX command rather than filename */
X
X    str_end = &inputline[num_read];
X
X   /*
X    * Find LAST occurence of a delimiter in the inputline.
X    * The word start is one character past it.
X    */
X    for (word_start = str_end; word_start > inputline; --word_start)
X	if (index (delims, word_start[-1]))
X	    break;
X
X    space_left = inputline_size - (word_start - inputline) - 1;
X
X    is_a_cmd = starting_a_command (word_start, inputline);
X
X    for (cmd_start = word_start, wp = word; cmd_start < str_end;
X    	 *wp++ = *cmd_start++);
X    *wp = 0;
X   
X    return search (word, wp, command, command_routine, space_left, is_a_cmd);
X}
X
Xchar *CharPtr;
Xstatic
XCharAppend (c)
X{
X    putchar (c);
X    *CharPtr++ = c;
X    *CharPtr   = 0;
X}
X    
Xtenex (inputline, inputline_size)
Xchar   *inputline;
Xint     inputline_size;
X{
X    register int numitems, num_read;
X
X    setup_tty (ON);
X    termchars ();
X    while((num_read = read (SHIN, inputline, inputline_size)) > 0)
X    {
X	register char *str_end, last_char, should_retype;
X	COMMAND command;
X	int tty_local = 0;			/* tty "local mode" bits */
X
X	last_char = inputline[num_read - 1] & 0177;
X
X	if (last_char == '\n' || num_read == inputline_size)
X	    break;
X
X	ioctl (SHIN, TIOCLGET, &tty_local);
X
X	if (last_char == ESC)		/* RECOGNIZE */
X	{
X	    if (tty_local & LCTLECH)
X		printf ("\210\210  \210\210");	/* Erase ^[ */
X	    /*
X	    if (num_read == 1)
X	    {
X	        num_read = tenedit (inputline, inputline_size, "");
X		break;
X	    }
X	    else	
X	    */
X		command = RECOGNIZE;
X		num_read--;
X	}
X	else				/* LIST */
X	    command = LIST,
X	    putchar ('\n');
X
X	CharPtr = str_end = &inputline[num_read];
X	*str_end = '\0';
X
X	numitems = tenematch (inputline, inputline_size, num_read, command,
X			  command == LIST ? putchar : CharAppend);
X	flush ();
X			  
X	if (command == RECOGNIZE)
X	    if (numitems != 1) 			/* Beep = No match/ambiguous */
X		beep ();
X
X	/*
X	 * Tabs in the input line cause trouble after a pushback.
X	 * tty driver won't backspace over them because column positions
X	 * are now incorrect. This is solved by retyping over current line.
X	 */
X	should_retype = FALSE;
X	if (index (inputline, '\t')		/* tab in input line? */
X	    || (tty_local & LCTLECH) == 0)	/* Control chars don't echo? */
X	{
X	    back_to_col_1 ();
X	    should_retype = TRUE;
X	}
X	if (command == LIST)			/* Always retype after LIST */
X	    should_retype = TRUE;
X
X	if (should_retype)
X	    printprompt ();
X
X	pushback (inputline);
X
X	if (should_retype)
X	    retype ();
X    }
X
X    setup_tty (OFF);
X
X    return (num_read);
X}
X
X#ifdef TEST
X
Xshort SHIN = 0, SHOUT = 1;
X
Xprintprompt ()
X{
X    (void) write (SHOUT, "-> ", 3);
X    return (1);
X}
X
Xmain (argc, argv)
Xchar **argv;
X{
X    char    string[128];
X    int n;
X    while (printprompt () && (n = tenex (string, 127)) > 0)
X    {
X	string[n] = '\0';
X	printf ("Tenex returns \"%s\"\n", string);
X    }
X}
X#endif
X
X
END_OF_FILE
if test 19004 -ne `wc -c <'tenex.c'`; then
    echo shar: \"'tenex.c'\" unpacked with wrong size!
fi
# end of 'tenex.c'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mark Nagel
Department of Information and Computer Science, UC Irvine
nagel@ics.uci.edu             (ARPA)             When they ship styrofoam...
{sdcsvax|ucbvax}!ucivax!nagel (UUCP)             ...what do they pack it in?