UNIX-WIZARDS Digest V5#093

Mike Muuss Unix-Wizards-Request at arpa.brl
Sat Jul 9 17:45:28 AEST 1988


UNIX-WIZARDS Digest          Sat, 09 Jul 1988              V5#093

Today's Topics:
                          Re: Speaking of ksh
                           3B1 Kernel Tuning
                         OSF without AT&T code
                   Getting the pathname from a FILE*.
                    Re: Why DEC doesn't need an ABI
         Re: "dd conv=unblock cbs=80 - really grep replacement"
                       Fork() works & returns -1
                   Re: Joy of Joys (porting C-shell)
                      Dynamic Linking possible???
                         Re: How do I get RCS?
                            ile source code
                              ile man page
                             ile is coming
         Why does BSD Fast File System call syncip from itrunc?
                 Re: Getting the pathname from a FILE*.

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

From: "Derek E. Terveer" <det at hawkmoon.mn.org>
Subject: Re: Speaking of ksh
Date: 6 Jul 88 15:48:14 GMT
To:       unix-wizards at brl-sem.arpa

In article <15064 at tut.cis.ohio-state.edu>, lvc at tut.cis.ohio-state.edu (Lawrence V. Cipriani) writes:
> Also, what version of C are you running (use the command what /bin/cc).

Hmmm, this produces the nice but not very informative message on my machine:

$ what /bin/cc
/bin/cc:
	 cc  Final Release

Do you know of any other way to extract the make and model of the c compiler
one is using?

Ps. my machine is a Acer-1100 running V/386 3.0-L2.2
-- 
Derek Terveer	det at hawkmoon.MN.ORG	uunet!rosevax!elric!hawkmoon!det

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

From: Deloy Johnson <dpj at wybbs.uucp>
Subject: 3B1 Kernel Tuning
Date: 6 Jul 88 20:52:33 GMT
Keywords: How, Help!!
To:       unix-wizards at SEM.BRL.MIL


We have an AT&T 3B1 running version 3.0 of UNIX and we need to
tune the kernel so that the NFLOCKS parameter is increased to
150 or 200 from 100.  Since we are using 3.0 of UNIX, AT&T isn't
helping because they don't support that version any more.  The
manuals we have do not help so can someone out there help?  If
you can I would appreciate a phone call (616-452-1191 8:00-5:00
EST or 616-538-6055 5:00-??:??EST).

This IS an emergency!!  Thanks ...

Deloy Johnson
Data Services
Grand Rapids, MI

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

From: David Lubkin <lubkin at apollo.uucp>
Subject: OSF without AT&T code
Date: 8 Jul 88 20:49:00 GMT
To:       unix-wizards at SEM.BRL.MIL

OSF is presumably going to use the established method for getting
out from under licenses:  hire employees who have demonstrably never
had access to source code, have them work at a separate facility,
be able to prove it, and have the backbone and wherewithal to fight
in court.

Phoenix did this to create a work-alike to the MS-DOS BIOS, which 
they then licensed to many manufacturers, thereby creating the PC 
clone market.  Lynx recently announced a "POSIX compatible, SVID 
compatible" (whatever that means) real-time almost-UNIX that does 
not require an AT&T license.

Given the money and the will, it's not that hard.

I wonder if they'll start from scratch or include the various public
domain pieces floating around the net (including the pd MINIX add-ons).


-- David Lubkin.


      mammalian ARPA:  lubkin at apollo.com
      reptilian ARPA:  apollo!lubkin at eddie.mit.edu

      mammalian UUCP:  lubkin at apollo.uucp
      reptilian UUCP:  {mit-erl,yale,uw-beaver,umix,decvax}!apollo!lubkin

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

From: "Karl Berry." <karl at umb.umb.edu>
Subject: Getting the pathname from a FILE*.
Date: 8 Jul 88 21:38:02 GMT
To:       unix-wizards at SEM.BRL.MIL


The title says it all. Am I missing something obvious?
The pathname doesn't seem to be a field in either the
_iobuf or the structure returned by stat.
ttyname or ctermid will give you the pathname
of your terminal, but I want the pathname of an
arbitrary FILE* I have fopen. Or am I missing some
reason why this is not feasible?

Karl.
karl at umb.edu     ...!harvard!umb!karl

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

From: Tom Betz <tbetz at dasys1.uucp>
Subject: Re: Why DEC doesn't need an ABI
Date: 8 Jul 88 15:52:06 GMT
To:       unix-wizards at SEM.BRL.MIL

*n article <8221 at ncoast.UUCP> allbery at ncoast.UUCP (Brandon S. Allbery) writes:
>As quoted from <686 at spectrix.UUCP> by clewis at spectrix.UUCP (Chris Lewis (It's loose again!)):
*+---------------
>| In article <8185 at ncoast.UUCP> allbery at ncoast.UUCP (Brandon S. Allbery) writes:
*| |that (a) DEC does not license the VAX processor for anyone else's use, and
*| |(b) DEC sells the only true commercial VAX UNIX.
*| 
*| And I agree that most of the other versions of UNIX you mention aren't
*| "commercial".  But, what about HCR's SVR3 for VAXen?  They've been shipping
*| for several months now (I think).  That I would consider "commercial"...
*+---------------
*
*I hadn't heard of that, and hereby withdraw my argument.  Thanks for the
*update.
*

What about Mt. Xinu's BSD?  They also support AppleNet and TOPS connectivity, 
and I understand they have been shipping for more than a year.



-- 
Tom Betz                          UUCP: ...!cmcl2!phri!dasys1!tbetz  
ZCNY                             "When personal computers are outlawed, 
Yonkers, NY, USA 10701-2509       only outlaws will have personal computers."
 

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

From: "Brandon S. Allbery" <allbery at ncoast.uucp>
Subject: Re: "dd conv=unblock cbs=80 - really grep replacement"
Date: 8 Jul 88 21:27:29 GMT
Followup-To: comp.unix.wizards
To:       unix-wizards at SEM.BRL.MIL

As quoted from <145 at insyte.UUCP> by jad at insyte.UUCP (Jill Diewald):
+---------------
| Ideally, grep could be passed a record size which it would use instead
| of newlines.  And/Or grep could be told to only search specified columns
| of every record.  This would enable me to easily deal with these files 
| without tieing up either memory or disk space.
+---------------

	dd if=... conv=unblock cbs=... | cut -c... | grep ...

(or replace "dd" with an unblocker program)

Cro-magnons win out over Neanderthals again.  ;-)

-> If you don't have "cut", that should tell you something.  What it tells
you depends on which variant of Unix you have.
-- 
Brandon S. Allbery, uunet!marque!ncoast!allbery			DELPHI: ALLBERY
	    For comp.sources.misc send mail to ncoast!sources-misc

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

From: Peter Jeremy <peter at stca77.stc.oz>
Subject: Fork() works & returns -1
Date: 7 Jul 88 05:53:43 GMT
To:       unix-wizards at SEM.BRL.MIL

I am running SCO Xenix Release 2.2.1.  I recently noticed that under some
circumstances fork() would return -1 to both parent and child, along with
errno=24 (Too many open files).  Naturally, this was rather upsetting to
the processes concerned.  At the time, the process had about 5 open files.
According to my understanding of fork(), that error is impossible anyway.

In addition, the process names reported by PS would randomly change to
the names of other processes. (Like having ps report 2 or 3 "init"s running
simultaneously).

Other than these problems, the system seemed to work normally.  Examination
of the configuration variables didn't reveal anything overly wrong and the
system didn't complain at all.

I eventually gave up and started from fresh /usr/sys/conf/{master,xenixconf}
files.  This seems to have cured the problem.  Has anyone else had similar
experiences?
-- 
Peter Jeremy (VK2PJ)         peter%stca77 at stcns3.stc.oz
Alcatel-STC Australia        ...!munnari!stcns3.stc.oz!stca77!peter
41 Mandible St               peter%stca77 at stcns3.stc.oz@uunet.UU.NET
ALEXANDRIA  NSW  2015

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

From: Boyd Roberts <boyd at basser.oz>
Subject: Re: Joy of Joys (porting C-shell)
Date: 8 Jul 88 01:27:00 GMT
To:       unix-wizards at brl-sem.arpa

Those coders who believe all the world is a VAX are just
wrong-wrong-wrong.  There's a machine in the UK called an
Orion made by some people called High Level Hardware.  It
has TWO (count them) stacks and is word (32 bit) addressed.
Miss-aligned byte references don't fault and the byte offset
is in the HIGH order address bits.  So you get a byte address
space that looks something like this:

	0x00000000
	0x10000000
	0x20000000
	0x30000000
	0x00000001
	0x10000001
	0x20000001
	0x30000001

	    etc...

But then the two stacks come in.  A high order bit (in fact two)
select which stack you're referencing.  Either, the ``scalar''
or the ``vector'' stack.  The address space is NOT contiguous.

Stick to K&R and you're fine.

Somehow High Level Hardware ported 4.2BSD to their box.  They
must have been crazy!!  I used it & it worked.  Hats off to
HLH, but their hardware people should've been shot.  Worse
still I think I heard rumours about them releasing NFS on it.

Now if XDR ain't VAX dependant, I don't know what is.  SysV NFS 2.0
XDR has some real cuties for non VAX byte order machines.  Puke!
One Sunday afternoon & a lot of printf's later I found it.  Assuming
how unions/structures are packed & passed to function calls as their
constituent elements is NO way to write a ``portable'' piece of code.


Boyd Roberts			boyd at basser.cs.su.oz
				boyd at necisa.necisa.oz

``When the going gets wierd, the weird turn pro...''

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

From: Scott Weitzenkamp <scott at gemini.laic.uucp>
Subject: Dynamic Linking possible???
Date: 8 Jul 88 08:51:48 GMT
Sender: news at laic.uucp
To:       unix-wizards at brl-sem.arpa


  Is dynamic linking possible on VMS and BSD Unix, in other words can
I load in routines on the fly and call them?  I want my C program to
generate some more C code, and then link it in with the already
running program.
  Is anybody out there using dynamic linking (or "incremental
loading", as the BSD man pages call it)?  This capability seems to be
built in to OS/2 and Sun OS 4.0, which leaves me curious as to why
I haven't seen much info on the topic.  Is this a tough thing to
implement in an OS?
  On BSD 4.2 and 4.3, I've seen two examples that do work: the first
uses "ld -d -r", and did all sorts of nasty relocation by hand.  The
second one (posted to comp.unix.wizards by djones at megatest) used "ld
-A" and was considerably cleaner.  Can anybody enlighten me on these
three flags (-A, -d, and -r)?  The man pages for ld, unfortunately,
leave me drooling for more information...  8-)
  While I'm at it, what does Sys V Unix have for dynamic 
linking?  Same as BSD?  
Scott Weitzenkamp           UUCP:  {lll-lcc.arpa,ucbvax}!leadsv!laic!scott
Lockheed AI Center	    ARPA:  farmie at portia.stanford.edu  

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

From: Scott Weitzenkamp <scott at gemini.laic.uucp>
Subject: Re: How do I get RCS?
Date: 8 Jul 88 09:22:07 GMT
Sender: news at laic.uucp
Keywords: RCS SCCS BSD
To:       unix-wizards at brl-sem.arpa

In article <285 at laic.UUCP>, scott at nova.laic.uucp (Scott Weitzenkamp) writes:
> 
>   I've used RCS a few times on various machines running BSD, but I can't
> seem to find RCS on our Suns running SUN OS 3.4.  Is RCS included
> normally with eitherBSD or SysV???  If not, how can I get it?
> 
Thanks to everyone who replied:
 ------------------------------------------------------------
>From mkhaw at teknowledge-vaxc.arpa (Mike Khaw)

RCS is not AT&T or Berkeley code.  It was written by Walter Tichy when he
was at Purdue.  Anonymous ftp from purdue.edu, or for a SunOS version,
from mimsy.umd.edu (the Sun version has patches to fix the infamous VAX
NULL pointer dereference problem, among others).

 ------------------------------------------------------------
>From narten at purdue.edu 

If you happen to look in pub/rcs on arthur.cs.purdue.edu with
anonymous ftp, you might just find what you are looking for.

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

Scott Weitzenkamp           UUCP:  {lll-lcc.arpa,ucbvax}!leadsv!laic!scott
Lockheed AI Center	    ARPA:  farmie at portia.stanford.edu  

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

From: Bob Pendleton <bpendlet at esunix.uucp>
Subject: ile source code
Date: 7 Jul 88 20:27:01 GMT
To:       unix-wizards at brl-sem.arpa


#! /bin/sh
# This is a shell archive, meaning:
# 1.  Remove everything above the #! /bin/sh line.
# 2.  Save the resulting test in a file
# 3.  Execute the file with /bin/sh (not csh) to create the files:
#
#ile.c
#
# Created by bpendlet (Bob Pendleton) on Thu Jul  7 14:16:19 MDT 1988
#
if test -f 'ile.c'
then
    echo shar: will not over-write existing file "ile.c"
else
echo extracting "ile.c"
sed 's/^X//' >ile.c <<'SHAR_EOF'
X/*
X                          COPYRIGHT 1988
X              Evans & Sutherland Computer Corporation
X                        Salt Lake City, Utah
X                        All Rights Reserved.
X
X     THE INFORMATION  IN  THIS  SOFTWARE  IS  SUBJECT  TO  CHANGE
X     WITHOUT  NOTICE  AND SHOULD NOT BE CONSTRUED AS A COMMITMENT
X     BY  EVANS  &  SUTHERLAND.   EVANS  &  SUTHERLAND   MAKES  NO
X     REPRESENTATIONS  ABOUT  THE SUITABILITY OF THIS SOFTWARE FOR
X     ANY PURPOSE.  IT IS SUPPLIED  "AS  IS"  WITHOUT  EXPRESS  OR
X     IMPLIED WARRANTY.
X
X     IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING  DERIVATIVE
X     COPYRIGHT  RIGHTS,  APPROPRIATE LEGENDS MAY BE PLACED ON THE
X     DERIVATIVE WORK IN ADDITION TO THAT SET FORTH ABOVE.
X
X     Permission  to  use,  copy,  modify,  and  distribute   this
X     software  and  its documentation for any purpose and without
X     fee is hereby granted, provided  that  the  above  copyright
X     notice  appear  in  all  copies  and that both the copyright
X     notice and this permission notice appear in supporting docu-
X     mentation,  and  that  the name of Evans & Sutherland not be
X     used in advertising or publicity pertaining to  distribution
X     of the software without specific, written prior permission.
X
XWritten by:
X
X                        Robert C. Pendleton
X
XEvans & Sutherland, Interactive Systems Division, Salt Lake City, Utah.
X
X
X$Header: ile.c,v 1.21 88/07/07 11:02:52 bpendlet Exp $
X*/
X
X/*
Xile is compiled using:
X
Xcc ile.c -o ile -ltermcap
X*/
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <sgtty.h>
X#include <signal.h>
X#include <string.h>
X#include <pwd.h>
X#include <sys/ioctl.h>
X#include <sys/types.h>
X#include <sys/dir.h>
X
X/*------------------------------------------------------------------*/
X
X#define FALSE 0
X#define TRUE  1
X
X#define READ  0
X#define WRITE 1
X#define ERROR 2
X
X#define BUFFER_SIZE 256
X
X#define HISTORY_SIZE 101
X
X#define USER_NAME_SIZE 8
X
X#define EOL (-2)
X/*------------------------------------------------------------------*/
X
X/* special characters used by ile */
X
X#define del '\177'
X
X#define CA '\1'
X#define CB '\2'
X#define CC '\3'
X#define CD '\4'
X#define CE '\5'
X#define CF '\6'
X#define bel '\7'
X#define bs '\10'
X#define CI '\11'
X#define nl '\12'
X#define CK '\13'
X#define CL '\14'
X#define cr '\15'
X#define CN '\16'
X#define CO '\17'
X#define CP '\20'
X#define CQ '\21'
X#define CR '\22'
X#define CS '\23'
X#define CT '\24'
X#define CU '\25'
X#define CV '\26'
X#define CW '\27'
X#define CX '\30'
X#define CY '\31'
X#define CZ '\32'
X
X#define esc '\33'
X
X/*------------------------------------------------------------------*/
X/* areas and varaibles used to get termcap information */
X
Xchar *getenv();
X
Xchar *tgetnum();
Xchar *tgetflag();
Xchar *tgetstr();
X
Xchar termcap_entry[1024];	/* termcap entry for the users terminal */
Xchar term_seqs[1024];		/* area to store control sequences in */
Xchar *where = term_seqs;
X
Xchar *cle;			/* move cursor left one space */
X
Xchar *cce;			/* clear to end of line */
X
Xchar *cbl;			/* audible bell */
X
Xchar *cnl;			/* new line character */
X
Xchar *ccr;			/* carriage return */
X
X/*------------------------------------------------------------------*/
X
X/* file descriptors for tty and pty */
X
Xint master_pty;
Xint slave_tty;
X
X/*------------------------------------------------------------------*/
X
X/* the names of the tty and pty opened by getpty */
X
Xchar ttydev[] = "/dev/ttyxx";
Xchar ptydev[] = "/dev/ptyxx";
X
X/*------------------------------------------------------------------*/
X
X/* tty status flags */
X/*
X  The original tty status flags are stored so that they can be
X  restored when ile exits.
X*/
X
Xstruct sgttyb tty_params;
Xint tty_mode;
X
X/*------------------------------------------------------------------*/
X/*
XThe value of $HOME
X*/
X
Xchar *homedir = NULL;
X
X/*------------------------------------------------------------------*/
X/*
X  getpty opens a pty, storing file descriptors in pty and tty.
X  It trys pairs in order until it finds a pair that is not in use.
X*/
X
Xgetpty(pty, tty)
X    int *pty;
X    int *tty;
X
X{
X    int devindex;
X    int letter;
X
X    static char ptychar1[] = "pqrstuvwxyz";
X    static char ptychar2[] = "0123456789abcdef";
X
X    letter = 0;
X    while (letter < 11)
X    {
X	ttydev[strlen(ttydev) - 2] = ptychar1[letter];
X	ptydev[strlen(ptydev) - 2] = ptychar1[letter];
X	letter++;
X
X	devindex = 0;
X	while (devindex < 16)
X	{
X	    ttydev[strlen(ttydev) - 1] = ptychar2[devindex];
X	    ptydev[strlen(ptydev) - 1] = ptychar2[devindex];
X	    devindex++;
X
X	    if ((*pty = open(ptydev, O_RDWR)) >= 0)
X	    {
X		if ((*tty = open(ttydev, O_RDWR)) >= 0)
X		{
X		    return;
X		}
X		else
X		{
X		    (void) close(*pty);
X		}
X	    }
X	}
X    }
X
X    fprintf(stderr, "ile: unable to allocate pty/tty pair\n");
X    exit(1);
X}
X
X/*------------------------------------------------------------------*/
X/*
XTermcap entries may have a sequences of digits optionally followed
Xby a '*' in front of the actual sequence. This routine increments
Xthe pointer past this information.
X*/
Xvoid
Xstrip(ptr)
X    char **ptr;
X{
X    while (('0' <= **ptr) && (**ptr <= '9'))
X    {
X	(*ptr)++;
X    }
X
X    if (**ptr == '*')
X    {
X	(*ptr)++;
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XSet up everything needed to use the control sequences from the
Xtermcap entry for the terminal.
X*/
Xvoid
Xget_termcap()
X{
X    char *terminal_type;	/* type of terminal */
X
X    /* get the terminal name */
X
X    terminal_type = getenv("TERM");
X
X    /* get termcap entry */
X
X    if (tgetent(termcap_entry, terminal_type) < 1)
X    {
X	fprintf(stderr, "ile: can't find %s\n", terminal_type);
X	exit(1);
X    }
X
X    /* get the control sequences ile needs */
X
X    if (((cle = tgetstr("le", &where)) == NULL) ||
X	((cce = tgetstr("ce", &where)) == NULL) ||
X	((cbl = tgetstr("bl", &where)) == NULL) ||
X	((cnl = tgetstr("nl", &where)) == NULL) ||
X	((ccr = tgetstr("cr", &where)) == NULL))
X    {
X	fprintf(stderr, "ile: can't run on %s\n", terminal_type);
X	exit(1);
X    }
X
X    /* strip timing info from strings */
X
X    strip(&cle);
X    strip(&cce);
X    strip(&cbl);
X    strip(&cnl);
X    strip(&ccr);
X}
X/*------------------------------------------------------------------*/
X/*
Xclean up and leave.
X
XThis function is bound to the SIGCHLD signal so that when the
Xchild process exits, so does ile. It is also called when an exception
Xis detected by select() in ile().
X*/
Xvoid
Xcleanup()
X{
X    /* restore terminal status */
X
X    (void) ioctl(READ, TIOCSETP, &tty_params);
X    (void) ioctl(READ, TIOCLSET, &tty_mode);
X
X    /* clean up the tty/pty pair */
X
X    (void) close(master_pty);
X    (void) close(slave_tty);
X
X    /* make things look nice */
X
X    fputs(cnl, stdout);
X
X    exit(0);
X}
X/*------------------------------------------------------------------*/
X/*
XThe editing routines are called through the edit variable. This allows
Xthe quote and escape commands to be implemented as a straight forward
Xstate machine instead of requiring state flags and complex switch
Xstatements.
X*/
X/*------------------------------------------------------------------*/
X
X/* line edit buffer */
X
Xstatic char line[BUFFER_SIZE];
X
Xstatic int point;		/* insertion point */
Xstatic int length;		/* total chars in buffer */
X
X/* procedure to edit next character */
X
Xvoid (*edit) ();
X
X/* history buffer */
X
Xstruct
X{
X    int length;
X    char *line;
X} hist[HISTORY_SIZE];
X
Xint head;			/* insertion point */
Xint here;			/* current displayed line */
X
X/*------------------------------------------------------------------*/
X/*
XThe delimiter vector is used by the forward, backward, and delete
Xword operations to decide that a character is a delimiter.
X*/
X/*------------------------------------------------------------------*/
X
X#define CHAR_SET_SIZE 256
X
Xchar delimit[CHAR_SET_SIZE];
X
X/*------------------------------------------------------------------*/
X/*
XThe action_table is used to bind sequences of keys to operations or strings.
X*/
X/*------------------------------------------------------------------*/
X
Xtypedef enum
X{
X    is_action, is_string
X} action_type;
X
Xstruct
X{
X    action_type flag;
X    union
X    {
X	void (*action) ();
X	char *string;
X    } aors
X} action_table[4][CHAR_SET_SIZE];
X
X/*------------------------------------------------------------------*/
X
Xvoid echo();
Xvoid echoline();
Xvoid cleartoend();
Xvoid clearline();
Xvoid backspace();
Xvoid quote_edit();
Xvoid edit_0();
Xvoid edit_1();
Xvoid edit_2();
Xvoid edit_3();
Xvoid bell();
X
X/*------------------------------------------------------------------*/
X/*
XThe following routines are action routines that are executed by the
Xeditor to carry out commands. Each routine has a single character
Xargument. Each routine is invoked with the character that caused it
Xto be invoked as its argument.
X
XThe argument isn't always useful, but it is included to provide a
Xconsistent interface for the routines.
X*/
X/*------------------------------------------------------------------*/
X/*
XGiven a specific directory and the starting string of a file name,
Xfind the longest partial file name that starts with the substring.
X*/
Xvoid
Xcomplete_file_name(dir, name)
X    char *dir;
X    char *name;
X{
X    DIR *dirp;
X    struct direct *dp;
X
X    int len;
X    int maxlen;
X    int oldlen;
X
X    char oldname[MAXNAMLEN + 1];
X    char newname[MAXNAMLEN + 1];
X
X    if ((dir != NULL) &&
X	(name != NULL) &&
X	((oldlen = strlen(name)) != 0) &&
X	((dirp = opendir(dir)) != NULL))
X    {
X	maxlen = oldlen;
X	strcpy(oldname, name);
X	strcpy(newname, name);
X
X	/* find the longest name starting with name */
X
X	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
X	{
X	    if (dp->d_name != NULL)
X	    {
X		len = strlen(dp->d_name);
X		if ((maxlen < len) &&
X		    (strncmp(oldname, dp->d_name, oldlen) == 0))
X		{
X		    maxlen = len;
X		    strcpy(newname, dp->d_name);
X		}
X	    }
X	}
X
X	rewinddir(dirp);
X
X	/* find the longest common sub string */
X
X	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
X	{
X	    if (dp->d_name != NULL)
X	    {
X		len = strlen(dp->d_name);
X		if ((len <= maxlen) &&
X		    (strncmp(oldname, dp->d_name, oldlen) == 0))
X		{
X		    for (;
X			(oldlen < len) &&
X			(strncmp(newname, dp->d_name, len) != 0);
X			len--);
X
X		    maxlen = len;
X		    newname[maxlen] = '\0';
X		}
X	    }
X	}
X
X	if (strlen(name) != strlen(newname))
X	{
X	    /* return the extended name */
X
X	    strcpy(name, newname);
X	}
X	else
X	{
X	    /* no difference so beep */
X
X	    bell('\0');
X	}
X
X	closedir(dirp);
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XList all the files in a given directory that start with the string
Xpassed as name.
X*/
Xvoid
Xlist_file_names(dir, name)
X    char *dir;
X    char *name;
X{
X    DIR *dirp;
X    struct direct *dp;
X
X    int namelen;
X
X    int count;
X
X    /* make sure everything is ok */
X
X    if ((dir != NULL) &&
X	(name != NULL) &&
X	((dirp = opendir(dir)) != NULL))
X    {
X	namelen = strlen(name);
X
X	/* print all the names starting with name */
X
X	count = 0;
X	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
X	{
X	    if ((namelen <= dp->d_namlen) &&
X		(strncmp(name, dp->d_name, namelen) == 0) &&
X		(dp->d_name != NULL))
X	    {
X		if (80 < (count + dp->d_namlen + 1))
X		{
X
X		    fputs(ccr, stdout);
X		    fputs(cnl, stdout);
X		    count = 0;
X		}
X		count = count + dp->d_namlen + 1;
X
X		fputs(dp->d_name, stdout);
X		fputc(' ', stdout);
X
X		for (; (count % 16) != 0; count++)
X		{
X		    fputc(' ', stdout);
X		}
X
X	    }
X	}
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XAssuming that there is a file name under the cursor return a path
Xand a file name. If there is no path name return "."
X*/
Xint
Xget_dir_and_name(dir, name, username, userend, start, middle, tail)
X    char *dir;
X    char *name;
X    char *username;
X    int *userend;
X    int *start;
X    int *middle;
X    int *tail;
X{
X    int dirlen;
X
X    int newstart;
X
X    int punlen;
X    char pun[USER_NAME_SIZE + 1];
X    struct passwd *userpwd;
X
X    int i;
X
X    /* set the default path and file name */
X
X    dir[0] = '\0';
X    name[0] = '\0';
X    username[0] = '\0';
X
X    /* search for the start of the file name */
X
X    for ((*start) = point;
X	((0 < (*start)) &&
X	    (line[(*start) - 1] != ' '));
X	(*start)--);
X
X    /* search for the end of the file name */
X
X    for ((*tail) = point - 1;
X	(((*tail) < (length - 1)) &&
X	    (line[(*tail) + 1] != ' '));
X	(*tail)++);
X
X    /* search for the middle of the file name */
X
X    for ((*middle) = (*tail) + 1;
X	((0 < (*middle)) &&
X	    (line[(*middle) - 1] != '/') &&
X	    (line[(*middle) - 1] != ' '));
X	(*middle)--);
X
X    /* copy path from line to dir */
X
X    /* what base path */
X
X    newstart = (*start);
X
X    if ((line[newstart] == '~') &&
X	((newstart + 1) < length) &&
X	(line[newstart + 1] == '/'))
X    {
X	/* "~/" means use the value of $HOME */
X
X	newstart++;
X	strcpy(dir, homedir);
X    }
X    else if (line[newstart] == '~')
X    {
X	/* "~username" means use the users login directory */
X
X	/* search for the end of the user name */
X
X	for ((*userend) = newstart,
X	    punlen = 0;
X	    (((*userend) < (length - 1)) &&
X		(line[(*userend) + 1] != ' ') &&
X		(line[(*userend) + 1] != '/'));
X	    (*userend)++,
X	    punlen++);
X
X	/* make middle point to middle */
X
X	if ((*start) == (*middle))
X	{
X	    (*middle) = (*start) + punlen + 1;
X	}
X
X	/* extract partial user name from line */
X
X	strncpy(pun, &line[newstart + 1], punlen);
X	pun[punlen] = '\0';
X
X	/* search passwd file for partial match */
X
X	for (userpwd = getpwent();
X	    userpwd != NULL;
X	    userpwd = getpwent())
X	{
X	    if ((punlen <= strlen(userpwd->pw_name)) &&
X		(strncmp(pun, userpwd->pw_name, punlen) == 0))
X	    {
X
X		/* we have a partial match, record it */
X
X		if (strlen(dir) == 0)
X		{
X		    newstart = (*userend) + 1;
X		    strcpy(dir, userpwd->pw_dir);
X		    strcpy(username, userpwd->pw_name);
X		}
X		else
X		{
X		    /* second partial match, forget the first one. */
X
X		    newstart = (*start);
X		    dir[0] = '\0';
X		    username[0] = '\0';
X		    return (FALSE);
X		}
X
X	    }
X	}
X	setpwent();
X    }
X    else if ((line[newstart] == '.') &&
X	    ((newstart + 1) < length) &&
X	(line[newstart + 1] == '/'))
X    {
X	/* if it's "./" can't deal with it so leave it alone  */
X
X	return (FALSE);
X    }
X    else if ((line[newstart] == '.') &&
X	    ((newstart + 1) < length) &&
X	    (line[newstart + 1] == '.') &&
X	    ((newstart + 2) < length) &&
X	(line[newstart + 2] == '/'))
X    {
X	/* if it's "../" can't deal with it so leave it alone  */
X
X	return (FALSE);
X    }
X    else if (line[newstart] != '/')
X    {
X	/* ile can only handle an absolute path so leave it alone */
X
X	return (FALSE);
X    }
X
X    /* add on the rest of the path */
X
X    dirlen = strlen(dir);
X    for (i = 0; i < ((*middle) - newstart); i++)
X    {
X	dir[dirlen + i] = line[newstart + i];
X    }
X    dir[dirlen + i] = '\0';
X
X    /* copy file name from line to name */
X
X    for (i = 0; i < ((*tail) - (*middle) + 1); i++)
X    {
X	name[i] = line[(*middle) + i];
X    }
X    name[i] = '\0';
X
X    return (TRUE);
X}
X/*------------------------------------------------------------------*/
X/*
XPerform file name completion. Put the full path and file name in the
Xline.
X*/
Xvoid
Xcomplete_file_full(ch)
X    char ch;
X{
X    char dir[10 * (MAXNAMLEN + 1)];
X    char name[MAXNAMLEN + 1];
X    char username[USER_NAME_SIZE + 1];
X
X    char newline[BUFFER_SIZE];
X    int newlength;
X    int newpoint;
X
X    int userend;
X    int start;
X    int middle;
X    int tail;
X
X    int i;
X
X    /* get the path and file name in the line */
X
X    if (get_dir_and_name(dir, name, username, &userend, &start, &middle, &tail))
X    {
X
X	/* complete the file name if possible */
X
X	complete_file_name(dir, name);
X
X	/* create a new line */
X
X	/* start with the line prefix */
X
X	strncpy(newline, line, start);
X	newline[start] = '\0';
X
X	/* add in the new path */
X
X	strcat(newline, dir);
X
X	/* stick in the new file name */
X
X	strcat(newline, name);
X	newpoint = strlen(newline);
X
X	/* finish with the line postfix */
X
X	strncat(newline, &line[tail + 1], (length - tail - 1));
X	newlength = strlen(newline);
X
X	/* display the new line */
X
X	clearline('\0');
X
X	point = newpoint;
X	length = newlength;
X	strncpy(line, newline, newlength);
X
X	echoline(line, length);
X
X	for (i = point; i < length; i++)
X	{
X	    backspace(line[i]);
X	}
X    }
X    else
X    {
X	bell('\0');
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XPerform file name completion in much the same style as csh.
X*/
Xvoid
Xcomplete_file(ch)
X    char ch;
X{
X    void insert();
X
X    char dir[10 * (MAXNAMLEN + 1)];
X    char name[MAXNAMLEN + 1];
X    char username[USER_NAME_SIZE + 1];
X
X    int userend;
X    int start;
X    int middle;
X    int tail;
X
X    char newline[BUFFER_SIZE];
X    int newlength;
X    int newpoint;
X
X    int userlen;
X    int len;
X
X    int i;
X
X    /* get the path and file name in the line */
X
X    if (get_dir_and_name(dir, name, username, &userend, &start, &middle, &tail))
X    {
X	/* how long is the user name */
X
X	userlen = strlen(username);
X
X	/* complete the file name if possible */
X
X	complete_file_name(dir, name);
X	/* create a new line */
X
X	/* start with the line prefix */
X
X	strncpy(newline, line, start);
X	newline[start] = '\0';
X
X	/* add in the new username */
X
X	if (userlen != 0)
X	{
X	    /* put in new user name */
X
X	    strcat(newline, "~");
X	    strcat(newline, username);
X	    len = strlen(newline);
X
X	    /* put in the existing path */
X
X	    strncat(newline, &line[userend + 1], middle - userend - 1);
X	    newline[len + (middle - userend - 1)] = '\0';
X	}
X	else
X	{
X	    /* put in the existing path */
X
X	    len = strlen(newline);
X	    strncat(newline, &line[start], middle - start);
X	    newline[len + (middle - start)] = '\0';
X	}
X
X	/* stick in the new file name */
X
X	strcat(newline, name);
X	newpoint = strlen(newline);
X
X	/* finish with the line postfix */
X
X	strncat(newline, &line[tail + 1], (length - tail - 1));
X	newlength = strlen(newline);
X
X	/* display the new line */
X
X	clearline('\0');
X
X	point = newpoint;
X	length = newlength;
X	strncpy(line, newline, newlength);
X
X	echoline(line, length);
X
X	for (i = point; i < length; i++)
X	{
X	    backspace(line[i]);
X	}
X    }
X    else
X    {
X	bell('\0');
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XList the names of files that start with the directory path and
Xfile name under the cursor.
X*/
Xvoid
Xshow_files(ch)
X    char ch;
X{
X    static char divider[] = "----------";
X
X    void retype_line();
X
X    char dir[10 * (MAXNAMLEN + 1)];
X    char name[MAXNAMLEN + 1];
X    char username[USER_NAME_SIZE + 1];
X
X    int userend;
X    int start;
X    int middle;
X    int tail;
X
X    if (get_dir_and_name(dir, name, username, &userend, &start, &middle, &tail))
X    {
X
X	fputs(ccr, stdout);
X	fputs(cnl, stdout);
X
X	fputs(divider, stdout);
X
X	fputs(ccr, stdout);
X	fputs(cnl, stdout);
X
X	list_file_names(dir, name);
X
X	fputs(ccr, stdout);
X	fputs(cnl, stdout);
X
X	fputs(divider, stdout);
X
X	fputs(ccr, stdout);
X	fputs(cnl, stdout);
X
X	retype_line('\0');
X    }
X    else
X    {
X	bell('\0');
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XRing the bell on the terminal.
X*/
Xvoid
Xbell(ch)
X    char ch;
X{
X    fputs(cbl, stdout);
X}
X/*------------------------------------------------------------------*/
X/*
XPass characters to the slave. Don't mess with them at all.
X*/
Xvoid
Xpass(ch)
X    char ch;
X{
X    int cc;
X
X    cc = write(master_pty, &ch, 1);
X}
X/*------------------------------------------------------------------*/
X/*
XInsert a character at point in the line buffer. While we are at it
Xupdate the display to show the insertion.
X*/
Xvoid
Xinsert(ch)
X    char ch;
X{
X    int i;
X
X    if (length < (BUFFER_SIZE - 2))
X    {
X
X	/* display the character */
X
X	echo(ch);
X
X	/* redisplay the rest of the line */
X
X	echoline(&line[point], (length - point));
X
X	/* move the characters in the line buffer */
X	/* and put the cursor back at point */
X
X	for (i = length; i > point; i--)
X	{
X	    line[i] = line[i - 1];
X	    backspace(line[i]);
X	}
X
X	/* add the character to the line buffer */
X	/* and increment point and length */
X
X	line[point] = ch;
X	length++;
X	point++;
X    }
X    else
X    {
X	bell('\0');
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XTranspose the letter under the cursor and the letter immediately to
Xthe left of the cursor.
X*/
Xvoid
Xtranspose_chars(ch)
X    char ch;
X{
X    char tch;
X
X    if ((0 < point) && (point < length))
X    {
X	/* first, update the display */
X
X	backspace(line[point]);
X
X	echo(line[point]);
X	echo(line[point - 1]);
X
X	/* now swap the chars in the line buffer */
X
X	tch = line[point];
X	line[point] = line[point - 1];
X	line[point - 1] = tch;
X
X	/* point moved forward one char */
X
X	point++;
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XDelete a character at point in the line buffer. While we are at it
Xupdate the display to reflect the deletion.
X*/
Xvoid
Xdelete_char_under(ch)
X    char ch;
X{
X    int i;
X
X    if (point < length)
X    {
X
X	/* clear to the end of the line */
X
X	cleartoend();
X
X	/* retype the rest of the line */
X
X	echoline(&line[point + 1], (length - point - 1));
X
X	/* build the new line */
X
X	for (i = point + 1; i < length; i++)
X	{
X	    line[i - 1] = line[i];
X	    backspace(line[i]);
X	}
X
X	length--;
X
X	if (point > length)
X	{
X	    point = length;
X	}
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XDelete the character to the left of point in the line buffer. While we
Xare at it update the display to reflect the deletion.
X*/
Xvoid
Xdelete_char(ch)
X    char ch;
X{
X    int i;
X
X    if (point > 0)
X    {
X	/* move the cursor left one character */
X
X	backspace(line[point - 1]);
X
X	/* clear to the end of the line */
X
X	cleartoend();
X
X	/* retype the rest of the line */
X
X	echoline(&line[point], (length - point));
X
X	/* build the new line */
X
X	for (i = point; i < length; i++)
X	{
X	    line[i - 1] = line[i];
X	    backspace(line[i]);
X	}
X
X	length--;
X	point--;
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XBind the edit vector to quote_edit so that the next character
Xwill be placed in the line buffer.
X*/
Xvoid
Xquote(ch)
X    char ch;
X{
X    edit = quote_edit;
X}
X/*------------------------------------------------------------------*/
X/*
XThe next character will select an action from action_table[1]
X*/
Xvoid
Xescape_1(ch)
X    char ch;
X{
X    edit = edit_1;
X}
X/*------------------------------------------------------------------*/
X/*
XThe next character will select an action from action_table[2]
X*/
Xvoid
Xescape_2(ch)
X    char ch;
X{
X    edit = edit_2;
X}
X/*------------------------------------------------------------------*/
X/*
XThe next character will select an action from action_table[3]
X*/
Xvoid
Xescape_3(ch)
X    char ch;
X{
X    edit = edit_3;
X}
X/*------------------------------------------------------------------*/
X/*
XDelete the word to the left of the cursor.
X*/
Xvoid
Xdelete_word(ch)
X    char ch;
X{
X    int i;
X    int old;
X
X    if (length > 0)
X    {
X	/* find the new deletion point */
X
X	old = point;
X
X	/* first skip over any delimiters */
X
X	for (; (point > 0) && (delimit[line[point - 1]]); point--)
X	{
X	    backspace(line[point - 1]);
X	}
X
X	/* now delete until we find a delimiter */
X
X	for (; (point > 0) && (!delimit[line[point - 1]]); point--)
X	{
X	    backspace(line[point - 1]);
X	}
X
X	/* clear to the end of the line */
X
X	cleartoend();
X
X	/* retype the rest of the line */
X
X	echoline(&line[old], (length - old));
X
X	/* construct the new line */
X
X	for (i = 0; i < (length - old); i++)
X	{
X	    line[point + i] = line[old + i];
X	    backspace(line[point + i]);
X	}
X
X	/* update the length */
X
X	length = length - (old - point);
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XGo forward one word.
X*/
Xvoid
Xforward_word(ch)
X    char ch;
X{
X    if (length > 0)
X    {
X	/* first skip any delimiters */
X
X	for (; (point < length) && (delimit[line[point]]); point++)
X	{
X	    echo(line[point]);
X	}
X
X	/* now skip until we find a delimiter */
X
X	for (; (point < length) && (!delimit[line[point]]); point++)
X	{
X	    echo(line[point]);
X	}
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XLower case the word.
X*/
Xvoid
Xlower_word(ch)
X    char ch;
X{
X    if (length > 0)
X    {
X	/* first skip any delimiters */
X
X	for (; (point < length) && (delimit[line[point]]); point++)
X	{
X	    echo(line[point]);
X	}
X
X	/* now skip until we find a delimiter */
X
X	for (; (point < length) && (!delimit[line[point]]); point++)
X	{
X	    if ((line[point] >= 'A') && (line[point] <= 'Z'))
X	    {
X		line[point] = line[point] - 'A' + 'a';
X		echo(line[point]);
X	    }
X	    else
X	    {
X		echo(line[point]);
X	    }
X	}
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XUpper case the word.
X*/
Xvoid
Xupper_word(ch)
X    char ch;
X{
X    if (length > 0)
X    {
X	/* first skip any delimiters */
X
X	for (; (point < length) && (delimit[line[point]]); point++)
X	{
X	    echo(line[point]);
X	}
X
X	/* now skip until we find a delimiter */
X
X	for (; (point < length) && (!delimit[line[point]]); point++)
X	{
X	    if ((line[point] >= 'a') && (line[point] <= 'z'))
X	    {
X		line[point] = line[point] - 'a' + 'A';
X		echo(line[point]);
X	    }
X	    else
X	    {
X		echo(line[point]);
X	    }
X	}
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XCapitalize the word.
X*/
Xvoid
Xcapitalize_word(ch)
X    char ch;
X{
X    if (length > 0)
X    {
X	/* first skip any delimiters */
X
X	for (; (point < length) && (delimit[line[point]]); point++)
X	{
X	    echo(line[point]);
X	}
X
X	/* now skip until we find a delimiter */
X
X	if ((point < length) && (!delimit[line[point]]))
X	{
X	    if ((line[point] >= 'a') && (line[point] <= 'z'))
X	    {
X		line[point] = line[point] - 'a' + 'A';
X		echo(line[point]);
X	    }
X	    else
X	    {
X		echo(line[point]);
X	    }
X	}
X	point++;
X
X	for (; (point < length) && (!delimit[line[point]]); point++)
X	{
X	    if ((line[point] >= 'A') && (line[point] <= 'Z'))
X	    {
X		line[point] = line[point] - 'A' + 'a';
X		echo(line[point]);
X	    }
X	    else
X	    {
X		echo(line[point]);
X	    }
X	}
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XGo backward one word.
X*/
Xvoid
Xbackward_word(ch)
X    char ch;
X{
X    if (length > 0)
X    {
X	/* first backspace over any delimiters */
X
X	for (; (point > 0) && (delimit[line[point - 1]]); point--)
X	{
X	    backspace(line[point - 1]);
X	}
X
X	/* now backspace until we find a delimiter */
X
X	for (; (point > 0) && (!delimit[line[point - 1]]); point--)
X	{
X	    backspace(line[point - 1]);
X	}
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XMove the cursor to the start of the line.
X*/
Xvoid
Xstart_of_line(ch)
X    char ch;
X{
X    int i;
X
X    if (length > 0)
X    {
X	for (i = 0; i < point; i++)
X	{
X	    backspace(line[i]);
X	}
X	point = 0;
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XMove the cursor one character to the left.
X*/
Xvoid
Xbackward_char(ch)
X    char ch;
X{
X    if ((length > 0) && (point > 0))
X    {
X	backspace(line[point - 1]);
X	point--;
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XMove the cursor to the right of the last character on the line.
X*/
Xvoid
Xend_of_line(ch)
X    char ch;
X{
X    if ((length > 0) && (point < length))
X    {
X	echoline(&line[point], (length - point));
X	point = length;
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XMove the cursor one character to the right.
X*/
Xvoid
Xforward_char(ch)
X    char ch;
X{
X    if ((length > 0) && (point < length))
X    {
X	echo(line[point]);
X	point++;
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XAdd a line to the history buffer and pass it to the child process
Xas input.
X*/
Xvoid
Xadd_to_history(ch)
X    char ch;
X{
X    /* Put the line in the history buffer. Make here point to the current
X     * line. And increment head to point to the next history slot. */
X
X    /* If the current line is identical to the current history line, don't
X     * add it. */
X
X    /* don't save blank lines */
X
X    int prev;
X    int cc;
X
X    if ((head - 1) < 0)
X    {
X	prev = HISTORY_SIZE - 1;
X    }
X    else
X    {
X	prev = head - 1;
X    }
X
X    if ((length != 0) &&
X	((length != hist[prev].length) ||
X	    (strncmp(hist[prev].line, line, length) != 0)))
X    {
X	/* set the length of the entry */
X
X	hist[head].length = length;
X
X	/* make sure there is enough storage for the new line */
X
X	if (hist[head].line == NULL)
X	{
X	    if ((hist[head].line = (char *) malloc(length)) == NULL)
X	    {
X		perror("ile");
X	    }
X	}
X	else
X	{
X	    if ((hist[head].line =
X		    (char *) realloc(hist[head].line, length))
X		== NULL)
X	    {
X		perror("ile");
X	    }
X	}
X
X	(void) strncpy(hist[head].line, line, length);
X
X	head = (head + 1) % HISTORY_SIZE;
X
X	if (hist[head].line != NULL)
X	{
X	    free(hist[head].line);
X	    hist[head].length = 0;
X	    hist[head].line = NULL;
X	}
X    }
X
X    /* reset here */
X
X    here = head;
X
X    /* Echo a carriage return or a newline as a cr-nl sequence. Then send the
X     * line to the child process. Finally, clear the buffer for later use. */
X
X    fputs(ccr, stdout);
X    fputs(cnl, stdout);
X
X    line[length] = nl;
X    length++;
X
X    cc = write(master_pty, line, length);
X
X    point = 0;
X    length = 0;
X
X}
X/*------------------------------------------------------------------*/
X/*
XErase the entire line.
X*/
Xvoid
Xerase_line(ch)
X    char ch;
X{
X    /* remove any text from the display */
X
X    clearline(ch);
X
X    /* nothing in the line buffer */
X
X    point = 0;
X    length = 0;
X
X    /* reset here */
X
X    here = head;
X
X}
X/*------------------------------------------------------------------*/
X/*
XErase from the current cursor position to the end of the line.
X*/
Xvoid
Xerase_to_end_of_line(ch)
X    char ch;
X{
X    if ((length > 0) && (point < length))
X    {
X	cleartoend();
X	length = point;
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XRetype the current contents of the edit buffer.
X*/
Xvoid
Xretype_line(ch)
X    char ch;
X{
X    int i;
X
X    fputs(ccr, stdout);
X    fputs(cnl, stdout);
X
X    echoline(line, length);
X
X    for (i = point; i < length; i++)
X    {
X	backspace(line[i]);
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XGo to the the next entry in the history buffer and display it.
XIf we are past the last history entry, then beep.
X*/
Xvoid
Xforward_history(ch)
X    char ch;
X{
X    if (here != head)
X    {
X	clearline(ch);
X
X	here = (here + 1) % HISTORY_SIZE;
X	length = hist[here].length;
X	point = length;
X
X	strncpy(line, hist[here].line, length);
X	echoline(line, length);
X    }
X    else
X    {
X	bell('\0');
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XGo back one entry in the history buffer and display it. If we are
Xalready at the last entry, then beep.
X*/
Xvoid
Xbackward_history(ch)
X    char ch;
X{
X    int prev;
X
X    prev = here - 1;
X
X    if (prev < 0)
X    {
X	prev = HISTORY_SIZE - 1;
X    }
X
X    if (hist[prev].line != NULL)
X    {
X	clearline(ch);
X
X	here = prev;
X	length = hist[here].length;
X	point = length;
X
X	strncpy(line, hist[here].line, length);
X	echoline(line, length);
X    }
X    else
X    {
X	bell('\0');
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XThe following routines are utility routines used by the editing
Xroutines.
X*/
X/*------------------------------------------------------------------*/
X/*
XClear to the end of the current input line.
X*/
Xvoid
Xcleartoend()
X{
X    /* send the clear character */
X
X    fputs(cce, stdout);
X
X    /* send somes nulls for padding */
X
X    fputs("\0\0\0\0", stdout);
X}
X/*------------------------------------------------------------------*/
X/*
XClear the input line. Backspace to the start of the line. Then clear
Xto the end of the line.
X*/
Xvoid
Xclearline(ch)
X    char ch;
X{
X    int i;
X
X    for (i = 0; i < point; i++)
X    {
X	backspace(line[i]);
X    }
X
X    cleartoend();
X}
X/*------------------------------------------------------------------*/
X/*
XEcho a character. Not all characters are created equal. Control characters
Xare echoed in ^X form. So they take up two character positions instead of
Xthe normal 1 character position.
X*/
Xvoid
Xecho(ch)
X    char ch;
X{
X    /* how should we echo the char? */
X
X    if (ch < ' ')
X    {
X	fputc('^', stdout);
X	fputc('@' + ch, stdout);
X    }
X    else
X    {
X	fputc(ch, stdout);
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XEcho a line. Print a whole line with control characters printed in
X^X form.
X*/
Xvoid
Xecholine(line, length)
X    char *line;
X    int length;
X{
X    int i;
X
X    for (i = 0; i < length; i++)
X    {
X	echo(*line++);
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XBackspace over a character. Generate enough bs characters to backspace
Xover any character.
X*/
Xvoid
Xbackspace(ch)
X    char ch;
X{
X    if (ch < ' ')
X    {
X	fputs(cle, stdout);
X	fputs(cle, stdout);
X    }
X    else
X    {
X	fputs(cle, stdout);
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XAdd any character to the line buffer.
X*/
Xvoid
Xquote_edit(ch)
X    char ch;
X{
X    insert(ch);
X
X    edit = edit_0;
X}
X/*------------------------------------------------------------------*/
X/*
XGiven a character and an action table number either execute the
Xaction or pass the string to (*edit)(ch)
X*/
Xvoid
Xdispatch(table, ch)
X    int table;
X    char ch;
X{
X    char *cptr;
X
X    switch (action_table[table][ch].flag)
X    {
X    case is_action:
X
X	(*(action_table[table][ch].aors.action)) (ch);
X
X	break;
X
X    case is_string:
X
X	cptr = action_table[table][ch].aors.string;
X	while ((*cptr) != '\0')
X	{
X	    (*edit) (*cptr);
X	    cptr++;
X	}
X
X	break;
X    }
X}
X/*------------------------------------------------------------------*/
X/*
XSelect an action from action_table[3] and execute it.
X*/
Xvoid
Xedit_3(ch)
X    char ch;
X{
X    /* reset so that next input is handled by edit_0 unless over ridden by
X     * the action. */
X
X    edit = edit_0;
X    dispatch(3, ch);
X    fflush(stdout);
X}
X/*------------------------------------------------------------------*/
X/*
XSelect an action from action_table[2] and execute it.
X*/
Xvoid
Xedit_2(ch)
X    char ch;
X{
X    /* reset so that next input is handled by edit_0 unless over ridden by
X     * the action. */
X
X    edit = edit_0;
X    dispatch(2, ch);
X    fflush(stdout);
X}
X/*------------------------------------------------------------------*/
X/*
XSelect an action from action_table[1] and execute it.
X*/
Xvoid
Xedit_1(ch)
X    char ch;
X{
X    /* reset so that next input is handled by edit_0 unless over ridden by
X     * the action. */
X
X    edit = edit_0;
X    dispatch(1, ch);
X    fflush(stdout);
X}
X/*------------------------------------------------------------------*/
X/*
XSelect an action from action_table[0] and execute it.
X*/
Xvoid
Xedit_0(ch)
X    char ch;
X{
X    dispatch(0, ch);
X    fflush(stdout);
X}
X/*------------------------------------------------------------------*/
X/*
XInput line editor.
X
XInitialize the world. Then loop forever using select to wait for
Xcharacters to be available from either stdin or from master_pty.
XWhen characters are available, pass them on after doing any needed
Xediting.
X*/
Xvoid
Xile()
X{
X    /* general purpose integer variable */
X
X    int i;
X
X    /* arguments for read and write calls */
X
X    char buffer[BUFFER_SIZE];
X    int cc;
X
X    /* current slave_tty parameters */
X
X    struct sgttyb slave_params;
X
X    /* Arguments for select call */
X
X    int nfds;
X    int width;
X    int readfds;
X
X    /* what to do if the child dies */
X
X    (void) signal(SIGCHLD, cleanup);
X    (void) signal(SIGSEGV, cleanup);
X    (void) signal(SIGBUS, cleanup);
X
X    /* initialize tty status flags */
X
X    /* get current params */
X
X    (void) ioctl(READ, TIOCGETP, &tty_params);
X
X    /* set slave params */
X    /* the slave must not echo characters */
X
X    {
X	struct sgttyb params;
X
X	params = tty_params;
X	params.sg_flags &= ~ECHO;
X	(void) ioctl(slave_tty, TIOCSETP, &params);
X    }
X
X    /* set raw mode */
X    {
X	struct sgttyb params;
X
X	params = tty_params;
X	params.sg_flags = EVENP | ODDP | RAW;
X	(void) ioctl(READ, TIOCSETP, &params);
X    }
X
X    /* get current mode */
X
X    (void) ioctl(READ, TIOCLGET, &tty_mode);
X
X    /* set it on slave */
X
X    (void) ioctl(slave_tty, TIOCLSET, &tty_mode);
X
X    /* set new mode on tty */
X
X    {
X	int mode;
X	mode = LNOFLSH | LDECCTQ | LLITOUT;
X	(void) ioctl(READ, TIOCLSET, &mode);
X    }
X
X    /* get descriptor table size */
X
X    width = getdtablesize();
X    if (width > 32)
X    {
X	width = 32;
X    }
X
X    /* set initial edit function */
X
X    edit = edit_0;
X
X    /* initialize line buffer */
X
X    point = 0;
X    length = 0;
X
X    /* initialize history buffer */
X
X    head = 0;
X    here = 0;
X
X    for (i = 0; i < HISTORY_SIZE; i++)
X    {
X	hist[i].length = 0;
X	hist[i].line = NULL;
X    }
X
X    for (;;)
X    {
X	readfds = (1 << READ) | (1 << master_pty);
X
X	/* wait for input from stdin or master_pty */
X
X	nfds = select(width, &readfds, NULL, NULL, NULL);
X
X	if (nfds == -1)		/* an exception has occured */
X	{
X	    perror("ile");
X	    cleanup();
X	}
X	else if ((nfds > 0) && (readfds != 0))	/* something to read */
X	{
X	    if ((readfds & (1 << master_pty)) != 0)
X	    {
X		cc = read(master_pty, buffer, BUFFER_SIZE);
X		cc = write(WRITE, buffer, cc);
X	    }
X
X	    if ((readfds & (1 << READ)) != 0)
X	    {
X		/* read the pending characters. */
X
X		cc = read(READ, buffer, BUFFER_SIZE);
X
X		/* if the slave is in RAW or CBREAK mode then we should not
X		 * mess with its input characters */
X
X		(void) ioctl(slave_tty, TIOCGETP, &slave_params);
X
X		if ((slave_params.sg_flags & (RAW | CBREAK)) != 0)
X		{
X		    edit = pass;
X		}
X		else if ((char *) edit == (char *) pass)
X		{
X		    edit = edit_0;
X		}
X
X		/* if the slave has set ECHO, then we must clear it */
X
X		if ((slave_params.sg_flags & ECHO) != 0)
X		{
X		    slave_params.sg_flags &= ~ECHO;
X		    (void) ioctl(slave_tty, TIOCSETP, &slave_params);
X
X		}
X
X		/* decide what to do with the characters. */
X
X		for (i = 0; i < cc; i++)
X		{
X		    (*edit) (buffer[i]);
X		}
X	    }
X
X	}
X    }
X
X}
X/*------------------------------------------------------------------*/
X/*
XThe child process.
X
XMake the pty the processes controling terminal. Bind the pty to
Xstdin, stdout, and stderr. Then exec the users program.
X*/
Xvoid
Xchild(argv)
X    char *argv[];
X{
X    /* close all file descriptors */
X
X    (void) close(READ);
X    (void) close(WRITE);
X    (void) close(ERROR);
X    (void) close(slave_tty);
X
X    /* get rid of controlling terminal */
X
X    {
X	int tty;
X
X	if ((tty = open("/dev/tty", O_RDWR) == -1) ||
X	    (ioctl(0, TIOCNOTTY, 0) == -1) ||
X	    (close(tty) == -1))
X	{
X	    perror("ile");
X	}
X    }
X
X    /* open the tty again */
X    /* this makes the pty the controlling terminal */
X
X    if ((slave_tty = open(ttydev, O_RDWR)) == -1)
X    {
X	perror("ile");
X    }
X
X    /* slave_tty is now stdin */
X
X    /* bind slave_tty to stdout */
X
X    (void) dup2(slave_tty, WRITE);
X
X    /* bind slave_tty to stderr */
X
X    (void) dup2(slave_tty, ERROR);
X
X    /* close master_pty descriptor */
X
X    (void) close(master_pty);
X
X    /* Fire up application program. If no program name is given then fire up
X     * csh. */
X
X    if (argv[1] == NULL)
X    {
X	execlp("csh", "csh", 0);
X    }
X    else if (*argv[1] == '-')
X    {
X	if (argv[2] == NULL)
X	{
X	    execlp("csh", "csh", 0);
X	}
X	else
X	{
X	    execvp(argv[2], &argv[2]);
X	}
X    }
X    else
X    {
X	execvp(argv[1], &argv[1]);
X    }
X
X    /* this executes if exec fails */
X
X    perror("ile");
X    exit(1);
X}
X/*------------------------------------------------------------------*/
X/*
XSet up default key bindings and delimeters.
X*/
Xvoid
Xdefault_bindings()
X{
X    int i;
X
X    /* clear delimiter vector and the action table */
X
X    for (i = 0; i < CHAR_SET_SIZE; i++)
X    {
X	delimit[i] = FALSE;
X
X	action_table[0][i].aors.action = insert;
X	action_table[1][i].aors.action = bell;
X	action_table[2][i].aors.action = bell;
X	action_table[3][i].aors.action = bell;
X
X	action_table[0][i].flag = is_action;
X	action_table[1][i].flag = is_action;
X	action_table[2][i].flag = is_action;
X	action_table[3][i].flag = is_action;
X    }
X
X    /* default delimiters */
X
X    delimit[' '] = TRUE;	/* blank */
X    delimit['/'] = TRUE;	/* slash */
X    delimit['.'] = TRUE;	/* dot */
X    delimit['-'] = TRUE;	/* dash */
X
X    /* default action_table[0] */
X
X    action_table[0][CA].aors.action = start_of_line;
X    action_table[0][CB].aors.action = backward_char;
X    action_table[0][CE].aors.action = end_of_line;
X    action_table[0][CF].aors.action = forward_char;
X    action_table[0][CK].aors.action = erase_to_end_of_line;
X    action_table[0][CU].aors.action = erase_line;
X    action_table[0][CL].aors.action = retype_line;
X    action_table[0][CN].aors.action = forward_history;
X    action_table[0][CP].aors.action = backward_history;
X    action_table[0][CT].aors.action = transpose_chars;
X    action_table[0][CV].aors.action = quote;
X    action_table[0][del].aors.action = delete_char;
X    action_table[0][esc].aors.action = escape_1;
X    action_table[0][cr].aors.action = add_to_history;
X    action_table[0][nl].aors.action = add_to_history;
X    action_table[0][CX].aors.action = delete_char_under;
X
X    action_table[0][CC].aors.action = pass;
X    action_table[0][CD].aors.action = pass;
X    action_table[0][CQ].aors.action = pass;
X    action_table[0][CS].aors.action = pass;
X    action_table[0][CZ].aors.action = pass;
X
X    /* default action_table[1] ^[ c */
X
X    action_table[1]['b'].aors.action = backward_word;
X    action_table[1]['f'].aors.action = forward_word;
X    action_table[1][del].aors.action = delete_word;
X    action_table[1]['u'].aors.action = upper_word;
X    action_table[1]['l'].aors.action = lower_word;
X    action_table[1]['c'].aors.action = capitalize_word;
X    action_table[1]['['].aors.action = escape_2;
X    action_table[1][esc].aors.action = complete_file;
X    action_table[1]['s'].aors.action = complete_file_full;
X    action_table[1]['d'].aors.action = show_files;
X
X    /* default action_table[2] ^[ [ */
X
X    action_table[2]['A'].aors.action = backward_history;
X    action_table[2]['B'].aors.action = forward_history;
X    action_table[2]['C'].aors.action = forward_char;
X    action_table[2]['D'].aors.action = backward_char;
X
X}
X/*------------------------------------------------------------------*/
X/*
XReturn a character or EOF. This routine reads characters from input
Xand converts them into a character using the following rules.
X
XThe character may be a single character, a control
Xcharacter indicated by ^x, an octal number starting with \, or an
Xescaped character indictated by \x.
X*/
Xint
Xscan_char(input)
X    FILE *input;
X{
X    char ch;
X    int value;
X
X    ch = fgetc(input);
X    switch (ch)
X    {
X    case '^':
X
X	/* it is a control character */
X
X	for (ch = fgetc(input); '@' <= ch; ch = ch - '@');
X
X	break;
X
X    case '\\':
X
X	/* octal or an escaped character? */
X
X	ch = fgetc(input);
X	if (('0' <= ch) && (ch <= '7'))
X	{
X
X	    /* its an octal number */
X
X	    value = 0;
X	    while (('0' <= ch) && (ch <= '7'))
X	    {
X		value = (value * 8) + (ch - '0');
X		ch = fgetc(input);
X	    }
X	    ungetc(ch, input);
X
X	    ch = value & 0177;	/* make sure it is in range */
X	}
X	else
X	{
X	    /* its an escaped character */
X
X	    ch = fgetc(input);
X	}
X
X	break;
X
X    case '\n':
X
X	/* the real end of the line */
X
X	ch = EOL;
X
X	break;
X
X    default:
X
X	/* it is just itself */
X
X	break;
X    }
X
X    return (ch);
X
X}
X/*------------------------------------------------------------------*/
X/*
XSet key bindings and delimiters from the users file.
X*/
Xvoid
Xuser_bindings(file)
X    FILE *file;
X{
X
X#define NAME_SIZE 40
X
X    static struct action_name_table
X    {
X	char *name;
X	void (*action) ();
X    } action_name_table[] =
X    {
X	{
X	    "complete_file_full", complete_file_full
X	},
X	{
X	    "complete_file", complete_file
X	},
X	{
X	    "show_files", show_files
X	},
X	{
X	    "bell", bell
X	},
X	{
X	    "pass", pass
X	},
X	{
X	    "insert", insert
X	},
X	{
X	    "transpose_chars", transpose_chars
X	},
X	{
X	    "delete_char", delete_char
X	},
X	{
X	    "delete_char_under", delete_char_under
X	},
X	{
X	    "quote", quote
X	},
X	{
X	    "escape_1", escape_1
X	},
X	{
X	    "escape_2", escape_2
X	},
X	{
X	    "escape_3", escape_3
X	},
X	{
X	    "delete_word", delete_word
X	},
X	{
X	    "upper_word", upper_word
X	},
X	{
X	    "lower_word", lower_word
X	},
X	{
X	    "capitalize_word", capitalize_word
X	},
X	{
X	    "forward_word", forward_word
X	},
X	{
X	    "backward_word", backward_word
X	},
X	{
X	    "start_of_line", start_of_line
X	},
X	{
X	    "backward_char", backward_char
X	},
X	{
X	    "end_of_line", end_of_line
X	},
X	{
X	    "forward_char", forward_char
X	},
X	{
X	    "add_to_history", add_to_history
X	},
X	{
X	    "erase_line", erase_line
X	},
X	{
X	    "erase_to_end_of_line", erase_to_end_of_line
X	},
X	{
X	    "retype_line", retype_line
X	},
X	{
X	    "forward_history", forward_history
X	},
X	{
X	    "backward_history", backward_history
X	},
X	{
X	    "", NULL
X	}
X    };
X
X    char name[NAME_SIZE];
X
X    char ch;
X    int i;
X
X    int line;
X    int table;
X    int entry;
X
X    /* First clear the default delimiters */
X
X    for (i = 0; i < CHAR_SET_SIZE; i++)
X    {
X	delimit[ch] = FALSE;
X    }
X
X    /* Now read the delimiter characters */
X
X    while (((ch = fgetc(file)) != EOF) && (ch != '\n'))
X    {
X	delimit[ch] = TRUE;
X    }
X
X    line = 2;
X
X    /* Now read the character binding pairs */
X
X    while ((ch = fgetc(file)) != EOF)
X    {
X	switch (ch)
X	{
X	case '\n':
X
X	    /* skipping a blank line */
X	    line++;
X
X	    break;
X
X	case '0':
X	case '1':
X	case '2':
X	case '3':
X
X	    /* which table is this entry directed to? */
X
X	    table = ch - '0';
X
X	    /* get the character code */
X
X	    entry = scan_char(file);
X
X	    /* make sure the '=' is there */
X
X	    ch = fgetc(file);
X	    if (ch != '=')
X	    {
X		fprintf(stderr, "ile: \'=' missing on line %d\n", line);
X		exit(1);
X	    }
X
X	    /* collect the action name or string */
X
X	    for (ch = scan_char(file), i = 0;
X		(ch != EOL) && (i < (NAME_SIZE - 1));
X		ch = scan_char(file), i++)
X	    {
X		name[i] = ch;
X		name[i + 1] = '\0';
X	    }
X
X	    /* look it up in the action_name_table */
X
X	    for (i = 0;
X		(action_name_table[i].action != NULL) &&
X		(strcmp(name, action_name_table[i].name) != 0);
X		i++);
X
X	    /* if it was found, put it in the action array */
X
X	    if (action_name_table[i].action == NULL)
X	    {
X		/* must be a string */
X
X		action_table[table][entry].flag = is_string;
X		action_table[table][entry].aors.string =
X		    (char *) malloc(strlen(name) + 1);
X		strcpy(action_table[table][entry].aors.string, name);
X	    }
X	    else
X	    {
X		/* its an action */
X
X		action_table[table][entry].flag = is_action;
X		action_table[table][entry].aors.action =
X		    action_name_table[i].action;
X	    }
X
X	    line++;		/* count the line */
X
X	    break;
X
X	default:
X	    fprintf(stderr,
X		"\nile: error in initialization file on line %d\n",
X		line);
X	    exit(1);
X	    break;
X	}
X    }
X
X    fclose(file);
X}
X/*------------------------------------------------------------------*/
X/*
XInitialize key bindings and delimiters.
X*/
Xvoid
Xinitialize(argv)
X    char *argv[];
X{
X    FILE *file;
X    char name[BUFFER_SIZE];
X
X    /* set up the default bindings */
X
X    default_bindings();
X
X    /* Look for an initialization file. If it's there, load it. */
X
X    name[0] = '\0';
X    homedir = getenv("HOME");
X    strcpy(name, homedir);
X    strcat(name, "/.ilerc");
X
X    if ((argv[1] != NULL) &&
X	(*argv[1] == '-') &&
X	((file = fopen(argv[1] + 1, "r")) != NULL))
X    {
X	/* load the users bindings */
X
X	user_bindings(file);
X    }
X    else if (((file = fopen("./.ilerc", "r")) != NULL) ||
X	((file = fopen(name, "r")) != NULL))
X    {
X	user_bindings(file);
X    }
X}
X/*------------------------------------------------------------------*/
X/*
X*/
Xmain(argc, argv)
X    int argc;
X    char *argv[];
X{
X    /* Child process id */
X
X    int cpid;
X
X    /* identify yourself */
X
X    printf("Starting ILE...\n");
X
X    /* create the tty/pty pair */
X
X    getpty(&master_pty, &slave_tty);
X
X    /* get control sequences from termcap */
X
X    get_termcap();
X
X    /* initialize the dispatch vectors */
X
X    initialize(argv);
X
X    /* create the child process */
X
X    cpid = fork();
X
X    switch (cpid)
X    {
X    case 0:			/* child process */
X
X	child(argv);
X	break;
X
X    case -1:			/* fork failed */
X
X	perror("ile");
X	exit(1);
X	break;
X
X    default:			/* parent process */
X	ile();
X	break;
X    }
X
X}
SHAR_EOF
if test 48056 -ne "`wc -c < ile.c`"
then
    echo shar: error transmitting "ile.c" '(should have been 48056 characters)'
fi
fi
# end of shell archive
exit 0
-- 
Bob Pendleton @ Evans & Sutherland
UUCP Address:  {decvax,ucbvax,allegra}!decwrl!esunix!bpendlet
Alternate:     utah-cs!utah-gr!uplherc!esunix!bpendlet
        I am solely responsible for what I say.

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

From: Bob Pendleton <bpendlet at esunix.uucp>
Subject: ile man page
Date: 7 Jul 88 20:25:25 GMT
To:       unix-wizards at brl-sem.arpa


#! /bin/sh
# This is a shell archive, meaning:
# 1.  Remove everything above the #! /bin/sh line.
# 2.  Save the resulting test in a file
# 3.  Execute the file with /bin/sh (not csh) to create the files:
#
#ile.l
#
# Created by bpendlet (Bob Pendleton) on Thu Jul  7 14:15:44 MDT 1988
#
if test -f 'ile.l'
then
    echo shar: will not over-write existing file "ile.l"
else
echo extracting "ile.l"
sed 's/^X//' >ile.l <<'SHAR_EOF'
X.de EX		\"Begin example
X.ne 5
X.if n .sp 1
X.if t .sp .5
X.nf
X.in +.5i
X..
X.de EE
X.fi
X.in -.5i
X.if n .sp 1
X.if t .sp .5
X..
X.TH ILE 1 "5 May 1988" 
X.SH NAME
X.PP
Xile - An input line editor for UNIX (Input Line Editor)
X.PP
X.SH SYNTAX
X.PP
X\fBile\fP [-file/name\fP] \fP[prog arg1 arg2 ... argn\fP]
X.PP
X.SH DESCRIPTION
X.PP
XThe \fIile\fP program is an input line editor that provides an easier
Xto use history mechanism than the shell.
X.PP
XThe \fIile\fP program can be run as a simple shell around any program. It gives
Xany program an input line editing and a history mechanism. It can also be
Xrun around your favorite shell. When run around the shell \fIile\fP records the
Xinput to programs as well as input to the shell in its history buffer.
X
X\fIile\fP takes two optional command line arguments. The first argument is
Xthe name of an initialization file containing user defined key and
Xdelimiter bindings. The second argument is the name of a program to execute
Xand the command line arguments for that program. 
X.PP
XIf no initialization file
Xis given on the command line \fIile\fP first looks in \fI./.ilerc\fP and
Xthen in \fI$HOME/.ilerc\fP. If no initialization file is found \fIile\fP
Xprovides default values for delimiter and key bindings.
X.PP
XIf no program name is given on the command line \fIile\fP executes \fIcsh\fp.
X.PP
X.SH DEFAULT BINDINGS
X.PP
XNot everyone wants to have to figure out yet another initialization
Xfile format so \fIile\fP provides a complete set of default bindings for
Xall its operations.
X.PP
X.SH Delimiters
X.PP
XDelimiters are used in \fIile\fP to mark the beginnings and ends of words
Xfor the \fBforward_word\fP, \fBbackward_word\fP, and \fBdelete_word\fP
Xactions. The
Xdefault delimiters are ' ' (blank), '/' (slash), '.' (period), 
Xand '-' (dash). These were chosen because the author decided they were
X"natural" stopping characters in a UNIX environment.
X.PP
X.SH Keys
X.PP
XThe following table shows the default bindings of keys and key sequences
Xprovided by \fIile\fP. These are based on the emacs key bindings for
Xsimilar operations.
X.EX 0
X^A      - start_of_line
X^B      - backward_char
X^E      - end_of_line
X^F      - forward_char
X^K      - erase_to_end_of_line
X^L      - retype_line
X^N      - forward_history
X^P      - backward_history
X^V      - quote
X^T      - transpose_chars
Xdel     - delete_char
X^M      - add_to_history
X^J      - add_to_history
X^U      - erase_line
X^X      - delete_char_under
X
X^C      - pass
X^D      - pass
X^Q      - pass
X^S      - pass
X^Z      - pass
X
Xesc b   - backward_word
Xesc f   - forward_word
Xesc del - delete_word
Xesc esc - complete_file
Xesc s   - complete_file_full
Xesc d   - show_files
Xesc u   - upper_word
Xesc l   - lower_word
Xesc c   - capitalize_word
X
Xesc [ A - backward_history (up arrow)
Xesc [ B - forward_history  (down arrow)
Xesc [ C - forward_char     (right arrow)
Xesc [ D - backward_char    (left arrow)
X.EE
X
X.SH INITIALIZATION FILE
X.PP
XThe \fIile\fP initialization file has two parts. The first part is also the
Xfirst line of the file. This line contains the delimiter characters that
Xwill be used by the \fBforward_word\fP, \fBbackward_word\fP, 
Xand \fBdelete_word\fP actions.
XEach character on the line becomes a delimiter character.
X
XThe second part of the file is a list of table numbers, characters, and
Xactions or strings. \fIile\fP has 4 action tables. Each action 
Xtable contains an action or string
Xfor each possible character. \fIile\fP decides what to do with a character
Xby looking it up in the table and executing the action associated with the
Xcharacter or by passing the string one character at a time into \fIile\fP
Xas if it had been typed by the user. Normally only table 0 is used. But, the
X\fBescape\fP actions cause
Xthe next character to be looked up in a different table. The \fBescape\fP
Xactions make it possible to map multiple character sequences to actions.
X
XBy default, all entries in table 0 are bound to the \fBinsert\fP action, and
Xall entries in the other tables are bound to the \fBbell\fP action. User 
Xspecified bindings override these defaults. The following example is an 
Xinitialization file that sets up the same key and delimiter bindings as the
X\fIile\fP default bindings.
X.PP
X.SH Example \fI.ilerc\fP file
X.EX 0
X /.-
X
X0^A=start_of_line
X0^B=backward_char
X0^E=end_of_line
X0^F=forward_char
X0^K=erase_to_end_of_line
X0^L=retype_line
X0^N=forward_history
X0^P=backward_history
X0^V=quote
X0^T=transpose_chars
X0\\177=delete_char
X0^[=escape_1
X0^M=add_to_history
X0^J=add_to_history
X0^U=erase_line
X0^X=delete_char_under
X
X0^C=pass
X0^D=pass
X0^Q=pass
X0^S=pass
X0^Z=pass
X
X1b=backward_word
X1f=forward_word
X1\\177=delete_word
X1[=escape_2
X1^[=complete_file
X1s=complete_file_full
X1d=show_files
X1u=upper_word
X1l=lower_word
X1c=capitalize_word
X
X2A=backward_history
X2B=forward_history
X2C=forward_char
X2D=backward_char
X.EE
X.PP
XThe first character on each key binding line is the index of the table
Xto place the key binding in. Valid values for the index are 0, 1, 2, and 3.
X.PP
XThe second character on the line is either the character to bind or
Xan indicator that tells how to find out what character to bind. If the 
Xsecond character is any character besides '^' or '\\' then the action is
Xbound to that character.
X.PP
XIf the second character on the line is '^' then the next character is taken
Xas the name of a control character. So ^H is backspace and ^[ is escape.
X.PP
XIf the second character on the line is a '\\' and the next character is
Xa digit between 0 and 7 the the following characters
Xare interpreted as an octal number that indicates which character to bind
Xthe action to. If the character immediately after the '\\' is not an
Xoctal digit then the action is bound to that character. For example,
Xto get the '^' character you would use '\\^'.
X.PP
XThe next character on the line is always '='. Following the equal sign is
Xthe name of an action or a string. The actions are defined in the following 
Xtable.
X.PP
X.SH Actions
X
X.IP "\fBbell\fP" 20
X 
XSend a bell (^G) character to the terminal. Hopefully the bell will ring.
XThis action is a nice way to tell the user that an invalid sequence of
Xkeys has been typed.
X
X.IP "\fBpass\fP" 20
X 
XPass the character to the program running under \fIile\fP. Do not echo
Xthe character, do not insert it into the edit buffer. Just pass it along.
XThis is useful for characters like ^C, ^Z, ^Q, and ^S that have special
Xmeaning and shouldn't be held up in the edit buffer waiting to be sent.
X
X.IP "\fBinsert\fP" 20
X 
XInsert the character into the edit buffer. If there are already 256 
Xcharacters in the buffer \fIile\fP will beep and refuse to put the character
Xin the buffer.
X
X.IP "\fBtranspose_chars\fP" 20
X
XSwap the character under the cursor with the character to the left of
Xthe cursor and move the cursor one character to the right. This is handy
Xfor correcting letter transposition errors.
X 
X.IP "\fBdelete_char\fP" 20
X 
XDelete the character directly to the left of the cursor from the edit buffer.
X
X.IP "\fBdelete_char_under\fP" 20
X 
XDelete the character under the cursor from the edit buffer.
X
X.IP "\fBquote\fP" 20
X 
XThe next character to come into \fIile\fP will be inserted into the edit
Xbuffer. This allows you to put characters into the edit buffer
Xthat are bound to an action other than insert.
X
X.IP "\fBescape_1\fP" 20
X 
XLook up the next character in action table 1 instead of action table 0.
X
X.IP "\fBescape_2\fP" 20
X 
XLook up the next character in action table 2 instead of action table 0.
X
X.IP "\fBescape_3\fP" 20
X 
XLook up the next character in action table 3 instead of action table 0.
X
X.IP "\fBdelete_word\fP" 20
X 
XDelete the word directly to the left of the cursor. A word is a sequence
Xof characters surrounded by delimiter characters.
X
X.IP "\fBforward_word\fP" 20
X 
XMove the cursor to the right past the next word. A word is a sequence of
Xcharacters surrounded by delimiter characters.
X
X.IP "\fBbackward_word\fP" 20
X 
XMove the cursor to the left past the next word. A word is a sequence of
Xcharacters surrounded by delimiter characters.
X
X.IP "\fBupper_word\fP" 20
X
XStarting with the character under the cursor, convert the word to the 
Xright of the cursor to upper case.
X
X.IP "\fBlower_word\fP" 20
X
XStarting with the character under the cursor, convert the word to the
Xright of the cursor to lower case.
X
X.IP "\fBcapitalize_word\fP" 20
X
XConvert the character under the cursor to upper case. Convert the word to
Xthe right of the cursor to lower case.
X
X.IP "\fBstart_of_line\fP" 20
X 
XMove the cursor to the left most character in the edit buffer.
X
X.IP "\fBbackward_char\fP" 20
X 
XMove the cursor to the left one character.
X
X.IP "\fBend_of_line\fP" 20
X 
XMove the cursor past the last character in the edit buffer.
X
X.IP "\fBforward_char\fP" 20
X 
XMove the cursor to the right one character.
X
X.IP "\fBadd_to_history\fP" 20
X 
XAdd the contents of the edit buffer to the history buffer and pass the line
Xalong to the program running under \fIile\fP.
X
X.IP "\fBerase_line\fP" 20
X 
XClear the line. Erase all characters on the line.
X
X.IP "\fBerase_to_end_of_line\fP" 20
X 
XDelete the character under the cursor and all character to the left
Xof the cursor from the edit buffer.
X
X.IP "\fBretype_line\fP" 20
X 
XRetype the contents of the current edit buffer. This is handy when system
Xmessages or other asynchronous output has garbled the input line.
X
X.IP "\fBforward_history\fP" 20
X 
XDisplay the next entry in the history buffer. If you are already at the
Xmost recent entry display a blank line. If you try to go forward past
Xthe blank line this command will beep at you.
X
X.IP "\fBbackward_history\fP" 20
X 
XDisplay the previous entry in the history buffer. If there are no older
Xentries in the buffer, beep.
X
X.IP "\fBcomplete_file\fP" 20
X
XTake the word currently under, or immediately to the left of the cursor and
Xtreat it as a partial file name and path name. If there is only one file in the
Xdirectory that starts with the partial file name then fill in the rest of
Xthe file name in the input line. If more than one file starts with the
Xpartial file name fill in the longest common starting string of those file
Xnames.
X 
XIf the path is specified as "~/" then look in the directory named
Xby $HOME. 
X
XIf the path is specified as "~name", where name is a user login name or
Xa partial user login name, then look in the users login directory. If
Xmore than one match is found for a partial user name then \fIile\fP
Xwill beep. When completing a file name, a partial user name will be
Xcompleted at the same time the file name is being completed.
X
XIf you don't use one of the abbreviated path names, you must provide and
Xabsolute path. \fIile\fP is not the shell and does not know the path to
Xthe current directory. This means that \fIile\fP cannot do file name
Xcompletion on relative paths like "dir/file", "./file", and "../file".
X
X.IP "\fBcomplete_file_full\fP" 20
X
XLike \fBcomplete_file\fP but abbreviations like "~/" are replaced by
Xthe full path that they stand for. This is handy when you want to
Xuse abbreviated path names but the program you are talking to doesn't
Xunderstand the abbreviations.
X
XRead the discussion of file name completion under \fBcomplete_file\fP
Xfor more information.
X
X.IP "\fBshow_files\fP" 20
X 
XTake the word currently under, or immediately to the left of, the cursor and 
Xtreat it as a partial file name and path name. List all the files that start
Xwith the partial file name in the directory specified by the path name.
X
XRead the discussion of file name completion under \fBcomplete_file\fP
Xfor more information.
X
X.PP
X.SH Strings
X
XIn addition to being able to bind a character sequence to an action \fIile\fP
Xallows characters sequences to be bound to strings of characters. When a string
Xis invoked the characters in the string are treated as if they were typed
Xby the user. For example, if the line:
X.EX 0
X0^G=ring^Ma^Mbell^M
X.EE
Xwas in your \fI.ilerc\fP file, typing control G would cause three
Xlines to be typed as if the user typed them. Using the default bindings,
Xunless there is a ^J or ^M in the string the string will be inserted
Xin the current line but not sent along until the the user actually
Xpresses return.
X
X.PP
X.SH Error Messages
X.PP
XWhen \fIile\fP encounters errors it prints a message and terminates.
X\fIile\fP can print several standard error message. It can also print
Xa few messages that are specific to \fIile\fP.
X
X.IP "\fBile: unable to allocate pty/tty pair\fP" 20
X
XThere are no free pty devices in the system. You can either try again later,
Xand hope someone has freed a pty for you to use, or you can grab your
Xsystem manager and try to get more pty devices configured.
X
X.IP "\fBile: '=' missing on line #\fP" 20
X
XIn a character binding line you left out the '=' character. Or, you did 
Xsomething that confused the initialization file reader into thinking there
Xshould be an '=' where you didn't think there should be one.
X
X.IP "\fBile: error in initialization file on line #\fP" 20
X
XThis means that the first character of a character binding line wasn't
Xa newline or a '0', '1', '2', or '3'. It could also mean that the 
Xinitialization file reader is confused.
X
X.IP "\fBile: can't find terminal\fP" 20
X
X\fIile\fP could not find a termcap entry for the terminal named by the TERM
Xenvironment variable. Since it can't find it \fIile\fP can't figure out
Xhow to use it.
X
X.IP "\fBile: can't run on terminal\fP" 20
X
XThe terminal named in your TERM environment variable doesn't support
Xthe capabilities \fIile\fP needs to run. So \fIile\fP doesn't even try.
X
X.PP
X.SH BUGS
X.PP
X\fIile\fP changes the input mode on the controlling terminal to RAW. This
Xconfuses xterm. It is a good idea to include the line:
X.EX 0
Xstty cooked -nl echo tabs crt decctlq -litout
X.EE
Xin your .cshrc file when using xterm. Otherwise your new xterm windows
Xcome up in an unusable state.
X.PP
X\fIile\fP requires a terminal that supports the termcap le, ce, bl, 
Xnl, and cr capabilities. If your terminal doesn't provide these,
X\fIile\fP will refuse to run on your terminal.
X.PP
XA misspelled action name in an \fIilerc\fP will be treated as a string.
XThis means that typing the sequence of characters that should
Xinvoke the action will actually cause the misspelled name to be inserted
Xin the input line. 
X.PP
X.SH FILES
X.PP
X $HOME/.ilerc
X ./.ilerc
X.PP 
X.SH SEE ALSO
X.PP
Xstty(1), xterm(1), csh(1), termcap(5)
X
X.SH COPYRIGHT
X.ce 4
XCOPYRIGHT 1988
XEvans & Sutherland Computer Corporation
XSalt Lake City, Utah
XAll Rights Reserved.
X.LP
XTHE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
XSHOULD NOT BE CONSTRUED AS A COMMITMENT BY EVANS & SUTHERLAND.
XEVANS & SUTHERLAND  MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY
XOF THIS SOFTWARE FOR
XANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
X.LP
XIF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS,
XAPPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT
XSET FORTH ABOVE.
X.LP
XPermission to use, copy, modify, and distribute this software and its
Xdocumentation for any purpose and without fee is hereby granted, provided
Xthat the above copyright notice appear in all copies and that both the
Xcopyright notice and this permission notice appear in supporting documentation,
Xand that the name of Evans & Sutherland not be used in advertising
Xor publicity pertaining to distribution of the software without specific, 
Xwritten prior permission.
X.SH AUTHOR
XRobert C. Pendleton
XEvans & Sutherland, Interactive Systems Division, Salt Lake City, Utah.
X.LP
X
SHAR_EOF
if test 15428 -ne "`wc -c < ile.l`"
then
    echo shar: error transmitting "ile.l" '(should have been 15428 characters)'
fi
fi
# end of shell archive
exit 0
-- 
Bob Pendleton @ Evans & Sutherland
UUCP Address:  {decvax,ucbvax,allegra}!decwrl!esunix!bpendlet
Alternate:     utah-cs!utah-gr!uplherc!esunix!bpendlet
        I am solely responsible for what I say.

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

From: Bob Pendleton <bpendlet at esunix.uucp>
Subject: ile is coming
Date: 7 Jul 88 20:23:30 GMT
To:       unix-wizards at brl-sem.arpa


I've recieved MANY requests for ile. So, with the kind permission of
the folks at E&S I'm distributing it to the world.

Please let me know about any code bugs or documentation bugs you find
in ile. If you add new features to ile please send the revised code to
me. If there are features you want that aren't there... well I might
add them, but I really don't have time right now to do much with ile.

One word of warning; DO NOT run su under ile. Ile will echo the
password and store it in the history buffer. The history buffer in ile
is large and some amazing things get stored there.

		Enjoy

			Bob Pendleton

P.S.

It feels really nice to be posting something after having recieved so
much from the net.

-- 
Bob Pendleton @ Evans & Sutherland
UUCP Address:  {decvax,ucbvax,allegra}!decwrl!esunix!bpendlet
Alternate:     utah-cs!utah-gr!uplherc!esunix!bpendlet
        I am solely responsible for what I say.

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

From: "John J. Wallace" <jjw at palladium.uucp>
Subject: Why does BSD Fast File System call syncip from itrunc?
Date: 7 Jul 88 19:20:37 GMT
Keywords: BSD Fast File System
To:       unix-wizards at SEM.BRL.MIL

Wizard-level question:

	In the FFS implementation, why does itrunc() call syncip() when
	it wants to synch the inode to disk instead of simply calling
	iupdat()?

Explanation of question:

	Itrunc(ip, new_size) truncates an inode to a new size,
	typically zero.  New_size can be non-zero when some program
	uses one of the ftruncate family of system calls.

	When itrunc() wants to synch the inode to disk it calls
	syncip(), not iupdat().  The difference is that syncip makes
	sure all *data blocks* for the file are synched to disk
	(synchronously, I believe) before writing the inode to disk
	(also synchronously).  Iupdat() just does the inode, which
	would be quicker.

	So... why is this so.  Is there some implication that
	ftruncate() also does an fsync()?  I hope not.  Is it some
	crash-resistant concern?  Unix is not careful about writing
	data blocks before the inode, so why should this be any
	different?  Or is this another example of ... (byte my
	tongue).


Thanks, I will summarize if any interest.

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

From: Philip Budne <budd at bu-cs.bu.edu>
Subject: Re: Getting the pathname from a FILE*.
Date: 9 Jul 88 04:50:24 GMT
Followup-To: comp.unix.wizards
To:       unix-wizards at brl-sem.arpa

Names are a property of directory entries, not files (inodes).  Un*x
files do not have "names" since many directory entries can reference
the same file.  The stdio library echos this.  Since you called
fopen() you can save the info.  The real pain is figuring out what an
fd from a parent is, or chasing down what file finally was opened
after 3 symlinks.

Philip Budne, Boston U
(Ahh for JFNS%)

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


End of UNIX-WIZARDS Digest
**************************



More information about the Comp.unix.wizards mailing list