ile source code

Bob Pendleton bpendlet at esunix.UUCP
Fri Jul 8 06:27:01 AEST 1988


#! /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.



More information about the Comp.unix.wizards mailing list