[net.sources] tcsh with editor

paul@osu-dbs.UUCP (Paul Placeway) (04/20/84)

(I'm trying again for everyone who missed some of it)

The following code is my changes to Ken Greer's tcsh.  I have added a visual
mini-editor to the shell, and cleaned up the expansion routines some what.
note that this is the 4.1 version.  When we get 4.2 up I'll repost the new
changes.

Please send any changes back to me so that I can update our version.

Note: this is part 1 of 7, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul@ohio-state)

================ cut here ================
: This is a shar archive.  Extract with sh, not csh.
echo x - INSTALL
cat > INSTALL << '!Funky!Stuff!'
Installing the new T-C shell...

1. Make a new directory and copy the original csh sources to it.

2. Copy these sources to that directory.

3. The file "DIFFS" is a list of differences between the orignal csh and the
   same files in tcsh.  Go through the file, making the changes to the files.

4. make

5. Move ./tcsh wherever you like.

6. Enjoy.

Note:
   The files dir.h and dir14.c support the old 14 character UNIX
   directories.  Change the makefile if you have the Berkeley arbitrary
   length file names.

   Any references to /u0/osu should be changed to whatever your local
   software directory is.

See tcsh.1 (man file) for changes made.

If you make any fixes or changes, please mail them back to

					Paul W. Placeway
					The Ohio State University IRCC
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul@ohio-state)

!Funky!Stuff!
echo x - DIFFS1
cat > DIFFS1 << '!Funky!Stuff!'
========	diff -bc csh/sh.c tcsh/sh.c	========

*** csh/sh.c	Fri Apr 13 10:43:58 1984
--- tcsh/sh.c	Fri Apr 13 10:45:00 1984
***************
*** 12,18
   * April 1980
   */
  
! char	*pathlist[] =	{ ".", "/usr/ucb", "/bin", "/usr/bin", 0 };
  char	HIST = '!';
  char	HISTSUB = '^';
  bool	nofile;

--- 12,20 -----
   * April 1980
   */
  
! #define AUTOLOGOUT      "60"            /* 1 Hour alarm */
! char    *pathlist[] =   { ".", "/u0/osu/bin", "/usr/ucb", "/bin",
! 			  "/usr/bin", "/u0/local/bin", 0 };
  char    HIST = '!';
  char    HISTSUB = '^';
  bool    nofile;
***************
*** 23,28
  bool	fast;
  bool	prompt = 1;
  
  main(c, av)
  	int c;
  	char **av;

--- 25,38 -----
  bool    fast;
  bool    prompt = 1;
  
+ auto_logout ()
+ {
+     printf("auto-logout\n");
+     close(SHIN);
+     child++;
+     goodbye();
+ }
+ 
  main(c, av)
          int c;
          char **av;
***************
*** 54,59
  	 */
  
  	set("status", "0");
  	dinit(cp = getenv("HOME"));	/* dinit thinks that HOME == cwd in a
  					 * login shell */
  	if (cp == NOSTR)

--- 64,72 -----
           */
  
          set("status", "0");
+ 	set("tcsh", "1");
+         set("autologout", AUTOLOGOUT);
+         sigset (SIGALRM, auto_logout);
          dinit(cp = getenv("HOME"));     /* dinit thinks that HOME == cwd in a
                                           * login shell */
          if (cp == NOSTR)
***************
*** 69,74
  	if ((cp = getenv("TERM")) != NOSTR)
  		set("term", savestr(cp));
  	/*
  	 * Re-initialize path if set in environment
  	 */
  	if ((cp = getenv("PATH")) == NOSTR)

--- 82,99 -----
          if ((cp = getenv("TERM")) != NOSTR)
                  set("term", savestr(cp));
  	/*
+ 	 * set the shell-level var to 1 or increment it.
+ 	 */
+         if ((cp = getenv("SHLVL")) != NOSTR) {
+ 		char buff[128];
+ 		itoa (1 + atoi (cp), buff);
+                 set("shlvl", savestr (buff));
+ 		setenv ("SHLVL", buff);
+ 	} else {
+ 		set("shlvl", savestr("1"));
+ 		setenv ("SHLVL", "1");
+ 	}
+         /*
           * Re-initialize path if set in environment
           */
          if ((cp = getenv("PATH")) == NOSTR)
***************
*** 95,100
  		pv[i] = 0;
  		set1("path", pv, &shvhed);
  	}
  	set("shell", SHELLPATH);
  
  	doldol = putn(getpid());		/* For $$ */

--- 120,126 -----
                  pv[i] = 0;
                  set1("path", pv, &shvhed);
          }
+ 
          set("shell", SHELLPATH);
  
          doldol = putn(getpid());                /* For $$ */
***************
*** 236,243
  	/*
  	 * Set up the prompt.
  	 */
! 	if (prompt)
! 		set("prompt", uid == 0 ? "# " : "% ");
  
  	/*
  	 * If we are an interactive shell, then start fiddling

--- 262,270 -----
          /*
           * Set up the prompt.
           */
!         if (prompt) {
!                 set("prompt", uid == 0 ? "} " : "> ");
! 	}
  
          /*
           * If we are an interactive shell, then start fiddling
***************
*** 312,317
  	haderr = 0;		/* In case second time through */
  	if (!fast && reenter == 0) {
  		reenter++;
  		/* Will have value("home") here because set fast if don't */
  		srccat(value("home"), "/.cshrc");
  		if (!fast && !arginp && !onelflg)

--- 339,349 -----
          haderr = 0;             /* In case second time through */
          if (!fast && reenter == 0) {
                  reenter++;
+                 if (!fast && !arginp && !onelflg) {
+ 		    tw_init_bindings();
+ 		    ilsetup(SHIN, SHOUT);
+ 	    		/* setup the editor BEFORE doing .tcshrc */
+ 		}    
                  /* Will have value("home") here because set fast if don't */
                  srccat(value("home"), "/.tcshrc");
                  if (!fast && !arginp && !onelflg) {
***************
*** 313,320
  	if (!fast && reenter == 0) {
  		reenter++;
  		/* Will have value("home") here because set fast if don't */
! 		srccat(value("home"), "/.cshrc");
! 		if (!fast && !arginp && !onelflg)
  			dohash();
  		if (loginsh) {
  			int ldisc;

--- 345,352 -----
  	    		/* setup the editor BEFORE doing .tcshrc */
  		}    
                  /* Will have value("home") here because set fast if don't */
!                 srccat(value("home"), "/.tcshrc");
!                 if (!fast && !arginp && !onelflg) {
                          dohash();
  		}		/* setup the command compleation path */
                  if (loginsh) {
***************
*** 316,321
  		srccat(value("home"), "/.cshrc");
  		if (!fast && !arginp && !onelflg)
  			dohash();
  		if (loginsh) {
  			int ldisc;
  			srccat(value("home"), "/.login");

--- 348,354 -----
                  srccat(value("home"), "/.tcshrc");
                  if (!fast && !arginp && !onelflg) {
                          dohash();
+ 		}		/* setup the command compleation path */
                  if (loginsh) {
                          int ldisc;
                          srccat(value("home"), "/.login");
***************
*** 414,419
  #ifdef INGRES
  	srcunit(unit, 0);
  #else
  	srcunit(unit, 1);
  #endif
  }

--- 447,455 -----
  #ifdef INGRES
          srcunit(unit, 0);
  #else
+ #ifdef OSU
+         srcunit(unit, 0);
+ #else
          srcunit(unit, 1);
  #endif
  #endif
***************
*** 416,421
  #else
  	srcunit(unit, 1);
  #endif
  }
  
  /*

--- 452,458 -----
  #else
          srcunit(unit, 1);
  #endif
+ #endif
  }
  
  /*
***************
*** 420,426
  
  /*
   * Source to a unit.  If onlyown it must be our file or our group or
!  * we don't chance it.	This occurs on ".cshrc"s and the like.
   */
  srcunit(unit, onlyown)
  	register int unit;

--- 457,463 -----
  
  /*
   * Source to a unit.  If onlyown it must be our file or our group or
!  * we don't chance it.  This occurs on ".tcshrc"s and the like.
   */
  srcunit(unit, onlyown)
          register int unit;
***************
*** 623,629
  	getexit(osetexit);
  	for (;;) {
  		pendjob();
! 		paraml.next = paraml.prev = &paraml;
  		paraml.word = "";
  		t = 0;
  		setexit();

--- 660,666 -----
          getexit(osetexit);
          for (;;) {
                  pendjob();
!                 paraml.next = paraml .prev = &paraml;
                  paraml.word = "";
                  t = 0;
                  setexit();
***************
*** 677,697
  			 * need or want to prompt.
  			 */
  			if (fseekp == feobp)
! 				if (!whyles)
! 					for (cp = value("prompt"); *cp; cp++)
! 						if (*cp == HIST)
! 							printf("%d", eventno + 1);
! 						else {
! 							if (*cp == '\\' && cp[1] == HIST)
! 								cp++;
! 							putchar(*cp | QUOTE);
! 						}
! 				else
! 					/*
! 					 * Prompt for forward reading loop
! 					 * body content.
! 					 */
! 					printf("? ");
  			flush();
  		}
  		err = 0;

--- 714,720 -----
                           * need or want to prompt.
                           */
                          if (fseekp == feobp)
!                                 printprompt ();
                          flush();
                          if (cp = value("autologout"))
                              alarm (atoi(cp) * 60);
***************
*** 693,698
  					 */
  					printf("? ");
  			flush();
  		}
  		err = 0;
  

--- 716,723 -----
                          if (fseekp == feobp)
                                  printprompt ();
                          flush();
+                         if (cp = value("autologout"))
+                             alarm (atoi(cp) * 60);
                  }
                  err = 0;
  
***************
*** 705,710
  			prlex(&paraml);
  			haderr = 0;
  		}
  
  		/*
  		 * The parser may lose space if interrupted.

--- 730,736 -----
                          prlex(&paraml);
                          haderr = 0;
                  }
+                 alarm (0);                      /* Turn auto-logout off */
  
                  /*
                   * The parser may lose space if interrupted.
***************
*** 859,862
  #else
  	_exit(i);
  #endif
  }

--- 885,917 -----
  #else
          _exit(i);
  #endif
+ }
+ 
+ printprompt ()
+ {
+     register char *cp;
+     extern char *value ();
+     char buff[128];
+     PromptBuf[0] = '\0';
+     if (!whyles) {
+             for (cp = value("prompt"); *cp; cp++) {
+                     if (*cp == HIST) {
+                             itoa(eventno + 1, buff);
+ 			    strcat(PromptBuf, buff);
+ 		    }
+                     else {
+                             if (*cp == '\\' && cp[1] == HIST) {
+                                     cp++;
+ 			    }
+                             strncat(PromptBuf, cp, 1);
+                     }
+ 	    }
+     }
+     else {
+             /*
+              * Prompt for forward reading loop
+              * body content.
+              */
+ 	    strncat(PromptBuf, "? ", 2);
+     }
  }

========	diff -bc csh/sh.exec.c tcsh/sh.exec.c	========

*** csh/sh.exec.c	Fri Apr 13 10:44:01 1984
--- tcsh/sh.exec.c	Fri Apr 13 10:45:04 1984
***************
*** 252,257
  	struct stat stb;
  	struct direct dirbuf[BUFSIZ / sizeof (struct direct)];
  	char d_name[DIRSIZ + 1];
  	register int dirf, cnt;
  	int i = 0;
  	struct varent *v = adrof("path");

--- 252,258 -----
  	struct stat stb;
  	struct direct dirbuf[BUFSIZ / sizeof (struct direct)];
  	char d_name[DIRSIZ + 1];
+ 	char fnamebuf[BUFSIZ];
  	register int dirf, cnt;
  	int i = 0;
  	struct varent *v = adrof("path");
***************
*** 257,262
  	struct varent *v = adrof("path");
  	char **pv;
  
  	havhash = 1;
  	for (cnt = 0; cnt < HSHSIZ; cnt++)
  		xhash[cnt] = 0;

--- 258,264 -----
  	struct varent *v = adrof("path");
  	char **pv;
  
+ 	tw_clear_comm_list();
  	havhash = 1;
  	for (cnt = 0; cnt < HSHSIZ; cnt++)
  		xhash[cnt] = 0;
***************
*** 272,278
  			close(dirf);
  			continue;
  		}
! 		while ((cnt = read(dirf, (char *) dirbuf, sizeof dirbuf)) >= sizeof dirbuf[0]) {
  			register struct direct *ep = dirbuf;
  
  			for (cnt /= sizeof(struct direct); cnt > 0; cnt--, ep++) {

--- 274,282 -----
  			close(dirf);
  			continue;
  		}
! 				/* put check for non-exes here */
! 		while ((cnt = read(dirf, (char *) dirbuf, 
! 		    sizeof dirbuf)) >= sizeof dirbuf[0]) {
  			register struct direct *ep = dirbuf;
  
  			for (cnt /= sizeof(struct direct); 
***************
*** 275,281
  		while ((cnt = read(dirf, (char *) dirbuf, sizeof dirbuf)) >= sizeof dirbuf[0]) {
  			register struct direct *ep = dirbuf;
  
! 			for (cnt /= sizeof(struct direct); cnt > 0; cnt--, ep++) {
  				if (ep->d_ino == 0)
  					continue;
  				copdent(d_name, ep->d_name);

--- 279,286 -----
  		    sizeof dirbuf)) >= sizeof dirbuf[0]) {
  			register struct direct *ep = dirbuf;
  
! 			for (cnt /= sizeof(struct direct); 
! 			    cnt > 0; cnt--, ep++) {
  				if (ep->d_ino == 0)
  					continue;
  				copdent(d_name, ep->d_name);
***************
*** 279,284
  				if (ep->d_ino == 0)
  					continue;
  				copdent(d_name, ep->d_name);
  				xhash[hash(d_name)] |= (1 << i);
  			}
  		}

--- 284,299 -----
  				if (ep->d_ino == 0)
  					continue;
  				copdent(d_name, ep->d_name);
+ 				if (*d_name == '.')
+ 					continue;
+ 
+ 				strcpy (fnamebuf, *pv);
+ 				strcat (fnamebuf, "/");
+ 				strcat (fnamebuf, d_name);
+ 
+ 				if (access(fnamebuf, 1)) /* if not exe file */
+ 					continue;
+ 
  				xhash[hash(d_name)] |= (1 << i);
  				tw_add_comm_name (d_name);
  			}
***************
*** 280,285
  					continue;
  				copdent(d_name, ep->d_name);
  				xhash[hash(d_name)] |= (1 << i);
  			}
  		}
  		close(dirf);

--- 295,301 -----
  					continue;
  
  				xhash[hash(d_name)] |= (1 << i);
+ 				tw_add_comm_name (d_name);
  			}
  		}
  		close(dirf);
***************
*** 284,289
  		}
  		close(dirf);
  	}
  }
  
  dounhash()

--- 300,308 -----
  		}
  		close(dirf);
  	}
+ 	tw_add_builtins();
+ 	tw_add_aliases();
+ 	tw_sort_comms ();	/* re-build the command path for twenex.c */
  }
  
  dounhash()

!Funky!Stuff!

paul@osu-dbs.UUCP (Paul Placeway) (04/20/84)

(I'm trying again for everyone who missed some of it)

The following code is my changes to Ken Greer's tcsh.  I have added a visual
mini-editor to the shell, and cleaned up the expansion routines some what.
note that this is the 4.1 version.  When we get 4.2 up I'll repost the new
changes.

Please send any changes back to me so that I can update our version.

Note: this is part 2 of 7, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul@ohio-state)

================ cut here ================
: This is a shar archive.  Extract with sh, not csh.
echo x - inputl.c
cat > inputl.c << '!Funky!Stuff!'
/* @(#)inputl.c	1.1  (Ohio State)  6/24/83  Paul Placeway */

/* 
 * $Compile: cc -DTEST -o foobar -O %f -ltermcap
 */

/*****************************************************************
 * inputl() -- get a promped line from the keyboard with input editing
 * 
 * SYNOPSIS:
 * 	char *
 *	inputl(prompt, string, stringlen)
 *	char *prompt;
 *	char *string;
 *	int stringlen;		length of the above string
 *
 *	ilsetup (instr, outstr)
 *	short instr, outstr;	input and output streams - normaly 0 & 1
 *
 *	int
 *	ilpushback(string)
 *	char *string;
 *
 * DESCRIPTION:
 *	Inputl reads a string from the terminal and allows editing by the
 *	user.  The editing is done by control keys and special use keys such
 *	as DELETE or BACKSPACE.  A list of the controls follows:
 *
 *	^@		Nothing (becuase some parts won't cope with it)
 *
 *	^A		Moves the cursor to the begining of the line (after
 *			the prompt)
 *
 *	^B		Moves the cursor back one space.  Beeps if it is
 *			allready in column one.
 *
 *	^C		Sends a SIGINTR to the current process (from the tty
 *			driver)
 *
 *	^D		Deletes the character that is over the cursor.
 *
 *	^E		Moves the cursor to the end of the line.
 *
 *	^F		Moves the cursor forward one space.  Beeps if at the
 *			end of the line.
 *
 *	^G		Erase entire line and start over.
 *
 *	^H	BS	Same as ^B
 *
 *	^I	TAB	Inserts a tab character
 *
 *	^J	LF	Moves the cursor to the end of the line, inserts a
 *			newline character, and sends the line.
 *
 *	^K		Delete all characters from the current cursor
 *			possition to the end inclusive and save them in a
 *			special buffer to be called back later (see ^Y).
 *
 *	^L		Put a ^L at the end of the line and send it (without
 *			echoing a newline).
 *
 *	^M	RETURN	Same as ^J
 *
 *	^N		Move to the next following word (i.e. skip to the
 *			end of the current whitespace, then skip to the end
 *			of the non-whitespace)
 *
 *	^O		Flush tty output until the next tty input request
 *			(tty driver).
 *
 *	^P		Same as ^N but go backwards.
 *
 *	^Q		Restart output. (see ^S)
 *
 *	^R		Redisplay the current line and move the cursor to
 *			it's original place.
 *
 *	^S		Stop the tty's output.
 *
 *	^T		Transpose previous two characters.
 *
 *	^U		Same as ^K but kill to the beginning of the line
 *			exclusive.
 *
 *	^V		Clear the screen and redisplay the line
 *
 *	^W		Delete the previous word.
 *
 *	^X		Delete the next word (see ^W).
 *
 *	^Y		Yank the characters saved (by ^K or ^U) as if they
 *			were typed on the keyboard.
 *
 *	^Z		Send a SIGTSTP to the current process (tty driver).
 *
 *	^[	ESC	put a ESC at the end of the line and send it
 *			(without echoing a newline)
 *
 *	^\		send a SIGQUIT to the current process (tty driver)
 *
 *	^]		Send a SIGTSTP when the program attempts to read the
 *			NULL. (tty driver) This is usually immeaditly
 *			because of CBREAK mode, but the assignment is left
 *			so that cooked mode uses it.
 *
 *	^^		Insert the next character regardless of it's
 *			meaning.
 *
 *	^_		Put an end of file mark at the end and send the
 *			line. (tty driver)
 *
 *	^?	DELETE	Delete the character immeaditely to the left of the
 *			cursor.  Beeps if the cursor is at the beginning of
 *			the line.
 *
 *	ALL OTHERS	All other characters insert themselfs into the line.
 *
 *	  Ilpushback pushes the string onto the input line exactly as if it
 *	had been typed on the console.  The next call to inputl will print
 *	the string and put the cursor at the end of the line.  If control
 *	characters are passed to ilpushback, they will act as if they had
 *	been quoted.
 *
 * NOTE
 *	This program is in the public domain.  It may be copied and 
 *	modified, but not sold or included in another product which
 *	is sold.
 *
 *	If you change anything, PLEASE send me mail describing your changes
 *	so that I can include them in later versions.  A diff listing would
 *	be very helpful, also.
 *
 *	Also, please send any bug reports to me.  Thanks,
 *
 *					Paul W. Placeway
 *					The Ohio State University IRCC
 *					(UUCP: cbosgd!osu-dbs!paul)
 *					(CSNet: paul@ohio-state)
 *
 *****************************************************************
 */

#define CSH
#define MAKE_INPUTL
				/* so that some defines get done */
#include "inputl.h"

/*
    Interface to termcap library.
*/

static char    *ClearEOL;
static char    *ClearScr;
static char    *DeleteCh;
static char    *StartIns;
static char    *EndIns;
static char    *FwdCh;
static char    *BkdCh;


static char input_line[1024];	/* what i'm getting */
static char kill_buf[1024];	/* for ^K, ^U, ^Y */
static char last_inp[1024];	/* for matching input compleation and ^@ */
static int  endptr;		/* the last char in the line */
static int  cursor;		/* The current cursor posisition (0 = beg) */
static int  killbuf_len = 0;	/* Length of the kill buffer string */
static int  lastin_len = 0;	/* for last input saving */
static int  matching_length = 0;   /* for command compleation */

static int  pushed_input = FALSE;  /* weather some input has been pushed on */
static char *prompt;		/* a global pointer to the prompt */
static int  prompt_length;	/* how long the prompt given is */
static int  edit_flags;		/* flags that effect the cursor
				   posisition and other things */
static int  do_redisplay = FALSE;	/* redraw what you have now */
static int  has_been_setup = FALSE;	/* for autosetup of the terminal */
static int  tty_driver_not_setup = TRUE; /* for autosetup of the tty driver */
static int  keybindings[256];	/* the currently assigned keys */
static int  bindings_been_inited = FALSE;	/* for setup of bindings */
						/* the first time */

#ifdef CSH

extern short SHIN, SHOUT;

#else

static short SHIN = 0;		/* for input and output munging */
static short SHOUT = 1;

#endif

/* vars for the tty driver */    

struct sgttyb old_term;
struct sgttyb new_term;
struct sgttyb none_term;
struct sgttyb test_term;	/* for testing wether the modes have been */
				/* changed on us so we can compensate */

struct tchars  old_tchars;      /* INT, QUIT, XON, XOFF, EOF, BRK */
struct tchars  new_tchars;
struct tchars  none_tchars;

struct ltchars old_ltchars;	/* SUSP, DSTOP, RPRNT, FLUSH, WERASE, LNEXT */
struct ltchars new_ltchars;
struct ltchars none_ltchars;

long old_modwd;			/* both for testing if things have changed */
long test_modwd;		/* on us during proccessing */

/* 
 * inputl.c  --	get an input line from the terminal using full line editing
 *		and take the line on an escape, control-V , return, 
 *		or line feed.
 */

char
inputl(prompt_str, string_to_get, string_to_get_length)
char *prompt_str;		/* prompt string */
char *string_to_get;
int string_to_get_length;	/* max length of the input line */
{
    register int i;
    char c;
    int doing_input;
    char temp_c1;
    char temp_c2;
    int old_cursor;
    int quoted;
    int outputchar();
    char retval;		/* return value - char typed to send line */

    if (isatty (SHIN) == 0) {
	if (read (SHIN, string_to_get, 512) <= 0) {
	    string_to_get[0] = -1;
	    string_to_get[1] = '\0';
	}
	return ('\n');
    }
    
    if (has_been_setup == FALSE) {
        ilsetup();
    }
    
    prompt = prompt_str;
    prompt_length = strlen (prompt);
    if (prompt_length > 70) prompt_length = 70;

    set_term();			/* turn on CBREAK and NOECHO */

    if (pushed_input == FALSE) {
	if (ClearEOL != 0) {
	    write (SHOUT, "\r", 1);		/* start at the beg of line */
	    write (SHOUT, prompt, prompt_length);   /* write the prompt */
	    tputs (ClearEOL, 0, outputchar);
	} else {
	    write (SHOUT, prompt, prompt_length);   /* write the prompt */
	}
	cursor = 0;		/* start cursor off at beginning */
	endptr = 0;
    } else {
	if (do_redisplay == FALSE) {
	    for (i = matching_length; i < endptr; i++) {
		PrintChar (input_line[i], i+1);
	    }
	}
	pushed_input = FALSE;
    }
    

    doing_input = TRUE;

    if (do_redisplay == TRUE) {
	Redisplay();
	do_redisplay = FALSE;
    }

    quoted = FALSE;

    while (doing_input) {
	if (read (SHIN, &c, 1) != 1) {		/* get an input character */
	    input_line[0] = -1;		/* fool it into thinking that the */
	    endptr = 1;			/* end-of-file char was typed */
	    break;
	}
	if (quoted == TRUE) {
	    quoted = FALSE;
	    Insert(c);
	    set_term();		/* reset the special characters */
	}
	else switch (keybindings[c]) {
			/* and parse it for kbrd commands */

	    case YANK_LAST_INPUT:	/* ^@ -- YANK_LAST_INPUT */
		for (i = 0; i < lastin_len; i++) {
		    Insert(last_inp[i]);
		}
		break;
		
	    case BEGINNING_OF_LINE:	/* ^A -- BEGINNING_OF_LINE */
		MoveCursor(cursor, 0);
		cursor = 0;
		break;
		
	    case BACKWARD_CHAR:	/* ^B -- BACKWARD_CHAR */
		if (cursor != 0) {
		    MoveCursor(cursor, cursor - 1);
		    cursor--;
		}
		else beep();
		break;
		
	    case TTY_SIGINTR:	/* ^C -- SIGINTR (tty driver) */
		break;
		
	    case DELETE_CHAR_FORWARD:	/* ^D -- DELETE_CHAR_FORWARD */
		Delete();
		break;
		
	    case END_OF_LINE:	/* ^E -- END_OF_LINE */
		MoveCursor(cursor, endptr);
		cursor = endptr;
		break;
		
	    case FORWARD_CHAR:	/* ^F -- FORWARD_CHAR */
		if (cursor != endptr) {
		    MoveCursor(cursor, cursor +1);  /* move the cursor fwd */
		    cursor++;			  /* by writing its current */
		}				   /* character */
		else beep();	/* at end-of-line */
		break;
		
	    case START_OVER:	/* ^G -- START_OVER */
		ClearLine();
		(void) write (SHOUT, prompt, prompt_length);
		endptr = 0;
		cursor = 0;
		break;
		
	    case KILL_TO_END:	/* ^K -- KILL_TO_END */
		if (cursor == endptr) {
		    beep();
		    break;
		}
		else {
		    killbuf_len = endptr - cursor;
		    for (i = 0; i < killbuf_len; i++) {
		        kill_buf[i] = input_line[cursor + i];
		    }
		    endptr = cursor;
		    if (ClearEOL != 0) {
			tputs (ClearEOL, 0, outputchar);
		    }
		    else {
			Redisplay();
		    }
		    break;
		}
		
		
	    case SEND_EOF:	/* ^_ -- TTY_EOF */
		c = -1;		/* sends an EOF */
		/* fall through to */
	    case SEND_LINE:	/* ^V -- SEND_LINE */
		input_line[endptr] = '\0';	/* just in case */
		doing_input = FALSE;		/* send it */
		MoveCursor(cursor, endptr);	/* make sure your at end */
		retval = c;			/* set the return value */
		break;

	    case NL_SEND_LINE:	/* ^J LF -- NL_SEND_LINE */
		if (endptr > 0) {		/* save the input for ^H */
		    lastin_len = endptr;
		    for (i = 0; i <=lastin_len; i++) {
		        last_inp[i] = input_line[i];
		    }
		}
		input_line[endptr] = '\n';
		retval = '\n';		/* like the name says: NL... */
		doing_input = FALSE;		/* send it */
		if ((c == '\n') || (c == '\r')) {
		    outputchar('\n');
		}
		break;
		
	    case FORWARD_WORD:	/* ^N -- FORWARD_WORD */
		old_cursor = cursor;
		while ((IsInWord (input_line[cursor]) == 0)
		    && (cursor != endptr)) {
		    ++cursor;
		}
		while ((IsInWord (input_line[cursor]))
		    && (cursor != endptr)) {
		    ++cursor;
		}
		MoveCursor(old_cursor, cursor);
		break;

	    case ERR:		/* beep on this key */
		beep();		/* fall through to... */
	    case NOP:		/* no operation */
		break;

	    case TTY_FLUSH_OUTPUT:	/* ^O -- TTY_FLUSH_OUTPUT */
		break;

	    case BACKWARD_WORD:		/* ^P -- BACKWARD_WORD */
		old_cursor = cursor;
		while ((IsInWord (input_line[cursor-1]) == 0)
		    && (cursor != 0)) {
		    --cursor;
		}
		while ((IsInWord (input_line[cursor-1]))
		    && (cursor != 0)) {
		    --cursor;
		}
		MoveCursor(old_cursor, cursor);
		break;

	    case TTY_START_OUTPUT:	/* ^Q -- START_OUTPUT (tty driver) */
		break;
		
	    case REDISPLAY:	/* ^R -- REDISPLAY */
		Redisplay();
		break;
		
	    case TTY_STOP_OUTPUT:	/* ^S -- STOP_OUTPUT */
		break;

	    case TRANSPOSE_CHARS:	/* ^T -- TRANSPOSE_PREVIOUS_TWO */
		if (cursor >= 2) {
		    temp_c2 = input_line[cursor - 2];
		    temp_c1 = input_line[cursor - 1];
		    DelPrev();
		    DelPrev();
		    Insert(temp_c1);
		    Insert(temp_c2);
		}
		else beep();
		break;
		
	    case KILL_TO_BEGINNING:	/* ^U -- KILL_TO_BEGINNING */
		if (cursor == 0) {
		    beep();
		    break;
		}
		else {
		    killbuf_len = cursor;
		    for (i = 0; i < cursor; i++) {
		        kill_buf[i] = input_line[i];
		    }
		    while (cursor != 0) {
		        DelPrev();
		    }
		    break;
		}

	    case CLEAR_SCREEN:	/* ^L -- CLEAR_SCREEN */
		ClearScreen();
		Redisplay();
		break;

	    case DELETE_WORD_BACKWARD:	/* ^W -- DELETE_WORD_BACKWARD */
		while ((IsInWord (input_line[cursor-1]) == 0)
		    && (cursor != 0)) {
		    DelPrev();
		}
		while ((IsInWord (input_line[cursor-1])) 
		    && (cursor != 0)) {
		    DelPrev();
		}
		break;

	    case DELETE_WORD_FORWARD:	/* ^X -- DELETE_WORD_FORWARD */
		while ((IsInWord (input_line[cursor]) == 0)
		    && (cursor != endptr)) {
		    Delete();
		}
		while ((IsInWord (input_line[cursor]))
		    && (cursor != endptr)) {
		    Delete();
		}
		break;

	    case YANK_KILLBUFFER:	/* ^Y -- YANK_KILLBUFFER */
		for (i = 0; i < killbuf_len; i++) {
		    Insert(kill_buf[i]);
		}
		break;

	    case TTY_SIGTSUSP:	/* ^Z -- TTY_SIGTSUSP */
		break;

	    case TTY_SIGQUIT:	/* ^\ -- TTY_SIGQUIT */
		break;

	    case TTY_DSUSP:	/* ^] -- TTY_DSUSP */
		break;

	    case QUOTE_NEXT:	/* ^^ -- QUOTE_NEXT */
		quoted = TRUE;
		nospecial();	/* this is gross -- turn off all the chars */
		break;

	    case DELETE_CHAR_BACKWARD:	/* ^? DEL -- DELETE_CHAR_BACKWARD */
		DelPrev();
		break;

	    case AUTO_INSERT:	/* it's a normal character, insert it */
		Insert(c);
		break;

	    default:		/* it's garbage, ignore it */
		break;
		
	    }			/* end of switch (c) */

	}			/* end of while (doing_input) */

	    
    input_line[endptr+1] = NULL;
    reset_term();
    
    strncpy(string_to_get, input_line, string_to_get_length- 1);
    string_to_get[string_to_get_length] = '\0';
				/* string_to_get = input_line */
    return (retval);
}


/************************************************************/

#ifdef CSH

ilsetup () {

#else

ilsetup(Shin, Shout)
short Shin, Shout;		/* SHIN = input stream, SHOUT = output str. */
{

#endif
    static  char    bp[1024];
    static  char    buffer[1024];
    char    *area = buffer;
    char    *getenv();
    char    *tgetstr();
    char    *name;
    int     retcode;

#ifndef CSH
    SHIN = Shin;
    SHOUT = Shout;
#endif

    name = getenv("TERM");

    retcode = tgetent(bp, name);

    switch(retcode) {

        case -1:
            printf("can't open termcap file.\n");
            exit(1);
            break;

        case 0:
            printf("No termcap entry for %s, using \"dumb\"\n", name);
            if ((tgetent(bp, "dumb")) == -1) {
		printf("can't open termcap file.\n");
		exit (1);
	    }
            break;

    }

    StartIns  = tgetstr("im", &area);
    EndIns    = tgetstr("ei", &area);
    DeleteCh  = tgetstr("dc", &area);
    ClearEOL  = tgetstr("ce", &area);
    ClearScr  = tgetstr("cl", &area);
    FwdCh     = tgetstr("nd", &area);
    BkdCh     = tgetstr("bc", &area);

    InitBindings ();

    has_been_setup = TRUE;

}

static
InitBindings ()
{
    register int i;

    if (bindings_been_inited) return;

    get_tty_special ();		/* get the old chars */

    for (i=0; i<128; i++) {	/* assign all the keys */
	DoBindToKey (AUTO_INSERT, i);
    }
    DoBindToKey(BEGINNING_OF_LINE, 001);
    DoBindToKey(BACKWARD_CHAR, 002);
    DoBindToKey(TTY_SIGINTR, 003);
    DoBindToKey(DELETE_CHAR_FORWARD, 004);
    DoBindToKey(END_OF_LINE, 005);
    DoBindToKey(FORWARD_CHAR, 006);
    DoBindToKey(START_OVER, 007);
    DoBindToKey(YANK_LAST_INPUT, 010);
    DoBindToKey(KILL_TO_END, 013);
    DoBindToKey(SEND_EOF, 037);
    DoBindToKey(SEND_LINE, 000);
    DoBindToKey(SEND_LINE, 077);
    DoBindToKey(SEND_LINE, 033);
    DoBindToKey(NL_SEND_LINE, 012);
    DoBindToKey(NL_SEND_LINE, 015);
    DoBindToKey(FORWARD_WORD, 016);
    DoBindToKey(TTY_FLUSH_OUTPUT, 017);
    DoBindToKey(BACKWARD_WORD, 020);
    DoBindToKey(TTY_START_OUTPUT, 021);
    DoBindToKey(REDISPLAY, 022);
    DoBindToKey(TTY_STOP_OUTPUT, 023);
    DoBindToKey(TRANSPOSE_CHARS, 024);
    DoBindToKey(KILL_TO_BEGINNING, 025);
    DoBindToKey(CLEAR_SCREEN, 014);
    DoBindToKey(DELETE_WORD_BACKWARD, 027);
    DoBindToKey(DELETE_WORD_FORWARD, 030);
    DoBindToKey(YANK_KILLBUFFER, 031);
    DoBindToKey(TTY_SIGTSUSP, 032);
    DoBindToKey(TTY_SIGQUIT, 034);
    DoBindToKey(TTY_DSUSP, 035);
    DoBindToKey(QUOTE_NEXT, 026);	/* ^^ -> ^V (back to normal) */
    DoBindToKey(DELETE_CHAR_BACKWARD, 0177);

    reset_term ();		/* export bindings */
    bindings_been_inited = TRUE;
}

static
ClearLine() {
    int outputchar();

    if (ClearEOL != 0) {
	outputchar ('\r');
	tputs (ClearEOL, 0, outputchar);
    } else {
	outputchar ('\n');
    }
}

static
ClearScreen() {
    int outputchar();
    
    if (ClearScr != 0) {
	tputs (ClearScr, 0, outputchar);
    }
}


static
DelPrevChar() {
    register int distance;
    int Count();
    
    MoveCursor(cursor, cursor-1);
    distance = Count(cursor) - Count(cursor - 1);

    DelScrChar(distance);
}

static
DelChar() {
    register int distance;
    int Count();
    
    distance = Count(cursor + 1) - Count(cursor);

    DelScrChar(distance);
}

static
DelScrChar(distance)
int distance;
{
    register int i;
    int outputchar();

    if (DeleteCh == 0) {	/* do a huge pain */
	for (i = cursor; i < endptr; i++) {
	    PrintChar(input_line[i], i+1);	/* i+1??? brain-dammage */
	}
	for (i = 0; i < distance; i++) {
	    outputchar(' ');
	}

/* 	MoveCursor(endptr + distance, cursor); INCONSISTANT USAGE... */
	for (i = distance; i > 0; i--) {
	    if (BkdCh != 0) {
	        tputs (BkdCh, 0, outputchar);
	    }
	    else {
		outputchar ('\b');
	    }
	}			/* now I'm at the end... */
	MoveCursor(endptr, cursor);
    }
    else {
	for (i = 0; i < distance; i++) {
	    tputs (DeleteCh, 0, outputchar);
	}
    }
}

static
InsChar(c)
char c;
{
    register int i;
    int outputchar();

    if (StartIns == 0) {
	for (i = cursor-1; i < endptr; i++) {
	    PrintChar(input_line[i], i+1);
	}
	MoveCursor(endptr, cursor);
    }
    else {
	if (cursor != endptr) {
	    tputs (StartIns, 0, outputchar);
	}
	
	PrintChar(c, cursor);
	
	if (cursor != endptr) {
	    tputs (EndIns, 0, outputchar);
	}
	
    }

}

static
MoveCursor(where_from, where_to)
int where_from;
int where_to;
{
    int diff;
    int i;
    int Count();
    int outputchar();
    
    diff = Count(where_to) - Count(where_from);

    if (diff > 0) {
	if (FwdCh != 0) {
	    for (i = 0; i < diff; i++) {
	        tputs (FwdCh, 0, outputchar);
	    }
	}
	else {
	    for (i = where_from; i < where_to; i++) {
		PrintChar(input_line[i], i);
	    }
	}
    }
    else if (diff < 0) {
	diff = -diff;
	for (i = 0; i < diff; i++) {
	    if (BkdCh != 0) {
	        tputs (BkdCh, 0, outputchar);
	    }
	    else {
		outputchar ('\b');
	    }
	}
    }
}


/*
    Set terminal to CBREAK and NOECHO.
*/
static
set_term()
{
    if(tty_driver_not_setup == 0) {	/* tty driver is set up */
	ioctl (SHIN, TIOCGETP, &test_term);	/* get the setup */
	ioctl (SHIN, TIOCLGET, &test_modwd);	/* get local mode word */
	if ((old_term.sg_ispeed != test_term.sg_ispeed) ||
	    (old_term.sg_ospeed != test_term.sg_ospeed) ||
	    (old_term.sg_flags != test_term.sg_flags) ||
	    (old_modwd != test_modwd)) {

/* 	    printf ("tty has changed.\n");	 */

	    tty_driver_not_setup = 1;
				/* things have changed, fake it */
	}
    }
    if (tty_driver_not_setup) {
			/* hasn't been setup yet or reseting do to a change */
	get_tty_modes ();	/* get ONLY the mode word and term strucs */
        tty_driver_not_setup = 0;
    }

    ioctl (SHIN, TIOCSETN, &new_term);    /* set the terminal in CBREAK */
    ioctl (SHIN, TIOCSETC, &new_tchars);	/* with these special chars */
    ioctl (SHIN, TIOCSLTC, &new_ltchars);	/* and these local specials */

/* 	printf("in: %d out: %d\n", new_term.sg_ispeed,
		new_term.sg_ospeed); */

}

/* 
	get the special characters from the tty driver.
 */
static
get_tty_special () {

        ioctl (SHIN, TIOCGETP, &old_term);
        ioctl (SHIN, TIOCGETC, &old_tchars);
        ioctl (SHIN, TIOCGLTC, &old_ltchars);

        ioctl (SHIN, TIOCGETP, &new_term);
	ioctl (SHIN, TIOCGETP, &none_term);

        ioctl (SHIN, TIOCGETC, &new_tchars);
        ioctl (SHIN, TIOCGETC, &none_tchars);

        ioctl (SHIN, TIOCGLTC, &new_ltchars);
        ioctl (SHIN, TIOCGLTC, &none_ltchars);

	ioctl (SHIN, TIOCLGET, &old_modwd);	/* get local mode word */
					       /* so that we know old setup */
/* test prints */
/* 	printf("in: %d out: %d\n", old_term.sg_ispeed,
		old_term.sg_ospeed); */


/* the old setup for when inputl exits */

        old_term.sg_flags &= ~(RAW | CBREAK);		/* unset */
        old_term.sg_flags |= (ECHO | CRMOD);		/* set */

	DoBindToKey (DELETE_CHAR_BACKWARD, old_term.sg_erase);
	DoBindToKey (KILL_TO_BEGINNING, old_term.sg_kill);

	DoBindToKey (TTY_SIGINTR, old_tchars.t_intrc);
	DoBindToKey (TTY_SIGQUIT, old_tchars.t_quitc);
	DoBindToKey (TTY_START_OUTPUT, old_tchars.t_startc);
	DoBindToKey (TTY_STOP_OUTPUT, old_tchars.t_stopc);
	DoBindToKey (SEND_EOF, old_tchars.t_eofc);

	old_tchars.t_brkc = -1;		/* none */

	DoBindToKey (TTY_SIGTSUSP, old_ltchars.t_suspc);
	DoBindToKey (TTY_DSUSP, old_ltchars.t_dsuspc);
	DoBindToKey (REDISPLAY, old_ltchars.t_rprntc);
	DoBindToKey (TTY_FLUSH_OUTPUT, old_ltchars.t_flushc);
	DoBindToKey (DELETE_WORD_BACKWARD, old_ltchars.t_werasc);
	DoBindToKey (QUOTE_NEXT, old_ltchars.t_lnextc);

/* now for the current setup (CBREAK with some de-assigned special chars */

        new_term.sg_flags &= ~(ECHO | XTABS);		/* unset */
        new_term.sg_flags |= (CBREAK | CRMOD);		/* set */

				/* the new chars should get worked out in the
				   old chars bind-to-keys */

/*  now the none structures for literal nexting of characters */

        none_term.sg_flags &= ~(ECHO | CRMOD);		/* unset */
        none_term.sg_flags |= (CBREAK);			/* set */

	none_tchars.t_intrc = -1;
	none_tchars.t_quitc = -1;
	none_tchars.t_startc = -1;
	none_tchars.t_stopc = -1;
	none_tchars.t_eofc = -1;
	none_tchars.t_brkc = -1;

	none_ltchars.t_suspc = -1;
	none_ltchars.t_dsuspc = -1;
	none_ltchars.t_rprntc = -1;
	none_ltchars.t_flushc = -1;
	none_ltchars.t_werasc = -1;
	none_ltchars.t_lnextc = -1;

	reset_term ();		/* export the new bound values */
}

/* 
	get only the modes from the tty driver.
 */
static
get_tty_modes () {

        ioctl (SHIN, TIOCGETP, &new_term);	/* get new now, old later */
	ioctl (SHIN, TIOCGETP, &none_term);

	ioctl (SHIN, TIOCLGET, &old_modwd);	/* get local mode word */
					       /* so that we know old setup */
/* now for the current setup (CBREAK with some de-assigned special chars */

        new_term.sg_flags &= ~(ECHO | XTABS);		/* unset */
        new_term.sg_flags |= (CBREAK | CRMOD);		/* set */

	new_term.sg_erase = old_term.sg_erase;	/* do this so that the */
	new_term.sg_kill = old_term.sg_kill;	/* erase and kill chars */
						/* don't get changed to */
        ioctl (SHIN, TIOCGETP, &old_term);	/* something else. */

	old_term.sg_erase = new_term.sg_erase;
	old_term.sg_kill = new_term.sg_kill;

/* the old setup for when inputl exits */

        old_term.sg_flags &= ~(RAW | CBREAK);		/* unset */
        old_term.sg_flags |= (ECHO | CRMOD);		/* set */

/*  now the none structures for literal nexting of characters */

        none_term.sg_flags &= ~(ECHO | CRMOD);		/* unset */
        none_term.sg_flags |= (CBREAK);			/* set */

	reset_term ();		/* export the new bound values */
}

/*
    Reset the terminal to normal mode.
*/
static
reset_term() {

    ioctl (SHIN, TIOCSETN, &old_term);        /* set the terminal in CBREAK */
    ioctl (SHIN, TIOCSETC, &old_tchars);	/* with these special chars */
    ioctl (SHIN, TIOCSLTC, &old_ltchars);	/* and these local specials */

}

/*  unset all special chars */
static
nospecial() {
    ioctl (SHIN, TIOCSETN, &none_term);       /* set the terminal in CBREAK */
    ioctl (SHIN, TIOCSETC, &none_tchars);	/* with these special chars */
    ioctl (SHIN, TIOCSLTC, &none_ltchars);	/* and these local specials */
}

/* ******************************************************* */
#ifdef TEST
main()
{
    char buffer[1024];
    char retnval;
    
    while (1) {

	if ((retnval = inputl("inputl:", buffer, 72)) == '\033') {
	    strcat(buffer, "foo");
	    ilpushback(buffer);
	    continue;
	}
	else
	    printf ("Return value = %o,\nI saw :%s\n", retnval, buffer);
	if (buffer[0] == 'Q') exit(0);
    }
}
#endif
/* ********************************************************* */


static int
outputchar(c)
char c;
{
    (void) write (SHOUT, &c, 1);
}

static
beep()
{
    outputchar(BELL);
}

static
Delete()
{
    register int i;
    
    if (cursor < endptr) {
	for (i = cursor; i < endptr; i++) {
	    input_line[i] = input_line[i+1];
	}
	endptr--;
	DelChar();
    }
    else beep();
}

static
DelPrev()
{
    register int i;
    
    if (cursor != 0) {
	DelPrevChar();
	--cursor;
	if (cursor <0 ) cursor = 0;
	for (i = cursor; i < endptr; i++) {
	    input_line[i] = input_line[i+1];
	}
	endptr--;
	if (endptr <0 ) endptr = 0;
    }
    else beep();
}

static
Redisplay()
{
    register int i;
    
    ClearLine();
    (void) write (SHOUT, prompt, prompt_length);
    for (i = 1; i <= endptr; i++) {
	PrintChar(input_line[i -1], i);
    }
    MoveCursor(endptr, cursor);
}


static
Insert(c)
char c;
{
    register int i;

    if (cursor >= endptr){	/* put it at the end of the line */
	endptr++;
	if (endptr > 1023) endptr = 1023;
	input_line[cursor++] = c;
    }
    else {		/* its in the middle of the line, shift line
				   first, then put in char */
        endptr++;
	if (endptr > 1023) endptr = 1023;
        for (i = endptr; i >= cursor; i--) {
            input_line[i+1] = input_line[i];
        }
        input_line[cursor++] = c;
    }
    InsChar(c);
}

static int
IsInWord (c)
char c;
{
    register char *cp;
    register int is_word_char = 0;
    for (cp = WORD_CHARS; *cp; cp++)
	if (*cp == c)
	    is_word_char = 1;
    return (is_word_char);
}

static int
Count(to_where)			/* count the number of spaces that many */
int to_where;			/* characters take up on the screen */
{
    register int i;
    register int cntr = 0;
    
    for (i = 0; i < to_where; i++) {
	if (input_line[i] == '\t') {
	    cntr += prompt_length;
	    cntr = (((cntr/8)+1)*8);
	    cntr -= prompt_length;
	}
	else if ((input_line[i] < ' ') || (input_line[i] == '\0177')) {
	    cntr += 2;
	}
	else cntr++;
    }
    return (cntr);
}

static
PrintChar(c, pos)
char c;
int pos;			/* where the character is */
{
    register int count;
    register int i;
    
    c &= 0177;
    if (c == '\177') {
	outputchar('^');
	c = '?';
    }
    else if (c == '\t') {
	count = Count(pos) - Count(pos -1);
	for (i = 0; i < count; i++) {
	    outputchar(' ');
	}
	return;
    }
    else if (c < ' ') {
	outputchar('^');
	c |= 0100;
    }
    outputchar(c);
}

/* **************************************************************** */
ilpushback(string)
char string[];
{
    register int i;
    int string_length;
    int matched;
    
    string_length = strlen(string);
    if (string_length > 1023) string_length = 1023;
    matching_length = 0;
    matched = 1;

    for (i = 0; i <= string_length; i++) {
        if ((input_line[i] == string[i]) && (matched)) {
	    matching_length++;
	}
	else {
	    matched = FALSE;
	    input_line[i] = string[i];
	}
    }
    
    endptr = string_length;
    if (endptr > 1023) endptr = 1023;
    cursor = endptr;
    
    pushed_input = TRUE;
/*  do_redisplay = TRUE; */
    
}

ilredisplay()
{
    do_redisplay = TRUE;
}

/* this next pair is done this way so that I don't get an infinate recursion
   bug with ilsetup.  it is important that ilsetup is called first, so that
   the keys actually get bound. */

ilbindtokey (func, key)
int func;
char key;
{
    if (has_been_setup == FALSE) {
        ilsetup();
    }

    DoBindToKey (func, key);
}

/* force a reset_term */

ilreset_tty()
{
    reset_term();
}
    
static
DoBindToKey (func, key)
int func;
char key;
{
    if (key == -1) {		/* trap unsetings */
	return (-1);
    }

    if ((func < 0) || (func > 200)) {
	return(-1);
    }

    keybindings[key] = func;
    switch (func) {
	    case TTY_DSUSP:
	        old_ltchars.t_dsuspc = key;
	        new_ltchars.t_dsuspc = key;
		break;
		
	    case TTY_FLUSH_OUTPUT:
	        old_ltchars.t_flushc = key;	/* ^O */
	        new_ltchars.t_flushc = key;	/* ^O */
		break;

	    case TTY_SIGINTR:
		old_tchars.t_intrc = key;	/* ^C */
		new_tchars.t_intrc = key;	/* ^C */
		break;

	    case TTY_SIGQUIT:
		old_tchars.t_quitc = key;	/* ^\ */
		new_tchars.t_quitc = key;	/* ^\ */
		break;

	    case TTY_SIGTSUSP:
		old_ltchars.t_suspc = key;	/* ^Z */
		new_ltchars.t_suspc = key;	/* ^Z */
		break;

	    case TTY_START_OUTPUT:
		old_tchars.t_startc = key;	/* ^Q */
		new_tchars.t_startc = key;	/* ^Q */
		break;

	    case TTY_STOP_OUTPUT:
		old_tchars.t_stopc = key;	/* ^S */
		new_tchars.t_stopc = key;	/* ^S */
		break;

	    case REDISPLAY:
		old_ltchars.t_rprntc = key;	/* ^R */
		new_ltchars.t_rprntc = -1;	/* ^R */
		break;

	    case SEND_EOF:
		old_tchars.t_eofc = key;	/* ^_ */
		new_tchars.t_eofc = -1;		/* ^_ */
		break;

	    case QUOTE_NEXT:
		old_ltchars.t_lnextc = key;	/* ^^ */
		new_ltchars.t_lnextc = -1;	/* ^^ */
		break;

	    case DELETE_WORD_BACKWARD:
		old_ltchars.t_werasc = key;	/* ^W */
		new_ltchars.t_werasc = -1;	/* ^W */
		break;

	    case DELETE_CHAR_BACKWARD:
		old_term.sg_erase = key;
		new_term.sg_erase = -1;
		break;

	    case KILL_TO_BEGINNING:
		old_term.sg_kill = key;
		new_term.sg_kill = -1;
		break;

	    default:
		return;
    }
}

int
ilgetbinding(key)
char key;
{
    return (keybindings[key]);
}


static int saved_ldisc;		/* line discipline */
static struct sgttyb saved_term;	/* old terminal modes + ERASE, KILL */
static struct tchars  saved_tchars;      /* INT, QUIT, XON, XOFF, EOF, BRK */
static struct ltchars saved_ltchars;
				/* SUSP, DSTOP, RPRNT, FLUSH, WERASE, LNEXT */
static long saved_modwd;
static int tty_has_been_saved = 0;

/* 
 * Save and restore all of the terminal states
 */

ilsavetty () {
    ioctl (SHIN, TIOCGETD, &saved_ldisc);
    ioctl (SHIN, TIOCGETP, &saved_term);
    ioctl (SHIN, TIOCGETC, &saved_tchars);
    ioctl (SHIN, TIOCGLTC, &saved_ltchars);
    ioctl (SHIN, TIOCLGET, &saved_modwd);
    tty_has_been_saved = 1;
}

ilrestoretty () {
    if (tty_has_been_saved == 0) return;
    ioctl (SHIN, TIOCSETD, &saved_ldisc);
    ioctl (SHIN, TIOCSETN, &saved_term);
    ioctl (SHIN, TIOCSETC, &saved_tchars);
    ioctl (SHIN, TIOCSLTC, &saved_ltchars);
    ioctl (SHIN, TIOCLSET, &saved_modwd);
}
!Funky!Stuff!
echo x - inputl.h
cat > inputl.h << '!Funky!Stuff!'
#ifdef MAKE_INPUTL

#define	TRUE	1
#define	FALSE	0
#define	NULL	0
#define	BELL	'\007'

/* this next is for all characters that should be considered a word to the
   shell */

#define WORD_CHARS	"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*?_-."

#include <sgtty.h>

#endif

#define	AUTO_INSERT		0
#define	BACKWARD_CHAR		1
#define	BACKWARD_WORD		2
#define	BEGINNING_OF_LINE	3
#define	CLEAR_SCREEN		4
#define	DELETE_CHAR_BACKWARD	5
#define	DELETE_CHAR_FORWARD	6
#define	DELETE_WORD_BACKWARD	7
#define	DELETE_WORD_FORWARD	8
#define	END_OF_LINE		9
#define	SEND_EOF		10
#define	FORWARD_CHAR		11
#define	FORWARD_WORD		12
#define	KILL_TO_BEGINNING	13
#define	KILL_TO_END		14
#define	NL_SEND_LINE		15
#define	QUOTE_NEXT		16
#define	REDISPLAY		17
#define	SEND_LINE		18
#define	START_OVER		19
#define	TRANSPOSE_CHARS		20
#define	YANK_KILLBUFFER		21
#define	YANK_LAST_INPUT		22
#define NOP			23
#define ERR			24

#define	TTY_DSUSP		101
#define	TTY_FLUSH_OUTPUT	102
#define	TTY_SIGINTR		103
#define	TTY_SIGQUIT		104
#define	TTY_SIGTSUSP		105
#define	TTY_START_OUTPUT	106
#define	TTY_STOP_OUTPUT		107
!Funky!Stuff!

paul@osu-dbs.UUCP (Paul Placeway) (04/20/84)

(I'm trying again for everyone who missed some of it)

The following code is my changes to Ken Greer's tcsh.  I have added a visual
mini-editor to the shell, and cleaned up the expansion routines some what.
note that this is the 4.1 version.  When we get 4.2 up I'll repost the new
changes.

Please send any changes back to me so that I can update our version.

Note: this is part 3 of 7, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul@ohio-state)

================ cut here ================
: This is a shar archive.  Extract with sh, not csh.
echo x - TCSH.README
cat > TCSH.README << '!Funky!Stuff!'
------------------------------------------------------------------
C-shell with tenex-style file name recognition (and more).

I've included the full source of the changed files here.
If you've made local changes to your C shell, use diff
to see what I've done. 

If you fix any bugs or make improvements, please send them
back to me!   (Please note bug mentioned in manual tcsh.1!! -
NB: that has been fixed --fjl)

                        Ken Greer
                        Hewlett-Packard
                        Computer Research Center
                        Palo Alto, Ca. 94301

                        ucbvax!hplabs!kg     (on uucp)
                        kg@hplabs            (on CSNET)
                        kg.hplabs@udel-relay (on ARPANET)
                        (415) 857-8801       (on MA-BELL NET)


The following files have been added or changed:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sh.c (changed):
1) The code to print out the shell prompt was moved into
   a procedure "printprompt" from the previous in-line code.
   The routine "tenex" (in tenex.c) calls it.
2) Added an "autologout" facility.  No action at prompt level
   for one hour logs you out.  Time controlled by shell variable
   "autologout" set to minutes.  An unset or setting to 0 turns
   autologout off.
--------------
sh.lex.c (changed):
Added code to call "tenex" routine when reading from terminal.
--------------
tenex.c (new):
The file name recognition code.
--------------
tcsh.1 (new):
Man entry for changes made.
--------------
makefile (changed):
Changed to load with tenex.o.  Output executable called "tcsh".
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTES:
1) To make autologout useful on dialups, add to getty
   at line 190:
            ioctl(0, TIOCHPCL, 0);              /* Hang up on close */
   (This should be there anyway to hangup up the modem on logout.)

2) If you run "tcsh" as a login shell, login won't know to set you
   up with the new tty driver. Here's my login.c fix with
   < = oldlogin, > = newlogin...

140c140
<               if (!strcmp(pwd->pw_shell, "/bin/csh")) {
---
>               if (cshell (pwd->pw_shell)) {
364a365,379
> }
> 
> /*
>  * return TRUE if the shell is the C shell.
>  * Algorithm: TRUE if "csh" appears anywhere in string.
>  */
> cshell (shell)
> char *shell;
> {
>     register char *p;
>     extern char *index ();
>     for (p = shell; p = index (p, 'c'); p++)
>       if (strncmp (p, "csh", 3) == 0)
>           return (TRUE);
>     return (FALSE);
------------------------------------------------------------------
!Funky!Stuff!
echo x - READ_TOO
cat > READ_TOO << '!Funky!Stuff!'
Lepreau's notes:

1. Here's a minor change I made to kg's tenex.c.  You may want
        to change the value to simething else, perhaps depending if you
        have auto-wrap on your terminals.

Use the full 80 columns for alternatives display.
Should probably do a getwidth ioctl and use that instead...

176c176
<     columns = 78 / maxwidth;
---
>     columns = 80 / maxwidth;


2. makefile.orig is kg's original.
   makefile is the above plus some local Utah flags, minus a real SCCS, and set
        up to install tcsh as linked to csh.

3. The man page should mention the "autologout" shell variable.
!Funky!Stuff!
echo x - READ_THREE
cat > READ_THREE << '!Funky!Stuff!'
Placeway's notes:

1. An editor has been added to tcsh which is called by twenex().  The
routines for packing things back into the tty driver have been removed since
the editor does that stuff itself.

2. The makefile has been changed to make tcsh (not linked to csh) and put it
in /usr/bin.  /u0/osu is the local changes directory.  Our man(1) command
searches multiple paths, that's why tcsh.1 gets put there.  The make copy
command is used to save a working set of sources in our local directory.

3. I *think* that I have documented all the changes to tcsh in the manual
pages.

4. The editor's handling of up/down the history list isn't perfect, because
the orignal shells handling wasn't.  *sigh*

----------------------------------------------------------------

If you make any changes or additions, please send them to me so that I can
update my own version.

					Paul W. Placeway
					The Ohio State University IRCC
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul@ohio-state)
					(Phone: (614) 422-0915)
!Funky!Stuff!
echo x - tcsh.1
cat > tcsh.1 << '!Funky!Stuff!'
.TH TCSH 1 Ohio-State
.SH NAME
tcsh \- C shell with file name completion and command line editing
.SH SYNOPSIS
chsh username /bin/tcsh    -- to get it
.SH DESCRIPTION
.I Tcsh
is an enhanced version of the Berkeley UNIX C shell
.I csh (1).
It behaves exactly like the C shell,
except for the added utilities of:
.PP
.in +6
.ti -3
1) Editing of the command line using control characters.
.sp
.ti -3
2) Interactive command, file name and user name completion.
.sp
.ti -3
3) File/Directory/User list in the middle of a typed command.
.sp
.ti -3
4) Lookup of command documentation in the middle of a typed command.
.sp
.ti -3
5) Visual step up/down through the history list.
.sp
.ti -3
6) Automatic logout after long amounts of idle time.
.in -6
.PP
.SH "FILE NAME COMPLETION"
In typing file names as arguments to commands,
it is no longer necessary to type a complete name,
only a unique abbreviation is necessary.
When you type an ESCAPE to
.I tcsh
it will complete the file name for you,
echoing the full name on the terminal.
If the file name prefix you typed matches no file name, the terminal
bell is enunciated.
The file name may be partially completed if the prefix matches several
longer file names.  If this is the case, the name is extended up to
the ambiguous deviation, and the bell is enunciated.
.PP
.I Example
.PP
In the following example, assume the plus character ``+''
is where the user typed the ESCAPE key.
Assume the current directory contained the files:
.sp
.nf
   DSC        bin        cmd       lib        memos
   DSC.NEW    chaosnet   cmtest    mail       netnews
   bench      class      dev       mbox       new
.fi
.sp
The command:
.sp
                > vi ch+
.sp
would cause 
.I tcsh
to complete the command with the name chaosnet.  If instead, the
user had typed:
.sp
                > vi D+
.sp
.I tcsh
would have extended the name to DSC and enunciated the terminal bell, 
indicating partial completion.
.PP
File name completion works equally well when other directories are addressed.
Additionally, 
.I tcsh 
understands the C shell tilde (~) convention for home directories.
Thus,
.sp
                > cd ~speech/data/fr+
.sp
does what one might expect.  This may also be used to expand login names only.
Thus,
.sp
                > cd ~sy+
.sp
does a 
.I cd
to the 
.I synthesis
directory.
.SH "COMMAND NAME RECOGNITION"
Command name recognition and completion
works in the same manner as file name recognition
and completion above.
The current value of the environment variable \fBPATH\fR is used
in searching for the command.
For example
.sp
		> newa+
.sp
might expand to
.sp
		> newaliases
.sp
Also,
.sp
		> new?
.sp
would list all commands (along PATH) that begin with "new".
.SH "FILE/DIRECTORY LIST"
At any point in typing a command, you may request "what files are available".
Thus, when you have typed, perhaps:
.sp
                > cd ~speech/data/fritz/
.sp
you may wish to know what files or subdirectories exist (in ~speech/data/fritz),
without, of course, aborting the command you are typing.
Typing the character Question mark (?), will list the files available.
The files are listed in multicolumn format, sorted column-wise .
Directories and executable files are indicated with a trailing `/' and `*',
respectively.
Once printed, the command is re-echoed for you to complete.
.PP
Additionally, one may want to know which files match a prefix.
If the user had typed:
.sp
                > cd ~speech/data/fr?
.sp
all files and subdirectories whose prefix was
``fr'' would be printed.  Notice that the example before was simply
a degenerate case of this with a null trailing file name. 
(The null string is a prefix of all strings.)
Notice also, that
a trailing slash is required to pass to a new directory for 
both file name completion and listing.
.PP
The degenerate
.sp
                > ~?
.sp
will print a full list of login names on the current system.
.PP
The behavior of the completion can be changed by setting the shell variable
.I recexact.
This makes an exact command be expanded rather than just ringing the bell.
For example, assume the current directory has two subdirectorys
called foo and food, then with
.I recexact
set the following could be done:
.sp
		> cd fo+
.br
to ...
.br
		> cd foo+
.br
to ...
.br
		> cd foo/
.sp
rather than beeping on the second escape.
.SH EDITING
The shell edits the command line using a (slightly changed) emacs format.
It does so by setting the terminal to `CBREAK' mode and getting a single
character at a time.
The following is a list of which control characters do what.
.PP
.in +6
.ti -4
.sp
^@  (NUL) Prints a help file on the current command (usually a man page).
This help file is found by searching the path list HPATH for files of the
form foo.help, foo.1, foo.8, or foo.6 in that order (assuming that 
the current command is foo).  The file is just printed, not paged in any
way.  This is because ^@ is meant to be used to look up short help files,
not manual pages.
.ti -4
.sp
^A  Moves the cursor to the beginning of the line (after the prompt)
.ti -4
.sp
^B  Moves the cursor back one space.  Beeps if it is already in column one.
.ti -4
.sp
^C  Sends a SIGINTR to the current process (from the tty driver)
.ti -4
.sp
^D  Deletes the character that is over the cursor.
.ti -4
.sp
^E  Moves the cursor to the end of the line.
.ti -4
.sp
^F  Moves the cursor forward one space.  Beeps if at the end of the line.
.ti -4
.sp
^G  Erase entire line and start over.
.ti -4
.sp
^H  (BS) Yanks back the last command typed (ignoring empty lines) the same way
that ^Y does for saved text.  This is useful if you muff a command and want
to correct it.  Its also nice for re-doing the last command.
.ti -4
.sp
^I  (TAB) Inserts a tab character
.ti -4
.sp
^J  (LF) Moves the cursor to the end of the line, inserts a newline
character, and sends the line.
.ti -4
.sp
^K  Delete all characters from the current cursor position to the end
inclusive and save them in a special buffer to be called back later (see ^Y).
.ti -4
.sp
^L  Clear the screen and redisplay the line
.ti -4
.sp
^M  (RETURN) Same as ^J
.ti -4
.sp
^N  Move to the next following word.
A word is a contigous string of one of the characters A-Z, a-z, 0-9, *, ?,
\-, ., or _.
.ti -4
.sp
^O  Flush tty output until the next tty input request (tty driver).
.ti -4
.sp
^P  Same as ^N but go backwards. I.e. previous word.
.ti -4
.sp
^Q  Restart output. (see ^S)
.ti -4
.sp
^R  Redisplay the current line and move the cursor to it's original place.
.ti -4
.sp
^S  Stop the tty's output.
.ti -4
.sp
^T  Transpose previous two characters.
.ti -4
.sp
^U  Same as ^K but kill to the beginning of the line exclusive.
.ti -4
.sp
^V  Insert the next character regardless of it's meaning (quote it).
.ti -4
.sp
^W  Delete the previous word.
.ti -4
.sp
^X  Delete the next word (see ^W).
.ti -4
.sp
^Y  Yank the characters saved (by ^K or ^U) as if they were typed on the
keyboard.
.ti -4
.sp
^Z  Send a SIGTSTP (stop) to the current process (tty driver).
.ti -4
.sp
^[  (ESC) Try to complete the typed in command or file name (see ?).
.ti -4
.sp
^\\  send a SIGQUIT (quit and dump core) to the current process (tty driver)
.ti -4
.sp
^]  Send a SIGTSTP when the program attempts to read the ^]. (tty driver)
This is usually immediately because of cbreak mode, but the assignment is left
so that cooked mode uses it.
.ti -4
.sp
^_  Put an end of file mark at the end and send the line. (tty driver)  This
either exits a pushed shell or gives you a warning about how to log out.
.ti -4
.sp
?   (question mark) Show the possible choices for completing commands.
It does this by
putting a ? at the end of the line and sending it without echoing a
newline.  The completer then does it's stuff and sends the line back.
.ti -4
.sp
^?  (DELETE) Delete the character immediately to the left of the cursor.
Beeps if the cursor is at the beginning of the line.
.ti -4
.sp
ALL OTHERS  All other characters insert themselves into the line.
.in -6
.PP
There is a new shell command, 
.B bind,
that allows the user to redefine what any key does.
If given two arguments 
.B bind
binds the function (first argument) to the given key (second argument).
the key may be either the direct character or a carret-<letter> combination,
which is converted to control-<letter>.
If given one argument
.B bind
tells you what that key does.
If you give bind the lone argument of 'defaults', it resets each key to its
default value (see the above list).
The folowing are legal functions:
.PP
.in +6
.nf
function                default key
--------                -----------
auto_insert             all printables
backward_char           ^B
backward_word           ^P
beginning_of_line       ^A
clear_screen            ^L
delete_char_backward    ^?
delete_char_forward     ^D
delete_word_backward    ^W
delete_word_forward     ^X
end_of_line             ^E
send_eof                ^_
forward_char            ^F
forward_word            ^N
helpme                  ^@
kill_to_beginning       ^U
kill_to_end             ^K
nl_send_line            ^J, ^M
quote_next              ^V
redisplay               ^R
complete                ^[
list_options            ?
start_over              ^G
transpose_chars         ^T
yank_killbuffer         ^Y
yank_last_input         ^H
tty_dsusp               ^]
tty_flush_output        ^O
tty_sigintr             ^C
tty_sigquit             ^\\
tty_sigtsusp            ^Z
tty_start_output        ^Q
tty_stop_output         ^S
.in -6
.fi
.PP
The folowing functions are also provided, but not bound by default.
The history functions step up and down through the history list by placing
the next or previous line in the editor line.  The contents of the current
line are saved, but editing changes do not effect any commands that are up
the list.
These functions are normaly bound to ^P and ^N (previous and next).
.PP
.in +6
.nf
function
--------
down_history
up_history
.in -6
.fi
.PP
If the bind command is given no arguments, all the current bindings are
listed.
.SH "OTHER THINGS"
There is also a new shell variable called
.I shlvl
Which is equal to the number of shells which the user has pushed (where one
is the login shell).
This is done by maintaining the environment variable
.I SHLVL
and incrementing it whenever a new shell is started.
.PP
The automatic logout time is controled by the variable
.I autologout
who's value is the number of minutes of inactivity will be allowed before
automaticly loging the user out.  When that many minutes have been reached,
the shell prints "autologout" and dies (without executing ~/.logout).
.SH FYI
This shell uses cbreak mode but takes typed-ahead characters anyway.
You can still use
.I stty(1)
to set some of the modes of your terminal (but not bindings).
.PP
This shell will restore your tty to a sane mode if it appears to
return from some command in raw, cbreak, or noecho mode.
.SH FILES
~/.tcshrc -- TC-shell startup file
.SH ENVORONMENT
HPATH -- path to look for command documentation
.br
SHLVL -- the number of shell levels pushed (handled be the shell)
.br
TERM -- used to tell how to handle the terminal
.SH "SHELL VARIABLES"
shlvl, term -- see above
.br
recexact -- recognize exact matches even if they are ambigous
.SH SEE ALSO
csh (1), chsh (1)
.SH BUGS
Currently it has many bugs.
.PP
Startup of the shell can be very slow.
.PP
The editor will occasionally get confused as to what it thinks is on the
screen if a tab character is deleted in the middle of a line.
.PP
Any other bugs should be sent to Paul Placeway (osu-dbs!paul or
paul@ohio-state).
.SH AUTHOR
Paul Placeway, OSU IRCC wrote the editor and is supporting tcsh at OSU.
.sp
Ken Greer, HP Labs wrote the command completion.
.sp
Mike Ellis, Fairchild, added command name recognition/completion.
!Funky!Stuff!
echo x - makefile
cat > makefile << '!Funky!Stuff!'
#
#	makefile	4.1	10/9/80
#
# C Shell with process control; VM/UNIX VAX Makefile
# Bill Joy UC Berkeley; Jim Kulp IIASA, Austria
#
CFLAGS=	-O -DTELL -DVMUNIX -Ddebug -DVFORK -I
XSTR=	/usr/ucb/xstr
ED=	-ed
AS=	-as
RM=	-rm
CXREF=	/usr/ucb/cxref
CTAGS=	/usr/ucb/ctags
LIBES=	-ljobs -ltermcap
DESTDIR= /u1/paul
SRCDEST= /u1/paul/src/tcsh/COPY
DESTMAN= /u1/paul/man/man1
# strings.o must be last in OBJS since it can change when
# previous files compile
OBJS=	sh.o sh.dol.o sh.err.o sh.exec.o sh.exp.o sh.func.o sh.glob.o \
	sh.hist.o sh.lex.o sh.misc.o sh.parse.o sh.print.o sh.sem.o sh.set.o \
	sh.proc.o sh.dir.o sh.time.o alloc.o sh.init.o printf.o \
	tw.help.o tw.main.o tw.init.o tw.parse.o sh.nfunc.o dir14.o inputl.o \
	strings.o doprnt.o

# for use in copying a good version to a safe place

SRCS=	sh.c sh.dol.c sh.err.c sh.exec.c sh.exp.c sh.func.c sh.glob.c \
	sh.hist.c sh.lex.c sh.misc.c sh.parse.c sh.print.c sh.sem.c sh.set.c \
	sh.proc.c sh.dir.c sh.time.c alloc.c sh.init.c printf.c sh.nfunc.c \
	dir14.c doprnt.c dir.h sh.dir.h sh.h sh.local.h inputl.c \
	sh.proc.h makefile READ_THREE READ_TOO TODO TCSH.README inputl.h \
	tcsh.1 tw.help.c tw.main.c tw.init.c tw.parse.c tw.h

# Special massaging of C files for sharing of strings
.c.o:
	${CC} -E ${CFLAGS} $*.c | ${XSTR} -c -
	${CC} -c ${CFLAGS} x.c 
	mv x.o $*.o

tcsh: ${OBJS} sh.local.h
	rm -f tcsh
	cc ${OBJS} -o tcsh ${LIBES}

csh.prof: ${OBJS} sh.prof.o sh.local.h mcrt0.o
	rm -f csh.prof
	ld -X mcrt0.o ${OBJS} -o csh.prof ${LIBES} -lc

sh.o.prof:
	cp sh.c sh.prof.c
	cc -c ${CFLAGS} -DPROF sh.prof.c

.DEFAULT:
	${SCCS} get $<

# need an old doprnt, whose output we can trap
doprnt.o: doprnt.c
	cc -E doprnt.c > doprnt.s
	as -o doprnt.o doprnt.s
	rm -f doprnt.s

# strings.o and sh.init.o are specially processed to be shared
strings.o: strings
	${XSTR}
	${CC} -c -R xs.c
	mv xs.o strings.o

sh.init.o:
	${CC} -E ${CFLAGS} sh.init.c | ${XSTR} -c -
	${CC} ${CFLAGS} -c -R x.c
	mv x.o sh.init.o
	
lint:
	lint ${CFLAGS} sh*.c inputl.c tw*.c

print:
	@pr READ_ME
	@pr makefile makefile.*
	@(size -l a.out; size *.o) | pr -h SIZES
	@${CXREF} sh*.c | pr -h XREF
	@ls -l | pr 
	@pr sh*.h [a-rt-z]*.h sh*.c alloc.c

vprint:
	@pr -l84 READ_ME TODO
	@pr -l84 makefile makefile.*
	@(size -l a.out; size *.o) | pr -l84 -h SIZES
	@${CXREF} sh*.c | pr -l84 -h XREF
	@ls -l | pr -l84
	@${CXREF} sh*.c | pr -l84 -h XREF
	@pr -l84 sh*.h [a-rt-z]*.h sh*.c alloc.c

vgrind:
	@cp /dev/null index
	@for i in *.h; do vgrind -t -h "C Shell" $$i >/crp/bill/csh/$$i.t; done
	@for i in *.c; do vgrind -t -h "C Shell" $$i >/crp/bill/csh/$$i.t; done
	@vgrind -t -x -h Index index >/crp/bill/csh/index.t

install: tcsh sh.local.h
	rm -f ${DESTDIR}/bin/tcsh
	cp tcsh ${DESTDIR}/bin/tcsh
#	cp tcsh.1 ${DESTMAN}

clean:
	${RM} -f a.out strings x.c xs.c tcsh
	${RM} -f *.o sh.prof.c

copy: ${SRCS}
	cp ${SRCS} ${SRCDEST}

tags:	/tmp
	${CTAGS} sh*.c
!Funky!Stuff!

paul@osu-dbs.UUCP (Paul Placeway) (04/20/84)

(I'm trying again for everyone who missed some of it)

The following code is my changes to Ken Greer's tcsh.  I have added a visual
mini-editor to the shell, and cleaned up the expansion routines some what.
note that this is the 4.1 version.  When we get 4.2 up I'll repost the new
changes.

Please send any changes back to me so that I can update our version.

Note: this is part 4 of 7, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul@ohio-state)

================ cut here ================
: This is a shar archive.  Extract with sh, not csh.
echo x - tw.h
cat > tw.h << '!Funky!Stuff!'
#ifdef MAKE_TWENEX

#include <sys/types.h>
#include <sys/stat.h>
#include <sgtty.h>
#include "dir.h"
#include <signal.h>
#include <pwd.h>

/*
 * Input lines are parsed into doubly linked circular
 * lists of words of the following form.
 */
struct	wordent {
	char	*word;
	struct	wordent *prev;
	struct	wordent *next;
};

/*
 * History list
 *
 * Each history list entry contains an embedded wordlist
 * from the scanner, a number for the event, and a reference count
 * to aid in discarding old entries.
 *
 * Essentially "invisible" entries are put on the history list
 * when history substitution includes modifiers, and thrown away
 * at the next discarding since their event numbers are very negative.
 */
struct	Hist {
	struct	wordent Hlex;
	int	Hnum;
	int	Href;
	struct	Hist *Hnext;
} Histlist;

/* Don't include stdio.h!  Csh doesn't like it!! */

extern short SHIN, SHOUT;

#define FREE_ITEMS(items)\
{\
    sighold (SIGINT);\
    free_items (items);\
    items = NULL;\
    sigrelse (SIGINT);\
}

#define FREE_DIR(fd)\
{\
    sighold (SIGINT);\
    closedir (fd);\
    fd = NULL;\
    sigrelse (SIGINT);\
}

#define TRUE		1
#define FALSE		0
#define ON		1
#define OFF		0
#define FILSIZ		512		/* Max reasonable file name length */
#define ESC		'\033'
#define equal(a, b)	(strcmp(a, b) == 0)

#define is_set(var)	adrof(var)

#define BUILTINS	"/usr/local/lib/builtins/" /* fake builtin bin */
#define MAXITEMS 2048
#define SEARCHLIST "HPATH"	/* Env. param for helpfile searchlist */
#define DEFAULTLIST ":/u0/osu/man/cat1:/u0/osu/man/cat8:/u0/osu/man/cat6:/usr/man/cat1:/usr/man/cat8:/usr/man/cat6:/u0/local/man/cat1:/u0/local/man/cat8:/u0/local/man/cat6"    /* if no HPATH */

extern char  PromptBuf[];

extern char *getenv ();

typedef enum {LIST, RECOGNIZE, PRINT_HELP} COMMAND;


#define	beep()	write (SHOUT, BELL, 1)

char *getentry();
char GetAChar();
char *tilde();
char filetype();

#ifndef DONT_EXTERN

extern char *BELL;
extern char *command_list[];  /* the pre-digested list of commands
					 for speed and general usefullness */
extern int numcommands;

extern int commands_file;	/* for a global file discriptor */

extern int  key_bindings[];	/* the currently assigned keys */
extern int  bindings_are_inited;	/* for setup of bindings */
						/* the first time */

extern int  dirctr;		/* -1 0 1 2 ... */
extern char dirflag[5];		/*  ' nn\0' - dir #s -  . 1 2 ... */


#endif
#endif

#define	ERROR			-1
#define	NO_OP			0
#define	UP_HIST			1
#define DOWN_HIST		2
#define	COMPLETE		3
#define	HELPME			4
#define	LIST_CHOICES		5
!Funky!Stuff!
echo x - tw.help.c
cat > tw.help.c << '!Funky!Stuff!'
#define MAKE_TWENEX		/* flag to include definitions */
#include "tw.h"


/* actually look up and print documentation on a file.  Look down the path
   for an approiate file, then print it.  Note that the printing is NOT 
   PAGED.  This is because the function is NOT ment to look at manual pages,
   it only does so if there is no .help file to look in. */

do_help (command)
char *command;
{
    char name[FILSIZ + 1];
    char *cmd_p;
    int f;
    char curdir[128];		/* Current directory being looked at */
    char *getenv();
    register char *hpath;	/* The environment parameter */
    char *skipslist();
    char full[128], buf[512];	/* full path name and buffer for read */
    int len;			/* length of read buffer */

				/* copy the string to a safe place */
    copyn (name, command, sizeof (name));

			/* trim off the garbage that may be at the end */
    for (cmd_p = name; *cmd_p != '\0'; cmd_p++) {
	if (*cmd_p == ' ' || *cmd_p == '\t') *cmd_p = '\0';
    }

				/* if nothing left, return */
    if (*name == '\0') return;

		/* got is, now "cat" the file based on the path $HPATH */

    hpath = getenv(SEARCHLIST);
    if(hpath == (char *)0)
        hpath = DEFAULTLIST;

    while (1)
    {
	if(!*hpath)
	{
	    printf("No help file for %s\n", name);
	    break;
	}
	nextslist(hpath, curdir);
	hpath = skipslist(hpath);

/* now make the full path name - try first /bar/foo.help, then /bar/foo.1,
   /bar/foo.8, then finally /bar/foo.6 .  This is so that you don't spit
   a binary at the tty when $HPATH == $PATH.  I know, I know, gotos are
   BOGUS */

	copyn (full, curdir, sizeof (full));
	catn (full, "/", sizeof (full));
	catn (full, name, sizeof (full));
	catn (full, ".help", sizeof (full));
	f = open (full, 0);	/* try to open for reading */
	if (f != -1) goto cat_it;	/* no file there */

	copyn (full, curdir, sizeof (full));
	catn (full, "/", sizeof (full));
	catn (full, name, sizeof (full));
	catn (full, ".1", sizeof (full));
	f = open (full, 0);	/* try to open for reading */
	if (f != -1) goto cat_it;	/* no file there */

	copyn (full, curdir, sizeof (full));
	catn (full, "/", sizeof (full));
	catn (full, name, sizeof (full));
	catn (full, ".8", sizeof (full));
	f = open (full, 0);	/* try to open for reading */
	if (f != -1) goto cat_it;	/* no file there */

	copyn (full, curdir, sizeof (full));
	catn (full, "/", sizeof (full));
	catn (full, name, sizeof (full));
	catn (full, ".6", sizeof (full));
	f = open (full, 0);	/* try to open for reading */
	if (f == -1) continue;	/* no file there */

cat_it:
	while ((len = read (f, buf, 512)) != 0) {	/* the file is here */
	    write (SHOUT, buf, len);			/* so cat it to the */
	}						/* terminal */
	break;
    }
}

/* these next two are stolen from CMU's man(1) command for looking down 
 * paths.  they are prety straight forward. */

/*
 * nextslist takes a search list and copies the next path in it
 * to np.  A null search list entry is expanded to ".".
 * If there are no entries in the search list, then np will point
 * to a null string.
 */

nextslist(sl, np)
  register char *sl;
  register char *np;
{
    if(!*sl)
	*np = '\000';
    else if(*sl == ':')
    {
	*np++ = '.';
	*np = '\000';
    }
    else
    {
	while(*sl && *sl != ':')
	    *np++ = *sl++;
	*np = '\000';
    }
}

/*
 * skipslist returns the pointer to the next entry in the search list.
 */

char *
skipslist(sl)
  register char *sl;
{
    while(*sl && *sl++ != ':');
    return(sl);
}

!Funky!Stuff!
echo x - tw.init.c
cat > tw.init.c << '!Funky!Stuff!'
#define MAKE_TWENEX		/* flag to include definitions */
#include "tw.h"

/*
 * Build the command name list (and print the results).  This is a HACK until
 * I can get the rehash command to include its results in the command list.
 */

char *extract_dir_from_path();
int fcompare();

static int tw_been_inited = 0;
			/* to avoid double reading of commands file */

extern struct	biltins {
	char	*bname;
	int	(*bfunct)();
	short	minargs, maxargs;
} bfunc[];

extern struct	varent {
	char	**vec;		/* Array of words which is the value */
	char	*name;		/* Name of variable/alias */
	struct	varent *link;
} aliases;

tw_clear_comm_list() {
    register int i;

/*     printf ("Building the completion path...");
    flush(); */

    if (numcommands != 0) {
/* 	printf("Freeing..."); */
        for (i = 0; command_list[i]; i++) {
	    if (command_list[i] == 0) {
		printf ("tried to free a null, i = %d\n", i);
	    } else {
		free (command_list[i]);
	    }
	    command_list[i] = NULL;
	}
	numcommands = 0;
    }
}

tw_sort_comms () {
    register int i,j;

/*     printf ("sorting...");
    flush(); */
    
/* sort the list. */
    qsort (command_list, numcommands, sizeof (command_list[0]), fcompare);

/* get rid of multiple entries */
    for (i = 0; i < numcommands - 1; i++) {
	if (strcmp (command_list[i], command_list[i+1]) == 0) {
	    if (command_list[i] == 0) {
		printf ("tried to free a null, i = %d, previous = %s\n", i,
		    command_list [i-1]);
	    } else {
		free (command_list[i]);
	    }
	    for (j = i; j < numcommands; j++) {
		command_list[j] = command_list[j+1];
	    }
	    numcommands--;	/* keep count right */
	    i--;		/* to catch many versions */
	}
    }

/*     print_by_column (looking_for_lognames ? NULL:tilded_dir, command_list,
  			 numcommands, 1);	 */

/*     printf ("Done.\n"); */
    flush();
}

tw_add_comm_name (name)
char *name;
{
    register int length;

    if (numcommands >= MAXITEMS) {
        printf ("\nYikes!! Too many commands in PATH!!\n");
        return;
    }

    length = strlen(name) + 1;

    if ((command_list[numcommands] = (char *)malloc (length)) == NULL) {
        printf ("\nYikes!! I ran out of memory!!!\n");
        return;
    }

    copyn (command_list[numcommands], name, MAXNAMLEN);
    numcommands++;
}

tw_add_builtins() {
    register char *cp;
    register struct biltins *bptr;

    for (bptr = bfunc; cp = bptr->bname; bptr++) {
	tw_add_comm_name (cp);
    }
}

tw_add_aliases ()
{
    register struct varent *vp;

    vp = &aliases;

    for (vp = vp->link; vp != 0; vp = vp->link) {
	tw_add_comm_name(vp->name);
    }

}
!Funky!Stuff!
echo x - tw.main.c
cat > tw.main.c << '!Funky!Stuff!'
/* tw.c -- input parser main routines.  84/04/02 Paul Placeway */

/* 
 * This is a re-write of the routines by Ken Greer, Mike Ellis, and myself
 * for doing twenex (Tops-20 (tm)) style command and file recognition,
 * expansion, and listing, as well as other nice additions to my (inputl)
 * input line parser for C-shell and other things.  This is styled after the
 * Tops-20 COMND jsys, but somewhat changed to what I think it should be in
 * Unix.
 */

#define MAKE_TWENEX		/* flag to include definitions */
#define DONT_EXTERN		/* don't do the extern ... stuff */
#include "tw.h"


static char
    *BELL = "\07";

char *command_list[2048] = NULL;  /* the pre-digested list of commands
					 for speed and general usefullness */
int numcommands = 0;

int commands_file = -1;	/* for a global file discriptor */

int  key_bindings[256];	/* the currently assigned keys */
int  bindings_are_inited = FALSE;	/* for setup of bindings */

int  dirctr;		/* -1 0 1 2 ... */
char dirflag[5];		/*  ' nn\0' - dir #s -  . 1 2 ... */

/* the main routine -- the inputline and size are the location to place the
 * expanded line into. */

twenex (inputline, inputline_size)
char   *inputline;		/* pointer to a buffer */
int     inputline_size;		/* size of the buffer */
{
    register int numitems, num_read;
    int hist_num = 0;
    int hnumcntr;
    char linebuf[512];
    struct Hist *hp;

    if (bindings_are_inited = FALSE) tw_init_bindings();

    while (1) {
	register char *str_end, last_char, should_retype;
	char inputl();
	COMMAND command;

	last_char = inputl (PromptBuf, inputline, inputline_size);
						/* PWP: use the editor */
	num_read = strlen(inputline);

	if (last_char == -1)		/* PWP: for end-of-file */
	    break;			/* did i hear you screem HACK? */

	last_char &= 0177;	/* keep it to 7 bits */

	if (last_char == '\n' || num_read == inputline_size)
	    break;		/* send the line */

	switch (key_bindings[last_char]) {
	    case UP_HIST:	/* ^P -- previous hist line */
	        if (hist_num == 0)
		    copyn(linebuf, inputline, 511);

	        hp = Histlist.Hnext;
	        if (hp == 0) {
		    beep();
		    ilpushback (inputline);
		    break;
	        }
	        hist_num++;
	        for (hnumcntr = 1; hnumcntr < hist_num; hnumcntr++) {
		    if ((hp->Hnext) == 0) {
		        beep();
		        hist_num--;
		        break;
		    }
		    hp = hp -> Hnext;
	        }

	        sprlex (inputline, &hp->Hlex);
	        if (inputline[strlen(inputline)-1] == '\n')
		    inputline[strlen(inputline)-1] = '\0';

	        printprompt ();
	        ilpushback (inputline);
	        ilredisplay ();
	        break;

	    case DOWN_HIST:	/* ^N -- next hist line */
	        if (hist_num == 0) {
		    ilpushback (inputline);
		    beep();
		    break;
	        }
	        if (hist_num == 1) {
		    copyn(inputline, linebuf, inputline_size);
		    hist_num--;
		    printprompt ();
		    ilpushback (inputline);
		    ilredisplay();
		    break;
	        }

	        hp = Histlist.Hnext;
	        hist_num--;
	        for (hnumcntr = 1; hnumcntr < hist_num; hnumcntr++) {
		    if ((hp->Hnext) == 0) break;
		    hp = hp -> Hnext;
	        }

	        sprlex (inputline, &hp->Hlex);
	        if (inputline[strlen(inputline)-1] == '\n')
		    inputline[strlen(inputline)-1] = '\0';

	        printprompt ();
	        ilpushback (inputline);
	        ilredisplay ();
	        break;

	    case COMPLETE:		/* RECOGNIZE */
		do_recognize (inputline, inputline_size, num_read);
		break;

	    case HELPME:	/* help */
		print_help (inputline, inputline_size, num_read);
		break;

	    case LIST_CHOICES:	/* LIST */
		list_choices (inputline, inputline_size, num_read);
		break;

	    case NO_OP:		/* just push it back */
		ilpushback (inputline);
		break;

	    case ERROR:
	    default:
		beep();		/* complain */
		ilpushback (inputline);
		break;

	}			/* end case */
    }				/* end while (1) */

    return (num_read);
}


/* handle the keybindings stuff like initing them, binding them, and seeing
   what is bound to a given key. */


/* bind function func to key key */

int
tw_bind_to_key (func, key)
int func;
char key;
{
    if (key == -1) {		/* trap unsetings */
	return (-1);
    }

    if ((func < 0) || (func > 10)) {
	return(-1);
    }

    if (bindings_are_inited = FALSE) tw_init_bindings();

    key_bindings[key] = func;
}

/* return what function is bound to key key */

int
tw_get_binding(key)
char key;
{
    return (key_bindings[key]);
}

/* initilize the bindings */

tw_init_bindings()
{
    register int i;

    if (bindings_are_inited) return;

    for (i=0; i<128; i++) {	/* assign all the keys */
	key_bindings[i] = NO_OP;
    }
    key_bindings[020] = NO_OP;		/* ^P */
    key_bindings[016] = NO_OP;		/* ^N */
    key_bindings[033] = COMPLETE;	/* ^[ (ESC) */
    key_bindings[000] = HELPME;		/* ^@ */
    key_bindings[077] = LIST_CHOICES;	/* ? */

    bindings_are_inited = TRUE;
}


/* the top level of the recognize and list procedures.  These are just to
   save a little work on the part of twenex(). */

/* do the recognize: take the inputline, expand the command name, if there is
   a white space character, hand the stuff to the parse expander */

do_recognize (inputline, inputline_size, num_read)
char *inputline;
int inputline_size;
int num_read;
{
/*  CharPtr = &inputline[num_read]; */
				/* do it!! */
    if (tenematch (inputline, inputline_size, num_read, RECOGNIZE) != 1)
	 		/* Beep = No match/ambiguous */
	beep ();

    flush ();
    ilpushback (inputline);		/* pushback for editor */
}

/* run the list choices option:  Same as above, but use a parse list options
   routine rather than an expander.  Nothing additional is pushed back onto
   the line (just what was typed in). */

list_choices (inputline, inputline_size, num_read)
char *inputline;
int inputline_size;
int num_read;
{
    putchar ('\n');
/*  CharPtr = &inputline[num_read]; */

			/* do this early so that you can ^C it */
    printprompt ();
    ilpushback (inputline);		/* pushback for editor */
    ilredisplay ();

    tenematch (inputline, inputline_size, num_read, LIST);
				/* do it!! */
    flush ();
}

/* print help:  Figure out what command is being entered, expand the name,
   then do a lookup on documentation of this command */

print_help (inputline, inputline_size, num_read)
char *inputline;
int inputline_size;
int num_read;
{
    putchar ('\n');
/*  CharPtr = &inputline[num_read]; */

			/* do this early so that you can ^C it */
    printprompt ();
    ilpushback (inputline);		/* pushback for editor */
    ilredisplay ();

    tenematch (inputline, inputline_size, num_read, PRINT_HELP);
				/* do it!! */
    flush ();
}



/*
 * Concatonate src onto tail of des.
 * Des is a string whose maximum length is count.
 * Always null terminate.
 */

catn (des, src, count)
register char *des, *src;
register count;
{
    while (--count >= 0 && *des)
	des++;
    while (--count >= 0)
	if ((*des++ = *src++) == 0)
	    return;
    *des = '\0';
}

max (a, b)
{
    if (a > b)
	return (a);
    return (b);
}

/*
 * like strncpy but always leave room for trailing \0
 * and always null terminate.
 */
copyn (des, src, count)
register char *des, *src;
register count;
{
    while (--count >= 0)
	if ((*des++ = *src++) == 0)
	    return;
    *des = '\0';
}
!Funky!Stuff!

paul@osu-dbs.UUCP (Paul Placeway) (04/20/84)

(I'm trying again for everyone who missed some of it)

The following code is my changes to Ken Greer's tcsh.  I have added a visual
mini-editor to the shell, and cleaned up the expansion routines some what.
note that this is the 4.1 version.  When we get 4.2 up I'll repost the new
changes.

Please send any changes back to me so that I can update our version.

Note: this is part 5 of 7, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul@ohio-state)

================ cut here ================
: This is a shar archive.  Extract with sh, not csh.
echo x - dir.h
cat > dir.h << '!Funky!Stuff!'
/* Copyright (c) 1982 Regents of the University of California */

/* @(#)ndir.h 4.4 3/30/82 */

/*
 * This sets the "page size" for directories.
 * Requirements are DEV_BSIZE <= DIRBLKSIZ <= MINBSIZE with
 * DIRBLKSIZ a power of two.
 * Dennis Ritchie feels that directory pages should be atomic
 * operations to the disk, so we use DEV_BSIZE.
 */
#define DIRBLKSIZ 512

/*
 * This limits the directory name length. Its main constraint
 * is that it appears twice in the user structure. (u. area)
 */
#define MAXNAMLEN 255

struct	direct {
	u_long	d_ino;
	short	d_reclen;
	short	d_namlen;
	char	d_name[MAXNAMLEN + 1];
	/* typically shorter */
};

struct _dirdesc {
	int	dd_fd;
	long	dd_loc;
	long	dd_size;
	char	dd_buf[DIRBLKSIZ];
};

/*
 * useful macros.
 */
#undef DIRSIZ
#define DIRSIZ(dp) \
    ((sizeof(struct direct) - MAXNAMLEN + (dp)->d_namlen + sizeof(ino_t) - 1) &\
    ~(sizeof(ino_t) - 1))
typedef	struct _dirdesc DIR;
#ifndef	NULL
#define	NULL	0
#endif

/*
 * functions defined on directories
 */
extern DIR *opendir();
extern struct direct *readdir();
extern long telldir();
extern void seekdir();
#define rewinddir(dirp)	seekdir((dirp), 0)
extern void closedir();
!Funky!Stuff!
echo x - dir14.c
cat > dir14.c << '!Funky!Stuff!'
/* Copyright (c) 1982 Regents of the University of California */

/* @(#)opendir.c 4.2.1.1 7/1/82 - HPL */

#include <sys/types.h>
#include <sys/stat.h>
#include <dir.h>

/*
 * open a directory.
 */
DIR *
opendir(name)
	char *name;
{
	register DIR *dirp;
	register int fd;
	struct stat sbuf;

	if ((fd = open(name, 0)) == -1)
		return NULL;
	fstat(fd, &sbuf);
	if (((sbuf.st_mode & S_IFDIR) == 0) ||
	    ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL)) {
		close (fd);
		return NULL;
	}
	dirp->dd_fd = fd;
	dirp->dd_loc = 0;
	return dirp;
}
/* Copyright (c) 1982 Regents of the University of California */

/* @(#)closedir.c 4.2 3/10/82 */

/*
 * close a directory.
 */
void
closedir(dirp)
	register DIR *dirp;
{
	close(dirp->dd_fd);
	dirp->dd_fd = -1;
	dirp->dd_loc = 0;
	free(dirp);
}
/* Copyright (c) 1982 Regents of the University of California */

/* @(#)readdir.c 4.2 3/12/82 */

/*
 * read an old stlye directory entry and present it as a new one
 */
#define	ODIRSIZ	14

struct	olddirect {
	ino_t	d_ino;
	char	d_name[ODIRSIZ];
};

/*
 * get next entry in a directory.
 */
struct direct *
readdir(dirp)
	register DIR *dirp;
{
	register struct olddirect *dp;
	static struct direct dir;

	for (;;) {
		if (dirp->dd_loc == 0) {
			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 
			    DIRBLKSIZ);
			if (dirp->dd_size <= 0)
				return NULL;
		}
		if (dirp->dd_loc >= dirp->dd_size) {
			dirp->dd_loc = 0;
			continue;
		}
		dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
		dirp->dd_loc += sizeof(struct olddirect);
		if (dp->d_ino == 0)
			continue;
		dir.d_ino = dp->d_ino;
		strncpy(dir.d_name, dp->d_name, ODIRSIZ);
		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
		dir.d_namlen = strlen(dir.d_name);
		dir.d_reclen = DIRSIZ(&dir);
		return (&dir);
	}
}
!Funky!Stuff!
echo x - sh.nfunc.c
cat > sh.nfunc.c << '!Funky!Stuff!'
/* static	char *sccsid = "@(#)sh.nfunc.c 1.0 84/01/31"; */

#include "sh.h"
#include "inputl.h"			/* for the function names */
#include "tw.h"

#include <sys/ioctl.h>

static char *seech ();

/*
 * Tops-C shell
 */

/* do a bind command -- 'bind FUNC key' */
/* list structure... */
struct kfuncs {
    char *name;
    int ifunc;			/* inputl function name */
    int tfunc;			/* twenex function name */
} fnames[] = {
    "auto_insert",		AUTO_INSERT,		NO_OP,
    "AUTO_INSERT",		AUTO_INSERT,		NO_OP,
    "backward_char",		BACKWARD_CHAR,		NO_OP,
    "BACKWARD_CHAR",		BACKWARD_CHAR,		NO_OP,
    "backward_word",		BACKWARD_WORD,		NO_OP,
    "BACKWARD_WORD",		BACKWARD_WORD,		NO_OP,
    "beginning_of_line",	BEGINNING_OF_LINE,	NO_OP,
    "BEGINNING_OF_LINE",	BEGINNING_OF_LINE,	NO_OP,
    "clear_screen",		CLEAR_SCREEN,		NO_OP,
    "CLEAR_SCREEN",		CLEAR_SCREEN,		NO_OP,
    "complete",			SEND_LINE,		COMPLETE,
    "COMPLETE",			SEND_LINE,		COMPLETE,
    "delete_char_backward",	DELETE_CHAR_BACKWARD,	NO_OP,
    "DELETE_CHAR_BACKWARD",	DELETE_CHAR_BACKWARD,	NO_OP,
    "delete_char_forward",	DELETE_CHAR_FORWARD,	NO_OP,
    "DELETE_CHAR_FORWARD",	DELETE_CHAR_FORWARD,	NO_OP,
    "delete_word_backward",	DELETE_WORD_BACKWARD,	NO_OP,
    "DELETE_WORD_BACKWARD",	DELETE_WORD_BACKWARD,	NO_OP,
    "delete_word_forward",	DELETE_WORD_FORWARD,	NO_OP,
    "DELETE_WORD_FORWARD",	DELETE_WORD_FORWARD,	NO_OP,
    "down_history",		SEND_LINE,		DOWN_HIST,
    "DOWN_HISTORY",		SEND_LINE,		DOWN_HIST,
    "end_of_line",		END_OF_LINE,		NO_OP,
    "END_OF_LINE",		END_OF_LINE,		NO_OP,
    "send_eof",			SEND_EOF,		NO_OP,
    "SEND_EOF",			SEND_EOF,		NO_OP,
    "forward_char",		FORWARD_CHAR,		NO_OP,
    "FORWARD_CHAR",		FORWARD_CHAR,		NO_OP,
    "forward_word",		FORWARD_WORD,		NO_OP,
    "FORWARD_WORD",		FORWARD_WORD,		NO_OP,
    "kill_to_beginning",	KILL_TO_BEGINNING,	NO_OP,
    "KILL_TO_BEGINNING",	KILL_TO_BEGINNING,	NO_OP,
    "kill_to_end",		KILL_TO_END,		NO_OP,
    "KILL_TO_END",		KILL_TO_END,		NO_OP,
    "nl_send_line",		NL_SEND_LINE,		NO_OP,
    "NL_SEND_LINE",		NL_SEND_LINE,		NO_OP,
    "quote_next",		QUOTE_NEXT,		NO_OP,
    "QUOTE_NEXT",		QUOTE_NEXT,		NO_OP,
    "redisplay",		REDISPLAY,		NO_OP,
    "REDISPLAY",		REDISPLAY,		NO_OP,
    "list_options",		SEND_LINE,		LIST_CHOICES,
    "LIST_OPTIONS",		SEND_LINE,		LIST_CHOICES,

    "send_line",		SEND_LINE,		ERROR,
    "SEND_LINE",		SEND_LINE,		ERROR,

    "start_over",		START_OVER,		NO_OP,
    "START_OVER",		START_OVER,		NO_OP,
    "transpose_chars",		TRANSPOSE_CHARS,	NO_OP,
    "TRANSPOSE_CHARS",		TRANSPOSE_CHARS,	NO_OP,
    "up_history",		SEND_LINE,		UP_HIST,
    "UP_HISTORY",		SEND_LINE,		UP_HIST,
    "yank_killbuffer",		YANK_KILLBUFFER,	NO_OP,
    "YANK_KILLBUFFER",		YANK_KILLBUFFER,	NO_OP,
    "yank_last_input",		YANK_LAST_INPUT,	NO_OP,
    "YANK_LAST_INPUT",		YANK_LAST_INPUT,	NO_OP,
    "tty_dsusp",		TTY_DSUSP,		NO_OP,
    "TTY_DSUSP",		TTY_DSUSP,		NO_OP,
    "tty_flush_output",		TTY_FLUSH_OUTPUT,	NO_OP,
    "TTY_FLUSH_OUTPUT",		TTY_FLUSH_OUTPUT,	NO_OP,
    "tty_sigintr",		TTY_SIGINTR,		NO_OP,
    "TTY_SIGINTR",		TTY_SIGINTR,		NO_OP,
    "tty_sigquit",		TTY_SIGQUIT,		NO_OP,
    "TTY_SIGQUIT",		TTY_SIGQUIT,		NO_OP,
    "tty_sigtsusp",		TTY_SIGTSUSP,		NO_OP,
    "TTY_SIGTSUSP",		TTY_SIGTSUSP,		NO_OP,
    "tty_start_output",		TTY_START_OUTPUT,	NO_OP,
    "TTY_START_OUTPUT",		TTY_START_OUTPUT,	NO_OP,
    "tty_stop_output",		TTY_STOP_OUTPUT,	NO_OP,
    "TTY_STOP_OUTPUT",		TTY_STOP_OUTPUT,	NO_OP,
    "helpme",			SEND_LINE,		HELPME,
    "HELPME",			SEND_LINE,		HELPME,
    0,				0,			0
};

int
dobind(v)
register char **v;
{
    register char c;
    register struct kfuncs *fp;
    register int i;
    register int isfound;
				/* assume at this point that i'm given 2 or 3
				   args - 'bind', the f-name, and the key;
				   or 'bind' key to print the func for that
				   key. */
    if (v[1] && v[2]) {
        for (fp = fnames; fp->name; fp++) {
	    if (strcmp(v[1], fp->name) == 0) {
	        if ((v[2][0] == '^') && v[2][1]) {
		    c = (v[2][1] == '?') ? 0177 : v[2][1] & 037;
	        }
	        else {
		    c = v[2][0];
	        }
		tw_bind_to_key (fp->tfunc, c);	/* bind for twenex()... */
	        ilbindtokey (fp->ifunc, c);	/* actually bind the thing */
		ilreset_tty ();
	        return;
	    }
        }
        bferr ("I don't know that function.");
    } else if (v[1]) {
	if (strcmp (v[1], "defaults") == 0) {
		/* reset keys to default */
	    for (i=0; i<128; i++) {	/* assign all the keys */
		ilbindtokey (AUTO_INSERT, i);
		tw_bind_to_key (NO_OP, i);
	    }

	    ilbindtokey (SEND_LINE, 000);
	    tw_bind_to_key (HELPME, 000);

	    ilbindtokey (BEGINNING_OF_LINE, 001);
	    ilbindtokey (BACKWARD_CHAR, 002);
	    ilbindtokey (TTY_SIGINTR, 003);
	    ilbindtokey (DELETE_CHAR_FORWARD, 004);
	    ilbindtokey (END_OF_LINE, 005);
	    ilbindtokey (FORWARD_CHAR, 006);
	    ilbindtokey (START_OVER, 007);
	    ilbindtokey (KILL_TO_END, 013);
	    ilbindtokey (SEND_EOF, 037);
	    ilbindtokey (YANK_LAST_INPUT, 010);
	    ilbindtokey (SEND_LINE, 077);
	    tw_bind_to_key (LIST_CHOICES, 077);
	    ilbindtokey (SEND_LINE, 033);
	    tw_bind_to_key (COMPLETE, 033);
	    ilbindtokey (NL_SEND_LINE, 012);
	    ilbindtokey (NL_SEND_LINE, 015);
	    ilbindtokey (FORWARD_WORD, 016);
	    tw_bind_to_key (NO_OP, 016);
	    ilbindtokey (TTY_FLUSH_OUTPUT, 017);
	    ilbindtokey (BACKWARD_WORD, 020);
	    tw_bind_to_key (NO_OP, 020);
	    ilbindtokey (TTY_START_OUTPUT, 021);
	    ilbindtokey (REDISPLAY, 022);
	    ilbindtokey (TTY_STOP_OUTPUT, 023);
	    ilbindtokey (TRANSPOSE_CHARS, 024);
	    ilbindtokey (KILL_TO_BEGINNING, 025);
	    ilbindtokey (QUOTE_NEXT, 026);
	    ilbindtokey (CLEAR_SCREEN, 014);
	    ilbindtokey (DELETE_WORD_BACKWARD, 027);
	    ilbindtokey (DELETE_WORD_FORWARD, 030);
	    ilbindtokey (YANK_KILLBUFFER, 031);
	    ilbindtokey (TTY_SIGTSUSP, 032);
	    ilbindtokey (TTY_SIGQUIT, 034);
	    ilbindtokey (TTY_DSUSP, 035);
	    ilbindtokey (DELETE_CHAR_BACKWARD, 0177);
	    ilreset_tty();
	}
	else {		/* want to know what this key does */
	    if ((v[1][0] == '^') && v[1][1]) {
	        c = (v[1][1] == '?') ? 0177 : v[1][1] & 037;
	    }
	    else {
	        c = v[1][0];
	    }
            for (fp = fnames; fp->name; fp++) {
	        if ((fp->ifunc == ilgetbinding(c)) && 
		    (fp->tfunc == tw_get_binding (c))) {
		    printf ("%s is bound to %s.\n", seech(c), fp->name);
		    return;
	        }
	    }
	    printf ("%s isn't bound to anything.\n", seech(c));
	    printf ("inputl: %d, twenex: %d\n", ilgetbinding (c),
		tw_get_binding (c));
	}
    }
    else {			/* list all the bindings */
	for (i = 0; i < 128; i++) {
	    isfound = 0;
	    for (fp = fnames; fp->name; fp++) {
	        if ((fp->ifunc == ilgetbinding(i)) && 
		    (fp->tfunc == tw_get_binding (i))) {
		    printf ("%s is bound to %s.\n", seech(i), fp->name);
		    isfound++;
		    break;
	        }
	    }
	    if (isfound) continue;
	    printf ("%s isn't bound to anything.\n", seech(i));
	}
    }
}

char *
sprlex(buf, sp0)
struct wordent *sp0;
char *buf;
{
    register struct wordent *sp = sp0->next;

    buf[0] = '\0';

    for (;;) {
        strcat(buf, sp->word);
        sp = sp->next;
        if (sp == sp0)
            break;
        strcat(buf, " ");
    }
    return(buf);
}

donhist(av)
char **av;
{
    char buf[512];
    struct Hist *hp;

    for (hp = Histlist.Hnext; hp != 0; hp = hp->Hnext) {
        sprlex (buf, &hp->Hlex);
        printf ("hist: %s\n", buf);
    }
}

static char *
seech (c)			/* 'c' -> "c", '^C' -> "^" + "C" */
register char c;
{
    static char tmp[3];
    
    if (c >= ' ') {
	tmp[0] = c;
	tmp[1] = '\0';
	return (tmp);
    }
    else {
	tmp[0] = '^';
	if (c == '\177') {
	    tmp[1] = '?';
	}
	else {
	    tmp[1] = '@' + (c & 037);
	}
	tmp[2] = '\0';
	return (tmp);
    }
}
!Funky!Stuff!

paul@osu-dbs.UUCP (Paul Placeway) (04/20/84)

(I'm trying again for everyone who missed some of it)

The following code is my changes to Ken Greer's tcsh.  I have added a visual
mini-editor to the shell, and cleaned up the expansion routines some what.
note that this is the 4.1 version.  When we get 4.2 up I'll repost the new
changes.

Please send any changes back to me so that I can update our version.

Note: this is part 6 of 7, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul@ohio-state)

================ cut here ================
: This is a shar archive.  Extract with sh, not csh.
echo x - DIFFS2
cat > DIFFS2 << '!Funky!Stuff!'
========	diff -bc csh/sh.func.c tcsh/sh.func.c	========

*** csh/sh.func.c	Fri Apr 13 10:44:03 1984
--- tcsh/sh.func.c	Fri Apr 13 10:45:07 1984
***************
*** 498,504
  		bseek(0l);
  	do {
  		if (intty && fseekp == feobp)
! 			printf("? "), flush();
  		aword[0] = 0, getword(aword);
  		switch (srchx(aword)) {
  

--- 498,504 -----
  		bseek(0l);
  	do {
  		if (intty && fseekp == feobp)
! 			printprompt(); /* printf("? "), flush(); */
  		aword[0] = 0, getword(aword);
  		switch (srchx(aword)) {
  
***************
*** 727,732
  		blkfree(gargv), gargv = 0;
  }
  
  char	**environ;
  
  dosetenv(v)

--- 727,733 -----
  		blkfree(gargv), gargv = 0;
  }
  
+ 
  char	**environ;
  
  dosetenv(v)
***************
*** 1005,1012
  	int old, ldisc;
  	short ctpgrp;
  
! 	if (loginsh)
! 		error("Can't suspend a login shell (yet)");
  	untty();
  	old = sigsys(SIGTSTP, SIG_DFL);
  	kill(0, SIGTSTP);

--- 1006,1063 -----
  	int old, ldisc;
  	short ctpgrp;
  
! 	if (loginsh) {
! 		error ("Use \'detach\' to suspend the login shell.");
! 	}
! 	untty();
! 	old = sigsys(SIGTSTP, SIG_DFL);
! 	kill(0, SIGTSTP);
! 	/* the shell stops here */
! 	sigsys(SIGTSTP, old);
! 	if (tpgrp != -1) {
! retry:
! 		ioctl(FSHTTY, TIOCGPGRP, &ctpgrp);
! 		if (ctpgrp != opgrp) {
! 			old = sigsys(SIGTTIN, SIG_DFL);
! 			kill(0, SIGTTIN);
! 			sigsys(SIGTTIN, old);
! 			goto retry;
! 		}
! 		ioctl(FSHTTY, TIOCSPGRP, &shpgrp);
! 		setpgrp(0, shpgrp);
! 	}
! 	ioctl(FSHTTY, TIOCGETD, &oldisc);
! 	if (oldisc != NTTYDISC) {
! 		printf("Switching to new tty driver...\n");
! 		ldisc = NTTYDISC;
! 		ioctl(FSHTTY, TIOCSETD, &ldisc);
! 	}
! }
! 
! /* dodetach - try to stop the shell and insulate it from the SIGKILL that
!    init will send it by forking and having the PARENT exit(0) */
! 
! dodetach()
! {
! 	int old, ldisc;
! 	short ctpgrp;
! 	int fork_val;
! 
! 	if (!loginsh) {
! 		dosuspend();
! 		return;
! 	}
! 	printf ("Trying to detach, hang on...");
! /* try to clone yourself */
! 	fork_val = fork();
! 
! /* if you did, then die, else spit an error */
! 	if (fork_val == -1) {
! 		error ("Could not make the fork for detaching.");
! 	} else if (fork_val != 0) {
! 		exit (0);	/* parent fork dies at this point, leaving
! 				   the orphan which we now are */
! 	}
  	untty();
  	old = sigsys(SIGTSTP, SIG_DFL);
  	kill(0, SIGTSTP);

========	diff -bc csh/sh.h tcsh/sh.h	========

*** csh/sh.h	Fri Apr 13 10:44:07 1984
--- tcsh/sh.h	Fri Apr 13 10:45:09 1984
***************
*** 5,10
  #include <sys/vtimes.h>
  #endif
  
  /*
   * C shell
   *

--- 5,12 -----
  #include <sys/vtimes.h>
  #endif
  
+ #define OSU
+ 				/* do the ohio state changes */
  /*
   * C shell
   *
***************
*** 73,79
  short	opgrp;			/* Initial pgrp and tty pgrp */
  int	oldisc;			/* Initial line discipline or -1 */
  struct	tms shtimes;		/* shell and child times for process timing */
! 
  /*
   * These are declared here because they want to be
   * initialized in sh.init.c (to allow them to be made readonly)

--- 75,83 -----
  short	opgrp;			/* Initial pgrp and tty pgrp */
  int	oldisc;			/* Initial line discipline or -1 */
  struct	tms shtimes;		/* shell and child times for process timing */
! char PromptBuf[256];		/* buffer for the actual printed prompt.
! 				   this is used in tenex.c and sh.c for
! 				   pegets.c */
  /*
   * These are declared here because they want to be
   * initialized in sh.init.c (to allow them to be made readonly)

========	diff -bc csh/sh.init.c tcsh/sh.init.c	========

*** csh/sh.init.c	Fri Apr 13 10:44:07 1984
--- tcsh/sh.init.c	Fri Apr 13 10:45:11 1984
***************
*** 8,13
  
  extern	int doalias();
  extern	int dobg();
  extern	int dobreak();
  extern	int dochngd();
  extern	int docontin();

--- 8,14 -----
  
  extern	int doalias();
  extern	int dobg();
+ extern	int dobind();
  extern	int dobreak();
  extern	int dochngd();
  extern	int docontin();
***************
*** 11,16
  extern	int dobreak();
  extern	int dochngd();
  extern	int docontin();
  extern	int dodirs();
  extern	int doecho();
  extern	int doelse();

--- 12,18 -----
  extern	int dobreak();
  extern	int dochngd();
  extern	int docontin();
+ extern	int dodetach();
  extern	int dodirs();
  extern	int doecho();
  extern	int doelse();
***************
*** 78,83
  	"alloc",	showall,	0,	1,
  #endif
  	"bg",		dobg,		0,	INF,
  	"break",	dobreak,	0,	0,
  	"breaksw",	doswbrk,	0,	0,
  #ifdef IIASA

--- 80,86 -----
  	"alloc",	showall,	0,	1,
  #endif
  	"bg",		dobg,		0,	INF,
+ 	"bind",		dobind,		0,	2,
  	"break",	dobreak,	0,	0,
  	"breaksw",	doswbrk,	0,	0,
  #ifdef IIASA
***************
*** 88,93
  	"chdir",	dochngd,	0,	1,
  	"continue",	docontin,	0,	0,
  	"default",	dozip,		0,	0,
  	"dirs",		dodirs,		0,	1,
  	"echo",		doecho,		0,	INF,
  	"else",		doelse,		0,	INF,

--- 91,97 -----
  	"chdir",	dochngd,	0,	1,
  	"continue",	docontin,	0,	0,
  	"default",	dozip,		0,	0,
+ /* 	"detach",	dodetach,	0,	0,	 */
  	"dirs",		dodirs,		0,	1,
  	"echo",		doecho,		0,	INF,
  	"else",		doelse,		0,	INF,

========	diff -bc csh/sh.lex.c tcsh/sh.lex.c	========

*** csh/sh.lex.c	Fri Apr 13 10:44:09 1984
--- tcsh/sh.lex.c	Fri Apr 13 10:45:14 1984
***************
*** 1,4
  static	char *sccsid = "@(#)sh.lex.c 4.1 10/9/80";
  
  #include "sh.h"
  

--- 1,5 -----
  static  char *sccsid = "@(#)sh.lex.c 4.1 10/9/80";
+ static char *SCCSid = "@(#)sh.lex.c     1.3 - 1/1/82 13:57:46 - HP/CRC";
  
  #include "sh.h"
  
***************
*** 1169,1174
  bgetc()
  {
  	register int buf, off, c;
  
  #ifdef TELL
  	if (cantell) {

--- 1170,1177 -----
  bgetc()
  {
          register int buf, off, c;
+         char ttyline[BUFSIZ];
+         register int numleft = 0, roomleft;
  
  #ifdef TELL
          if (cantell) {
***************
*** 1207,1212
  	if (fseekp >= feobp) {
  		buf = (int) feobp / BUFSIZ;
  		off = (int) feobp % BUFSIZ;
  		do
  			c = read(SHIN, fbuf[buf] + off, BUFSIZ - off);
  		while (c < 0 && errno == EINTR);

--- 1210,1216 -----
          if (fseekp >= feobp) {
                  buf = (int) feobp / BUFSIZ;
                  off = (int) feobp % BUFSIZ;
+                 roomleft = BUFSIZ - off;
                  do
                      if (intty)                  /* then use tenex routine */
                      {
***************
*** 1208,1214
  		buf = (int) feobp / BUFSIZ;
  		off = (int) feobp % BUFSIZ;
  		do
! 			c = read(SHIN, fbuf[buf] + off, BUFSIZ - off);
  		while (c < 0 && errno == EINTR);
  		if (c <= 0)
  			return (-1);

--- 1212,1235 -----
                  off = (int) feobp % BUFSIZ;
                  roomleft = BUFSIZ - off;
                  do
!                     if (intty)                  /* then use tenex routine */
!                     {
!                         c = numleft ? numleft : 
! 			    (isatty (SHIN) ? twenex(ttyline, BUFSIZ) :
! 					     read(SHIN, ttyline, BUFSIZ) );
!                         if (c > roomleft)       /* No room in this buffer? */
!                         {
!                             /* start with fresh buffer */
!                             feobp = fseekp = fblocks * BUFSIZ;
!                             numleft = c;
!                             goto again;
!                         }
!                         if (c > 0)
!                             copy (fbuf[buf] + off, ttyline, c);
!                         numleft = 0;
!                     }
!                     else
!                         c = read(SHIN, fbuf[buf] + off, roomleft);
                  while (c < 0 && errno == EINTR);
                  if (c <= 0)
                          return (-1);
***************
*** 1213,1218
  		if (c <= 0)
  			return (-1);
  		feobp += c;
  		goto again;
  	}
  	c = fbuf[buf][(int) fseekp % BUFSIZ];

--- 1234,1240 -----
                  if (c <= 0)
                          return (-1);
                  feobp += c;
+                 if (!intty)
                      goto again;
          }
          c = fbuf[buf][(int) fseekp % BUFSIZ];

========	diff -bc csh/sh.misc.c tcsh/sh.misc.c	========

*** csh/sh.misc.c	Fri Apr 13 10:44:10 1984
--- tcsh/sh.misc.c	Fri Apr 13 10:45:16 1984
***************
*** 370,372
  			return (0);
  	}
  }

--- 370,413 -----
  			return (0);
  	}
  }
+ 
+ itoa(n, s)			/* convert n to characters in s */
+ int n;
+ char *s;
+ {
+     int i, sign;
+     
+     if ((sign = n) < 0) 	/* record sign */
+ 	n = -n;
+     i = 0;
+     do {
+ 	s[i++] = n % 10 + '0';
+     } while ((n /= 10) > 0);
+     if (sign < 0)
+ 	s[i++] = '-';
+     s[i] = '\0';
+     Reverse(s);
+ }
+ 
+ Reverse(s)
+ char *s;
+ {
+ 	int c, i, j;
+ 
+ 	for (i=0, j = strlen(s)-1; i < j; i++, j--) {
+ 		c = s[i];
+ 		s[i] = s[j];
+ 		s[j] = c;
+ 	}
+ }
+ 
+ #include <sgtty.h>
+ 
+ isatty(f)
+ {
+ 	struct sgttyb ttyb;
+ 
+ 	if (gtty(f, &ttyb) < 0)
+ 		return(0);
+ 	return(1);
+ }

========	diff -bc csh/sh.proc.c tcsh/sh.proc.c	========

*** csh/sh.proc.c	Fri Apr 13 10:44:16 1984
--- tcsh/sh.proc.c	Fri Apr 13 10:45:22 1984
***************
*** 299,305
  	register int index;
  
  	if (pp->p_pid == 0) {
! 		printf("BUG: process flushed twice");
  		return;
  	}
  	while (pp->p_pid != pp->p_jobid)

--- 299,305 -----
  	register int index;
  
  	if (pp->p_pid == 0) {
! 		printf("BUG: process flushed twice\n");
  		return;
  	}
  	while (pp->p_pid != pp->p_jobid)
***************
*** 605,611
  					break;
  
  				default:
! 					printf("BUG: status=%-9o", status);
  				}
  			}
  		}

--- 605,611 -----
  					break;
  
  				default:
! 					printf("BUG: status=%-9o\n", status);
  				}
  			}
  		}

========	diff -bc csh/sh.set.c tcsh/sh.set.c	========

*** csh/sh.set.c	Fri Apr 13 10:44:20 1984
--- tcsh/sh.set.c	Fri Apr 13 10:45:24 1984
***************
*** 73,79
  
  			HIST = *p++;
  			HISTSUB = *p;
! 		} else if (eq(vp, "user"))
  			setenv("USER", value(vp));
  		else if (eq(vp, "term"))
  			setenv("TERM", value(vp));

--- 73,79 -----
  
  			HIST = *p++;
  			HISTSUB = *p;
! 		} else if (eq(vp, "user")) {
  			setenv("USER", value(vp));
  		} else if (eq(vp, "shlvl")) {
  			setenv("SHLVL", value(vp));
***************
*** 75,83
  			HISTSUB = *p;
  		} else if (eq(vp, "user"))
  			setenv("USER", value(vp));
! 		else if (eq(vp, "term"))
! 			setenv("TERM", value(vp));
! 		else if (eq(vp, "home"))
  			setenv("HOME", value(vp));
  	} while (p = *v++);
  }

--- 75,83 -----
  			HISTSUB = *p;
  		} else if (eq(vp, "user")) {
  			setenv("USER", value(vp));
! 		} else if (eq(vp, "shlvl")) {
! 			setenv("SHLVL", value(vp));
! 		} else if (eq(vp, "home")) {
  			setenv("HOME", value(vp));
  		} else if (eq(vp, "term")) {
  			setenv("TERM", value(vp));
***************
*** 79,84
  			setenv("TERM", value(vp));
  		else if (eq(vp, "home"))
  			setenv("HOME", value(vp));
  	} while (p = *v++);
  }
  

--- 79,88 -----
  			setenv("SHLVL", value(vp));
  		} else if (eq(vp, "home")) {
  			setenv("HOME", value(vp));
+ 		} else if (eq(vp, "term")) {
+ 			setenv("TERM", value(vp));
+ 			ilsetup(SHIN, SHOUT);
+ 			}
  	} while (p = *v++);
  }

!Funky!Stuff!

paul@osu-dbs.UUCP (Paul Placeway) (04/20/84)

(I'm trying again for everyone who missed some of it)

The following code is my changes to Ken Greer's tcsh.  I have added a visual
mini-editor to the shell, and cleaned up the expansion routines some what.
note that this is the 4.1 version.  When we get 4.2 up I'll repost the new
changes.

Please send any changes back to me so that I can update our version.

Note: this is part 7 of 7, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul@ohio-state)

================ cut here ================
: This is a shar archive.  Extract with sh, not csh.
echo x - tw.parse.c
cat > tw.parse.c << '!Funky!Stuff!'
#define MAKE_TWENEX		/* flag to include definitions */
#include "tw.h"

/* do the expand or list on the command line -- SHOULD BE REPLACED */
int fcompare();

tenematch (inputline, inputline_size, num_read, command)
char   *inputline;		/* match string prefix */
int     inputline_size;		/* max size of string */
int	num_read;		/* # actually in inputline */
COMMAND command;		/* LIST or RECOGNIZE or PRINT_HELP */

{
    static char 
	    *delims = " '\"\t;&<>()|^%";
    static char 
	    *cmd_delims = ";&(|`";
    char word [FILSIZ + 1];
    register char *str_end, *word_start, *cmd_start, *wp;
    char *cmd_st;
    int space_left;
    int is_a_cmd;		/* UNIX command rather than filename */
    int search_ret;		/* what search returned for debugging */

    str_end = &inputline[num_read];

   /*
    * Find LAST occurence of a delimiter in the inputline.
    * The word start is one character past it.
    */
    for (word_start = str_end; word_start > inputline; --word_start)
	if (index (delims, word_start[-1]))
	    break;

				/* space backward looking for the beginning
				   of this command */
    for (cmd_st = str_end; cmd_st > inputline; --cmd_st)
	if (index (cmd_delims, cmd_st[-1]))
	    break;
				/* step forward over leading spaces */
    while (*cmd_st != '\0' && (*cmd_st == ' ' || *cmd_st == '\t'))
	cmd_st++;

    space_left = inputline_size - (word_start - inputline) - 1;
    
    is_a_cmd = starting_a_command (word_start, inputline);

    for (cmd_start = word_start, wp = word; cmd_start < str_end;
    	 *wp++ = *cmd_start++);
    *wp = 0;

/*  printf ("\ncmd_st:%s:\nword_start:%s:\n", cmd_st, word_start); */
				/* for debugging */
    if (command != PRINT_HELP) {
        search_ret = search (word, wp, command, space_left, is_a_cmd);
        copyn (word_start, word, space_left);
				   /* put the stuf INTO the line */
        return (search_ret);
    }
    else {
	do_help (cmd_st);
	return (1);
    }
}



/*
 * return true if check items initial chars in template
 * This differs from PWB imatch in that if check is null
 * it items anything
 */

static
is_prefix (check, template)
char   *check,
       *template;
{
    register char  *check_char,
                   *template_char;

    check_char = check;
    template_char = template;
    do
	if (*check_char == 0)
	    return (TRUE);
    while (*check_char++ == *template_char++);
    return (FALSE);
}

/* return true if the command starting at wordstart is a command */

starting_a_command (wordstart, inputline)
register char *wordstart, *inputline;
{
    static char
	    *cmdstart = ";&(|`",
	    *cmdalive = " \t'\"";
    while (--wordstart >= inputline)
    {
	if (index (cmdstart, *wordstart))
	    break;
	if (!index (cmdalive, *wordstart))
	    return (FALSE);
    }
    if (wordstart > inputline && *wordstart == '&')	/* Look for >& */
    {
	while (wordstart > inputline &&
			(*--wordstart == ' ' || *wordstart == '\t'));
	if (*wordstart == '>')
		return (FALSE);
    }
    return (TRUE);
}


/*
 * Object: extend what user typed up to an ambiguity.
 * Algorithm:
 * On first match, copy full entry (assume it'll be the only match) 
 * On subsequent matches, shorten extended_name to the first
 * character mismatch between extended_name and entry.
 * If we shorten it back to the prefix length, stop searching.
 */
recognize (extended_name, entry, name_length, numitems)
char *extended_name, *entry;
{
    if (numitems == 1)				/* 1st match */
	copyn (extended_name, entry, MAXNAMLEN);
    else					/* 2nd and subsequent matches */
    {
	register char *x, *ent;
	register int len = 0;
	for (x = extended_name, ent = entry; *x && *x == *ent++; x++, len++);
	*x = '\0';				/* Shorten at 1st char diff */
	if (len == name_length)			/* Ambiguous to prefix? */
	    return (-1);		       /* So stop now and save time */
    }
    return (0);
}


/*
 * Perform a RECOGNIZE or LIST command on string "word".
 */
static
search (word, wp, command, max_word_length, looking_for_command)
char   *word,
       *wp;			/* original end-of-word */
COMMAND command;
{
    register numitems,
	    name_length,		/* Length of prefix (file name) */
	    looking_for_lognames;	/* True if looking for login names */
    int	    showpathn;			/* True if we want path number */
    struct stat
	    dot_statb,			/* Stat buffer for "." */
	    curdir_statb;	       /* Stat buffer for current directory */
    int	    dot_scan,			/* True if scanning "." */
	    dot_got;			/* True if have scanned dot already */
    char    tilded_dir[FILSIZ + 1],	/* dir after ~ expansion */
	    dir[FILSIZ + 1],		/* /x/y/z/ part in /x/y/z/f */
            name[MAXNAMLEN + 1],	/* f part in /d/d/d/f */
            extended_name[MAXNAMLEN+1],	/* the recognized (extended) name */
            *entry,		       /* single directory entry or logname */
	    *path;			/* hacked PATH environment variable */
    int     next_command = 0;		/* the next command to take out of */
					/* the list of commands */
    static DIR 
	    *dir_fd = NULL;
    static char
           **items = NULL;		/* file names when doing a LIST */

    if (items != NULL)
	FREE_ITEMS (items);
    numitems = 0;
    if (dir_fd != NULL)
	FREE_DIR (dir_fd);

    looking_for_lognames = (*word == '~') && (index (word, '/') == NULL);
    looking_for_command &= (*word != '~') && (index (word, '/') == NULL);


    dot_got = FALSE;
    stat (".", &dot_statb);

    if (looking_for_lognames)			/* Looking for login names? */
    {
	setpwent ();				/* Open passwd file */
	copyn (name, &word[1], MAXNAMLEN);	/* name sans ~ */
    }
    else if (!looking_for_command)
    {						/* Open directory */
	extract_dir_and_name (word, dir, name);
	if ((tilde (tilded_dir, dir) == 0) ||	/* expand ~user/... stuff */
	   ((dir_fd = opendir (*tilded_dir ? tilded_dir : ".")) == NULL))
	{
/* LOBOTOMIZE	printf ("No directory file discriptor.\n");	 */
		return (0);
	}

	dot_scan = FALSE;
    }

    name_length = strlen (name);
    showpathn = looking_for_command && is_set("listpathnum");

    while (1) {
        if (!looking_for_command) {
	    if ((entry = getentry (dir_fd, looking_for_lognames)) == NULL) {
		break;
	    }
	    
    	    /*
    	     * Don't match . files on null prefix match
    	     */
    	    if (name_length == 0 && entry[0] == '.' && !looking_for_lognames)
    	        continue;

	} else {
	    if (numcommands == 0) {
		dohash ();
	    }
	    if ((entry = command_list[next_command++]) == NULL)
		break;
	    copyn (name, word, MAXNAMLEN);	/* so it can match things */
	}

    	if (!is_prefix (name, entry))
    	    continue;
    
    	if (command == LIST)		/* LIST command */
    	{
    	    extern char *malloc ();
    	    register int length;
    	    if (numitems >= MAXITEMS)
    	    {
    		printf ("\nYikes!! Too many %s!!\n",
    		    looking_for_lognames ? "names in password file":"files");
		break;
    	    }
    	    if (items == NULL)
    	    {
    		items = (char **) calloc (sizeof (items[1]), MAXITEMS + 1);
    		if (items == NULL)
    		    break;
    	    }
    	    length = strlen(entry) + 1;
    	    if (showpathn)
    		length += strlen(dirflag);
    	    if ((items[numitems] = malloc (length)) == NULL)
    	    {
    		printf ("\nYikes!! I ran out of memory!!!\n");
    		break;
    	    }
    	    copyn (items[numitems], entry, MAXNAMLEN);
    	    if (showpathn)
    	        catn (items[numitems], dirflag, MAXNAMLEN);
    	    numitems++;
    	}
    	else {					/* RECOGNIZE command */
	    if (adrof ("recexact")) {
		if (strcmp (name, entry) == 0) {	/* EXACT match */
	            copyn (extended_name, entry, MAXNAMLEN);
		    numitems = 1;		/* fake into expanding */
		    break;
		}
	    }
    	    if (recognize (extended_name, entry, name_length, ++numitems))
    		break;
	}
    }

    if (!looking_for_command) {		
        if (looking_for_lognames)
	    endpwent ();
        else
	    FREE_DIR (dir_fd);
    }

    if (command == RECOGNIZE && numitems > 0)
    {
	if (looking_for_lognames)
	    copyn (word, "~", 1);
	else if (looking_for_command)
	    word[0] = 0;
	else
	    copyn (word, dir, max_word_length);	       /* put back dir part */
	catn (word, extended_name, max_word_length);   /* add extended name */
	if (numitems == 1) {
            if (looking_for_lognames) {			/* add / */
                catn (word, "/", max_word_length);
            }
            else {
                if (looking_for_command) {		/* add space */
                    catn (word, " ", max_word_length);
                }
                else {
                    if (filetype (tilded_dir, extended_name) == '/') {
                        catn (word, "/", max_word_length);
                    }
                    else {
                        catn (word, " ", max_word_length);
                    }
                }
            }
	}
	
	return (numitems);		        /* at the end */
    }

    if (command == LIST) {
	qsort (items, numitems, sizeof (items[1]), fcompare);
	print_by_column (looking_for_lognames ? NULL:tilded_dir, items,
			 numitems, looking_for_command);
	if (items != NULL)
	    FREE_ITEMS (items);
    }
    return (0);
}


/* stuff for general command line hacking */

/*
 * Strip next directory from path; return ptr to next unstripped directory.
 */
 
char *extract_dir_from_path (path, dir)
char *path, dir[];
{
    register char *d = dir;

    while (*path && (*path == ' ' || *path == ':')) path++;
    while (*path && (*path != ' ' && *path != ':')) *(d++) = *(path++);
    while (*path && (*path == ' ' || *path == ':')) path++;

    ++dirctr;
    if (*dir == '.')
        strcpy (dirflag, " .");
    else
    {
        dirflag[0] = ' ';
	if (dirctr <= 9)
	{
		dirflag[1] = '0' + dirctr;
		dirflag[2] = '\0';
	}
	else
	{
		dirflag[1] = '0' + dirctr / 10;
		dirflag[2] = '0' + dirctr % 10;
		dirflag[3] = '\0';
	}
    }
    *(d++) = '/';
    *d = 0;

    return path;
}


static
free_items (items)
register char **items;
{
    register int i;
    for (i = 0; items[i]; i++)
	free (items[i]);
    free (items);
}


/*
 * parse full path in file into 2 parts: directory and file names
 * Should leave final slash (/) at end of dir.
 */
static
extract_dir_and_name (path, dir, name)
char   *path, *dir, *name;
{
    extern char *rindex ();
    register char  *p;
    p = rindex (path, '/');
    if (p == NULL)
    {
	copyn (name, path, MAXNAMLEN);
	dir[0] = '\0';
    }
    else
    {
	p++;
	copyn (name, p, MAXNAMLEN);
	copyn (dir, path, p - path);
    }
}


char *
getentry (dir_fd, looking_for_lognames)
DIR *dir_fd;
{
    if (looking_for_lognames)			/* Is it login names we want? */
    {
	extern struct passwd *getpwent ();
	register struct passwd *pw;
	if ((pw = getpwent ()) == NULL)
	    return (NULL);
	return (pw -> pw_name);
    }
    else					/* It's a dir entry we want */
    {
	register struct direct *dirp;
	if (dirp = readdir (dir_fd))
	    return (dirp -> d_name);
	return (NULL);
    }
}


/*
 * expand "old" file name with possible tilde usage
 *		~person/mumble
 * expands to
 *		home_directory_of_person/mumble
 * into string "new".
 */

char *
tilde (new, old)
char *new, *old;
{
    extern struct passwd *getpwuid (), *getpwnam ();

    register char *o, *p;
    register struct passwd *pw;
    static char person[40] = {0};

    if (old[0] != '~')
    {
	strcpy (new, old);
	return (new);
    }

    for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++);
    *p = '\0';

    if (person[0] == '\0')			/* then use current uid */
	pw = getpwuid (getuid ());
    else
	pw = getpwnam (person);

    if (pw == NULL)
	return (NULL);

    strcpy (new, pw -> pw_dir);
    (void) strcat (new, o);
    return (new);
}

char
filetype (dir, file)
char *dir, *file;
{
    if (dir)
    {
	char path[512];
	struct stat statb;
	strcpy (path, dir);
	catn (path, file, sizeof path);
	if (stat (path, &statb) >= 0)
	{
	    if (statb.st_mode & S_IFDIR)
		return ('/');
	    if (statb.st_mode & 0111)
		return ('*');
	}
    }
    return (' ');
}

/*
 * Print sorted down columns
 */
print_by_column (dir, items, count, looking_for_command)
register char *dir, *items[];
{
    register int i, rows, r, c, maxwidth = 0, columns;
    for (i = 0; i < count; i++)
	maxwidth = max (maxwidth, strlen (items[i]));
    maxwidth += looking_for_command ? 1:2;    /* for the file tag and space */
    columns = 80 / maxwidth;
    rows = (count + (columns - 1)) / columns;
    for (r = 0; r < rows; r++)
    {
	for (c = 0; c < columns; c++)
	{
	    i = c * rows + r;
	    if (i < count)
	    {
		register int w;
		printf("%s", items[i]);
		w = strlen (items[i]);
		/* Print filename followed by '/' or '*' or ' ' */
		if (!looking_for_command)
			putchar (filetype (dir, items[i])), w++;
		if (c < (columns - 1))			/* Not last column? */
		    for (; w < maxwidth; w++)
			putchar (' ');
	    }
	}
	printf ("\n");
    }
}

/*
 * For qsort()
 */
fcompare (file1, file2)
char  **file1, **file2;
{
    return (strcmp (*file1, *file2));
}
!Funky!Stuff!