[alt.sources] Yet Another Shell

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 11)."
# Contents:  io.c reader/vi.c reader/words.c shell.h trap.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:03:52 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'io.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'io.c'\"
else
echo shar: Extracting \"'io.c'\" \(10624 characters\)
sed "s/^X//" >'io.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * I/O Functions
X *
X * $Id: io.c,v 1.5 89/02/22 10:16:32 dclemans Exp $
X *
X * $Log:	io.c,v $
X * Revision 1.5  89/02/22  10:16:32  dclemans
X * Fix bugs with process waiting, pid reporting, parsing of &
X * 
X * Revision 1.4  89/02/20  20:11:49  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X
Xchar    buffer[BUFSIZ];
Xchar    *bufstart = buffer;
Xchar    *bufend = buffer;
Xstruct  infile *buffile = (struct infile *)NULL;
Xstruct  strsave *strings = (struct strsave *)NULL;
X
Xint io_prompt(prompt_level)
Xint prompt_level;
X{
X    register char *p,*in,*out;
X    char buffer[256];
X
X    if (prompt_level > PROMPT_MAX)
X    {   /* a known prompt? */
X        sprintf(buffer,"? ");
X    }
X    else if (prompt_level < 0)
X    {   /* explicit non-prompting? */
X        return 0;
X    }
X    else
X    {   /* user specified prompt */
X        if (base_env.prompts[prompt_level] == (char *)NULL)
X            return 0;
X        base_env.prompts_issued[prompt_level]++;
X        out = buffer;
X        p = strcopy(base_env.prompts[prompt_level]);
X        if (p == (char *)NULL)
X        {   /* enough memory? */
X            /* message already printed */
X            return 0;
X        }
X        stripquotes(p);
X        for (in = p; *in; in++)
X        {   /* build the prompt */
X            if (*in == '!')
X            {   /* put in cmd number */
X                sprintf(out,"%d",base_env.prompts_issued[prompt_level]);
X                while (*out) out++;
X            }
X            else    *out++ = *in;
X        }
X        *out = '\0';
X        free(p);
X    }
X    io_writestring(0,buffer);
X    io_writestring(0,(char *)NULL);
X    return (int)(out - buffer);
X}   /* end of io_prompt */
X
Xint io_savestring(s)
Xchar *s;
X{
X    register struct strsave *sp;
X
X    if (s == (char *)NULL || strlen(s) == 0)
X        return 0;
X    sp = new_strsave(s);
X    if (sp == (struct strsave *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("io_savestring"));
X        return -1;
X    }
X    sp->next = strings;
X    strings = sp;
X    return 0;
X}   /* end of io_savestring */
X
Xint io_getchar(prompt_level)
Xint prompt_level;
X{
X    register int new_char;
X    register struct strsave *sp;
X    int markeof,rc;
X#ifdef  GEMDOS
X    int i;
X#endif  /* GEMDOS */
X    register struct infile *ip;
X
X    if (strings != (struct strsave *)NULL)
X    {   /* scanning a "pushed back" string? */
X        new_char = *strings->ptr++;
X        if (*strings->ptr == '\0')
X        {   /* finished with the string? */
X            free(strings->string);
X            sp = strings;
X            strings = strings->next;
X            free(sp);
X        }
X        return new_char;
X    }
X    if (buffile != (struct infile *)NULL)
X    {   /* if reading inserted input */
X        if (buffile->start >= buffile->end)
X        {   /* if need more input */
X#ifdef  GEMDOS
X            rc = Fread(buffile->fp,(long)(sizeof buffile->buffer-1),buffile->buffer);
X            if (rc > 0)
X            {   /* got a good buffer? */
X                buffile->buffer[rc] = '\0';
X                for (i = 0; i < rc; i++)
X                {   /* kill any double eol's */
X                    if (buffile->buffer[i] == '\r' &&
X                        (i+1) < rc &&
X                        buffile->buffer[i+1] == '\n')
X                        buffile->buffer[i] = base_env.separators[0];
X                }
X            }
X#else
X            rc = read(buffile->fp,buffile->buffer,sizeof buffile->buffer - 1);
X            if (rc > 0)
X                buffile->buffer[rc] = '\0';
X#endif  /* GEMDOS */
X            if (rc <= 0)
X            {   /* at end of temp file? */
X#ifdef  GEMDOS
X                Fclose(buffile->fp);
X#else
X                close(buffile->fp);
X                main_fdput(buffile->fp);
X#endif  /* GEMDOS */
X                if (buffile->delete)
X                    delete_later(buffile->name);
X                free(buffile->name);
X                ip = buffile;
X                buffile = buffile->next;
X                markeof = ip->markeof;
X                free(ip);
X                if (markeof)
X                    return SYM_MARKER;
X                else return io_getchar(prompt_level);
X            }
X            buffile->buffer[sizeof buffile->buffer - 1] = '\0';
X            if (flag_echoinput)
X            {   /* what did we just read? */
X                io_writestring(0,buffile->buffer);
X            }
X            for (buffile->end = buffile->buffer; *buffile->end != '\0'; buffile->end++)
X                /* do nothing */;
X            buffile->start = buffile->buffer;
X        }
X        new_char = *buffile->start++;
X        if (buffile->nonl)
X        {   /* no nls from this file? */
X            if (new_char == '\r' || new_char == '\n')
X                new_char = ' ';
X        }
X        return new_char;
X    }
X    if (bufstart >= bufend)
X    {   /* need more characters? */
X        if (buffile != (struct infile *)NULL)
X        {   /* reading from temp file? */
X        }
X        else
X        {   /* read from normal input */
X            if (flag_interactive)
X                io_prompt(prompt_level);
X#ifdef  LINED
X            if (flag_interactive)
X            {   /* get a line */
X                buffer[0] == '\0';
X                rc = Reader(base_env.fd_input,buffer,sizeof buffer - 1);
X                if (rc == 1 && (buffer[0] == '\r' || buffer[0] == '\n') && prompt_level <= PROMPT_MAX)
X                    base_env.prompts_issued[prompt_level]--;
X            }
X            else
X                rc = read(base_env.fd_input,buffer,sizeof buffer-1);
X#else
X            rc = read(base_env.fd_input,buffer,sizeof buffer-1);
X            if (rc == 1 && (buffer[0] == '\r' || buffer[0] == '\n') && prompt_level <= PROMPT_MAX)
X                base_env.prompts_issued[prompt_level]--;
X#ifdef  GEMDOS
X            if (flag_interactive && rc >= 0 && rc < (sizeof buffer -1))
X            {   /* add in eol if necessary */
X                if (rc == 0 && prompt_level >= 0 && prompt_level <= PROMPT_MAX)
X                    base_env.prompts_issued[prompt_level]--;
X                if (rc > 0 && buffer[rc-1] != '\n')
X                    buffer[rc++] = '\n';
X                else if (rc > 0 && buffer[rc-1] == '\032')
X                    rc = 0;
X                else if (rc == 0)
X                    buffer[rc++] = '\n';
X                io_writestring(0,"\n");
X            }
X#endif  /* GEMDOS */
X#endif  /* LINED */
X            if (rc <= 0)
X                return SYM_EOF;
X            buffer[rc] = '\0';
X        }
X        buffer[sizeof buffer - 1] = '\0';
X        if (flag_echoinput)
X        {   /* what did we just read? */
X            io_writestring(0,buffer);
X        }
X        for (bufend = buffer; *bufend != '\0'; bufend++)
X            /* do nothing */;
X        bufstart = buffer;
X    }
X    new_char = *bufstart++;
X    return new_char;
X}   /* end of io_getchar */
X
Xvoid io_pushback(c)
Xchar c;
X{
X    char buf[2];
X
X    buf[0] = c;
X    buf[1] = '\0';
X    io_savestring(buf);
X}   /* end of io_pushback */
X
Xint io_pushtoken(name,strip)
Xregister char *name;
Xint strip;
X{
X    register char *cp;
X    register int result;
X
X    cp = new_string(strlen(name)+2);
X    if (cp == (char *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("io_pushtoken"));
X        return -1;
X    }
X    strcpy(cp,name);
X    if (strip)
X        stripquotes(cp);
X    strcat(cp," ");
X    result = io_savestring(cp);
X    free(cp);
X    return result;
X}   /* end of io_pushtoken */
X
Xint io_pushfile(file,delete,nonl,markeof)
Xchar *file;
Xint delete;
Xint nonl;
Xint markeof;
X{
X    register struct infile *ip;
X#ifndef GEMDOS
X    int tfd;
X#endif  /* GEMDOS */
X
X    ip = new_infile();
X    if (ip == (struct infile *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("io_pushfile"));
X        return -1;
X    }
X    ip->delete = delete;
X    ip->nonl = nonl;
X    ip->markeof = markeof;
X    ip->name = strcopy(file);
X    if (ip->name == (char *)NULL)
X    {   /* enough memory? */
X        /* message already printed */
X        free(ip);
X        return -1;
X    }
X    stripquotes(ip->name);
X#ifdef  GEMDOS
X    ip->fp = Fopen(ip->name,0);
X    if (ip->fp < 0)
X    {   /* did the file open? */
X        errmsg(0,LOC("io_pushfile"),"can't open %s",file);
X        free(ip);
X        return -1;
X    }
X#else
X    ip->fp = open(ip->name,0);
X    if (ip->fp < 0)
X    {   /* did the file open? */
X        errmsg(0,LOC("io_pushfile"),"can't open %s",file);
X        free(ip);
X        return -1;
X    }
X    else
X    {   /* put the fd into a safe place */
X        tfd = main_fdget();
X        if (dup2(ip->fp,tfd) < 0)
X        {   /* everything setup? */
X            errmsg(0,LOC("io_pushfile"),"can't associate %s with right fd",file);
X            main_fdput(tfd);
X            free(ip);
X            return -1;
X        }
X        close(ip->fp);
X        ip->fp = tfd;
X    }
X#endif  /* GEMDOS */
X    ip->start = ip->end = ip->buffer;
X    ip->next = buffile;
X    buffile = ip;
X    return 0;
X}   /* end of io_pushfile */
X
Xvoid io_writestring(file,s)
Xint file;
Xchar *s;
X{
X    static char *buffer = (char *)NULL;
X    static int bufsiz = 0;
X    register int fd;
X    register char *in,*out;
X    
X    if (buffer == (char *)NULL)
X    {   /* do we have any buffer space? */
X        bufsiz = 128;
X        buffer = new_string(bufsiz);
X        if (buffer == (char *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("io_writestring"));
X            return;
X        }
X    }
X
X    switch (file)
X    {   /* what file descriptor should we use? */
X        case 0:
X            fd = base_env.io->output;
X            break;
X        case 1:
X            fd = base_env.io->errout;
X            break;
X        case 2:
X            fd = 1;
X            break;
X        case 3:
X            fd = 2;
X            break;
X        default:
X            errmsg(0,LOC("io_writestring"),"illegal file specified");
X            return;
X    }
X
X    for (out = buffer; *out; out++)
X        /* do nothing */;
X    if (s == (char *)NULL)
X    {   /* just flush existing buffer */
X        write(fd,buffer,(unsigned)(out - buffer));
X        out = buffer;
X        *out = '\0';
X        return;
X    }
X    for (in = s; *in; in++)
X    {   /* add in the string */
X#ifdef  GEMDOS
X        if (*in == '\n')
X            *out++ = '\r';
X#endif  /* GEMDOS */
X        *out++ = *in;
X        *out = '\0';
X        if (out >= &buffer[bufsiz-1] || *in == '\n')
X        {   /* dump current contents of buffer */
X            write(fd,buffer,(unsigned)(out - buffer));
X            out = buffer;
X            *out = '\0';
X        }
X    }
X}   /* end of io_writestring */
X
END_OF_FILE
if test 10624 -ne `wc -c <'io.c'`; then
    echo shar: \"'io.c'\" unpacked with wrong size!
fi
# end of 'io.c'
fi
if test -f 'reader/vi.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/vi.c'\"
else
echo shar: Extracting \"'reader/vi.c'\" \(9300 characters\)
sed "s/^X//" >'reader/vi.c' <<'END_OF_FILE'
X/*
X * vi		- an "vi" style single line editor for reader
X *
X * Dave Clemans, 4/84 (initial version); 1/89 (more generality)
X *
X * Commands supported:
X *    Input mode:
X *	alphabetic chars are inserted
X *	normal backspace, kill supported
X *	kill word	^W
X *	eof		^D
X *    Editing mode:
X *	forward		l
X *	forward word	e
X *	fwd word nws	E
X *	back word	b
X *	back word nws	B
X *	next word	w
X *	next word nws	W
X *	backward	h
X *	start line	0
X *	start line nws	^
X *	end line	$
X *	prev line	k
X *	next line	j
X *	search prev	/
X *	append		a
X *	append eol	A
X *	kill eol&append	C
X *	kill end line	D
X *	insert		i
X *	insert, 1st nws	I
X *	repeat prev	.
X *	delete char	x
X *	retype		^L
X *	erase		^H
X *	replace char	r
X *	repeat search	n
X *	find char	f
X *	repeat find	;
X *	change chars	c
X *	delete chars	d
X *	pop kill, ins	P
X *	pop kill, app	p
X *	undo change	u
X *	undo all	U
X *	goto		G
X *	arguments	digits
X *
X * If we return 0, we did not handle the input character; it should be
X * handled by the standard editor.
X * If we return <0, signal end-of-file.
X * Otherwise the character has been
X * handled and the next character can be read.
X *
X * $Id: vi.c,v 1.2 89/02/20 20:20:30 dclemans Exp $
X *
X * $Log:	vi.c,v $
X * Revision 1.2  89/02/20  20:20:30  dclemans
X * Add RCS identifiers
X * 
X */
X#include <ctype.h>
X#ifndef GEMDOS
X#include <sgtty.h>
X#endif  GEMDOS
X
X#include "history.h"
X
X/*
X * If true, in "vi" editing mode; else just normal tty style editing
X */
Xstatic int editingMode;
X
X/*
X * Saved copies of things for undo's, etc.
X */
Xstatic char editingChar;
Xstatic char searchChar;
Xstatic struct historyLine saveChange;
Xstatic struct historyLine saveAll;
Xstatic int argument = 1;
X
X/*
X * Check for "vi" style commands
X */
Xint _doVi(c)
Xregister char c;
X{
X	register char cc;
X	register int counter;
X	register int cursor;
X
X	if (!editingMode)
X	{	/* if not doing real editing; i.e. just text insertion */
X		if (c == '\033')
X		{	/* switch to editing mode */
X			strncpy(saveAll.contents,History.currentLine->contents,History.currentLine->size);
X			saveAll.size = History.currentLine->size;
X			saveAll.cursor = History.currentLine->cursor;
X			saveAll.ttycursor = History.currentLine->ttycursor;
X			strncpy(saveChange.contents,History.currentLine->contents,History.currentLine->size);
X			saveChange.size = History.currentLine->size;
X			saveChange.cursor = History.currentLine->cursor;
X			saveChange.ttycursor = History.currentLine->ttycursor;
X			editingMode = 1;
X			return(1);
X		}
X		if (c == '\004')
X		{	/* end of file */
X			editingMode = 0;
X			return(-1);
X		}
X		return(0);
X	}
X
X	/*
X	 * Check for vi style editing commands
X	 */
X	if (c == '.')
X		cc = editingChar;
X	else	cc = c;
X	switch (cc)
X	{	/* if going to make a change, save current state */
X	    case 'x':
X	    case 'a':
X	    case 'A':
X	    case 'i':
X	    case 'I':
X	    case 'c':
X	    case 'C':
X	    case 'd':
X	    case 'D':
X	    case '\010':
X	    case 'p':
X	    case 'P':
X	    case 'G':
X		strncpy(saveChange.contents,History.currentLine->contents,History.currentLine->size);
X		saveChange.size = History.currentLine->size;
X		saveChange.cursor = History.currentLine->cursor;
X		saveChange.ttycursor = History.currentLine->ttycursor;
X		break;
X	    default:	/* no need to do anything here */
X		break;
X	}
X	switch (c)
X	{	/* see which command ... */
X	    case 'l':	/* forward one character */
X		_doStandardSpace(c);
X		break;
X	    case 'h':	/* backward one character */
X		_doStandardBackspace(c);
X		break;
X	    case 'e':	/* forward one word */
X		_spaceNextStdWord();
X		break;
X	    case 'b':	/* backward one word */
X		_spacePrevStdWord();
X		break;
X	    case 'w':	/* to next word */
X		_skipNextStdWord();
X		break;
X	    case 'W':	/* to next word; non-white space */
X		_skipNextWord(" \t");
X		break;
X	    case '0':	/* beginning of line */
X		_doStandardStartLine(c);
X		break;
X	    case '$':	/* end of line */
X		_doStandardEndLine(c);
X		break;
X	    case 'k':	/* previous line */
X		_doStandardPrevLine(c);
X		break;
X	    case 'j':	/* next line */
X		_doStandardNextLine(c);
X		break;
X	    case '/':	/* search lines */
X		_doStandardSrchPrevLine(c,0);
X		break;
X	    case 'n':	/* repeat last search */
X		_doStandardSrchPrevLine(c,1);
X		break;
X	    case 'a':	/* append chars */
X		_doStandardSpace(c);
X		editingMode = 0;
X		break;
X	    case 'A':	/* append to end of line */
X		_doStandardEndLine(c);
X		editingMode = 0;
X		break;
X	    case 'C':	/* delete to end of line; start appending */
X		_doStandardDelEndLine(c);
X		editingMode = 0;
X		break;
X	    case 'D':	/* delete to end of line */
X		_doStandardDelEndLine(c);
X		break;
X	    case 'i':	/* start inserting */
X		editingMode = 0;
X		break;
X	    case 'x':	/* delete current character */
X		_doStandardDelete(c);
X		break;
X	    case '\014':	/* retype line */
X		_doStandardRetype(c);
X		break;
X	    case '^':	/* first non-white space in line */
X		_doStandardStartLine(c);
X		_skipNextWord(" \t");
X		break;
X	    case 'I':	/* goto to first non-white space char in line, start inserting */
X		_doStandardStartLine(c);
X		while ((History.currentLine->cursor < History.currentLine->size) &&
X		    !isspace(History.currentLine->contents[History.currentLine->cursor]))
X			_doStandardSpace(c);
X		editingMode = 0;
X		break;
X	    case '\010':/* do character erase */
X		_doStandardErase(c);
X		break;
X	    case '.':	/* repeat previous editing command */
X		_savechar(editingChar);
X		break;
X	    case 'r':	/* replace char */
X		if (_savedState.stream < 0)
X		{	/* nothing to read from? */
X			_writechar('\007');
X			return(1);
X		}
X		cc = _readchar(_savedState.stream);
X		_doStandardDelete(c);
X		_doStandardCharacter(cc);
X		break;
X	    case '\030':/* expand command or filename */
X		_doStandardExpand(c);
X		break;
X	    case 'E':	/* next word defined by white space */
X		_spaceNextWord(" \t");
X		break;
X	    case 'B':	/* prev word defined by white space */
X		_spacePrevWord(" \t");
X		break;
X	    case 'f':	/* find char */
X		if (_savedState.stream < 0)
X		{	/* nothing to read from? */
X			_writechar('\007');
X			return(1);
X		}
X		searchChar = _readchar(_savedState.stream);
X		/* FALL THROUGH!!! */
X	    case ';':	/* repeat last find char */
X		for (counter = History.currentLine->cursor+1; counter < History.currentLine->size; counter++)
X			if (History.currentLine->contents[counter] == searchChar)
X				break;
X		if ((counter >= History.currentLine->size) ||
X		    (History.currentLine->contents[counter] != searchChar))
X		{	/* not found, at end of line, etc. */
X			_writechar('\007');
X			return(1);
X		}
X		while (History.currentLine->cursor < counter)
X			_doStandardSpace('\000');
X		break;
X	    case 'c':	/* change chars */
X	    case 'd':	/* delete chars */
X		if (_savedState.stream < 0)
X		{	/* nothing to read from? */
X			_writechar('\007');
X			return(1);
X		}
X		cc = _readchar(_savedState.stream);
X		counter = History.currentLine->cursor;
X		_doVi(cc);
X		editingChar = c;
X		if (counter <= History.currentLine->cursor)
X		{	/* set start and stop points for range delete */
X			cursor = counter;
X			counter = History.currentLine->cursor;
X		}
X		else
X		{	/* moved other direction */
X			cursor = History.currentLine->cursor;
X		}
X		_deleteTo(cursor,counter);
X		if (c == 'c')
X			editingMode = 0;
X		break;
X	    case 'P':	/* pop from kill ring, inserting before cursor */
X		_savechar('i');
X		_popOffKillRing();
X		_savechar('\033');
X		break;
X	    case 'p':	/* pop from kill ring, appending to cursor */
X		_savechar('a');
X		_popOffKillRing();
X		_savechar('\033');
X		break;
X	    case 'U':	/* undo all changes */
X		_doStandardKill('\000');
X		strncpy(History.currentLine->contents,saveAll.contents,saveAll.size);
X		History.currentLine->size = saveAll.size;
X		History.currentLine->cursor = saveAll.cursor;
X		History.currentLine->ttycursor = saveAll.ttycursor;
X		_doStandardRetype('\000');
X		break;
X	    case 'u':	/* undo last changes */
X		_doStandardKill('\000');
X		strncpy(History.currentLine->contents,saveChange.contents,saveChange.size);
X		History.currentLine->size = saveChange.size;
X		History.currentLine->cursor = saveChange.cursor;
X		History.currentLine->ttycursor = saveChange.ttycursor;
X		_doStandardRetype('\000');
X		break;
X	    case 'G':	/* goto a command */
X		/* so far, support only for default case; goto oldest command */
X		_doStandardKill('\000');
X		strncpy(History.currentLine->contents,History.listBottom->contents,History.listBottom->size);
X		History.currentLine->size = History.listBottom->size;
X		History.currentLine->cursor = History.listBottom->cursor;
X		History.currentLine->ttycursor = History.listBottom->ttycursor;
X		_doStandardRetype('\000');
X		_doStandardEndLine('\000');
X		break;
X	    case '\004':/* eof; use default handling */
X	    case '\r':	/* eol; use default handling */
X	    case '\n':
X		editingMode = 0;
X		return(0);
X	    case '1':	/* for setting numerical argument */
X	    case '2':
X	    case '3':
X	    case '4':
X	    case '5':
X	    case '6':
X	    case '7':
X	    case '8':
X	    case '9':
X		argument = 0;
X		if (_savedState.stream < 0)
X		{	/* nothing to read from? */
X			_writechar('\007');
X			return(1);
X		}
X		argument = 0;
X		for (; isdigit(c); c = _readchar(_savedState.stream))
X		{	/* build value */
X			argument *= 10;
X			argument += (int)((int)c - (int)'0');
X		}
X		if (c == EOF)
X			return(-1);
X		_savechar(c);
X		return(1);
X	    default:	/* we don't know about it */
X		_writechar('\007');
X		break;
X	}
X	editingChar = c;
X	argument = 1;
X	return(1);
X};	/* end of _doVi */
END_OF_FILE
if test 9300 -ne `wc -c <'reader/vi.c'`; then
    echo shar: \"'reader/vi.c'\" unpacked with wrong size!
fi
# end of 'reader/vi.c'
fi
if test -f 'reader/words.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/words.c'\"
else
echo shar: Extracting \"'reader/words.c'\" \(8564 characters\)
sed "s/^X//" >'reader/words.c' <<'END_OF_FILE'
X/*
X * words		- routines to find words in lines for reader
X *
X * Dave Clemans, 4/84 (initial version); 1/89 (more generality)
X *
X * Entry points:
X *	_findNextWord	- find next "word" given by specified delimiters
X *	_findPrevWord	- find prev "word" given by specified delimiters
X *	_deleteNextWord	- delete next "word"
X *	_deletePrevWord	- delete prev "word"
X *	_spaceNextWord	- space to next "word"
X *	_spacePrevWord	- space to previous "word"
X *	_skipNextWord	- skip to next "word"
X *	_findNextStdWord	- find next word given by [A-Z][a-z][0-9]_
X *	_findPrevStdWord	- find prev word given by [A-Z][a-z][0-9]_
X *	_deleteNextStdWord	- delete next standard word
X *	_deletePrevStdWord	- delete prev standard word
X *	_spaceNextStdWord	- space to next word
X *	_spacePrevStdWord	- space to previous word
X *	_skipNextStdWord	- skip to next word
X *	_capitalizeStdWord	- capitalize this word
X *
X * $Id: words.c,v 1.2 89/02/20 20:20:33 dclemans Exp $
X *
X * $Log:	words.c,v $
X * Revision 1.2  89/02/20  20:20:33  dclemans
X * Add RCS identifiers
X * 
X */
X#include <ctype.h>
X#ifndef GEMDOS
X#include <sgtty.h>
X#endif  GEMDOS
X
X#include "history.h"
X
X/*
X * Interesting function definitions
X */
Xextern char *index();
X
X/*
X * Find the next word in the global History.currentLine given by the
X * passed delimiters; return index of end of that word
X */
Xint _findNextWord(delimiters)
Xchar *delimiters;
X{
X	register char *rc;
X	register int ip;
X
X	ip = History.currentLine->cursor+1;
X	while (index(delimiters,History.currentLine->contents[ip]))
X	{	/* between words */
X		ip++;
X		if (ip >= History.currentLine->size)
X			break;
X	}
X	for (; ip < History.currentLine->size; ++ip)
X	{	/* go through rest of line */
X		rc = index(delimiters,History.currentLine->contents[ip]);
X		if (rc)	/* a delimiter is found! */
X			return(ip-1);
X	}
X	return(ip);	/* ip == end of line at this point */
X};	/* end of _findNextWord */
X
X/*
X * Find the previous word in the global History.currentLine given by the
X * passed delimiters; return index of end of that word
X */
Xint _findPrevWord(delimiters)
Xchar *delimiters;
X{
X	register char *rc;
X	register int ip;
X
X	ip = History.currentLine->cursor-1;
X	while (index(delimiters,History.currentLine->contents[ip]))
X	{	/* between words */
X		ip--;
X		if (ip < 0)
X			break;
X	}
X	for (; ip >= 0; --ip)
X	{	/* go through rest of line */
X		rc = index(delimiters,History.currentLine->contents[ip]);
X		if (rc)	/* a delimiter is found! */
X			return(ip+1);
X	}
X	return(0);	/* beginning of line */
X};	/* end of _findPrevWord */
X
X/*
X * Delete the next word in the line given by "delimiters"
X */
X_deleteNextWord(delimiters)
Xchar *delimiters;
X{
X	register int word;
X	register int counter;
X
X	word = _findNextWord(delimiters);
X	_pushOnKillRing(History.currentLine->cursor,word);
X	for (counter = 0; counter < (word-History.currentLine->cursor+1); counter++)
X		_doStandardDelete('\000');
X};	/* end of _deleteNextWord */
X
X/* Delete the previous word in the line given by "delimiters"
X */
X_deletePrevWord(delimiters)
Xchar *delimiters;
X{
X	register int word;
X	register int counter;
X
X	word = _findPrevWord(delimiters);
X	_doStandardSpace('\000');
X	for (counter = 0; counter < (History.currentLine->cursor-word+1); counter++)
X		_doStandardErase('\000');
X};	/* end of _deletePrevWord */
X
X/*
X * Space forward to next word in line
X */
X_spaceNextWord(delimiters)
Xchar *delimiters;
X{
X	register int word;
X
X	word = _findNextWord(delimiters);
X	while (History.currentLine->cursor < word)
X		_doStandardSpace('\000');
X};	/* end of _spaceNextWord */
X
X/*
X * Space backward to next word in line
X */
X_spacePrevWord(delimiters)
Xchar *delimiters;
X{
X	register int word;
X
X	word = _findPrevWord(delimiters);
X	while (History.currentLine->cursor > word)
X		_doStandardBackspace('\000');
X};	/* end of _spacePrevWord */
X
X/*
X * Skip to beginning of word after one that we are in.
X */
X_skipNextWord(delimiters)
Xchar *delimiters;
X{
X	register int ip;
X
X	ip = History.currentLine->cursor;
X	if (!index(delimiters,History.currentLine->contents[ip]))
X	{	/* in a word; goto end */
X		for (;ip < History.currentLine->size;)
X		{	/* goto end of current word */
X			if (index(delimiters,History.currentLine->contents[ip]))
X				break;
X			ip++;
X		}
X	}
X	if (index(delimiters,History.currentLine->contents[ip]))
X	{	/* between words */
X		for (;ip < History.currentLine->size;)
X		{	/* goto next word */
X			if (!index(delimiters,History.currentLine->contents[ip]))
X				break;
X			ip++;
X		}
X	}
X	while (History.currentLine->cursor < ip)
X		_doStandardSpace('\000');
X};	/* end of _skipNextWord */
X
X/*
X * Find the next word in the line where a word is composed of alphanumeric's
X * and _.
X */
Xint _findNextStdWord()
X{
X	register int ip;
X	register char cc;
X
X	ip = History.currentLine->cursor+1;
X	cc = History.currentLine->contents[ip];
X	if (!isalpha(cc) && !isdigit(cc) && (cc != '_'))
X	{	/* between words */
X		for (;ip < History.currentLine->size;)
X		{	/* between words; look for next one */
X			cc = History.currentLine->contents[ip];
X			if (isalpha(cc) || isdigit(cc) || (cc == '_'))
X				break;
X			ip++;
X		}
X	}
X	for (; ip < History.currentLine->size; ip++)
X	{	/* search for words */
X		cc = History.currentLine->contents[ip];
X		if (!isalpha(cc) && !isdigit(cc) && (cc != '_'))
X			return(ip-1);
X	}
X	return(ip);	/* will always be end of line */
X};	/* end of _findNextStdWord */
X
X/*
X * Find the previous word in the line where a word is composed of alphanumeric's
X * and _.
X */
Xint _findPrevStdWord()
X{
X	register int ip;
X	register char cc;
X
X	ip = History.currentLine->cursor-1;
X	cc = History.currentLine->contents[ip];
X	if (!isalpha(cc) && !isdigit(cc) && (cc != '_'))
X	{	/* between words */
X		for (;ip >= 0;)
X		{	/* between words; look for next one */
X			cc = History.currentLine->contents[ip];
X			if (isalpha(cc) || isdigit(cc) || (cc == '_'))
X				break;
X			ip--;
X		}
X	}
X	for (; ip > 0; --ip)
X	{	/* search for words */
X		cc = History.currentLine->contents[ip];
X		if (!isalpha(cc) && !isdigit(cc) && (cc != '_'))
X			return(ip+1);
X	}
X	return(0);	/* beginning of line */
X};	/* end of _findPrevStdWord */
X
X/*
X * Delete the next word in the line
X */
X_deleteNextStdWord()
X{
X	register int word;
X	register int counter;
X
X	word = _findNextStdWord();
X	_pushOnKillRing(History.currentLine->cursor,word);
X	for (counter = 0; counter < (word-History.currentLine->cursor+1); counter++)
X		_doStandardDelete('\000');
X};	/* end of _deleteNextStdWord */
X
X/*
X * Delete the previous word in the line
X */
X_deletePrevStdWord()
X{
X	register int word;
X	register int counter;
X
X	word = _findPrevStdWord();
X	_doStandardSpace('\000');
X	for (counter = 0; counter < (History.currentLine->cursor-word+1); counter++)
X		_doStandardErase('\000');
X};	/* end of _deletePrevStdWord */
X
X/*
X * Space forward to next word in line
X */
X_spaceNextStdWord()
X{
X	register int word;
X
X	word = _findNextStdWord();
X	while (History.currentLine->cursor < word)
X		_doStandardSpace('\000');
X};	/* end of _spaceNextStdWord */
X
X/*
X * Space backward to next word in line
X */
X_spacePrevStdWord()
X{
X	register int word;
X
X	word = _findPrevStdWord();
X	while (History.currentLine->cursor > word)
X		_doStandardBackspace('\000');
X};	/* end of _spacePrevStdWord */
X
X/*
X * Skip to beginning of word after one that we are in.
X */
X_skipNextStdWord()
X{
X	register char cc;
X	register int ip;
X
X	ip = History.currentLine->cursor;
X	cc = History.currentLine->contents[ip];
X	if (isalpha(cc) || isdigit(cc) || (cc == '_'))
X	{	/* in a word; goto end */
X		for (;ip < History.currentLine->size;)
X		{	/* goto end of current word */
X			if (!isalpha(cc) && !isdigit(cc) && (cc != '_'))
X				break;
X			ip++;
X			cc = History.currentLine->contents[ip];
X		}
X	}
X	if (!isalpha(cc) && !isdigit(cc) && (cc != '_'))
X	{	/* between words */
X		for (;ip < History.currentLine->size;)
X		{	/* goto next word */
X			if (isalpha(cc) || isdigit(cc) || (cc == '_'))
X				break;
X			ip++;
X			cc = History.currentLine->contents[ip];
X		}
X	}
X	while (History.currentLine->cursor < ip)
X		_doStandardSpace('\000');
X};	/* end of _skipNextStdWord */
X
X/*
X * Capitalize the word that we are now in
X */
X_capitalizeStdWord()
X{
X	register char cc;
X	register int ip,size;
X
X	ip = History.currentLine->cursor;
X	cc = History.currentLine->contents[ip];
X	if (!isalpha(cc) && !isdigit(cc) && (cc != '_'))
X	{	/* not in word??? */
X		_writechar('\007');
X		return;
X	}
X	_spaceNextStdWord();
X	_spacePrevStdWord();
X	ip = _findNextStdWord();
X	_pushOnKillRing(History.currentLine->cursor,ip);
X	size = ip-History.currentLine->cursor+1;
X	for (ip = 0; ip < size; ip++)
X	{	/* go through word, capitalizing it */
X		_doStandardCapitalize('\000');
X		_doStandardSpace('\000');
X	}
X};	/* end of _capitalizeStdWord */
END_OF_FILE
if test 8564 -ne `wc -c <'reader/words.c'`; then
    echo shar: \"'reader/words.c'\" unpacked with wrong size!
fi
# end of 'reader/words.c'
fi
if test -f 'shell.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'shell.h'\"
else
echo shar: Extracting \"'shell.h'\" \(8325 characters\)
sed "s/^X//" >'shell.h' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * $Id: shell.h,v 1.10 89/03/05 15:38:52 dclemans Exp $
X *
X * $Log:	shell.h,v $
X * Revision 1.10  89/03/05  15:38:52  dclemans
X * more fixes
X * 
X * Revision 1.9  89/02/22  21:32:02  dclemans
X * Implement simple background job monitoring facility
X * 
X * Revision 1.8  89/02/22  13:19:00  dclemans
X * Implement $!
X * 
X * Revision 1.7  89/02/22  08:17:15  dclemans
X * implement left-justified, right-justified, etc. parameter attributes
X * 
X * Revision 1.6  89/02/21  19:40:11  dclemans
X * Implement RANDOM and SECONDS variables
X * 
X * Revision 1.5  89/02/20  20:07:16  dclemans
X * Add RCS identifiers
X * 
X */
X#ifdef  USG
X#include <string.h>
X#else
X#include <strings.h>
X#endif  /* USG */
X#include <ctype.h>
X#ifdef  GEMDOS
X#include <osbind.h>
X#ifdef  MWC
X#define DEFSTACK    6144L
X#endif  /* MWC */
X#endif  /* GEMDOS */
X
X#define LOC(r)  __FILE__,r,__LINE__
X
X#include <errno.h>
X
X#ifndef GEMDOS
X#ifndef USG
X#define strchr  index
X#define strrchr rindex
X#define memcpy(t,f,n) bcopy(f,t,n)
X#endif  /* USG */
X#else
X#define	bcopy(f,t,n) memcpy(t,f,n)
X#endif  /* GEMDOS */
X
X#ifdef  GEMDOS
X#define NO_FILE(rc) (rc == AEFILNF || rc == AEPTHNF || rc == AENHNDL || rc == AENSMEM || rc == AEDRIVE || rc == AENMFIL)
X#define NOT_EXEC(rc)    (rc == AEPLFMT)
X#else
X/* fix these */
X#define NO_FILE(rc) (rc == ENOENT)
X#define NOT_EXEC(rc)    (rc == ENOEXEC)
X#endif  /* GEMDOS */
X
X#define SYM_MARKER          -3
X#define SYM_EOF             -2
X#define SYM_EOL             -1
X#define SYM_WORD            0
X#define SYM_SEMI            1
X#define SYM_BACK            2
X#define SYM_LT              3
X#define SYM_LLT             4
X#define SYM_RT              5
X#define SYM_RRT             6
X#define SYM_LBT             7
X#define SYM_RBT             8
X#define SYM_LBRACE          9
X#define SYM_RBRACE          10
X#define SYM_PIPE            11
X#define SYM_ORIF            12
X#define SYM_ANDIF           13
X#define SYM_KEYWORD         14
X#define SYM_NUMBER          15
X#define SYM_LPAREN          16
X#define SYM_RPAREN          17
X#define SYM_PIPER           18      /* pseudo-marker used in pipe tagging */
X#define SYM_BQUOTE          19
X#define SYM_DLPAREN         20
X#define SYM_DRPAREN         21
X#define SYM_LLTL            22
X#define SYM_DLBRACK         23
X#define SYM_DRBRACK         24
X#define SYM_DSEMI           25
X
X#define DEFAULT_HISTORY 16
X#define PROMPT_MAX      3
X#ifdef  GEMDOS
X#define ESCAPE_CHAR     '@'
X#define DIR_SEPARATOR   '\\'
X#define MAXUFD          5
X#else
X#define ESCAPE_CHAR     '\\'
X#define DIR_SEPARATOR   '/'
X#define MAXUFD          ((_NFILE > 20) ? 20 : _NFILE)
X#endif  /* GEMDOS */
X
X#define SHERR_NOMEM     -1
X#define SHERR_PERROR    -2
X
X#define TYPE_EXPORTED   0x01
X#define TYPE_READONLY   0x02
X#define TYPE_INTEGER    0x04
X#define TYPE_UPPERCASE	0x10
X#define TYPE_LOWERCASE	0x20
X#define TYPE_TAGGED     0x40
X#define TYPE_FUNCTION   0x80
X#define TYPE_LEFTJUST   0x100
X#define TYPE_RIGHTJUST  0x200
X#define TYPE_ZEROS      0x400
X#define TYPE_HOSTMAP    0x800
X#define TYPE_DELETED    0x8000
X
X#define TYPE_TRACKED    0x4000
X
X/*
X * Shell lexing structures
X */
Xstruct token
X{
X    int     type;
X    struct  token *next;
X    char    name[1];        /* over-indexed, must be at end */
X};
Xstruct  iotoken
X{
X    int     fd;
X    int     type;
X    char    *tempfile;
X    struct  iotoken *next;
X    char    file[1];        /* over-indexed; must be at end */
X};
Xstruct phrase
X{
X    struct  token *type;
X    struct  token *body;
X    struct  token *body_end;
X    struct  token *var;
X    struct  token *var_end;
X    struct  iotoken *io;
X    struct  iotoken *io_end;
X    struct  phrase *group;
X    struct  phrase *group_end;
X    struct  phrase *next;
X};
Xstruct  infile
X{
X    char    *name;
X    int     fp;
X    int     delete;
X    int     nonl;
X    int     markeof;
X    char    *start;
X    char    *end;
X    char    buffer[256];
X    struct  infile *next;
X};
Xstruct strsave
X{
X    char    *string;
X    char    *ptr;
X    struct  strsave *next;
X};
X#ifndef GEMDOS
Xstruct procs
X{
X    int pid;
X    struct procs *next;
X};
Xstruct bg_job
X{
X    struct phrase *job;
X    struct procs *cmds;
X    struct bg_job *next;
X};
X#endif  /* GEMDOS */
X
X/*
X * Shell environment structures
X */
Xstruct  commands
X{
X    char    *name;
X    int     (*cmd)();
X};
Xstruct variable
X{
X    char    *name;
X    char    *value;
X    int     cmd;
X    int     type;
X    int     misc;
X    struct  variable *left,*right;
X};
Xstruct  aliases
X{
X    char    *name;
X    struct  token *tp;
X    int     type;
X    struct  aliases *left,*right;
X};
Xstruct  function
X{
X    char    *name;
X    struct  phrase *code;
X    struct  function *left,*right;
X};
Xstruct  iostack
X{
X    int input;
X    int output;
X    int errout;
X    struct  iostack *next;
X};
Xstruct  dirstack
X{
X    char    *current;
X    struct  dirstack *next;
X};
Xstruct  varstack
X{
X    struct  variable *table;
X    struct  varstack *next;
X};
Xstruct  herestack
X{
X    struct  iotoken *doc;
X    struct  herestack *next;
X};
Xstruct  hist_phrase
X{   int     number;
X    struct  phrase *cmd;
X};
Xstruct  envinfo
X{
X    long    start_at;
X    int     prompts_issued[PROMPT_MAX+1];
X    char    *prompts[PROMPT_MAX+1];
X    char    *homedir;
X    char    *envfile;
X    char    *shellname;
X    int     columns,lines;
X    char    *separators;
X    char    *cd_path;
X    char    *exec_path;
X    int     break_level;
X    int     continue_level;
X    int     temp_count;
X    int     history_size;
X    struct  hist_phrase *history_list;
X#ifdef  GEMDOS
X    int     fd_con,fd_aux,fd_prn;
X    char    *tmpdir;
X    char    *exec_suff;
X#endif  /* GEMDOS */
X    struct  varstack *var;
X    struct  aliases *alias_table;
X    struct  function *func_table;
X    struct  dirstack *dir;
X#ifndef GEMDOS
X    struct  bg_job *jobs;
X#endif  /* GEMDOS */
X    int     fd_input;
X    int     background_pid;
X    struct  iostack *io;
X    struct  herestack *pending_heredocs;
X};
X
Xextern  char    shell_version[];
Xextern  struct  envinfo base_env;
X
Xextern  char    *var_arg0;
Xextern  int     var_argc;
Xextern  char    **var_argv;
Xextern  char    *var_init[];
Xextern  void    var_dump();
Xextern  void    var_settype();
Xextern  void    var_resetargs();
Xextern  void    var_shiftargs();
Xextern  void    var_define0();
Xextern  char    *var_makeenv();
Xextern  char    *var_normal();
Xextern  char    *var_reference();
X
Xextern  void    alias_dump();
Xextern  int     alias_print();
Xextern  void    alias_define();
Xextern  int     alias_tracked();
Xextern  struct  aliases *alias_get();
X
Xextern  void    io_writestring();
Xextern  struct  infile *buffile;
Xextern  struct  strsave *strings;
X
Xextern  int     lchdir();
Xextern  char    *strcopy();
Xextern  void    stripquotes();
Xextern  int     errmsg();
Xextern  char    *new_string();
Xextern  char    **new_argv();
Xextern  struct  phrase *copy_phrase();
Xextern  struct  phrase *copy_group();
Xextern  struct  phrase *new_phrase();
Xextern  struct  token *new_token();
Xextern  struct  iotoken *new_iotoken();
Xextern  struct  variable *new_variable();
Xextern  struct  aliases *new_alias();
Xextern  struct  function *new_function();
Xextern  struct  infile *new_infile();
Xextern  struct  strsave *new_strsave();
X
Xextern  struct  phrase *lex_sentence();
Xextern	struct	phrase *lex_phrase();
Xextern  struct  token *lex_token();
Xextern  struct  token *lex_reparse_tokens();
X
Xextern  struct  function *func_get();
Xextern  char    *exec_pathsearch();
Xextern  int     exec_phrase();
Xextern	void	phrase_dump();
Xextern  void    sentence_dump();
Xextern	void	phrase_free();
Xextern  void    sentence_free();
Xextern  void    tokens_free();
X
Xextern  int     flag_echoinput;
Xextern  int     flag_echoexec;
Xextern  int     flag_keywords;
Xextern  int     flag_cmdhash;
Xextern  int     flag_varerr;
Xextern  int     flag_noglob;
Xextern  int     flag_allexport;
Xextern  int     flag_noexec;
Xextern  int     flag_interactive;
Xextern  int     flag_monitor;
X
Xextern  int     cmd_lastrc;
Xextern  int     cmd_forceexit;
Xextern  int     cmd_returnexit;
Xextern  int     cmd_count;
Xextern  struct  commands cmd_builtin[];
X
Xextern  struct  token *wild_search();
X
X#ifndef GEMDOS
Xextern  int     main_fdget();
Xextern  int     main_fdput();
X#endif  /* GEMDOS */
Xextern  int     main_iopush();
Xextern  int     main_iopop();
Xextern  int     main_dirpush();
Xextern  int     main_dirpop();
END_OF_FILE
if test 8325 -ne `wc -c <'shell.h'`; then
    echo shar: \"'shell.h'\" unpacked with wrong size!
fi
# end of 'shell.h'
fi
if test -f 'trap.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'trap.c'\"
else
echo shar: Extracting \"'trap.c'\" \(14049 characters\)
sed "s/^X//" >'trap.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Signal handling; the "kill" built-in; the "jobs" built-in
X *
X * $Id: trap.c,v 1.9 89/03/05 15:38:55 dclemans Exp $
X *
X * $Log:	trap.c,v $
X * Revision 1.9  89/03/05  15:38:55  dclemans
X * more fixes
X * 
X * Revision 1.8  89/02/23  08:08:42  dclemans
X * make kill work with background jobs list
X * 
X * Revision 1.7  89/02/22  21:32:04  dclemans
X * Implement simple background job monitoring facility
X * 
X * Revision 1.6  89/02/21  20:37:29  dclemans
X * Fix bug with signal_map on systems with varying
X * numbers of signals
X * 
X * Revision 1.5  89/02/21  20:30:00  dclemans
X * Fix bug with shell variable references in history lists.
X * 
X * Revision 1.4  89/02/21  08:36:51  dclemans
X * Implement pseudo-signals ERR, EXIT, DEBUG
X * 
X * Revision 1.3  89/02/20  22:54:24  dclemans
X * correct signal names
X * 
X * Revision 1.2  89/02/20  22:49:11  dclemans
X * bug fix
X * implement kill -l
X * 
X * Revision 1.1  89/02/20  22:27:23  dclemans
X * Initial revision
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X
X#ifndef GEMDOS
X#include <signal.h>
X#else
X#define NSIG    0
X#endif  /* GEMDOS */
X
Xstatic char *signal_map[] =
X{
X    "EXIT",
X#ifndef GEMDOS
X    "HUP",
X    "INT",
X    "QUIT",
X    "ILL",
X    "TRAP",
X    "IOT",
X    "EMT",
X    "FPE",
X    "KILL",
X    "BUS",
X    "SEGV",
X    "SYS",
X    "PIPE",
X    "ALRM",
X    "TERM",
X    "USR1",
X#if NSIG > 16
X    "USR2",
X    "CLD",
X    "",
X    "STOP",
X    "TSTP",
X    "",
X    "CHLD",
X    "TTIN",
X    "TTOU",
X    "TINT",
X    "XCPU",
X    "XFSZ",
X    "VTALRM",
X    "PROF",
X    "URG",
X    "WINCH",
X#endif
X#endif  /* GEMDOS */
X    "ERR",
X    "DEBUG",
X    (char *)NULL
X};
X
Xstruct  signal_record
X{
X    struct  phrase *action;
X    int     status;
X};
X#define IGNORED 0x0001
X#define ACTION  0x0002
Xstatic struct signal_record signals[NSIG+3];
X
Xstatic int signal_number(sig)
Xregister char *sig;
X{
X    register int i;
X    register char *cp;
X    char buffer[16];
X
X    for (i = 0; signal_map[i] != (char *)NULL; i++)
X    {   /* look for a match */
X        if (strlen(signal_map[i]) == 0)
X            continue;
X        if (strcmp(sig,signal_map[i]) == 0)
X            return i;
X        strcpy(buffer,signal_map[i]);
X        for (cp = buffer; *cp; cp++)
X            if (isupper(*cp))
X                *cp = _tolower(*cp);
X        if (strcmp(sig,buffer) == 0)
X            return i;
X    }
X    return -1;
X}   /* end of signal_name */
X
Xint signal_handler(sig)
Xint sig;
X{
X    register struct signal_record *this_sig;
X    char buffer[256];
X
X    if (sig >= 1 || sig <= NSIG)
X    {   /* need to reset the signal? */
X#ifndef GEMDOS
X#ifndef USG
X        /* don't need anything here; BSD semantics don't need resetting */
X#else
X        signal(sig,signal_handler);
X#endif  /* USG */
X#endif  /* GEMDOS */
X    }
X    if (sig < 0 || sig >= NSIG+3)
X        return -1;
X    this_sig = &signals[sig];
X    if (this_sig->status & IGNORED)
X        return 0;
X    if (this_sig->status & ACTION)
X    {   /* execute the specified phrase */
X        if (this_sig->action != (struct phrase *)NULL)
X            exec_phrase(this_sig->action,0);
X        return 0;
X    }
X    if (sig == 0 || sig > NSIG)
X        return 0;
X    errmsg(0,LOC("signal_handler"),"received signal #%d; exiting.",sig);
X    cmd_forceexit = 1;
X    return 0;
X}   /* end of signal_handler */
X
Xvoid force_signal(name)
Xchar *name;
X{
X    register int sig;
X
X    sig = signal_number(name);
X    if (sig < 0)
X    {   /* if unknown signal */
X        errmsg(0,LOC("force_signal"),"unable to decode pseudo-signal name %s",name);
X        return;
X    }
X    signal_handler(sig);
X}   /* end of force_signal */
X
Xint signal_init()
X{
X#ifdef  never
X    /* don't know what to do here yet... don't want to hit every signal */
X    register int i;
X#ifndef GEMDOS
X#ifndef USG
X    struct sigvec handler;
X#endif  /* USG */
X#endif  /* GEMDOS */
X
X#ifndef GEMDOS
X#ifndef USG
X    handler.sv_handler = signal_handler;
X    handler.sv_onstack = 0;
X    for (i = 1; i <= NSIG; i++)
X    {   /* set up the appropriate handlers */
X        handler.sv_mask = sigmask(i);
X        sigvec(i,&handler,(struct sigvec *)NULL);
X    }
X#else
X    for (i = 1; i <= NSIG; i++)
X        signal(i,signal_handler);
X#endif  /* USG */
X#endif  /* GEMDOS */
X#endif  /* never */
X}   /* end of signal_init */
X
Xint cmd_trap(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X    register int sig;
X    struct phrase *torun,*toend,*np;
X    char *cp;
X    char tempfile[64];
X    struct token *action;
X    int status,fd;
X#ifndef GEMDOS
X#ifndef USG
X    struct sigvec handler;
X#endif  /* USG */
X#endif  /* GEMDOS */
X
X    if (pp->body->next == (struct token *)NULL)
X    {   /* any parms? */
X        errmsg(0,LOC("cmd_trap"),"missing action or condition args");
X        return -1;
X    }
X    action = pp->body->next;
X    tp = action->next;
X    if (tp == (struct token *)NULL)
X    {   /* any conditions? */
X        errmsg(0,LOC("cmd_trap"),"missing condition args");
X        return -1;
X    }
X    if (strlen(action->name) == 0)
X    {   /* ignore signal action? */
X        action = (struct token *)NULL;
X        status = IGNORED;
X    }
X    else if (strcmp(action->name,"-") == 0)
X    {   /* reset signal action? */
X        action = (struct token *)NULL;
X        status = 0;
X    }
X    else status = ACTION;
X    if (action->type == SYM_NUMBER || signal_number(action->name) >= 0)
X    {   /* was a action REALLY specified? */
X        tp = action;
X        status = 0;
X        action = (struct token *)NULL;
X    }
X    torun = (struct phrase *)NULL;
X    if (action != (struct token *)NULL)
X    {   /* need to reparse tokens into an executable phrase */
X        cp = strcopy(action->name);
X        if (cp == (char *)NULL)
X        {   /* enough memory? */
X            /* message already printed */
X            return -1;
X        }
X        stripquotes(cp);
X#ifdef  GEMDOS
X        sprintf(tempfile,"%stemp%d.tmp",base_env.tmpdir,base_env.temp_count++);
X#else
X        sprintf(tempfile,"/tmp/sh%d.temp",base_env.temp_count++);
X#endif  /* GEMDOS */
X        fd = creat(tempfile,0666);
X        if (fd < 0)
X        {   /* did we get a temporary file? */
X            errmsg(0,LOC("cmd_trap"),"unable to open tempfile");
X            return 1;
X        }
X        write(fd,cp,strlen(cp));
X        write(fd,"\n",1);
X        close(fd);
X        free(cp);
X
X        if (io_pushfile(tempfile,1,0,1) < 0)
X        {   /* if saving the file failed */
X            errmsg(0,LOC("cmd_trap"),"unable to put tempfile into input stream");
X            delete_later(tempfile);
X            return 1;
X        }
X        torun = toend = (struct phrase *)NULL;
X        while ((np = lex_phrase(1,0)) != (struct phrase *)NULL)
X        {   /* get back contents of file */
X            if (np->type != (struct token *)NULL && np->type->type == SYM_MARKER)
X            {   /* reached end of file */
X                phrase_free(np);
X                break;
X            }
X            if (torun == (struct phrase *)NULL)
X                torun = toend = np;
X            else
X            {   /* tack onto end */
X                toend->next = np;
X                toend = np;
X            }
X        }
X    }
X
X    while (tp != (struct token *)NULL)
X    {   /* while there is a condition we need to set */
X        if (tp->type == SYM_NUMBER)
X            sig = atoi(tp->name);
X        else sig = signal_number(tp->name);
X        if (sig < 0 || sig >= NSIG+3)
X        {   /* good signal number? */
X            errmsg(0,LOC("cmd_trap"),"bad signal number %s=%d",tp->name,sig);
X            tp = tp->next;
X            continue;
X        }
X        if (signals[sig].action != (struct phrase *)NULL)
X        {   /* free old action? */
X            phrase_free(signals[sig].action);
X            signals[sig].action = (struct phrase *)NULL;
X        }
X        switch (status)
X        {   /* what to do... */
X            case ACTION:
X#ifndef GEMDOS
X                if (sig >= 1 && sig <= NSIG)
X                {   /* if a valid system signal */
X#ifndef USG
X                    handler.sv_handler = signal_handler;
X                    handler.sv_mask = sigmask(sig);
X                    handler.sv_onstack = 0;
X                    sigvec(sig,&handler,(struct sigvec *)NULL);
X#else
X                    signal(sig,signal_handler);
X#endif  /* USG */
X                }
X#endif  /* GEMDOS */
X                signals[sig].status = ACTION;
X                if (torun != (struct phrase *)NULL)
X                    signals[sig].action = copy_phrase(torun,0,0,1);
X                break;
X            case IGNORED:
X#ifndef GEMDOS
X                if (sig >= 1 && sig <= NSIG)
X                {   /* if a valid system signal */
X#ifndef USG
X                    handler.sv_handler = SIG_IGN;
X                    handler.sv_mask = sigmask(sig);
X                    handler.sv_onstack = 0;
X                    sigvec(sig,&handler,(struct sigvec *)NULL);
X#else
X                    signal(sig,SIG_IGN);
X#endif  /* USG */
X                }
X#endif  /* GEMDOS */
X                signals[sig].status = IGNORED;
X                break;
X            default:
X#ifndef GEMDOS
X                if (sig >= 1 && sig <= NSIG)
X                {   /* if a valid system signal */
X#ifndef USG
X                    handler.sv_handler = SIG_DFL;
X                    handler.sv_mask = sigmask(sig);
X                    handler.sv_onstack = 0;
X                    sigvec(sig,&handler,(struct sigvec *)NULL);
X#else
X                    signal(sig,SIG_DFL);
X#endif  /* USG */
X                }
X#endif  /* GEMDOS */
X                signals[sig].status = 0;
X                break;
X        }
X        tp = tp->next;
X    }
X    if (torun != (struct phrase *)NULL)
X        phrase_free(torun);
X
X    return 0;
X}   /* end of cmd_trap */
X
X#ifndef GEMDOS
Xint cmd_kill(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X    register int sig;
X    register char *p;
X    struct bg_job *job;
X    struct procs *cmd;
X    char *savep;
X    int i,ncol,maxlength;
X    char buffer[BUFSIZ];
X
X    tp = pp->body->next;
X    if (tp == (struct token *)NULL)
X    {   /* anything to do? */
X        errmsg(0,LOC("cmd_kill"),"no args specified");
X        return -1;
X    }
X    if (strcmp(tp->name,"-l") == 0 || strcmp(tp->name,"-L") == 0)
X    {   /* list out known signal names */
X        maxlength = 0;
X        for (sig = 1; sig <= NSIG; sig++)
X            if (strlen(signal_map[sig]) > maxlength)
X                maxlength = strlen(signal_map[sig]);
X        maxlength += 7;     /* for column sep, etc. */
X        ncol = base_env.columns / maxlength;
X        if ((NSIG < (base_env.lines*2/3)) || (ncol < 1))
X        {   /* just a single list? */
X            for (sig = 1; sig <= NSIG; sig++)
X            {   /* print the menu */
X                sprintf(buffer,"%d) %s\n",sig,signal_map[sig]);
X                io_writestring(0,buffer);
X            }
X        }
X        else
X        {   /* a multi-column list? */
X            for (sig = 1; sig <= NSIG; )
X            {   /* build lines... */
X                p = buffer;
X                *p = '\0';
X                for (i = 0; i < ncol && (sig+i) <= NSIG; i++)
X                {   /* for each column */
X                    sprintf(p,"%d) %s",sig+i,signal_map[sig+i]);
X                    savep = p;
X                    while (*p)
X                        p++;
X                    while ((int)(p - savep) < maxlength)
X                        *p++ = ' ';
X                    *p = '\0';
X                }
X                sig += ncol;
X                strcpy(p,"\n");
X                io_writestring(0,buffer);
X            }
X        }
X        return 0;
X    }
X    if (tp->name[0] != '-')
X        sig = SIGTERM;
X    else
X    {   /* decode the value */
X        sig = atoi(&tp->name[1]);
X        if (sig < 1 || sig > NSIG)
X            sig = signal_number(&tp->name[1]);
X        if (sig < 1 || sig > NSIG)
X        {   /* a good value? */
X            errmsg(0,LOC("cmd_kill"),"bad signal number %d; range is 1 <-> %d",sig,NSIG);
X            return 1;
X        }
X        tp = tp->next;
X    }
X    for (; tp != (struct token *)NULL; tp = tp->next)
X    {   /* send sig to the specified pids */
X        if (tp->name[0] == '%')
X        {   /* check in pending jobs list? */
X            i = 1;
X            for (job = base_env.jobs; job != (struct bg_job *)NULL; job = job->next)
X            {   /* found what we are looking for? */
X                if (i == atoi(&tp->name[1]))
X                    break;
X                i++;
X            }
X            if (job == (struct bg_job *)NULL)
X            {   /* if we didn't find the job */
X                errmsg(0,LOC("cmd_kill"),"job %s doesn't exist",tp->name);
X                continue;
X            }
X            for (cmd = job->cmds; cmd != (struct procs *)NULL; cmd = cmd->next)
X            {   /* kill each process involved in job */
X                if (kill(cmd->pid,sig) < 0)
X                    errmsg(0,LOC("cmd_kill"),"unable to send signal %d to process %d",sig,cmd->pid);
X            }
X            continue;
X        }
X        if (kill(atoi(tp->name),sig) < 0)
X            errmsg(0,LOC("cmd_kill"),"unable to send signal %d to process %s",sig,tp->name);
X    }
X
X    return 0;
X}   /* end of cmd_kill */
X
Xvoid cmd_jobs_dump(curr,num,stat)
Xstruct phrase *curr;
Xint num;
Xchar *stat;
X{
X    char buffer[33];
X    register struct phrase *pp;
X
X    if (curr == (struct phrase *)NULL)
X        return;
X    sprintf(buffer,"[%d] + %s",num,stat);
X    while (strlen(buffer) < 32)
X        strcat(buffer," ");
X    io_writestring(0,buffer);
X    for (pp = curr; pp != (struct phrase *)NULL; pp = pp->next)
X    {   /* dump out the lines */
X        phrase_dump(pp,1,32);
X        if (pp->next != (struct phrase *)NULL)
X            io_writestring(0,"\n                                ");
X    }
X}   /* end of cmd_jobs_dump */
X
Xint cmd_jobs()
X{
X    register int cmd;
X    register struct bg_job *job;
X
X    exec_waitfor();
X    cmd = 1;
X    for (job = base_env.jobs; job != (struct bg_job *)NULL; job = job->next)
X    {   /* step through pending jobs */
X        cmd_jobs_dump(job->job,cmd,"Running");
X        cmd++;
X    }
X}   /* end of cmd_jobs */
X#endif  /* GEMDOS */
END_OF_FILE
if test 14049 -ne `wc -c <'trap.c'`; then
    echo shar: \"'trap.c'\" unpacked with wrong size!
fi
# end of 'trap.c'
fi
echo shar: End of archive 2 \(of 11\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 11)."
# Contents:  README alias.c alloca.c makefile.st makefile.unx malloc.c
#   ndir.c ndir.h reader reader/emacs.c reader/history.h
#   reader/killring.c reader/makefile.st reader/makefile.unx
#   reader/tester.c version.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:03:50 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2986 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XShell				Dave Clemans
X				12/89 <-> 1/89
X
XA command line interpreter whose syntax is based on the
XUnix "Korn" shell.  While not guaranteeing complete
Xsyntactical and semantic compatibility with the "Korn"
Xshell, the intention is that this shell will be close
Xenough to run most "Korn" shell scripts transparently
X(after accounting for the difference in filename syntax
Xbetween Unix and other systems).
X
XDocumentation: (until I come up with some)
X    The Korn SHell Command and Programming Language
X        Morris I. Bolsky & David G. Korn
X    The KSH Reference Card (correct title?)
X        (author?)
X
XCompiling:
X	Atari ST
X		Mark Williams C V3.0
X			supports Mark Williams C method for passing
X			more than 127 argument characters to executed
X			programs
X		use makefile.st with "make"
X        Only tested on TOS 1.4; might not work on earlier versions
X
X		-DGEMDOS=1 -DMWC -DMWC_ARGV -DMYMALLOC=1
X
X	Unix
X		use makefile.unx
X
X		(BSD systems) -Dunix=1
X		(SYSV systems) -Dunix=1 -DUSG=1
X
XVersion 0.0:			1/23/89
X
X	basic shell functionality
X	version builtin command gives current version
X	memory builtin command gives info about memory usage
X	pipelines
X	backquoted substitution in commands
X	aliases (including tracked aliases)
X	shell variables
X	compound statements (for, if, case, while, until, select)
X	parenthesized commands
X	shell functions
X	"emacs" and "vi" input modes
X	Tenex-like command and file-name completion
X	etc.
X
XVersion 0.1:			2/5/89
X
X	shell command history; HISTSIZE variable
X	sleep builtin command
X	time builtin command
X	eval builtin command
X	exec builtin command
X	fc builtin command
X	memory builtin command gives more information
X	sizes of stack and dynamic memory have been
X	made more realistic
X        fix shell aliases so that they work more like
X	what would be "expected"
X	fix bug in compound statements that lets
X	commands like the following work.
X
X		for i in h:\cm*.arc
X		do
X			j=${i##*\}
X			k=${j%%.*}
X			echo $k
X		done
X
X	fix bug in the history list manager used by the
X	"emacs" and "vi" line editors so that the size of
X	the list can be changed dynamically.  The default
X	size of the list is 32 lines; changing the HISTSIZE
X	shell variable changes the size of both the line
X	list used by the editors, and the statement list
X	used by the fc command.
X
XVersion 0.2:            2/20/89
X
X	initial steps to get this running under Unix
X	(i.e. no job control yet)
X	many bug fixes (too many to mention)
X	full expression support (using code from GNU cccp)
X	the emacs&vi code doesn't seem to work at the moment under Unix
X
XVersion 0.3:            3/7/89
X
X	initial coding of trap and kill builtin commands
X    Build RCS structure containing all released past versions;
X    all future development will be recorded here
X    RANDOM and SECONDS shell variable implemented
X    more typeset attributes implemented (basically everything is
X        there now except non-base 10 numbers)
X    [[ and ]] brackets implemented
X    the emacs&vi code now should work on bsd4.3 (but not on Sys V)
END_OF_FILE
if test 2986 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'alias.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'alias.c'\"
else
echo shar: Extracting \"'alias.c'\" \(5902 characters\)
sed "s/^X//" >'alias.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Command Aliasing
X *
X * $Id: alias.c,v 1.4 89/02/20 20:15:24 dclemans Exp $
X *
X * $Log:	alias.c,v $
X * Revision 1.4  89/02/20  20:15:24  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X
Xstatic char *def_aliases[] =
X{
X    "autoload",         "typeset -fu",
X    "echo",             "print -",
X    "false",            "let 0",   
X    "functions",        "typeset -f",
X    "hash",             "alias -t",
X    "history",          "fc -l",
X    "integer",          "typeset -i",
X    "r",                "fc -e -",
X    "source",           ".",
X    "true",             ":",
X    "type",             "whence -v",
X    (char *)NULL,       (char *)NULL
X};
X
Xstatic void dump_alias(svp)
Xstruct aliases *svp;
X{
X    register struct token *tp;
X
X    io_writestring(0,svp->name);
X    io_writestring(0,"=");
X    for (tp = svp->tp; tp != (struct token *)NULL; tp = tp->next)
X    {   /* dump each token */
X        io_writestring(0,tp->name);
X        if (tp->next != (struct token *)NULL)
X            io_writestring(0," ");
X    }
X    io_writestring(0,"\n");
X}   /* end of dump_alias */
X
Xstruct aliases *alias_get(name)
Xregister char *name;
X{
X    register struct aliases *svp;
X    register int rc;
X
X    for (svp = base_env.alias_table; svp != (struct aliases *)NULL; )
X    {   /* look for where to put the variable */
X        rc = strcmp(name,svp->name);
X        if (rc == 0)
X        {   /* found it */
X            return svp;
X        }
X        else if (rc < 0)
X        {   /* go down the left side? */
X            if (svp->left == (struct aliases *)NULL)
X                break;
X            svp = svp->left;
X        }
X        else
X        {   /* go down right side */
X            if (svp->right == (struct aliases *)NULL)
X                break;
X            svp = svp->right;
X        }
X    }
X    return (struct aliases *)NULL;
X}   /* end of alias_get */
X
Xvoid alias_define(name,value,type)
Xchar *name;
Xstruct token *value;
Xint type;
X{
X    register struct aliases *vp,*svp;
X    register struct token *tp;
X    struct token *otp,*last;
X    int rc;
X
X    vp = new_alias();
X    if (vp == (struct aliases *)NULL)
X    {   /* enough memory */
X        errmsg(SHERR_NOMEM,LOC("alias_define"));
X        return;
X    }
X    last = (struct token *)NULL;
X    for (tp = value; tp != (struct token *)NULL; tp = tp->next)
X    {   /* for each value token */
X        otp = new_token(strlen(tp->name));
X        if (otp == (struct token *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("alias_define"));
X            tokens_free(vp->tp);
X            free(vp);
X            return;
X        }
X        otp->type = tp->type;
X        strcpy(otp->name,tp->name);
X        if (vp->tp == (struct token *)NULL)
X            vp->tp = last = otp;
X        else
X        {   /* tack onto end */
X            last->next = otp;
X            last = otp;
X        }
X    }
X
X    rc = 0;
X    for (svp = base_env.alias_table; svp != (struct aliases *)NULL; )
X    {   /* look for where to put the variable */
X        rc = strcmp(name,svp->name);
X        if (rc == 0)
X        {   /* replace existing value */
X            tokens_free(svp->tp);
X            svp->tp = vp->tp;
X            svp->type = type;
X            free(vp);
X            return;
X        }
X        else if (rc < 0)
X        {   /* go down the left side? */
X            if (svp->left == (struct aliases *)NULL)
X                break;
X            svp = svp->left;
X        }
X        else
X        {   /* go down right side */
X            if (svp->right == (struct aliases *)NULL)
X                break;
X            svp = svp->right;
X        }
X    }
X
X    vp->name = strcopy(name);
X    vp->type = type;
X    if (base_env.alias_table == (struct aliases *)NULL)
X        base_env.alias_table = vp;
X    else if (rc < 0)
X        svp->left = vp;
X    else
X        svp->right = vp;
X}   /* end of alias_define */
X
Xvoid alias_sdefine(name,value,type)
Xchar *name;
Xchar *value;
Xint type;
X{
X    register struct token *tp,*ftp,*ltp;
X    struct strsave *savep;
X
X    savep = strings;
X    io_pushtoken(value,1);
X    ftp = ltp = (struct token *)NULL;
X    while (!lex_sempty(savep))
X    {   /* lex the alias value into tokens */
X        tp = lex_token(1);
X        if (tp == (struct token *)NULL)
X            break;
X        if (ftp == (struct token *)NULL)
X            ftp = ltp = tp;
X        else
X        {   /* tack onto end */
X            ltp->next = tp;
X            ltp = tp;
X        }
X    }
X    alias_define(name,ftp,type);
X    tokens_free(ftp);
X}   /* end of alias_sdefine */
X
Xint alias_tracked(name)
Xchar *name;
X{
X    register struct aliases *svp;
X
X    svp = alias_get(name);
X    if (svp != (struct aliases *)NULL)
X    {   /* if the alias was found */
X        return svp->type & TYPE_TRACKED;
X    }
X    return 0;
X}   /* end of alias_tracked */
X
Xint alias_print(name)
Xchar *name;
X{
X    register struct aliases *svp;
X
X    svp = alias_get(name);
X    if (svp != (struct aliases *)NULL)
X    {   /* if variable was found */
X        if (svp->tp != (struct token *)NULL)
X            dump_alias(svp);
X        return 0;
X    }
X    return 1;
X}   /* end of alias_print */
X
Xvoid alias_dump(vp,type)
Xregister struct aliases *vp;
Xint type;
X{
X    if (vp == (struct aliases *)NULL)
X        return;
X    if (vp->left != (struct aliases *)NULL)
X        alias_dump(vp->left,type);
X    if (vp->tp != (struct token *)NULL &&
X        ((type == 0) || (vp->type & type)))
X    {   /* if the alias is really defined and type matches */
X        dump_alias(vp);
X    }
X    if (vp->right != (struct aliases *)NULL)
X        alias_dump(vp->right,type);
X}   /* end of alias_dump */
X
Xvoid alias_init()
X{
X    register int i;
X
X    for (i = 0; def_aliases[i] != (char *)NULL; i += 2)
X    {   /* for each default alias */
X        alias_sdefine(def_aliases[i],def_aliases[i+1],0);
X    }
X}   /* end of alias_init */
END_OF_FILE
if test 5902 -ne `wc -c <'alias.c'`; then
    echo shar: \"'alias.c'\" unpacked with wrong size!
fi
# end of 'alias.c'
fi
if test -f 'alloca.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'alloca.c'\"
else
echo shar: Extracting \"'alloca.c'\" \(5656 characters\)
sed "s/^X//" >'alloca.c' <<'END_OF_FILE'
X/*
X	alloca -- (mostly) portable public-domain implementation -- D A Gwyn
X	last edit:	86/05/30	rms
X	   include config.h, since on VMS it renames some symbols.
X	   Use xmalloc instead of malloc.
XCBARRON:
X	xmalloc is just malloc with a error message if malloc
X	fails because of being out of memory.Use malloc instead
X
X	 David
X
X	This implementation of the PWB library alloca() function,
X	which is used to allocate space off the run-time stack so
X	that it is automatically reclaimed upon procedure exit,
X	was inspired by discussions with J. Q. Johnson of Cornell.
X	It should work under any C implementation that uses an
X	actual procedure stack (as opposed to a linked list of
X	frames).  There are some preprocessor constants that can
X	be defined when compiling for your specific system, for
X	improved efficiency; however, the defaults should be okay.
X	The general concept of this implementation is to keep
X	track of all alloca()-allocated blocks, and reclaim any
X	that are found to be deeper in the stack than the current
X	invocation.  This heuristic does not reclaim storage as
X	soon as it becomes invalid, but it will do so eventually.
X	As a special case, alloca(0) reclaims storage without
X	allocating any.  It is a good idea to use alloca(0) in
X	your main control loop, etc. to force garbage collection.
X *
X * $Id: alloca.c,v 1.2 89/02/20 20:15:36 dclemans Exp $
X *
X * $Log:	alloca.c,v $
X * Revision 1.2  89/02/20  20:15:36  dclemans
X * Add RCS identifiers
X * 
X*/
X#ifdef MWC
X#undef lint
X#undef emacs
X#undef XJ311
X#endif
X
X#ifndef lint
Xstatic char	SCCSid[] = "@(#)alloca.c	1.1";	/* for the "what" utilit
Xy */
X#endif
X#ifdef emacs
X#include "config.h"
X#ifdef static
X/* actually, only want this if static is defined as ""
X   -- this is for usg, in which emacs must undefine static
X   in order to make unexec workable
X   */
X#ifndef STACK_DIRECTION
Xyou
Xlose
X-- must know STACK_DIRECTION at compile-time
X#endif /* STACK_DIRECTION undefined */
X#endif static
X#endif emacs
X#ifdef X3J11
Xtypedef void	*pointer;		/* generic pointer type */
X#else
Xtypedef char	*pointer;		/* generic pointer type */
X#endif
X#define	NULL	0			/* null pointer constant */
Xextern void	free();
X#ifndef MWC
Xextern pointer	xmalloc();
X#else
Xextern char *malloc();
X#define xmalloc malloc
X#endif
X/*
X	Define STACK_DIRECTION if you know the direction of stack
X	growth for your system; otherwise it will be automatically
X	deduced at run-time.
X	STACK_DIRECTION > 0 => grows toward higher addresses
X	STACK_DIRECTION < 0 => grows toward lower addresses
X	STACK_DIRECTION = 0 => direction of growth unknown */
X#ifdef MWC
X#define STACK_DIRECTION 0
X#define pointer char *
X#endif
X#ifndef STACK_DIRECTION
X#define	STACK_DIRECTION	0		/* direction unknown */
X#endif
X#if STACK_DIRECTION != 0
X#define	STACK_DIR	STACK_DIRECTION	/* known at compile-time */
X#else	/* STACK_DIRECTION == 0; need run-time code */
Xstatic int	stack_dir;		/* 1 or -1 once known */
X#define	STACK_DIR	stack_dir
Xstatic void
Xfind_stack_direction (/* void */)
X{
X  static char	*addr = NULL;	/* address of first
X				   `dummy', once known */
X  auto char	dummy;		/* to get stack address */
X  if (addr == NULL)
X    {				/* initial entry */
X      addr = &dummy;
X      find_stack_direction ();	/* recurse once */
X    }
X  else				/* second entry */
X    if (&dummy > addr)
X      stack_dir = 1;		/* stack grew upward */
X    else
X      stack_dir = -1;		/* stack grew downward */
X}
X#endif	/* STACK_DIRECTION == 0 */
X/*
X	An "alloca header" is used to:
X	(a) chain together all alloca()ed blocks;
X	(b) keep track of stack depth.
X	It is very important that sizeof(header) agree with malloc()
X	alignment chunk size.  The following default should work okay.*/
X#ifndef	ALIGN_SIZE
X#define	ALIGN_SIZE	sizeof(double)
X#endif
Xtypedef union hdr
X{
X  char	align[ALIGN_SIZE];	/* to force sizeof(header) */
X  struct
X    {
X      union hdr *next;		/* for chaining headers */
X      char *deep;		/* for stack depth measure */
X    } h;
X} header;
X/*
X	alloca( size ) returns a pointer to at least `size' bytes of
X	storage which will be automatically reclaimed upon exit from
X	the procedure that called alloca().  Originally, this space
X	was supposed to be taken from the current stack frame of the
X	caller, but that method cannot be made to work for some
X	implementations of C, for example under Gould's UTX/32. */
Xstatic header *last_alloca_header = NULL; /* -> last alloca header */
Xpointer
Xalloca (size)			/* returns pointer to storage */
X     unsigned	size;		/* # bytes to allocate */
X{
X  auto char	probe;		/* probes stack depth: */
X  register char	*depth = &probe;
X#if STACK_DIRECTION == 0
X  if (STACK_DIR == 0)		/* unknown growth direction */
X    find_stack_direction ();
X#endif
X				/* Reclaim garbage, defined as all alloca()ed st
Xorage that
X				   was allocated from deeper in the stack than c
Xurrently. */
X  {
X    register header	*hp;	/* traverses linked list */
X    for (hp = last_alloca_header; hp != NULL;)
X      if (STACK_DIR > 0 && hp->h.deep > depth
X	  || STACK_DIR < 0 && hp->h.deep < depth)
X	{
X	  register header	*np = hp->h.next;
X	  free ((pointer) hp);	/* collect garbage */
X	  hp = np;		/* -> next header */
X	}
X      else
X	break;			/* rest are not deeper */
X    last_alloca_header = hp;	/* -> last valid storage */
X  }
X  if (size == 0)
X    return NULL;		/* no allocation required */
X  /* Allocate combined header + user data storage. */
X  {
X    register pointer	new = xmalloc (sizeof (header) + size);
X    /* address of header */
X    ((header *)new)->h.next = last_alloca_header;
X    ((header *)new)->h.deep = depth;
X    last_alloca_header = (header *)new;
X    /* User storage begins just after header. */
X    return (pointer)((char *)new + sizeof(header));
X  }
X}
END_OF_FILE
if test 5656 -ne `wc -c <'alloca.c'`; then
    echo shar: \"'alloca.c'\" unpacked with wrong size!
fi
# end of 'alloca.c'
fi
if test -f 'makefile.st' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile.st'\"
else
echo shar: Extracting \"'makefile.st'\" \(1349 characters\)
sed "s/^X//" >'makefile.st' <<'END_OF_FILE'
X#
X# Command Input Shell
X# Dave Clemans
X# 12/88-1/89
X#
X# Makefile
X#
X# $Id: makefile.st,v 1.6 89/02/25 17:40:14 dclemans Exp $
X#
X# $Log:	makefile.st,v $
X# Revision 1.6  89/02/25  17:40:14  dclemans
X# miscellaneous bug fixes/speedups
X# 
X# Revision 1.5  89/02/20  22:35:39  dclemans
X# Add a "clean" action
X# 
X# Revision 1.4  89/02/20  22:32:09  dclemans
X# Add in new file that defines traps
X# 
X# Revision 1.3  89/02/20  20:12:02  dclemans
X# Add RCS identifiers
X# 
XCFLAGS	= -O -VPEEP -VPSTR -DGEMDOS=1 -DMWC_ARGV=1 -DMWC=1 -DMYMALLOC=1 -DLINED=1
XLFLAGS	= -VPSTR
XOFILES	= main.o version.o lex.o exec.o var.o cexp_tab.o alias.o io.o \
X	cmd1.o cmd2.o cmd3.o trap.o wild.o util.o ndir.o alloca.o malloc.o
X
Xshell.ttp: $(OFILES) reader\libreader.a
X	$(CC) $(LFLAGS) $(OFILES) reader\libreader.a -o $@
X
Xcexp_tab.c:	cexp.y
X	f:\gnu\bison\bison.prg cexp.y
X
Xcexp_tab.o:	cexp_tab.c shell.h
Xmain.o:		main.c shell.h
Xversion.o:	version.c shell.h
Xlex.o:		lex.c shell.h
Xexec.o:		exec.c shell.h
Xvar.o:		var.c shell.h
Xexp.o:		exp.c shell.h
Xalias.o:	alias.c shell.h
Xio.o:		io.c shell.h
Xcmd1.o:		cmd1.c shell.h
Xcmd2.o:		cmd2.c shell.h
Xcmd3.o:		cmd3.c shell.h
Xtrap.o:		trap.c shell.h
Xwild.o:		wild.c shell.h ndir.h
Xndir.o:		ndir.c ndir.h
Xutil.o:		util.c shell.h
Xalloca.o:	alloca.c
Xmalloc.o:	malloc.c
X
Xreader\libreader.a:
X	cd reader;make
X
Xclean:
X	rm -f shell.ttp *.o cexp_tab.c
END_OF_FILE
if test 1349 -ne `wc -c <'makefile.st'`; then
    echo shar: \"'makefile.st'\" unpacked with wrong size!
fi
# end of 'makefile.st'
fi
if test -f 'makefile.unx' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile.unx'\"
else
echo shar: Extracting \"'makefile.unx'\" \(1239 characters\)
sed "s/^X//" >'makefile.unx' <<'END_OF_FILE'
X#
X# Command Input Shell
X# Dave Clemans
X# 12/88-1/89
X#
X# Makefile
X#
X# $Id: makefile.unx,v 1.5 89/02/25 17:40:18 dclemans Exp $
X#
X# $Log:	makefile.unx,v $
X# Revision 1.5  89/02/25  17:40:18  dclemans
X# miscellaneous bug fixes/speedups
X# 
X# Revision 1.4  89/02/20  22:35:45  dclemans
X# Add a "clean" action
X# 
X# Revision 1.3  89/02/20  22:32:21  dclemans
X# Add in new file that defines traps
X# 
X# Revision 1.2  89/02/20  20:09:34  dclemans
X# Add RCS identifiers
X# 
XCFLAGS	= -O
XLFLAGS	= 
XOFILES	= main.o version.o lex.o exec.o var.o cexp.tab.o alias.o io.o \
X	cmd1.o cmd2.o cmd3.o trap.o wild.o util.o
X
X#shell.ttp: $(OFILES) reader/libreader.a
X#	$(CC) $(LFLAGS) $(OFILES) reader/libreader.a -o $@
Xshell: $(OFILES)
X	$(CC) $(LFLAGS) $(OFILES) -o $@
X
Xcexp.tab.c: cexp.y
X	bison cexp.y
Xcexp.tab.o: cexp.tab.c shell.h
X	$(CC) $(CFLAGS) -c -Dconst= cexp.tab.c
X
Xmain.o:		main.c shell.h
Xversion.o:	version.c shell.h
Xlex.o:		lex.c shell.h
Xexec.o:		exec.c shell.h
Xvar.o:		var.c shell.h
Xalias.o:	alias.c shell.h
Xio.o:		io.c shell.h
Xcmd1.o:		cmd1.c shell.h
Xcmd2.o:		cmd2.c shell.h
Xcmd3.o:		cmd3.c shell.h
Xtrap.o:		trap.c shell.h
Xwild.o:		wild.c shell.h ndir.h
Xutil.o:		util.c shell.h
X
Xreader/libreader.a:
X	cd reader;make
X
Xclean:
X	rm -f shell *.o cexp.tab.c
END_OF_FILE
if test 1239 -ne `wc -c <'makefile.unx'`; then
    echo shar: \"'makefile.unx'\" unpacked with wrong size!
fi
# end of 'makefile.unx'
fi
if test -f 'malloc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'malloc.c'\"
else
echo shar: Extracting \"'malloc.c'\" \(8160 characters\)
sed "s/^X//" >'malloc.c' <<'END_OF_FILE'
X/*
X * Dynamic Memory Allocation Routines
X *
X *  malloc  - allocate a block of memory
X *      char *malloc(unsigned size);
X *  char *lmalloc(unsigned long size);
X *
X *  calloc  - allocate a block of memory, clearing it to zero first
X *      char *calloc(unsigned unitsize,unsigned nunits);
X *  char *lcalloc(unsigned long unitsize,unsigned long nunits);
X *
X *  realloc - reallocate a block of memory, making it smaller or larger
X *  char *realloc(char *ptr,unsigned size);
X *  char *lrealloc(char *ptr,unsigned long size);
X *
X *  free    - free a block of memory
X *      free(char *ptr);
X *
X * Dave Clemans, 1/88
X *
X * $Id: malloc.c,v 1.3 89/02/20 20:09:31 dclemans Exp $
X *
X * $Log:	malloc.c,v $
X * Revision 1.3  89/02/20  20:09:31  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#ifndef unix
X#include <osbind.h>
X#endif
X
X#ifdef  unix
Xextern char *sbrk();
X#endif
X
Xextern char *malloc(),*lmalloc();
Xextern char *calloc(),*lcalloc();
Xextern char *realloc(),*lrealloc();
Xextern free();
X
X/*
X * Set up the arena we work in
X */
X#define HUNK    (18L*1024L)         /* basic hunk size for allocation */
X
Xstruct memblock
X{
X    long    size;
X    struct memblock *next;
X};
X
Xstatic struct memblock *arena = (struct memblock *)NULL;
Xstatic struct memblock *arenaTail = (struct memblock *)NULL;
Xlong poolSize = 0;
Xlong mallocTotal = 0;
Xlong mallocHighWater = 0;
X
X/*
X * The user-visible routines are merely shells; they get memory
X * by calling the following routines.  They are declared as
X * "long *" because they guarantee returning a memory block
X * with a starting address suitable for storing a long.
X */
Xstatic long *memget();
Xstatic memput();
X
X/*
X * Get a block of memory
X */
Xchar *malloc(size)
Xunsigned size;
X{
X    return lmalloc((unsigned long)size);
X}   /* end of malloc */
X
Xchar *lmalloc(size)
Xunsigned long size;
X{
X    register long *mem;
X    register long rsize;
X
X    rsize = size + sizeof(long);
X    rsize = ((rsize + (long)sizeof(long) - 1L) / (long)sizeof(long)) * (long)sizeof(long);
X    mem = memget(rsize);
X    if (mem == (long *)NULL)
X        return (char *)NULL;
X    *mem++ = rsize;
X    mallocTotal += rsize;
X    if (mallocTotal > mallocHighWater)
X        mallocHighWater = mallocTotal;
X    return (char *)mem;
X}   /* end of lmalloc */
X
X/*
X * Get a block of memory, clearing it to nulls
X */
Xchar *calloc(unitsize,nunits)
Xunsigned unitsize;
Xunsigned nunits;
X{
X    return lcalloc((unsigned long)unitsize,(unsigned long)nunits);
X}   /* end of calloc */
X
Xchar *lcalloc(unitsize,nunits)
Xunsigned long unitsize;
Xunsigned long nunits;
X{
X    register long rsize,size;
X    register char *ptr;
X    register long *mem;
X
X    size = (long)unitsize * (long)nunits;
X    rsize = size + sizeof(long);
X    rsize = ((rsize + (long)sizeof(long) - 1L) / (long)sizeof(long)) * (long)sizeof(long);
X    mem = memget(rsize);
X    ptr = (char *)mem;
X    if (mem == (long *)NULL)
X        return (char *)NULL;
X    while (size--)
X        *ptr++ = '\0';
X    *mem++ = rsize;
X    mallocTotal += rsize;
X    if (mallocTotal > mallocHighWater)
X        mallocHighWater = mallocTotal;
X    return (char *)mem;
X}   /* end of lcalloc */
X
X/*
X * Reallocate a block of memory
X */
Xchar *realloc(ptr,size)
Xchar *ptr;
Xunsigned size;
X{
X    return lrealloc(ptr,(unsigned long)size);
X}   /* end of realloc */
X
Xchar *lrealloc(ptr,size)
Xchar *ptr;
Xunsigned long size;
X{
X    long *mem;
X    register long actual_size;
X    register char *newptr;
X    register char *to,*from;
X
X    mem = (long *)ptr;
X    size = ((size + (long)sizeof(long) - 1L) / (long)sizeof(long)) * (long)sizeof(long);
X    actual_size = *--mem;
X
X    if ((size + sizeof(long) + sizeof(struct memblock)) < actual_size)
X    {   /* if the block is being shrunk */
X        newptr = ptr + size;
X        memput(newptr,actual_size - size - sizeof(long));
X        mallocTotal -= (actual_size - size - sizeof(long));
X        *mem = size + sizeof(long);
X    }
X    else
X    {   /* the block is being expanded */
X        newptr = (char *)memget(size + (long)sizeof(long));
X        if (newptr == (char *)NULL)
X            return newptr;
X        mallocTotal += size + sizeof(long);
X        if (mallocTotal > mallocHighWater)
X            mallocHighWater = mallocTotal;
X        actual_size -= sizeof(long);
X        to = newptr;
X        from = ptr;
X        while (actual_size--)
X            *to++ = *from++;
X        free(ptr);
X    }
X    return newptr;
X}   /* end of lrealloc */
X
X/*
X * Free a block of memory
X */
Xfree(ptr)
Xchar *ptr;
X{
X    register long *mem;
X    register long size;
X
X    mem = (long *)ptr;
X    size = *--mem;
X    memput((long *)mem,size);
X    mallocTotal -= size;
X}   /* end of free */
X
X/*
X * Finally! we reach the heart of this memory allocation package
X */
X
X/*
X * Allocate a block of memory
X */
Xstatic long *memget(size)
Xlong size;
X{
X    register struct memblock *mem,*memp;
X    register char *fptr;
X    register long toAlloc;
X    long *ptr;
X
X    if (size < sizeof(struct memblock))
X        size = sizeof(struct memblock);
X
X    memp = (struct memblock *)NULL;
X    for (mem = arena; mem != (struct memblock *)NULL; mem = mem->next)
X    {   /* look for a block with enough space */
X        if (mem->size == size || mem->size >= (size + sizeof(struct memblock)))
X        {   /* we've found some memory; unlink and return it */
X            if (memp == (struct memblock *)NULL)
X                arena = mem->next;
X            else
X                memp->next = mem->next;
X            if (mem == arenaTail)
X                arenaTail = memp;
X            if (mem->size > size)
X            {   /* if we need to split this block */
X                fptr = (char *)mem;
X                fptr += size;
X                memput((long *)fptr,mem->size - size);
X            }
X            return (long *)mem;
X        }
X        memp = mem;
X    }
X
X    /* no block with enough space; try to get more memory from system */
X    /* if that fails, we signal no memory */
X
X    toAlloc = (size < HUNK) ? HUNK : (((size + HUNK - 1L) / HUNK) * HUNK);
X#ifdef  unix
X    ptr = (long *)sbrk(toAlloc);
X#else
X    ptr = (long *)Malloc(toAlloc);
X#endif
X    if (ptr == (long *)NULL)
X        return (long *)NULL;
X    poolSize += toAlloc;
X    memput(ptr,toAlloc);
X    return memget(size);
X}   /* end of memget */
X
X/*
X * Free a block of memory
X */
Xstatic memput(ptr,size)
Xlong *ptr;
Xlong size;
X{
X    register struct memblock *mem,*memp;
X    register struct memblock *new;
X
X    if (size < sizeof(struct memblock))
X        size = sizeof(struct memblock);
X
X    memp = (struct memblock *)NULL;
X    for (mem = arena; mem != (struct memblock *)NULL; mem = mem->next)
X    {   /* look for place to put this freed block */
X        if (ptr < (long *)mem)
X        {   /* then we put the block before this block */
X            new = (struct memblock *)ptr;
X            new->size = size;
X            new->next = mem;
X            if (mem == arena)
X                arena = new;
X            else
X                memp->next = new;
X            /* now try to compact new block backwards and forwards */
X            if ((char *)new + size == (char *)mem)
X            {   /* forwards compaction */
X                new->size += mem->size;
X                new->next = mem->next;
X        if (arenaTail == mem)
X            arenaTail = new;
X            }
X            if (memp != (struct memblock *)NULL &&
X               ((char *)memp + memp->size == (char *)new))
X            {   /* backwards compaction */
X                memp->size += new->size;
X                memp->next = new->next;
X                if (arenaTail == new)
X                    arenaTail = memp;
X            }
X            return;
X        }
X        memp = mem;
X    }
X
X    /* if reach here, just tack block to end of list */
X    mem = (struct memblock *)ptr;
X    mem->size = size;
X    mem->next = (struct memblock *)NULL;
X    if (arena == (struct memblock *)NULL)
X        arena = arenaTail = mem;
X    else
X    {   /* tack onto end */
X        /* try compacting first */
X        if ((char *)arenaTail + arenaTail->size == (char *)mem)
X            arenaTail->size += mem->size;
X        else
X        {   /* really tack onto end */
X            arenaTail->next = mem;
X            arenaTail = mem;
X        }
X    }
X}   /* end of memput */
END_OF_FILE
if test 8160 -ne `wc -c <'malloc.c'`; then
    echo shar: \"'malloc.c'\" unpacked with wrong size!
fi
# end of 'malloc.c'
fi
if test -f 'ndir.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ndir.c'\"
else
echo shar: Extracting \"'ndir.c'\" \(5609 characters\)
sed "s/^X//" >'ndir.c' <<'END_OF_FILE'
X/*
X * @(#)msd_dir.c 1.4 87/11/06   Public Domain.
X *
X *  A public domain implementation of BSD directory routines for
X *  MS-DOS.  Written by Michael Rendell ({uunet,utai}michael@garfield),
X *  August 1987
X *
X *  GEMDOS (Atari ST) mods by Dave Clemans, November 1988
X *
X * $Id: ndir.c,v 1.2 89/02/20 20:09:22 dclemans Exp $
X *
X * $Log:	ndir.c,v $
X * Revision 1.2  89/02/20  20:09:22  dclemans
X * Add RCS identifiers
X * 
X */
X
X#ifndef GEMDOS
X#include    <sys/types.h>
X#include    <sys/stat.h>
X#include    <sys/dir.h>
X#else
X#include    "ndir.h"
X#endif  GEMDOS
X#include    <strings.h>
X#ifndef GEMDOS
X#include    <malloc.h>
X#include    <dos.h>
X#else
X#include    <osbind.h>
Xextern char *malloc(),*calloc();
X#endif  GEMDOS
X
X#ifndef NULL
X# define    NULL    0
X#endif  /* NULL */
X
X#ifndef MAXPATHLEN
X# define    MAXPATHLEN  255
X#endif  /* MAXPATHLEN */
X
X/* attribute stuff */
X#define A_RONLY     0x01
X#define A_HIDDEN    0x02
X#define A_SYSTEM    0x04
X#define A_LABEL     0x08
X#define A_DIR       0x10
X#define A_ARCHIVE   0x20
X
X/* dos call values */
X#define DOSI_FINDF  0x4e
X#define DOSI_FINDN  0x4f
X#define DOSI_SDTA   0x1a
X
X#define Newisnull(a, t)     ((a = (t *) malloc(sizeof(t))) == (t *) NULL)
X#define ATTRIBUTES      (A_DIR | A_HIDDEN | A_SYSTEM)
X
X/* what find first/next calls look use */
Xtypedef struct {
X    char        d_buf[21];
X    char        d_attribute;
X    unsigned short  d_time;
X    unsigned short  d_date;
X    long        d_size;
X    char        d_name[13];
X} Dta_buf;
X
Xstatic  char    *getdirent();
Xstatic  void    setdta();
Xstatic  void    free_dircontents();
X
Xstatic  Dta_buf     dtabuf;
Xstatic  Dta_buf     *dtapnt = &dtabuf;
X#ifndef GEMDOS
Xstatic  union REGS  reg, nreg;
X
X#if defined(M_I86LM)
Xstatic  struct SREGS    sreg;
X#endif
X#endif  GEMDOS
X
XDIR *
Xopendir(name)
X    char    *name;
X{
X#ifndef GEMDOS
X    struct  stat        statb;
X#endif  GEMDOS
X    register DIR         *dirp;
X    char            c;
X    char            *s;
X    register struct _dircontents *dp;
X    char            nbuf[MAXPATHLEN + 1];
X
X#ifndef GEMDOS
X    if (stat(name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)
X        return (DIR *) NULL;
X#endif  GEMDOS
X    if (Newisnull(dirp, DIR))
X        return (DIR *) NULL;
X    if (*name && (c = name[strlen(name) - 1]) != '\\' && c != '/')
X        (void) strcat(strcpy(nbuf, name), "\\*.*");
X    else 
X        (void) strcat(strcpy(nbuf, name), "*.*");
X    dirp->dd_loc = 0;
X#ifndef GEMDOS
X    setdta();
X#else
X    Fsetdta(&dtabuf);
X#endif  GEMDOS
X    dirp->dd_contents = dirp->dd_cp = (struct _dircontents *) NULL;
X    if ((s = getdirent(nbuf)) == (char *) NULL)
X        return dirp;
X    do {
X        if (Newisnull(dp, struct _dircontents) || (dp->_d_entry =
X            malloc((unsigned) (strlen(s) + 1))) == (char *) NULL)
X        {
X            if (dp)
X                free((char *) dp);
X            free_dircontents(dirp->dd_contents);
X            return (DIR *) NULL;
X        }
X        if (dirp->dd_contents)
X            dirp->dd_cp = dirp->dd_cp->_d_next = dp;
X        else
X            dirp->dd_contents = dirp->dd_cp = dp;
X        (void) strcpy(dp->_d_entry, s);
X        dp->_d_next = (struct _dircontents *) NULL;
X    } while ((s = getdirent((char *) NULL)) != (char *) NULL);
X    dirp->dd_cp = dirp->dd_contents;
X
X    return dirp;
X}
X
Xvoid
Xclosedir(dirp)
X    DIR *dirp;
X{
X    free_dircontents(dirp->dd_contents);
X    free((char *) dirp);
X}
X
Xstruct direct   *
Xreaddir(dirp)
X    register DIR *dirp;
X{
X    static  struct direct   dp;
X    
X    if (dirp->dd_cp == (struct _dircontents *) NULL)
X        return (struct direct *) NULL;
X    dp.d_namlen = dp.d_reclen =
X        strlen(strcpy(dp.d_name, dirp->dd_cp->_d_entry));
X    dp.d_ino = 0;
X    dirp->dd_cp = dirp->dd_cp->_d_next;
X    dirp->dd_loc++;
X
X    return &dp;
X}
X
Xvoid
Xseekdir(dirp, off)
X    register DIR *dirp;
X    long    off;
X{
X    long            i = off;
X    struct _dircontents *dp;
X
X    if (off < 0)
X        return;
X    for (dp = dirp->dd_contents ; --i >= 0 && dp ; dp = dp->_d_next)
X        ;
X    dirp->dd_loc = off - (i + 1);
X    dirp->dd_cp = dp;
X}
X
Xlong
Xtelldir(dirp)
X    DIR *dirp;
X{
X    return dirp->dd_loc;
X}
X
Xstatic  void
Xfree_dircontents(dp)
X    register struct  _dircontents    *dp;
X{
X    register struct _dircontents *odp;
X
X    while (dp) {
X        if (dp->_d_entry)
X            free(dp->_d_entry);
X        dp = (odp = dp)->_d_next;
X        free((char *) odp);
X    }
X}
X
Xstatic  char    *
Xgetdirent(dir)
X    register char    *dir;
X{
X#ifndef GEMDOS
X    if (dir != (char *) NULL) {     /* get first entry */
X        reg.h.ah = DOSI_FINDF;
X        reg.h.cl = ATTRIBUTES;
X#if defined(M_I86LM)
X        reg.x.dx = FP_OFF(dir);
X        sreg.ds = FP_SEG(dir);
X#else
X        reg.x.dx = (unsigned) dir;
X#endif
X    } else {                /* get next entry */
X        reg.h.ah = DOSI_FINDN;
X#if defined(M_I86LM)
X        reg.x.dx = FP_OFF(dtapnt);
X        sreg.ds = FP_SEG(dtapnt);
X#else
X        reg.x.dx = (unsigned) dtapnt;
X#endif
X    }
X#if defined(M_I86LM)
X    intdosx(&reg, &nreg, &sreg);
X#else
X    intdos(&reg, &nreg);
X#endif
X    if (nreg.x.cflag)
X        return (char *) NULL;
X
X#else
X    int status;
X
X    if (dir != (char *) NULL) {     /* get first entry */
X        status = Fsfirst(dir,A_DIR);
X    } else {
X        status = Fsnext();
X    }
X    if (status != 0)
X        return (char *)NULL;
X#endif  GEMDOS
X    return dtabuf.d_name;
X}
X
X#ifndef GEMDOS
Xstatic  void
Xsetdta()
X{
X    reg.h.ah = DOSI_SDTA;
X#if defined(M_I86LM)
X    reg.x.dx = FP_OFF(dtapnt);
X    sreg.ds = FP_SEG(dtapnt);
X    intdosx(&reg, &nreg, &sreg);
X#else
X    reg.x.dx = (int) dtapnt;
X    intdos(&reg, &nreg);
X#endif
X}
X#endif  GEMDOS
END_OF_FILE
if test 5609 -ne `wc -c <'ndir.c'`; then
    echo shar: \"'ndir.c'\" unpacked with wrong size!
fi
# end of 'ndir.c'
fi
if test -f 'ndir.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ndir.h'\"
else
echo shar: Extracting \"'ndir.h'\" \(1265 characters\)
sed "s/^X//" >'ndir.h' <<'END_OF_FILE'
X/*
X * @(#)msd_dir.h 1.4 87/11/06   Public Domain.
X *
X *  A public domain implementation of BSD directory routines for
X *  MS-DOS.  Written by Michael Rendell ({uunet,utai}michael@garfield),
X *  August 1987
X *
X * Gemdos mods by Dave Clemans
X *
X * $Id: ndir.h,v 1.2 89/02/20 20:09:29 dclemans Exp $
X *
X * $Log:	ndir.h,v $
X * Revision 1.2  89/02/20  20:09:29  dclemans
X * Add RCS identifiers
X * 
X */
X
Xtypedef long ino_t;
X
X#define rewinddir(dirp) seekdir(dirp, 0L)
X
X#define MAXNAMLEN   12
X
Xstruct direct {
X    ino_t   d_ino;          /* a bit of a farce */
X    int d_reclen;       /* more farce */
X    int d_namlen;       /* length of d_name */
X    char    d_name[MAXNAMLEN + 1];      /* garentee null termination */
X};
X
Xstruct _dircontents {
X    char    *_d_entry;
X    struct _dircontents *_d_next;
X};
X
Xtypedef struct _dirdesc {
X    int     dd_id;  /* uniquely identify each open directory */
X    long        dd_loc; /* where we are in directory entry is this */
X    struct _dircontents *dd_contents;   /* pointer to contents of dir */
X    struct _dircontents *dd_cp; /* pointer to current position */
X} DIR;
X
Xextern  DIR     *opendir();
Xextern  struct direct   *readdir();
Xextern  void        seekdir();
Xextern  long        telldir();
Xextern  void        closedir();
END_OF_FILE
if test 1265 -ne `wc -c <'ndir.h'`; then
    echo shar: \"'ndir.h'\" unpacked with wrong size!
fi
# end of 'ndir.h'
fi
if test ! -d 'reader' ; then
    echo shar: Creating directory \"'reader'\"
    mkdir 'reader'
fi
if test -f 'reader/emacs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/emacs.c'\"
else
echo shar: Extracting \"'reader/emacs.c'\" \(7822 characters\)
sed "s/^X//" >'reader/emacs.c' <<'END_OF_FILE'
X/*
X * emacs		- an "emacs" style single line editor for reader
X *
X * Dave Clemans, 4/84 (initial version); 1/89 (more generality)
X *
X * Commands supported:
X *	kill line	users kill character
X *	erase char	users erase character
X *	next line	^N
X *	prev line	^P
X *	forward		^F
X *	backward	^B
X *	delete		^D
X *	beginning line	^A
X *	end of line	^E
X *	retype line	^L
X *	uppercase char	^C
X *	transpose chars	^T
X *	reverse search	^R
X *	forwards search	^S
X *	kill to end	^K
X *	mark		^@
X *	delete region	^W
X *	yank region	^Y
X *	quote char	^Q
X *	erase char	DEL
X *	kill next word	ESC d
X *	kill prev word	ESC ^H
X *	kill prev word	ESC DEL
X *	expand name	ESC ESC
X *	next word	ESC f
X *	prev word	ESC b
X *	uppercase char	ESC c
X *	uppercase word	ESC C
X *	list expand	ESC ^L
X *	mark		ESC SPACE
X *	copy region	ESC p
X *	yank region	ESC y
X *	delete line	ESC ^K
X *	delete line	ESC ^U
X *	switch mark	^X^X
X *	arg mult	^U
X *	set arg		ESC digits
X *
X * If we return 0, we did not handle the input character; it should be
X * handled by the standard editor.
X * If we return <0, signal end-of-file.
X * Otherwise the character has been
X * handled and the next character can be read.
X *
X * $Id: emacs.c,v 1.2 89/02/20 20:20:12 dclemans Exp $
X *
X * $Log:	emacs.c,v $
X * Revision 1.2  89/02/20  20:20:12  dclemans
X * Add RCS identifiers
X * 
X */
X#include <ctype.h>
X#ifndef GEMDOS
X#include <sgtty.h>
X#endif  GEMDOS
X
X#include "history.h"
X
X/*
X * A marked point in a line; corresponds to a cursor position
X */
Xstatic short int markedPoint = -1;
X
X/*
X * For numerical arguments
X */
Xstatic int argument = 1;
Xstatic int argumentMultiplier = 1;
X
X/*
X * Check for "emacs" style commands
X */
Xint _doEmacs(c)
Xregister char c;
X{
X	register int counter,saveCursor;
X
X#ifndef GEMDOS
X	if (c == _savedState.basicAttributes.sg_erase)
X	{	/* erase character */
X		for (counter = 0; counter < argument*argumentMultiplier; counter++)
X			_doStandardErase(c);
X		argument = argumentMultiplier = 1;
X		return(1);
X	}
X#endif  GEMDOS
X	switch(c)
X	{	/* check to see if we know this command */
X#ifdef  GEMDOS
X            case '\010':
X#endif  GEMDOS
X	    case '\177':	/* erase character */
X		_doStandardErase(c);
X		break;
X	    case '\000':	/* mark current position */
X		markedPoint = History.currentLine->cursor;
X		break;
X	    case '\016':	/* next line */
X		for (counter = 0; counter < argument*argumentMultiplier; counter++)
X			_doStandardNextLine(c);
X		break;
X	    case '\020':	/* prev line */
X		for (counter = 0; counter < argument*argumentMultiplier; counter++)
X			_doStandardPrevLine(c);
X		break;
X	    case '\006':	/* forward */
X		for (counter = 0; counter < argument*argumentMultiplier; counter++)
X			_doStandardSpace(c);
X		break;
X	    case '\002':	/* backward */
X		for (counter = 0; counter < argument*argumentMultiplier; counter++)
X			_doStandardBackspace(c);
X		break;
X	    case '\004':	/* delete */
X		for (counter = 0; counter < argument*argumentMultiplier; counter++)
X			_doStandardDelete(c);
X		break;
X	    case '\001':	/* beginning of line */
X		_doStandardStartLine(c);
X		break;
X	    case '\005':	/* end of line */
X		_doStandardEndLine(c);
X		break;
X	    case '\014':	/* retype line */
X		_doStandardRetype(c);
X		break;
X	    case '\003':	/* capitalize character */
X		_doStandardCapitalize(c);
X		break;
X	    case '\024':	/* transpose characters */
X		_doStandardTranspose(c);
X		break;
X	    case '\022':	/* backwards search */
X		_doStandardSrchPrevLine(c,0);
X		break;
X	    case '\023':	/* forwards search */
X		_doStandardSearchNextLine(c,0);
X		break;
X	    case '\013':	/* kill to end of line */
X		_doStandardDelEndLine(c);
X		break;
X	    case '\027':	/* delete region */
X		doEmacsDeleteRegion();
X		break;
X	    case '\031':	/* yank region */
X		_popOffKillRing();
X		break;
X	    case '\021':	/* quote character */
X		if (_savedState.stream < 0)
X			return(0);
X		c = _readchar(_savedState.stream);
X		if (c == EOF)
X			return(-1);
X		_doStandardCharacter(c);
X		break;
X	    case '\025':	/* set arg multiplier to 4 */
X		argumentMultiplier = 4;
X		return(1);
X	    case '\033':	/* prefix character */
X		if (_savedState.stream < 0)
X			return(0);
X		c = _readchar(_savedState.stream);
X		if (c == EOF)
X			return(-1);
X		if (isdigit(c))
X		{	/* supplying argument? */
X			argument = 0;
X			for (; isdigit(c); c = _readchar(_savedState.stream))
X			{	/* build argument value */
X				argument *= 10;
X				argument += (int)((int)c - (int)'0');
X			}
X			if (c == EOF)
X				return(-1);
X			_savechar(c);
X			return(1);
X		}
X		switch (c)
X		{		/* a meta command? */
X		    case 'd':	/* delete next word */
X			_deleteNextStdWord();
X			break;
X		    case 'C':	/* capitalize next word */
X			_capitalizeStdWord();
X			break;
X		    case 'c':	/* capitalize next char */
X			_doStandardCapitalize(c);
X			break;
X		    case '\010': /* delete previous word */
X		    case '\177': /* delete previous word */
X			_deletePrevStdWord();
X			break;
X		    case '\033': /* expand command or filename */
X			_doStandardExpand(c);
X			break;
X		    case 'f':	/* space forward over a word */
X			_spaceNextStdWord();
X			break;
X		    case 'b':	/* space backward over a word */
X			_spacePrevStdWord();
X			break;
X		    case '\014': /* list command or filename expansion possibilities */
X			ExpandAName(History.currentLine->contents,
X				sizeof(History.currentLine->contents)-1,
X				History.currentLine->size, 0);
X			break;
X		    case ' ':	/* mark point */
X			markedPoint = History.currentLine->cursor;
X			break;
X		    case 'p':	/* copy region */
X			if (!isMarkValid())
X			{	/* a good mark? */
X				_writechar('\007');
X				break;
X			}
X			_pushOnKillRing(markedPoint,History.currentLine->cursor);
X			markedPoint = -1;
X			break;
X		    case 'y':	/* yank region */
X			_popOffKillRing();
X			break;
X		    case '\013': /* kill line */
X		    case '\025': /* kill line */
X			_doStandardKill(c);
X			break;
X		    default:	/* unknown */
X			_writechar('\007');
X			break;
X		}
X		break;
X	    case '\030':	/* other prefix character */
X		if (_savedState.stream < 0)
X			return(0);
X		c = _readchar(_savedState.stream);
X		if (c == EOF)
X			return(-1);
X		switch (c)
X		{		/* a prefix command? */
X		    case '\030':/* switch mark and point */
X			if ((markedPoint < 0) || (markedPoint > History.currentLine->size))
X			{	/* no mark */
X				_writechar('\007');
X				break;
X			}
X			saveCursor = History.currentLine->cursor;
X			if (saveCursor < markedPoint)
X			{	/* need to move forward */
X				while (History.currentLine->cursor < markedPoint)
X					_doStandardSpace('\000');
X			}
X			else if (saveCursor > markedPoint)
X			{	/* need to move backward */
X				while (History.currentLine->cursor > markedPoint)
X					_doStandardBackspace('\000');
X			}
X			markedPoint = saveCursor;
X			break;
X		    default:	/* unknown */
X			_writechar('\007');
X			break;
X		}
X		break;
X	    default:		/* we don't know this command */
X		return(0);
X	}
X	argument = argumentMultiplier = 1;
X	return(1);
X};	/* end of _doEmacs */
X
X/*
X * Delete region from mark to point
X */
Xstatic doEmacsDeleteRegion()
X{
X	register int counter,size;
X
X	if (!isMarkValid())
X	{	/* not marked */
X		_writechar('\007');
X		return;
X	}
X
X	_pushOnKillRing(markedPoint,History.currentLine->cursor);
X
X	if (markedPoint < History.currentLine->cursor)
X	{	/* a normal region */
X		size = History.currentLine->cursor - markedPoint + 1;
X		while (History.currentLine->cursor > markedPoint)
X			_doStandardBackspace('\000');
X		for (counter = 0; counter < size; counter++)
X			_doStandardDelete('\000');
X		markedPoint = -1;
X		return;
X	}
X
X	size = markedPoint - History.currentLine->cursor + 1;
X	for (counter = 0; counter < size; counter++)
X		_doStandardDelete('\000');
X	markedPoint = -1;
X};	/* end of doEmacsDeleteRegion */
X
X/*
X * Is the current mark valid? (i.e., does it exist and within the current
X * line?)
X */
Xstatic int isMarkValid()
X{
X	if (markedPoint < 0)
X		return(0);
X	if (History.currentLine->size < markedPoint)
X		return(0);
X	return(1);
X};	/* end of isMarkValid */
END_OF_FILE
if test 7822 -ne `wc -c <'reader/emacs.c'`; then
    echo shar: \"'reader/emacs.c'\" unpacked with wrong size!
fi
# end of 'reader/emacs.c'
fi
if test -f 'reader/history.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/history.h'\"
else
echo shar: Extracting \"'reader/history.h'\" \(2057 characters\)
sed "s/^X//" >'reader/history.h' <<'END_OF_FILE'
X/*
X * Data structures for keeping and referencing a "history" of lines
X * input from a user.
X *
X * Dave Clemans, 4/84 (initial version); 1/89 (more generality)
X *
X * Interesting globals:
X *	History:		general structure for "history" information
X *	History.maxSize		maximum number of lines in history list
X *	History.currentSize	current number of lines in history list
X *	History.listTop		top of list of history lines
X *	History.listBottom	bottom of list of history lines
X *	History.currentLine	current entry in history list
X *				if reader is called, a copy of what it
X *				returns will be pointed at by this
X *
X * $Id: history.h,v 1.5 89/02/20 20:20:14 dclemans Exp $
X *
X * $Log:	history.h,v $
X * Revision 1.5  89/02/20  20:20:14  dclemans
X * Add RCS identifiers
X * 
X */
X#ifndef DEFAULT_MAXSIZE
X#define	DEFAULT_MAXSIZE		16	/* Default History.maxSize */
X#endif  DEFAULT_MAXSIZE
X#define	HISTORY_BUFFER		512	/* Sizeof a history line */
X
X#include <stdio.h>
X
Xstruct historyLine
X{
X	char contents[HISTORY_BUFFER];
X	short int size;
X	short int cursor;
X	short int ttycursor;
X	int command;
X	struct historyLine *next;
X};
X
Xextern struct History
X{
X	int maxSize;
X	int currentSize;
X	int currentCommand;
X	struct historyLine *listTop;
X	struct historyLine *listBottom;
X	struct historyLine *listPointer;
X	struct historyLine *currentLine;
X} History;
X
X/*
X * The users "state"; tty attributes, control characters, etc.
X */
Xextern struct	savedState
X{
X	short int stream;
X#ifndef GEMDOS
X	struct sgttyb basicAttributes;
X	struct tchars basicCharacters;
X#endif  GEMDOS
X	short int localAttributes;
X#ifndef GEMDOS
X	struct ltchars localCharacters;
X#endif  GEMDOS
X	char *localEditor;
X	short int isEmacs;
X	short int isVi;
X	int sigint;
X	int sigquit;
X	int sigtstp;
X} _savedState;
Xextern struct	newState
X{
X#ifndef GEMDOS
X	struct sgttyb basicAttributes;
X	struct tchars basicCharacters;
X#endif  GEMDOS
X	short int localAttributes;
X#ifndef GEMDOS
X	struct ltchars localCharacters;
X#endif  GEMDOS
X} _newState;
X
X#define	EOF	(-1)		/* end of file */
X#define	TTY	2		/* file descriptor for tty access */
END_OF_FILE
if test 2057 -ne `wc -c <'reader/history.h'`; then
    echo shar: \"'reader/history.h'\" unpacked with wrong size!
fi
# end of 'reader/history.h'
fi
if test -f 'reader/killring.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/killring.c'\"
else
echo shar: Extracting \"'reader/killring.c'\" \(2265 characters\)
sed "s/^X//" >'reader/killring.c' <<'END_OF_FILE'
X/*
X * killring		- implement a simple kill ring for reader
X *
X * Dave Clemans, 4/84 (initial version); 1/89 (more generality)
X *
X * Entry points:
X *	_pushOnKillRing		push string on kill ring
X *	_popOffKillRing		pull string off onto pendingInput buffer
X *
X * $Id: killring.c,v 1.2 89/02/20 20:20:16 dclemans Exp $
X *
X * $Log:	killring.c,v $
X * Revision 1.2  89/02/20  20:20:16  dclemans
X * Add RCS identifiers
X * 
X */
X#ifndef GEMDOS
X#include <sgtty.h>
X#endif  GEMDOS
X#ifndef LOC
X#define LOC(r)  __FILE__,r,__LINE__
X#endif  LOC
X
X#include "history.h"
X
X/*
X * The kill ring
X */
X#define	MAX_RING	4
Xstruct	killRingElement
X{
X	char *contents;
X	short int size;
X};
Xstatic struct killRingElement killRing[MAX_RING];
Xstatic int nextKillRing;
X
X/*
X * Push a string onto the kill ring
X *
X * The string body comes from the global History.currentLine
X */
X_pushOnKillRing(first,last)
Xint first;
Xint last;
X{
X	register int temp;
X	register char *in,*out;
X
X	if (killRing[nextKillRing].contents)
X	{	/* re-using an old element */
X		free(killRing[nextKillRing].contents);
X		killRing[nextKillRing].contents = (char *)NULL;
X	}
X	if (first > last)
X	{	/* given in wrong order; swap around to be nice */
X		temp = last;
X		last = first;
X		first = temp;
X	}
X	killRing[nextKillRing].size = last - first + 1;
X	killRing[nextKillRing].contents =
X		(char *)malloc(killRing[nextKillRing].size+1);
X	if (!killRing[nextKillRing].contents)
X	{	/* out of core */
X		errmsg(-1,LOC("pushOnKillRing"));
X		exit(-1);
X	}
X	temp = killRing[nextKillRing].size;
X	in = &History.currentLine->contents[first];
X	out = killRing[nextKillRing].contents;
X	while (temp--)
X		*out++ = *in++;
X	nextKillRing++;
X	if (nextKillRing >= MAX_RING)
X		nextKillRing = 0;
X};	/* end of _pushOnKillRing */
X
X/*
X * Pop the top string off the kill ring into the pending input buffer
X */
X_popOffKillRing()
X{
X	register int counter,size;
X	register char *p;
X
X	nextKillRing--;
X	if (nextKillRing < 0)
X		nextKillRing = MAX_RING - 1;
X	p = killRing[nextKillRing].contents;
X	if (!p)
X	{	/* nothing there */
X		_writechar('\007');
X		return;
X	}
X	size = killRing[nextKillRing].size;
X	for (counter = 0; counter < size; counter++)
X		_savechar(*p++);
X	free(killRing[nextKillRing].contents);
X	killRing[nextKillRing].contents = (char *)NULL;
X};	/* end of popOffKillRing */
END_OF_FILE
if test 2265 -ne `wc -c <'reader/killring.c'`; then
    echo shar: \"'reader/killring.c'\" unpacked with wrong size!
fi
# end of 'reader/killring.c'
fi
if test -f 'reader/makefile.st' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/makefile.st'\"
else
echo shar: Extracting \"'reader/makefile.st'\" \(751 characters\)
sed "s/^X//" >'reader/makefile.st' <<'END_OF_FILE'
X#
X# Makefile for the "reader" "get a line with editing" package
X#
X# $Id: makefile.st,v 1.2 89/02/20 20:20:19 dclemans Exp $
X#
X# $Log:	makefile.st,v $
X# Revision 1.2  89/02/20  20:20:19  dclemans
X# Add RCS identifiers
X# 
X
XCFLAGS	= -O -VPEEP -VPSTR -DGEMDOS=1 -I..
X
XOBJS	= reader.o editing.o tenex.o emacs.o vi.o killring.o words.o
X
Xtester.tos: libreader.a tester.o
X	$(CC) -o $@ tester.o libreader.a ..\ndir.o ..\wild.o
X
Xlibreader.a: $(OBJS)
X	ar rv $@ $(OBJS)
X
Xclean:
X	rm -f $(OBJS) make.log tester tester.o
X
Xdistribution: clean
X	rm -f libreader.a
X
Xtester.o:	tester.c history.h
X
Xreader.o:	reader.c history.h
X
Xediting.o:	editing.c history.h
X
Xemacs.o:	emacs.c history.h
X
Xvi.o:		vi.c history.h
X
Xkillring.o:	killring.c history.h
X
Xwords.o:	words.c history.h
END_OF_FILE
if test 751 -ne `wc -c <'reader/makefile.st'`; then
    echo shar: \"'reader/makefile.st'\" unpacked with wrong size!
fi
# end of 'reader/makefile.st'
fi
if test -f 'reader/makefile.unx' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/makefile.unx'\"
else
echo shar: Extracting \"'reader/makefile.unx'\" \(714 characters\)
sed "s/^X//" >'reader/makefile.unx' <<'END_OF_FILE'
X#
X# Makefile for the "reader" "get a line with editing" package
X#
X# $Id: makefile.unx,v 1.2 89/02/20 20:20:21 dclemans Exp $
X#
X# $Log:	makefile.unx,v $
X# Revision 1.2  89/02/20  20:20:21  dclemans
X# Add RCS identifiers
X# 
X
XCFLAGS	= -O -I..
X
XOBJS	= reader.o editing.o tenex.o emacs.o vi.o killring.o words.o
X
Xtester: libreader.a tester.o
X	$(CC) -o $@ tester.o libreader.a ../wild.o
X
Xlibreader.a: $(OBJS)
X	ar rv $@ $(OBJS)
X
Xclean:
X	rm -f $(OBJS) make.log tester tester.o
X
Xdistribution: clean
X	rm -f libreader.a
X
Xtester.o:	tester.c history.h
X
Xreader.o:	reader.c history.h
X
Xediting.o:	editing.c history.h
X
Xemacs.o:	emacs.c history.h
X
Xvi.o:		vi.c history.h
X
Xkillring.o:	killring.c history.h
X
Xwords.o:	words.c history.h
END_OF_FILE
if test 714 -ne `wc -c <'reader/makefile.unx'`; then
    echo shar: \"'reader/makefile.unx'\" unpacked with wrong size!
fi
# end of 'reader/makefile.unx'
fi
if test -f 'reader/tester.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/tester.c'\"
else
echo shar: Extracting \"'reader/tester.c'\" \(3781 characters\)
sed "s/^X//" >'reader/tester.c' <<'END_OF_FILE'
X/*
X * $Id: tester.c,v 1.6 89/03/07 19:37:46 dclemans Exp $
X *
X * $Log:	tester.c,v $
X * Revision 1.6  89/03/07  19:37:46  dclemans
X * make work on bsd systems
X * 
X * Revision 1.5  89/02/20  20:20:28  dclemans
X * Add RCS identifiers
X * 
X */
X#ifndef GEMDOS
X#include <sgtty.h>
X#endif  GEMDOS
X
X#include "history.h"
X
Xmain()
X{
X	char buffer[1024];
X	int rc;
X	int count;
X	struct historyLine *hp;
X
X	for (count = 1; ; count++)
X	{
X		printf("%d-> ",count);
X		fflush(stdout);
X		rc = Reader(fileno(stdin),buffer,sizeof buffer - 1);
X		if (rc <= 0)
X		{	/* check for error or eof */
X            printf("\r\n");
X			break;
X		}
X		printf("\r\nline %d: %s\n",count,buffer);
X		printf("History list:\r\n");
X		for (hp = History.listTop; hp; hp = hp->next)
X			if (hp->command)
X				printf("\t%d:\t%s\n",hp->command,hp->contents);
X	}
X};
X
X#ifdef  GEMDOS
X#define	ESCAPE_CHAR	'@'
X#else
X#define ESCAPE_CHAR '\\'
X#endif  /* GEMDOS */
X#define	SHERR_NOMEM	-1
X#ifndef LOC
X#define LOC(r)  __FILE__,r,__LINE__
X#endif  LOC
X
Xlong io_savestring()
X{
X	return 0L;
X}
X
Xlong phrase_free()
X{
X	return 0L;
X}
X
Xlong lex_token()
X{
X	return 0L;
X}
X
Xchar *var_arg0 = "tester";
X
Xstruct token
X{
X    int type;
X    struct token *next;
X    char name[4];
X};
X
Xstruct token *new_token(len)
Xint len;
X{
X    register struct token *tp;
X
X    tp = (struct token *)malloc(sizeof(*tp)+len);
X    if (tp == (struct token *)NULL)
X        return (struct token *)NULL;
X    tp->next = (struct token *)NULL;
X    tp->type = -1;
X    tp->name[0] = '\0';
X
X    return tp;
X}   /* end of new_token */
X
Xint errmsg(code,file,routine,line,fmt,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9)
Xint code;
Xchar *file;
Xchar *routine;
Xint line;
Xchar *fmt;
X{
X    extern int errno;
X    char buffer[BUFSIZ];
X    char *p;
X    int fd;
X
X    fd = 1;
X    sprintf(buffer,"%s: %s(%s,%d): ",var_arg0,file,routine,line);
X    for (p = buffer; *p; p++)
X        /* do nothing */;
X    switch (code)
X    {   /* check for common err messages; else do normal one */
X        case -1:
X            strcpy(p,"out of memory");
X            break;
X        default:
X            sprintf(p,fmt,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9);
X            break;
X    }
X    while (*p)
X        p++;
X#ifdef  GEMDOS
X    *p++ = '\r';
X#endif  GEMDOS
X    *p++ = '\n';
X    *p = '\0';
X    write(fd,buffer,(unsigned)strlen(buffer));
X    return 0;
X}   /* end of errmsg */
X
Xchar *strcopy(s)
Xchar *s;
X{
X    char *ns;
X
X    ns = (char *)malloc(strlen(s)+1);
X    if (ns == (char *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("strcopy"));
X        return ns;
X    }
X    strcpy(ns,s);
X    return ns;
X}   /* end of strcopy */
X
Xchar *new_string(len)
Xint len;
X{
X    register char *cp;
X
X    cp = (char *)malloc(len);
X    if (cp == (char *)NULL)
X        return (char *)NULL;
X    *cp = '\0';
X
X    return cp;
X}   /* end of new_string */
X
Xvoid stripquotes(s)
Xchar *s;
X{
X    char *p;
X
X    for (p = s; *p != '\0'; p++)
X    {   /* check for special chars */
X        switch (*p)
X        {   /* a special char? */
X            case '\'':
X            strcpy(p,p+1);
X            while (*p && *p != '\'')
X                p++;
X            if (*p == '\'')
X                strcpy(p,p+1);
X            break;
X            case '"':
X            strcpy(p,p+1);
X            while (*p && *p != '"')
X            {   /* scan the string */
X                if (*p == ESCAPE_CHAR)
X                    strcpy(p,p+1);
X                p++;
X            }
X            if (*p == '"')
X                strcpy(p,p+1);
X            break;
X            case ESCAPE_CHAR:
X            strcpy(p,p+1);
X            break;
X            default:
X            break;
X        }
X    }
X}   /* end of stripquotes */
X
Xchar *var_normal(name)
Xchar *name;
X{
X	char *p;
X	extern char *getenv();
X
X	p = getenv(&name[1]);
X	if (p == (char *)NULL)
X		return p;
X	return strcopy(p);
X}
X
Xstruct
X{
X	long x;
X} base_env;
END_OF_FILE
if test 3781 -ne `wc -c <'reader/tester.c'`; then
    echo shar: \"'reader/tester.c'\" unpacked with wrong size!
fi
# end of 'reader/tester.c'
fi
if test -f 'version.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'version.c'\"
else
echo shar: Extracting \"'version.c'\" \(638 characters\)
sed "s/^X//" >'version.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * What version is this...
X *
X * $Id: version.c,v 1.7 89/03/07 19:39:00 dclemans Exp $
X *
X * $Log:	version.c,v $
X * Revision 1.7  89/03/07  19:39:00  dclemans
X * bump date
X * 
X * Revision 1.6  89/02/20  22:33:46  dclemans
X * Bump to next version number.
X * 
X * Revision 1.5  89/02/20  20:30:21  dclemans
X * More RCS ID additions
X * 
X * Revision 1.4  89/02/20  20:05:55  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X
Xchar    shell_version[] = "Shell Version 0.3 (dgc); $Date: 89/03/07 19:39:00 $";
END_OF_FILE
if test 638 -ne `wc -c <'version.c'`; then
    echo shar: \"'version.c'\" unpacked with wrong size!
fi
# end of 'version.c'
fi
echo shar: End of archive 1 \(of 11\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 11)."
# Contents:  cexp.y reader/tenex.c wild.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:03:54 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cexp.y' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cexp.y'\"
else
echo shar: Extracting \"'cexp.y'\" \(16516 characters\)
sed "s/^X//" >'cexp.y' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * (below code modified for use in shell 2/89)
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Expression evaluation
X *
X * $Id: cexp.y,v 1.3 89/02/20 20:14:09 dclemans Exp $
X *
X * $Log:	cexp.y,v $
X * Revision 1.3  89/02/20  20:14:09  dclemans
X * Add RCS identifiers
X * 
X */
X/* Parse C expressions for CCCP.
X   Copyright (C) 1987 Free Software Foundation.
X
X		       NO WARRANTY
X
X  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
XNO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
XWHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
XRICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
XWITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
XBUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
XFITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
XAND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
XDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
XCORRECTION.
X
X IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
XSTALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
XWHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
XLIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
XOTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
XUSE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
XDATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
XA FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
XPROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
XDAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
X
X		GENERAL PUBLIC LICENSE TO COPY
X
X  1. You may copy and distribute verbatim copies of this source file
Xas you receive it, in any medium, provided that you conspicuously and
Xappropriately publish on each copy a valid copyright notice "Copyright
X(C) 1987 Free Software Foundation"; and include following the
Xcopyright notice a verbatim copy of the above disclaimer of warranty
Xand of this License.  You may charge a distribution fee for the
Xphysical act of transferring a copy.
X
X  2. You may modify your copy or copies of this source file or
Xany portion of it, and copy and distribute such modifications under
Xthe terms of Paragraph 1 above, provided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating
X    that you changed the files and the date of any change; and
X
X    b) cause the whole of any work that you distribute or publish,
X    that in whole or in part contains or is a derivative of this
X    program or any part thereof, to be licensed at no charge to all
X    third parties on terms identical to those contained in this
X    License Agreement (except that you may choose to grant more extensive
X    warranty protection to some or all third parties, at your option).
X
X    c) You may charge a distribution fee for the physical act of
X    transferring a copy, and you may at your option offer warranty
X    protection in exchange for a fee.
X
XMere aggregation of another unrelated program with this program (or its
Xderivative) on a volume of a storage or distribution medium does not bring
Xthe other program under the scope of these terms.
X
X  3. You may copy and distribute this program (or a portion or derivative
Xof it, under Paragraph 2) in object code or executable form under the terms
Xof Paragraphs 1 and 2 above provided that you also do one of the following:
X
X    a) accompany it with the complete corresponding machine-readable
X    source code, which must be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    b) accompany it with a written offer, valid for at least three
X    years, to give any third party free (except for a nominal
X    shipping charge) a complete machine-readable copy of the
X    corresponding source code, to be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    c) accompany it with the information you received as to where the
X    corresponding source code may be obtained.  (This alternative is
X    allowed only for noncommercial distribution and only if you
X    received the program in object code or executable form alone.)
X
XFor an executable file, complete source code means all the source code for
Xall modules it contains; but, as a special exception, it need not include
Xsource code for modules which are standard libraries that accompany the
Xoperating system on which the executable file runs.
X
X  4. You may not copy, sublicense, distribute or transfer this program
Xexcept as expressly provided under this License Agreement.  Any attempt
Xotherwise to copy, sublicense, distribute or transfer this program is void and
Xyour rights to use the program under this License agreement shall be
Xautomatically terminated.  However, parties who have received computer
Xsoftware programs from you with this License Agreement will not have
Xtheir licenses terminated so long as such parties remain in full compliance.
X
X  5. If you wish to incorporate parts of this program into other free
Xprograms whose distribution conditions are different, write to the Free
XSoftware Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet
Xworked out a simple rule that can be stated here, but we will often permit
Xthis.  We will be guided by the two goals of preserving the free status of
Xall derivatives of our free software and of promoting the sharing and reuse of
Xsoftware.
X
X
X In other words, you are welcome to use, share and improve this program.
X You are forbidden to forbid anyone else to use, share and improve
X what you give them.   Help stamp out software-hoarding!
X
X Adapted from expread.y of GDB by Paul Rubin, July 1986.
X
X/* Parse a C expression from text in a string  */
X   
X%{
X#include "shell.h"
X#include <setjmp.h>
X/* #define YYDEBUG 1 */
X
X  int yylex ();
X  void yyerror ();
X  int expression_value;
X
X  static jmp_buf parse_return_error;
X
X  /* some external tables of character types */
X  extern unsigned char is_idstart[], is_idchar[];
X
X  static int allow_side_effects = 0;
X
X%}
X
X%union {
X  long lval;
X  int voidval;
X  char *sval;
X}
X
X%type <lval> exp exp1 start
X%token <lval> INT CHAR
X%token <sval> NAME
X%token <lval> ERROR
X
X%right '?' ':'
X%left ','
X%left OR
X%left AND
X%left '|'
X%left '^'
X%left '&'
X%left EQUAL NOTEQUAL
X%left '<' '>' LEQ GEQ
X%left LSH RSH
X%left '+' '-'
X%left '*' '/' '%'
X%right UNARY
X
X/* %expect 40 */
X
X%%
X
Xstart   :	exp1
X		{ expression_value = $1; }
X	;
X
X/* Expressions, including the comma operator.  */
Xexp1	:	exp
X	|	exp1 ',' exp
X			{ $$ = $3; }
X	;
X
X/* Expressions, not including the comma operator.  */
Xexp	:	'-' exp    %prec UNARY
X			{ $$ = - $2; }
X	|	'!' exp    %prec UNARY
X			{ $$ = ! $2; }
X	|	'~' exp    %prec UNARY
X			{ $$ = ~ $2; }
X	|	'(' exp1 ')'
X			{ $$ = $2; }
X    |   NAME '=' exp1
X			{
X              if ($1 == (char *)NULL)
X                $$ = 0;
X              else { char buffer[16];
X                sprintf(buffer,"%d",$3);
X                if (allow_side_effects)
X                  var_define0(&$1[1],buffer,0);
X                free($1);
X                $$ = $3;
X              }
X            }
X	;
X
X/* Binary operators in order of decreasing precedence.  */
Xexp	:	exp '*' exp
X			{ $$ = $1 * $3; }
X	|	exp '/' exp
X			{ if ($3 == 0)
X			    {
X			      errmsg (0,LOC("yyparse"),"division by zero");
X			      $3 = 1;
X			    }
X			  $$ = $1 / $3; }
X	|	exp '%' exp
X			{ if ($3 == 0)
X			    {
X			      errmsg (0,LOC("yparse"),"division by zero");
X			      $3 = 1;
X			    }
X			  $$ = $1 % $3; }
X	|	exp '+' exp
X			{ $$ = $1 + $3; }
X	|	exp '-' exp
X			{ $$ = $1 - $3; }
X	|	exp LSH exp
X			{ $$ = $1 << $3; }
X	|	exp RSH exp
X			{ $$ = $1 >> $3; }
X	|	exp EQUAL exp
X			{ $$ = ($1 == $3); }
X	|	exp NOTEQUAL exp
X			{ $$ = ($1 != $3); }
X	|	exp LEQ exp
X			{ $$ = ($1 <= $3); }
X	|	exp GEQ exp
X			{ $$ = ($1 >= $3); }
X	|	exp '<' exp
X			{ $$ = ($1 < $3); }
X	|	exp '>' exp
X			{ $$ = ($1 > $3); }
X	|	exp '&' exp
X			{ $$ = ($1 & $3); }
X	|	exp '^' exp
X			{ $$ = ($1 ^ $3); }
X	|	exp '|' exp
X			{ $$ = ($1 | $3); }
X	|	exp AND exp
X			{ $$ = ($1 && $3); }
X	|	exp OR exp
X			{ $$ = ($1 || $3); }
X	|	exp '?' exp ':' exp
X			{ $$ = $1 ? $3 : $5; }
X	|	INT
X			{ $$ = yylval.lval; }
X	|	CHAR
X			{ $$ = yylval.lval; }
X	|	NAME
X			{
X              if (yylval.sval == (char *)NULL)
X                $$ = 0;
X              else { char *ptr;
X                ptr = var_normal(yylval.sval);
X                if (ptr != (char *)NULL) {
X                  $$ = atoi(ptr);
X                  free(ptr);
X                }
X                else $$ = 0;
X                free(yylval.sval);
X              }
X            }
X	;
X%%
X
X/* During parsing of a C expression, the pointer to the next character
X   is in this variable.  */
X
Xstatic char *lexptr;
X
X/* Take care of parsing a number (anything that starts with a digit).
X   Set yylval and return the token type; update lexptr.
X   LEN is the number of characters in it.  */
X
X/* maybe needs to actually deal with floating point numbers */
X
Xint
Xparse_number (olen)
X     int olen;
X{
X  register char *p = lexptr;
X  register long n = 0;
X  register int c;
X  register int base = 10;
X  register len = olen;
X
X  extern double atof ();
X
X  for (c = 0; c < len; c++)
X    if (p[c] == '.') {
X      /* It's a float since it contains a point.  */
X      yyerror ("floating point numbers not allowed in expressions");
X      return ERROR;
X      
X/* ****************
X	 yylval.dval = atof (p);
X	 lexptr += len;
X	 return FLOAT;
X		 ****************  */
X    }
X  
X  if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) {
X    p += 2;
X    base = 16;
X    len -= 2;
X  }
X  else if (*p == '0')
X    base = 8;
X  
X  while (len-- > 0) {
X    c = *p++;
X    n *= base;
X    if (c >= '0' && c <= '9')
X      n += c - '0';
X    else {
X      if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
X      if (base == 16 && c >= 'a' && c <= 'f')
X	n += c - 'a' + 10;
X      else if (len == 0 && c == 'l')
X	;
X      else {
X	yyerror ("Invalid number in expression");
X	return ERROR;
X      }
X    }
X  }
X
X  lexptr = p;
X  yylval.lval = n;
X  return INT;
X}
X
Xstruct etoken {
X  char *operator;
X  int token;
X};
X
X#ifndef	NULL
X#define NULL 0
X#endif
X
Xstatic struct etoken tokentab2[] = {
X  {"&&", AND},
X  {"||", OR},
X  {"<<", LSH},
X  {">>", RSH},
X  {"==", EQUAL},
X  {"!=", NOTEQUAL},
X  {"<=", LEQ},
X  {">=", GEQ},
X  {NULL, ERROR}
X};
X
X/* Read one token, getting characters through lexptr.  */
X
Xint
Xyylex ()
X{
X  register int c;
X  register int namelen;
X  register char *tokstart;
X  register struct etoken *toktab;
X  char *name;
X
X retry:
X
X  tokstart = lexptr;
X  c = *tokstart;
X  /* See if it is a special token of length 2.  */
X  for (toktab = tokentab2; toktab->operator != NULL; toktab++)
X    if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) {
X      lexptr += 2;
X      return toktab->token;
X    }
X
X  switch (c) {
X  case 0:
X    return 0;
X    
X  case ' ':
X  case '\t':
X  case '\n':
X    lexptr++;
X    goto retry;
X    
X  case '\'':
X    lexptr++;
X    c = *lexptr++;
X    if (c == '\\')
X      c = parse_escape (&lexptr);
X    yylval.lval = c;
X    c = *lexptr++;
X    if (c != '\'') {
X      yyerror ("Invalid character constant");
X      return ERROR;
X    }
X    
X    return CHAR;
X
X    /* some of these chars are invalid in constant expressions;
X       maybe do something about them later */
X  case '/':
X  case '+':
X  case '-':
X  case '*':
X  case '%':
X  case '|':
X  case '&':
X  case '^':
X  case '~':
X  case '!':
X  case '@':
X  case '<':
X  case '>':
X  case '(':
X  case ')':
X  case '[':
X  case ']':
X  case '.':
X  case '?':
X  case ':':
X  case '=':
X  case '{':
X  case '}':
X  case ',':
X    lexptr++;
X    return c;
X    
X  case '"':
X    yyerror ("double quoted strings not allowed in expressions");
X    return ERROR;
X  }
X  if (c >= '0' && c <= '9') {
X    /* It's a number */
X    for (namelen = 0;
X	 c = tokstart[namelen], is_idchar[c] || c == '.'; 
X	 namelen++)
X      ;
X    return parse_number (namelen);
X  }
X  
X  if (!is_idstart[c]) {
X    yyerror ("Invalid token in expression");
X    return ERROR;
X  }
X  
X  /* It is a name.  See how long it is.  */
X
X  for (namelen = 0; is_idchar[tokstart[namelen]]; namelen++)
X    ;
X  name = new_string(namelen+2);
X  if (name == (char *)NULL)
X    errmsg(SHERR_NOMEM,LOC("yylex"));
X  yylval.sval = name;
X  if (name != (char *)NULL) {
X    name[0] = '$';
X    strncpy(&name[1],tokstart,namelen);
X    name[namelen+1] = '\0';
X  }
X  
X  lexptr += namelen;
X  return NAME;
X}
X
X
X/* Parse a C escape sequence.  STRING_PTR points to a variable
X   containing a pointer to the string to parse.  That pointer
X   is updated past the characters we use.  The value of the
X   escape sequence is returned.
X
X   A negative value means the sequence \ newline was seen,
X   which is supposed to be equivalent to nothing at all.
X
X   If \ is followed by a null character, we return a negative
X   value and leave the string pointer pointing at the null character.
X
X   If \ is followed by 000, we return 0 and leave the string pointer
X   after the zeros.  A value of 0 does not mean end of string.  */
X
Xint
Xparse_escape (string_ptr)
X     char **string_ptr;
X{
X  register int c = *(*string_ptr)++;
X  switch (c)
X    {
X    case 'a':
X      return '\a';
X    case 'b':
X      return '\b';
X    case 'e':
X      return 033;
X    case 'f':
X      return '\f';
X    case 'n':
X      return '\n';
X    case 'r':
X      return '\r';
X    case 't':
X      return '\t';
X    case 'v':
X      return '\v';
X    case '\n':
X      return -2;
X    case 0:
X      (*string_ptr)--;
X      return 0;
X    case '^':
X      c = *(*string_ptr)++;
X      if (c == '\\')
X	c = parse_escape (string_ptr);
X      if (c == '?')
X	return 0177;
X      return (c & 0200) | (c & 037);
X      
X    case '0':
X    case '1':
X    case '2':
X    case '3':
X    case '4':
X    case '5':
X    case '6':
X    case '7':
X      {
X	register int i = c - '0';
X	register int count = 0;
X	while (++count < 3)
X	  {
X	    if ((c = *(*string_ptr)++) >= '0' && c <= '7')
X	      {
X		i *= 8;
X		i += c - '0';
X	      }
X	    else
X	      {
X		(*string_ptr)--;
X		break;
X	      }
X	  }
X	return i;
X      }
X    default:
X      return c;
X    }
X}
X
Xvoid
Xyyerror (s)
X     char *s;
X{
X  errmsg (0,LOC("yyerror"),s);
X  longjmp (parse_return_error, 1);
X}
X
X/* This page contains the entry point to this file.  */
X
X/* Parse STRING as an expression, and complain if this fails
X   to use up all of the contents of STRING.  */
X/* We do not support C comments.  They should be removed before
X   this function is called.  */
X
Xint
Xparse_c_expression (string,side_effects)
X     char *string;
X     int side_effects;
X{
X  static int initialized;
X
X  if (!initialized) {
X    initialize_random_junk();
X    initialized++;
X  }
X
X  lexptr = string;
X  allow_side_effects = side_effects;
X  
X  if (lexptr == 0 || *lexptr == 0) {
X    return 0;
X  }
X
X  /* if there is some sort of scanning error, just return 0 and assume
X     the parsing routine has printed an error message somewhere.
X     there is surely a better thing to do than this.     */
X  if (setjmp(parse_return_error))
X    return 0;
X
X  if (yyparse ())
X    return 0;			/* actually this is never reached
X				   the way things stand. */
X  if (*lexptr)
X    errmsg (0,LOC("parse_c_expression"),"Junk after end of expression.");
X
X  return expression_value;	/* set by yyparse() */
X}
X
X/* table to tell if char can be part of a C identifier. */
Xunsigned char is_idchar[256];
X/* table to tell if char can be first char of a c identifier. */
Xunsigned char is_idstart[256];
X
X/*
X * initialize random junk in the hash table and maybe other places
X */
Xinitialize_random_junk()
X{
X  register int i;
X
X  /*
X   * Set up is_idchar and is_idstart tables.  These should be
X   * faster than saying (is_alpha(c) || c == '_'), etc.
X   * Must do set up these things before calling any routines tthat
X   * refer to them.
X   */
X  for (i = 'a'; i <= 'z'; i++) {
X    ++is_idchar[i - 'a' + 'A'];
X    ++is_idchar[i];
X    ++is_idstart[i - 'a' + 'A'];
X    ++is_idstart[i];
X  }
X  for (i = '0'; i <= '9'; i++)
X    ++is_idchar[i];
X  ++is_idchar['_'];
X  ++is_idstart['_'];
X#ifdef DOLLARS_IN_IDENTIFIERS
X  ++is_idchar['$'];
X  ++is_idstart['$'];
X#endif
X}
X
X
X#ifdef TEST_EXP_READER
X/* main program, for testing purposes. */
Xmain()
X{
X  int n;
X  char buf[1024];
X  extern int yydebug;
X/*
X  yydebug = 1;
X*/
X  initialize_random_junk ();
X
X  for (;;) {
X    printf("enter expression: ");
X    n = 0;
X    while ((buf[n] = getchar()) != '\n')
X      n++;
X    buf[n] = '\0';
X    printf("parser returned %d\n", parse_c_expression(buf));
X  }
X}
X
Xerrmsg (code,f,r,l,msg)
Xint code;
Xchar *f;
Xchar *r;
Xint l;
X{
X  printf("error: %s\n", msg);
X}
X#endif
END_OF_FILE
if test 16516 -ne `wc -c <'cexp.y'`; then
    echo shar: \"'cexp.y'\" unpacked with wrong size!
fi
# end of 'cexp.y'
fi
if test -f 'reader/tenex.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/tenex.c'\"
else
echo shar: Extracting \"'reader/tenex.c'\" \(16108 characters\)
sed "s/^X//" >'reader/tenex.c' <<'END_OF_FILE'
X/*
X * tenex -		file name or command expansion for "reader"
X *
X * Dave Clemans, 4/84 (initial version); 1/89 (more generality)
X *
X * Modeled from, (or taken from), the equivalent routines from
X * Ken Greer's "Tenex" C-shell.
X *
X * Main entry point:
X *	ExpandAName	given a line, expands last word in it
X *
X * $Id: tenex.c,v 1.3 89/02/20 20:20:26 dclemans Exp $
X *
X * $Log:	tenex.c,v $
X * Revision 1.3  89/02/20  20:20:26  dclemans
X * Add RCS identifiers
X * 
X */
X#ifndef GEMDOS
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <pwd.h>
X#include <signal.h>
X#else
X#include <types.h>
X#include <stat.h>
X#include "ndir.h"
X#include <strings.h>
X#include <ctype.h>
X#define index  strchr
X#define rindex strrchr
X#ifndef NULL
X#define	NULL	((char *)0)
X#endif  NULL
X#endif  GEMDOS
X#ifndef LOC
X#define LOC(r)  __FILE__,r,__LINE__
X#endif  LOC
X
Xtypedef enum {LIST, RECOGNIZE} COMMAND;
X
X#define	TRUE	1
X#define	FALSE	0
X#define	FILSIZ	512		/* Max reasonable file name length */
X
X#ifndef DIR_SEPARATOR
X#ifndef GEMDOS
X#define DIR_SEPARATOR '/'
X#else
X#define DIR_SEPARATOR '\\'
X#endif  GEMDOS
X#endif  DIR_SEPARATOR
X
X/*
X * Interesting function definitions
X */
Xextern char *index();
Xextern char *var_normal();
Xextern int _writechar();
Xstatic char filetype();
X
X/*
X * max of a,b
X */
Xstatic
Xmax (a, b)
X{
X    if (a > b)
X	return (a);
X    return (b);
X};	/* end of max */
X
X/*
X * For qsort()
X */
Xstatic
Xfcompare (file1, file2)
Xchar  **file1, **file2;
X{
X    return (strcmp (*file1, *file2));
X};	/* end of fcompare */
X
X/*
X * Append characters to end of global place
X */
Xstatic char *CharPtr;
Xstatic
XCharAppend (c)
Xchar c;
X{
X    *CharPtr++ = c;
X    *CharPtr   = 0;
X};	/* end of CharAppend */
X
X/*
X * Set up to search for names
X */
XExpandAName (inputline, inputline_size, num_read, doRecognize)
Xchar   *inputline;		/* match string prefix */
Xint     inputline_size;		/* max size of string */
Xint	num_read;		/* # actually in inputline */
X{
X    static char 
X	    delims[] = " '\"\t;&<>()|^%";
X    char word [FILSIZ + 1];
X    register char *str_end, *word_start, *cmd_start, *wp;
X    int space_left;
X    int is_a_cmd;		/* UNIX command rather than filename */
X    int rc;
X
X    CharPtr = str_end = &inputline[num_read];
X
X   /*
X    * Find LAST occurence of a delimiter in the inputline.
X    * The word start is one character past it.
X    */
X    for (word_start = str_end; word_start > inputline; --word_start)
X	if (index (delims, word_start[-1]))
X	    break;
X
X    space_left = inputline_size - (word_start - inputline) - 1;
X
X    is_a_cmd = starting_a_command (word_start, inputline);
X
X    for (cmd_start = word_start, wp = word; cmd_start < str_end;
X    	 *wp++ = *cmd_start++);
X    *wp = 0;
X
X    if (doRecognize)
X	return search (word, wp, space_left, is_a_cmd, RECOGNIZE, CharAppend);
X    else
X    {	/* do LIST command */
X#ifdef  GEMDOS
X        _writechar('\r');
X#endif  GEMDOS
X	_writechar('\n');
X	rc = search (word, wp, space_left, is_a_cmd, LIST, _writechar);
X	_doStandardRetype('\0');
X	return rc;
X    }
X};	/* end of ExpandAName */
X
X/*
X * Are we starting a command (i.e., still in the first word on the line?
X */
Xstatic starting_a_command (wordstart, inputline)
Xregister char *wordstart, *inputline;
X{
X    static char
X	    cmdstart[] = ";&(|`",
X	    cmdalive[] = " \t'\"";
X    while (--wordstart >= inputline)
X    {
X	if (index (cmdstart, *wordstart))
X	    break;
X	if (!index (cmdalive, *wordstart))
X	    return (FALSE);
X    }
X    if (wordstart > inputline && *wordstart == '&')	/* Look for >& */
X    {
X	while (wordstart > inputline &&
X			(*--wordstart == ' ' || *wordstart == '\t'));
X	if (*wordstart == '>')
X		return (FALSE);
X    }
X    return (TRUE);
X};	/* end of starting_a_command */
X
X/*
X * expand "old" file name with possible tilde usage
X *		~person/mumble
X * expands to
X *		home_directory_of_person/mumble
X * into string "new".
X */
X
Xstatic char *
Xtilde (new, old)
Xchar *new, *old;
X{
X#ifndef GEMDOS
X    extern struct passwd *getpwuid (), *getpwnam ();
X
X    register char *o, *p;
X    register struct passwd *pw;
X    static char person[40] = {0};
X
X    if (old[0] != '~')
X    {
X	strcpy (new, old);
X	return (new);
X    }
X
X    for (p = person, o = &old[1]; *o && *o != DIR_SEPARATOR; *p++ = *o++);
X    *p = '\0';
X
X    if (person[0] == '\0')			/* then use current uid */
X	pw = getpwuid (getuid ());
X    else
X	pw = getpwnam (person);
X
X    if (pw == NULL)
X	return (NULL);
X
X    strcpy (new, pw -> pw_dir);
X    (void) strcat (new, o);
X    return (new);
X#else
X    strcpy (new, old);
X    return (new);
X#endif  GEMDOS
X};	/* end of tilde */
X
X/*
X * parse full path in file into 2 parts: directory and file names
X * Should leave final slash (/) at end of dir.
X */
Xstatic
Xextract_dir_and_name (path, dir, name)
Xchar   *path, *dir, *name;
X{
X    extern char *rindex ();
X    register char  *p;
X
X    p = rindex (path, DIR_SEPARATOR);
X    if (p == NULL)
X    {
X	copyn (name, path, MAXNAMLEN);
X	dir[0] = '\0';
X    }
X    else
X    {
X	p++;
X	copyn (name, p, MAXNAMLEN);
X	copyn (dir, path, p - path);
X    }
X};	/* end of extract_dir_and_name */
X
X
X/*
X * Get a directory entry
X */
Xstatic char *
Xgetentry (dir_fd, looking_for_lognames)
XDIR *dir_fd;
X{
X#ifndef GEMDOS
X    if (looking_for_lognames)			/* Is it login names we want? */
X    {
X	extern struct passwd *getpwent ();
X	register struct passwd *pw;
X	if ((pw = getpwent ()) == NULL)
X	    return (NULL);
X	return (pw -> pw_name);
X    }
X    else					/* It's a dir entry we want */
X    {
X#endif  GEMDOS
X	register struct direct *dirp;
X        register char *p;
X
X	if (dirp = readdir (dir_fd))
X	{
X#ifdef  GEMDOS
X            for (p = dirp->d_name; *p; p++)
X                if (isupper(*p))
X                    *p = _tolower(*p);
X#endif  GEMDOS
X	    return (dirp -> d_name);
X        }
X	return (NULL);
X#ifndef GEMDOS
X    }
X#endif  GEMDOS
X};	/* end of getentry */
X
X/*
X * Free a list of strings
X */
Xstatic
Xfree_items (items)
Xregister char **items;
X{
X    register int i;
X    for (i = 0; items[i]; i++)
X	free (items[i]);
X    free (items);
X};	/* end of free_items */
X
X#define FREE_ITEMS(items)\
X{\
X    free_items (items);\
X    items = NULL;\
X}
X#define FREE_DIR(fd)\
X{\
X    closedir (fd);\
X    fd = NULL;\
X}
X
X/*
X * Strip next directory from path; return ptr to next unstripped directory.
X */
X 
Xstatic char *extract_dir_from_path (path, dir)
Xchar *path, dir[];
X{
X    register char *d = dir;
X    char sep;
X
X#ifndef GEMDOS
X    sep = ':';
X#else
X    sep = ',';
X#endif  GEMDOS
X    while (*path && (*path == ' ' || *path == sep)) path++;
X    while (*path && (*path != ' ' && *path != sep)) *(d++) = *(path++);
X    while (*path && (*path == ' ' || *path == sep)) path++;
X
X    *(d++) = DIR_SEPARATOR;
X    *d = 0;
X
X    return path;
X};	/* end of extract_dir_from_path */
X
X/*
X * Perform a RECOGNIZE or LIST command on string "word".
X */
Xstatic
Xsearch (word, wp, max_word_length, looking_for_command, command, routine)
Xchar   *word,
X       *wp;			/* original end-of-word */
Xint max_word_length,
X	looking_for_command;
XCOMMAND command;
Xint (*routine) ();
X{
X#define	MAXITEMS	2048
X    register numitems,
X	    name_length,		/* Length of prefix (file name) */
X	    looking_for_lognames;	/* True if looking for login names */
X    struct stat
X	    dot_statb,			/* Stat buffer for "." */
X	    curdir_statb;		/* Stat buffer for current directory */
X    int	    dot_scan,			/* True if scanning "." */
X	    dot_got;			/* True if have scanned dot already */
X    char    tilded_dir[FILSIZ + 1],	/* dir after ~ expansion */
X	    dir[FILSIZ + 1],		/* /x/y/z/ part in /x/y/z/f */
X            name[MAXNAMLEN + 1],	/* f part in /d/d/d/f */
X            extended_name[MAXNAMLEN+1],	/* the recognized (extended) name */
X            *entry,			/* single directory entry or logname */
X	    *path,			/* hacked PATH environment variable */
X            *savepath;
X    static DIR 
X	    *dir_fd = NULL;
X    static char
X            **items = NULL;		/* file names when doing a LIST */
X
X    savepath = (char *)NULL;
X    tilded_dir[0] = '\0';
X    if (items != NULL)
X	FREE_ITEMS (items);
X    if (dir_fd != NULL)
X	FREE_DIR (dir_fd);
X
X#ifndef GEMDOS
X    looking_for_lognames = (*word == '~') && (index (word, DIR_SEPARATOR) == NULL);
X#else
X    looking_for_lognames = 0;
X#endif  GEMDOS
X    looking_for_command &= (*word != '~') && (index (word, DIR_SEPARATOR) == NULL);
X
X    if (looking_for_command)
X    {
X        copyn (name, word, MAXNAMLEN);
X        if ((savepath = path = var_normal ("$PATH")) == NULL)
X	    path = ".";
X    	path = extract_dir_from_path (path, dir);
X    }
X    numitems = 0;
X
X    dot_got = FALSE;
X    stat (".", &dot_statb);
X
Xcmdloop:	/* One loop per directory in PATH, if looking_for_command */
X
X    if (looking_for_lognames)			/* Looking for login names? */
X    {
X#ifndef GEMDOS
X	setpwent ();				/* Open passwd file */
X#endif  GEMDOS
X	copyn (name, &word[1], MAXNAMLEN);	/* name sans ~ */
X    }
X    else
X    {						/* Open directory */
X        if (!looking_for_command)
X	    extract_dir_and_name (word, dir, name);
X	if ((tilde (tilded_dir, dir) == 0) ||	/* expand ~user/... stuff */
X	    
X	   ((dir_fd = opendir (*tilded_dir ? tilded_dir : ".")) == NULL))
X	{
X	    if (looking_for_command)
X	        goto try_next_path;
X	    else
X            {
X                if (savepath != (char *)NULL)
X                    free(savepath);
X		return (0);
X            }
X	}
X	dot_scan = FALSE;
X#ifndef GEMDOS
X	if (looking_for_command)
X	{
X	    /*
X	     * Are we searching "."?
X	     */
X	    fstat (dir_fd->dd_fd, &curdir_statb);
X	    if (curdir_statb.st_dev == dot_statb.st_dev &&
X	        curdir_statb.st_ino == dot_statb.st_ino)
X	    {
X	        if (dot_got)			/* Second time in PATH? */
X			goto try_next_path;
X		dot_scan = TRUE;
X		dot_got = TRUE;
X	    }
X        }
X#endif  GEMDOS
X    }
X
X    name_length = strlen (name);
X
X    while (entry = getentry (dir_fd, looking_for_lognames))
X    {
X	if (!is_prefix (name, entry))
X	    continue;
X
X	/*
X	 * Don't match . files on null prefix match
X	 */
X	if (name_length == 0 && entry[0] == '.' && !looking_for_lognames)
X	    continue;
X
X	/*
X	 * Skip non-executables if looking for commands:
X	 * Only done for directory "." for speed.
X	 * (Benchmarked with and without:
X	 * With filetype check, a full search took 10 seconds.
X	 * Without filetype check, a full search took 1 second.)
X	 *                                   -Ken Greer
X         */
X	if (looking_for_command && dot_scan && filetype (dir, entry) != '*')
X	    continue;
X
X	if (command == LIST)		/* LIST command */
X	{
X	    extern char *malloc ();
X	    register int length;
X	    if (numitems >= MAXITEMS)
X	    {
X#ifndef GEMDOS
X                errmsg(0,LOC("search"),"\nYikes!! Too many %s !!",
X                    looking_for_lognames?"names in password file":"files");
X#else
X                errmsg(0,LOC("search"),"\r\nYikes!! Too many files !!");
X#endif  GEMDOS
X		break;
X	    }
X	    if (items == NULL)
X	    {
X		items = (char **) malloc (sizeof (items[1]) * (MAXITEMS + 1));
X		if (items == NULL)
X		    break;
X		for (length = 1; length < MAXITEMS + 1; length++)
X		    items[length] = (char *)NULL;
X	    }
X	    length = strlen(entry) + 1;
X	    if ((items[numitems] = malloc (length)) == NULL)
X	    {
X                errmsg(0,LOC("search"),"out of memory");
X		break;
X	    }
X	    copyn (items[numitems], entry, MAXNAMLEN);
X	    numitems++;
X	}
X	else					/* RECOGNIZE command */
X	    if (recognize (extended_name, entry, name_length, ++numitems))
X		break;
X    }
X
X    if (looking_for_lognames)
X#ifndef GEMDOS
X	endpwent ();
X#else
X        ;
X#endif  GEMDOS
X    else
X	FREE_DIR (dir_fd);
X
Xtry_next_path:
X    if (looking_for_command && *path &&
X    	(path = extract_dir_from_path (path, dir), dir)) 
X    	goto cmdloop;
X    
X    if (command == RECOGNIZE && numitems > 0)
X    {
X	if (looking_for_lognames)
X	    copyn (word, "~", 1);
X	else if (looking_for_command)
X	    word[0] = 0;
X	else
X	    copyn (word, dir, max_word_length);		/* put back dir part */
X	catn (word, extended_name, max_word_length);	/* add extended name */
X	while (*wp) (*routine) (*wp++);
X        if (savepath != (char *)NULL)
X            free(savepath);
X	return (numitems);
X    }
X
X    if (command == LIST)
X    {
X	qsort (items, numitems, sizeof (items[1]), fcompare);
X	print_by_column (looking_for_lognames ? NULL:tilded_dir, items,
X			 numitems, looking_for_command);
X	if (items != NULL)
X	    FREE_ITEMS (items);
X    }
X    if (savepath != (char *)NULL)
X        free(savepath);
X    return (0);
X};	/* end of search */
X
X/*
X * Object: extend what user typed up to an ambiguity.
X * Algorithm:
X * On first match, copy full entry (assume it'll be the only match) 
X * On subsequent matches, shorten extended_name to the first
X * character mismatch between extended_name and entry.
X * If we shorten it back to the prefix length, stop searching.
X */
Xstatic recognize (extended_name, entry, name_length, numitems)
Xchar *extended_name, *entry;
X{
X    if (numitems == 1)				/* 1st match */
X	copyn (extended_name, entry, MAXNAMLEN);
X    else					/* 2nd and subsequent matches */
X    {
X	register char *x, *ent;
X	register int len = 0;
X	for (x = extended_name, ent = entry; *x && *x == *ent++; x++, len++);
X	*x = '\0';				/* Shorten at 1st char diff */
X	if (len == name_length)			/* Ambiguous to prefix? */
X	    return (-1);			/* So stop now and save time */
X    }
X    return (0);
X};	/* end of recognize */
X
X/*
X * return true if check items initial chars in template
X * This differs from PWB imatch in that if check is null
X * it items anything
X */
Xstatic
Xis_prefix (check, template)
Xchar   *check,
X       *template;
X{
X    register char  *check_char,
X                   *template_char;
X
X    check_char = check;
X    template_char = template;
X    do
X	if (*check_char == 0)
X	    return (TRUE);
X    while (*check_char++ == *template_char++);
X    return (FALSE);
X};	/* end of is_prefix */
X
X/*
X * like strncpy but always leave room for trailing \0
X * and always null terminate.
X */
Xstatic copyn (des, src, count)
Xregister char *des, *src;
Xregister count;
X{
X    while (--count >= 0)
X	if ((*des++ = *src++) == 0)
X	    return;
X    *des = '\0';
X};	/* end of copyn */
X
X/*
X * Concatonate src onto tail of des.
X * Des is a string whose maximum length is count.
X * Always null terminate.
X */
Xstatic catn (des, src, count)
Xregister char *des, *src;
Xregister count;
X{
X    while (--count >= 0 && *des)
X	des++;
X    while (--count >= 0)
X	if ((*des++ = *src++) == 0)
X	    return;
X    *des = '\0';
X};	/* end of catn */
X
X/*
X * Return a character denoting the type of a file:
X *	* if executable
X *	/ if directory
X *	space otherwise
X */
Xstatic char
Xfiletype (dir, file)
Xchar *dir, *file;
X{
X    if (dir)
X    {
X	char path[512];
X	struct stat statb;
X	strcpy (path, dir);
X	catn (path, file, sizeof path);
X#ifndef GEMDOS
X	if (lstat (path, &statb) >= 0)
X	{
X	    if ((statb.st_mode & S_IFMT) == S_IFDIR)
X		return (DIR_SEPARATOR);
X	    if ((statb.st_mode & S_IFMT) == S_IFLNK)
X		return ('@');
X	    if (statb.st_mode & (S_IEXEC |
X	       (S_IEXEC>>3) | (S_IEXEC>>6)))
X		return ('*');
X	}
X#else
X        if (stat (path, &statb) >= 0)
X        {
X            if (statb.st_mode & S_IJDIR)
X                return (DIR_SEPARATOR);
X        }
X#endif  GEMDOS
X    }
X    return (' ');
X};	/* end of filetype */
X
X/*
X * Print sorted down columns
X */
Xstatic
Xprint_by_column (dir, items, count, looking_for_command)
Xregister char *dir, *items[];
X{
X    register int i, rows, r, c, maxwidth = 0, columns;
X    for (i = 0; i < count; i++)
X	maxwidth = max (maxwidth, strlen (items[i]));
X    maxwidth += looking_for_command ? 1:2;	/* for the file tag and space */
X    columns = 80 / maxwidth;
X    rows = (count + (columns - 1)) / columns;
X    for (r = 0; r < rows; r++)
X    {
X	for (c = 0; c < columns; c++)
X	{
X	    i = c * rows + r;
X	    if (i < count)
X	    {
X		register int w;
X		prstring (items[i]);
X		w = strlen (items[i]);
X		/* Print filename followed by '/' or '*' or ' ' */
X		if (!looking_for_command)
X			_writechar (filetype (dir, items[i])), w++;
X		if (c < (columns - 1))			/* Not last column? */
X		    for (; w < maxwidth; w++)
X			_writechar (' ');
X	    }
X	}
X#ifdef  GEMDOS
X        _writechar ('\r');
X#endif
X	_writechar ('\n');
X    }
X};	/* end of print_by_column */
X
Xstatic prstring(s)
Xregister char *s;
X{
X	while (*s)
X	{	/* dump the string */
X		_writechar(*s);
X		s++;
X	}
X};	/* end of prstring */
END_OF_FILE
if test 16108 -ne `wc -c <'reader/tenex.c'`; then
    echo shar: \"'reader/tenex.c'\" unpacked with wrong size!
fi
# end of 'reader/tenex.c'
fi
if test -f 'wild.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'wild.c'\"
else
echo shar: Extracting \"'wild.c'\" \(16451 characters\)
sed "s/^X//" >'wild.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Wild card expansion
X *
X * $Id: wild.c,v 1.4 89/02/20 20:03:40 dclemans Exp $
X *
X * $Log:	wild.c,v $
X * Revision 1.4  89/02/20  20:03:40  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X#ifdef  GEMDOS
X#include "ndir.h"
X#else
X#include <sys/types.h>
X#ifndef USG
X#include <sys/dir.h>
X#else
X#include <dirent.h>
X#endif  /* USG */
X#endif  /* GEMDOS */
X
X#define WILDCARDS       "*?["
X
X/**** Start of "glob" package ****/
X/* This software is copyright (c) 1986 by Stichting Mathematisch Centrum.
X * You may use, modify and copy it, provided this notice is present in all
X * copies, modified or unmodified, and provided you don't make money for it.
X *
X * Written 86-jun-28 by Guido van Rossum, CWI, Amsterdam <guido@mcvax.uucp>
X * Stripped down for local use, more portability by Dave Clemans
X */
X
X/* Pattern matching function for filenames */
X/* Each occurrence of the * pattern causes a recursion level */
X
X#define EOS '\0'
X
X#define BOOL int
X#define NO 0
X#define YES 1
X
X#define M_ALL '\001'        /* * */
X#define M_ONE '\002'        /* ? */
X#define M_SET '\003'        /* [ */
X#define M_CMP '\004'        /* ^ */
X#define M_RNG '\005'        /* - */
X#define M_END '\006'        /* ] */
X
Xstatic BOOL match(name, pat)
X    char *name;
X    char *pat;
X{
X    register char c, k;
X    BOOL ok,cmp;
X
X    for (c = *pat++; c != EOS; c = *pat++) {
X        switch (c) {
X
X        case M_ONE:
X            if (*name++ == EOS)
X                return NO;
X            break;
X
X        case M_ALL:
X            if (*pat == EOS)
X                return YES;
X            for (; *name != EOS; ++name) {
X                if (match(name, pat))
X                    return YES;
X            }
X            return NO;
X
X        case M_SET:
X            cmp= NO;
X            ok= NO;
X            k= *name++;
X            while ((c= *pat++) != M_END) {
X                if (c == M_CMP)
X                    cmp= YES;
X                else if (*pat == M_RNG) {
X                    if (c <= k && k <= pat[1])
X                        ok= YES;
X                    pat += 2;
X                }
X                else if (c == k)
X                    ok= YES;
X            }
X            if (!cmp && !ok)
X                return NO;
X            if (cmp && ok)
X                return NO;
X            break;
X
X        default:
X            if (*name++ != c)
X                return NO;
X            break;
X
X        }
X    }
X    return *name == EOS;
X}
X/**** End of "glob" package ****/
X
Xchar *wild_compile(pattern,upcase)
Xchar *pattern;
Xint upcase;
X{
X    register char *compiled;
X    register char *in,*out;
X
X    compiled = new_string(strlen(pattern)+1);
X    if (compiled == (char *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("wild_compile"));
X        return (char *)NULL;
X    }
X    out = compiled;
X    for (in = pattern; *in; in++)
X    {   /* copy, "compiling" the pattern */
X        switch (*in)
X        {   /* a "special" character? */
X            case '\'':
X                in++;
X                while (*in && *in != '\'')
X                    *out++ = *in++;
X                if (*in != '\'')
X                    break;
X                break;
X            case '"':
X                in++;
X                while (*in && *in != '"')
X                {   /* scan the string */
X                    if (*in == ESCAPE_CHAR)
X                    {   /* embedded escape char? */
X                        in++;
X                        if (*in == '\0')
X                            break;
X                    }
X                    *out++ = *in++;
X                }
X                if (*in != '"')
X                    break;
X                break;
X            case ESCAPE_CHAR:
X                in++;
X                if (*in == '\0')
X                    break;
X                *out++ = *in;
X                break;
X            case '*':
X                *out++ = M_ALL;
X                break;
X            case '?':
X                *out++ = M_ONE;
X                break;
X            case '[':
X                *out++ = M_SET;
X                in++;
X                while (*in && *in != ']')
X                {   /* scan the set */
X                    if (*in == '-' &&
X                       (in[-1] == '[' || (in[-1] == '^' && in[-2] == '[')))
X                        *out++ = '-';
X                    else if (*in == '-')
X                        *out++ = M_RNG;
X                    else if (*in == '^' && in[-1] == '[')
X                        *out++ = M_CMP;
X                    else if (*in == '^')
X                        *out++ = '^';
X                    else if (*in == ESCAPE_CHAR && in[1] != '\0')
X                        *out++ = *++in;
X                    else if (*in == ESCAPE_CHAR)
X                        *out++ = ESCAPE_CHAR;
X                    else *out++ = *in;
X                    in++;
X                }
X                if (*in != ']')
X                {   /* if error found */
X                    errmsg(0,LOC("wild_compile"),"incomplete character class: missing ']'");
X                    free(compiled);
X                    return (char *)NULL;
X                }
X                *out++ = M_END;
X                break;
X            default:
X                *out++ = *in;
X                break;
X        }
X        if (*in == '\0')
X            break;
X    }
X#ifdef  GEMDOS
X    *out = '\0';
X    for (out = compiled; *out; out++)
X    {   /* fixups for mono-case matching */
X        if (!upcase)
X            continue;
X        if (islower(*out))
X            *out = _toupper(*out);
X    }
X#endif  /* GEMDOS */
X    *out = '\0';
X
X    /* finally done, return compiled string */
X    return compiled;
X}   /* end of wild_compile */
X
Xint wild_match(string,pattern)
Xchar *string;
Xchar *pattern;
X{
X    register char *compiled;
X    int rc;
X
X    compiled = wild_compile(pattern,0);
X    if (compiled == (char *)NULL)
X    {   /* any errors? message already printed */
X        return 0;
X    }
X    rc = match(string,compiled);
X    free(compiled);
X    return rc;
X}   /* end of wild_match */
X
Xvoid wild_save(path,tokens,exp)
Xchar *path;
Xstruct token **tokens;
Xint exp;
X{
X    register struct token *new,*tp,*ltp;
X    int rc;
X    char buffer[6],*dir,*npath;
X#ifdef  GEMDOS
X    char *p;
X#endif  /* GEMDOS */
X
X    dir = (char *)NULL;
X    if (path[0] == '~')
X    {   /* need tilde expansion? */
X        sprintf(buffer,"~%c*",DIR_SEPARATOR);
X        if (wild_match(path,buffer) || strcmp(path,"~") == 0)
X        {   /* paste in home directory? */
X            dir = strcopy(base_env.homedir);
X            npath = &path[1];
X        }
X        sprintf(buffer,"~+%c*",DIR_SEPARATOR);
X        if (wild_match(path,buffer) || strcmp(path,"~+") == 0)
X        {   /* paste in current directory? */
X            dir = strcopy(base_env.dir->current);
X            npath = &path[2];
X        }
X        sprintf(buffer,"~-%c*",DIR_SEPARATOR);
X        if (wild_match(path,buffer) || strcmp(path,"~-") == 0)
X        {   /* paste in old current directory */
X            dir = var_normal("$OLDPWD");
X            npath = &path[2];
X        }
X        if (dir != (char *)NULL)
X        {   /* if something to paste in */
X            path = new_string(strlen(dir)+strlen(npath)+1);
X            if (path == (char *)NULL)
X            {   /* enough memory? */
X                errmsg(SHERR_NOMEM,LOC("wild_save"));
X                return;
X            }
X            sprintf(path,"%s%s",dir,npath);
X        }
X    }
X    new = new_token(strlen(path));
X    if (new == (struct token *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("wild_save"));
X        return;
X    }
X    new->type = SYM_WORD;
X    strcpy(new->name,path);
X    if (dir != (char *)NULL)
X        free(path);
X#ifdef  GEMDOS
X    if (exp)
X    {   /* mono-case support */
X        for (p = new->name; *p; p++)
X            if (isupper(*p))
X                *p = _tolower(*p);
X    }
X#endif  /* GEMDOS */
X
X    ltp = (struct token *)NULL;
X    for (tp = *tokens; tp != (struct token *)NULL; tp = tp->next)
X    {   /* look for place to put name */
X        rc = strcmp(new->name,tp->name);
X        if (rc < 0)
X            break;
X        ltp = tp;
X    }
X    if (*tokens == (struct token *)NULL)
X        *tokens = new;
X    else if (ltp == (struct token *)NULL)
X    {   /* head of list? */
X        new->next = *tokens;
X        *tokens = new;
X    }
X    else
X    {   /* middle of list */
X        new->next = tp;
X        ltp->next = new;
X    }
X}   /* end of wild_save */
X
Xstruct token *wild_search(path,tokens)
Xchar *path;
Xstruct token **tokens;
X{
X    register char *leadin,*pattern,*trailer;
X    char *p,*pp;
X    int rc,wild,sep,mask;
X    char *compiled;
X    DIR *source;
X#ifndef USG
X    struct direct *name;
X#else
X    struct dirent *name;
X#endif  /* USG */
X    char lead[2];
X    struct token *expansion;
X
X    /* break into 3 pieces: leadin, pattern, everything else */
X    wild = sep = 0;
X    leadin = pattern = path;
X    trailer = (char *)NULL;
X    for (p = path; *p; p++)
X    {   /* scan, looking for special characters */
X        switch (*p)
X        {   /* an interesting char? */
X            case ESCAPE_CHAR:
X                p++;
X                break;
X            case '\'':
X                p++;
X                while (*p && *p != '\'')
X                    p++;
X                break;
X            case '"':
X                while (*p && *p != '"')
X                    p++;
X                break;
X#ifdef  GEMDOS
X            case ':':
X#endif  /* GEMDOS */
X            case DIR_SEPARATOR:
X                sep++;
X                if (wild)
X                {   /* if wild cards in last segment */
X                    if (leadin == pattern)
X                        leadin = ".";
X                    trailer = p;
X                    break;
X                }
X                pattern = p+1;
X                break;
X            case '*':
X                wild++;
X                break;
X            case '?':
X                wild++;
X                break;
X            case ']':
X                wild++;
X                while (*p && *p != ']')
X                    p++;
X                break;
X            default:
X                break;
X        }
X        if (*p == '\0')
X            break;
X        if (trailer != (char *)NULL)
X            break;
X    }
X    if (tokens == (struct token **)NULL)
X        tokens = &expansion;
X    expansion = (struct token *)NULL;
X    if (wild == 0)
X    {   /* then no expansion (except for possible tildes) */
X        if (path[0] != '~')
X            return (struct token *)NULL;
X        wild_save(path,tokens,0);
X        return *tokens;
X    }
X    if (trailer == (char *)NULL)
X    {   /* if pieces not set up yet */
X        if (sep == 0)
X        {   /* just a pattern... */
X            leadin = ".";
X            pattern = path;
X        }
X        else
X        {   /* leadin - pattern */
X            p = strrchr(path,DIR_SEPARATOR);
X            if (p == (char *)NULL)
X                p = strrchr(path,':');
X            leadin = path;
X            pattern = p+1;
X        }
X        for (trailer = pattern; *trailer; trailer++)
X            /* do nothing */;
X    }
X    lead[0] = lead[1] = '\0';
X    if (strcmp(leadin,".") != 0)
X    {   /* make leadin string usable */
X        /* ASSERT that leadin == path */
X        leadin = new_string((int)(pattern-path)+1);
X        if (leadin == (char *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("wild_search"));
X            return (struct token *)NULL;
X        }
X        strncpy(leadin,path,(int)(pattern-path));
X        leadin[(int)(pattern-path)] = '\0';
X        for (p = leadin; *p; p++)
X            /* do nothing */;
X        p--;
X        if (*p == DIR_SEPARATOR && p != leadin
X#ifdef  GEMDOS
X                                               && p[-1] != ':'
X#endif  /* GEMDOS */
X                                                              )
X        {   /* remove trailing sep */
X            *p = '\0';
X            lead[0] = DIR_SEPARATOR;
X        }
X        stripquotes(leadin);
X        if (strcmp(leadin,"~") == 0)
X        {   /* paste in home directory */
X            free(leadin);
X            leadin = strcopy(base_env.homedir);
X            if (leadin == (char *)NULL)
X            {   /* enough memory? */
X                /* message already printed */
X                return (struct token *)NULL;
X            }
X        }
X        else if (strcmp(leadin,"~+") == 0)
X        {   /* paste in current directory */
X            free(leadin);
X            leadin = strcopy(base_env.dir->current);
X            if (leadin == (char *)NULL)
X            {   /* enough memory? */
X                /* message already printed */
X                return (struct token *)NULL;
X            }
X        }
X        else if (strcmp(leadin,"~-") == 0)
X        {   /* paste in old current directory */
X            free(leadin);
X            leadin = var_normal("$OLDPWD");
X            if (leadin == (char *)NULL)
X                leadin = strcopy(base_env.dir->current);
X            if (leadin == (char *)NULL)
X            {   /* enough memory? */
X                /* message already printed */
X                return (struct token *)NULL;
X            }
X        }
X    }
X    else sep = 0;
X    pp = new_string((int)(trailer-pattern)+1);
X    if (pp == (char *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("wild_search"));
X        if (sep)
X            free(leadin);
X        return (struct token *)NULL;
X    }
X    strncpy(pp,pattern,(int)(trailer-pattern));
X    pp[(int)(trailer-pattern)] = '\0';
X    pattern = pp;
X    compiled = wild_compile(pattern,1);
X    if (compiled == (char *)NULL)
X    {   /* any errors? message already printed */
X        free(pattern);
X        if (sep)
X            free(leadin);
X        return (struct token *)NULL;
X    }
X
X#ifdef  GEMDOS
X    if (*trailer == ':')
X    {   /* wild-carded drive name? */
X        lead[1] = '\0';
X        mask = Drvmap();
X        for (rc = 0; rc < 16; rc++)
X        {   /* for each possible drive */
X            if (mask & (1<<rc))
X            {   /* if drive present */
X                lead[0] = 'A' + rc;
X                if (match(lead,compiled))
X                {   /* if this drive is wanted */
X                    pp = new_string(strlen(trailer)+2);
X                    if (pp == (char *)NULL)
X                    {   /* enough memory? */
X                        errmsg(SHERR_NOMEM,LOC("wild_search"));
X                        break;
X                    }
X                    sprintf(pp,"%s%s",lead,trailer);
X                    wild_search(pp,tokens);
X                    free(pp);
X                }
X            }
X        }
X        free(pattern);
X        free(compiled);
X        return *tokens;
X    }
X#endif  /* GEMDOS */
X
X    source = opendir(leadin);
X    if (source == (DIR *)NULL)
X    {   /* did it work? */
X        errmsg(0,LOC("wild_search"),"can't open directory %s",leadin);
X        free(compiled);
X        free(pattern);
X        if (sep)
X            free(leadin);
X        return (struct token *)NULL;
X    }
X    while ((name = readdir(source)) != NULL)
X    {   /* while there are names... */
X        if (strcmp(name->d_name,".") == 0 || strcmp(name->d_name,"..") == 0)
X            continue;
X        rc = match(name->d_name,compiled);
X        if (rc)
X        {   /* if a match was found */
X            if (*trailer == '\0' && sep)
X            {   /* save the path */
X                pp = new_string(strlen(leadin)+strlen(lead)+strlen(name->d_name)+1);
X                if (pp == (char *)NULL)
X                {   /* enough memory? */
X                    errmsg(SHERR_NOMEM,LOC("wild_search"));
X                    break;
X                }
X                sprintf(pp,"%s%s%s",leadin,lead,name->d_name);
X                wild_save(pp,tokens,1);
X                free(pp);
X            }
X            else if (*trailer == '\0')
X                wild_save(name->d_name,tokens,1);
X            else
X            {   /* need to recurse to handle trailer */
X                pp = new_string(strlen(leadin)+strlen(name->d_name)+strlen(trailer)+2);
X                if (pp == (char *)NULL)
X                {   /* enough memory */
X                    errmsg(SHERR_NOMEM,LOC("wild_search"));
X                    break;
X                }
X                sprintf(pp,"%s%s%s%s",leadin,lead,name->d_name,trailer);
X                wild_search(pp,tokens);
X                free(pp);
X            }
X        }
X    }
X    closedir(source);
X    free(compiled);
X    free(pattern);
X    if (sep)
X        free(leadin);
X    return *tokens;
X}   /* end of wild_search */
END_OF_FILE
if test 16451 -ne `wc -c <'wild.c'`; then
    echo shar: \"'wild.c'\" unpacked with wrong size!
fi
# end of 'wild.c'
fi
echo shar: End of archive 3 \(of 11\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 4 (of 11)."
# Contents:  main.c reader/editing.c reader/reader.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:03:55 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(16634 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Main Routine
X *
X * $Id: main.c,v 1.11 89/03/07 19:06:03 dclemans Exp $
X *
X * $Log:	main.c,v $
X * Revision 1.11  89/03/07  19:06:03  dclemans
X * portability fixes
X * 
X * Revision 1.10  89/02/22  21:32:00  dclemans
X * Implement simple background job monitoring facility
X * 
X * Revision 1.9  89/02/22  15:39:30  dclemans
X * and also the SYSV default window size
X * 
X * Revision 1.8  89/02/22  15:06:52  dclemans
X * pick up BSD default window size.
X * 
X * Revision 1.7  89/02/21  19:39:25  dclemans
X * Implement RANDOM and SECONDS variables
X * 
X * Revision 1.6  89/02/21  08:36:48  dclemans
X * Implement pseudo-signals ERR, EXIT, DEBUG
X * 
X * Revision 1.5  89/02/20  22:30:31  dclemans
X * Add hooks to setup default signals
X * 
X * Revision 1.4  89/02/20  20:12:00  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X#include <time.h>
Xextern unsigned long time();
X#ifndef GEMDOS
X#ifdef  unix
X#ifndef USG
X#include <sgtty.h>
X#else
X#include <termio.h>
X#endif  /* USG */
X#include <sys/param.h>
X#include <fcntl.h>
Xextern int errno;
X#endif  /* unix */
X#endif  /* GEMDOS */
X
X#ifdef  GEMDOS
X#ifdef  MWC
Xlong    _stksize = DEFSTACK;
X#endif  /* MWC */
X#endif  /* GEMDOS */
X
Xint flag_echoinput = 0;
Xint flag_echoexec = 0;
Xint flag_keywords = 0;
Xint flag_cmdhash = 0;
Xint flag_varerr = 0;
Xint flag_noglob = 0;
Xint flag_allexport = 0;
Xint flag_noexec = 0;
Xint flag_interactive = 1;
X#ifndef GEMDOS
Xint flag_monitor = 1;
X#else
Xint flag_monitor = 0;
X#endif  /* GEMDOS */
Xstruct  envinfo base_env;
X
X#ifndef GEMDOS
Xstatic int max_sysfd = NOFILE;
Xstatic char open_sysfds[NOFILE/8+1];
X#endif  /* GEMDOS */
X
X#ifdef  MWC
Xextern  char *_iovector;
X#endif  /* MWC */
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X    register struct phrase *pp;
X    register struct phrase *sp;
X    register int i;
X    int flag,rc;
X    char *p;
X    char *cmd;
X    char buffer[16];
X#ifndef GEMDOS
X#ifndef USG
X    struct winsize window;
X#endif  /* USG */
X#endif  /* GEMDOS */
X
X    base_env.start_at = time(0L);
X
X    cmd_forceexit = 0;
X    cmd_count = 1;
X    for (i = 0; i <= PROMPT_MAX; i++)
X    {   /* set up prompts */
X        base_env.prompts[i] = (char *)NULL;
X        base_env.prompts_issued[i] = 0;
X    }
X    base_env.columns = 0;
X    base_env.lines = 0;
X    base_env.homedir = (char *)NULL;
X    base_env.shellname = (char *)NULL;
X    base_env.envfile = (char *)NULL;
X    base_env.cd_path = (char *)NULL;
X    base_env.exec_path = (char *)NULL;
X    base_env.separators = (char *)NULL;
X    base_env.var = (struct varstack *)NULL;
X    base_env.alias_table = (struct aliases *)NULL;
X    base_env.func_table = (struct function *)NULL;
X    base_env.temp_count = 0;
X    base_env.break_level = 0;
X    base_env.continue_level = 0;
X#ifdef  GEMDOS
X    base_env.fd_con = base_env.fd_aux = base_env.fd_prn = -1;
X    base_env.tmpdir = (char *)NULL;
X    base_env.exec_suff = (char *)NULL;
X#endif  /* GEMDOS */
X    base_env.dir = (struct dirstack *)NULL;
X    base_env.io = (struct iostack *)NULL;
X    base_env.pending_heredocs = (struct herestack *)NULL;
X    base_env.history_list = (struct hist_phrase *)NULL;
X    base_env.history_size = 0;
X
X    if (main_varpush() < 0)
X        exit(1);
X    if (main_iopush() < 0)
X    {   /* enough io set up to run? */
X        errmsg(0,LOC("main"),"unable to create I/O environment to run in");
X        exit(1);
X    }
X    base_env.fd_input = base_env.io->input;
X    rc = lchdir(".",&p);
X    if (rc != 0)
X    {   /* if we couldn't find it... */
X        errmsg(0,LOC("main"),"couldn't get home directory");
X        exit(2);
X    }
X    if (main_dirpush() < 0)
X    {   /* enough memory? */
X        /* message already printed */
X        exit(3);
X    }
X    var_define0("HOME",p,0);
X    var_define0("PWD",p,0);
X    free(p);
X    var_settype("RANDOM",TYPE_FUNCTION,~0);
X    var_settype("SECONDS",TYPE_FUNCTION,~0);
X
X    for (i = 0; var_init[i] != (char *)NULL; i += 2)
X        var_define0(var_init[i],var_init[i+1],0);
X    sprintf(buffer,"%d",DEFAULT_HISTORY);
X    var_define0("HISTSIZE",buffer,0);
X#ifndef GEMDOS
X#ifndef USG
X    ioctl(base_env.io->input,TIOCGWINSZ,&window);
X#else
X    ioctl(base_env.io->input,TCGETWINSZ,&window);
X#endif  /* USG */
X    sprintf(buffer,"%d",window.ws_row);
X    var_define0("LINES",buffer,0);
X    sprintf(buffer,"%d",window.ws_col);
X    var_define0("COLUMNS",buffer,0);
X#endif  /* GEMDOS */
X    var_readenv();          /* defines environment variables */
X
X    alias_init();           /* defines default aliases */
X
X    signal_init();          /* sets up default signals */
X
X    if (argc > 0 && argv != (char **)NULL && argv != (char **)-1)
X    {   /* if there are any args */
X        var_setarg0(argv[0]);
X        for (i = 1; i < argc; i++)
X        {   /* look for our own args */
X            if (argv[i][0] != '-')
X            {   /* a file to read... */
X#ifdef  GEMDOS
X                if (strlen(argv[i]) == 0)
X                    break;
X#endif  /* GEMDOS */
X#ifdef  GEMDOS
X                base_env.fd_input = Fopen(argv[i],0);
X#else
X                base_env.fd_input = open(argv[i],0);
X#endif  /* GEMDOS */
X                if (base_env.io->input < 0)
X                {   /* is that a file? */
X                    errmsg(0,LOC("main"),"can't open %s",argv[i]);
X                    exit(4);
X                }
X                flag_interactive = 0;
X                i++;
X                break;
X            }
X            p = &argv[i][1];
X            if (*p == '\0' || *p == '-')
X            {   /* end of switches? */
X                if (*p == '\0')
X                    flag_echoinput = flag_echoexec = 0;
X                i++;
X                break;
X            }
X            for (; *p != '\0'; p++)
X            {   /* for each arg character */
X                switch (*p)
X                {   /* which arg? */
X                    case 'a':   /* export all? */
X                        flag_allexport = 1;
X                        break;
X                    case 'f':   /* no file expansion? */
X                        flag_noglob = 1;
X                        break;
X                    case 'm':   /* monitor jobs? */
X                        flag_monitor = 1;
X                        break;
X                    case 'n':   /* no execution? */
X                        flag_noexec = 1;
X                        break;
X                    case 'u':   /* unknown var errors? */
X                        flag_varerr = 1;
X                        break;
X                    case 'v':   /* echo input? */
X                        flag_echoinput = 1;
X                        break;
X                    case 'x':   /* echo execution? */
X                        flag_echoexec = 1;
X                        break;
X                    case 'k':   /* all keywords? */
X                        flag_keywords = 1;
X                        break;
X                    case 'h':   /* command hashing? */
X                        flag_cmdhash = 1;
X                        break;
X                    case 'i':   /* force interactiveness? */
X                        flag_interactive = 1;
X                        break;
X                    case 't':   /* only one command? */
X                        cmd_forceexit = 1;
X                        break;
X                    case 'c':   /* exec one command? */
X                        i++;
X                        cmd = argv[i];
X                        if (cmd == (char *)NULL)
X                        {   /* if nothing there */
X                            errmsg(0,LOC("main"),"missing command arg to -c switch");
X                            exit(5);
X                        }
X                        io_savestring(cmd);
X                        cmd_forceexit = 1;
X                        flag_interactive = 0;
X                        break;
X                    default:
X                        errmsg(0,LOC("main"),"bad argument: '%s'",argv[i]);
X                        exit(6);
X                }
X            }
X        }
X        var_setargs(argc-i+1,&argv[i-1]);
X    }
X
X    if (base_env.envfile != (char *)NULL)
X        io_pushfile(base_env.envfile,0,0,0);
X
X    while ((sp = lex_sentence(0)) != (struct phrase *)NULL)
X    {   /* get sentences until EOF */
X        if (base_env.prompts_issued[0] > 0 && base_env.history_size > 0 && sp->group != (struct phrase *)NULL)
X        {   /* stick onto history list */
X            if (base_env.history_list[base_env.history_size-1].cmd != (struct phrase *)NULL)
X                sentence_free(base_env.history_list[base_env.history_size-1].cmd);
X            for (i = base_env.history_size-1; i > 0; i--)
X            {   /* slide up the commands */
X                base_env.history_list[i].cmd = base_env.history_list[i-1].cmd;
X                base_env.history_list[i].number = base_env.history_list[i-1].number;
X            }
X            base_env.history_list[0].cmd = copy_group(sp->group,0,0,1);
X            base_env.history_list[0].number = base_env.prompts_issued[0];
X        }
X        rc = exec_sentence(sp,0);
X        flag = 1;
X        for (pp = sp->group; pp != (struct phrase *)NULL; pp = pp->next)
X        {   /* check for early termination */
X            if (pp->type == (struct token *)NULL)
X                flag = 0;
X        }
X        sentence_free(sp);
X        do_deletes();
X        if (!flag || rc == 1)
X            break;
X        cmd_count++;
X    }
X    force_signal("EXIT");
X
X    exit(cmd_lastrc);
X}   /* end of main */
X
X#ifndef GEMDOS
Xint main_fdget()
X{
X    register int i;
X
X    i = max_sysfd-1;
X    while (i >= 0)
X    {   /* look for unused fd */
X        if (!(open_sysfds[i/8] & (1<<(i%8))))
X        {   /* an unused fd? */
X            open_sysfds[i/8] |= (1<<(i%8));
X            return i;
X        }
X        i--;
X    }
X}   /* end of main_fdget */
X
Xint main_fdput(i)
Xint i;
X{
X    open_sysfds[i/8] &= ~(1<<(i%8));
X    return 0;
X}   /* end of main_fdput */
X#endif  /* GEMDOS */
X
Xint main_iopush()
X{
X    long fd;
X    int i;
X    register struct iostack *iop;
X#ifndef GEMDOS
X    int topfd,rc;
X#endif  /* GEMDOS */
X
X    iop = (struct iostack *)malloc(sizeof(*iop));
X    if (iop == (struct iostack *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("main_iopush"));
X        return -1;
X    }
X    iop->next = base_env.io;
X    iop->input = iop->output = iop->errout = -1;
X    base_env.io = iop;
X
X#ifdef  GEMDOS
X#define FD_INP  0
X#define FD_OUT  1
X#define FD_AUX  2
X#define FD_PRN  3
X
X    if (base_env.fd_con < 0)
X    {   /* if console, default stderr not there yet? */
X        fd = Fopen("con:",2);
X        if (fd < 0)
X        {   /* all set up for console? */
X            iop->errout = FD_OUT;
X            errmsg(0,LOC("main_iopush"),"can't set up stderr");
X            return -1;
X        }
X        iop->errout = Fdup(fd);
X        if (iop->errout < 0)
X        {   /* all set up for console? */
X            iop->errout = FD_OUT;
X            errmsg(0,LOC("main_iopush"),"can't set up stderr");
X            return -1;
X        }
X        base_env.fd_con = iop->errout;
X    }
X    else
X    {   /* just dup currently setup stderr */
X        iop->errout = Fdup(2);
X        if (iop->errout < 0)
X        {   /* all set up for stderr? */
X            iop->errout = base_env.fd_con;
X            errmsg(0,LOC("main_iopush"),"can't set up stderr");
X            return -1;
X        }
X    }
X    iop->input = Fdup(FD_INP);
X    if (iop->input < 0)
X    {   /* can we get a new stdin? */
X        errmsg(0,LOC("main_iopush"),"can't setup stdin for use");
X        return -1;
X    }
X    iop->output = Fdup(FD_OUT);
X    if (iop->output < 0)
X    {   /* can we get a new stdout? */
X        errmsg(0,LOC("main_iopush"),"can't setup stdout for use");
X        return -1;
X    }
X    if (base_env.fd_aux < 0)
X    {   /* got ptr to aux: */
X        base_env.fd_aux = Fdup(FD_AUX);
X        if (base_env.fd_aux < 0)
X        {   /* can we save the aux: handle? */
X            errmsg(0,LOC("main_iopush"),"can't save aux:");
X            return -1;
X        }
X    }
X    if (base_env.fd_prn < 0)
X    {   /* get ptr to prn: */
X        base_env.fd_prn = Fdup(FD_PRN);
X        if (base_env.fd_prn < 0)
X        {   /* can we save the prn: handle? */
X            errmsg(0,LOC("main_iopush"),"can't save prn:");
X            return -1;
X        }
X    }
X    for (i = 0; i <= MAXUFD; i++)
X        Fclose(i);
X#else
X    topfd = main_fdget();
X    if ((rc = dup2(2,topfd)) < 0)
X    {   /* set up standard error? */
X        if (iop->next != (struct iostack *)NULL)
X            iop->errout = iop->next->errout;
X        else iop->errout = 2;
X        errmsg(0,LOC("main_iopush"),"can't setup stderr rc=%d,errno=%d",rc,errno);
X        return -1;
X    }
X    iop->errout = topfd;
X    fcntl(iop->errout,F_SETFD,1);
X    topfd = main_fdget();
X    if ((rc = dup2(1,topfd)) < 0)
X    {   /* set up standard output? */
X        errmsg(0,LOC("main_iopush"),"can't setup stdout rc=%d,errno=%d",rc,errno);
X        return -1;
X    }
X    iop->output = topfd;
X    fcntl(iop->output,F_SETFD,1);
X    topfd = main_fdget();
X    if ((rc = dup2(0,topfd)) < 0)
X    {   /* set up standard input? */
X        errmsg(0,LOC("main_iopush"),"can't setup stdin rc=%d,errno=%d",rc,errno);
X        return -1;
X    }
X    iop->input = topfd;
X    fcntl(iop->input,F_SETFD,1);
X
X    for (i = 0; i < MAXUFD; i++)
X        close(i);
X#endif  /* GEMDOS */
X    return 0;
X}   /* end of main_iopush */
X
Xint main_iopop()
X{
X    register struct iostack *iop;
X
X    iop = base_env.io;
X    base_env.io = iop->next;
X
X#ifdef  GEMDOS
X    Fclose(iop->input);
X    Fclose(iop->output);
X    Fclose(iop->errout);
X#else
X    if (iop->input != -1)
X    {   /* close it down */
X        close(iop->input);
X        main_fdput(iop->input);
X    }
X    if (iop->output != -1)
X    {   /* close it down */
X        close(iop->output);
X        main_fdput(iop->output);
X    }
X    if (iop->errout != -1)
X    {   /* close it down */
X        close(iop->errout);
X        main_fdput(iop->errout);
X    }
X#endif  /* GEMDOS */
X
X    free(iop);
X    return 0;
X}   /* end of main_iopop */
X
Xint main_dirpush()
X{
X    register struct dirstack *dp;
X
X    dp = (struct dirstack *)malloc(sizeof(*dp));
X    if (dp == (struct dirstack *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("main_dirpush"));
X        return -1;
X    }
X    dp->next = base_env.dir;
X    base_env.dir = dp;
X    dp->current = (char *)NULL;
X    return 0;
X}   /* end of main_dirpush */
X
Xint main_dirpop()
X{
X    int rc;
X    char *newdir;
X    register struct dirstack *dp;
X
X    dp = base_env.dir;
X    base_env.dir = dp->next;
X
X    newdir = (char *)NULL;
X    if (base_env.dir != (struct dirstack *)NULL)
X    {   /* if something to go back to */
X        rc = lchdir(base_env.dir->current,&newdir);
X        if (rc == 0)
X        {   /* if we got back to old dir */
X            var_define0("PWD",newdir,0);
X            free(newdir);
X        }
X    }
X    if (dp->current != (char *)NULL)
X        free(dp->current);
X    free(dp);
X    return rc;
X}   /* end of main_dirpop */
X
Xint main_varpush()
X{
X    register struct varstack *vs;
X
X    vs = (struct varstack *)malloc(sizeof(struct varstack));
X    if (vs == (struct varstack *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("main_varpush"));
X        return -1;
X    }
X    vs->table = (struct variable *)NULL;
X    vs->next = base_env.var;
X    base_env.var = vs;
X    return 0;
X}   /* end of main_varpush */
X
Xint main_varpop()
X{
X    register struct varstack *vs;
X
X    if (base_env.var == (struct varstack *)NULL)
X        return 1;
X    vs = base_env.var;
X    base_env.var = vs->next;
X
X    var_tablefree(vs->table);
X    free(vs);
X    return 0;
X}   /* end of main_varpop */
X
Xstruct argstack
X{
X    int     argc;
X    char    **argv;
X    struct  argstack *next;
X};
Xstatic struct argstack *argtop = (struct argstack *)NULL;
X
Xint main_argspush(pp)
Xstruct phrase *pp;
X{
X    register struct argstack *as;
X
X    as = (struct argstack *)malloc(sizeof(*as));
X    if (as == (struct argstack *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("main_argspush"));
X        return -1;
X    }
X    as->next = argtop;
X    argtop = as;
X    as->argc = var_argc;
X    as->argv = var_argv;
X    var_argc = 0;
X    var_argv = (char **)NULL;
X    if (pp->body != (struct token *)NULL)
X        var_resetargs(pp->body->next);
X    return 0;
X}   /* end of main_argspush */
X
Xint main_argspop()
X{
X    register int i;
X
X    if (argtop == (struct argstack *)NULL)
X        return 1;
X    for (i = 1; i < var_argc; i++)
X        if (var_argv[i] != (char *)NULL)
X            free(var_argv[i]);
X    if (var_argv != (char **)NULL)
X        free(var_argv);
X    var_argc = argtop->argc;
X    var_argv = argtop->argv;
X    argtop = argtop->next;
X    return 0;
X}   /* end of main_argspop */
END_OF_FILE
if test 16634 -ne `wc -c <'main.c'`; then
    echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
if test -f 'reader/editing.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/editing.c'\"
else
echo shar: Extracting \"'reader/editing.c'\" \(18659 characters\)
sed "s/^X//" >'reader/editing.c' <<'END_OF_FILE'
X/*
X * Various editing subroutines for internal use by the "reader" package
X *
X * Dave Clemans, 4/84 (initial version); 1/89 (more generality)
X *
X * $Id: editing.c,v 1.2 89/02/20 20:20:06 dclemans Exp $
X *
X * $Log:	editing.c,v $
X * Revision 1.2  89/02/20  20:20:06  dclemans
X * Add RCS identifiers
X * 
X */
X#include <ctype.h>
X#ifndef GEMDOS
X#include <sgtty.h>
X#endif  GEMDOS
X#ifndef LOC
X#define LOC(r)  __FILE__,r,__LINE__
X#endif  LOC
X
X#include "history.h"
X
X/*
X * Interesting function definitions
X */
Xextern char *substr();
X
X/*
X * Is this character printable?  ...  i.e., would we want to print it
X * directly or generate a translation of it.
X */
Xstatic int printable(c)
Xregister char c;
X{
X	if (isprint(c) || (c == '\n') || (c == '\r') || (c == '\t'))
X		return(1);
X	return(0);
X};	/* end of printable */
X
X/*
X * How much space will this character take up on the screen?
X */
Xstatic int charwidth(c,ttycursor,cursor,size)
Xregister char c;
Xregister int ttycursor;
X{
X	register int width;
X
X	if (cursor == size)
X		width = 1;
X	else if (!printable(c))
X		width = 2;
X        else if (c == '\r')
X                width = 0;
X	else if (c != '\t')
X		width = 1;
X	else
X	{	/* expand a tab; tab stop every 8 spaces */
X		width = 1;
X		while ((ttycursor+width) & 07)
X			width++;
X	}
X	return(width);
X};	/* end of charwidth */
X
X/*
X * What is the on-screen width of the line up to given character?
X */
Xstatic int linewidth(index)
Xregister int index;
X{
X	register int counter,width;
X
X	width = 0;
X	for (counter = 0; counter < index; counter++)
X	{	/* find tty width of line before tab */
X		width += charwidth(History.currentLine->contents[counter],width,
X			History.currentLine->cursor,History.currentLine->size);
X	}
X	return(width);
X};	/* end of linewidth */
X
X/*
X * Go back a character on the display
X */
Xstatic backchar()
X{
X	register char c;
X	register int counter,width;
X
X	counter = History.currentLine->cursor-1;
X	if (counter < 0)	/* already all the way back */
X		return;
X	c = History.currentLine->contents[counter];
X	History.currentLine->cursor--;
X
X        if (c != '\r')
X		_writechar('\b');
X	History.currentLine->ttycursor--;
X
X	if (c != '\t')
X	{	/* only tabs handled specially here */
X		return;
X	}
X
X	width = linewidth(counter);
X
X	while (History.currentLine->ttycursor & 07)	/* tab=8 spaces */
X	{	/* go back to tab stop */
X		if (width >= History.currentLine->ttycursor)
X			break;
X		_writechar('\b');
X		History.currentLine->ttycursor--;
X	}
X};	/* end of backchar */
X
X/*
X * Echo a character; if non-printing print as ^c
X */
X_echochar(c)
Xregister char c;
X{
X	History.currentLine->ttycursor++;
X	if (printable(c))
X	{	/* normal stuff for printable characters */
X		if (c == '\t')
X		{	/* special stuff for tab */
X			_writechar(' ');
X			while (History.currentLine->ttycursor & 07) /* tab=8 spaces */
X			{	/* expand a tab */
X				_writechar(' ');
X				History.currentLine->ttycursor++;
X			}
X		}
X		else if (c == '\r')
X		    History.currentLine->ttycursor--;
X       		else	_writechar(c);
X	}
X	else
X	{	/* special handling for non-printables */
X		_writechar('^');
X		if (c == '\177')
X			_writechar('?');
X		else	_writechar((char)((int)c | 0100));
X		History.currentLine->ttycursor++;
X	}
X};	/* end of _echochar */
X
X/*
X * Do a standard character erase
X */
X_doStandardErase(c)
Xchar c;
X{
X	register int i,width;
X
X	if (History.currentLine->cursor > 0)
X	{	/* kill a character */
X#ifndef GEMDOS
X		if (_savedState.localAttributes & LCRTBS)
X		{	/* crt style erase */
X#endif  GEMDS
X			i = 1;
X			if (!printable(History.currentLine->contents[History.currentLine->cursor-1]))
X				i = 2;
X			for (; i > 0; --i)
X			{	/* start erasing */
X				backchar();
X				_writechar(' ');
X				_writechar('\b');
X				if (i > 1)
X					History.currentLine->cursor++;
X			}
X#ifndef GEMDOS
X		}
X		else if (_savedState.localAttributes & LPRTERA)
X		{	/* paper style erase */
X			_writechar('/');
X			_echochar(History.currentLine->contents[History.currentLine->cursor-1]);
X			_writechar('/');
X		}
X		else	backchar();
X#endif  GEMDOS
X		History.currentLine->cursor++;
X		if (History.currentLine->cursor == History.currentLine->size)
X		{	/* cursor at end of line */
X			History.currentLine->size--;
X			History.currentLine->cursor--;
X		}
X		else if (History.currentLine->cursor > History.currentLine->size)
X		{	/* line buffer screwed up */
X			errmsg(0,LOC("doStandardErase"),"line buffer screwed up");
X			if (_savedState.stream >= 0)
X				_terminate(_savedState.stream);
X			_savedState.stream = -1;
X			exit(-1);
X		}
X		else
X		{	/* else take out from middle of line */
X			strncpy(&History.currentLine->contents[History.currentLine->cursor-1],
X				&History.currentLine->contents[History.currentLine->cursor],
X				History.currentLine->size-History.currentLine->cursor);
X			History.currentLine->cursor--;
X			History.currentLine->size--;
X			width = History.currentLine->ttycursor;
X			_doStandardEndLine(c);
X			_writechar(' ');
X			_writechar('\b');
X			while (History.currentLine->ttycursor > width)
X				_doStandardBackspace(c);
X		}
X	}
X};	/* end of _doStandardErase */
X
X/*
X * Do a standard line kill
X */
X_doStandardKill(c)
Xchar c;
X{
X	register int i,j;
X	char cc;
X
X	_doStandardEndLine('\000');
X#ifndef GEMDOS
X	if (_savedState.localAttributes & LCRTERA)
X	{	/* crt style kill */
X#endif  GEMDOS
X		for (i = History.currentLine->size; i > 0; i--)
X		{	/* erase the line */
X			j = 1;
X			cc = History.currentLine->contents[i-1];
X			if (!printable(cc))
X				j = 2;
X			for (; j > 0; --j)
X			{	/* start killing */
X				backchar();
X				_writechar(' ');
X				_writechar('\b');
X				if (j > 1)
X					History.currentLine->cursor++;
X			}
X		}
X#ifndef GEMDOS
X	}
X	else if (_savedState.localAttributes & LPRTERA)
X	{	/* printing style kill */
X		if (c)
X			_echochar(c);
X		_writechar('\n');
X	}
X	else
X	{	/* go to start of line */
X		for (i = History.currentLine->size; i > 0; i--)
X                {
X			backchar();
X                }
X	}
X#endif  GEMDOS
X	History.currentLine->size = 0;
X	History.currentLine->cursor = 0;
X	History.currentLine->ttycursor = 0;
X};	/* end of _doStandardKill */
X
X/*
X * Retype the line
X */
X_doStandardRetype(c)
Xchar c;
X{
X	register int i,cursor;
X
X	cursor = History.currentLine->cursor;
X	if (c)
X	{	/* if a user typed character */
X		_doStandardEndLine(c);
X		_echochar(c);
X		_writechar('\n');
X	}
X	History.currentLine->ttycursor = 0;
X	for (i = 0; i < History.currentLine->size; i++)
X	{	/* reprint the line */
X		_echochar(History.currentLine->contents[i]);
X	}
X	_doStandardStartLine(c);
X	while (History.currentLine->cursor < cursor)
X		_doStandardSpace(c);
X};	/* end of _doStandardRetype */
X
X/*
X * Do unambigous command/filename expansion for last word in current line
X */
X_doStandardExpand(c)
Xchar c;
X{
X	register int rc,i;
X
X	History.currentLine->contents[History.currentLine->size] = '\0';
X	rc = ExpandAName(History.currentLine->contents,
X		sizeof(History.currentLine->contents)-1,
X		History.currentLine->size, 1);
X	i = History.currentLine->size;
X	while (History.currentLine->contents[i])
X	{	/* enter the characters in as input */
X		_savechar(History.currentLine->contents[i++]);
X	}
X	if (rc != 1)
X	{	/* if ambigous or unknown */
X		_writechar('\007');
X	}
X};	/* end of _doStandardExpand */
X
X/*
X * Insert or append a character to the current line
X */
X_doStandardCharacter(c)
Xchar c;
X{
X	register int counter,width;
X	char buffer[HISTORY_BUFFER];
X
X	if (History.currentLine->cursor == History.currentLine->size)
X	{	/* append to line */
X		_echochar(c);
X		History.currentLine->contents[History.currentLine->size] = c;
X		History.currentLine->size++;
X		History.currentLine->cursor++;
X	}
X	else if (History.currentLine->cursor > History.currentLine->size)
X	{	/* line buffer screwed up */
X		errmsg(0,LOC("doStandardCharacter"),"line buffer screwed up");
X		if (_savedState.stream >= 0)
X			_terminate(_savedState.stream);
X		_savedState.stream = -1;
X		exit(-1);
X	}
X	else
X	{	/* insert into line */
X		strncpy(buffer,History.currentLine->contents,History.currentLine->cursor);
X		buffer[History.currentLine->cursor] = c;
X		strncpy(&buffer[History.currentLine->cursor+1],
X			&History.currentLine->contents[History.currentLine->cursor],
X			History.currentLine->size-History.currentLine->cursor);
X		strncpy(History.currentLine->contents,buffer,History.currentLine->size+1);
X		History.currentLine->size++;
X		History.currentLine->cursor++;
X		_echochar(c);
X		width = History.currentLine->ttycursor;
X		for (counter = History.currentLine->cursor; counter < History.currentLine->size; counter++)
X		{	/* repaint rest of line */
X			_echochar(History.currentLine->contents[counter]);
X			History.currentLine->cursor++;
X		}
X		while (History.currentLine->ttycursor > width)
X			_doStandardBackspace('\0');
X	}
X};	/* end of _doStandardCharacter */
X
X/*
X * Delete next character in line
X */
X_doStandardDelete(c)
Xchar c;
X{
X	register int counter,width;
X
X	if ((History.currentLine->cursor >= 0) &&
X	    (History.currentLine->cursor < History.currentLine->size))
X	{	/* something there to delete */
X		width = linewidth(History.currentLine->cursor);
X		strncpy(&History.currentLine->contents[History.currentLine->cursor],
X			&History.currentLine->contents[History.currentLine->cursor+1],
X			History.currentLine->size-History.currentLine->cursor-1);
X		History.currentLine->size--;
X		width = History.currentLine->ttycursor;
X		_doStandardEndLine(c);
X		_writechar(' ');
X		_writechar('\b');
X		while (History.currentLine->ttycursor > width)
X			_doStandardBackspace(c);
X	}
X};	/* end of _doStandardDelete */
X
X/*
X * Move the cursor back
X */
X_doStandardBackspace(c)
Xchar c;
X{
X	if (History.currentLine->cursor > 0)
X	{	/* if something to backspace over */
X		backchar();
X	}
X};	/* end of _doStandardBackspace */
X
X/*
X * Move the cursor forward
X */
X_doStandardSpace(c)
Xchar c;
X{
X	if (History.currentLine->cursor < History.currentLine->size)
X	{	/* if something to go forward over */
X		_echochar(History.currentLine->contents[History.currentLine->cursor]);
X		History.currentLine->cursor++;
X	}
X};	/* end of _doStandardSpace */
X
X/*
X * Get an older line from the history list and clobber the current line
X * in the history list with it.
X */
X_doStandardPrevLine(c)
Xchar c;
X{
X	register struct historyLine *hp;
X
X	hp = History.listPointer->next;
X	if (hp && hp->command)
X	{	/* there's an older line there */
X		_doStandardKill('\0');
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size-1;
X		History.currentLine->cursor = hp->size-1;
X		_doStandardRetype('\0');
X		History.listPointer = hp;
X	}
X};	/* end of _doStandardPrevLine */
X
X/*
X * Get a newer line from the history list and clobber the current line
X * in the history list with it.
X */
X_doStandardNextLine(c)
Xchar c;
X{
X	register struct historyLine *hp;
X
X	for (hp = History.listTop; hp && (hp->next != History.listPointer); hp = hp->next)
X		continue;
X	if (hp && (hp != History.currentLine))
X	{	/* if a newer line exists */
X		_doStandardKill('\0');
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size-1;
X		History.currentLine->cursor = hp->size-1;
X		_doStandardRetype('\0');
X		History.listPointer = hp;
X	}
X};	/* end of _doStandardNextLine */
X
X/*
X * Goto the beginning of the line
X */
X_doStandardStartLine(c)
Xchar c;
X{
X	while (History.currentLine->cursor > 0)
X	{	/* while stuff to space over */
X		backchar();
X	}
X};	/* end of _doStandardStartLine */
X
X
X/*
X * Goto the end of the line
X */
X_doStandardEndLine(c)
Xchar c;
X{
X	while (History.currentLine->cursor < History.currentLine->size)
X	{	/* if something to go forward over */
X		_echochar(History.currentLine->contents[History.currentLine->cursor]);
X		History.currentLine->cursor++;
X	}
X};	/* end of _doStandardEndLine */
X
X/*
X * Capitalize the current character
X */
X_doStandardCapitalize(c)
Xchar c;
X{
X	register int cursor;
X
X	cursor = History.currentLine->cursor;
X	if (cursor == History.currentLine->size)
X		return;		/* no character at end of line */
X	if (islower(History.currentLine->contents[cursor]))
X		History.currentLine->contents[cursor] =
X		    toupper(History.currentLine->contents[cursor]);
X	_echochar(History.currentLine->contents[cursor]);
X	History.currentLine->cursor++;
X	backchar();
X	History.currentLine->cursor--;
X};	/* end of _doStandardCapitalize */
X
X/*
X * Transpose the current and next characters
X */
X_doStandardTranspose(c)
Xchar c;
X{
X	register char cc;
X	register int cursor;
X
X	cursor = History.currentLine->cursor;
X	if (cursor == History.currentLine->size)
X		return;		/* nothing to transpose */
X	cc = History.currentLine->contents[cursor];
X	if (cursor+1 < History.currentLine->size)
X	{	/* if room for two chars */
X		History.currentLine->contents[cursor]=
X			History.currentLine->contents[cursor+1];
X		History.currentLine->contents[cursor+1] = cc;
X		_echochar(History.currentLine->contents[cursor]);
X		History.currentLine->cursor++;
X		_echochar(cc);
X		backchar();
X		History.currentLine->cursor--;
X		backchar();
X	}
X	else
X	{	/* else no; treat end of line like space */
X		History.currentLine->contents[cursor] = ' ';
X		_echochar(' ');
X		History.currentLine->cursor++;
X		_doStandardCharacter(cc);
X		_doStandardBackspace(c);
X		backchar();
X		History.currentLine->cursor--;
X	}
X};	/* end of _doStandardTranspose */
X
X/*
X * Search lines backwards in time for a string.
X * If found, set global History.listPointer to point to it.
X * The search starts at History.listPointer.
X * If string not found, History.listPointer is not changed.
X * Returns index of start of search string if found, -1 if not found.
X */
Xstatic int backsearchfor(string)
Xchar *string;
X{
X	register struct historyLine *hp;
X	register char *cp;
X
X	cp = (char *)NULL;
X	for (hp = History.listPointer->next; hp; hp = hp->next)
X	{	/* start searching */
X		hp->contents[hp->size] = '\0';
X		cp = substr(hp->contents,string);
X		if (cp)
X			break;
X	}
X	if (!cp || !hp)
X		return(-1);
X	History.listPointer = hp;
X	return((int)cp - (int)hp->contents);
X};	/* end of backsearchfor */
X
X/*
X * Search lines forwards in time for a string.
X * If found, set global History.listPointer to point to it.
X * The search starts at History.listPointer.
X * If string not found, History.listPointer is not changed.
X * Returns index of start of search string if found, -1 if not found.
X */
Xstatic int searchfor(string)
Xchar *string;
X{
X	register struct historyLine *hp,*bp;
X	register char *cp;
X
X	cp = (char *)NULL;
X	for (hp = History.listPointer; hp && (hp != History.listTop); )
X	{	/* start searching */
X		for (bp = History.listTop; bp && (bp->next != hp); bp = bp->next)
X			continue;
X		if (!bp || (bp == History.listTop))
X			break;
X		hp = bp;
X		hp->contents[hp->size] = '\0';
X		cp = substr(hp->contents,string);
X		if (cp)
X			break;
X	}
X	if (!cp || !hp)
X		return(-1);
X	History.listPointer = hp;
X	return((int)cp - (int)hp->contents);
X};	/* end of searchfor */
X
X/*
X * Simple read a line routine; just turn on normal tty mode; read a
X * line with normal tty editing; and put the tty modes back.
X */
Xstatic readline(buffer,size)
Xregister char *buffer;
Xregister int size;
X{
X	register int cursor;
X	register int c;
X#ifndef GEMDOS
X	struct sgttyb ttymodes;
X#endif  GEMDOS
X
X	buffer[0] = '\0';
X	if (_savedState.stream < 0)
X		return;
X#ifndef GEMDOS
X	ttymodes = _savedState.basicAttributes;
X	ttymodes.sg_flags &= ~ECHO;
X	ioctl(_savedState.stream, TIOCSETP, &ttymodes);
X#endif  GEMDOS
X	for (cursor = 0; cursor < size; )
X	{	/* start filling the buffer till cr or nl */
X		c = _readchar(_savedState.stream);
X		if ((c == '\n') || (c == '\r') || (c == EOF))
X			break;
X		buffer[cursor++] = c;
X	}
X	buffer[cursor] = '\0';
X#ifndef GEMDOS
X	ioctl(_savedState.stream, TIOCSETP, &_newState.basicAttributes);
X#endif  GEMDOS
X};	/* end of readline */
X
Xstatic char searchString[HISTORY_BUFFER];
X
X/*
X * Search for older lines from the history list and clobber the current line
X * in the history list with it.
X */
X_doStandardSrchPrevLine(c,flag)
Xchar c;
Xint flag;
X{
X	register struct historyLine *hp;
X	register int cursor;
X	char search[HISTORY_BUFFER];
X
X	search[0] = '\0';
X	if (!flag)
X		readline(search,sizeof search-1);
X	if (!search[0])
X		strcpy(search,searchString);
X	if (!search[0])
X	{	/* nothing to search for */
X		_writechar('\007');
X		return;
X	}
X	cursor = backsearchfor(search);
X	if (cursor < 0)
X		return;
X	hp = History.listPointer;
X	if (hp)
X	{	/* there's an older line there */
X		_doStandardKill('\0');
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size-1;
X		History.currentLine->cursor = hp->size-1;
X		_doStandardRetype('\0');
X		History.listPointer = hp;
X	}
X	strcpy(searchString,search);
X};	/* end of _doStandardSrchPrevLine */
X
X/*
X * Search for newer lines from the history list and clobber the current line
X * in the history list with it.
X */
X_doStandardSearchNextLine(c,flag)
Xchar c;
Xint flag;
X{
X	register struct historyLine *hp;
X	register int cursor;
X	char search[HISTORY_BUFFER];
X
X	search[0] = '\0';
X	if (!flag)
X		readline(search,sizeof search-1);
X	if (!search[0])
X		strcpy(search,searchString);
X	if (!search[0])
X	{	/* nothing to search for */
X		_writechar('\007');
X		return;
X	}
X	cursor = searchfor(search);
X	if (cursor < 0)
X		return;
X	hp = History.listPointer;
X	if (hp)
X	{	/* if a newer line exists */
X		_doStandardKill('\0');
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size-1;
X		History.currentLine->cursor = hp->size-1;
X		_doStandardRetype('\0');
X		History.listPointer = hp;
X	}
X	strcpy(searchString,search);
X};	/* end of _doStandardSearchNextLine */
X
X/*
X * move to a given position in the current line
X */
Xstatic moveto(position)
Xregister int position;
X{
X	if (position < 0)
X		position = 0;
X	else if (position > History.currentLine->size)
X		position = History.currentLine->size;
X	if (History.currentLine->cursor > position)
X	{	/* move backwards */
X		while (History.currentLine->cursor > position)
X			_doStandardBackspace('\0');
X	}
X	else if (History.currentLine->cursor < position)
X	{	/* move forwards */
X		while (History.currentLine->cursor < position)
X			_doStandardSpace('\0');
X	}
X};	/* end of moveto */
X
X/*
X * delete a given range of characters
X */
X_deleteTo(start,stop)
Xregister int start,stop;
X{
X	register int count;
X	register int savecursor;
X
X	if (start < 0)
X		start = 0;
X	else if (start > History.currentLine->size)
X		start = History.currentLine->size;
X	if (stop < 0)
X		stop = 0;
X	else if (stop > History.currentLine->size)
X		stop = History.currentLine->size;
X	savecursor = History.currentLine->cursor;
X
X	_pushOnKillRing(start,stop);
X
X	count = stop-start+1;
X	if (count <= 0)
X		return;
X
X	moveto(start);
X	for (; count > 0; --count)
X	{	/* start deleting characters */
X		_doStandardDelete('\0');
X	}
X	moveto(savecursor);
X};	/* end of _deleteTo */
X
X/*
X * Delete from current position to end of line
X */
X_doStandardDelEndLine(c)
Xchar c;
X{
X	_deleteTo(History.currentLine->cursor,History.currentLine->size);
X};	/* end of _doStandardDelEndLine */
END_OF_FILE
if test 18659 -ne `wc -c <'reader/editing.c'`; then
    echo shar: \"'reader/editing.c'\" unpacked with wrong size!
fi
# end of 'reader/editing.c'
fi
if test -f 'reader/reader.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/reader.c'\"
else
echo shar: Extracting \"'reader/reader.c'\" \(17920 characters\)
sed "s/^X//" >'reader/reader.c' <<'END_OF_FILE'
X/*
X * reader	-	read line with editing
X *
X * Dave Clemans, 4/84 (first version); 1/89 more generality
X *
X * This module in a sense is a superset of 'fgets' in that it reads a
X * line at a time from a file stream.  However, it is also a lot more.
X *
X * Depending on the value of the "EDITOR" environment variable, reader
X * provides 'vi' or 'emacs' style input editing.
X *
X * Reader keeps a history of the lines that it has seen.  The history
X * list can be referenced and its size changed through global variables.
X * There is an alternate point to reader (editor) that also asks for
X * a line from the user; except that the buffer starts out with the
X * contents of some line from the history list instead of being empty.
X *
X * External entry points:
X *	Reader		- the 'reader' get a line routine
X *	Editor		- the 'editor' edit a line routine
X *
X * $Id: reader.c,v 1.7 89/03/07 19:37:36 dclemans Exp $
X *
X * $Log:	reader.c,v $
X * Revision 1.7  89/03/07  19:37:36  dclemans
X * make work on bsd systems
X * 
X * Revision 1.6  89/02/20  20:32:33  dclemans
X * More RCS ID work
X * 
X * Revision 1.5  89/02/20  20:20:23  dclemans
X * Add RCS identifiers
X * 
X */
Xchar reader_version[] = "Reader Version 0.2 (dgc); $Date: 89/03/07 19:37:36 $";
X
X#include <ctype.h>
X#include <strings.h>
X#ifdef	GEMDOS
X#include <osbind.h>
X#define	index	strchr
X#define	rindex	strrchr
X#else
X#include <sys/types.h>
X#include <sgtty.h>
X#include <signal.h>
X#endif	GEMDOS
X#include <errno.h>
X#ifndef LOC
X#define LOC(r)  __FILE__,r,__LINE__
X#endif  LOC
X
X#include "shell.h"
X#define DEFAULT_MAXSIZE DEFAULT_HISTORY
X#include "history.h"
Xstruct History History;
Xstruct savedState _savedState;
Xstruct newState _newState;
X
X#ifndef DIR_SEPARATOR
X#ifndef GEMDOS
X#define DIR_SEPARATOR '/'
X#else
X#define DIR_SEPARATOR '\\'
X#endif  GEMDOS
X#endif  DIR_SEPARATOR
X
X/*
X * Interesting function declarations
X */
Xextern char *var_normal();
Xextern int wild_match();
X
X/*
X * For saving pending input during terminal mode changes
X */
Xstatic struct	pendingInput
X{
X	char buffer[BUFSIZ];
X	int size;
X	int next;
X} pendingInput;
X
X#ifndef GEMDOS
X/*
X * For restoring things and exiting after catching a signal
X */
Xstatic catchSignal(sig)
Xint sig;
X{
X	_savechar(EOF);
X	signal(sig,catchSignal);
X};	/* end of catchSignal */
X
X/*
X * Handle a suspend signal
X */
Xstatic catchSuspend()
X{
X	int stream;
X
X	stream = _savedState.stream;
X	if (stream >= 0)
X		_terminate(stream);
X	signal(SIGTSTP,SIG_DFL);
X	killpg(getpgrp(getpid()),SIGTSTP);
X	if (stream >= 0)
X		initialize(stream);
X};	/* end of catchSuspend */
X#endif  GEMDOS
X
X/*
X * Initialize the users environment and terminal attributes to match
X * our needs.
X *
X * Before we change anything, state is saved.
X *
X * We do control character munging so that we can see characters that
X * CBREAK mode would otherwise hide from us.
X */
Xstatic initialize(fp)
Xregister int fp;
X{
X#ifndef GEMDOS
X	register int rc;
X
X	if (!isatty(fp))
X		return;
X#endif  GEMDOS
X	_savedState.stream = fp;
X
X#ifndef GEMDOS
X	/*
X	 * Get the initial state
X	 */
X	ioctl(fp, TIOCGETP, &_savedState.basicAttributes);
X	ioctl(fp, TIOCGETC, &_savedState.basicCharacters);
X	ioctl(fp, TIOCLGET, &_savedState.localAttributes);
X	ioctl(fp, TIOCGLTC, &_savedState.localCharacters);
X	_savedState.sigint = (int)signal(SIGINT,SIG_IGN);
X	_savedState.sigquit = (int)signal(SIGQUIT,SIG_IGN);
X	_savedState.sigtstp = (int)signal(SIGTSTP,SIG_IGN);
X	signal(SIGINT,_savedState.sigint);
X	signal(SIGQUIT,_savedState.sigquit);
X	signal(SIGTSTP,_savedState.sigtstp);
X	_newState.basicAttributes = _savedState.basicAttributes;
X	_newState.basicCharacters = _savedState.basicCharacters;
X	_newState.localAttributes = _savedState.localAttributes;
X	_newState.localCharacters = _savedState.localCharacters;
X
X	/*
X	 * We want to turn off all control characters
X	 */
X	_newState.basicAttributes.sg_erase = '\377';
X	_newState.basicAttributes.sg_kill = '\377';
X	if (_savedState.sigint == (int)SIG_IGN)
X		_newState.basicCharacters.t_intrc = '\377';
X	signal(SIGINT,catchSignal);
X	if (_savedState.sigquit == (int)SIG_IGN)
X		_newState.basicCharacters.t_quitc = '\377';
X	signal(SIGQUIT,catchSignal);
X	_newState.basicCharacters.t_startc = '\377';
X	_newState.basicCharacters.t_stopc = '\377';
X	_newState.basicCharacters.t_eofc = '\377';
X	_newState.basicCharacters.t_brkc = '\377';
X	if (_savedState.sigtstp == (int)SIG_IGN)
X	{	/* then can clobber suspension chars */
X		_newState.localCharacters.t_suspc = '\377';
X		_newState.localCharacters.t_dsuspc = '\377';
X	}
X	signal(SIGTSTP,catchSuspend);
X	_newState.localCharacters.t_rprntc = '\377';
X	_newState.localCharacters.t_flushc = '\377';
X	_newState.localCharacters.t_werasc = '\377';
X	_newState.localCharacters.t_lnextc = '\377';
X
X	/*
X	 * We want to use cbreak mode, no echo, etc.
X	 */
X	_newState.basicAttributes.sg_flags &= ~(RAW|ECHO);
X	_newState.basicAttributes.sg_flags |= CBREAK;
X
X	/*
X	 * Do all the mode changes
X	 */
X	ioctl(fp, TIOCSETN, &_newState.basicAttributes);
X	ioctl(fp, TIOCSETC, &_newState.basicCharacters);
X	ioctl(fp, TIOCLSET, &_newState.localAttributes);
X	ioctl(fp, TIOCSLTC, &_newState.localCharacters);
X#endif  GEMDOS
X};	/* end of initialize */
X
X/*
X * Reset the users environment and terminal attributes to where they
X * were when we were entered.  Typeahead (which would otherwise be
X * lost) is handled by re-inputting it after everything is in the
X * right state.
X */
X#ifndef GEMDOS
X_terminate(fp)
Xregister int fp;
X{
X	register int rc;
X	long available;
X	char c;
X
X	if (!isatty(fp))
X		return;
X
X	signal(SIGINT,_savedState.sigint);
X	signal(SIGQUIT,_savedState.sigquit);
X	signal(SIGTSTP,_savedState.sigtstp);
X
X	ioctl(fp, TIOCSETN, &_savedState.basicAttributes);
X	ioctl(fp, TIOCSETC, &_savedState.basicCharacters);
X	ioctl(fp, TIOCLSET, &_savedState.localAttributes);
X	ioctl(fp, TIOCSLTC, &_savedState.localCharacters);
X};	/* end of _terminate */
X#else
X_terminate()
X{
X};	/* end of _terminate */
X#endif  GEMDOS
X
X/*
X * Get a line to use for an input buffer from the history list
X *
X * Returns it by setting the global History.currentLine
X */
Xstatic getHistoryBuffer()
X{
X	register int counter;
X	register struct historyLine *hp,*bottom;
X
X	if (!History.listTop)
X		History.maxSize = DEFAULT_MAXSIZE;
X	if (History.maxSize <= 0)
X		History.maxSize = 1;
X	if (History.currentSize > History.maxSize)
X	{	/* user has shrunk the stack; so free up the excess entries */
X		bottom = (struct historyLine *)NULL;
X		for (counter = 0,hp = History.listTop;
X		    hp; hp = hp->next,counter++)
X		{	/* go through what we will keep */
X			if (counter == (History.maxSize-1))
X			{	/* set new end of list */
X				bottom = hp;
X                                hp = hp->next;
X                                break;
X			}
X		}
X		if (bottom)
X		{	/* set new end of list */
X			History.listBottom = bottom;
X			bottom->next = (struct historyLine *)NULL;
X		}
X		for (; hp; )
X		{	/* free up excess entries */
X			bottom = hp;
X			hp = hp->next;
X			free(bottom);
X		}
X	}
X
X	if (History.currentLine && (!History.currentLine->size ||
X	    ((History.currentLine->size == 1) &&
X	    ((History.currentLine->contents[0] == '\n') ||
X	    (History.currentLine->contents[0] == '\r')))))
X	{	/* empty entry at top; so use it */
X		/* History.currentLine->command = ++History.currentCommand; leave same */
X		History.listPointer = History.currentLine;
X		History.currentLine->cursor = 0;
X		History.currentLine->size = 0;
X		History.currentLine->ttycursor = 0;
X		History.currentLine->contents[0] = '\0';
X		return;
X	}
X
X	if (History.currentSize < History.maxSize)
X	{	/* more room available; make new entry */
X		while (History.currentSize < History.maxSize)
X		{	/* make all new entries at once */
X			History.currentSize++;
X			hp = (struct historyLine *)malloc(sizeof(*hp));
X			if (!hp)
X			{	/* out of core */
X				errmsg(-1,LOC("getHistoryBuffer"));
X				exit(-1);
X			}
X			hp->contents[0] = '\0';
X			hp->size = 0;
X			hp->cursor = 0;
X			hp->ttycursor = 0;
X			hp->command = 0;
X			hp->next = (struct historyLine *)NULL;
X			if (!History.listTop)
X				History.listTop = History.listBottom = hp;
X			else
X			{	/* push onto top */
X				hp->next = History.listTop;
X				History.listTop = hp;
X			}
X		}
X		History.currentLine = hp;
X		History.listPointer = History.currentLine;
X		hp->command = ++History.currentCommand;
X		return;
X	}
X
X	/* Take entry from bottom and put on top */
X	hp = History.listBottom;
X	for (bottom = History.listTop; bottom && (bottom->next != hp); bottom = bottom->next)
X		continue;
X	if (!bottom)
X	{	/* list screwed up */
X		errmsg(0,LOC("getHistoryBuffer"),"screwed up history list");
X		exit(-1);
X	}
X	History.listBottom = bottom;
X	bottom->next = (struct historyLine *)NULL;
X	hp->next = History.listTop;
X	History.listTop = hp;
X	History.currentLine = hp;
X	History.listPointer = hp;
X	hp->size = 0;
X	hp->cursor = 0;
X	hp->ttycursor = 0;
X	hp->contents[0] = '\0';
X	hp->command = ++History.currentCommand;
X};	/* end of getHistoryBuffer */
X
X/*
X * Get the next user input character; if pending input take from that
X * else get it from the system.
X */
Xint _readchar(fp)
Xregister int fp;
X{
X	extern errno;
X	char c;
X    int cc;
X	register int rc;
X
X	if (pendingInput.size > 0)
X	{	/* take from already read in chars */
X		if (pendingInput.next != pendingInput.size)
X		{	/* if something was already there */
X			c = pendingInput.buffer[pendingInput.next++];
X			c &= 0x7f;
X			if (pendingInput.next == pendingInput.size)
X				pendingInput.next = pendingInput.size = 0;
X			return(c);
X		}
X	}
X	errno = 0;
X	cc = EOF;
X#ifndef GEMDOS
X	rc = read(fp,&c,1);
X	if ((rc <= 0) && (errno == EINTR))
X		return(_readchar(fp));
X    cc = c;
X#else
X        if (fp >= 0)
X        {
X            rc = 1;
X            cc = Bconin(2);
X        }
X        else rc = 0;
X#endif  GEMDOS
X	cc &= 0x7f;
X	if (rc <= 0)
X		cc = EOF;
X	return(cc);
X};	/* end of _readchar */
X
X/*
X * Simple output routine
X */
X_writechar(c)
Xchar c;
X{
X#ifndef GEMDOS
X	write(TTY,&c,1);
X#else
X        Bconout(2,c);
X#endif  GEMDOS
X};	/* end of _writechar */
X
X/*
X * Save a character in the pending input buffer to be read later
X */
X_savechar(c)
Xchar c;
X{
X	if (pendingInput.size >= sizeof(pendingInput.buffer)-1)
X		return;		/* no space to save it */
X	pendingInput.buffer[pendingInput.size++] = c;
X	pendingInput.buffer[pendingInput.size] = '\0';
X};	/* end of _savechar */
X
X/*
X * The basic "get a line" with editing routine.
X * We return the number of characters read, or
X *		0 on end-of-file
X *	       <0 on I/O error
X *
X * If history structure not allocated yet, allocate it.
X */
Xstatic int localReader(hp,fp,buffer,size)
Xstruct historyLine *hp;
Xregister int fp;
Xchar *buffer;
Xint size;
X{
X	char nextCharacter;
X	register char *cp;
X	register int rc;
X	extern errno;
X
X	/*
X	 * Get an input buffer
X	 */
X	getHistoryBuffer();
X	if (hp)
X	{	/* start with a non-empty line */
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size;
X		History.currentLine->cursor = hp->size;
X		_doStandardRetype('\0');
X	}
X
X	/*
X	 * Non-terminal mode dependant initialization
X	 */
X        if (_savedState.localEditor == (char *)NULL)
X	{
X		_savedState.localEditor = var_normal("$VISUAL");
X		if (_savedState.localEditor == (char *)NULL)
X			_savedState.localEditor = var_normal("$EDITOR");
X		if (_savedState.localEditor == (char *)NULL)
X			_savedState.localEditor = var_normal("$FCEDIT");
X	}
X	if (_savedState.localEditor != (char *)NULL)
X	{
X		cp = rindex(_savedState.localEditor,DIR_SEPARATOR);
X		if (cp != (char *)NULL)
X			cp++;
X		else	cp = _savedState.localEditor;
X		if (_savedState.isEmacs >= 0 && _savedState.isVi >= 0)
X		{
X			if (wild_match(cp,"*[eE][mM][aA][cC][sS]*"))
X			{
X				_savedState.isEmacs = 1;
X				_savedState.isVi = 0;
X			}
X			else
X			{	/* else check for other editors */
X				if (wild_match(cp,"*[vV][iI]*"))
X				{
X					_savedState.isEmacs = 0;
X					_savedState.isVi = 1;
X				}
X			}
X		}
X	}
X
X	/*
X	 * Set up terminal modes
X	 */
X	initialize(fp);
X
X	/*
X	 * Start reading the line
X	 */
X	for (;;)
X	{	/* loop til eol */
X		nextCharacter = _readchar(fp);
X		if (nextCharacter == EOF)
X		{	/* I/O error */
X			History.currentLine->size = 0;
X			History.currentLine->cursor = 0;
X			size = -1;
X			break;
X		}
X#ifndef GEMDOS
X		if ((nextCharacter == _savedState.basicCharacters.t_eofc) &&
X		    (History.currentLine->size == 0))
X		{	/* end-of-file */
X			break;
X		}
X		if ((nextCharacter == '\\') ||
X		    (nextCharacter == _savedState.localCharacters.t_lnextc))
X		{	/* quote the next character */
X			if (nextCharacter == '\\')
X			{	/* save this character */
X				_doStandardCharacter(nextCharacter);
X				if (History.currentLine->size >= sizeof(History.currentLine->contents)-1)
X					break;
X			}
X			nextCharacter = _readchar(fp);
X			if (nextCharacter == EOF)
X			{	/* end of file */
X				History.currentLine->cursor = 0;
X				History.currentLine->size = 0;
X				size = -1;
X				break;
X			}
X			_doStandardCharacter(nextCharacter);
X			if (History.currentLine->size >= sizeof(History.currentLine->contents)-1)
X				break;
X			continue;
X		}
X#else
X		if ((nextCharacter == '\032') &&
X		    (History.currentLine->size == 0))
X		{	/* end-of-file */
X			break;
X		}
X#endif  GEMDOS
X
X		/*
X		 * Check for special editors
X		 */
X		if (_savedState.isEmacs)
X		{	/* emacs style editing */
X			rc = _doEmacs(nextCharacter);
X			if (rc < 0)
X			{	/* end of file */
X				break;
X			}
X			if (rc > 0)
X				continue;
X		}
X		if (_savedState.isVi)
X		{	/* vi style editing */
X			rc = _doVi(nextCharacter);
X			if (rc < 0)
X			{	/* end of file */
X				break;
X			}
X			if (rc > 0)
X				continue;
X		}
X
X		/*
X		 * The "basic" editor; it basically matches the UCB 4.2
X		 * "newtty" driver except for the extension that the
X		 * "escape" character does command or filename completion.
X		 */
X#ifndef  GEMDOS
X		if (nextCharacter == _savedState.basicCharacters.t_eofc)
X		{	/* an eof */
X			break;
X		}
X		if (nextCharacter == _savedState.basicAttributes.sg_erase)
X		{	/* a simple backspace */
X			_doStandardErase(nextCharacter);
X			continue;
X		}
X		if (nextCharacter == _savedState.basicAttributes.sg_kill)
X		{	/* kill a line */
X			_doStandardKill(nextCharacter);
X			continue;
X		}
X		if (nextCharacter == _savedState.localCharacters.t_rprntc)
X		{	/* retype a line */
X			_doStandardRetype(nextCharacter);
X			continue;
X		}
X		if (nextCharacter == _savedState.localCharacters.t_werasc)
X		{	/* erase a word */
X			_deletePrevStdWord();
X			continue;
X		}
X		switch(nextCharacter)
X		{	/* decide what to do with it, if not caught above */
X		    case '\032': /* other end-of-file character */
X			size = 0;
X			break;
X
X		    default:	/* a character with no special meaning */
X			_doStandardCharacter(nextCharacter);
X			if (History.currentLine->size >= sizeof(History.currentLine->contents)-1)
X				break;
X			continue;
X
X		    case '\033':/* name expansion requested */
X			_doStandardExpand(nextCharacter);
X			continue;
X
X		    case '\014':/* list expansion possibilities character */
X			ExpandAName(History.currentLine->contents,
X				sizeof(History.currentLine->contents)-1,
X				History.currentLine->size, 0);
X			continue;
X
X		    case '\n':	/* eol */
X		    case '\r':
X			_doStandardEndLine(nextCharacter);
X			_doStandardCharacter(nextCharacter);
X			break;
X		}
X		break;
X#else
X		switch(nextCharacter)
X		{	/* decide what to do with it, if not caught above */
X                    case '\027':
X			_deletePrevStdWord();
X			continue;
X
X                    case '\022':
X			_doStandardRetype(nextCharacter);
X			continue;
X
X                    case '\030':
X			_doStandardKill(nextCharacter);
X			continue;
X
X		    case '\010':
X			_doStandardErase(nextCharacter);
X                        continue;
X
X		    case '\004': /* other end-of-file character */
X                    case '\032':
X			size = 0;
X			break;
X
X		    default:	/* a character with no special meaning */
X			_doStandardCharacter(nextCharacter);
X			if (History.currentLine->size >= sizeof(History.currentLine->contents)-1)
X				break;
X			continue;
X
X		    case '\033':/* name expansion requested */
X			_doStandardExpand(nextCharacter);
X			continue;
X
X		    case '\014':/* list expansion possibilities character */
X			ExpandAName(History.currentLine->contents,
X				sizeof(History.currentLine->contents)-1,
X				History.currentLine->size, 0);
X			continue;
X
X		    case '\r':
X		    	_writechar('\n');
X			_doStandardEndLine(nextCharacter);
X			_doStandardCharacter(nextCharacter);
X			break;
X		    case '\n':	/* eol */
X			_writechar('\r');
X			_doStandardEndLine(nextCharacter);
X			_doStandardCharacter(nextCharacter);
X			break;
X		}
X		break;
X#endif  GEMDOS
X	}
X
X	/*
X	 * Clean up and return
X	 */
X	_terminate(fp);
X	_savedState.stream = -1;
X	buffer[0] = '\0';
X	size = (size < History.currentLine->size) ? size : History.currentLine->size;
X	if (size >= 0)
X	{	/* copy in the line to be returned */
X		if (size > 0)
X			strncpy(buffer,History.currentLine->contents,size);
X		buffer[size] = '\0';
X		History.currentLine->contents[History.currentLine->size] = '\0';
X	}
X	else	History.currentLine->contents[0] = '\0';
X#ifdef  GEMDOS
X        _writechar('\r');
X#endif  GEMDOS
X	if (_savedState.localEditor != (char *)NULL)
X		free(_savedState.localEditor);
X	_savedState.localEditor = (char *)NULL;
X	return(size);
X};	/* end of localReader */
X
X/*
X * The main "Reader" entry point; get a completely new line
X */
Xint Reader(fp,buffer,size)
Xint fp;
Xchar *buffer;
Xint size;
X{
X	return(localReader((struct historyLine *)NULL,fp,buffer,size));
X};	/* end of Reader */
X
X/*
X * The main "Editor" entry point; edit an old line
X */
Xint Editor(hp,fp,buffer,size)
Xstruct historyLine *hp;
Xint fp;
Xchar *buffer;
Xint size;
X{
X	struct historyLine line;
X	register int rc;
X
X	strncpy(line.contents,hp->contents,hp->size);
X	line.size = hp->size;
X	line.cursor = hp->cursor;
X	if (line.contents[line.size-1] == '\n')
X		line.size--;
X	if (line.contents[line.size-1] == '\r')
X		line.size--;
X	rc = localReader(&line,fp,buffer,size);
X	return(rc);
X};	/* end of Editor */
X
X/*
X *	Is s2 a substring of s1?
X */
Xchar *substr(s1,s2)
Xchar *s1;
Xregister char *s2;
X{
X	register char *s;
X	register int sl;
X
X	sl = strlen(s2);
X	for (s = s1; *s; s++)
X	{
X		if (strncmp(s,s2,sl) == 0)
X			return(s);
X	}
X	return((char *)NULL);
X};	/* end of substr */
END_OF_FILE
if test 17920 -ne `wc -c <'reader/reader.c'`; then
    echo shar: \"'reader/reader.c'\" unpacked with wrong size!
fi
# end of 'reader/reader.c'
fi
echo shar: End of archive 4 \(of 11\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 5 (of 11)."
# Contents:  cmd1.c util.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:03:57 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cmd1.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cmd1.c'\"
else
echo shar: Extracting \"'cmd1.c'\" \(21733 characters\)
sed "s/^X//" >'cmd1.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Built-in Commands (part 1)
X *
X * $Id: cmd1.c,v 1.8 89/02/25 17:39:38 dclemans Exp $
X *
X * $Log:	cmd1.c,v $
X * Revision 1.8  89/02/25  17:39:38  dclemans
X * miscellaneous bug fixes/speedups
X * 
X * Revision 1.7  89/02/22  21:31:37  dclemans
X * Implement simple background job monitoring facility
X * 
X * Revision 1.6  89/02/22  16:27:05  dclemans
X * Implement [[, ]] brackets
X * 
X * Revision 1.5  89/02/20  22:29:08  dclemans
X * Add new builtin commands "trap" and "kill" to command switch table.
X * 
X * Revision 1.4  89/02/20  20:14:15  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X#ifdef  LINED
X#include "reader\history.h"
X#endif  /* LINED */
X
Xint cmd_lastrc = 0;
Xint cmd_forceexit = 0;
Xint cmd_returnexit = 0;
Xint cmd_count = 0;
X
Xextern  int cmd_for();      /* from part 2 */
Xextern  int cmd_select();
Xextern  int cmd_while();
Xextern  int cmd_function();
Xextern  int cmd_if();
Xextern  int cmd_case();
Xextern  int cmd_break();
Xextern  int cmd_continue();
Xextern  int cmd_return();
Xextern  int cmd_fc();
Xextern  int cmd_eval();
Xextern  int cmd_exec();
Xextern  int cmd_time();
X
Xextern  int cmd_test();     /* from part 3 */
Xextern  int cmd_version();
X#ifdef  MYMALLOC
Xextern  int cmd_memory();
X#endif  /* MYMALLOC */
Xextern  int cmd_typeset();
Xextern  int cmd_let();
Xextern  int cmd_dparen();
Xextern  int cmd_whence();
X
Xextern  int cmd_trap();     /* from trap */
X#ifndef GEMDOS
Xextern  int cmd_kill();
Xextern  int cmd_jobs();
X#endif  /* GEMDOS */
X
Xint cmd_null()
X{
X    return 0;
X}   /* end of cmd_null */
X
Xint cmd_print(pp)
Xstruct phrase *pp;
X{
X    int flag,noesc;
X    register struct token *tp;
X    register char *p,*b;
X    char buffer[BUFSIZ];
X
X    flag = 1;
X    noesc = 0;
X    b = buffer;
X    tp = pp->body->next;
X    while (tp != (struct token *)NULL && tp->name[0] == '-')
X    {   /* check for special flags */
X        if (tp->name[1] == '\0')
X        {   /* forced end of args? */
X            tp = tp->next;
X            break;
X        }
X        for (p = &tp->name[1]; *p; p++)
X        {   /* for each possibility */
X            switch (*p)
X            {   /* select what it does */
X                case 'n':
X                    flag = 0;
X                    break;
X                case 'R':
X                case 'r':
X                    noesc = 1;
X                    break;
X                default:
X                    errmsg(0,LOC("cmd_print"),"unknown print arg in: %s",tp->name);
X                    return 1;
X            }
X        }
X        tp = tp->next;
X    }
X    for (; tp != (struct token *)NULL; tp = tp->next)
X    {   /* for each argument, echo its characters */
X        for (p = tp->name; *p; p++)
X        {   /* for each char */
X            switch (*p)
X            {   /* what char? */
X                case '\'':
X                    p++;
X                    while (*p && *p != '\'')
X                    {   /* dump the string */
X                        *b++ = *p;
X                        p++;
X                    }
X                    break;
X                case '"':
X                    p++;
X                    while (*p && *p != '"')
X                    {   /* dump the string */
X                        if (!noesc && *p == ESCAPE_CHAR)
X                        {   /* process the escape char */
X                            p++;
X                            switch (*p)
X                            {   /* what was escaped */
X                                case 'a':
X                                    *b++ = '\007';
X                                    break;
X                                case 'b':
X                                    *b++ = '\b';
X                                    break;
X                                case 'c':
X                                    flag = 0;
X                                    break;
X                                case 'f':
X                                    *b++ = '\f';
X                                    break;
X                                case 'n':
X                                    *b++ = '\n';
X                                    break;
X                                case 'r':
X                                    *b++ = '\r';
X                                    break;
X                                case 't':
X                                    *b++ = '\t';
X                                    break;
X                                default:
X                                    *b++ = *p;
X                                    break;
X                            }
X                        }
X                        else    *b++ = *p;
X                        p++;
X                    }
X                    break;
X                case ESCAPE_CHAR:
X                    if (!noesc)
X                    {   /* if just ordinary chars... */
X                        p++;
X                        switch (*p)
X                        {   /* what was escaped */
X                            case 'a':
X                                *b++ = '\007';
X                                break;
X                            case 'b':
X                                *b++ = '\b';
X                                break;
X                            case 'c':
X                                flag = 0;
X                                break;
X                            case 'f':
X                                *b++ = '\f';
X                                break;
X                            case 'n':
X                                *b++ = '\n';
X                                break;
X                            case 'r':
X                                *b++ = '\r';
X                                break;
X                            case 't':
X                                *b++ = '\t';
X                                break;
X                            default:
X                                *b++ = *p;
X                                break;
X                        }
X                    } else *b++ = *p;
X                    break;
X                default:
X                    *b++ = *p;
X                    break;
X            }
X        }
X        if (tp->next != (struct token *)NULL)
X            *b++ = ' ';
X    }
X    if (flag)
X    {   /* add in an eol */
X        *b++ = '\n';
X    }
X    *b = '\0';
X    io_writestring(0,buffer);
X    return 0;
X}   /* end of cmd_print */
X
Xint cmd_alias(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp,*args;
X    register char *p;
X    int type;
X
X    type = 0;
X    args = pp->body->next;
X    while (args != (struct token *)NULL && args->name[0] == '-')
X    {   /* if there are some arguments */
X        for (p = &args->name[1]; *p; p++)
X        {   /* what kind of alias? */
X            switch (*p)
X            {   /* what switch? */
X                case 't':
X                    type = TYPE_TRACKED;
X                    break;
X                default:
X                    errmsg(0,LOC("cmd_alias"),"bad switch: %s",args->name);
X                    break;
X            }
X        }
X        args = args->next;
X    }
X
X    if (args == (struct token *)NULL)
X        alias_dump(base_env.alias_table,type);
X    else
X    {   /* should be a definition */
X        tp = args;
X        p = strchr(tp->name,'=');
X        if (p == (char *)NULL && tp->next == (struct token *)NULL && type == 0)
X        {   /* print definition of alias */
X            alias_print(tp->name);
X            return 0;
X        }
X        if (p != (char *)NULL && tp->next == (struct token *)NULL)
X        {   /* ksh style alias */
X            *p++ = '\0';
X            alias_sdefine(tp->name,p,type);
X            *--p = '=';
X        }
X        else
X        {   /* csh style alias */
X            alias_define(tp->name,tp->next,type);
X        }
X    }
X    return 0;
X}   /* end of cmd_alias */
X
Xint cmd_unalias(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X
X    for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X        alias_define(tp->name,(struct token *)NULL,0);
X    return 0;
X}   /* end of cmd_unalias */
X
Xstatic struct token *proc_opts(tp,value)
Xregister struct token *tp;
Xint value;
X{
X    char buffer[BUFSIZ];
X
X    buffer[0] = '\0';
X    if (tp->next == (struct token *)NULL)
X    {   /* just print current options state */
X        if (flag_allexport)
X            strcat(buffer,"allexport ");
X        if (flag_noglob)
X            strcat(buffer,"noglob ");
X        if (flag_cmdhash)
X            strcat(buffer,"trackall ");
X        if (flag_interactive)
X            strcat(buffer,"interactive ");
X        if (flag_keywords)
X            strcat(buffer,"keyword ");
X        if (flag_monitor)
X            strcat(buffer,"monitor ");
X        if (flag_noexec)
X            strcat(buffer,"noexec ");
X        if (flag_varerr)
X            strcat(buffer,"nounset ");
X        if (flag_echoinput)
X            strcat(buffer,"verbose ");
X        if (flag_echoexec)
X            strcat(buffer,"xtrace ");
X#ifdef  LINED
X        if (_savedState.isEmacs)
X            strcat(buffer,"emacs ");
X        if (_savedState.isVi)
X            strcat(buffer,"vi ");
X#endif  /* LINED */
X        strcat(buffer,"\n");
X        io_writestring(0,buffer);
X        return tp;
X    }
X    if (strcmp(tp->next->name,"allexport") == 0)
X    {   /* export all? */
X        flag_allexport = value;
X        return tp->next;
X    }
X    if (strcmp(tp->next->name,"keyword") == 0)
X    {   /* flag keywords? */
X        flag_keywords = value;
X        return tp->next;
X    }
X    if (strcmp(tp->next->name,"monitor") == 0)
X    {   /* monitor background jobs */
X        flag_monitor = value;
X        return tp->next;
X    }
X    if (strcmp(tp->next->name,"noexec") == 0)
X    {   /* no execution? */
X        flag_noexec = value;
X        return tp->next;
X    }
X    if (strcmp(tp->next->name,"noglob") == 0)
X    {   /* file expansion? */
X        flag_noglob = value;
X        return tp->next;
X    }
X    if (strcmp(tp->next->name,"nounset") == 0)
X    {   /* unknown variables? */
X        flag_varerr = value;
X        return tp->next;
X    }
X    if (strcmp(tp->next->name,"trackall") == 0)
X    {   /* command hashing? */
X        flag_cmdhash = value;
X        return tp->next;
X    }
X    if (strcmp(tp->next->name,"verbose") == 0)
X    {   /* input echoing */
X        flag_echoinput = value;
X        return tp->next;
X    }
X    if (strcmp(tp->next->name,"xtrace") == 0)
X    {   /* exec echoing */
X        flag_echoexec = value;
X        return tp->next;
X    }
X#ifdef  LINED
X    if (strcmp(tp->next->name,"emacs") == 0)
X    {   /* emacs mode? */
X        _savedState.isEmacs = -1;
X        _savedState.isVi = 0;
X        return tp->next;
X    }
X    if (strcmp(tp->next->name,"vi") == 0)
X    {   /* vi mode? */
X        _savedState.isEmacs = 0;
X        _savedState.isVi = -1;
X    }
X#endif  /* LINED */
X    errmsg(0,LOC("proc_opts"),"unknown 'set -o' option: %s",tp->next->name);
X    return tp;
X}   /* end of proc_opts */
X
Xint cmd_set(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X    register char *p;
X    int rc,value;
X
X    rc = 0;
X    if (pp->body == pp->body_end)
X    {   /* print out defined variables */
X        var_dump(0,1);
X    }
X    else
X    {   /* set flags and/or positional args */
X        for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X        {   /* look for flags */
X            if (tp->name[0] != '-' && tp->name[0] != '+')
X                break;
X            else if (strcmp(tp->name,"-") == 0)
X            {   /* forced end of flags */
X                flag_echoinput = 0;
X                flag_echoexec = 0;
X                tp = tp->next;
X                break;
X            }
X            else if (strcmp(tp->name,"--") == 0)
X            {   /* forced end of flags */
X                tp = tp->next;
X                break;
X            }
X            if (tp->name[0] == '-')
X                value = 1;
X            else value = 0;
X            for (p = &tp->name[1]; *p; p++)
X            {   /* set switches as appropriate */
X                switch (*p)
X                {   /* what switch? */
X                    case 'o':   /* options? */
X                        tp = proc_opts(tp,value);
X                        break;
X                    case 'a':   /* export all? */
X                        flag_allexport = value;
X                        break;
X                    case 'f':   /* no file expansion? */
X                        flag_noglob = value;
X                        break;
X                    case 'm':   /* monitor jobs? */
X                        flag_monitor = value;
X                        break;
X                    case 'n':   /* no execution? */
X                        flag_noexec = value;
X                        break;
X                    case 'u':   /* flag var errors */
X                        flag_varerr = value;
X                        break;
X                    case 'v':   /* echo input? */
X                        flag_echoinput = value;
X                        break;
X                    case 'x':   /* echo execution? */
X                        flag_echoexec = value;
X                        break;
X                    case 'k':   /* all keywords */
X                        flag_keywords = value;
X                        break;
X                    case 'h':   /* command hashing? */
X                        flag_cmdhash = value;
X                        break;
X                    case 'i':   /* interactive? */
X                        flag_interactive = value;
X                        break;
X                    default:
X                        errmsg(0,LOC("cmd_set"),"bad switch: %s",tp->name);
X                        rc = 1;
X                        break;
X                }
X            }
X            if (tp == (struct token *)NULL)
X                break;
X        }
X        if (tp != (struct token *)NULL)
X        {   /* any positional args to reset? */
X            var_resetargs(tp);
X        }
X    }
X    return rc;
X}   /* end of cmd_set */
X
Xint cmd_unset(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X
X    for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X        var_settype(tp->name,TYPE_DELETED,~TYPE_DELETED);
X    return 0;
X}   /* end of cmd_unset */
X
Xint cmd_export(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X
X    if (pp->body->next == (struct token *)NULL)
X    {   /* just list currently exported things? */
X        var_dump(TYPE_EXPORTED,1);
X    }
X    else
X    {   /* actually export some things */
X        for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X            var_settype(tp->name,TYPE_EXPORTED,~TYPE_EXPORTED);
X    }
X    return 0;
X}   /* end of cmd_export */
X
Xint cmd_unexport(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X
X    for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X        var_settype(tp->name,0,~TYPE_EXPORTED);
X    return 0;
X}   /* end of cmd_unexport */
X
Xint cmd_readonly(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X
X    if (pp->body->next == (struct token *)NULL)
X    {   /* just list currently readonly things? */
X        var_dump(TYPE_READONLY,1);
X    }
X    else
X    {   /* actually make some things readonly */
X        for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X            var_settype(tp->name,TYPE_READONLY,~TYPE_READONLY);
X    }
X    return 0;
X}   /* end of cmd_readonly */
X
Xint cmd_shift(pp)
Xstruct phrase *pp;
X{
X    if (pp->body->next == (struct token *)NULL)
X        var_shiftargs(1);
X    else if (pp->body->next->type == SYM_NUMBER)
X        var_shiftargs(atoi(pp->body->next->name));
X    else    errmsg(0,LOC("cmd_shift"),"bad argument: %s",pp->body->next->name);
X    return 0;
X}   /* end of cmd_shift */
X
Xint cmd_exit(pp)
Xstruct phrase *pp;
X{
X    cmd_forceexit = 1;
X    if (pp->body->next == (struct token *)NULL)
X        return cmd_lastrc;
X    else    return atoi(pp->body->next->name);
X}   /* end of cmd_exit */
X
Xint cmd_source(pp)
Xstruct phrase *pp;
X{
X    if (pp->body->next == (struct token *)NULL)
X    {   /* any file to source? */
X        errmsg(0,LOC("cmd_source"),"no file given");
X        return 1;
X    }
X    if (io_pushfile(pp->body->next->name,0,0,0))
X        return 1;
X    return 0;
X}   /* end of cmd_source */
X
Xint cmd_chdir(pp)
Xstruct phrase *pp;
X{
X    struct token *tp;
X    char *dir,*newdir;
X    int rc;
X
X    tp = pp->body->next;
X    if (tp == (struct token *)NULL)
X    {   /* goto home directory */
X        dir = base_env.homedir;
X    }
X    else
X    {   /* goto specified directory */
X        dir = new_string(strlen(tp->name)+1);
X        if (dir == (char *)NULL)
X        {   /* enough memory */
X            errmsg(SHERR_NOMEM,LOC("cmd_chdir"));
X            return 1;
X        }
X        strcpy(dir,tp->name);
X        stripquotes(dir);
X        while (dir[0] == '\'' || dir[0] == '"')
X            stripquotes(dir);
X        if (strlen(dir) == 0)
X        {   /* if nothing left */
X            free(dir);
X            dir = base_env.homedir;
X            tp = (struct token *)NULL;
X        }
X    }
X    newdir = (char *)NULL;
X    rc = lchdir(dir,&newdir);
X    if (rc != 0)
X    {   /* if we didn't get there */
X        errmsg(0,LOC("cmd_chdir"),"couldn't change to %s",dir);
X        if (tp != (struct token *)NULL)
X            free(dir);
X        return rc;
X    }
X    if (tp != (struct token *)NULL)
X        free(dir);
X    var_define0("OLDPWD",base_env.dir->current,0);
X    var_define0("PWD",newdir,0);
X    free(newdir);
X    return 0;
X}   /* end of cmd_chdir */
X
Xint cmd_read(pp)
Xstruct phrase *pp;
X{
X    char buffer[BUFSIZ+1];
X    register char *first,*last;
X    register struct token *tp;
X    int rc,ctr;
X
X    tp = pp->body->next;
X    buffer[0] = '\0';
X    for (ctr = 0; ; ctr++)
X    {   /* get a line */
X        rc = read(base_env.io->input,&buffer[ctr],1);
X        if (rc <= 0)
X            return 1;
X        if (buffer[ctr] == '\n')
X            break;
X    }
X    buffer[++ctr] = '\0';
X    for (first = buffer; *first; first++)
X        if (*first == '\n')
X            *first = '\0';
X    for (last = buffer; *last; )
X    {   /* break up into words, assign to variables */
X        while (*last && strchr(base_env.separators,*last) != (char *)NULL)
X            last++;
X        first = last;
X        if (tp == (struct token *)NULL)
X        {   /* just assign everything to REPLY? */
X            var_define0("REPLY",first,0);
X            break;
X        }
X        else if (tp->next == (struct token *)NULL)
X        {   /* just assign everything to one variable */
X            var_define0(tp->name,first,0);
X            break;
X        }
X        /* else break up into words, keep going */
X        while (*last && strchr(base_env.separators,*last) == (char *)NULL)
X            last++;
X        *last++ = '\0';
X        var_define0(tp->name,first,0);
X        tp = tp->next;
X    }
X    return 0;
X}   /* end of cmd_read */
X
Xint cmd_pwd()
X{
X    char buffer[BUFSIZ];
X
X    sprintf(buffer,"%s\n",base_env.dir->current);
X    io_writestring(0,buffer);
X    return 0;
X}   /* end of cmd_pwd */
X
Xint cmd_sleep(pp)
Xstruct phrase *pp;
X{
X#ifdef  GEMDOS
X    long tstart,tend;
X#endif  /* GEMDOS */
X    int tdiff;
X
X    if (pp->body->next == (struct token *)NULL)
X        return 0;
X    if (pp->body->next->type != SYM_NUMBER)
X    {   /* an amount of time to sleep? */
X        errmsg(0,LOC("cmd_sleep"),"bad argument to sleep: %s",pp->body->next->name);
X        return 1;
X    }
X    tdiff = atoi(pp->body->next->name);
X    if (tdiff < 0)
X        return 0;
X#ifdef  GEMDOS
X    tstart = time(0L);
X    do
X    {   /* while the waiting period is in progress */
X        tend = time(0L);
X    } while ((tstart + (long)tdiff) > tend);
X#else
X    sleep(tdiff);
X#endif  /* GEMDOS */
X    return 0;
X}   /* end of cmd_sleep */
X
Xstruct commands cmd_builtin[] =
X{   /* keep this list sorted... */
X    { "((",         cmd_dparen },
X    { ".",          cmd_source },
X    { ":",          cmd_null },
X    { "[",          cmd_test },
X    { "[[",         cmd_test },
X    { "alias",      cmd_alias },
X    { "break",      cmd_break },
X    { "case",       cmd_case },
X    { "cd",         cmd_chdir },
X    { "chdir",      cmd_chdir },
X    { "continue",   cmd_continue },
X    { "do",         cmd_null },
X    { "done",       cmd_null },
X    { "elif",       cmd_null },
X    { "else",       cmd_null },
X    { "esac",       cmd_null },
X    { "eval",       cmd_eval },
X    { "exec",       cmd_exec },
X    { "exit",       cmd_exit },
X    { "export",     cmd_export },
X    { "fc",         cmd_fc },
X    { "fi",         cmd_null },
X    { "for",        cmd_for },
X    { "function",   cmd_function },
X    { "if",         cmd_if },
X#ifndef GEMDOS
X    { "jobs",       cmd_jobs },
X    { "kill",       cmd_kill },
X#endif  /* GEMDOS */
X    { "let",        cmd_let },
X#ifdef  MYMALLOC
X    { "memory",     cmd_memory },
X#endif  /* MYMALLOC */
X    { "print",      cmd_print },
X    { "pwd",        cmd_pwd },
X    { "read",       cmd_read },
X    { "readonly",   cmd_readonly },
X    { "return",     cmd_return },
X    { "select",     cmd_select },
X    { "set",        cmd_set },
X    { "shift",      cmd_shift },
X    { "sleep",      cmd_sleep },
X    { "test",       cmd_test },
X    { "then",       cmd_null },
X    { "time",       cmd_time },
X    { "trap",       cmd_trap },
X    { "typeset",    cmd_typeset },
X    { "unalias",    cmd_unalias },
X    { "unexport",   cmd_unexport },
X    { "unset",      cmd_unset },
X    { "until",      cmd_while },
X    { "version",    cmd_version },
X    { "whence",     cmd_whence },
X    { "while",      cmd_while },
X    { "{",          cmd_null },
X    { "}",          cmd_null },
X    { (char *)NULL }
X};
X
Xint builtin_word(word)
Xchar *word;
X{
X    register int i;
X
X    for (i = 0; cmd_builtin[i].name != (char *)NULL; i++)
X    {   /* is this a builtin command? */
X        if (strcmp(cmd_builtin[i].name,word) == 0)
X            return 1;
X    }
X    return 0;
X}   /* end of builtin_word */
END_OF_FILE
if test 21733 -ne `wc -c <'cmd1.c'`; then
    echo shar: \"'cmd1.c'\" unpacked with wrong size!
fi
# end of 'cmd1.c'
fi
if test -f 'util.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'util.c'\"
else
echo shar: Extracting \"'util.c'\" \(20385 characters\)
sed "s/^X//" >'util.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Utility Routines
X *
X * $Id: util.c,v 1.6 89/02/25 17:40:21 dclemans Exp $
X *
X * $Log:	util.c,v $
X * Revision 1.6  89/02/25  17:40:21  dclemans
X * miscellaneous bug fixes/speedups
X * 
X * Revision 1.5  89/02/20  20:07:09  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X
X#ifdef  GEMDOS
X#include <types.h>
X#include <stat.h>
X#else
X#ifndef USG
X#include <sys/param.h>
X#endif  /* USG */
X#include <sys/types.h>
X#include <sys/stat.h>
X#ifndef USG
X#include <sys/file.h>
X#else
X#include <fcntl.h>
X#define	F_OK		0	/* does file exist */
X#define	X_OK		1	/* is it executable by caller */
X#define	W_OK		2	/* writable by caller */
X#define	R_OK		4	/* readable by caller */
X#include <dirent.h>
X#define MAXPATHLEN  DIRBUF
X#endif  /* USG */
Xextern char *getwd();
X#endif  /* GEMDOS */
X
Xstruct to_del
X{
X    struct  to_del *next;
X    char    file[1];            /* over-indexed; must be at end */
X};
Xstatic struct to_del *deletes = (struct to_del *)NULL;
X
Xvoid delete_later(file)
Xchar *file;
X{
X    register struct to_del *td;
X
X    td = (struct to_del *)malloc(sizeof(*td)+strlen(file));
X    if (td == (struct to_del *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("delete_later"));
X        errmsg(0,LOC("delete_later"),"can't delete %s",file);
X        return;
X    }
X    strcpy(td->file,file);
X    td->next = deletes;
X    deletes = td;
X}   /* end of delete_later */
X
Xvoid do_deletes()
X{
X    register struct to_del *td,*otd;
X
X    for (td = deletes; td != (struct to_del *)NULL; )
X    {   /* delete files in list; free list */
X        otd = td;
X        td = td->next;
X        unlink(otd->file);
X        free(otd);
X    }
X    deletes = (struct to_del *)NULL;
X}   /* end of do_deletes */
X
Xchar *strcopy(s)
Xregister char *s;
X{
X    register char *ns;
X
X    if (s == (char *)NULL)
X        return (char *)NULL;
X    ns = new_string(strlen(s)+1);
X    if (ns == (char *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("strcopy"));
X        return (char *)NULL;
X    }
X    strcpy(ns,s);
X    return ns;
X}   /* end of strcopy */
X
Xvoid stripquotes(s)
Xregister char *s;
X{
X    register char *p;
X
X    for (p = s; *p != '\0'; p++)
X    {   /* check for special chars */
X        switch (*p)
X        {   /* a special char? */
X            case '\'':
X                strcpy(p,p+1);
X                while (*p && *p != '\'')
X                    p++;
X                if (*p == '\'')
X                    strcpy(p,p+1);
X                break;
X            case '"':
X                strcpy(p,p+1);
X                while (*p && *p != '"')
X                {   /* scan the string */
X                    if (*p == ESCAPE_CHAR)
X                        strcpy(p,p+1);
X                    p++;
X                }
X                if (*p == '"')
X                    strcpy(p,p+1);
X                break;
X            case ESCAPE_CHAR:
X                strcpy(p,p+1);
X                break;
X            default:
X                break;
X        }
X    }
X}   /* end of stripquotes */
X
Xint isexec(path)
Xregister char *path;
X{
X#ifdef  GEMDOS
X    struct stat statb;
X
X    if (stat(path,&statb) < 0)
X        return 0;
X    else
X    {   /* check the modes */
X        return 1;
X    }
X#else
X    return (access(path,X_OK) == 0);
X#endif  /* GEMDOS */
X}   /* end of isexec */
X
Xint isread(path)
Xregister char *path;
X{
X#ifdef  GEMDOS
X    struct stat statb;
X
X    if (stat(path,&statb) < 0)
X        return 0;
X    else
X    {   /* check the modes */
X        return 1;
X    }
X#else
X    return (access(path,R_OK) == 0);
X#endif  /* GEMDOS */
X}   /* end of isread */
X
Xint isfullpath(path)
Xregister char *path;
X{
X    char drive;
X
X    if (path[0] == '.' && path[1] == DIR_SEPARATOR)
X        return 1;
X    if (path[0] == '.' && path[1] == '.' && path[2] == DIR_SEPARATOR)
X        return 1;
X    if (path[0] == DIR_SEPARATOR)
X        return 1;
X#ifdef	GEMDOS
X    drive = path[0];
X    if (islower(drive))
X        drive = _toupper(drive);
X    if (drive >= 'A' && drive <= 'P' && path[1] == ':')
X        return 1;
X#endif	/* GEMDOS */
X    return 0;
X}   /* end of isfullpath */
X
X#ifdef  GEMDOS
Xchar *xchdir(path)
Xregister char *path;
X{
X    char drive,currdrive;
X    register char *ldir;
X    int mask,rc;
X    char newpath[258];
X
X    drive = path[0];
X    if (islower(drive))
X        drive = _toupper(drive);
X    if (drive >= 'A' && drive <= 'P' && path[1] == ':')
X    {   /* dir includes a drive spec */
X        ldir = &path[2];
X    }
X    else
X    {   /* local to current drive */
X        drive = Dgetdrv() + 'A';
X        ldir = path;
X    }
X    currdrive = Dgetdrv() + 'A';
X    if (drive != currdrive)
X    {   /* need to switch to another drive? */
X        mask = Drvmap();
X        if (!(mask && (1 << (drive-'A'))))
X            return (char *)NULL;
X        Dsetdrv(drive-'A');
X    }
X    rc = Dsetpath(ldir);
X    if (rc != 0)
X        return (char *)NULL;
X    rc = Dgetpath(&newpath[2],drive-'A'+1);
X    if (rc != 0)
X        return (char *)NULL;
X    newpath[0] = drive;
X    newpath[1] = ':';
X    return strcopy(newpath);
X}   /* end of xchdir */
X#else
Xchar *xchdir(path)
Xregister char *path;
X{
X    char buffer[MAXPATHLEN];
X
X    if (chdir(path) < 0)
X        return (char *)NULL;
X#ifndef USG
X    if (getwd(buffer) == (char *)NULL)
X#else
X    if (getcwd(buffer,sizeof buffer) == (char *)NULL)
X#endif  /* USG */
X    {   /* if couldn't find new path */
X        errmsg(0,LOC("xchdir"),buffer);
X        return (char *)NULL;
X    }
X    return strcopy(buffer);
X}   /* end of xchdir */
X#endif  /* GEMDOS */
X
Xint lchdir(dir,path)
Xchar *dir;
Xchar **path;
X{
X    char *ldir;
X    register char *first,*last,*fullpath;
X
X    if (strcmp(dir,"-") == 0)
X    {   /* change to OLDPWD? */
X        ldir = var_normal("$OLDPWD");
X	if (ldir == (char *)NULL)
X        {   /* something to go to */
X            *path = (char *)NULL;
X            return 1;
X        }
X        *path = xchdir(ldir);
X        free(ldir);
X        if (*path == (char *)NULL)
X            return 1;
X        io_writestring(0,*path);
X        io_writestring(0,"\n");
X        return 0;
X    }
X    if (base_env.cd_path == (char *)NULL || isfullpath(dir))
X    {   /* if no path supplied */
X        *path = xchdir(dir);
X        if (*path == (char *)NULL)
X            return 1;
X#ifdef  GEMDOS
X        for (ldir = *path; *ldir; ldir++)
X            if (isupper(*ldir))
X                *ldir = _tolower(*ldir);
X#endif  /* GEMDOS */
X        return 0;
X    }
X    for (first = last = base_env.cd_path; *last; last++)
X    {   /* for each element of path */
X        while (*last && *last != ',')
X            last++;
X        fullpath = new_string(strlen(dir)+(int)(last-first)+2);
X        if (fullpath == (char *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("lchdir"));
X            return 1;
X        }
X        if ((int)(last-first) > 0)
X        {   /* if a prefix present */
X            strncpy(fullpath,first,(int)(last-first));
X            if (fullpath[(int)(last-first)-1] != DIR_SEPARATOR)
X                fullpath[(int)(last-first)] = DIR_SEPARATOR;
X            fullpath[(int)(last-first)+1] = '\0';
X        }
X        else    fullpath[0] = '\0';
X        strcat(fullpath,dir);
X        *path = xchdir(fullpath);
X        free(fullpath);
X        if (*path != (char *)NULL)
X        {   /* if directory finally found */
X#ifdef  GEMDOS
X            for (ldir = *path; *ldir; ldir++)
X                if (isupper(*ldir))
X                    *ldir = _tolower(*ldir);
X#endif  /* GEMDOS */
X            io_writestring(0,*path);
X            io_writestring(0,"\n");
X            return 0;
X        }
X        first = last+1;
X    }
X    return 1;
X}   /* end of lchdir */
X
Xint errmsg(code,file,routine,line,fmt,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9)
Xint code;
Xchar *file;
Xchar *routine;
Xint line;
Xchar *fmt;
X{
X    extern int errno;
X    char buffer[BUFSIZ];
X    register char *p;
X
X    if (var_arg0 == (char *)NULL)
X        var_arg0 = "";
X    sprintf(buffer,"%s: %s(%s,%d): ",var_arg0,file,routine,line);
X    for (p = buffer; *p; p++)
X        /* do nothing */;
X    switch (code)
X    {   /* check for common err messages; else do normal one */
X        case SHERR_NOMEM:
X            strcpy(p,"out of memory");
X            break;
X        case SHERR_PERROR:
X            /* perror(a0,a1); */
X            /* fall through... */
X        default:
X            sprintf(p,fmt,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9);
X            break;
X    }
X    io_writestring(1,buffer);
X    io_writestring(1,"\n");
X    return 0;
X}   /* end of errmsg */
X
Xint copy_file(old,new)
Xchar *old;
Xchar *new;
X{
X    register int ifd,ofd,rc;
X    char buffer[BUFSIZ];
X
X    ifd = open(old,0);
X    if (ifd < 0)
X        return -1;
X    ofd = creat(new,0666);
X    if (ofd < 0)
X    {   /* did the file get created? */
X        close(ifd);
X        return -1;
X    }
X    while ((rc = read(ifd,buffer,sizeof buffer)) > 0)
X        write(ofd,buffer,rc);
X    close(ifd);
X    close(ofd);
X    return 0;
X}   /* end of copy_file */
X
Xstatic struct token *copy_tokens(ftp,strip)
Xstruct token *ftp;
Xint strip;
X{
X    register struct token *stp,*etp,*tp;
X
X    stp = etp = (struct token *)NULL;
X    while (ftp != (struct token *)NULL)
X    {   /* copy the list */
X        tp = new_token(strlen(ftp->name));
X        if (tp == (struct token *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("copy_tokens"));
X            tokens_free(stp);
X            return (struct token *)NULL;
X        }
X        strcpy(tp->name,ftp->name);
X        if (strip)
X            stripquotes(tp->name);
X        tp->type = ftp->type;
X        if (stp == (struct token *)NULL)
X            stp = etp = tp;
X        else
X        {   /* tack onto end */
X            etp->next = tp;
X            etp = tp;
X        }
X        ftp = ftp->next;
X    }
X    return stp;
X}   /* end of copy_tokens */
X
Xstruct phrase *copy_phrase(fpp,expand,strip,copybody)
Xstruct phrase *fpp;
Xint expand;
Xint strip;
Xint copybody;
X{
X    register struct phrase *npp;
X    struct phrase *pp,*newpp;
X    register struct token *newtp;
X    struct iotoken *io,*newio;
X    char *ptr;
X    int len;
X
X    npp = new_phrase();
X    if (npp == (struct phrase *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("copy_phrase"));
X        return (struct phrase *)NULL;
X    }
X
X    if (fpp->type != (struct token *)NULL)
X        len = strlen(fpp->type->name);
X    else len = 0;
X    newtp = new_token(len);
X    if (newtp == (struct token *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("copy_phrase"));
X        phrase_free(npp);
X        return (struct phrase *)NULL;
X    }
X    if (fpp->type != (struct token *)NULL)
X    {   /* if a type token to copy */
X        newtp->type = fpp->type->type;
X        strcpy(newtp->name,fpp->type->name);
X        if (strip)
X            stripquotes(newtp->name);
X    }
X    else
X    {   /* stick in a dummy type */
X        newtp->type = SYM_SEMI;
X        newtp->name[0] = '\0';
X    }
X    npp->type = newtp;
X
X    if (expand)
X        npp->var = lex_reparse_tokens(fpp->var,strip);
X    else npp->var = copy_tokens(fpp->var,strip);
X    for (newtp = npp->var; newtp != (struct token *)NULL; newtp = newtp->next)
X        if (newtp->next == (struct token *)NULL)
X            break;
X    if (newtp != (struct token *)NULL)
X        npp->var_end = newtp;
X
X    if (expand)
X        npp->body = lex_reparse_tokens(fpp->body,strip);
X    else npp->body = copy_tokens(fpp->body,strip);
X    for (newtp = npp->body; newtp != (struct token *)NULL; newtp = newtp->next)
X        if (newtp->next == (struct token *)NULL)
X            break;
X    if (newtp != (struct token *)NULL)
X        npp->body_end = newtp;
X
X    for (io = fpp->io; io != (struct iotoken *)NULL; io = io->next)
X    {   /* for each iotoken in phrase */
X        newtp = (struct token *)NULL;
X        if (expand && io_pushtoken(io->file,0) == 0)
X        {   /* set up for var expansion */
X            newtp = lex_token(1);
X            ptr = newtp->name;
X        }
X        else    ptr = io->file;
X        newio = new_iotoken(strlen(ptr));
X        if (newio == (struct iotoken *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("copy_phrase"));
X            phrase_free(npp);
X            if (newtp != (struct token *)NULL)
X                free(newtp);
X            return (struct phrase *)NULL;
X        }
X        if (io->tempfile != (char *)NULL)
X        {   /* if temp file here, need to copy it */
X            len = 16;
X#ifdef  GEMDOS
X            len += strlen(base_env.tmpdir);
X#endif  /* GEMDOS */
X            newio->tempfile = new_string(len+1);
X            if (newio->tempfile == (char *)NULL)
X                errmsg(SHERR_NOMEM,LOC("copy_phrase"));
X            else
X            {   /* copy the file */
X#ifdef  GEMDOS
X                sprintf(newio->tempfile,"%stemp%d.tmp",base_env.tmpdir,
X                    base_env.temp_count++);
X#else
X                sprintf(newio->tempfile,"/tmp/sh%d.temp",base_env.temp_count++);
X#endif  /* GEMDOS */
X                len = copy_file(io->tempfile,newio->tempfile);
X                if (len < 0)
X                {   /* if copy didn't take */
X                    errmsg(0,LOC("copy_phrase"),"error copying %s to %s",
X                        io->tempfile,newio->tempfile);
X                    free(newio->tempfile);
X                    newio->tempfile = (char *)NULL;
X                }
X            }
X        }
X        newio->fd = io->fd;
X        newio->type = io->type;
X        strcpy(newio->file,ptr);
X        if (newtp != (struct token *)NULL)
X            free(newtp);
X        if (npp->io == (struct iotoken *)NULL)
X            npp->io = npp->io_end = newio;
X        else
X        {   /* tack onto end */
X            npp->io_end->next = newio;
X            npp->io_end = newio;
X        }
X    }
X
X    for (pp = fpp->group; copybody && pp != (struct phrase *)NULL; pp = pp->next)
X    {   /* for each phrase in sentence */
X        newpp = copy_phrase(pp,0,0,copybody);
X        if (newpp == (struct phrase *)NULL)
X        {   /* enough memory? */
X            /* message already printed */
X            phrase_free(npp);
X            return (struct phrase *)NULL;
X        }
X        if (npp->group == (struct phrase *)NULL)
X            npp->group = npp->group_end = newpp;
X        else
X        {   /* tack onto end */
X            npp->group_end->next = newpp;
X            npp->group_end = newpp;
X        }
X    }
X
X    return npp;
X}   /* end of copy_phrase */
X
Xstruct phrase *copy_group(fpp,expand,strip,copybody)
Xstruct phrase *fpp;
Xint expand;
Xint strip;
Xint copybody;
X{
X    register struct phrase *ppf,*ppl,*cpp;
X
X    ppf = ppl = (struct phrase *)NULL;
X    while (fpp != (struct phrase *)NULL)
X    {   /* copy list of phrases */
X        cpp = copy_phrase(fpp,expand,strip,copybody);
X        if (cpp == (struct phrase *)NULL)
X        {   /* enough memory? */
X            /* message already printed */
X            if (ppf != (struct phrase *)NULL)
X                sentence_free(ppf);
X            return (struct phrase *)NULL;
X        }
X        if (ppf == (struct phrase *)NULL)
X            ppf = ppl = cpp;
X        else
X        {   /* tack onto end */
X            ppl->next = cpp;
X            ppl = cpp;
X        }
X        fpp = fpp->next;
X    }
X    return ppf;
X}   /* end of copy_group */
X
Xstruct phrase *new_phrase()
X{
X    register struct phrase *pp;
X
X    pp = (struct phrase *)malloc(sizeof(*pp));
X    if (pp == (struct phrase *)NULL)
X        return (struct phrase *)NULL;
X    pp->type = pp->body = pp->body_end = (struct token *)NULL;
X    pp->var = pp->var_end = (struct token *)NULL;
X    pp->io = pp->io_end = (struct iotoken *)NULL;
X    pp->group = pp->group_end = (struct phrase *)NULL;
X    pp->next = (struct phrase *)NULL;
X
X    return pp;
X}   /* end of new_phrase */
X
Xstruct token *new_token(len)
Xint len;
X{
X    register struct token *tp;
X
X    tp = (struct token *)malloc(sizeof(*tp)+len);
X    if (tp == (struct token *)NULL)
X        return (struct token *)NULL;
X    tp->next = (struct token *)NULL;
X    tp->type = -1;
X    tp->name[0] = '\0';
X
X    return tp;
X}   /* end of new_token */
X
Xstruct iotoken *new_iotoken(len)
Xint len;
X{
X    register struct iotoken *ip;
X
X    ip = (struct iotoken *)malloc(sizeof(*ip)+len);
X    if (ip == (struct iotoken *)NULL)
X        return (struct iotoken *)NULL;
X    ip->fd = -1;
X    ip->type = -1;
X    ip->tempfile = (char *)NULL;
X    ip->next = (struct iotoken *)NULL;
X    ip->file[0] = '\0';
X
X    return ip;
X}   /* end of new_iotoken */
X
Xstruct variable *new_variable()
X{
X    register struct variable *vp;
X
X    vp = (struct variable *)malloc(sizeof(*vp));
X    if (vp == (struct variable *)NULL)
X        return (struct variable *)NULL;
X    vp->name = vp->value = (char *)NULL;
X    vp->cmd = vp->type = vp->misc = 0;
X    vp->left = vp->right = (struct variable *)NULL;
X
X    return vp;
X}   /* end of new_variable */
X
Xstruct aliases *new_alias()
X{
X    register struct aliases *ap;
X
X    ap = (struct aliases *)malloc(sizeof(*ap));
X    if (ap == (struct aliases *)NULL)
X        return (struct aliases *)NULL;
X    ap->name = (char *)NULL;
X    ap->tp = (struct token *)NULL;
X    ap->type = 0;
X    ap->left = ap->right = (struct aliases *)NULL;
X
X    return ap;
X}   /* end of new_alias */
X
Xstruct function *new_function()
X{
X    register struct function *fp;
X
X    fp = (struct function *)malloc(sizeof(*fp));
X    if (fp == (struct function *)NULL)
X        return (struct function *)NULL;
X    fp->name = (char *)NULL;
X    fp->code = (struct phrase *)NULL;
X    fp->left = fp->right = (struct function *)NULL;
X
X    return fp;
X}   /* end of new_function */
X
Xchar *new_string(len)
Xint len;
X{
X    register char *cp;
X
X    cp = (char *)malloc(len);
X    if (cp == (char *)NULL)
X        return (char *)NULL;
X    *cp = '\0';
X
X    return cp;
X}   /* end of new_string */
X
Xstruct infile *new_infile()
X{
X    register struct infile *ip;
X
X    ip = (struct infile *)malloc(sizeof(*ip));
X    if (ip == (struct infile *)NULL)
X        return (struct infile *)NULL;
X    ip->name = ip->start = ip->end = (char *)NULL;
X#ifdef  GEMDOS
X    ip->fp = -1;
X#else
X    ip->fp = -1;
X#endif  /* GEMDOS */
X    ip->delete = ip->nonl = ip->markeof = 0;
X    ip->next = (struct infile *)NULL;
X
X    return ip;
X}   /* end of new_infile */
X
Xchar **new_argv(count)
Xint count;
X{
X    register char **av;
X    register int i;
X
X    av = (char **)malloc(sizeof(char *) * (count+1));
X    if (av == (char **)NULL)
X        return (char **)NULL;
X    for (i = 0; i <= count; i++)
X        av[i] = (char *)NULL;
X
X    return av;
X}   /* end of new_argv */
X
Xstruct strsave *new_strsave(s)
Xchar *s;
X{
X    register char *news;
X    register struct strsave *sp;
X
X    news = strcopy(s);
X    if (news == (char *)NULL)
X        return (struct strsave *)NULL;
X    sp = (struct strsave *)malloc(sizeof(*sp));
X    if (sp == (struct strsave *)NULL)
X    {   /* enough memory? */
X        free(news);
X        return (struct strsave *)NULL;
X    }
X    sp->next = (struct strsave *)NULL;
X    sp->string = sp->ptr = news;
X
X    return sp;
X}   /* end of new_strsave */
X
Xvoid phrase_free(pp)
Xstruct phrase *pp;
X{
X    register struct phrase *ppp,*opp;
X    register struct iotoken *iop;
X    struct iotoken *oiop;
X
X    if (pp->type != (struct token *)NULL)
X        free(pp->type);
X    for (ppp = pp->group; ppp != (struct phrase *)NULL; )
X    {
X        opp = ppp;
X        ppp = ppp->next;
X        phrase_free(opp);
X    }
X    tokens_free(pp->body);
X    tokens_free(pp->var);
X    for (iop = pp->io; iop != (struct iotoken *)NULL; )
X    {
X        oiop = iop;
X        iop = iop->next;
X        if (oiop->tempfile != (char *)NULL)
X        {   /* a tempfile (probably a heredoc) */
X            unlink(oiop->tempfile);
X            free(oiop->tempfile);
X        }
X        free(oiop);
X    }
X    free(pp);
X}   /* end of phrase_free */
X
Xvoid sentence_free(fsp)
Xstruct phrase *fsp;
X{
X/*
X    struct phrase *pp,*opp;
X*/
X    struct phrase *osp,*sp;
X
X    for (sp = fsp; sp != (struct phrase *)NULL; )
X    {   /* delete whole paragraph */
X/*
X        for (pp = sp->group; pp != (struct phrase *)NULL; )
X        {
X            opp = pp;
X            pp = pp->next;
X            phrase_free(opp);
X        }
X*/
X        osp = sp;
X        sp = sp->next;
X        phrase_free(osp);
X    }
X}   /* end of sentence_free */
X
Xvoid tokens_free(ftp)
Xstruct token *ftp;
X{
X    register struct token *tp,*otp;
X
X    for (tp = ftp; tp != (struct token *)NULL ; )
X    {
X        otp = tp;
X        tp = tp->next;
X        free(otp);
X    }
X}   /* end of tokens_free */
END_OF_FILE
if test 20385 -ne `wc -c <'util.c'`; then
    echo shar: \"'util.c'\" unpacked with wrong size!
fi
# end of 'util.c'
fi
echo shar: End of archive 5 \(of 11\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 6 (of 11)."
# Contents:  cmd3.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:03:57 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cmd3.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cmd3.c'\"
else
echo shar: Extracting \"'cmd3.c'\" \(24304 characters\)
sed "s/^X//" >'cmd3.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Built-in Commands (part 3)
X *
X * $Id: cmd3.c,v 1.8 89/02/25 17:40:00 dclemans Exp $
X *
X * $Log:	cmd3.c,v $
X * Revision 1.8  89/02/25  17:40:00  dclemans
X * miscellaneous bug fixes/speedups
X * 
X * Revision 1.7  89/02/22  16:27:08  dclemans
X * Implement [[, ]] brackets
X * 
X * Revision 1.6  89/02/22  08:16:43  dclemans
X * implement left-justified, right-justified, etc. parameter attributes
X * 
X * Revision 1.5  89/02/20  20:14:22  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X
X#ifdef  GEMDOS
X#include <types.h>
X#include <stat.h>
X#ifdef  MWC
X#include <basepage.h>
X#endif  /* MWC */
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif  /* GEMDOS */
X
Xstatic int test_parse(tp)
Xstruct token *tp;
X{
X    register char *s1,*s2,*arg;
X    int n1,n2;
X    int rc;
X    struct stat statb;
X    long date1;
X
X    if (tp == (struct token *)NULL)
X        return 0;
X    if (strcmp(tp->name,"]") == 0)
X        return 0;
X    if (strcmp(tp->name,"]]") == 0)
X        return 0;
X    if (strcmp(tp->name,"!") == 0)
X        return test_parse(tp->next);
X    if (strcmp(tp->name,"(") == 0)
X        return !test_parse(tp->next);
X    if (strcmp(tp->name,")") == 0)
X        tp = tp->next;
X    rc = 0;
X
X    arg = tp->name;
X    s1 = (char *)NULL;
X    tp = tp->next;
X    if (tp != (struct token *)NULL)
X    {   /* get file */
X        s1 = tp->name;
X        tp = tp->next;
X    }
X    if (arg != (char *)NULL)
X        stripquotes(arg);
X    if (s1 != (char *)NULL)
X        stripquotes(s1);
X    if (strcmp(arg,"-b") == 0)
X    {   /* a block file? */
X#ifdef  GEMDOS
X        if (s1 == (char *)NULL)
X            rc = 1;
X        else
X        {   /* is it a drive name? */
X            if (islower(*s1))
X                *s1 = _toupper(*s1);
X            if (strcmp(&s1[1],":") == 0 && *s1 >= 'A' && *s1 <= 'P')
X                rc = 0;
X            else    rc = 1;
X        }
X#else
X        rc = stat(s1,&statb);
X        if (rc >= 0)
X        {   /* if something to look at */
X            if ((statb.st_mode & S_IFMT) == S_IFBLK)
X                rc = 0;
X            else rc = 1;
X        }
X#endif  /* GEMDOS */
X    }
X    else if (strcmp(arg,"-c") == 0)
X    {   /* a character file? */
X#ifdef  GEMDOS
X        if (s1 == (char *)NULL)
X            rc = 1;
X        else
X        {   /* check for char device names */
X            for (s2 = s1; *s2; s2++)
X                if (islower(*s2))
X                    *s2 = _toupper(*s2);
X            if (strcmp(s1,"CON:") == 0 ||
X                strcmp(s1,"TTY:") == 0 ||
X                strcmp(s1,"AUX:") == 0 ||
X                strcmp(s1,"PRN:") == 0 ||
X                strcmp(s1,"PRT:") == 0)
X                rc = 0;
X            else    rc = 1;
X        }
X#else
X        rc = stat(s1,&statb);
X        if (rc >= 0)
X        {   /* if something to look at */
X            if ((statb.st_mode & S_IFMT) == S_IFCHR)
X                rc = 0;
X            else rc = 1;
X        }
X#endif  /* GEMDOS */
X    }
X    else if (strcmp(arg,"-d") == 0)
X    {   /* is a directory? */
X        if (s1 == (char *)NULL || stat(s1,&statb) < 0)
X            rc = 1;
X        else
X        {   /* check the modes */
X#ifdef  GEMDOS
X            if (statb.st_mode & S_IJDIR)
X                rc = 0;
X            else    rc = 1;
X#else
X        rc = stat(s1,&statb);
X        if (rc >= 0)
X        {   /* if something to look at */
X            if ((statb.st_mode & S_IFMT) == S_IFDIR)
X                rc = 0;
X            else rc = 1;
X        }
X#endif  /* GEMDOS */
X        }
X    }
X    else if (strcmp(arg,"-f") == 0)
X    {   /* is a regular file? */
X        if (s1 == (char *)NULL || stat(s1,&statb) < 0)
X            rc = 1;
X        else
X        {   /* check the modes */
X#ifdef  GEMDOS
X            if (statb.st_mode & S_IJVOL)
X                rc = 1;
X            else    rc = 0;
X#else
X        rc = stat(s1,&statb);
X        if (rc >= 0)
X        {   /* if something to look at */
X            if ((statb.st_mode & S_IFMT) == S_IFREG)
X                rc = 0;
X            else rc = 1;
X        }
X#endif  /* GEMDOS */
X        }
X    }
X    else if (strcmp(arg,"-g") == 0)
X    {   /* setgid set? */
X#ifdef  GEMDOS
X        rc = -1;
X#else
X        rc = stat(s1,&statb);
X        if (rc >= 0)
X        {   /* if something to look at */
X            if (statb.st_mode & S_ISGID)
X                rc = 0;
X            else rc = 1;
X        }
X#endif  /* GEMDOS */
X    }
X    else if (strcmp(arg,"-k") == 0)
X    {   /* sticky set? */
X#ifdef  GEMDOS
X        rc = -1;
X#else
X        rc = stat(s1,&statb);
X        if (rc >= 0)
X        {   /* if something to look at */
X            if (statb.st_mode & S_ISVTX)
X                rc = 0;
X            else rc = 1;
X        }
X#endif  /* GEMDOS */
X    }
X    else if (strcmp(arg,"-l") == 0)
X    {   /* is a link? */
X#ifdef  GEMDOS
X        rc = -1;
X#else
X        rc = stat(s1,&statb);
X        if (rc >= 0)
X        {   /* if something to look at */
X#ifndef USG
X            if ((statb.st_mode & S_IFMT) == S_IFLNK)
X                rc = 0;
X            else rc = 1;
X#else
X            rc = -1;
X#endif  /* USG */
X        }
X#endif  /* GEMDOS */
X    }
X    else if (strcmp(arg,"-n") == 0)
X    {   /* is string length zero? */
X        if (s1 == (char *)NULL || strlen(s1) == 0)
X            rc = 0;
X        else    rc = 1;
X    }
X    else if (strcmp(arg,"-p") == 0)
X    {   /* is a pipe? */
X#ifdef  GEMDOS
X        rc = -1;
X#else
X        rc = -1;
X#endif  /* GEMDOS */
X    }
X    else if (strcmp(arg,"-r") == 0)
X    {   /* is readable? */
X        if (s1 == (char *)NULL)
X            rc = 1;
X        else rc = !isread(s1);
X    }
X    else if (strcmp(arg,"-t") == 0)
X    {   /* is a terminal? */
X#ifdef  GEMDOS
X        rc = -1;
X#else
X        rc = !isatty(base_env.io->input);
X#endif  /* GEMDOS */
X    }
X    else if (strcmp(arg,"-u") == 0)
X    {   /* setuid set? */
X#ifdef  GEMDOS
X        rc = -1;
X#else
X        rc = stat(s1,&statb);
X        if (rc >= 0)
X        {   /* if something to look at */
X            if (statb.st_mode & S_ISUID)
X                rc = 0;
X            else rc = 1;
X        }
X#endif  /* GEMDOS */
X    }
X    else if (strcmp(arg,"-w") == 0)
X    {   /* is writable? */
X        if (s1 == (char *)NULL || stat(s1,&statb) < 0)
X            rc = 1;
X        else
X        {   /* check the modes */
X#ifdef  GEMDOS
X            if (statb.st_mode & S_IJRON)
X                rc = 1;
X            else    rc = 0;
X#else
X            rc = -1;
X#endif  /* GEMDOS */
X        }
X    }
X    else if (strcmp(arg,"-x") == 0)
X    {   /* is executable? */
X        if (s1 == (char *)NULL)
X            rc = 1;
X        else rc = !isexec(s1);
X    }
X
X    s2 = s1;
X    s1 = arg;
X    arg = s2;
X    if (tp != (struct token *)NULL)
X        s2 = tp->name;
X    else    s2 = (char *)NULL;
X    if (s2 != (char *)NULL)
X        stripquotes(s2);
X    if (s1 != (char *)NULL)
X        n1 = atoi(s1);
X    else    n1 = -1;
X    if (s2 != (char *)NULL)
X        n2 = atoi(s2);
X    else    n2 = -1;
X    if (tp != (struct token *)NULL)
X    {   /* if possible dyadic? */
X        if (strcmp(arg,"=") == 0)
X        {   /* equivalent strings? */
X            rc = strcmp(s1,s2);             
X            tp = tp->next;
X        }
X        else if (strcmp(arg,"!=") == 0)
X        {   /* not equivalent strings */
X            rc = !strcmp(s1,s2);
X            tp = tp->next;
X        }
X        else if (strcmp(arg,"-eq") == 0)
X        {   /* equal numbers? */
X            if (n1 == n2)
X                rc = 0;
X            else    rc = 1;
X            tp = tp->next;
X        }
X        else if (strcmp(arg,"-gt") == 0)
X        {   /* greater than? */
X            if (n1 > n2)
X                rc = 0;
X            else    rc = 1;
X            tp = tp->next;
X        }
X        else if (strcmp(arg,"-ge") == 0)
X        {   /* greater than or equal to? */
X            if (n1 >= n2)
X                rc = 0;
X            else    rc = 1;
X            tp = tp->next;
X        }
X        else if (strcmp(arg,"-lt") == 0)
X        {   /* less than? */
X            if (n1 < n2)
X                rc = 0;
X            else    rc = 1;
X            tp = tp->next;
X        }
X        else if (strcmp(arg,"-le") == 0)
X        {   /* less than or equal to? */
X            if (n1 <= n2)
X                rc = 0;
X            else    rc = 1;
X            tp = tp->next;
X        }
X        else if (strcmp(arg,"-ne") == 0)
X        {   /* not equal to? */
X            if (n1 != n2)
X                rc = 0;
X            else    rc = 1;
X            tp = tp->next;
X        }
X        else if (strcmp(arg,"-nt") == 0)
X        {   /* file newer? */
X            if (stat(s1,&statb) < 0)
X                date1 = -1;
X            else    date1 = statb.st_mtime;
X            if (stat(s2,&statb) < 0)
X                rc = 1;
X            else
X            {   /* check dates */
X                if (date1 > statb.st_mtime)
X                    rc = 0;
X                else    rc = 1;
X            }
X            tp = tp->next;
X        }
X        else if (strcmp(arg,"-ot") == 0)
X        {   /* file older? */
X            if (stat(s1,&statb) < 0)
X                date1 = -1;
X            else    date1 = statb.st_mtime;
X            if (stat(s2,&statb) < 0)
X                rc = 1;
X            else
X            {   /* check dates */
X                if (date1 < statb.st_mtime)
X                    rc = 0;
X                else    rc = 1;
X            }
X            tp = tp->next;
X        }
X    }
X
X    if (s1 != (char *)NULL && arg == (char *)NULL && s2 == (char *)NULL)
X    {   /* just a single string? */
X        if (strlen(s1) != 0)
X            rc = 0;
X        else    rc = 1;
X    }
X
X    if (tp != (struct token *)NULL)
X    {   /* check for continued expressions? */
X        if (strcmp(tp->name,"-a") == 0)
X        {   /* anded exprs? */
X            if (rc != 0)
X                return rc;
X            return test_parse(tp->next);
X        }
X        else if (strcmp(tp->name,"-o") == 0)
X        {   /* ored exprs? */
X            if (rc == 0)
X                return rc;
X            return test_parse(tp->next);
X        }
X        else if (strcmp(tp->name,"]") == 0)
X            return rc;
X        else if (strcmp(tp->name,"]]") == 0)
X            return rc;
X    }
X
X    if (tp != (struct token *)NULL)
X        errmsg(0,LOC("test_parse"),"expression syntax error at token %s",tp->name);
X    return rc;
X}   /* end of test_parse */
X
Xint cmd_test(pp)
Xstruct phrase *pp;
X{
X    return test_parse(pp->body->next);
X}   /* end of cmd_test */
X
Xint cmd_version()
X{
X    char buffer[BUFSIZ];
X#ifdef  GEMDOS
X    unsigned version;
X    long oldssp;
X    int *sysbase;
X    int romvers;
X#endif  /* GEMDOS */
X
X    strcpy(buffer,shell_version);
X    strcat(buffer,"\n");
X    io_writestring(0,buffer);
X#ifdef  LINED
X    sprintf(buffer,"    emacs & vi line editing code installed.\n");
X    io_writestring(0,buffer);
X#endif  /* LINED */
X#ifdef  GEMDOS
X    sprintf(buffer,"    compiled for Atari ST systems.\n");
X    io_writestring(0,buffer);
X#endif  /* GEMDOS */
X#ifdef  unix
X#ifndef USG
X    sprintf(buffer,"    compiled for BSD systems.\n");
X#else
X    sprintf(buffer,"    compiled for SYSV systems.\n");
X#endif  /* USG */
X    io_writestring(0,buffer);
X#endif  /* unix */
X
X#ifdef  GEMDOS
X    oldssp = Super(0L);
X    sysbase = *(int **)0x4f2;
X    romvers = *(sysbase+1);
X    Super(oldssp);
X    version = Sversion();
X    sprintf(buffer,"    running on Atari ST; TOS %d.%d; GEM %d.%d\n",(romvers >> 8) & 0xFF,romvers & 0xFF,version & 0xFF,(version >> 8) & 0xFF);
X    io_writestring(0,buffer);
X    sprintf(buffer,"\nAuthor:\n");
X    io_writestring(0,buffer);
X    sprintf(buffer,"    Dave Clemans\n");
X    io_writestring(0,buffer);
X    sprintf(buffer,"    c/o ST Enthusiasts Of Portland\n");
X    io_writestring(0,buffer);
X    sprintf(buffer,"    4470 SW Hall Blvd., Suite 325\n");
X    io_writestring(0,buffer);
X    sprintf(buffer,"    Beaverton, OR 97005\n");
X    io_writestring(0,buffer);
X#endif  /* GEMDOS */
X
X    return 0;
X}   /* end of cmd_version */
X
Xstatic char *kbytes(value)
Xlong value;
X{
X    static char buf[16];
X    register long first;
X    register int last;
X
X    first = value / 1024L;
X    last = ((int)((value % 1024L) * 10)) / 1024;
X    sprintf(buf,"%ld.%dK",first,last);
X    return buf;
X}   /* end of kbytes */
X
X#ifdef  MYMALLOC
Xint cmd_memory()
X{
X    char buffer[BUFSIZ];
X    register char *p;
X    extern long poolSize,mallocTotal,mallocHighWater;
X    long memblock;
X
X#ifdef  GEMDOS
X#ifdef  MWC
X    long totalMemory;
X    extern long _stksize;
X    int percent;
X
X    sprintf(buffer,"%s: text base,length=0x%lx,%s bytes\n",var_arg0,
X        BP->p_tbase,kbytes(BP->p_tlen));
X    totalMemory = BP->p_tlen;
X    io_writestring(0,buffer);
X    sprintf(buffer,"    data base,length=0x%lx,%s bytes\n",
X        BP->p_dbase,kbytes(BP->p_dlen));
X    totalMemory += BP->p_dlen;
X    io_writestring(0,buffer);
X    sprintf(buffer,"    bss base,length=0x%lx,%s bytes\n",
X        BP->p_bbase,kbytes(BP->p_blen));
X    totalMemory += BP->p_blen;
X    io_writestring(0,buffer);
X    p = (char *)_stksize;
X    for (p = p+1; *p == '\0' && p < (char *)(_stksize+DEFSTACK); p++)
X        /* do nothing */;
X    percent = (int)(((DEFSTACK-(long)((long)p-_stksize))*100L)/DEFSTACK);
X    sprintf(buffer,"  Stack: %s bytes; estimated max usage=%d%%\n",kbytes(DEFSTACK),percent);
X    io_writestring(0,buffer);
X    percent = (int)(((DEFSTACK-(long)((long)buffer-_stksize))*100L)/DEFSTACK);
X    sprintf(buffer,"         estimated current usage=%d%%\n",percent);
X    totalMemory += DEFSTACK;
X    io_writestring(0,buffer);
X#else
X    sprintf(buffer,"%s:\n",var_arg0);
X    io_writestring(0,buffer);
X#endif  /* MWC */
X#else
X    sprintf(buffer,"%s:\n",var_arg0);
X    io_writestring(0,buffer);
X#endif  /* GEMDOS */
X
X    sprintf(buffer,"  Dynamic memory: pool=%s bytes, allocated=%ld bytes\n",
X        kbytes(poolSize),mallocTotal);
X    io_writestring(0,buffer);
X    sprintf(buffer,"                  max allocated=%ld bytes\n",mallocHighWater);
X    io_writestring(0,buffer);
X#ifdef  GEMDOS
X#ifdef  MWC
X    totalMemory += poolSize;
X    sprintf(buffer,"  Total memory used: %s bytes\n",kbytes(totalMemory));
X    io_writestring(0,buffer);
X#endif  /* MWC */
X    memblock = Malloc(-1L);
X    sprintf(buffer,"\n  Largest free memory block in system: %s\n",kbytes(memblock));
X    io_writestring(0,buffer);
X#endif  /* GEMDOS */
X
X    return 0;
X}   /* end of cmd_memory */
X#endif  /* MYMALLOC */
X
Xvoid func_dump(fp,flag)
Xregister struct function *fp;
Xregister int flag;
X{
X    if (fp == (struct function *)NULL)
X        return;
X    if (fp->left != (struct function *)NULL)
X        func_dump(fp->left,flag);
X    phrase_dump(fp->code,flag,0);
X    if (fp->right != (struct function *)NULL)
X        func_dump(fp->right,flag);
X}   /* end of func_dump */
X
Xint cmd_typeset(pp)
Xstruct phrase *pp;
X{
X    register char *p;
X    char *cp;
X    int type,mask,misc,functions;
X    register struct token *tp;
X    char buffer[BUFSIZ];
X
X    functions = type = 0;
X    mask = ~0;
X    misc = 0;
X    tp = pp->body->next;
X    if (tp != (struct token *)NULL)
X        stripquotes(tp->name);
X    if (tp != (struct token *)NULL && (tp->name[0] == '-' || tp->name[0] == '+'))
X    {   /* pick up options */
X        for (p = &tp->name[1]; *p; p++)
X        {   /* what options? */
X            switch (*p)
X            {   /* select which one */
X                case 'f':
X                    if (tp->name[0] == '-')
X                        functions = 1;
X                    else functions = 2;
X                    break;
X                case 'r':
X                    if (tp->name[0] == '-')
X                        type |= TYPE_READONLY;
X                    else mask &= ~TYPE_READONLY;
X                    break;
X                case 'x':
X                    if (tp->name[0] == '-')
X                        type |= TYPE_EXPORTED;
X                    else mask &= ~TYPE_EXPORTED;
X                    break;
X                case 'i':
X                    if (tp->name[0] == '-')
X                        type |= TYPE_INTEGER;
X                    else mask &= ~TYPE_INTEGER;
X                    break;
X                case 'u':
X                    if (tp->name[0] == '-')
X                        type |= TYPE_UPPERCASE;
X                    else mask &= ~TYPE_UPPERCASE;
X                    break;
X                case 'l':
X                    if (tp->name[0] == '-')
X                        type |= TYPE_LOWERCASE;
X                    else mask &= ~TYPE_LOWERCASE;
X                    break;
X                case 't':
X                    if (tp->name[0] == '-')
X                        type |= TYPE_TAGGED;
X                    else mask &= ~TYPE_TAGGED;
X                    break;
X                case 'R':
X                    if (tp->name[0] == '-')
X                        type |= TYPE_RIGHTJUST;
X                    else mask &= ~TYPE_RIGHTJUST;
X                    if (p[1] == 'Z')
X                    {   /* add in zero stuff? */
X                        cp = &p[2];
X                        if (tp->name[0] == '-')
X                            type |= TYPE_ZEROS;
X                        else mask &= ~TYPE_ZEROS;
X                    }
X                    else cp = &p[1];
X                    misc = atoi(cp);
X                    p = (char *)NULL;
X                    mask &= ~TYPE_LEFTJUST;
X                    break;
X                case 'L':
X                    if (tp->name[0] == '-')
X                        type |= TYPE_LEFTJUST;
X                    else mask &= ~TYPE_LEFTJUST;
X                    if (p[1] == 'Z')
X                    {   /* add in zero stuff? */
X                        cp = &p[2];
X                        if (tp->name[0] == '-')
X                            type |= TYPE_ZEROS;
X                        else mask &= ~TYPE_ZEROS;
X                    }
X                    else cp = &p[1];
X                    misc = atoi(cp);
X                    p = (char *)NULL;
X                    mask &= ~TYPE_RIGHTJUST;
X                    break;
X                case 'Z':
X                    if (tp->name[0] == '-')
X                        type |= (TYPE_RIGHTJUST|TYPE_ZEROS);
X                    else mask &= ~(TYPE_RIGHTJUST|TYPE_ZEROS);
X                    misc = atoi(&p[1]);
X                    p = (char *)NULL;
X                    mask &= ~TYPE_LEFTJUST;
X                    break;
X                case 'H':
X                    if (tp->name[0] == '-')
X                        type |= TYPE_HOSTMAP;
X                    else mask &= ~TYPE_HOSTMAP;
X                    break;
X                default:
X                    errmsg(0,LOC("cmd_typeset"),"unknown typeset option in: %s",tp->name);
X                    return 1;
X            }
X            if (p == (char *)NULL)
X                break;
X        }
X        tp = tp->next;		/* skip past options */
X    }
X    if (tp == (struct token *)NULL)
X    {   /* just dump out definitions */
X        if (functions)
X            func_dump(base_env.func_table,functions == 1);
X        else
X        {   /* dump all or part of variable table */
X            if (mask == ~0)
X                var_dump(type,1);
X            else var_dump(~mask,0);
X        }
X        return 0;
X    }
X    for (; tp != (struct token *)NULL; tp = tp->next)
X    {   /* for the rest of the args */
X        stripquotes(tp->name);
X        p = strchr(tp->name,'=');
X        if (p != (char *)NULL)
X        {   /* set type and value? */
X            strncpy(buffer,tp->name,(int)(p-tp->name));
X            buffer[(int)(p-tp->name)] = '\0';
X        }
X        else strcpy(buffer,tp->name);
X        if (p != (char *)NULL)
X            var_define0(buffer,&p[1],1);
X        var_setmisc(buffer,misc);
X        var_settype(buffer,type,mask);
X    }
X    return 0;
X}   /* end of cmd_typeset */
X
Xint cmd_let(pp)
Xstruct phrase *pp;
X{
X    int result;
X    register struct token *tp;
X
X    result = 0;
X    for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X    {   /* eval each argument */
X        stripquotes(tp->name);
X        result = parse_c_expression(tp->name,1);
X    }
X    if (result != 0)
X        return 0;
X    else return 1;
X}   /* end of cmd_let */
X
Xint cmd_dparen(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X    int length,result;
X    char *expression;
X
X    length = 0;
X    for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X    {   /* get length of args */
X        stripquotes(tp->name);
X        length += strlen(tp->name);
X        if (tp->next != (struct token *)NULL)
X            length++;
X    }
X    expression = new_string(length+1);
X    if (expression == (char *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("cmd_dparen"));
X        return 1;
X    }
X    expression[0] = '\0';
X    for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X    {   /* get length of args */
X        if (tp->type == SYM_DRPAREN)
X            continue;
X        strcat(expression,tp->name);
X    }
X    result = parse_c_expression(expression,1);
X    free(expression);
X    if (result != 0)
X        return 0;
X    return 1;
X}   /* end of cmd_dparen */
X
Xint cmd_whence(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp,*ftp;
X    int verbose;
X    struct aliases *ap;
X    char *ptr;
X    char buffer[BUFSIZ];
X
X    verbose = 0;
X    tp = pp->body->next;
X    if (tp != (struct token *)NULL && strcmp(tp->name,"-v") == 0)
X    {   /* verbose flag set? */
X        verbose = 1;
X        tp = tp->next;
X    }
X    for (; tp != (struct token *)NULL; tp = tp->next)
X    {   /* for each arg */
X        stripquotes(tp->name);
X        if (verbose)
X        {   /* check reserved words, etc. */
X            buffer[0] = '\0';
X            if (reserved_word(tp->name))
X            {   /* is it this? */
X                sprintf(buffer,"%s is a reserved word",tp->name);
X            }
X            else if ((ap = alias_get(tp->name)) != (struct aliases *)NULL)
X            {   /* is it this? */
X                if (ap->type & TYPE_TRACKED)
X                    sprintf(buffer,"%s is a tracked alias for ",tp->name);
X                else sprintf(buffer,"%s is an alias for ",tp->name);
X                for (ftp = ap->tp; ftp != (struct token *)NULL; ftp = ftp->next)
X                {   /* dump each token */
X                    strcat(buffer,ftp->name);
X                    if (ftp->next != (struct token *)NULL)
X                        strcat(buffer," ");
X                }
X            }
X            else if (func_get(tp->name) != (struct function *)NULL)
X            {   /* is it this? */
X                sprintf(buffer,"%s is a shell function",tp->name);
X            }
X            else if (builtin_word(tp->name))
X            {   /* is it this? */
X                sprintf(buffer,"%s is a shell built-in",tp->name);
X            }
X            if (buffer[0] != '\0')
X            {   /* generated a message? */
X                io_writestring(0,buffer);
X                io_writestring(0,"\n");
X                continue;
X            }
X        }
X        if ((ap = alias_get(tp->name)) != (struct aliases *)NULL)
X        {   /* if alias or tracked alias */
X            if (ap->type & TYPE_TRACKED)
X            {   /* give full name of tracked alias */
X                buffer[0] = '\0';
X                for (ftp = ap->tp; ftp != (struct token *)NULL; ftp = ftp->next)
X                {   /* dump each token */
X                    strcat(buffer,ftp->name);
X                    if (ftp->next != (struct token *)NULL)
X                        strcat(buffer," ");
X                }
X                io_writestring(0,buffer);
X            }
X            else io_writestring(0,tp->name);
X        }
X        else if (reserved_word(tp->name) ||
X            func_get(tp->name) != (struct function *)NULL ||
X            builtin_word(tp->name))
X        {   /* is it this? */
X            io_writestring(0,tp->name);
X        }
X        else
X        {   /* normal path search for program */
X            ptr = exec_pathsearch(tp->name);
X            if (ptr == (char *)NULL)
X            {   /* not there */
X                if (!verbose)
X                    sprintf(buffer,"%s",tp->name);
X                else sprintf(buffer,"%s not found",tp->name);
X            }
X            else
X            {   /* actually found the program */
X                if (!verbose)
X                    sprintf(buffer,"%s",ptr);
X		else sprintf(buffer,"%s is %s",tp->name,ptr);
X                free(ptr);
X            }
X            io_writestring(0,buffer);
X        }
X        io_writestring(0,"\n");
X    }
X
X    return 0;
X}   /* end of cmd_whence */
END_OF_FILE
if test 24304 -ne `wc -c <'cmd3.c'`; then
    echo shar: \"'cmd3.c'\" unpacked with wrong size!
fi
# end of 'cmd3.c'
fi
echo shar: End of archive 6 \(of 11\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.  It is NOT yet
bug free; I am VERY interested in fixes; enhancements, etc.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 7 (of 11)."
# Contents:  cexp.tab.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:03:58 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cexp.tab.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cexp.tab.c'\"
else
echo shar: Extracting \"'cexp.tab.c'\" \(35689 characters\)
sed "s/^X//" >'cexp.tab.c' <<'END_OF_FILE'
X
X/*  A Bison parser, made from cexp.y  */
X
X#define	INT	258
X#define	CHAR	259
X#define	NAME	260
X#define	ERROR	261
X#define	OR	262
X#define	AND	263
X#define	EQUAL	264
X#define	NOTEQUAL	265
X#define	LEQ	266
X#define	GEQ	267
X#define	LSH	268
X#define	RSH	269
X#define	UNARY	270
X
X#line 125 "cexp.y"
X
X#include "shell.h"
X#include <setjmp.h>
X/* #define YYDEBUG 1 */
X
X  int yylex ();
X  void yyerror ();
X  int expression_value;
X
X  static jmp_buf parse_return_error;
X
X  /* some external tables of character types */
X  extern unsigned char is_idstart[], is_idchar[];
X
X  static int allow_side_effects = 0;
X
X
X#line 143 "cexp.y"
Xtypedef union {
X  long lval;
X  int voidval;
X  char *sval;
X} YYSTYPE;
X
X#ifndef YYLTYPE
Xtypedef
X  struct yyltype
X    {
X      int timestamp;
X      int first_line;
X      int first_column;
X      int last_line;
X      int last_column;
X      char *text;
X   }
X  yyltype;
X
X#define YYLTYPE yyltype
X#endif
X
X#define	YYACCEPT	return(0)
X#define	YYABORT	return(1)
X#define	YYERROR	goto yyerrlab
X#include <stdio.h>
X
X#ifndef __STDC__
X#define const
X#endif
X
X
X
X#define	YYFINAL		61
X#define	YYFLAG		-32768
X#define	YYNTBASE	34
X
X#define YYTRANSLATE(x) ((unsigned)(x) <= 270 ? yytranslate[x] : 37)
X
Xstatic const char yytranslate[] = {     0,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,    29,     2,     2,     2,    27,    14,     2,    31,
X    32,    25,    23,     9,    24,     2,    26,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     8,     2,    17,
X    33,    18,     7,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,    13,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,    12,     2,    30,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
X     2,     2,     2,     2,     2,     1,     2,     3,     4,     5,
X     6,    10,    11,    15,    16,    19,    20,    21,    22,    28
X};
X
Xstatic const short yyrline[] = {     0,
X   172,   177,   178,   183,   185,   187,   189,   191,   206,   208,
X   215,   222,   224,   226,   228,   230,   232,   234,   236,   238,
X   240,   242,   244,   246,   248,   250,   252,   254,   256,   258
X};
X
Xstatic const char * const yytname[] = {     0,
X"error","$illegal.","INT","CHAR","NAME","ERROR","'?'","':'","','","OR",
X"AND","'|'","'^'","'&'","EQUAL","NOTEQUAL","'<'","'>'","LEQ","GEQ",
X"LSH","RSH","'+'","'-'","'*'","'/'","'%'","UNARY","'!'","'~'",
X"'('","')'","'='","start"
X};
X
Xstatic const short yyr1[] = {     0,
X    34,    35,    35,    36,    36,    36,    36,    36,    36,    36,
X    36,    36,    36,    36,    36,    36,    36,    36,    36,    36,
X    36,    36,    36,    36,    36,    36,    36,    36,    36,    36
X};
X
Xstatic const short yyr2[] = {     0,
X     1,     1,     3,     2,     2,     2,     3,     3,     3,     3,
X     3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
X     3,     3,     3,     3,     3,     3,     5,     1,     1,     1
X};
X
Xstatic const short yydefact[] = {     0,
X    28,    29,    30,     0,     0,     0,     0,     1,     2,     0,
X     4,     5,     6,     0,     0,     0,     0,     0,     0,     0,
X     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
X     0,     0,     0,     0,     8,     7,     3,     0,    26,    25,
X    24,    23,    22,    16,    17,    20,    21,    18,    19,    14,
X    15,    12,    13,     9,    10,    11,     0,    27,     0,     0,
X     0
X};
X
Xstatic const short yydefgoto[] = {    59,
X     8,     9
X};
X
Xstatic const short yypact[] = {    28,
X-32768,-32768,   -23,    28,    28,    28,    28,    26,    76,    28,
X-32768,-32768,-32768,    29,    28,    28,    28,    28,    28,    28,
X    28,    28,    28,    28,    28,    28,    28,    28,    28,    28,
X    28,    28,    28,    28,    26,-32768,    94,    55,   111,   127,
X   142,   156,    24,   167,   167,   -18,   -18,   -18,   -18,   172,
X   172,   175,   175,-32768,-32768,-32768,    28,    76,    36,    54,
X-32768
X};
X
Xstatic const short yypgoto[] = {-32768,
X    27,    -4
X};
X
X
X#define	YYLAST		202
X
X
Xstatic const short yytable[] = {    11,
X    12,    13,    28,    29,    30,    31,    32,    33,    34,    10,
X    37,    38,    39,    40,    41,    42,    43,    44,    45,    46,
X    47,    48,    49,    50,    51,    52,    53,    54,    55,    56,
X     1,     2,     3,    14,    15,    60,    35,    15,    22,    23,
X    24,    25,    26,    27,    28,    29,    30,    31,    32,    33,
X    34,     4,    58,    61,     0,     0,     5,     6,     7,     0,
X    36,    16,    57,     0,    17,    18,    19,    20,    21,    22,
X    23,    24,    25,    26,    27,    28,    29,    30,    31,    32,
X    33,    34,    16,     0,     0,    17,    18,    19,    20,    21,
X    22,    23,    24,    25,    26,    27,    28,    29,    30,    31,
X    32,    33,    34,    17,    18,    19,    20,    21,    22,    23,
X    24,    25,    26,    27,    28,    29,    30,    31,    32,    33,
X    34,    18,    19,    20,    21,    22,    23,    24,    25,    26,
X    27,    28,    29,    30,    31,    32,    33,    34,    19,    20,
X    21,    22,    23,    24,    25,    26,    27,    28,    29,    30,
X    31,    32,    33,    34,    20,    21,    22,    23,    24,    25,
X    26,    27,    28,    29,    30,    31,    32,    33,    34,    21,
X    22,    23,    24,    25,    26,    27,    28,    29,    30,    31,
X    32,    33,    34,    24,    25,    26,    27,    28,    29,    30,
X    31,    32,    33,    34,    30,    31,    32,    33,    34,    32,
X    33,    34
X};
X
Xstatic const short yycheck[] = {     4,
X     5,     6,    21,    22,    23,    24,    25,    26,    27,    33,
X    15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
X    25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
X     3,     4,     5,     7,     9,     0,    10,     9,    15,    16,
X    17,    18,    19,    20,    21,    22,    23,    24,    25,    26,
X    27,    24,    57,     0,    -1,    -1,    29,    30,    31,    -1,
X    32,     7,     8,    -1,    10,    11,    12,    13,    14,    15,
X    16,    17,    18,    19,    20,    21,    22,    23,    24,    25,
X    26,    27,     7,    -1,    -1,    10,    11,    12,    13,    14,
X    15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
X    25,    26,    27,    10,    11,    12,    13,    14,    15,    16,
X    17,    18,    19,    20,    21,    22,    23,    24,    25,    26,
X    27,    11,    12,    13,    14,    15,    16,    17,    18,    19,
X    20,    21,    22,    23,    24,    25,    26,    27,    12,    13,
X    14,    15,    16,    17,    18,    19,    20,    21,    22,    23,
X    24,    25,    26,    27,    13,    14,    15,    16,    17,    18,
X    19,    20,    21,    22,    23,    24,    25,    26,    27,    14,
X    15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
X    25,    26,    27,    17,    18,    19,    20,    21,    22,    23,
X    24,    25,    26,    27,    23,    24,    25,    26,    27,    25,
X    26,    27
X};
X#define YYPURE 1
X
X#line 2 "bison.simple"
X
X/* Skeleton output parser for bison,
X   copyright (C) 1984 Bob Corbett and Richard Stallman
X
X		       NO WARRANTY
X
X  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
XNO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
XWHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
XRICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
XWITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
XBUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
XFITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
XAND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
XDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
XCORRECTION.
X
X IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
XSTALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
XWHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
XLIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
XOTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
XUSE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
XDATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
XA FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
XPROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
XDAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
X
X		GENERAL PUBLIC LICENSE TO COPY
X
X  1. You may copy and distribute verbatim copies of this source file
Xas you receive it, in any medium, provided that you conspicuously and
Xappropriately publish on each copy a valid copyright notice "Copyright
X(C) 1985 Free Software Foundation, Inc."; and include following the
Xcopyright notice a verbatim copy of the above disclaimer of warranty
Xand of this License.  You may charge a distribution fee for the
Xphysical act of transferring a copy.
X
X  2. You may modify your copy or copies of this source file or
Xany portion of it, and copy and distribute such modifications under
Xthe terms of Paragraph 1 above, provided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating
X    that you changed the files and the date of any change; and
X
X    b) cause the whole of any work that you distribute or publish,
X    that in whole or in part contains or is a derivative of this
X    program or any part thereof, to be licensed at no charge to all
X    third parties on terms identical to those contained in this
X    License Agreement (except that you may choose to grant more extensive
X    warranty protection to some or all third parties, at your option).
X
X    c) You may charge a distribution fee for the physical act of
X    transferring a copy, and you may at your option offer warranty
X    protection in exchange for a fee.
X
XMere aggregation of another unrelated program with this program (or its
Xderivative) on a volume of a storage or distribution medium does not bring
Xthe other program under the scope of these terms.
X
X  3. You may copy and distribute this program (or a portion or derivative
Xof it, under Paragraph 2) in object code or executable form under the terms
Xof Paragraphs 1 and 2 above provided that you also do one of the following:
X
X    a) accompany it with the complete corresponding machine-readable
X    source code, which must be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    b) accompany it with a written offer, valid for at least three
X    years, to give any third party free (except for a nominal
X    shipping charge) a complete machine-readable copy of the
X    corresponding source code, to be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    c) accompany it with the information you received as to where the
X    corresponding source code may be obtained.  (This alternative is
X    allowed only for noncommercial distribution and only if you
X    received the program in object code or executable form alone.)
X
XFor an executable file, complete source code means all the source code for
Xall modules it contains; but, as a special exception, it need not include
Xsource code for modules which are standard libraries that accompany the
Xoperating system on which the executable file runs.
X
X  4. You may not copy, sublicense, distribute or transfer this program
Xexcept as expressly provided under this License Agreement.  Any attempt
Xotherwise to copy, sublicense, distribute or transfer this program is void and
Xyour rights to use the program under this License agreement shall be
Xautomatically terminated.  However, parties who have received computer
Xsoftware programs from you with this License Agreement will not have
Xtheir licenses terminated so long as such parties remain in full compliance.
X
X  5. If you wish to incorporate parts of this program into other free
Xprograms whose distribution conditions are different, write to the Free
XSoftware Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet
Xworked out a simple rule that can be stated here, but we will often permit
Xthis.  We will be guided by the two goals of preserving the free status of
Xall derivatives of our free software and of promoting the sharing and reuse of
Xsoftware.
X
X
XIn other words, you are welcome to use, share and improve this program.
XYou are forbidden to forbid anyone else to use, share and improve
Xwhat you give them.   Help stamp out software-hoarding!  */
X
X/* This is the parser code that is written into each bison parser
X  when the %semantic_parser declaration is not specified in the grammar.
X  It was written by Richard Stallman by simplifying the hairy parser
X  used when %semantic_parser is specified.  */
X
X/* Note: there must be only one dollar sign in this file.
X   It is replaced by the list of actions, each action
X   as one case of the switch.  */
X
X#define yyerrok		(yyerrstatus = 0)
X#define yyclearin	(yychar = YYEMPTY)
X#define YYEMPTY		-2
X#define YYEOF		0
X#define YYFAIL		goto yyerrlab;
X
X#define YYTERROR	1
X
X#ifndef YYIMPURE
X#define YYLEX		yylex()
X#endif
X
X#ifndef YYPURE
X#define YYLEX		yylex(&yylval, &yylloc)
X#endif
X
X/* If nonreentrant, generate the variables here */
X
X#ifndef YYIMPURE
X
Xint	yychar;			/*  the lookahead symbol		*/
XYYSTYPE	yylval;			/*  the semantic value of the		*/
X				/*  lookahead symbol			*/
X
XYYLTYPE yylloc;			/*  location data for the lookahead	*/
X				/*  symbol				*/
X
Xint yynerr;			/*  number of parse errors so far       */
X
X#ifdef YYDEBUG
Xint yydebug = 0;		/*  nonzero means print parse trace	*/
X#endif
X
X#endif  /* YYIMPURE */
X
X
X/*  YYMAXDEPTH indicates the initial size of the parser's stacks	*/
X
X#ifndef	YYMAXDEPTH
X#define YYMAXDEPTH 200
X#endif
X
X/*  YYMAXLIMIT is the maximum size the stacks can grow to
X    (effective only if the built-in stack extension method is used).  */
X
X#ifndef YYMAXLIMIT
X#define YYMAXLIMIT 10000
X#endif
X
X
X#line 167 "bison.simple"
Xint
Xyyparse()
X{
X  register int yystate;
X  register int yyn;
X  register short *yyssp;
X  register YYSTYPE *yyvsp;
X  YYLTYPE *yylsp;
X  int yyerrstatus;	/*  number of tokens to shift before error messages enabled */
X  int yychar1;		/*  lookahead token as an internal (translated) token number */
X
X  short	yyssa[YYMAXDEPTH];	/*  the state stack			*/
X  YYSTYPE yyvsa[YYMAXDEPTH];	/*  the semantic value stack		*/
X  YYLTYPE yylsa[YYMAXDEPTH];	/*  the location stack			*/
X
X  short *yyss = yyssa;		/*  refer to the stacks thru separate pointers */
X  YYSTYPE *yyvs = yyvsa;	/*  to allow yyoverflow to reallocate them elsewhere */
X  YYLTYPE *yyls = yylsa;
X
X  int yymaxdepth = YYMAXDEPTH;
X
X#ifndef YYPURE
X  int yychar;
X  YYSTYPE yylval;
X  YYLTYPE yylloc;
X#endif
X
X#ifdef YYDEBUG
X  extern int yydebug;
X#endif
X
X
X  YYSTYPE yyval;		/*  the variable used to return		*/
X				/*  semantic values from the action	*/
X				/*  routines				*/
X
X  int yylen;
X
X#ifdef YYDEBUG
X  if (yydebug)
X    fprintf(stderr, "Starting parse\n");
X#endif
X
X  yystate = 0;
X  yyerrstatus = 0;
X  yynerr = 0;
X  yychar = YYEMPTY;		/* Cause a token to be read.  */
X
X  /* Initialize stack pointers.
X     Waste one element of value and location stack
X     so that they stay on the same level as the state stack.  */
X
X  yyssp = yyss - 1;
X  yyvsp = yyvs;
X  yylsp = yyls;
X
X/* Push a new state, which is found in  yystate  .  */
X/* In all cases, when you get here, the value and location stacks
X   have just been pushed. so pushing a state here evens the stacks.  */
Xyynewstate:
X
X  *++yyssp = yystate;
X
X  if (yyssp >= yyss + yymaxdepth - 1)
X    {
X      /* Give user a chance to reallocate the stack */
X      /* Use copies of these so that the &'s don't force the real ones into memory. */
X      YYSTYPE *yyvs1 = yyvs;
X      YYLTYPE *yyls1 = yyls;
X      short *yyss1 = yyss;
X
X      /* Get the current used size of the three stacks, in elements.  */
X      int size = yyssp - yyss + 1;
X
X#ifdef yyoverflow
X      /* Each stack pointer address is followed by the size of
X	 the data in use in that stack, in bytes.  */
X      yyoverflow("parser stack overflow",
X		 &yyss1, size * sizeof (*yyssp),
X		 &yyvs1, size * sizeof (*yyvsp),
X		 &yyls1, size * sizeof (*yylsp),
X		 &yymaxdepth);
X
X      yyss = yyss1; yyvs = yyvs1; yyls = yyls1;
X#else /* no yyoverflow */
X      /* Extend the stack our own way.  */
X      if (yymaxdepth >= YYMAXLIMIT)
X	yyerror("parser stack overflow");
X      yymaxdepth *= 2;
X      if (yymaxdepth > YYMAXLIMIT)
X	yymaxdepth = YYMAXLIMIT;
X      yyss = (short *) alloca (yymaxdepth * sizeof (*yyssp));
X      bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
X      yyvs = (YYSTYPE *) alloca (yymaxdepth * sizeof (*yyvsp));
X      bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
X#ifdef YYLSP_NEEDED
X      yyls = (YYLTYPE *) alloca (yymaxdepth * sizeof (*yylsp));
X      bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
X#endif
X#endif /* no yyoverflow */
X
X      yyssp = yyss + size - 1;
X      yyvsp = yyvs + size - 1;
X#ifdef YYLSP_NEEDED
X      yylsp = yyls + size - 1;
X#endif
X
X#ifdef YYDEBUG
X      if (yydebug)
X	fprintf(stderr, "Stack size increased to %d\n", yymaxdepth);
X#endif
X
X      if (yyssp >= yyss + yymaxdepth - 1)
X	YYABORT;
X    }
X
X#ifdef YYDEBUG
X  if (yydebug)
X    fprintf(stderr, "Entering state %d\n", yystate);
X#endif
X
X/* Do appropriate processing given the current state.  */
X/* Read a lookahead token if we need one and don't already have one.  */
Xyyresume:
X
X  /* First try to decide what to do without reference to lookahead token.  */
X
X  yyn = yypact[yystate];
X  if (yyn == YYFLAG)
X    goto yydefault;
X
X  /* Not known => get a lookahead token if don't already have one.  */
X
X  /* yychar is either YYEMPTY or YYEOF
X     or a valid token in external form.  */
X
X  if (yychar == YYEMPTY)
X    {
X#ifdef YYDEBUG
X      if (yydebug)
X	fprintf(stderr, "Reading a token: ");
X#endif
X      yychar = YYLEX;
X    }
X
X  /* Convert token to internal form (in yychar1) for indexing tables with */
X
X  if (yychar <= 0)		/* This means end of input. */
X    {
X      yychar1 = 0;
X      yychar = YYEOF;		/* Don't call YYLEX any more */
X
X#ifdef YYDEBUG
X      if (yydebug)
X	fprintf(stderr, "Now at end of input.\n");
X#endif
X    }
X  else
X    {
X      yychar1 = YYTRANSLATE(yychar);
X
X#ifdef YYDEBUG
X      if (yydebug)
X	fprintf(stderr, "Next token is %d (%s)\n", yychar, yytname[yychar1]);
X#endif
X    }
X
X  yyn += yychar1;
X  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
X    goto yydefault;
X
X  yyn = yytable[yyn];
X
X  /* yyn is what to do for this token type in this state.
X     Negative => reduce, -yyn is rule number.
X     Positive => shift, yyn is new state.
X       New state is final state => don't bother to shift,
X       just return success.
X     0, or most negative number => error.  */
X
X  if (yyn < 0)
X    {
X      if (yyn == YYFLAG)
X	goto yyerrlab;
X      yyn = -yyn;
X      goto yyreduce;
X    }
X  else if (yyn == 0)
X    goto yyerrlab;
X
X  if (yyn == YYFINAL)
X    YYACCEPT;
X
X  /* Shift the lookahead token.  */
X
X#ifdef YYDEBUG
X  if (yydebug)
X    fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
X#endif
X
X  /* Discard the token being shifted unless it is eof.  */
X  if (yychar != YYEOF)
X    yychar = YYEMPTY;
X
X  *++yyvsp = yylval;
X#ifdef YYLSP_NEEDED
X  *++yylsp = yylloc;
X#endif
X
X  /* count tokens shifted since error; after three, turn off error status.  */
X  if (yyerrstatus) yyerrstatus--;
X
X  yystate = yyn;
X  goto yynewstate;
X
X/* Do the default action for the current state.  */
Xyydefault:
X
X  yyn = yydefact[yystate];
X  if (yyn == 0)
X    goto yyerrlab;
X
X/* Do a reduction.  yyn is the number of a rule to reduce with.  */
Xyyreduce:
X  yylen = yyr2[yyn];
X  yyval = yyvsp[1-yylen]; /* implement default value of the action */
X
X#ifdef YYDEBUG
X  if (yydebug)
X    {
X      if (yylen == 1)
X	fprintf (stderr, "Reducing 1 value via line %d, ",
X		 yyrline[yyn]);
X      else
X	fprintf (stderr, "Reducing %d values via line %d, ",
X		 yylen, yyrline[yyn]);
X    }
X#endif
X
X
X  switch (yyn) {
X
Xcase 1:
X#line 173 "cexp.y"
X{ expression_value = yyvsp[0].lval; ;
X    break;}
Xcase 3:
X#line 179 "cexp.y"
X{ yyval.lval = yyvsp[0].lval; ;
X    break;}
Xcase 4:
X#line 184 "cexp.y"
X{ yyval.lval = - yyvsp[0].lval; ;
X    break;}
Xcase 5:
X#line 186 "cexp.y"
X{ yyval.lval = ! yyvsp[0].lval; ;
X    break;}
Xcase 6:
X#line 188 "cexp.y"
X{ yyval.lval = ~ yyvsp[0].lval; ;
X    break;}
Xcase 7:
X#line 190 "cexp.y"
X{ yyval.lval = yyvsp[-1].lval; ;
X    break;}
Xcase 8:
X#line 192 "cexp.y"
X{
X              if (yyvsp[-2].sval == (char *)NULL)
X                yyval.lval = 0;
X              else { char buffer[16];
X                sprintf(buffer,"%d",yyvsp[0].lval);
X                if (allow_side_effects)
X                  var_define0(&yyvsp[-2].sval[1],buffer,0);
X                free(yyvsp[-2].sval);
X                yyval.lval = yyvsp[0].lval;
X              }
X            ;
X    break;}
Xcase 9:
X#line 207 "cexp.y"
X{ yyval.lval = yyvsp[-2].lval * yyvsp[0].lval; ;
X    break;}
Xcase 10:
X#line 209 "cexp.y"
X{ if (yyvsp[0].lval == 0)
X			    {
X			      errmsg (0,LOC("yyparse"),"division by zero");
X			      yyvsp[0].lval = 1;
X			    }
X			  yyval.lval = yyvsp[-2].lval / yyvsp[0].lval; ;
X    break;}
Xcase 11:
X#line 216 "cexp.y"
X{ if (yyvsp[0].lval == 0)
X			    {
X			      errmsg (0,LOC("yparse"),"division by zero");
X			      yyvsp[0].lval = 1;
X			    }
X			  yyval.lval = yyvsp[-2].lval % yyvsp[0].lval; ;
X    break;}
Xcase 12:
X#line 223 "cexp.y"
X{ yyval.lval = yyvsp[-2].lval + yyvsp[0].lval; ;
X    break;}
Xcase 13:
X#line 225 "cexp.y"
X{ yyval.lval = yyvsp[-2].lval - yyvsp[0].lval; ;
X    break;}
Xcase 14:
X#line 227 "cexp.y"
X{ yyval.lval = yyvsp[-2].lval << yyvsp[0].lval; ;
X    break;}
Xcase 15:
X#line 229 "cexp.y"
X{ yyval.lval = yyvsp[-2].lval >> yyvsp[0].lval; ;
X    break;}
Xcase 16:
X#line 231 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval == yyvsp[0].lval); ;
X    break;}
Xcase 17:
X#line 233 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval != yyvsp[0].lval); ;
X    break;}
Xcase 18:
X#line 235 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval <= yyvsp[0].lval); ;
X    break;}
Xcase 19:
X#line 237 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval >= yyvsp[0].lval); ;
X    break;}
Xcase 20:
X#line 239 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval < yyvsp[0].lval); ;
X    break;}
Xcase 21:
X#line 241 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval > yyvsp[0].lval); ;
X    break;}
Xcase 22:
X#line 243 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval & yyvsp[0].lval); ;
X    break;}
Xcase 23:
X#line 245 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval ^ yyvsp[0].lval); ;
X    break;}
Xcase 24:
X#line 247 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval | yyvsp[0].lval); ;
X    break;}
Xcase 25:
X#line 249 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval && yyvsp[0].lval); ;
X    break;}
Xcase 26:
X#line 251 "cexp.y"
X{ yyval.lval = (yyvsp[-2].lval || yyvsp[0].lval); ;
X    break;}
Xcase 27:
X#line 253 "cexp.y"
X{ yyval.lval = yyvsp[-4].lval ? yyvsp[-2].lval : yyvsp[0].lval; ;
X    break;}
Xcase 28:
X#line 255 "cexp.y"
X{ yyval.lval = yylval.lval; ;
X    break;}
Xcase 29:
X#line 257 "cexp.y"
X{ yyval.lval = yylval.lval; ;
X    break;}
Xcase 30:
X#line 259 "cexp.y"
X{
X              if (yylval.sval == (char *)NULL)
X                yyval.lval = 0;
X              else { char *ptr;
X                ptr = var_normal(yylval.sval);
X                if (ptr != (char *)NULL) {
X                  yyval.lval = atoi(ptr);
X                  free(ptr);
X                }
X                else yyval.lval = 0;
X                free(yylval.sval);
X              }
X            ;
X    break;}
X}
X   /* the action file gets copied in in place of this dollarsign */
X#line 408 "bison.simple"
X
X  yyvsp -= yylen;
X  yyssp -= yylen;
X#ifdef YYLSP_NEEDED
X  yylsp -= yylen;
X#endif
X
X#ifdef YYDEBUG
X  if (yydebug)
X    {
X      short *ssp1 = yyss - 1;
X      fprintf (stderr, "state stack now");
X      while (ssp1 != yyssp)
X	fprintf (stderr, " %d", *++ssp1);
X      fprintf (stderr, "\n");
X    }
X#endif
X
X  *++yyvsp = yyval;
X
X#ifdef YYLSP_NEEDED
X  yylsp++;
X  if (yylen == 0)
X    {
X      yylsp->first_line = yylloc.first_line;
X      yylsp->first_column = yylloc.first_column;
X      yylsp->last_line = (yylsp-1)->last_line;
X      yylsp->last_column = (yylsp-1)->last_column;
X      yylsp->text = 0;
X    }
X  else
X    {
X      yylsp->last_line = (yylsp+yylen-1)->last_line;
X      yylsp->last_column = (yylsp+yylen-1)->last_column;
X    }
X#endif
X
X  /* Now "shift" the result of the reduction.
X     Determine what state that goes to,
X     based on the state we popped back to
X     and the rule number reduced by.  */
X
X  yyn = yyr1[yyn];
X
X  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
X  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
X    yystate = yytable[yystate];
X  else
X    yystate = yydefgoto[yyn - YYNTBASE];
X
X  goto yynewstate;
X
Xyyerrlab:   /* here on detecting error */
X
X  if (! yyerrstatus)
X    /* If not already recovering from an error, report this error.  */
X    {
X      ++yynerr;
X      yyerror("parse error");
X    }
X
X  if (yyerrstatus == 3)
X    {
X      /* if just tried and failed to reuse lookahead token after an error, discard it.  */
X
X      /* return failure if at end of input */
X      if (yychar == YYEOF)
X	YYABORT;
X
X#ifdef YYDEBUG
X      if (yydebug)
X	fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
X#endif
X
X      yychar = YYEMPTY;
X    }
X
X  /* Else will try to reuse lookahead token
X     after shifting the error token.  */
X
X  yyerrstatus = 3;		/* Each real token shifted decrements this */
X
X  goto yyerrhandle;
X
Xyyerrdefault:  /* current state does not do anything special for the error token. */
X
X#if 0
X  /* This is wrong; only states that explicitly want error tokens
X     should shift them.  */
X  yyn = yydefact[yystate];  /* If its default is to accept any token, ok.  Otherwise pop it.*/
X  if (yyn) goto yydefault;
X#endif
X
Xyyerrpop:   /* pop the current state because it cannot handle the error token */
X
X  if (yyssp == yyss) YYABORT;
X  yyvsp--;
X  yystate = *--yyssp;
X#ifdef YYLSP_NEEDED
X  yylsp--;
X#endif
X
X#ifdef YYDEBUG
X  if (yydebug)
X    {
X      short *ssp1 = yyss - 1;
X      fprintf (stderr, "Error: state stack now");
X      while (ssp1 != yyssp)
X	fprintf (stderr, " %d", *++ssp1);
X      fprintf (stderr, "\n");
X    }
X#endif
X
Xyyerrhandle:
X
X  yyn = yypact[yystate];
X  if (yyn == YYFLAG)
X    goto yyerrdefault;
X
X  yyn += YYTERROR;
X  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
X    goto yyerrdefault;
X
X  yyn = yytable[yyn];
X  if (yyn < 0)
X    {
X      if (yyn == YYFLAG)
X	goto yyerrpop;
X      yyn = -yyn;
X      goto yyreduce;
X    }
X  else if (yyn == 0)
X    goto yyerrpop;
X
X  if (yyn == YYFINAL)
X    YYACCEPT;
X
X#ifdef YYDEBUG
X  if (yydebug)
X    fprintf(stderr, "Shifting error token, ");
X#endif
X
X  *++yyvsp = yylval;
X#ifdef YYLSP_NEEDED
X  *++yylsp = yylloc;
X#endif
X
X  yystate = yyn;
X  goto yynewstate;
X}
X#line 273 "cexp.y"
X
X
X/* During parsing of a C expression, the pointer to the next character
X   is in this variable.  */
X
Xstatic char *lexptr;
X
X/* Take care of parsing a number (anything that starts with a digit).
X   Set yylval and return the token type; update lexptr.
X   LEN is the number of characters in it.  */
X
X/* maybe needs to actually deal with floating point numbers */
X
Xint
Xparse_number (olen)
X     int olen;
X{
X  register char *p = lexptr;
X  register long n = 0;
X  register int c;
X  register int base = 10;
X  register len = olen;
X
X  extern double atof ();
X
X  for (c = 0; c < len; c++)
X    if (p[c] == '.') {
X      /* It's a float since it contains a point.  */
X      yyerror ("floating point numbers not allowed in expressions");
X      return ERROR;
X      
X/* ****************
X	 yylval.dval = atof (p);
X	 lexptr += len;
X	 return FLOAT;
X		 ****************  */
X    }
X  
X  if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) {
X    p += 2;
X    base = 16;
X    len -= 2;
X  }
X  else if (*p == '0')
X    base = 8;
X  
X  while (len-- > 0) {
X    c = *p++;
X    n *= base;
X    if (c >= '0' && c <= '9')
X      n += c - '0';
X    else {
X      if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
X      if (base == 16 && c >= 'a' && c <= 'f')
X	n += c - 'a' + 10;
X      else if (len == 0 && c == 'l')
X	;
X      else {
X	yyerror ("Invalid number in expression");
X	return ERROR;
X      }
X    }
X  }
X
X  lexptr = p;
X  yylval.lval = n;
X  return INT;
X}
X
Xstruct etoken {
X  char *operator;
X  int token;
X};
X
X#ifndef	NULL
X#define NULL 0
X#endif
X
Xstatic struct etoken tokentab2[] = {
X  {"&&", AND},
X  {"||", OR},
X  {"<<", LSH},
X  {">>", RSH},
X  {"==", EQUAL},
X  {"!=", NOTEQUAL},
X  {"<=", LEQ},
X  {">=", GEQ},
X  {NULL, ERROR}
X};
X
X/* Read one token, getting characters through lexptr.  */
X
Xint
Xyylex ()
X{
X  register int c;
X  register int namelen;
X  register char *tokstart;
X  register struct etoken *toktab;
X  char *name;
X
X retry:
X
X  tokstart = lexptr;
X  c = *tokstart;
X  /* See if it is a special token of length 2.  */
X  for (toktab = tokentab2; toktab->operator != NULL; toktab++)
X    if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) {
X      lexptr += 2;
X      return toktab->token;
X    }
X
X  switch (c) {
X  case 0:
X    return 0;
X    
X  case ' ':
X  case '\t':
X  case '\n':
X    lexptr++;
X    goto retry;
X    
X  case '\'':
X    lexptr++;
X    c = *lexptr++;
X    if (c == '\\')
X      c = parse_escape (&lexptr);
X    yylval.lval = c;
X    c = *lexptr++;
X    if (c != '\'') {
X      yyerror ("Invalid character constant");
X      return ERROR;
X    }
X    
X    return CHAR;
X
X    /* some of these chars are invalid in constant expressions;
X       maybe do something about them later */
X  case '/':
X  case '+':
X  case '-':
X  case '*':
X  case '%':
X  case '|':
X  case '&':
X  case '^':
X  case '~':
X  case '!':
X  case '@':
X  case '<':
X  case '>':
X  case '(':
X  case ')':
X  case '[':
X  case ']':
X  case '.':
X  case '?':
X  case ':':
X  case '=':
X  case '{':
X  case '}':
X  case ',':
X    lexptr++;
X    return c;
X    
X  case '"':
X    yyerror ("double quoted strings not allowed in expressions");
X    return ERROR;
X  }
X  if (c >= '0' && c <= '9') {
X    /* It's a number */
X    for (namelen = 0;
X	 c = tokstart[namelen], is_idchar[c] || c == '.'; 
X	 namelen++)
X      ;
X    return parse_number (namelen);
X  }
X  
X  if (!is_idstart[c]) {
X    yyerror ("Invalid token in expression");
X    return ERROR;
X  }
X  
X  /* It is a name.  See how long it is.  */
X
X  for (namelen = 0; is_idchar[tokstart[namelen]]; namelen++)
X    ;
X  name = new_string(namelen+2);
X  if (name == (char *)NULL)
X    errmsg(SHERR_NOMEM,LOC("yylex"));
X  yylval.sval = name;
X  if (name != (char *)NULL) {
X    name[0] = '$';
X    strncpy(&name[1],tokstart,namelen);
X    name[namelen+1] = '\0';
X  }
X  
X  lexptr += namelen;
X  return NAME;
X}
X
X
X/* Parse a C escape sequence.  STRING_PTR points to a variable
X   containing a pointer to the string to parse.  That pointer
X   is updated past the characters we use.  The value of the
X   escape sequence is returned.
X
X   A negative value means the sequence \ newline was seen,
X   which is supposed to be equivalent to nothing at all.
X
X   If \ is followed by a null character, we return a negative
X   value and leave the string pointer pointing at the null character.
X
X   If \ is followed by 000, we return 0 and leave the string pointer
X   after the zeros.  A value of 0 does not mean end of string.  */
X
Xint
Xparse_escape (string_ptr)
X     char **string_ptr;
X{
X  register int c = *(*string_ptr)++;
X  switch (c)
X    {
X    case 'a':
X      return '\a';
X    case 'b':
X      return '\b';
X    case 'e':
X      return 033;
X    case 'f':
X      return '\f';
X    case 'n':
X      return '\n';
X    case 'r':
X      return '\r';
X    case 't':
X      return '\t';
X    case 'v':
X      return '\v';
X    case '\n':
X      return -2;
X    case 0:
X      (*string_ptr)--;
X      return 0;
X    case '^':
X      c = *(*string_ptr)++;
X      if (c == '\\')
X	c = parse_escape (string_ptr);
X      if (c == '?')
X	return 0177;
X      return (c & 0200) | (c & 037);
X      
X    case '0':
X    case '1':
X    case '2':
X    case '3':
X    case '4':
X    case '5':
X    case '6':
X    case '7':
X      {
X	register int i = c - '0';
X	register int count = 0;
X	while (++count < 3)
X	  {
X	    if ((c = *(*string_ptr)++) >= '0' && c <= '7')
X	      {
X		i *= 8;
X		i += c - '0';
X	      }
X	    else
X	      {
X		(*string_ptr)--;
X		break;
X	      }
X	  }
X	return i;
X      }
X    default:
X      return c;
X    }
X}
X
Xvoid
Xyyerror (s)
X     char *s;
X{
X  errmsg (0,LOC("yyerror"),s);
X  longjmp (parse_return_error, 1);
X}
X
X/* This page contains the entry point to this file.  */
X
X/* Parse STRING as an expression, and complain if this fails
X   to use up all of the contents of STRING.  */
X/* We do not support C comments.  They should be removed before
X   this function is called.  */
X
Xint
Xparse_c_expression (string,side_effects)
X     char *string;
X     int side_effects;
X{
X  static int initialized;
X
X  if (!initialized) {
X    initialize_random_junk();
X    initialized++;
X  }
X
X  lexptr = string;
X  allow_side_effects = side_effects;
X  
X  if (lexptr == 0 || *lexptr == 0) {
X    return 0;
X  }
X
X  /* if there is some sort of scanning error, just return 0 and assume
X     the parsing routine has printed an error message somewhere.
X     there is surely a better thing to do than this.     */
X  if (setjmp(parse_return_error))
X    return 0;
X
X  if (yyparse ())
X    return 0;			/* actually this is never reached
X				   the way things stand. */
X  if (*lexptr)
X    errmsg (0,LOC("parse_c_expression"),"Junk after end of expression.");
X
X  return expression_value;	/* set by yyparse() */
X}
X
X/* table to tell if char can be part of a C identifier. */
Xunsigned char is_idchar[256];
X/* table to tell if char can be first char of a c identifier. */
Xunsigned char is_idstart[256];
X
X/*
X * initialize random junk in the hash table and maybe other places
X */
Xinitialize_random_junk()
X{
X  register int i;
X
X  /*
X   * Set up is_idchar and is_idstart tables.  These should be
X   * faster than saying (is_alpha(c) || c == '_'), etc.
X   * Must do set up these things before calling any routines tthat
X   * refer to them.
X   */
X  for (i = 'a'; i <= 'z'; i++) {
X    ++is_idchar[i - 'a' + 'A'];
X    ++is_idchar[i];
X    ++is_idstart[i - 'a' + 'A'];
X    ++is_idstart[i];
X  }
X  for (i = '0'; i <= '9'; i++)
X    ++is_idchar[i];
X  ++is_idchar['_'];
X  ++is_idstart['_'];
X#ifdef DOLLARS_IN_IDENTIFIERS
X  ++is_idchar['$'];
X  ++is_idstart['$'];
X#endif
X}
X
X
X#ifdef TEST_EXP_READER
X/* main program, for testing purposes. */
Xmain()
X{
X  int n;
X  char buf[1024];
X  extern int yydebug;
X/*
X  yydebug = 1;
X*/
X  initialize_random_junk ();
X
X  for (;;) {
X    printf("enter expression: ");
X    n = 0;
X    while ((buf[n] = getchar()) != '\n')
X      n++;
X    buf[n] = '\0';
X    printf("parser returned %d\n", parse_c_expression(buf));
X  }
X}
X
Xerrmsg (code,f,r,l,msg)
Xint code;
Xchar *f;
Xchar *r;
Xint l;
X{
X  printf("error: %s\n", msg);
X}
X#endif
END_OF_FILE
if test 35689 -ne `wc -c <'cexp.tab.c'`; then
    echo shar: \"'cexp.tab.c'\" unpacked with wrong size!
fi
# end of 'cexp.tab.c'
fi
echo shar: End of archive 7 \(of 11\).
cp /dev/null ark7isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.  It is NOT yet
bug free; I am VERY interested in fixes; enhancements, etc.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 8 (of 11)."
# Contents:  cmd2.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:03:59 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cmd2.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cmd2.c'\"
else
echo shar: Extracting \"'cmd2.c'\" \(36160 characters\)
sed "s/^X//" >'cmd2.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Built-in Commands (part 2)
X *
X * $Id: cmd2.c,v 1.8 89/03/05 15:37:54 dclemans Exp $
X *
X * $Log:	cmd2.c,v $
X * Revision 1.8  89/03/05  15:37:54  dclemans
X * more fixes
X * 
X * Revision 1.7  89/02/25  17:39:55  dclemans
X * miscellaneous bug fixes/speedups
X * 
X * Revision 1.6  89/02/22  21:31:52  dclemans
X * Implement simple background job monitoring facility
X * 
X * Revision 1.5  89/02/21  20:29:10  dclemans
X * Fix bug with shell variable references in history lists.
X * 
X * Revision 1.4  89/02/20  20:14:18  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#ifndef GEMDOS
X#include <sys/types.h>
X#endif  /* GEMDOS */
X#include <time.h>
X#include "shell.h"
X
Xstatic int run_phrase(pp)
Xstruct phrase *pp;
X{
X    register struct phrase *cpp;
X
X    cpp = copy_phrase(pp,1,0,1);
X    if (cpp == (struct phrase *)NULL)
X    {   /* enough memory? */
X        /* message already printed */
X        return 1;
X    }
X    if (flag_echoexec)
X        phrase_dump(cpp,0,0);
X    exec_phrase(cpp,0);
X    if (cpp->body != (struct token *)NULL)
X        var_define0("_",cpp->body_end->name,0);
X    phrase_free(cpp);
X    return 0;
X}   /* end of run_phrase */
X
Xstatic int run_args(pp)
Xstruct phrase *pp;
X{
X    register struct phrase *cpp;
X    register struct token *tp;
X    register struct iotoken *iop;
X
X    tp = pp->body;
X    pp->body = tp->next;
X    iop = pp->io;
X    pp->io = (struct iotoken *)NULL;
X
X    cpp = copy_phrase(pp,1,1,0);
X    if (cpp == (struct phrase *)NULL)
X    {   /* enough memory? */
X        /* message already printed */
X        pp->body = tp;
X        pp->io = iop;
X        return 1;
X    }
X    if (flag_echoexec)
X        phrase_dump(cpp,0,0);
X    exec_phrase(cpp,0);
X    if (cpp->body != (struct token *)NULL)
X        var_define0("_",cpp->body_end->name,0);
X    phrase_free(cpp);
X    pp->body = tp;
X    pp->io = iop;
X    return 0;
X}   /* end of run_args */
X
Xstatic int run_group(npp)
Xregister struct phrase *npp;
X{
X    for (; npp != (struct phrase *)NULL; npp = npp->next)
X    {   /* finally, do the execution */
X        if (run_phrase(npp))
X            return 1;
X        if (npp->type != (struct token *)NULL) switch (npp->type->type)
X        {   /* any special handling? */
X            case SYM_ANDIF:
X                if (cmd_lastrc != 0 && npp != (struct phrase *)NULL)
X                    npp = npp->next;
X                break;
X            case SYM_ORIF:
X                if (cmd_lastrc == 0 && npp != (struct phrase *)NULL)
X                    npp = npp->next;
X                break;
X        }
X        if (npp == (struct phrase *)NULL)
X            break;
X        if (cmd_returnexit)
X            break;
X        if (base_env.break_level > 0)
X            break;
X        if (base_env.continue_level > 0)
X            break;
X    }
X    return 0;
X}   /* end of run_group */
X
Xint cmd_for(pp)
Xregister struct phrase *pp;
X{
X    register struct token *tp,*args;
X    int ctr;
X
X    if (pp->body->next == (struct token *)NULL)
X    {   /* missing operands? */
X        errmsg(0,LOC("cmd_for"),"no operands to iterate over");
X        return 1;
X    }
X    tp = pp->body->next;
X    if (tp->next == (struct token *)NULL)
X    {   /* iterate over positional args? */
X        for (ctr = 1; ctr < var_argc; ctr++)
X        {   /* for each known arg */
X            var_define0(tp->name,var_argv[ctr],0);
X            if (run_group(pp->group))
X                break;
X            if (cmd_returnexit)
X                break;
X            if (base_env.break_level > 0)
X            {   /* if breaking */
X                base_env.break_level--;
X                break;
X            }
X            if (base_env.continue_level > 0)
X            {   /* if continuing */
X                base_env.continue_level--;
X                if (base_env.continue_level > 0)
X                    break;
X            }
X        }
X        return cmd_lastrc;
X    }
X    if (strcmp(tp->next->name,"in") != 0)
X    {   /* good separator? */
X        errmsg(0,LOC("cmd_for"),"bad for usage: for <var> in <args>");
X        return 1;
X    }
X    args = lex_reparse_tokens(tp->next->next,1);
X    while (args != (struct token *)NULL)
X    {   /* while there are args to iterate over */
X        var_define0(tp->name,args->name,0);
X        if (run_group(pp->group))
X            break;
X        args = args->next;
X        if (cmd_returnexit)
X            break;
X        if (base_env.break_level > 0)
X        {   /* if breaking */
X            base_env.break_level--;
X            break;
X        }
X        if (base_env.continue_level > 0)
X        {   /* if continuing */
X            base_env.continue_level--;
X            if (base_env.continue_level > 0)
X                break;
X        }
X    }
X    if (args != (struct token *)NULL)
X        tokens_free(args);
X    return cmd_lastrc;
X}   /* end of cmd_for */
X
Xstatic int select_menu(atp)
Xstruct token *atp;
X{
X    register int ctr,i;
X    int options,maxlength,ncol;
X    register struct token *tp;
X    char **args;
X    char *p,*savep;
X    char buffer[BUFSIZ];
X
X    maxlength = 0;
X    if (atp == (struct token *)NULL)
X    {   /* look at positional args */
X        options = var_argc-1;
X        for (ctr = 1; ctr <= options; ctr++)
X            if (strlen(var_argv[ctr]) > maxlength)
X                maxlength = strlen(var_argv[ctr]);
X        args = var_argv;
X    }
X    else
X    {   /* look at token args */
X        options = 0;
X        for (tp = atp; tp != (struct token *)NULL; tp = tp->next)
X        {   /* count, check max length */
X            options++;
X            if (strlen(tp->name) > maxlength)
X                maxlength = strlen(tp->name);
X        }
X        ctr = 1;
X        args = new_argv(options+1);
X        if (args == (char **)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("select_menu"));
X            return -1;
X        }
X        for (tp = atp; tp != (struct token *)NULL; tp = tp->next)
X        {   /* build a fake argv */
X            args[ctr] = tp->name;
X            ctr++;
X        }
X    }
X    maxlength += 7;     /* space for menu #, column separation */
X
X    for (;;)
X    {   /* until we get eof or a good menu response */
X        ncol = base_env.columns / maxlength;
X        if ((options < (base_env.lines*2/3)) || (ncol < 1))
X        {   /* just a single list? */
X            for (ctr = 1; ctr <= options; ctr++)
X            {   /* print the menu */
X                sprintf(buffer,"%d) %s\n",ctr,args[ctr]);
X                io_writestring(0,buffer);
X            }
X        }
X        else
X        {   /* a multi-column list? */
X            for (ctr = 1; ctr <= options; )
X            {   /* build lines... */
X                p = buffer;
X                *p = '\0';
X                for (i = 0; i < ncol && (ctr+i) <= options; i++)
X                {   /* for each column */
X                    sprintf(p,"%d) %s",ctr+i,args[ctr+i]);
X                    savep = p;
X                    while (*p)
X                        p++;
X                    while ((int)(p - savep) < maxlength)
X                        *p++ = ' ';
X                    *p = '\0';
X                }
X                ctr += ncol;
X                strcpy(p,"\n");
X                io_writestring(0,buffer);
X            }
X        }
X        io_prompt(2);
X        ctr = read(base_env.io->input,buffer,sizeof buffer);
X        if (ctr <= 0)
X        {   /* eof yet? */
X            if (atp != (struct token *)NULL)
X                free(args);
X            return ctr;
X        }
X        buffer[ctr] = '\0';
X        for (p = buffer; *p; p++)
X#ifdef  GEMDOS
X            if (*p == '\n' || *p == '\r')
X#else
X            if (*p == '\n')
X#endif  /* GEMDOS */
X                *p = '\0';
X        for (p = buffer; *p && isspace(*p); p++)
X            /* do nothing */;
X        ctr = atoi(p);
X        if (ctr > 0 && ctr <= options)
X        {   /* a selection made? */
X            if (atp != (struct token *)NULL)
X                free(args);
X            return ctr;
X        }
X    }
X}   /* end of select_menu */
X
Xint cmd_select(pp)
Xregister struct phrase *pp;
X{
X    register struct token *tp,*args;
X    struct token *atp;
X    int ctr;
X
X    if (pp->body->next == (struct token *)NULL)
X    {   /* missing operands? */
X        errmsg(0,LOC("cmd_select"),"no operands to iterate over");
X        return 1;
X    }
X    tp = pp->body->next;
X    if (tp->next == (struct token *)NULL)
X    {   /* iterate over positional args? */
X        while ((ctr = select_menu((struct token *)NULL)) > 0)
X        {   /* for each known arg */
X            var_define0(tp->name,var_argv[ctr],0);
X            if (run_group(pp->group))
X                break;
X            if (cmd_returnexit)
X                break;
X        }
X        return cmd_lastrc;
X    }
X    if (strcmp(tp->next->name,"in") != 0)
X    {   /* good separator? */
X        errmsg(0,LOC("cmd_select"),"bad select usage: select <var> in <args>");
X        return 1;
X    }
X    args = lex_reparse_tokens(tp->next->next,1);
X    while (args != (struct token *)NULL && (ctr = select_menu(args)) > 0)
X    {   /* while there are args to iterate over */
X        for (atp = args; ctr > 1; ctr--)
X            atp = atp->next;
X        var_define0(tp->name,atp->name,0);
X        var_define0("REPLY",atp->name,0);
X        if (run_group(pp->group))
X            break;
X        if (cmd_returnexit)
X            break;
X    }
X    if (args != (struct token *)NULL)
X        tokens_free(args);
X    return cmd_lastrc;
X}   /* end of cmd_select */
X
Xint cmd_while(pp)
Xregister struct phrase *pp;
X{
X    register char *cmd;
X    int until;
X
X    until = 0;
X    cmd = strcopy(pp->body->name);
X    if (cmd != (char *)NULL)
X    {   /* if we got the copy */
X        stripquotes(cmd);
X        if (strcmp(cmd,"until") == 0)
X            until = 1;
X        free(cmd);
X    }
X    if (pp->body->next == (struct token *)NULL)
X    {   /* missing operands? */
X        errmsg(0,LOC("cmd_while"),"no operands to loop over");
X        return 1;
X    }
X
X    for (;;)
X    {   /* while we have to loop */
X        if (run_args(pp))
X            return 1;
X        if (!until && cmd_lastrc != 0)
X            break;
X        if (until && cmd_lastrc == 0)
X            break;
X
X        if (run_group(pp->group))
X            break;
X        if (cmd_returnexit)
X            break;
X        if (base_env.break_level > 0)
X        {   /* if breaking */
X            base_env.break_level--;
X            break;
X        }
X        if (base_env.continue_level > 0)
X        {   /* if continuing */
X            base_env.continue_level--;
X            if (base_env.continue_level > 0)
X                break;
X        }
X    }
X    return cmd_lastrc;
X}   /* end of cmd_while */
X
Xint cmd_if(pp)
Xstruct phrase *pp;
X{
X    register struct phrase *npp;
X    int condition;
X
X    if (pp->body->next == (struct token *)NULL)
X    {   /* missing operands? */
X        errmsg(0,LOC("cmd_if"),"no condition to test");
X        return 1;
X    }
X    if (run_args(pp))
X        return 1;
X    condition = cmd_lastrc;
X
X    for (npp = pp->group; npp != (struct phrase *)NULL; npp = npp->next)
X    {   /* finally execute the sentence */
X        if (npp->body != (struct token *)NULL)
X        {   /* check for else, etc. */
X            if (strcmp(npp->body->name,"else") == 0)
X                condition = !condition;
X            else if (strcmp(npp->body->name,"elif") == 0)
X            {   /* an else-if test... */
X                if (npp->body->next == (struct token *)NULL)
X                {   /* missing operands? */
X                    errmsg(0,LOC("cmd_if"),"no condition to test");
X                    return 1;
X                }
X                if (run_args(npp))
X                    return 1;
X                condition = cmd_lastrc;
X            }
X        }
X        if (condition)
X            continue;
X        if (run_phrase(npp))
X            return 1;
X    }
X    return cmd_lastrc;
X}   /* end of cmd_if */
X
Xint cmd_function(pp)
Xstruct phrase *pp;
X{
X    register struct function *fp,*sfp;
X    register struct token *tp;
X    int rc;
X
X    tp = pp->body->next;
X    if (tp == (struct token *)NULL)
X    {   /* if no name, just dump table */
X        errmsg(0,LOC("cmd_function"),"unnamed functions are illegal");
X        return 1;
X    }
X
X    fp = new_function();
X    if (fp == (struct function *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("cmd_function"));
X        return -1;
X    }
X    fp->code = copy_phrase(pp,0,0,1);
X    if (fp->code == (struct phrase *)NULL)
X    {   /* enough memory? */
X        /* message already printed */
X        free(fp);
X        return -1;
X    }
X
X    for (sfp = base_env.func_table; sfp != (struct function *)NULL; )
X    {   /* look for where to put the function */
X        rc = strcmp(tp->name,sfp->name);
X        if (rc == 0)
X        {   /* replace existing value */
X            if (sfp->code != (struct phrase *)NULL)
X                phrase_free(sfp->code);
X            sfp->code = fp->code;
X            free(fp);
X            return 0;
X        }
X        else if (rc < 0)
X        {   /* go down the left side? */
X            if (sfp->left == (struct function *)NULL)
X                break;
X            sfp = sfp->left;
X        }
X        else
X        {   /* go down right side */
X            if (sfp->right == (struct function *)NULL)
X                break;
X            sfp = sfp->right;
X        }
X    }
X    fp->name = strcopy(tp->name);
X    if (fp->name == (char *)NULL)
X    {   /* enough memory? */
X        /* message already printed */
X        phrase_free(fp->code);
X        free(fp);
X        return -1;
X    }
X    if (base_env.func_table == (struct function *)NULL)
X        base_env.func_table = fp;
X    else if (rc < 0)
X        sfp->left = fp;
X    else
X        sfp->right = fp;
X    return 0;
X}   /* end of cmd_function */
X
Xint cmd_case(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X    register struct phrase *npp,*cpp;
X    char *exp;
X    int match,state;
X
X    tp = pp->body->next;
X    if (tp == (struct token *)NULL)
X    {   /* if no operand to switch on */
X        errmsg(0,LOC("cmd_case"),"no operands for comparison");
X        return -1;
X    }
X    exp = tp->name;
X
X    match = state = 0;
X    cpp = (struct phrase *)NULL;
X    for (npp = pp->group; npp != (struct phrase *)NULL; )
X    {   /* step through the case */
X        if (cpp != (struct phrase *)NULL)
X            phrase_free(cpp);
X        if (npp->next == (struct phrase *)NULL)
X            break;
X        cpp = copy_phrase(npp,1,0,1);
X        npp = npp->next;
X        if (state == 0)
X        {   /* if looking for case match */
X            if (cpp->type == (struct token *)NULL)
X                continue;
X            if (cpp->type->type == SYM_EOL &&
X                cpp->body == (struct token *)NULL &&
X                cpp->io == (struct iotoken *)NULL)
X                continue;
X            if (cpp->type->type != SYM_PIPE && cpp->type->type != SYM_RPAREN)
X            {   /* bad syntax... */
X                errmsg(0,LOC("cmd_case"),"bad case argument");
X                return -1;
X            }
X            if (!match)
X            {   /* a case match? */
X                if (cpp->body != (struct token *)NULL)
X                {   /* try a wild-card match */
X                    match = wild_match(exp,cpp->body->name);
X                }
X            }
X            if (cpp->type->type == SYM_RPAREN)
X            {   /* end of match stuff */
X                if (match)
X                    state = 1;
X                else state = -1;
X                continue;
X            }
X        }
X        if (state == 0)
X        {   /* if still looking */
X            continue;
X        }
X        if (state < 0)
X        {   /* if skipping some sentences */
X            if (cpp->type == (struct token *)NULL)
X                continue;
X            if (cpp->type->type == SYM_DSEMI)
X            {   /* end of group? */
X                state = 0;
X            }
X            continue;
X        }
X        /* else actually exec some sentences */
X        if (cpp->type == (struct token *)NULL)
X            continue;
X        if (flag_echoexec)
X            phrase_dump(cpp,0,0);
X        exec_phrase(cpp,0);
X        if (cpp->body != (struct token *)NULL)
X            var_define0("_",cpp->body_end->name,0);
X        if (cmd_returnexit)
X            break;
X        if (base_env.break_level > 0)
X        {   /* if breaking */
X            base_env.break_level--;
X            break;
X        }
X        if (base_env.continue_level > 0)
X        {   /* if continuing */
X            base_env.continue_level--;
X            if (base_env.continue_level > 0)
X                break;
X        }
X        if (cpp->type->type == SYM_DSEMI)
X        {   /* end of group? */
X            phrase_free(cpp);
X            return cmd_lastrc;
X        }
X    }
X
X    return cmd_lastrc;
X}   /* end of cmd_case */
X
Xint cmd_break(pp)
Xstruct phrase *pp;
X{
X    if (pp->body->next == (struct token *)NULL)
X        base_env.break_level = 1;
X    else    base_env.break_level = atoi(pp->body->next->name);
X    return 0;
X}   /* end of cmd_break */
X
Xint cmd_continue(pp)
Xstruct phrase *pp;
X{
X    if (pp->body->next == (struct token *)NULL)
X        base_env.continue_level = 1;
X    else    base_env.continue_level = atoi(pp->body->next->name);
X    return 0;
X}   /* end of cmd_continue */
X
Xint cmd_return(pp)
Xstruct phrase *pp;
X{
X    if (pp->body->next != (struct token *)NULL)
X        cmd_lastrc = atoi(pp->body->next->name);
X    cmd_returnexit = 1;
X}   /* end of cmd_return */
X
Xstatic void cmd_fc_dump(curr,nonum)
Xstruct hist_phrase *curr;
Xint nonum;
X{
X    char buffer[16];
X    register struct phrase *pp;
X
X    if (curr->cmd == (struct phrase *)NULL)
X        return;
X    if (!nonum)
X    {   /* print out the command number? */
X        sprintf(buffer,"%-5d",curr->number);
X        io_writestring(0,buffer);
X    }
X    for (pp = curr->cmd; pp != (struct phrase *)NULL; pp = pp->next)
X    {   /* dump out the lines */
X        phrase_dump(pp,1,5);
X        if (pp->next != (struct phrase *)NULL)
X            io_writestring(0,"\n     ");
X    }
X}   /* end of cmd_fc_dump */
X
Xstatic struct phrase *cmd_fc_edit(first,last,edit)
Xstruct hist_phrase *first;
Xstruct hist_phrase *last;
Xchar *edit;
X{
X    register struct hist_phrase *curr;
X    int save_output;
X    char *editor;
X    register struct phrase *torun,*toend;
X    struct phrase *pp;
X    struct token *tp;
X
X    editor = edit;
X    if (editor == (char *)NULL)
X        editor = var_normal("$FCEDIT");
X    if (editor == (char *)NULL)
X        editor = var_normal("$VISUAL");
X    if (editor == (char *)NULL)
X        editor = var_normal("$EDITOR");
X    if (editor == (char *)NULL)
X    {   /* any editor selected? */
X        errmsg(0,LOC("cmd_fc_edit"),"no editor available to edit commands");
X        return (struct phrase *)NULL;
X    }
X    torun = new_phrase();
X    if (torun == (struct phrase *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("cmd_fc_edit"));
X        return (struct phrase *)NULL;
X    }
X    tp = new_token(1);
X    if (tp == (struct token *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("cmd_fc_edit"));
X        free(torun);
X        return (struct phrase *)NULL;
X    }
X    tp->type = SYM_EOL;
X    strcpy(tp->name,"\n");
X    torun->type = tp;
X    tp = new_token(strlen(editor));
X    if (tp == (struct token *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("cmd_fc_edit"));
X        phrase_free(torun);
X        return (struct phrase *)NULL;
X    }
X    tp->type = SYM_WORD;
X    strcpy(tp->name,editor);
X    torun->body = torun->body_end = tp;
X    tp = new_token(16
X#ifdef  GEMDOS
X                     +strlen(base_env.tmpdir)
X#endif  /* GEMDOS */
X                                             );
X    if (tp == (struct token *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("cmd_fc_edit"));
X        phrase_free(torun);
X        return (struct phrase *)NULL;
X    }
X    tp->type = SYM_WORD;
X#ifdef  GEMDOS
X    sprintf(tp->name,"%stemp%d.tmp",base_env.tmpdir,base_env.temp_count++);
X#else
X    sprintf(tp->name,"/tmp/sh%d.temp",base_env.temp_count++);
X#endif  /* GEMDOS */
X    torun->body_end->next = tp;
X    torun->body_end = tp;
X    if (edit == (char *)NULL)
X        free(editor);
X
X    save_output = base_env.io->output;
X    base_env.io->output = creat(tp->name,0666);
X    if (base_env.io->output < 0)
X    {   /* did we get a temporary file? */
X        errmsg(0,LOC("cmd_fc_edit"),"unable to open tempfile");
X        phrase_free(torun);
X        return (struct phrase *)NULL;
X    }
X    if (first < last) for (curr = first; curr <= last; curr++)
X        cmd_fc_dump(curr,1);
X    else for (curr = last; curr >= first; curr--)
X        cmd_fc_dump(curr,1);
X    close(base_env.io->output);
X    base_env.io->output = save_output;
X
X    phrase_dump(torun,1,0);
X    exec_phrase(torun,1);
X    phrase_free(torun);
X
X    if (io_pushfile(tp->name,1,0,1) < 0)
X    {   /* if saving the file failed */
X        errmsg(0,LOC("cmd_fc_edit"),"unable to put tempfile into input stream");
X        delete_later(tp->name);
X        return (struct phrase *)NULL;
X    }
X    torun = toend = (struct phrase *)NULL;
X    while ((pp = lex_phrase(1,0)) != (struct phrase *)NULL)
X    {   /* get back contents of file */
X        if (pp->type != (struct token *)NULL && pp->type->type == SYM_MARKER)
X        {   /* reached end of file */
X            phrase_free(pp);
X            break;
X        }
X        if (torun == (struct phrase *)NULL)
X            torun = toend = pp;
X        else
X        {   /* tack onto end */
X            toend->next = pp;
X            toend = pp;
X        }
X    }
X    return torun;
X}   /* end of cmd_fc_edit */
X
Xstatic int cmd_fc_copy(curr,cpp,lcpp)
Xstruct hist_phrase *curr;
Xstruct phrase **cpp;
Xstruct phrase **lcpp;
X{
X    register struct phrase *pp,*np;
X
X    if (curr->cmd == (struct phrase *)NULL)
X        return 0;
X    for (pp = curr->cmd; pp != (struct phrase *)NULL; pp = pp->next)
X    {   /* copy block of phrases */
X        np = copy_phrase(pp,0,0,1);
X        if (np == (struct phrase *)NULL)
X        {   /* enough memory? */
X            /* message already printed */
X            sentence_free(*cpp);
X            return 1;
X        }
X        if (*cpp == (struct phrase *)NULL)
X            *cpp = *lcpp = np;
X        else
X        {   /* tack onto end */
X            (*lcpp)->next = np;
X            *lcpp = np;
X        }
X    }
X    return 0;
X}   /* end of cmd_fc_copy */
X
Xstatic char *cmd_fc_substr(s1,s2,l)
Xchar *s1;
Xregister char *s2;
Xregister int l;
X{
X    register char *s;
X
X    for (s = s1; *s; s++)
X    {   /* scan the string */
X        if (strncmp(s,s2,l) == 0)
X            return(s);
X    }
X    return (char *)NULL;
X}   /* end of cmd_fc_substr */
X
Xint cmd_fc_smatch(hp,num,pfx)
Xstruct hist_phrase *hp;
Xint num;
Xchar *pfx;
X{
X    struct phrase *pp;
X    struct token *tp;
X    register char *p,*q;
X    register int rc;
X    int len;
X
X    pp = hp->cmd;
X    if (pp == (struct phrase *)NULL)
X        return 0;
X    if (num < 0 || num >= base_env.history_size)
X    {   /* if to do string selection */
X        tp = pp->body;
X        if (tp == (struct token *)NULL)
X            return 0;
X        p = strcopy(tp->name);
X        if (p == (char *)NULL)
X            return 0;
X        stripquotes(p);
X        q = strrchr(p,DIR_SEPARATOR);
X        if (q == (char *)NULL)
X            q = p;
X        else q++;
X        len = strlen(pfx);
X        rc = strncmp(q,pfx,len);
X        if (rc != 0)
X            rc = strncmp(p,pfx,len);
X        free(p);
X        return (rc == 0);
X    }
X    else return (hp->number == num);
X}   /* end of cmd_fc_smatch */
X
Xint cmd_fc(pp)
Xstruct phrase *pp;
X{
X    register struct token *tp;
X    register int ctr;
X    register char *p;
X    char *localEd;
X    char *rStart,*rEnd,*q;
X    int list,nonum,reverse,rKey;
X    struct hist_phrase *first,*last,*curr;
X    struct phrase *np,*cpp,*lcpp;
X    struct token *ntp,*lntp;
X    int iFirst,iLast;
X
X    localEd = rStart = rEnd = (char *)NULL;
X    iFirst = iLast = -1;
X    rKey = list = nonum = reverse = 0;
X    for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X    {   /* look for options / range start & end */
X        if (tp->name[0] == '-')
X        {   /* if an option */
X            stripquotes(tp->name);
X            for (p = &tp->name[1]; *p; p++)
X            {   /* for each option char */
X                switch (*p)
X                {   /* which option? */
X                    case 'e':
X                        if (tp->next != (struct token *)NULL &&
X                            (tp->next->name[0] != '-' ||
X                            strcmp(tp->next->name,"-") == 0))
X                        {   /* get the arg if there */
X                            localEd = tp->next->name;
X                            stripquotes(localEd);
X                            tp = tp->next;
X                        }
X                        else if (tp->next != (struct token *)NULL &&
X                            tp->next->name[0] == '-')
X                        {   /* use FCEDIT */
X                            localEd = "";
X                        }
X                        else
X                        {   /* no editor name... */
X                            errmsg(0,LOC("cmd_fc"),"no editor name supplied for -e");
X                            return -1;
X                        }
X                        break;
X                    case 'l':
X                        list = 1;
X                        break;
X                    case 'r':
X                        reverse = 1;
X                        break;
X                    case 'n':
X                        nonum = 1;
X                        break;
X                    default:
X                        errmsg(0,LOC("cmd_fc"),"unknown option in arg: %s",p);
X                        return -1;
X                }
X            }
X        }
X        else
X        {   /* range start & end */
X            rStart = tp->name;
X            stripquotes(rStart);
X            if (strchr(rStart,'=') != (char *)NULL)
X                rKey = 1;
X            else rKey = 0;
X            if (tp->type == SYM_NUMBER)
X                iFirst = atoi(rStart);
X            if (tp->next != (struct token *)NULL)
X            {   /* end of range marker */
X                rEnd = tp->next->name;
X                stripquotes(rEnd);
X                if (tp->next->type == SYM_NUMBER)
X                    iLast = atoi(rEnd);
X            }
X            break;
X        }
X    }
X
X    first = last = curr = (struct hist_phrase *)NULL;
X    if (rStart == (char *)NULL && rEnd == (char *)NULL)
X    {   /* select whole range */
X        if (list)
X        {   /* if listing, default to full table */
X            first = &base_env.history_list[1];
X            last = &base_env.history_list[base_env.history_size-1];
X        }
X        else
X        {   /* default to last used entry */
X            first = last = &base_env.history_list[1];
X        }
X    }
X    else if (rStart != (char *)NULL && rEnd == (char *)NULL)
X    {   /* select just one */
X        rKey = 0;
X        for (ctr = 1; ctr < base_env.history_size; ctr++)
X        {   /* look for a leading match */
X            if (cmd_fc_smatch(&base_env.history_list[ctr],iFirst,rStart))
X            {   /* found a match */
X                first = last = &base_env.history_list[ctr];
X                break;
X            }
X        }
X    }
X    else if (!rKey && rStart != (char *)NULL && rEnd != (char *)NULL)
X    {   /* select a whole range */
X        for (ctr = 1; ctr < base_env.history_size; ctr++)
X        {   /* look for a leading match */
X            if (first == (struct hist_phrase *)NULL)
X            {   /* looking for 1st phrase? */
X                if (cmd_fc_smatch(&base_env.history_list[ctr],iFirst,rStart))
X                {   /* found a match */
X                    first = &base_env.history_list[ctr];
X                }
X            }
X            else
X            {   /* find end of match */
X                if (cmd_fc_smatch(&base_env.history_list[ctr],iLast,rEnd))
X                {   /* found a match */
X                    last = &base_env.history_list[ctr];
X                    break;
X                }
X            }
X        }
X    }
X    else if (rKey && rStart != (char *)NULL && rEnd != (char *)NULL)
X    {   /* select just one, but do a string substitution */
X        for (ctr = 1; ctr < base_env.history_size; ctr++)
X        {   /* look for a leading match */
X            if (cmd_fc_smatch(&base_env.history_list[ctr],iLast,rEnd))
X            {   /* found a match */
X                first = last = &base_env.history_list[ctr];
X                break;
X            }
X        }
X    }
X    else
X    {   /* illegal condition */
X        errmsg(0,LOC("cmd_fc"),"reached illegal state in command");
X        return 1;
X    }
X    if (first == (struct hist_phrase *)NULL || last == (struct hist_phrase *)NULL)
X    {   /* anything selected? */
X        errmsg(0,LOC("cmd_fc"),"nothing selected for fc to operate on");
X        return 1;
X    }
X
X    if (list)
X    {   /* if just want to dump out the lines */
X        if (reverse) for (curr = first; curr <= last; curr++)
X        {   /* step through the selected part of the array */
X            cmd_fc_dump(curr,nonum);
X        }
X        else for (curr = last; curr >= first; curr--)
X        {   /* reverse step through the selected part of the array */
X            cmd_fc_dump(curr,nonum);
X        }
X        return 0;
X    }
X
X    if (localEd == (char *)NULL ||
X        (localEd != (char *)NULL && strcmp(localEd,"-") != 0))
X    {   /* edit the commands in a real editor */
X        cpp = cmd_fc_edit(first,last,localEd);
X        if (cpp == (struct phrase *)NULL)
X        {   /* did it work? */
X            /* error message already printed */
X            return 1;
X        }
X    }
X    else
X    {   /* just make a copy of the sentences to execute */
X        cpp = lcpp = (struct phrase *)NULL;
X        if (!reverse) for (curr = first; curr <= last; curr++)
X        {   /* step through the selected part of the array */
X            if (cmd_fc_copy(curr,&cpp,&lcpp))
X            {   /* if copy didn't work */
X                /* message already printed */
X                return 1;
X            }
X        }
X        else for (curr = last; curr >= first; curr--)
X        {   /* reverse step through the selected part of the array */
X            if (cmd_fc_copy(curr,&cpp,&lcpp))
X            {   /* if copy didn't work */
X                /* message already printed */
X                return 1;
X            }
X        }
X        if (rKey)
X        {   /* if need to do a string substitution */
X            p = strchr(rStart,'=');
X            /* ASSERT that p != NULL */
X            for (np = cpp; np != (struct phrase *)NULL; np = np->next)
X            {   /* for each selected sentence */
X                for (lcpp = np; lcpp != (struct phrase *)NULL; lcpp = lcpp->next)
X                {   /* for base contents of each sentence */
X                    lntp = (struct token *)NULL;
X                    for (ntp = lcpp->body; ntp != (struct token *)NULL; ntp = ntp->next)
X                    {   /* look at words in phrase */
X                        q = cmd_fc_substr(ntp->name,rStart,(int)(p-rStart));
X                        if (q != (char *)NULL)
X                        {   /* if need to do token replacement */
X                            tp = new_token(strlen(ntp->name)-(int)(p-rStart)+strlen(&p[1]));
X                            if (tp == (struct token *)NULL)
X                            {   /* enough memory? */
X                                errmsg(SHERR_NOMEM,LOC("cmd_fc"));
X                                sentence_free(cpp);
X                                return 1;
X                            }
X                            tp->next = ntp->next;
X                            tp->type = ntp->type;
X                            strcpy(tp->name,ntp->name);
X                            strcpy(&tp->name[(int)(q-ntp->name)],&p[1]);
X                            strcat(tp->name,&ntp->name[(int)(q-ntp->name)+(int)(p-rStart)]);
X                            if (lntp != (struct token *)NULL)
X                                lntp->next = tp;
X                            else lcpp->body = tp;
X                            free(ntp);
X                            break;
X                        }
X                        lntp = ntp;
X                    }
X                    if (ntp != (struct token *)NULL)
X                        break;
X                }
X                if (lcpp != (struct phrase *)NULL)
X                    break;
X            }
X        }
X    }
X
X    if (base_env.history_size > 0)
X    {   /* if saving history of executed lines */
X        phrase_free(base_env.history_list[0].cmd);
X        base_env.history_list[0].cmd = copy_group(cpp,0,0,1);
X    }
X    for (np = cpp; np != (struct phrase *)NULL; np = np->next)
X    {   /* finally run the finished phrases */
X        phrase_dump(np,1,0);
X        lcpp = copy_phrase(np,1,0,1);
X        if (lcpp == (struct phrase *)NULL)
X        {   /* enough memory? */
X            /* message already printed */
X            continue;
X        }
X        exec_phrase(lcpp,0);
X        phrase_free(lcpp);
X        if (cmd_forceexit || cmd_returnexit)
X            break;
X    }
X    sentence_free(cpp);
X    return 0;
X}   /* end of cmd_fc */
X
Xint cmd_eval(pp)
Xstruct phrase *pp;
X{
X    register struct phrase *torun,*toend,*np;
X    struct token *tp;
X    char tempfile[32];
X    int save_output;
X
X    if (pp->body->next == (struct token *)NULL)
X    {   /* anything to eval? */
X        return 1;
X    }
X    np = copy_phrase(pp,1,1,1);
X    if (np == (struct phrase *)NULL)
X    {   /* enough memory? */
X        /* message already printed */
X        return 1;
X    }
X    tp = np->body;
X    np->body = tp->next;
X    free(tp);		/* free "eval" word */
X
X#ifdef  GEMDOS
X    sprintf(tempfile,"%stemp%d.tmp",base_env.tmpdir,base_env.temp_count++);
X#else
X    sprintf(tempfile,"/tmp/sh%d.temp",base_env.temp_count++);
X#endif  /* GEMDOS */
X    save_output = base_env.io->output;
X    base_env.io->output = creat(tempfile,0666);
X    if (base_env.io->output < 0)
X    {   /* did we get a temporary file? */
X        errmsg(0,LOC("cmd_eval"),"unable to open tempfile");
X        phrase_free(np);
X        return 1;
X    }
X    phrase_dump(np,1,0);
X    close(base_env.io->output);
X    base_env.io->output = save_output;
X    phrase_free(np);
X
X    if (io_pushfile(tempfile,1,0,1) < 0)
X    {   /* if saving the file failed */
X        errmsg(0,LOC("cmd_eval"),"unable to put tempfile into input stream");
X        delete_later(tempfile);
X        return 1;
X    }
X    torun = toend = (struct phrase *)NULL;
X    while ((np = lex_phrase(1,0)) != (struct phrase *)NULL)
X    {   /* get back contents of file */
X        if (np->type != (struct token *)NULL && np->type->type == SYM_MARKER)
X        {   /* reached end of file */
X            phrase_free(np);
X            break;
X        }
X        if (torun == (struct phrase *)NULL)
X            torun = toend = np;
X        else
X        {   /* tack onto end */
X            toend->next = np;
X            toend = np;
X        }
X    }
X    for (np = torun; np != (struct phrase *)NULL; np = np->next)
X    {   /* finally run the finished phrases */
X        exec_phrase(np,0);
X        if (cmd_forceexit || cmd_returnexit)
X            break;
X    }
X    return 0;
X}   /* end of cmd_eval */
X
Xint cmd_exec(pp)
Xstruct phrase *pp;
X{
X    if (pp->body->next == (struct token *)NULL)
X        return 0;
X    run_args(pp);
X    cmd_forceexit = 1;
X    return 0;
X}   /* end of cmd_exec */
X
Xint cmd_time(pp)
Xstruct phrase *pp;
X{
X    time_t tstart,tend;
X    long min,sec;
X    char buffer[256];
X
X    if (pp->body->next == (struct token *)NULL)
X        return 0;
X    tstart = time(0L);
X    run_args(pp);
X    tend = time(0L);
X    tend -= tstart;
X    min = tend / 60L;
X    sec = tend % 60L;
X    if (min == 0)
X    {   /* only seconds to worry about? */
X        if (sec == 1)
X            sprintf(buffer,"1 second");
X        else sprintf(buffer,"%ld seconds",sec);
X    }
X    else
X    {   /* both minutes and seconds */
X        if (min == 1 && sec == 1)
X            sprintf(buffer,"1 minute, 1 second");
X        else if (sec == 1)
X            sprintf(buffer,"%ld minutes, 1 second",min);
X        else if (min == 1)
X            sprintf(buffer,"1 minute, %ld seconds",sec);
X        else sprintf(buffer,"%ld minutes, %ld seconds",min,sec);
X    }
X    strcat(buffer,"\n");
X    io_writestring(1,buffer);
X    return 0;
X}   /* end of cmd_time */
END_OF_FILE
if test 36160 -ne `wc -c <'cmd2.c'`; then
    echo shar: \"'cmd2.c'\" unpacked with wrong size!
fi
# end of 'cmd2.c'
fi
echo shar: End of archive 8 \(of 11\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.  It is NOT yet
bug free; I am VERY interested in fixes; enhancements, etc.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 9 (of 11)."
# Contents:  lex.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:04:00 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'lex.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lex.c'\"
else
echo shar: Extracting \"'lex.c'\" \(37330 characters\)
sed "s/^X//" >'lex.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Input Lexer
X *
X * $Id: lex.c,v 1.11 89/03/05 15:38:27 dclemans Exp $
X *
X * $Log:	lex.c,v $
X * Revision 1.11  89/03/05  15:38:27  dclemans
X * more fixes
X * 
X * Revision 1.10  89/02/25  17:40:08  dclemans
X * miscellaneous bug fixes/speedups
X * 
X * Revision 1.9  89/02/22  21:47:25  dclemans
X * bug fix ] vs ]]
X * 
X * Revision 1.8  89/02/22  16:26:54  dclemans
X * Implement [[, ]] brackets
X * 
X * Revision 1.7  89/02/22  13:12:27  dclemans
X * Fix bug with null tokens
X * 
X * Revision 1.6  89/02/22  10:16:35  dclemans
X * Fix bugs with process waiting, pid reporting, parsing of &
X * 
X * Revision 1.5  89/02/21  20:29:56  dclemans
X * Fix bug with shell variable references in history lists.
X * 
X * Revision 1.4  89/02/20  20:11:56  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X
Xstatic int dont_expand = 0;
X
Xint lex_heredoc_do(iop)
Xstruct iotoken *iop;
X{
X    int ofd,len,next_char;
X    char buffer[BUFSIZ];
X
X    if (iop->type != SYM_LLT && iop->type != SYM_LLTL)
X        return -1;
X    len = 16;
X#ifdef  GEMDOS
X    len += strlen(base_env.tmpdir);
X#endif  /* GEMDOS */
X    iop->tempfile = new_string(len+1);
X    if (iop->tempfile == (char *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("io_heredoc"));
X        return -1;
X    }
X#ifdef  GEMDOS
X    sprintf(iop->tempfile,"%stemp%d.tmp",base_env.tmpdir,base_env.temp_count++);
X#else
X    sprintf(iop->tempfile,"/tmp/sh%d.temp",base_env.temp_count++);
X#endif  /* GEMDOS */
X    ofd = creat(iop->tempfile,0666);
X    if (ofd < 0)
X    {   /* was it created? */
X        errmsg(0,LOC("io_heredoc"),"couldn't create %s",iop->tempfile);
X        free(iop->tempfile);
X        iop->tempfile = (char *)NULL;
X        return -1;
X    }
X    len = 0;
X    if (iop->file[0] == ESCAPE_CHAR ||
X        iop->file[0] == '\'' || iop->file[0] == '"')
X        dont_expand = 1;
X    stripquotes(iop->file);
X    while ((next_char = io_getchar(1)) != SYM_EOF)
X    {   /* get chars to fill up heredoc */
X        buffer[len++] = next_char;
X        if (next_char == '\n' || len >= sizeof buffer)
X        {   /* end of line? */
X#ifdef  GEMDOS
X            if (next_char == '\n')
X                buffer[len-1] = '\r';
X#endif  /* GEMDOS */
X            while (iop->type == SYM_LLTL && isspace(buffer[0]) && len > 0)
X            {   /* strip leading white space? */
X                len--;
X                strncpy(buffer,&buffer[1],len);
X            }
X            if (len == 0)
X                continue;
X            if (strncmp(iop->file,buffer,strlen(iop->file)) == 0)
X            {   /* heredoc end of file? */
X                break;
X            }
X            write(ofd,buffer,len);
X            len = 0;
X#ifdef  GEMDOS
X            if (next_char == '\n')
X                write(ofd,"\n",1);
X#endif  /* GEMDOS */
X        }
X    }
X    dont_expand = 0;
X    close(ofd);
X    return 0;
X}   /* end of lex_heredoc_do */
X
Xint lex_heredoc()
X{
X    struct herestack *hs,*hsp,*ohsp;
X
X    hs = base_env.pending_heredocs;
X    base_env.pending_heredocs = (struct herestack *)NULL;
X    if (hs == (struct herestack *)NULL)
X        return 0;
X    for (hsp = hs; hsp != (struct herestack *)NULL; )
X    {   /* for each pending heredoc */
X        ohsp = hsp;
X        hsp = hsp->next;
X        lex_heredoc_do(ohsp->doc);
X        free(ohsp);
X    }
X    return 0;
X}   /* end of lex_heredoc */
X
Xint lex_heredoc_save(iop)
Xstruct iotoken *iop;
X{
X    struct herestack *hs,*hsp;
X
X    hsp = (struct herestack *)malloc(sizeof(*hsp));
X    if (hsp == (struct herestack *)NULL)
X    {   /* enough memory */
X        errmsg(SHERR_NOMEM,LOC("lex_heredoc_save"));
X        return -1;
X    }
X    hsp->next = (struct herestack *)NULL;
X    hsp->doc = iop;
X
X    if (base_env.pending_heredocs == (struct herestack *)NULL)
X        base_env.pending_heredocs = hsp;
X    else
X    {   /* tack onto end */
X        for (hs = base_env.pending_heredocs; hs->next != (struct herestack *)NULL; hs = hs->next)
X            /* do nothing */;
X        hs->next = hsp;
X    }
X    return 0;
X}   /* end of lex_heredoc_save */
X
Xstatic int typeof(word)
Xregister char *word;
X{
X    register char *p;
X    int flag;
X
X    if (isdigit(*word) || *word == '-' || *word == '+')
X    {   /* is the whole word numeric? */
X        flag = SYM_NUMBER;
X        for (p = &word[1]; *p; p++)
X            if (!isdigit(*p))
X                flag = SYM_WORD;
X        return flag;
X    }
X    switch (*word)
X    {   /* what kind of word? */
X        case '`':
X            return SYM_BQUOTE;
X        case ';':
X            if (word[1] == ';')
X                return SYM_DSEMI;
X            return SYM_SEMI;
X        case '<':
X            if (word[1] == '&')
X                return SYM_LBT;
X            else if (word[1] == '<')
X            {   /* if a here doc */
X                if (word[2] == '-')
X                    return SYM_LLTL;
X                return SYM_LLT;
X            }
X            else    return SYM_LT;
X        case '>':
X            if (word[1] == '&')
X                return SYM_RBT;
X            else if (word[1] == '>')
X                return SYM_RRT;
X            else    return SYM_RT;
X        case '{':
X            return SYM_LBRACE;
X        case '}':
X            return SYM_RBRACE;
X        case '(':
X            if (word[1] == '(')
X                return SYM_DLPAREN;
X            else return SYM_LPAREN;
X        case ')':
X            if (word[1] == ')')
X                return SYM_DRPAREN;
X            return SYM_RPAREN;
X        case '&':
X            if (word[1] == '&')
X                return SYM_ANDIF;
X            else    return SYM_BACK;
X        case '|':
X            if (word[1] == '|')
X                return SYM_ORIF;
X            else    return SYM_PIPE;
X        case '[':
X            if (word[1] == '[')
X                return SYM_DLBRACK;
X            else    return SYM_WORD;
X        case ']':
X            if (word[1] == ']')
X                return SYM_DRBRACK;
X            else    return SYM_DRBRACK;
X        case '\n':
X        case '\r':
X            return SYM_EOL;
X        default:
X            return SYM_WORD;
X    }
X    /* NOT REACHED */
X}   /* end of typeof */
X
Xstruct token *lex_token(level)
Xint level;
X{
X    char workbuf[BUFSIZ];
X    register char *first,*ptr;
X    int keepgoing,keyword,next_char,la_char;
X    register struct token *tp;
X    struct phrase *sp;
X    char *result;
X#define APPEND(c) ((first<&workbuf[sizeof workbuf])?*first++ = c:errmsg(0,LOC("lex_token"),"word too long"))
X
X    while ((next_char = io_getchar(level)) != SYM_EOF)
X    {   /* skip leading blanks */
X        if (strchr(base_env.separators,next_char) == (char *)NULL)
X            break;
X    }
X    if (next_char == SYM_EOF)
X        return (struct token *)NULL;
X    keyword = 0;
X    keepgoing = 1;
X    first = workbuf;
X    while (next_char != SYM_EOF && keepgoing && strchr(base_env.separators,next_char) == (char *)NULL)
X    {   /* go pick off a word */
X        switch (next_char)
X        {   /* look for quoting, etc. */
X            case '#':   /* rest of line a comment? */
X                while (next_char != '\r' && next_char != '\n')
X                {   /* scan til end of line */
X                    next_char = io_getchar(level);
X                    if (next_char == SYM_EOF)
X                        break;
X                }
X                io_pushback(next_char);
X                break;
X            case '`':   /* `` expansion string */
X                if (level == 0)
X                {   /* starting string? */
X                    sp = lex_sentence(1);
X                    exec_sentence(sp,0);
X                    io_pushfile(sp->group_end->io->file,1,1,0);
X                    sentence_free(sp);
X                }
X                else
X                {   /* end of string */
X                    keepgoing = 0;
X                    if (first != workbuf)
X                        io_pushback(next_char);
X                    else APPEND(next_char);
X                }
X                break;
X            case ESCAPE_CHAR:
X                APPEND(next_char);
X                next_char = io_getchar(1);
X                APPEND(next_char);
X                break;
X            case '"':   /* double quote; allow variables */
X                APPEND('"');
X                while ((next_char = io_getchar(1)) != '"')
X                {   /* until end of string */
X                    if (next_char == SYM_EOF)
X                        break;
X                    if (next_char == ESCAPE_CHAR)
X                    {   /* grab next char */
X                        APPEND(next_char);
X                        next_char = io_getchar(1);
X                    }
X                    else if (next_char == '$')
X                    {   /* expand out a variable */
X                        result = var_reference(dont_expand);
X                        if (result != (char *)NULL)
X                        {   /* stick in expansion */
X                            for (ptr = result; *ptr; ptr++)
X                                APPEND(*ptr);
X                            free(result);
X                        }
X                        continue;
X                    }
X                    else if (next_char == '`')
X                    {   /* substituted command */
X                        if (level == 0)
X                        {   /* starting string? */
X                            sp = lex_sentence(1);
X                            exec_sentence(sp,0);
X                            io_pushfile(sp->group_end->io->file,1,1,0);
X                            sentence_free(sp);
X                        }
X                        else
X                        {   /* end of string */
X                            keepgoing = 0;
X                            if (first != workbuf)
X                                io_pushback(next_char);
X                            else APPEND(next_char);
X                        }
X                        continue;
X                    }
X                    APPEND(next_char);
X                }
X                if (next_char == SYM_EOF)
X                {   /* bad end-of-string */
X                    errmsg(0,LOC("lex_token"),"end of string not found");
X                    break;
X                }
X                APPEND('"');
X                break;
X            case '\'':  /* single quote */
X                APPEND('\'');
X                while ((next_char = io_getchar(1)) != '\'')
X                {   /* until end of string */
X                    if (next_char == SYM_EOF)
X                        break;
X                    APPEND(next_char);
X                }
X                if (next_char == SYM_EOF)
X                {   /* bad end-of-string? */
X                    errmsg(0,LOC("lex_token"),"missing end of string");
X                    break;
X                }
X                APPEND('\'');
X                break;
X            case '[':
X            case ']':
X                la_char = io_getchar(level);
X                if (next_char != la_char)
X                {   /* if not a double [[ or ]] */
X                    io_pushback(la_char);
X                    APPEND(next_char);
X                    break;
X                }
X                if (first != workbuf)
X                {   /* ending current token? */
X                    io_pushback(la_char);
X                    io_pushback(next_char);
X                    keepgoing = 0;
X                    break;
X                }
X                APPEND(next_char);
X                APPEND(la_char);
X                keepgoing = 0;
X                break;
X            case ';':   /* end of phrase */
X            case '(':   /* start of group */
X            case ')':   /* end of group */
X                if (first != workbuf)
X                {   /* ending current token? */
X                    io_pushback(next_char);
X                    keepgoing = 0;
X                    break;
X                }
X                APPEND(next_char);
X                next_char = io_getchar(level);
X                if (next_char != SYM_EOF && first[-1] == next_char)
X                {   /* a two character symbol? */
X                    APPEND(next_char);
X                }
X                else
X                {   /* a one character symbol */
X                    io_pushback(next_char);
X                }
X                keepgoing = 0;
X                break;
X            case SYM_MARKER:
X                keepgoing = 0;
X                workbuf[0] = '\0';
X                first = workbuf;
X                break;
X            case '\n':  /* end of line */
X            case '\r':
X                lex_heredoc();
X                /* FALL THROUGH! */
X            case '{':   /* left brace */
X            case '}':   /* right brace */
X                if (first != workbuf)
X                {   /* ending current token? */
X                    io_pushback(next_char);
X                    keepgoing = 0;
X                    break;
X                }
X                APPEND(next_char);
X                keepgoing = 0;
X                break;
X            case '|':   /* a pipe? */
X            case '&':   /* background? */
X            case '<':   /* I/O? */
X            case '>':   /* I/O? */
X                if (first != workbuf)
X                {   /* ending current token? */
X                    io_pushback(next_char);
X                    keepgoing = 0;
X                    break;
X                }
X                APPEND(next_char);
X                keepgoing = 0;
X                next_char = io_getchar(level);
X                if ((next_char != SYM_EOF) &&
X                    (first[-1] == next_char ||
X                    (first[-1] == '<' && next_char == '&') ||
X                    (first[-1] == '>' && next_char == '&')))
X                {   /* a two character symbol? */
X                    APPEND(next_char);
X                    if (next_char == '<')
X                    {   /* three character (<<-)? */
X                        next_char = io_getchar(level);
X                        if (next_char == '-')
X                            APPEND(next_char);
X                        else io_pushback(next_char);
X                    }
X                }
X                else
X                {   /* a one character symbol */
X                    io_pushback(next_char);
X                }
X                break;
X            case '=':   /* possible assignment? */
X                keyword = 1;
X                /* FALL THROUGH */
X            default:    /* and everything else */
X                if (next_char == '$')
X                {   /* expand a variable? */
X                    result = var_reference(dont_expand);
X                    if (result != (char *)NULL)
X                    {   /* stick in expansion */
X                        for (ptr = result; *ptr; ptr++)
X                            APPEND(*ptr);
X                        free(result);
X                    }
X                    break;
X                }
X                APPEND(next_char);
X                break;
X        }
X        if (!keepgoing)
X            break;
X        if (next_char != SYM_EOF)
X            next_char = io_getchar(level);
X    }
X    if (first == workbuf && next_char != SYM_EOF && keepgoing && strchr(base_env.separators,next_char) != (char *)NULL)
X    {   /* reject this as useless token? */
X        return lex_token(level);
X    }
X    tp = new_token((int)(first-workbuf));
X    if (tp == (struct token *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("lex_token"));
X        return tp;
X    }
X    strncpy(tp->name,workbuf,(int)(first-workbuf));
X    tp->name[(int)(first-workbuf)] = '\0';
X    if (next_char == SYM_MARKER)
X        tp->type = SYM_MARKER;
X    else tp->type = typeof(tp->name);
X    if (keyword && tp->type == SYM_WORD)
X        tp->type = SYM_KEYWORD;
X    return tp;
X#undef  APPEND
X}   /* end of lex_token */
X
Xstruct brackets
X{
X    char    *name;
X    char    *start;
X    char    *end;
X};
Xstatic struct brackets cmd_brackets[] =
X{
X    { "case",       "",         "esac"  },
X    { "if",         "then",     "fi"    },
X    { "for",        "do",       "done"  },
X    { "for",        "{",        "}"     },
X    { "function",   "{",        "}"     },
X    { "select",     "do",       "done"  },
X    { "select",     "{",        "}"     },
X    { "until",      "do",       "done"  },
X    { "until",      "{",        "}"     },
X    { "while",      "do",       "done"  },
X    { "while",      "{",        "}"     },
X    { (char *)NULL, (char *)NULL,   (char *)NULL }
X};
X
Xint reserved_word(word)
Xchar *word;
X{
X    register int ctr;
X
X    for (ctr = 0; cmd_brackets[ctr].name != (char *)NULL; ctr++)
X    {   /* check each bracket set */
X        if (strcmp(cmd_brackets[ctr].name,word) == 0)
X            return 1;
X        if (strcmp(cmd_brackets[ctr].start,word) == 0)
X            return 1;
X        if (strcmp(cmd_brackets[ctr].end,word) == 0)
X            return 1;
X    }
X    if (strcmp("else",word) == 0)
X        return 1;
X    if (strcmp("elif",word) == 0)
X        return 1;
X    return 0;
X}   /* end of reserved_word */
X
Xstruct phrase *lex_paragraph(level,fpp)
Xint level;
Xstruct phrase *fpp;
X{
X    register struct phrase *pp;
X    struct token *tp,*stp,*etp;
X    register int ctr;
X
X    if (fpp == (struct phrase *)NULL)
X        return fpp;
X    tp = fpp->body;
X    if (tp == (struct token *)NULL)
X        return fpp;
X    for (ctr = 0; cmd_brackets[ctr].name != (char *)NULL; ctr++)
X    {   /* start of a paragraph? */
X        if (strcmp(tp->name,cmd_brackets[ctr].name) == 0)
X            break;
X    }
X    if (cmd_brackets[ctr].name == (char *)NULL)
X        return fpp;
X    if (strlen(cmd_brackets[ctr].start) != 0)
X    {   /* if need a starting bracket */
X        stp = lex_token(1);
X        if (stp == (struct token *)NULL)
X        {   /* no paragraph start? */
X            errmsg(0,LOC("lex_paragraph"),"missing start of paragraph");
X            return fpp;
X        }
X        if (stp->type == SYM_EOL && strcmp(stp->name,"\n") == 0)
X        {   /* skip empty token */
X            free(stp);
X            stp = lex_token(1);
X            if (stp == (struct token *)NULL)
X            {   /* no paragraph start? */
X                errmsg(0,LOC("lex_paragraph"),"missing start of paragraph");
X                return fpp;
X            }
X        }
X        for (ctr = 0; cmd_brackets[ctr].name != (char *)NULL; ctr++)
X        {   /* now can find "correct" brackets */
X            if (strcmp(tp->name,cmd_brackets[ctr].name) == 0 &&
X                strcmp(stp->name,cmd_brackets[ctr].start) == 0)
X                break;
X        }
X        if (cmd_brackets[ctr].name == (char *)NULL)
X        {   /* unknown combo? */
X            errmsg(0,LOC("lex_paragraph"),"unexpected paragraph tokens: %s,%s",
X                tp->name,stp->name);
X            io_pushtoken(stp->name,0);
X            free(stp);
X            return fpp;
X        }
X        pp = new_phrase();
X        if (pp == (struct phrase *)NULL)
X        {   /* enough memory? */
X            free(stp);
X            errmsg(SHERR_NOMEM,LOC("lex_paragraph"));
X            return fpp;
X        }
X        pp->body = pp->body_end = stp;
X        pp->type = new_token(1);
X        if (pp->type == (struct token *)NULL)
X        {   /* enough memory? */
X            free(pp);
X            free(stp);
X            errmsg(SHERR_NOMEM,LOC("lex_paragraph"));
X            return fpp;
X        }
X        pp->type->type = SYM_EOL;
X        strcpy(pp->type->name,"\n");
X        fpp->group = fpp->group_end = pp;
X    }
X
X    while (dont_expand = 1,(pp = lex_phrase(1,1)) != (struct phrase *)NULL)
X    {   /* loop til eof or end of paragraph */
X        if (pp->type == (struct token *)NULL)
X            break;
X        if (pp->io == (struct iotoken *)NULL &&
X            pp->group == (struct phrase *)NULL &&
X            pp->var == (struct token *)NULL &&
X            pp->type->type == SYM_EOL && pp->body == (struct token *)NULL)
X        {   /* ignore leading empty phrase */
X            phrase_free(pp);
X            continue;
X        }
X        if (fpp->group == (struct phrase *)NULL)
X            fpp->group = fpp->group_end = pp;
X        else
X        {   /* tack onto end */
X            fpp->group_end->next = pp;
X            fpp->group_end = pp;
X        }
X        etp = pp->body;
X        if (etp == (struct token *)NULL)
X            etp = pp->type;
X        if (etp == (struct token *)NULL)
X            continue;
X        if (strcmp(etp->name,cmd_brackets[ctr].end) == 0)
X        {   /* got end of paragraph? */
X            dont_expand = 0;
X            if (fpp->io == (struct iotoken *)NULL &&
X                pp->io != (struct iotoken *)NULL)
X            {   /* move io redirection to good place */
X                fpp->io = pp->io;
X                fpp->io_end = pp->io_end;
X                pp->io = pp->io_end = (struct iotoken *)NULL;
X            }
X            return fpp;
X        }
X    }
X    errmsg(0,LOC("lex_paragraph"),"end of paragraph missing");
X
X    if (pp != (struct phrase *)NULL)
X        phrase_free(pp);
X    dont_expand = 0;
X    return lex_phrase(level,1);
X}   /* end of lex_paragraph */
X
Xvoid lex_parens(level,fpp)
Xint level;
Xstruct phrase *fpp;
X{
X    register struct phrase *pp;
X    int doneflag;
X
X    if (fpp->type == (struct token *)NULL || fpp->type->type != SYM_LPAREN)
X        return;
X    doneflag = 0;
X    while ((pp = lex_phrase(level,1)) != (struct phrase *)NULL)
X    {   /* get the parenthized phrases */
X        if (pp->type == (struct token *)NULL)
X            break;
X        if (fpp->group == (struct phrase *)NULL)
X            fpp->group = fpp->group_end = pp;
X        else
X        {   /* tack onto end */
X            fpp->group_end->next = pp;
X            fpp->group_end = pp;
X        }
X        if (doneflag)
X            break;
X        if (pp->type->type == SYM_RPAREN)
X            doneflag++;
X    }
X    if (doneflag)
X    {   /* if a parenthized group was found */
X        if (pp->io != (struct iotoken *)NULL)
X        {   /* move group i/o to front */
X            if (fpp->io == (struct iotoken *)NULL)
X            {   /* just move it */
X                fpp->io = pp->io;
X                fpp->io_end = pp->io_end;
X            }
X            else
X            {   /* tack onto end */
X                fpp->io_end->next = pp->io;
X                fpp->io_end = pp->io_end;
X            }
X            pp->io = pp->io_end = (struct iotoken *)NULL;
X        }
X    }
X}   /* end of lex_parens */
X
Xstruct phrase *lex_phrase(level,expflag)
Xint level;
Xint expflag;
X{
X    register struct phrase *pp;
X    register struct token *tp,*tp1;
X    struct token *tp2;
X    struct iotoken *iop;
X    int save_expflag;
X
X    pp = new_phrase();
X    if (pp == (struct phrase *)NULL)
X        return pp;
X
X    save_expflag = dont_expand;
X    dont_expand = expflag;
X    while ((tp = lex_token(level)) != (struct token *)NULL)
X    {   /* get token until end of phrase is reached */
X        switch (tp->type)
X        {   /* check for end of phrase or other special tokens */
X            case SYM_DLPAREN:   /* start of arithmetic expression */
X                do
X                {   /* fill out rest of expression */
X                    if (pp->body == (struct token *)NULL)
X                    {   /* at start of list */
X                        pp->body = pp->body_end = tp;
X                    }
X                    else
X                    {   /* tack onto end */
X                        pp->body_end->next = tp;
X                        pp->body_end = tp;
X                    }
X                    if (tp->type == SYM_DRPAREN)
X                        break;
X                } while ((tp = lex_token(1)) != (struct token *)NULL);
X                break;
X            case SYM_DLBRACK:   /* start of conditional expression */
X                do
X                {   /* fill out rest of expression */
X                    if (pp->body == (struct token *)NULL)
X                    {   /* at start of list */
X                        pp->body = pp->body_end = tp;
X                    }
X                    else
X                    {   /* tack onto end */
X                        pp->body_end->next = tp;
X                        pp->body_end = tp;
X                    }
X                    if (tp->type == SYM_DRBRACK)
X                        break;
X                } while ((tp = lex_token(1)) != (struct token *)NULL);
X                break;
X            case SYM_LT:    /* various I/O redirection tokens */
X            case SYM_LLT:
X            case SYM_LLTL:
X            case SYM_LBT:
X            case SYM_RT:
X            case SYM_RRT:
X            case SYM_RBT:
X                for (tp1 = pp->body; tp1 != (struct token *)NULL && tp1->next != pp->body_end; tp1 = tp1->next)
X                    /* do nothing */;
X                if (pp->body != (struct token *)NULL && pp->body_end->type == SYM_NUMBER)
X                {   /* pick up file descriptor? */
X                    tp2 = pp->body_end;
X                    if (tp1 != (struct token *)NULL)
X                    {   /* remove tail of list */
X                        tp1->next = (struct token *)NULL;
X                        pp->body_end = tp1;
X                    }
X                    else    pp->body = pp->body_end = (struct token *)NULL;
X                }
X                else    tp2 = (struct token *)NULL;
X                tp1 = lex_token(level);
X                if (tp1 == (struct token *)NULL)
X                {   /* EOF or no memory? */
X                    errmsg(0,LOC("lex_phrase"),"incomplete I/O redirection");
X                    tp = tp1;
X                    break;
X                }
X                iop = new_iotoken(strlen(tp1->name));
X                if (iop == (struct iotoken *)NULL)
X                {   /* enough memory */
X                    errmsg(SHERR_NOMEM,LOC("lex_phrase"));
X                }
X                else
X                {   /* fill in the ioblock */
X                    iop->tempfile = (char *)NULL;
X                    iop->type = tp->type;
X                    strcpy(iop->file,tp1->name);
X                    if (iop->type == SYM_RT ||
X                        iop->type == SYM_RRT ||
X                        iop->type == SYM_RBT)
X                        iop->fd = 1;
X                    else    iop->fd = 0;
X                    if (tp2 != (struct token *)NULL)
X                        iop->fd = atoi(tp2->name);
X                    if (pp->io == (struct iotoken *)NULL)
X                        pp->io = pp->io_end = iop;
X                    else
X                    {   /* tack onto end */
X                        pp->io_end->next = iop;
X                        pp->io_end = iop;
X                    }
X                    if (iop->type == SYM_LLT || iop->type == SYM_LLTL)
X                        lex_heredoc_save(iop);
X                }
X                if (tp2 != (struct token *)NULL)
X                    free(tp2);
X                free(tp1);
X                free(tp);
X#ifdef  GEMDOS
X                if (iop->fd > MAXUFD ||
X                    ((iop->type == SYM_LBT || iop->type == SYM_RBT) &&
X                    atoi(iop->file) > MAXUFD))
X                {   /* are fds too big? */
X                    errmsg(0,LOC("lex_phrase"),"can't redirect fds > %d in GEMDOS",MAXUFD);
X                }
X#endif  /* GEMDOS */
X                break;
X            case SYM_MARKER:    /* special boundary marker */
X                pp->type = tp;
X                dont_expand = save_expflag;
X                return pp;
X            case SYM_EOL:       /* end of line */
X            case SYM_DSEMI:     /* double semicolon */
X            case SYM_SEMI:      /* semicolon */
X            case SYM_BACK:      /* background */
X            case SYM_PIPE:      /* piping together */
X            case SYM_ORIF:      /* or conditional */
X            case SYM_ANDIF:     /* and conditional */
X            case SYM_LPAREN:    /* start of group */
X            case SYM_RPAREN:    /* end of group */
X            case SYM_BQUOTE:    /* end of substitute group */
X                pp->type = tp;
X                if (tp->type == SYM_LPAREN)
X                    lex_parens(level,pp);
X                dont_expand = save_expflag;
X                return lex_paragraph(level,pp);
X            default:
X                if (tp->type == SYM_KEYWORD &&
X                   (pp->body == (struct token *)NULL || flag_keywords))
X                {   /* define a variable? */
X                    if (dont_expand)
X                    {   /* save for later? */
X                        if (pp->var == (struct token *)NULL)
X                            pp->var = pp->var_end = tp;
X                        else
X                        {   /* tack onto end */
X                            pp->var_end->next = tp;
X                            pp->var_end = tp;
X                        }
X                    }
X                    else
X                    {   /* expand the variable */
X                        var_define(tp->name,0,0);
X                        free(tp);
X                    }
X                    break;
X                }
X                if (pp->body == (struct token *)NULL)
X                {   /* at start of list */
X                    pp->body = pp->body_end = tp;
X                }
X                else
X                {   /* tack onto end */
X                    pp->body_end->next = tp;
X                    pp->body_end = tp;
X                }
X                break;
X        }
X        if (tp == (struct token *)NULL)
X            break;
X    }
X    dont_expand = save_expflag;
X    return pp;
X}   /* end of lex_phrase */
X
Xstruct phrase *lex_sentence(level)
Xint level;
X{
X    register struct phrase *sp;
X    struct phrase *pp,*xpp;
X    struct iotoken *iop;
X    int pcount;
X
X    sp = new_phrase();
X    if (sp == (struct phrase *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("lex_sentence"));
X        return sp;
X    }
X    while ((pp = lex_phrase(level,1)) != (struct phrase *)NULL)
X    {   /* fill out the sentence */
X        if (sp->group == (struct phrase *)NULL &&
X            pp->type != (struct token *)NULL &&
X            pp->type->type == SYM_EOL &&
X            pp->io == (struct iotoken *)NULL &&
X            pp->var == (struct token *)NULL &&
X            pp->body == (struct token *)NULL)
X        {   /* ignore empty line */
X            phrase_free(pp);
X            continue;
X        }
X        if (sp->group_end != (struct phrase *)NULL &&
X            sp->group_end->type != (struct token *)NULL &&
X            sp->group_end->type->type == SYM_PIPE &&
X            pp->type != (struct token *)NULL &&
X            pp->type->type == SYM_EOL &&
X            pp->body == (struct token *)NULL)
X        {   /* reject this phrase, to finish pipe */
X            level = 1;
X            phrase_free(pp);
X            continue;
X        }
X        if (sp->group == (struct phrase *)NULL)
X            sp->group = sp->group_end = pp;
X        else
X        {   /* tack onto end */
X            sp->group_end->next = pp;
X            sp->group_end = pp;
X        }
X        if (pp->group != (struct phrase *)NULL)
X            xpp = pp->group_end;
X        else xpp = pp;
X        if (xpp->type == (struct token *)NULL ||
X            xpp->type->type == SYM_MARKER ||
X            xpp->type->type == SYM_BQUOTE ||
X            xpp->type->type == SYM_EOL)
X        {   /* end of sentence? */
X            pcount = 0;
X            for (pp = sp->group; pp != (struct phrase *)NULL; pp = pp->next)
X            {   /* look for, set up pipes */
X                if ((pp->type != (struct token *)NULL &&
X                    pp->type->type == SYM_PIPE) ||
X                   (pp->group != (struct phrase *)NULL &&
X                    pp->group_end->type != (struct token *)NULL &&
X                    pp->group_end->type->type == SYM_PIPE))
X                {   /* found a pipe */
X                    iop = new_iotoken(16
X#ifdef  GEMDOS
X                                        +strlen(base_env.tmpdir)
X#endif  /* GEMDOS */
X                                                                                 );
X                    if (iop == (struct iotoken *)NULL)
X                    {   /* enough memory */
X                        errmsg(SHERR_NOMEM,LOC("lex_sentence"));
X                        break;
X                    }
X                    iop->fd = 1;
X#ifdef  GEMDOS
X                    sprintf(iop->file,"%spipe%d.tmp",base_env.tmpdir,pcount);
X#else
X                    sprintf(iop->file,"pipe%d",pcount);
X#endif  /* GEMDOS */
X                    iop->type = SYM_PIPE;
X                    if (pp->io == (struct iotoken *)NULL)
X                        pp->io = pp->io_end = iop;
X                    else
X                    {   /* stick on front */
X                        iop->next = pp->io;
X                        pp->io = iop;
X                    }
X                    if (pp->next == (struct phrase *)NULL)
X                    {   /* something to pipe to */
X                        errmsg(0,LOC("lex_sentence"),"missing pipe destination");
X                        continue;
X                    }
X                    iop = new_iotoken(16
X#ifdef  GEMDOS
X                                                                  +strlen(base_env.tmpdir)
X#endif  /* GEMDOS */
X                                                                                 );
X                    if (iop == (struct iotoken *)NULL)
X                    {   /* enough memory */
X                        errmsg(SHERR_NOMEM,LOC("lex_sentence"));
X                        break;
X                    }
X                    iop->fd = 0;
X#ifdef  GEMDOS
X                    sprintf(iop->file,"%spipe%d.tmp",base_env.tmpdir,pcount);
X#else
X                    sprintf(iop->file,"pipe%d",pcount);
X#endif  /* GEMDOS */
X                    iop->type = SYM_PIPER;
X                    if (pp->next->io == (struct iotoken *)NULL)
X                        pp->next->io = pp->next->io_end = iop;
X                    else
X                    {   /* stick on front */
X                        iop->next = pp->next->io;
X                        pp->next->io = iop;
X                    }
X                }
X                pcount++;
X            }
X            pp = sp->group_end;
X            if (pp->type != (struct token *)NULL &&
X                pp->type->type == SYM_BQUOTE)
X            {   /* need to capture output? */
X                iop = new_iotoken(16
X#ifdef  GEMDOS
X                                                              +strlen(base_env.tmpdir)
X#endif  /* GEMDOS */
X                                                                             );
X                if (iop == (struct iotoken *)NULL)
X                {   /* enough memory? */
X                    errmsg(SHERR_NOMEM,LOC("lex_sentence"));
X                }
X                else
X                {   /* fill out, tack on io block */
X                    iop->fd = 1;
X                    iop->type = SYM_RT;
X#ifdef  GEMDOS
X                    sprintf(iop->file,"%stemp%d.tmp",base_env.tmpdir,base_env.temp_count++);
X#else
X                    sprintf(iop->file,"/tmp/sh%d.temp",base_env.temp_count++);
X#endif  /* GEMDOS */
X                    if (pp->io == (struct iotoken *)NULL)
X                        pp->io = pp->io_end = iop;
X                    else
X                    {   /* push onto front */
X                        iop->next = pp->io;
X                        pp->io = iop;
X                    }
X                }
X            }
X            return sp;
X        }
X    }
X    free(sp);
X    return (struct phrase *)NULL;
X}   /* end of lex_sentence */
X
Xint lex_sempty(savep)
Xregister struct strsave *savep;
X{
X    if (strings == (struct strsave *)NULL || strings == savep)
X        return 1;
X    if (strings->next == (struct strsave *)NULL && strings->ptr[0] == ' ' && strings->ptr[1] == '\0')
X        return 1;
X    if (strings->next == savep && strings->ptr[0] == ' ' && strings->ptr[1] == '\0')
X        return 1;
X    return 0;
X}   /* end of lex_sempty */
X
Xstruct token *lex_reparse_tokens(ftp,strip)
Xstruct token *ftp;
Xint strip;
X{
X    register struct token *tp,*stp;
X    struct token *ltp;
X    register struct strsave *sp;
X    struct strsave *savep,*osp,*lsp;
X
X    savep = strings;
X    for (tp = ftp; tp != (struct token *)NULL; tp = tp->next)
X    {   /* go down initial token chain */
X        if (io_pushtoken(tp->name,strip) < 0)
X        {   /* did it work? */
X            for (sp = strings; sp != savep; )
X            {   /* eliminate any junk */
X                osp = sp;
X                sp = sp->next;
X                if (osp->string != (char *)NULL)
X                    free(osp->string);
X                free(osp);
X                strings = savep;
X            }
X            errmsg(0,LOC("lex_reparse_tokens"),"bad return");
X            return (struct token *)NULL;
X        }
X    }
X    osp = (struct strsave *)NULL;
X    for (sp = strings; strings != savep && sp != (struct strsave *)NULL; )
X    {   /* reverse leading part of list */
X        lsp = sp;
X        sp = sp->next;
X        lsp->next = osp;
X        osp = lsp;
X        if (sp == savep)
X            break;
X    }
X    if (osp != (struct strsave *)NULL)
X    {   /* if some items were reversed */
X        strings = osp;
X        for (lsp = osp; lsp->next != (struct strsave *)NULL; lsp = lsp->next)
X            /* do nothing */;
X        lsp->next = savep;
X    }
X
X    stp = ltp = (struct token *)NULL;
X    while (!lex_sempty(savep) && ((tp = lex_token(1)) != (struct token *)NULL))
X    {   /* build the new chain */
X        if (stp == (struct token *)NULL)
X            stp = ltp = tp;
X        else
X        {   /* tack onto end */
X            ltp->next = tp;
X            ltp = tp;
X        }
X    }
X    return stp;
X}   /* end of lex_reparse_tokens */
END_OF_FILE
if test 37330 -ne `wc -c <'lex.c'`; then
    echo shar: \"'lex.c'\" unpacked with wrong size!
fi
# end of 'lex.c'
fi
echo shar: End of archive 9 \(of 11\).
cp /dev/null ark9isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.  It is NOT yet
bug free; I am VERY interested in fixes; enhancements, etc.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 10 (of 11)."
# Contents:  exec.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:04:01 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'exec.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'exec.c'\"
else
echo shar: Extracting \"'exec.c'\" \(45883 characters\)
sed "s/^X//" >'exec.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Command Execution
X *
X * $Id: exec.c,v 1.11 89/03/05 15:38:22 dclemans Exp $
X *
X * $Log:	exec.c,v $
X * Revision 1.11  89/03/05  15:38:22  dclemans
X * more fixes
X * 
X * Revision 1.10  89/02/25  17:40:03  dclemans
X * miscellaneous bug fixes/speedups
X * 
X * Revision 1.9  89/02/22  21:31:56  dclemans
X * Implement simple background job monitoring facility
X * 
X * Revision 1.8  89/02/22  13:19:03  dclemans
X * Implement $!
X * 
X * Revision 1.7  89/02/22  10:16:05  dclemans
X * Fix bugs with process waiting, pid reporting, parsing of &
X * 
X * Revision 1.6  89/02/21  20:29:52  dclemans
X * Fix bug with shell variable references in history lists.
X * 
X * Revision 1.5  89/02/21  08:36:36  dclemans
X * Implement pseudo-signals ERR, EXIT, DEBUG
X * 
X * Revision 1.4  89/02/20  20:14:25  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X#ifndef GEMDOS
X#include <errno.h>
X#include <sys/types.h>
X#ifndef USG
X#include <sys/wait.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X#include <sys/file.h>
X#else
X#include <time.h>
X#include <fcntl.h>
X#endif  /* USG */
X#endif  /* GEMDOS */
X
X#ifdef  MWC_ARGV
Xstatic char mwc_iovec[] = "????????????????????????????";
X#endif  /* MWC_ARGV */
X
X#ifndef GEMDOS
Xstatic struct procs *waitStack = (struct procs *)NULL;
Xstatic struct procs *pipeStack = (struct procs *)NULL;
Xstatic struct procs *pipeStack_end;
Xstatic struct procs *bgJobs = (struct procs *)NULL;
X#endif  /* GEMDOS */
X
Xint exec_builtin(cmd,pp,iosetup)
Xchar *cmd;
Xstruct phrase *pp;
Xint iosetup;
X{
X    register int i,rc;
X
X    if (pp->body == (struct token *)NULL)
X        return -1;
X    for (i = 0; cmd_builtin[i].name != (char *)NULL; i++)
X    {   /* is this a builtin command? */
X        rc = strcmp(cmd,cmd_builtin[i].name);
X        if (rc > 0)
X            continue;
X        if (rc < 0)
X            return 0;
X        if (!iosetup)
X        {   /* make our own environment? */
X            if (main_iopush() < 0)
X            {   /* if couldn't save i/o env */
X                /* message already printed */
X                main_iopop();
X                cmd_lastrc = -1;
X                return -1;
X            }
X        }
X        cmd_lastrc = (*cmd_builtin[i].cmd)(pp);
X        if (!iosetup)
X            main_iopop();
X        return 1;
X    }
X    return 0;
X}   /* end of exec_builtin */
X
Xstruct function *func_get(name)
Xchar *name;
X{
X    register struct function *sfp;
X    register int rc;
X
X    for (sfp = base_env.func_table; sfp != (struct function *)NULL; )
X    {   /* look for where to put the function */
X        rc = strcmp(name,sfp->name);
X        if (rc == 0)
X        {   /* found it */
X            return sfp;
X        }
X        else if (rc < 0)
X        {   /* go down the left side? */
X            if (sfp->left == (struct function *)NULL)
X                break;
X            sfp = sfp->left;
X        }
X        else
X        {   /* go down right side */
X            if (sfp->right == (struct function *)NULL)
X                break;
X            sfp = sfp->right;
X        }
X    }
X    return (struct function *)NULL;
X}   /* end of func_get */
X
Xint exec_function(cmd,fpp)
Xchar *cmd;
Xstruct phrase *fpp;
X{
X    register struct function *sfp;
X    register struct phrase *pp,*cpp;
X    struct phrase *lpp;
X
X    sfp = func_get(cmd);
X    if (sfp != (struct function *)NULL)
X    {   /* if the function was found */
X        if (main_iopush() < 0)
X        {   /* if couldn't save i/o env */
X            /* message already printed */
X            main_iopop();
X            cmd_lastrc = -1;
X            return -1;
X        }
X        if (main_varpush() < 0)
X        {   /* if couldn't save var env */
X            /* message already printed */
X            main_iopop();
X            cmd_lastrc = -1;
X            return -1;
X        }
X        if (main_argspush(fpp) < 0)
X        {   /* if couldn't save args env */
X            /* message already printed */
X            main_iopop();
X            main_varpop();
X            cmd_lastrc = -1;
X            return -1;
X        }
X
X        if (sfp->code != (struct phrase *)NULL)
X        for (pp = sfp->code->group; pp != (struct phrase *)NULL; )
X        {   /* execute the sentence */
X            cpp = copy_phrase(pp,1,0,1);
X            lpp = pp;
X            pp = pp->next;
X            if (cpp == (struct phrase *)NULL)
X            {   /* enough memory? */
X                /* message already printed */
X                main_iopop();
X                main_varpop();
X                main_argspop();
X                cmd_lastrc = -1;
X                return -1;
X            }
X            if (flag_echoexec)
X                phrase_dump(cpp,0,0);
X            exec_phrase(cpp,0);
X            if (cpp->body != (struct token *)NULL)
X                var_define0("_",cpp->body_end->name,0);
X            phrase_free(cpp);
X            if (cmd_returnexit)
X                break;
X
X            if (lpp->type != (struct token *)NULL) switch (lpp->type->type)
X            {   /* any special handling? */
X                case SYM_ANDIF:
X                    if (cmd_lastrc != 0 && pp != (struct phrase *)NULL)
X                        pp = pp->next;
X                    break;
X                case SYM_ORIF:
X                    if (cmd_lastrc == 0 && pp != (struct phrase *)NULL)
X                        pp = pp->next;
X                    break;
X            }
X            if (pp == (struct phrase *)NULL)
X                break;
X        }
X        cmd_returnexit = 0;
X        main_iopop();
X        main_varpop();
X        main_argspop();
X        force_signal("EXIT");
X        return 1;
X    }
X    return 0;
X}   /* end of exec_function */
X
Xvoid exec_alias(name,path)
Xchar *name;
Xregister char *path;
X{
X    register char *newpath;
X    register int plen;
X
X    plen = strlen(path);
X    if (strchr(path,DIR_SEPARATOR) == (char *)NULL
X#ifdef  GEMDOS
X        || strchr(path,':') == (char *)NULL
X#endif  /* GEMDOS */
X                            )
X    {   /* if only a partial path name, add on currdir */
X        plen += strlen(base_env.dir->current)+1;
X    }
X    if (flag_cmdhash || alias_tracked(name))
X    {   /* if hashing commands */
X        newpath = new_string(plen+1);
X        if (newpath == (char *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("exec_alias"));
X            return;
X        }
X        if (plen != strlen(path))
X            sprintf(newpath,"%s%c%s",base_env.dir->current,DIR_SEPARATOR,path);
X        else    strcpy(newpath,path);
X        alias_sdefine(name,newpath,TYPE_TRACKED);
X        free(newpath);
X    }
X}   /* end of exec_alias */
X
X#ifndef GEMDOS
Xstatic int savepid(pp,pid)
Xregister struct phrase *pp;
Xint pid;
X{
X    register struct procs *newWait,*ws;
X    char buffer[64];
X
X    if (pp == (struct phrase *)NULL)
X        return;
X    if (pp->type != (struct token *)NULL && pp->type->type != SYM_BACK && pp->type->type != SYM_PIPE)
X    {   /* if need to wait for this job */
X        for (ws = pipeStack; ws != (struct procs *)NULL; )
X        {   /* this job included some previous pipe stuff */
X            newWait = ws;
X            ws = ws->next;
X            newWait->next = waitStack;
X            waitStack = newWait;
X        }
X        pipeStack = pipeStack_end = (struct procs *)NULL;
X        newWait = (struct procs *)malloc(sizeof(*newWait));
X        if (newWait == (struct procs *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("exec_real"));
X            return -1;
X        }
X        newWait->pid = pid;
X        newWait->next = waitStack;
X        waitStack = newWait;
X    }
X    else if (pp->type != (struct token *)NULL && pp->type->type == SYM_PIPE)
X    {   /* save a pipe pid for later reference */
X        newWait = (struct procs *)malloc(sizeof(*newWait));
X        if (newWait == (struct procs *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("exec_real"));
X            return -1;
X        }
X        newWait->pid = pid;
X        newWait->next = (struct procs *)NULL;
X        if (pipeStack == (struct procs *)NULL)
X            pipeStack = pipeStack_end = newWait;
X        else
X        {   /* tack onto end */
X            pipeStack_end->next = newWait;
X            pipeStack_end = newWait;
X        }
X    }
X    else if (pp->type != (struct token *)NULL && pp->type->type == SYM_BACK)
X    {   /* say what we did */
X        for (ws = pipeStack; ws != (struct procs *)NULL; ws = ws->next)
X        {   /* this background job included some previous pipe stuff */
X            sprintf(buffer,"%d ",ws->pid);
X            io_writestring(0,buffer);
X        }
X        bgJobs = pipeStack;
X        pipeStack = pipeStack_end = (struct procs *)NULL;
X        newWait = (struct procs *)malloc(sizeof(*newWait));
X        if (newWait == (struct procs *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("exec_real"));
X            return -1;
X        }
X        newWait->pid = pid;
X        newWait->next = bgJobs;
X        bgJobs = newWait;
X        base_env.background_pid = pid;
X        sprintf(buffer,"%d\n",pid);
X        io_writestring(0,buffer);
X    }
X}   /* end of savepid */
X#endif  /* GEMDOS */
X
X#ifdef  GEMDOS
Xint exec_real(cmd,pp)
Xchar *cmd;
Xstruct phrase *pp;
X{
X    char cmdtail[128];
X    struct token *tp;
X    char *arg,*env,*newenv,*envp;
X    int length,rlength;
X    int warn127;
X
X    var_define0("_",cmd,0);
X    warn127 = 0;
X    cmdtail[1] = '\0';
X    for (tp = pp->body->next; tp != (struct token *)NULL; tp = tp->next)
X    {   /* build a GEMDOS command tail */
X        arg = strcopy(tp->name);
X        if (arg == (char *)NULL)
X        {   /* enough memory? */
X            /* message already printed */
X            return -1;
X        }
X        stripquotes(arg);
X        if (!warn127 && ((strlen(&cmdtail[1])+strlen(arg)+1) > 127))
X        {   /* will it fit? */
X            errmsg(0,LOC("exec_real"),"warning: command args > 127 chars, command may not work");
X            warn127++;
X            break;
X        }
X        if (cmdtail[1] != '\0')
X            strcat(&cmdtail[1]," ");
X        strcat(&cmdtail[1],arg);
X        free(arg);
X    }
X    cmdtail[0] = strlen(&cmdtail[1]);
X    /* build environment variables */
X    env = var_makeenv();
X#ifdef  MWC_ARGV
X    /* add in MWC ARGV */
X    /* iovector: C=console, A=aux, P=printer, F=file */
X    length = 2;
X    if (env != (char *)NULL)
X    {   /* find out how big the environment is */
X        for (envp = env; ; envp++)
X        {   /* until we reach the end */
X            if (envp[0] == '\0' && envp[1] == '\0')
X                break;
X            length++;
X        }
X    }
X    rlength = length;
X    length += 34;       /* add in ARGV=<iovector> */
X    for (tp = pp->body; tp != (struct token *)NULL; tp = tp->next)
X    {   /* get total length of args */
X        length += strlen(tp->name)+1;
X    }
X    newenv = new_string(length);
X    if (newenv == (char *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("exec_real"));
X        if (env != (char *)NULL)
X            free(env);
X        return -1;
X    }
X    newenv[0] = newenv[1] = '\0';
X    if (env != (char *)NULL)
X        memcpy(newenv,env,rlength);
X    for (envp = newenv; ; envp++)
X    {   /* scan for end */
X        if (envp[0] == '\0' && envp[1] == '\0')
X            break;
X    }
X    if (envp != newenv)
X        envp++;
X    sprintf(envp,"ARGV=%s",mwc_iovec);
X    while (*envp)
X        envp++;
X    *envp++;
X    for (tp = pp->body; tp != (struct token *)NULL; tp = tp->next)
X    {   /* now put the args in */
X        strcpy(envp,tp->name);
X        stripquotes(envp);
X        while (*envp)
X            envp++;
X        envp++;
X    }
X    *envp = '\0';
X    if (env != (char *)NULL)
X        free(env);
X    env = newenv;
X#endif  /* MWC_ARGV */
X
X    cmd_lastrc = Pexec(0,cmd,cmdtail,env);
X    if (env != (char *)NULL)
X        free(env);
X    if (NO_FILE(cmd_lastrc))
X        return 0;
X    if (NOT_EXEC(cmd_lastrc) && base_env.shellname != (char *)NULL && strlen(base_env.shellname) != 0)
X    {   /* try as shell file */
X        if (strcmp(pp->body->name,base_env.shellname) == 0)
X            return 0;
X        tp = new_token(strlen(base_env.shellname));
X        if (tp == (struct token *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("exec_real"));
X            return 0;
X        }
X        tp->type = SYM_WORD;
X        strcpy(tp->name,base_env.shellname);
X        tp->next = pp->body;
X        pp->body = tp;
X        length = exec_phrase(pp,1);
X        if (pp->body != (struct token *)NULL)
X            var_define0("_",pp->body_end->name,0);
X        return length;
X    }
X    return 1;
X}   /* end of exec_real */
X#else
Xint exec_real(cmd,pp)
Xchar *cmd;
Xstruct phrase *pp;
X{
X    register struct token *tp;
X    register char *cp;
X    int pid;
X    char *envp,*first;
X    char **arg_vector,**env_vector;
X    int arg_count;
X
X    arg_count = 0;
X    for (tp = pp->body; tp != (struct token *)NULL; tp = tp->next)
X        arg_count++;
X    arg_vector = new_argv(arg_count);
X    if (arg_vector == (char **)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("exec_real"));
X        return -1;
X    }
X    arg_count = 0;
X    for (tp = pp->body; tp != (struct token *)NULL; tp = tp->next)
X    {   /* copy in args */
X        stripquotes(tp->name);
X        arg_vector[arg_count++] = tp->name;
X    }
X    arg_vector[arg_count] = (char *)NULL;
X
X    arg_count = 0;
X    envp = var_makeenv();
X    for (cp = envp; ; cp++)
X    {   /* until double null is found */
X        if (*cp == '\0')
X        {   /* found end of one env string */
X            arg_count++;
X            if (*(cp+1) == '\0')
X                break;
X        }
X    }
X    env_vector = new_argv(arg_count);
X    if (env_vector == (char **)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("exec_real"));
X        free(arg_vector);
X        return -1;
X    }
X    arg_count = 0;
X    first = envp;
X    for (cp = envp; ; cp++)
X    {   /* until double null is found */
X        if (*cp == '\0')
X        {   /* found end of one env string */
X            env_vector[arg_count++] = first;
X            first = cp+1;
X            if (*first == '\0')
X                break;
X        }
X    }
X    env_vector[arg_count] = (char *)NULL;
X
X    /* exec cmd */
X    pid = fork();
X    if (pid < 0)
X    {   /* can we get a child process? */
X        errmsg(0,LOC("exec_real"),"can't fork to run command");
X        free(env_vector);
X        if (envp != (char *)NULL)
X            free(envp);
X        free(arg_vector);
X        return -1;
X    }
X    if (pid == 0)
X    {   /* this is the child; run the command */
X        cmd_lastrc = execve(cmd,arg_vector,env_vector);
X        if (NO_FILE(cmd_lastrc))
X            exit(-1);
X        if (NOT_EXEC(cmd_lastrc) && base_env.shellname != (char *)NULL && strlen(base_env.shellname) != 0)
X        {   /* try as shell file */
X            if (strcmp(pp->body->name,base_env.shellname) == 0)
X                return 0;
X            tp = pp->body;
X            pp->body = tp->next;
X            free(tp);
X            tp = new_token(strlen(cmd));
X            if (tp == (struct token *)NULL)
X            {   /* enough memory? */
X                errmsg(SHERR_NOMEM,LOC("exec_real"));
X                exit(-1);
X            }
X            tp->type = SYM_WORD;
X            strcpy(tp->name,cmd);
X            tp->next = pp->body;
X            pp->body = tp;
X            tp = new_token(strlen(base_env.shellname));
X            if (tp == (struct token *)NULL)
X            {   /* enough memory? */
X                errmsg(SHERR_NOMEM,LOC("exec_real"));
X                exit(-1);
X            }
X            tp->type = SYM_WORD;
X            strcpy(tp->name,base_env.shellname);
X            tp->next = pp->body;
X            pp->body = tp;
X            pid = exec_phrase(pp,1);
X            if (pp->body != (struct token *)NULL)
X                var_define0("_",pp->body_end->name,0);
X            exit(pid);
X        }
X        exit(-1);
X    }
X
X    free(env_vector);
X    if (envp != (char *)NULL)
X        free(envp);
X    free(arg_vector);
X    savepid(pp,pid);
X
X    return pid;
X}   /* end of exec_real */
X#endif  /* GEMDOS */
X
Xchar *exec_pathsearch(name)
Xchar *name;
X{
X    register char *pf,*pl,*p;
X    char sep;
X#ifdef  GEMDOS
X    char *spf,*spl,*lp;
X#endif  /* GEMDOS */
X    char buffer[BUFSIZ];
X
X#ifdef  GEMDOS
X    sep = ',';
X    spf = spl = base_env.exec_suff;
X#else
X    sep = ':';
X#endif  /* GEMDOS */
X    if (isfullpath(name))
X    {   /* if don't need a path search */
X        strcpy(buffer,name);
X#ifdef  GEMDOS
X        if (strchr(name,'.') == (char *)NULL)
X        {   /* need to add a suffix? */
X            for (lp = buffer; *lp; lp++)
X                /* do nothing */;
X            for (;;)
X            {   /* while there are suffixes */
X                if (*spl != ',' && *spl != '\0')
X                {   /* not there yet? */
X                    spl++;
X                    continue;
X                }
X                strncpy(lp,spf,(int)(spl - spf));
X                lp[(int)(spl - spf)] = '\0';
X                if (isexec(buffer))
X                    return strcopy(buffer);
X                if (*spl == '\0')
X                    break;
X                spl++;
X                spf = spl;
X            }
X        }
X        else if (isexec(buffer))
X            return strcopy(buffer);
X#else
X        if (isexec(buffer))
X            return strcopy(buffer);
X#endif  /* GEMDOS */
X    }
X    pf = pl = base_env.exec_path;
X    for (;;)
X    {   /* look through the path options... */
X        if (*pl != sep && *pl != '\0')
X        {   /* keep going? */
X            pl++;
X            continue;
X        }
X        strncpy(buffer,pf,(int)(pl - pf));
X        buffer[(int)(pl - pf)] = '\0';
X        if ((int)(pl - pf) != 0)
X        {   /* need a dir separator? */
X            for (p = buffer; *p; p++)
X                /* do nothing */;
X            if (p[-1] != DIR_SEPARATOR)
X                *p++ = DIR_SEPARATOR;
X            *p = '\0';
X        }
X        strcat(buffer,name);
X#ifdef  GEMDOS
X        if (strchr(name,'.') == (char *)NULL)
X        {   /* need to add a suffix? */
X            for (lp = buffer; *lp; lp++)
X                /* do nothing */;
X            for (;;)
X            {   /* while there are suffixes */
X                if (*spl != ',' && *spl != '\0')
X                {   /* not there yet? */
X                    spl++;
X                    continue;
X                }
X                strncpy(lp,spf,(int)(spl - spf));
X                lp[(int)(spl - spf)] = '\0';
X                if (isexec(buffer))
X                {   /* if it apparently worked */
X                    exec_alias(name,buffer);
X                    return strcopy(buffer);
X                }
X                if (*spl == '\0')
X                    break;
X                spl++;
X                spf = spl;
X            }
X        }
X        else if (isexec(buffer))
X        {   /* if it apparently worked */
X            exec_alias(name,buffer);
X            return strcopy(buffer);
X        }
X#else
X        if (isexec(buffer))
X        {   /* if it apparently worked */
X            exec_alias(name,buffer);
X            return strcopy(buffer);
X        }
X#endif  /* GEMDOS */
X        if (*pl == '\0')
X            break;
X        pl++;
X        pf = pl;
X#ifdef  GEMDOS
X        spf = spl = base_env.exec_suff;
X#endif  /* GEMDOS */
X    }
X    return (char *)NULL;
X}   /* end of exec_pathsearch */
X
Xint exec_normal(cmd,pp)
Xchar *cmd;
Xstruct phrase *pp;
X{
X    register char *command;
X    int rc;
X
X    command = exec_pathsearch(cmd);
X    if (command == (char *)NULL)
X        return 0;
X    rc = exec_real(command,pp);
X    free(command);
X    return rc;
X}   /* end of exec_normal */
X
X#ifdef  GEMDOS
Xint exec_open(file,type,fd)
Xregister char *file;
Xint type;
Xint fd;
X{
X    int rc,rc1;
X
X    if (strcmp(file,"tty:") == 0 ||
X        strcmp(file,"TTY:") == 0 ||
X        strcmp(file,"CON:") == 0 ||
X        strcmp(file,"con:") == 0)
X    {   /* get console? */
X        rc = Fforce(fd,base_env.fd_con);
X        if (rc < 0)
X            errmsg(SHERR_PERROR,LOC("exec_open"),"can't setup %d<->con:",fd);
X#ifdef  MWC_ARGV
X        else mwc_iovec[fd] = 'C';
X#endif  /* MWC_ARGV */
X        return rc;
X    }
X    if (strcmp(file,"aux:") == 0 ||
X        strcmp(file,"AUX:") == 0)
X    {   /* get aux port */
X        rc = Fforce(fd,base_env.fd_aux);
X        if (rc < 0)
X            errmsg(SHERR_PERROR,LOC("exec_open"),"can't setup %d<->aux:",fd);
X#ifdef  MWC_ARGV
X        else mwc_iovec[fd] = 'A';
X#endif  /* MWC_ARGV */
X        return rc;
X    }
X    if (strcmp(file,"prn:") == 0 ||
X        strcmp(file,"PRN:") == 0 ||
X        strcmp(file,"prt:") == 0 ||
X        strcmp(file,"PRT:") == 0)
X    {   /* get printer */
X        rc = Fforce(fd,base_env.fd_prn);
X        if (rc < 0)
X            errmsg(SHERR_PERROR,LOC("exec_open"),"can't setup %d<->prn:",fd);
X#ifdef  MWC_ARGV
X        else mwc_iovec[fd] = 'P';
X#endif  /* MWC_ARGV */
X        return rc;
X    }
X    rc = -1;
X    switch (type)
X    {   /* what kind of open? */
X        case 0:
X            rc = Fopen(file,0);
X            break;
X        case 1:
X            Fdelete(file);
X            rc = Fcreate(file,0);
X            break;
X        case 2:
X            rc = Fopen(file,1);
X            break;
X    }
X    if (rc < 0)
X    {   /* if didn't work */
X        errmsg(SHERR_PERROR,LOC("exec_open"),"can't open %s",file);
X        return rc;
X    }
X    rc1 = Fforce(fd,rc);
X    if (rc1 < 0)
X    {   /* if didn't work */
X        Fclose(rc);
X        errmsg(SHERR_PERROR,LOC("exec_open"),"can't associate %d<->%s",fd,file);
X        return rc1;
X    }
X    Fclose(rc);
X#ifdef  MWC_ARGV
X    mwc_iovec[fd] = 'F';
X#endif  /* MWC_ARGV */
X    return 0;
X}   /* end of exec_open */
X#endif  /* GEMDOS */
X
Xstatic long fds_opened = 0;
X
Xint exec_ioredir(io)
Xstruct iotoken *io;
X{
X    register struct iotoken *iop;
X
X#ifdef  GEMDOS
X    int rc,tfd,sfd;
X
X    for (iop = io; iop != (struct iotoken *)NULL; iop = iop->next)
X    {   /* for each thing we need to switch */
X        if (iop->fd > MAXUFD)
X            continue;
X        switch (iop->type)
X        {   /* what kind of redirection? */
X            case SYM_LT:
X            case SYM_PIPER:
X                rc = exec_open(iop->file,0,iop->fd);
X                break;
X            case SYM_LLT:
X            case SYM_LLTL:
X                rc = exec_open(iop->tempfile,0,iop->fd);
X                break;
X            case SYM_LBT:
X            case SYM_RBT:
X                tfd = atoi(iop->file);
X                if (tfd > MAXUFD)
X                    continue;
X                rc = Fdup(tfd);
X                if (rc < 0)
X                {   /* did it work? */
X                    errmsg(SHERR_PERROR,LOC("exec_ioredir"),"can't duplicate %d",tfd);
X                    break;
X                }
X                sfd = rc;
X                rc = Fforce(iop->fd,sfd);
X                Fclose(sfd);
X                if (rc < 0)
X                    errmsg(SHERR_PERROR,LOC("exec_ioredir"),"can't associate %d<->%d",iop->fd,tfd);
X                break;
X            case SYM_RT:
X            case SYM_PIPE:
X                rc = exec_open(iop->file,1,iop->fd);
X                if (iop->type == SYM_PIPE)
X                    delete_later(iop->file);
X                break;
X            case SYM_RRT:
X                rc = exec_open(iop->file,2,iop->fd);
X                if (rc >= 0)
X                    rc = Fseek(0L,iop->fd,2);
X                break;
X        }
X        if (rc < 0)
X        {   /* couldn't set up the io */
X            /* message already printed */
X            return rc;
X        }
X        fds_opened |= (1L << (long)iop->fd);
X    }
X    if (!(fds_opened & (1L<<0L)))
X    {   /* was stdin setup */
X        rc = Fforce(0,base_env.io->input);
X        if (rc < 0)
X        {   /* did it work? */
X            errmsg(SHERR_PERROR,LOC("exec_ioredir"),"can't set default stdin");
X            return rc;
X        }
X#ifdef  MWC_ARGV
X        else mwc_iovec[0] = 'C';
X#endif  /* MWC_ARGV */
X        fds_opened |= (1L<<0L);
X    }
X    if (!(fds_opened & (1L<<1L)))
X    {   /* was stdout setup */
X        rc = Fforce(1,base_env.io->output);
X        if (rc < 0)
X        {   /* did it work? */
X            errmsg(SHERR_PERROR,LOC("exec_ioredir"),"can't set default stdout");
X            return rc;
X        }
X#ifdef  MWC_ARGV
X        else mwc_iovec[1] = 'C';
X#endif  /* MWC_ARGV */
X        fds_opened |= (1L<<1L);
X    }
X    if (!(fds_opened & (1L<<2L)))
X    {   /* was stderr setup */
X        rc = Fforce(2,base_env.io->errout);
X        if (rc < 0)
X        {   /* did it work? */
X            errmsg(SHERR_PERROR,LOC("exec_ioredir"),"can't set default stderr");
X            return rc;
X        }
X#ifdef  MWC_ARGV
X        else mwc_iovec[2] = 'C';
X#endif  /* MWC_ARGV */
X        fds_opened |= (1L<<2L);
X    }
X    return 0;
X#else
X    int rc,tfd;
X    static int pipefds[2];
X
X    for (iop = io; iop != (struct iotoken *)NULL; iop = iop->next)
X    {   /* for each thing we need to switch */
X        if (iop->fd > MAXUFD)
X            continue;
X        switch (iop->type)
X        {   /* what kind of redirection? */
X            case SYM_PIPER:
X                rc = 0;
X                if (pipefds[0] < 0)
X                {   /* pipe not created? */
X                    errmsg(0,LOC("exec_ioredir"),"no pipe created to read from");
X                    rc = -1;
X                    break;
X                }
X                if (pipefds[0] != iop->fd)
X                {   /* if need to move fd */
X                    rc = dup2(pipefds[0],iop->fd);
X                    close(pipefds[0]);
X                    main_fdput(pipefds[0]);
X                }
X                pipefds[0] = pipefds[1] = -1;
X                break;
X            case SYM_PIPE:
X                rc = pipe(pipefds);
X                if (rc < 0)
X                {   /* can't set up pipe? */
X                    errmsg(0,LOC("exec_ioredir"),"can't create pipe");
X                    pipefds[0] = pipefds[1] = -1;
X                    break;
X                }
X                if (pipefds[1] != iop->fd)
X                {   /* if need to move fd */
X                    rc = dup2(pipefds[1],iop->fd);
X                    close(pipefds[1]);
X                }
X                if (rc >= 0)
X                {   /* if successful so far, move pipefds[0] out of harms way */
X                    tfd = main_fdget();
X                    rc = dup2(pipefds[0],tfd);
X                    close(pipefds[0]);
X                    if (rc >= 0)
X                    {   /* if everything worked */
X                        pipefds[0] = tfd;
X                        pipefds[1] = iop->fd;
X                    }
X                    else
X                    {   /* it didn't work... */
X                        errmsg(0,LOC("exec_ioredir"),"can't finish off pipe");
X                        pipefds[0] = pipefds[1] = -1;
X                        main_fdput(tfd);
X                        break;
X                    }
X                }
X                break;
X            case SYM_LT:
X                rc = tfd = open(iop->file,O_RDONLY);
X                if (rc >= 0 && rc != iop->fd)
X                {   /* move to correct fd */
X                    rc = dup2(rc,iop->fd);
X                    close(tfd);
X                }
X                if (rc < 0)
X                    errmsg(0,LOC("exec_ioredir"),"can't open file %s",iop->file);
X                break;
X            case SYM_LLT:
X            case SYM_LLTL:
X                rc = tfd = open(iop->tempfile,O_RDONLY);
X                if (rc >= 0 && rc != iop->fd)
X                {   /* move to correct fd */
X                    rc = dup2(rc,iop->fd);
X                    close(tfd);
X                }
X                if (rc < 0)
X                    errmsg(0,LOC("exec_ioredir"),"can't open tempfile %s",iop->tempfile);
X                break;
X            case SYM_LBT:
X            case SYM_RBT:
X                tfd = atoi(iop->file);
X                if (tfd > MAXUFD)
X                    continue;
X                rc = dup2(tfd,iop->fd);
X                if (rc < 0)
X                    errmsg(SHERR_PERROR,LOC("exec_ioredir"),"can't associate %d<->%d",iop->fd,tfd);
X                break;
X            case SYM_RT:
X                rc = tfd = open(iop->file,O_WRONLY|O_CREAT|O_TRUNC,0666);
X                if (rc >= 0 && rc != iop->fd)
X                {   /* move to correct fd */
X                    rc = dup2(rc,iop->fd);
X                    close(tfd);
X                }
X                if (rc < 0)
X                    errmsg(0,LOC("exec_ioredir"),"can't create %s",iop->file);
X                break;
X            case SYM_RRT:
X                rc = tfd = open(iop->file,O_WRONLY|O_CREAT,0666);
X                if (rc >= 0 && rc != iop->fd)
X                {   /* move to correct fd */
X                    rc = dup2(rc,iop->fd);
X                    close(tfd);
X                }
X                if (rc >= 0)
X                    rc = lseek(iop->fd,0L,2);
X                if (rc < 0)
X                    errmsg(0,LOC("exec_ioredir"),"can't create %s",iop->file);
X                break;
X        }
X        if (rc < 0)
X        {   /* couldn't set up the io */
X            /* message already printed */
X            return rc;
X        }
X        fds_opened |= (1L << (long)iop->fd);
X    }
X    if (!(fds_opened & (1L<<0L)))
X    {   /* was stdin setup */
X        rc = dup2(base_env.io->input,0);
X        if (rc < 0)
X        {   /* did it work? */
X            errmsg(SHERR_PERROR,LOC("exec_ioredir"),"can't set default stdin");
X            return rc;
X        }
X        fds_opened |= (1L<<0L);
X    }
X    if (!(fds_opened & (1L<<1L)))
X    {   /* was stdout setup */
X        rc = dup2(base_env.io->output,1);
X        if (rc < 0)
X        {   /* did it work? */
X            errmsg(SHERR_PERROR,LOC("exec_ioredir"),"can't set default stdout");
X            return rc;
X        }
X        fds_opened |= (1L<<1L);
X    }
X    if (!(fds_opened & (1L<<2L)))
X    {   /* was stderr setup */
X        rc = dup2(base_env.io->errout,2);
X        if (rc < 0)
X        {   /* did it work? */
X            errmsg(SHERR_PERROR,LOC("exec_ioredir"),"can't set default stderr");
X            return rc;
X        }
X        fds_opened |= (1L<<2L);
X    }
X    return 0;
X#endif  /* GEMDOS */
X}   /* end of exec_ioredir */
X
Xstatic void exec_iodone()
X{
X    register int i;
X
X    for (i = 0; i <= MAXUFD; i++)
X    {   /* get back to known state */
X        if (fds_opened & (1L<<(long)i))
X        {   /* if it was opened */
X#ifdef  GEMDOS
X#ifdef  MWC_ARGV
X            mwc_iovec[i] = '?';
X#endif  /* MWC_ARGV */
X            Fclose(i);
X#else
X            close(i);
X#endif  /* GEMDOS */
X        }
X    }
X    fds_opened = 0;
X}   /* end of exec_iodone */
X
X#ifndef GEMDOS
Xint exec_waitfor()
X{
X    register int i;
X    int pid;
X    register struct procs *wp,*owp;
X    struct bg_job *jb,*ojb,*ljb;
X#ifndef USG
X    union wait status;
X    struct rusage usage;
X#else
X    int status;
X#endif  /* USG */
X
X    while (waitStack != (struct procs *)NULL)
X    {   /* wait for running jobs that need waiting for */
X#ifndef USG
X        pid = wait3(&status,0,&usage);
X        cmd_lastrc = status.w_T.w_Retcode;
X#else
X        pid = wait(&status);
X        cmd_lastrc = (status >> 8) & 0xFF;
X#endif  /* USG */
X        if (pid <= 0)
X            break;
X        owp = (struct procs *)NULL;
X        for (wp = waitStack; wp != (struct procs *)NULL; )
X        {   /* anything we can delete? */
X            if (wp->pid == pid)
X            {   /* then can delete this entry */
X                if (owp != (struct procs *)NULL)
X                    owp->next = wp->next;
X                else waitStack = wp->next;
X                free(wp);
X                break;
X            }
X            owp = wp;
X            wp = wp->next;
X        }
X        i = 1;
X        for (jb = base_env.jobs; jb != (struct bg_job *)NULL; )
X        {   /* or any background stuff to delete? */
X            ojb = jb;
X            jb = jb->next;
X            owp = (struct procs *)NULL;
X            for (wp = ojb->cmds; wp != (struct procs *)NULL; )
X            {   /* anything we can delete? */
X                if (wp->pid == pid)
X                {   /* then can delete this entry */
X                    if (owp != (struct procs *)NULL)
X                        owp->next = wp->next;
X                    else ojb->cmds = wp->next;
X                    free(wp);
X                    break;
X                }
X                owp = wp;
X                wp = wp->next;
X            }
X            if (ojb->cmds == (struct procs *)NULL)
X            {   /* if this job completely finished */
X                if (flag_monitor)
X                    cmd_jobs_dump(ojb->job,i,"Done");
X                phrase_free(ojb->job);
X                for (ljb = base_env.jobs; ljb != (struct bg_job *)NULL && ljb->next != ojb; ljb = ljb->next)
X                    /* do nothing */;
X                if (ljb != (struct bg_job *)NULL && ljb->next == ojb)
X                    ljb->next = ojb->next;
X                free(ojb);
X                if (ojb == base_env.jobs)
X                    base_env.jobs = jb;
X            }
X            i++;
X        }
X    }
X    return 0;
X}   /* end of exec_waitfor */
X#endif  /* GEMDOS */
X
Xint exec_phrase(pp,iosetup)
Xstruct phrase *pp;
Xint iosetup;
X{
X    int rc,pid,i;
X    char *cmd;
X    register struct token *tp,*ltp,*new;
X    struct token *ftp;
X    struct aliases *ap;
X    struct phrase *lpp,*llpp;
X    int sf,sr;
X
X    for (tp = pp->var; tp != (struct token *)NULL; tp = tp->next)
X    {   /* do any pending var definitions */
X        var_define(tp->name,0,0);
X    }
X    if (!iosetup)
X    {   /* if need redirection */
X        fds_opened = 0;
X        rc = exec_ioredir(pp->io);
X        if (rc != 0)
X        {   /* if the redirection didn't work */
X            /* message already printed */
X            return 1;
X        }
X    }
X    /* Alias expansion */
X    if (pp->body != (struct token *)NULL)
X    {   /* is this an alias */
X        ap = alias_get(pp->body->name);
X        if (ap != (struct aliases *)NULL && ap->tp != (struct token *)NULL)
X        {   /* it is an alias; do the replacement */
X            ftp = ltp = (struct token *)NULL;
X            for (tp = ap->tp; tp != (struct token *)NULL; tp = tp->next)
X            {   /* first make copy of tokens */
X                new = new_token(strlen(tp->name));
X                if (new == (struct token *)NULL)
X                {   /* enough memory? */
X                    errmsg(SHERR_NOMEM,LOC("exec_phrase"));
X                    tokens_free(ftp);
X                    return -1;
X                }
X                new->type = tp->type;
X                strcpy(new->name,tp->name);
X                if (ftp == (struct token *)NULL)
X                    ftp = ltp = new;
X                else
X                {   /* tack onto end */
X                    ltp->next = new;
X                    ltp = new;
X                }
X            }
X            if (ltp != (struct token *)NULL)
X            {   /* if something to substitute with */
X                ltp->next = pp->body->next;
X                tp = pp->body;
X                pp->body = ftp;
X                free(tp);
X            }
X        }
X    }
X    /* Wild card expansion */
X    if (flag_noglob == 0 && pp->body != (struct token *)NULL)
X    {   /* walk through the phrase */
X        tp = pp->body;
X        ltp = (struct token *)NULL;
X        for (tp = tp->next; tp != (struct token *)NULL; tp = tp->next)
X        {   /* do expansion */
X            new = wild_search(tp->name,(struct token **)NULL);
X            if (new == (struct token *)NULL)
X            {   /* if no expansion */
X                ltp = tp;
X                continue;
X            }
X            if (ltp == (struct token *)NULL)
X                pp->body->next = new;
X            else ltp->next = new;
X            ltp = tp;
X            while (new->next != (struct token *)NULL)
X                new = new->next;
X            tp = new;
X            tp->next = ltp->next;
X            free(ltp);
X            ltp = tp;
X        }
X    }
X    if (pp->body != (struct token *)NULL && pp->type != (struct token *)NULL)
X    {   /* if something to run */
X        cmd = strcopy(pp->body->name);
X        if (cmd == (char *)NULL)
X        {   /* enough memory? */
X            /* message already printed */
X            return -1;
X        }
X        rc = exec_function(cmd,pp);
X        if (rc <= 0)
X        {   /* if to try other places */
X            stripquotes(cmd);
X            rc = exec_builtin(cmd,pp,iosetup);
X            if (rc <= 0)
X            {   /* if not builtin, try normal execution */
X                rc = exec_normal(cmd,pp);
X            }
X        }
X        if (rc <= 0)
X        {   /* the command didn't work */
X            errmsg(SHERR_PERROR,LOC("exec_phrase"),"%s: command not found",cmd);
X            cmd_lastrc = 1;
X        }
X        free(cmd);
X    }
X    else    rc = 0;
X    if (pp->type != (struct token *)NULL && pp->type->type == SYM_LPAREN)
X    {   /* exec the contents of the parenthized group */
X#ifdef  GEMDOS
X        if (main_iopush() < 0)
X        {   /* if couldn't save i/o env */
X            /* message already printed */
X            main_iopop();
X            cmd_lastrc = -1;
X            return -1;
X        }
X        if (main_varpush() < 0)
X        {   /* if couldn't save var env */
X            /* message already printed */
X            main_iopop();
X            cmd_lastrc = -1;
X            return -1;
X        }
X        sf = cmd_forceexit;
X        sr = cmd_returnexit;
X        cmd_forceexit = cmd_returnexit = 0;
X        for (lpp = pp->group; lpp != (struct phrase *)NULL; )
X        {   /* exec the contents of the parenthized group */
X            llpp = lpp;
X            lpp = lpp->next;
X            exec_phrase(llpp,0);
X            if (cmd_forceexit || cmd_returnexit)
X                break;
X            if (llpp->body != (struct token *)NULL)
X                var_define0("_",llpp->body_end->name,0);
X        }
X        cmd_forceexit = sf;
X        cmd_returnexit = sr;
X        main_iopop();
X        main_varpop();
X#else
X        pid = fork();
X        if (pid < 0)
X        {   /* can't fork -> can't run */
X            errmsg(0,LOC("exec_phrase"),"can't fork to run parenthesized group of commands");
X        }
X        else if (pid == 0)
X        {   /* run the commands */
X            for (lpp = pp->group; lpp != (struct phrase *)NULL; )
X            {   /* exec the contents of the parenthized group */
X                llpp = lpp;
X                lpp = lpp->next;
X                exec_phrase(llpp,0);
X                if (cmd_forceexit || cmd_returnexit)
X                    break;
X                if (llpp->body != (struct token *)NULL)
X                    var_define0("_",llpp->body_end->name,0);
X            }
X            cmd_forceexit = 1;
X        }
X        else
X        {   /* set up to wait for completion if necessary */
X            for (lpp = pp->group; lpp != (struct phrase *)NULL && lpp->next != (struct phrase *)NULL;
X                lpp = lpp->next)
X                /* do nothing */;
X            savepid(lpp,pid);
X        }
X#endif  /* GEMDOS */
X    }
X    if (!iosetup)
X        exec_iodone();
X#ifndef GEMDOS
X    exec_waitfor();
X#endif  /* GEMDOS */
X    if (cmd_lastrc != 0)
X        force_signal("ERR");
X    force_signal("DEBUG");
X    return rc;
X}   /* end of exec_phrase */
X
Xstatic int empty_sentence(sp)
Xregister struct phrase *sp;
X{
X    if (sp->group == (struct phrase *)NULL)
X        return 1;
X    if (sp->group != (struct phrase *)NULL &&
X        sp->group == sp->group_end &&
X        sp->group->io == (struct iotoken *)NULL &&
X        sp->group->body == (struct token *)NULL &&
X        sp->group->var == (struct token *)NULL &&
X        sp->group->type != (struct token *)NULL &&
X        sp->group->type->type == SYM_EOL)
X        return 1;
X    return 0;
X}   /* end of empty_sentence */
X
Xint exec_sentence(sp,iosetup)
Xstruct phrase *sp;
Xint iosetup;
X{
X    register struct phrase *pp,*lpp;
X#ifndef GEMDOS
X    struct procs *wp,*owp;
X    struct bg_job *job,*jl;
X#endif  /* GEMDOS */
X    int rc;
X
X#ifndef GEMDOS
X    for (wp = bgJobs; wp != (struct procs *)NULL; )
X    {   /* delete any current stuff */
X        owp = wp;
X        wp = wp->next;
X        free(owp);
X    }
X    bgJobs = (struct procs *)NULL;
X#endif  /* GEMDOS */
X
X    if (empty_sentence(sp))
X        return 0;
X    if (flag_echoexec)
X        sentence_dump(sp);
X    base_env.break_level = base_env.continue_level = 0;
X    for (pp = sp->group; pp != (struct phrase *)NULL; )
X    {   /* for each part of sentence */
X        lpp = pp;
X        pp = pp->next;
X        if (flag_noexec)
X            continue;
X        lpp = copy_phrase(lpp,1,0,1);
X        if (lpp == (struct phrase *)NULL)
X        {   /* enough memory? */
X            /* message already printed */
X            continue;
X        }
X        if (lpp->type != (struct token *)NULL &&
X            lpp->type->type == SYM_SEMI &&
X            lpp->type->name[0] == '\0')
X            rc = exec_sentence(lpp,iosetup);
X        else rc = exec_phrase(lpp,iosetup);
X        if (lpp->body != (struct token *)NULL)
X            var_define0("_",lpp->body_end->name,0);
X        if (rc < 0)
X        {   /* abort this sentence? */
X            phrase_free(lpp);
X            continue;
X        }
X        if (base_env.break_level > 0 || base_env.continue_level > 0)
X        {   /* break out of sentence? */
X            phrase_free(lpp);
X            break;
X        }
X
X        if (lpp->type != (struct token *)NULL) switch (lpp->type->type)
X        {   /* any special handling? */
X            case SYM_ANDIF:
X                if (cmd_lastrc != 0 && pp != (struct phrase *)NULL)
X                    pp = pp->next;
X                break;
X            case SYM_ORIF:
X                if (cmd_lastrc == 0 && pp != (struct phrase *)NULL)
X                    pp = pp->next;
X                break;
X        }
X        phrase_free(lpp);
X        if (pp == (struct phrase *)NULL)
X            break;
X    }
X#ifndef GEMDOS
X    if (bgJobs != (struct procs *)NULL)
X    {   /* if part of this sentence is running in the background */
X        for (jl = base_env.jobs; jl != (struct bg_job *)NULL && jl->next != (struct bg_job *)NULL; jl = jl->next)
X            /* do nothing */;
X        job = (struct bg_job *)malloc(sizeof(*job));
X        if (job != (struct bg_job *)NULL)
X        {   /* enough memory? */
X            job->next = (struct bg_job *)NULL;
X            job->cmds = bgJobs;
X            bgJobs = (struct procs *)NULL;
X            job->job = copy_group(sp,0,0,1);
X            if (base_env.jobs == (struct bg_job *)NULL)
X                base_env.jobs = job;
X            else
X            {   /* tack onto end */
X                jl->next = job;
X            }
X        }
X        else
X        {   /* out of memory */
X            errmsg(SHERR_NOMEM,LOC("exec_sentence"));
X        }
X    }
X#endif  /* GEMDOS */
X    if (cmd_forceexit || cmd_returnexit)
X        return 1;
X    return 0;
X}   /* end of exec_sentence */
X
Xvoid phrase_dump(pp,printbody,pad)
Xstruct phrase *pp;
Xint printbody;
Xint pad;
X{
X    register struct token *tp;
X    register struct iotoken *iop;
X    register struct phrase *cpp;
X    char buffer[16];
X    int i,flag;
X
X    if (pp == (struct phrase *)NULL)
X        return;
X    flag = 0;
X    for (tp = pp->var; tp != (struct token *)NULL; tp = tp->next)
X    {
X        io_writestring(0,tp->name);
X        if (tp->next != (struct token *)NULL)
X            io_writestring(0," ");
X        flag++;
X    }
X    for (tp = pp->body; tp != (struct token *)NULL; tp = tp->next)
X    {
X        io_writestring(0,tp->name);
X        if (tp->next != (struct token *)NULL)
X            io_writestring(0," ");
X        flag++;
X    }
X    for (iop = pp->io; iop != (struct iotoken *)NULL; iop = iop->next)
X    {   /* io redirections? */
X        if (iop->type != SYM_PIPE && iop->type != SYM_PIPER)
X        {   /* what fd is used? */
X            sprintf(buffer," %d",iop->fd);
X            io_writestring(0,buffer);
X            flag++;
X        }
X        switch (iop->type)
X        {   /* what action? */
X            case SYM_LT:
X                io_writestring(0,"<");
X                flag++;
X                break;
X            case SYM_LLT:
X                io_writestring(0,"<<");
X                flag++;
X                break;
X            case SYM_LLTL:
X                io_writestring(0,"<<-");
X                flag++;
X                break;
X            case SYM_LBT:
X                io_writestring(0,"<&");
X                flag++;
X                break;
X            case SYM_RT:
X                io_writestring(0,">");
X                flag++;
X                break;
X            case SYM_RRT:
X                io_writestring(0,">>");
X                flag++;
X                break;
X            case SYM_RBT:
X                io_writestring(0,">&");
X                flag++;
X                break;
X            case SYM_PIPE:
X            case SYM_PIPER:
X                break;
X        }
X        if (iop->type != SYM_PIPE && iop->type != SYM_PIPER)
X        {   /* file being accessed */
X            io_writestring(0,iop->file);
X            if (strlen(iop->file) > 0)
X                flag++;
X        }
X    }
X    if (pp->type != (struct token *)NULL && pp->type->type != SYM_BQUOTE &&
X        strlen(pp->type->name) > 0 && pp->type->type != SYM_EOL)
X    {   /* if phrase type printable, print it */
X        if (flag)
X            io_writestring(0," ");
X        io_writestring(0,pp->type->name);
X    }
X    else if (pp->type != (struct token *)NULL && pp->type->type == SYM_EOL)
X    {   /* add in eol if needed */
X        io_writestring(0,"\n");
X    }
X    for (cpp = pp->group; printbody && cpp != (struct phrase *)NULL; cpp = cpp->next)
X    {   /* dump out contents of group */
X        for (i = 0; flag && i < pad; i++)
X            io_writestring(0," ");
X        phrase_dump(cpp,printbody,pad);
X    }
X}   /* end of phrase_dump */
X
Xvoid sentence_dump(sp)
Xstruct phrase *sp;
X{
X    register struct phrase *pp;
X    register struct phrase *fsp;
X    int len;
X
X    for (fsp = sp; fsp != (struct phrase *)NULL; fsp = fsp->next)
X    {
X        len = io_prompt(3);
X        for (pp = fsp->group; pp != (struct phrase *)NULL; pp = pp->next)
X        {
X            phrase_dump(pp,1,len);
X        }
X    }
X}   /* end of sentence_dump */
END_OF_FILE
if test 45883 -ne `wc -c <'exec.c'`; then
    echo shar: \"'exec.c'\" unpacked with wrong size!
fi
# end of 'exec.c'
fi
echo shar: End of archive 10 \(of 11\).
cp /dev/null ark10isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dclemans.falcon@mntgfx.mentor.com (Dave Clemans) (03/16/89)

With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.  It is NOT yet
bug free; I am VERY interested in fixes; enhancements, etc.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 11 (of 11)."
# Contents:  var.c
# Wrapped by dclemans@dclemans on Wed Mar 15 14:04:02 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'var.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'var.c'\"
else
echo shar: Extracting \"'var.c'\" \(50361 characters\)
sed "s/^X//" >'var.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Handle shell variables
X *
X * $Id: var.c,v 1.12 89/03/07 19:35:06 dclemans Exp $
X *
X * $Log:	var.c,v $
X * Revision 1.12  89/03/07  19:35:06  dclemans
X * fix typo in GEMDOS code
X * 
X * Revision 1.11  89/03/07  19:05:53  dclemans
X * portability fixes
X * 
X * Revision 1.10  89/02/25  17:40:24  dclemans
X * miscellaneous bug fixes/speedups
X * 
X * Revision 1.9  89/02/22  21:32:07  dclemans
X * Implement simple background job monitoring facility
X * 
X * Revision 1.8  89/02/22  13:18:53  dclemans
X * Implement $!
X * 
X * Revision 1.7  89/02/22  08:17:18  dclemans
X * implement left-justified, right-justified, etc. parameter attributes
X * 
X * Revision 1.6  89/02/21  21:47:22  dclemans
X * Fix bugs in % and %% variable expansions
X * 
X * Revision 1.5  89/02/21  19:40:13  dclemans
X * Implement RANDOM and SECONDS variables
X * 
X * Revision 1.4  89/02/20  20:07:19  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X#include <time.h>
Xextern unsigned long time();
X#ifdef  LINED
X#include "reader\history.h"
X#endif  /* LINED */
X#ifdef  GEMDOS
X#include <basepage.h>
X#endif  /* GEMDOS */
X
X/*
X * Positional arguments
X */
Xchar    *var_arg0;
Xint     var_argc;
Xchar    **var_argv;
X
X/*
X * Initial values of variables
X */
Xchar    *var_init[] =
X{
X    "PS1",      "$ ",
X    "PS2",      "> ",
X    "PS3",      "#? ",
X    "PS4",      "+ ",
X    "IFS",      " \t",
X#ifdef  GEMDOS
X    "PATH",     ",c:\\bin",
X    "SUFF",     ".ttp,.tos,.prg,.app,,.sh",
X    "TMPDIR",   "",
X    "COLUMNS",  "80",
X    "LINES",    "24",
X#else
X    "PATH",     ":/bin:/usr/bin",
X#endif  /* GEMDOS */
X    (char *)NULL,   (char *)NULL
X};
X
X/*
X * Set up "reserved"/special handling variables
X */
X#define VAR_CDPATH      0
X#define VAR_COLUMNS     1
X#define VAR_ENV         2
X#define VAR_HISTSIZE    3
X#define VAR_HOME        4
X#define VAR_IFS         5
X#define VAR_LINES       6
X#define VAR_PATH        7
X#define VAR_PS1         8
X#define VAR_PS2         9
X#define VAR_PS3         10
X#define VAR_PS4         11
X#define VAR_PWD         12
X#define VAR_SHELL       13
X#ifdef  GEMDOS
X#define VAR_SUFF        14
X#define VAR_TMPDIR      15
X#endif  /* GEMDOS */
Xstruct {
X    char    *name;
X    int     flag;
X} reserved_names[] =
X{   /* keep this list sorted... */
X    { "CDPATH",     VAR_CDPATH },
X    { "COLUMNS",    VAR_COLUMNS },
X    { "ENV",        VAR_ENV },
X    { "HISTSIZE",   VAR_HISTSIZE },
X    { "HOME",       VAR_HOME },
X    { "IFS",        VAR_IFS },
X    { "LINES",      VAR_LINES },
X    { "PATH",       VAR_PATH },
X    { "PS1",        VAR_PS1 },
X    { "PS2",        VAR_PS2 },
X    { "PS3",        VAR_PS3 },
X    { "PS4",        VAR_PS4 },
X    { "PWD",        VAR_PWD },
X    { "SHELL",      VAR_SHELL },
X#ifdef  GEMDOS
X    { "SUFF",       VAR_SUFF },
X    { "TMPDIR",     VAR_TMPDIR },
X#endif  /* GEMDOS */
X    { (char *)NULL, -1 }
X};  /* end of reserved_names */
X
Xint var_namecmp(n1,n2)
Xchar *n1;
Xchar *n2;
X{
X    int i1,i2,rc;
X    char *p1,*p2;
X    register char *p,*q;
X    char buffer[BUFSIZ];
X
X    p = strchr(n1,'[');
X    if (p == (char *)NULL)
X        q = p;
X    else q = strrchr(n1,']');
X    if (p == (char *)NULL || q == (char *)NULL)
X    {   /* not an indexed variable? */
X        p1 = n1;
X        i1 = 0;
X    }
X    else
X    {   /* indexed variable */
X        strncpy(buffer,&p[1],(int)(q-p)-1);
X        buffer[(int)(q-p)-1] = '\0';
X        i1 = parse_c_expression(buffer,0);
X        strncpy(buffer,n1,(int)(p-n1));
X        p1 = strcopy(buffer);
X    }
X    p = strchr(n2,'[');
X    if (p == (char *)NULL)
X        q = p;
X    else q = strrchr(n2,']');
X    if (p == (char *)NULL || q == (char *)NULL)
X    {   /* not an indexed variable? */
X        p2 = n2;
X        i2 = 0;
X    }
X    else
X    {   /* indexed variable */
X        strncpy(buffer,&p[1],(int)(q-p)-1);
X        buffer[(int)(q-p)-1] = '\0';
X        i2 = parse_c_expression(buffer,0);
X        strncpy(buffer,n2,(int)(p-n2));
X        p2 = strcopy(buffer);
X    }
X    rc = strcmp(p1,p2);
X    if (p1 != n1)
X        free(p1);
X    if (p2 != n2)
X        free(p2);
X    if (rc != 0)
X        return rc;
X    if (i1 < i2)
X        return -1;
X    if (i1 > i2)
X        return 1;
X    return 0;
X}   /* end of var_namecmp */
X
Xchar *var_subeval(name,side_effects)
Xregister char *name;
Xint side_effects;
X{
X    int temp;
X    register char *p,*q;
X    char buffer[BUFSIZ];
X
X    p = strchr(name,'[');
X    if (p == (char *)NULL)
X        return (char *)NULL;
X    q = strrchr(name,']');
X    if (q == (char *)NULL)
X        return (char *)NULL;
X    strncpy(buffer,&p[1],(int)(q-p)-1);
X    buffer[(int)(q-p)-1] = '\0';
X    temp = 1;
X    for (q = buffer; *q; q++)
X        if (!isdigit(*q))
X            temp = 0;
X    if (temp)
X    {   /* if doesn't really need evaluation */
X        return (char *)NULL;
X    }
X    temp = parse_c_expression(buffer,side_effects);
X    strncpy(buffer,name,(int)(p-name));
X    sprintf(&buffer[(int)(p-name)],"[%d]",temp);
X    return strcopy(buffer);
X}   /* end of var_subeval */
X
Xstatic void dump_var(vp,flag)
Xregister struct variable *vp;
Xint flag;
X{
X    long temp;
X    char buffer[32];
X#ifndef GEMDOS
X#ifndef USG
X    extern long random();
X#else
X#endif  /* USG */
X#endif  /* GEMDOS */
X
X    io_writestring(0,vp->name);
X    if (flag)
X    {   /* also write out value */
X        io_writestring(0,"=");
X        if (vp->type & TYPE_FUNCTION)
X        {   /* a "special" variable? */
X            if (strcmp(vp->name,"RANDOM") == 0)
X            {   /* return a random number */
X#ifndef GEMDOS
X#ifndef USG
X                temp = random();
X#else
X                temp = rand();
X#endif  /* USG */
X#else
X                temp = Random();
X#endif  /* GEMDOS */
X                sprintf(buffer,"%ld",temp);
X                io_writestring(0,buffer);
X            }
X            else if (strcmp(vp->name,"SECONDS") == 0)
X            {   /* how long have we been running? */
X                temp = time(0L);
X                sprintf(buffer,"%ld",temp-base_env.start_at);
X                io_writestring(0,buffer);
X            }
X        }
X        else io_writestring(0,vp->value);
X    }
X    io_writestring(0,"\n");
X}   /* end of dump_var */
X
Xstatic void var_reserved(vp)
Xregister struct variable *vp;
X{
X    register int i;
X    int temp1,temp2;
X    register struct hist_phrase *nh;
X
X    for (i = 0; reserved_names[i].name != (char *)NULL; i++)
X    {   /* a variable with special handling when set? */
X        temp1 = strcmp(vp->name,reserved_names[i].name);
X        if (temp1 > 0)
X            continue;
X        if (temp1 < 0)
X            return;
X        switch (reserved_names[i].flag)
X        {   /* found a variable that needs special handling; go do it */
X            case VAR_CDPATH:
X                base_env.cd_path = vp->value;
X                break;
X            case VAR_COLUMNS:
X                base_env.columns = atoi(vp->value)-1;   /* last usable column */
X                break;
X            case VAR_ENV:
X                base_env.envfile = vp->value;
X                break;
X            case VAR_HISTSIZE:
X                temp1 = atoi(vp->value);
X                if (temp1 <= 0)
X                {   /* completely kill old list */
X                    if (base_env.history_size > 0)
X                    {   /* need to free old stuff? */
X                        for (temp2 = 0; temp2 < base_env.history_size; temp2++)
X                            if (base_env.history_list[temp2].cmd != (struct phrase *)NULL)
X                                phrase_free(base_env.history_list[temp2].cmd);
X                        free(base_env.history_list);
X                        base_env.history_list = (struct hist_phrase *)NULL;
X                        base_env.history_size = 0;
X                    }
X#ifdef  LINED
X                    History.maxSize = 1;
X#endif  /* LINED */
X                    return;
X                }
X                nh = (struct hist_phrase *)malloc(sizeof(struct hist_phrase)*temp1);
X                if (nh == (struct hist_phrase *)NULL)
X                {   /* out of memory? */
X                    errmsg(SHERR_NOMEM,LOC("var_reserved"));
X                    return;
X                }
X                for (temp2 = 0; temp2 < temp1; temp2++)
X                {   /* make sure it starts out empty */
X                    nh[temp2].cmd = (struct phrase *)NULL;
X                    nh[temp2].number = -1;
X                }
X                for (temp2 = 0; temp2 < ((temp1<base_env.history_size)?temp1:base_env.history_size); temp2++)
X                {   /* copy in old stuff */
X                    nh[temp2].cmd = base_env.history_list[temp2].cmd;
X                    nh[temp2].number = base_env.history_list[temp2].number;
X                }
X                for (; temp2 < base_env.history_size; temp2++)
X                    if (base_env.history_list[temp2].cmd != (struct phrase *)NULL)
X                        phrase_free(base_env.history_list[temp2].cmd);
X                if (base_env.history_size > 0)
X                    free(base_env.history_list);
X                base_env.history_size = temp1;
X#ifdef  LINED
X                History.maxSize = temp1;
X#endif  /* LINED */
X                base_env.history_list = nh;
X                        break;
X            case VAR_HOME:
X                base_env.homedir = vp->value;
X                break;
X            case VAR_IFS:
X                base_env.separators = vp->value;
X                break;
X            case VAR_LINES:
X                base_env.lines = atoi(vp->value);
X                break;
X            case VAR_PS1:
X                base_env.prompts[0] = vp->value;
X                break;
X            case VAR_PS2:
X                base_env.prompts[1] = vp->value;
X                break;
X            case VAR_PS3:
X                base_env.prompts[2] = vp->value;
X                break;
X            case VAR_PS4:
X                base_env.prompts[3] = vp->value;
X                break;
X            case VAR_PATH:
X                base_env.exec_path = vp->value;
X                break;
X            case VAR_PWD:
X                if (base_env.dir->current != (char *)NULL)
X                    free(base_env.dir->current);
X                base_env.dir->current = strcopy(vp->value);
X                break;
X            case VAR_SHELL:
X                base_env.shellname = strcopy(vp->value);
X                break;
X#ifdef  GEMDOS
X            case VAR_SUFF:
X                base_env.exec_suff = vp->value;
X                break;
X            case VAR_TMPDIR:
X                base_env.tmpdir = vp->value;
X                break;
X#endif  /* GEMDOS */
X        }
X        break;
X    }
X}   /* end of var_reserved */
X
Xvoid var_setarg0(arg)
Xchar *arg;
X{
X    if (arg == (char *)NULL || strlen(arg) == 0)
X        var_arg0 = "shell";
X    else var_arg0 = arg;
X    var_define0("SHELL",var_arg0,0);
X}   /* end of var_setarg0 */
X
Xvoid var_setargs(argc,argv)
Xint argc;
Xchar *argv[];
X{
X    register int i;
X
X    var_argc = argc;
X    var_argv = new_argv(var_argc+1);
X    if (var_argv == (char **)NULL)
X    {   /* enough memory */
X        errmsg(SHERR_NOMEM,LOC("var_setargs"));
X        var_argc = 0;
X        return;
X    }
X    var_argv[0] = var_arg0;
X    for (i = 1; i < var_argc; i++)
X    {   /* now set up the args */
X        var_argv[i] = strcopy(argv[i]);
X    }
X    var_argv[i] = (char *)NULL;
X}   /* end of var_setargs */
X
Xvoid var_resetargs(tp)
Xstruct token *tp;
X{
X    register int i;
X    register struct token *tpp;
X
X    if (var_argc > 0)
X    {   /* free old args */
X        for (i = 1; i < var_argc; i++)
X            if (var_argv[i] != (char *)NULL)
X                free(var_argv[i]);
X        free(var_argv);
X    }
X    var_argc = 0;
X    for (tpp = tp; tpp != (struct token *)NULL; tpp = tpp->next)
X        var_argc++;
X    var_argv = new_argv(var_argc+2);
X    if (var_argv == (char **)NULL)
X    {   /* enough memory */
X        errmsg(SHERR_NOMEM,LOC("var_resetargs"));
X        var_argc = 0;
X        return;
X    }
X    var_argv[0] = var_arg0;
X    tpp = tp;
X    for (i = 0; i < var_argc; i++)
X    {   /* now set up the args */
X        var_argv[i+1] = strcopy(tpp->name);
X        tpp = tpp->next;
X    }
X    var_argv[i+1] = (char *)NULL;
X    var_argc++;
X}   /* end of var_resetargs */
X
Xvoid var_shiftargs(count)
Xint count;
X{
X    register int i,j;
X
X    if (var_argv[1] != (char *)NULL)
X        free(var_argv[1]);
X    for (i = 0; i < count; i++)
X    {   /* shift the required number of arguments */
X        for (j = 1; j < var_argc; j++)
X            var_argv[j] = var_argv[j+1];
X        var_argc--;
X    }
X}   /* end of var_shiftargs */
X
Xstatic void unpad(s)
Xregister char *s;
X{
X    register char *p;
X
X    while (*s == ' ')
X        strcpy(s,&s[1]);
X    for (p = s; *p; p++)
X        /* do nothing */;
X    p--;
X    while (p > s && *p == ' ')
X        *p-- = '\0';
X}   /* end of unpad */
X
Xstatic void var_fixtype(vp,side_effects)
Xregister struct variable *vp;
Xint side_effects;
X{
X    register char *p;
X    register int temp;
X    char buffer[16];
X    char *value;
X
X    if (vp->value == (char *)NULL)
X        return;
X    if (vp->type & TYPE_INTEGER)
X    {   /* if need to eval value */
X        temp = 1;
X        for (p = vp->value; *p; p++)
X            if (!isdigit(*p))
X                temp = 0;
X        if (!temp)
X        {   /* if need to eval an expression */
X            temp = parse_c_expression(vp->value,side_effects);
X            sprintf(buffer,"%d",temp);
X            free(vp->value);
X            vp->value = strcopy(buffer);
X        }
X    }
X    if (vp->type & TYPE_UPPERCASE)
X    {   /* if should be upcased... */
X        for (p = vp->value; *p; p++)
X            if (islower(*p))
X                *p = _toupper(*p);
X    }
X    if (vp->type & TYPE_LOWERCASE)
X    {   /* if should be lowercased... */
X        for (p = vp->value; *p; p++)
X            if (isupper(*p))
X                *p = _tolower(*p);
X    }
X    if (vp->type & TYPE_LEFTJUST)
X    {   /* left justify the value */
X        unpad(vp->value);
X        if (vp->type & TYPE_ZEROS)
X        {   /* eliminate any initial zeros */
X            while (vp->value[0] == '0')
X                strcpy(vp->value,&vp->value[1]);
X        }
X        value = new_string(vp->misc+1);
X        if (value == (char *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("var_fixtype"));
X            return;
X        }
X        sprintf(buffer,"%%-%ds",vp->misc);
X        sprintf(value,buffer,vp->value);
X        free(vp->value);
X        vp->value = value;
X    }
X    if (vp->type & TYPE_RIGHTJUST)
X    {   /* right justify the value */
X        unpad(vp->value);
X        value = new_string(vp->misc+1);
X        if (value == (char *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("var_fixtype"));
X            return;
X        }
X        if (vp->type & TYPE_ZEROS)
X            sprintf(buffer,"%%0%ds",vp->misc);
X        else sprintf(buffer,"%%%ds",vp->misc);
X        sprintf(value,buffer,vp->value);
X        free(vp->value);
X        vp->value = value;
X    }
X    if (vp->type & TYPE_HOSTMAP)
X    {   /* make into good filename */
X        /* a start... */
X#ifdef  GEMDOS
X        for (p = vp->value; *p; p++)
X        {   /* check each char... */
X            if (*p == '/')
X                *p = '\\';
X            if (isupper(*p))
X                *p = _tolower(*p);
X        }
X#else
X        for (p = vp->value; *p; p++)
X            if (*p == '\\')
X                *p = '/';
X#endif  /* GEMDOS */
X    }
X}   /* end of var_fixtype */
X
Xstatic void var_setvalue(svp,value)
Xregister struct variable *svp;
Xregister char *value;
X{
X    if (svp->type & TYPE_READONLY)
X    {   /* can value be changed? */
X        errmsg(0,LOC("var_define0"),"value of '%s' is readonly",svp->name);
X        return;
X    }
X    if (svp->type == TYPE_DELETED)
X        svp->type = 0;
X    if (svp->value != (char *)NULL)
X        free(svp->value);
X    svp->value = strcopy(value);
X    var_fixtype(svp,1);
X    var_reserved(svp);
X    svp->cmd = cmd_count;
X    if (flag_allexport)
X        svp->type |= TYPE_EXPORTED;
X}   /* end of var_setvalue */
X
Xvoid var_define0(name,value,create)
Xchar *name;
Xchar *value;
Xint create;
X{
X    int rc;
X    char *xname,*ptr;
X    register struct variable *vp,*svp;
X    struct varstack *vs;
X
X    if (strlen(name) == 1 && (strchr("*@$!?-#",*name) != (char *)NULL || isdigit(*name)))
X    {   /* can't do this directly */
X        errmsg(0,LOC("var_define0"),"illegal assignment: '%s'",name);
X        return;
X    }
X
X    vp = new_variable();
X    if (vp == (struct variable *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("var_define0"));
X        return;
X    }
X
X    xname = var_subeval(name,1);
X    if (xname == (char *)NULL)
X        ptr = name;
X    else ptr = xname;
X    vs = base_env.var;
X
X    if (!create)
X    {   /* if to use existing created slot if possible */
X        for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X        {   /* for each variable table */
X            for (svp = vs->table; svp != (struct variable *)NULL; )
X            {   /* look for where to get the variable */
X                rc = var_namecmp(ptr,svp->name);
X                if (rc == 0)
X                {   /* found the variable */
X                    if (svp->type == TYPE_DELETED)
X                        svp = (struct variable *)NULL;
X                    break;
X                }
X                else if (rc < 0)
X                {   /* go down the left side? */
X                    svp = svp->left;
X                }
X                else
X                {   /* go down right side */
X                    svp = svp->right;
X                }
X            }
X            if (svp != (struct variable *)NULL)
X                break;
X        }
X        if (svp != (struct variable *)NULL)
X        {   /* if found "correct" place for variable */
X            free(vp);
X            var_setvalue(svp,value);
X            if (xname != (char *)NULL)
X                free(xname);
X            return;
X        }
X        for (vs = base_env.var; vs->next != (struct varstack *)NULL; vs = vs->next)
X            /* do nothing */;
X    }
X
X    for (svp = vs->table; svp != (struct variable *)NULL; )
X    {   /* look for where to put the variable */
X        rc = var_namecmp(ptr,svp->name);
X        if (rc == 0)
X        {   /* replace existing value */
X            free(vp);
X            var_setvalue(svp,value);
X            if (xname != (char *)NULL)
X                free(xname);
X            return;
X        }
X        else if (rc < 0)
X        {   /* go down the left side? */
X            if (svp->left == (struct variable *)NULL)
X                break;
X            svp = svp->left;
X        }
X        else
X        {   /* go down right side */
X            if (svp->right == (struct variable *)NULL)
X                break;
X            svp = svp->right;
X        }
X    }
X    vp->name = strcopy(ptr);
X    if (base_env.var->table == (struct variable *)NULL)
X        base_env.var->table = vp;
X    else if (rc < 0)
X        svp->left = vp;
X    else
X        svp->right = vp;
X    var_setvalue(vp,value);
X    if (xname != (char *)NULL)
X        free(xname);
X}   /* end of var_define0 */
X
Xvoid var_define(word,type,create)
Xchar *word;
Xint type;
Xint create;
X{
X    register char *p,*ptr;
X    register struct variable *svp;
X    int result,rc;
X    char buffer[16];
X    struct varstack *vs;
X    char *xname;
X
X    result = 0;
X    for (p = word; result != 0 || *p != '='; p++)
X    {   /* find end of name */
X        if (*p == '[')
X            result++;
X        else if (*p == ']')
X            result--;
X    }
X    /* ASSERT that *p == '=' */
X    *p++ = '\0';
X    xname = var_subeval(word,1);
X    if (xname == (char *)NULL)
X        ptr = word;
X    else ptr = xname;
X
X    svp = (struct variable *)NULL;
X    for (vs = base_env.var; !create && vs != (struct varstack *)NULL; vs = vs->next)
X    {   /* for each variable table */
X        for (svp = vs->table; svp != (struct variable *)NULL; )
X        {   /* look for where to get the variable */
X            rc = var_namecmp(ptr,svp->name);
X            if (rc == 0)
X            {   /* found the variable */
X                if (svp->type == TYPE_DELETED)
X                    svp = (struct variable *)NULL;
X                break;
X            }
X            else if (rc < 0)
X            {   /* go down the left side? */
X                svp = svp->left;
X            }
X            else
X            {   /* go down right side */
X                svp = svp->right;
X            }
X        }
X        if (svp != (struct variable *)NULL)
X            break;
X    }
X    if (svp != (struct variable *)NULL)
X    {   /* known spot for variable */
X        if (svp->type & TYPE_INTEGER)
X        {   /* an integer type? */
X            sprintf(buffer,"%d",parse_c_expression(p,1));
X            var_setvalue(svp,buffer);
X        }
X        else
X        {   /* normal strategy */
X            var_setvalue(svp,p);
X        }
X        if (svp->type == type)
X        {   /* no type change needed? */
X            if (xname != (char *)NULL)
X                free(xname);
X            return;
X        }
X        var_settype(ptr,type,~0);
X        if (xname != (char *)NULL)
X            free(xname);
X        return;
X    }
X
X    var_define0(ptr,p,create);
X    var_settype(ptr,type,~0);
X    *--p = '=';
X    if (xname != (char *)NULL)
X        free(xname);
X}   /* end of var_define */
X
Xvoid var_settype(name,type,mask)
Xchar *name;
Xint type;
Xint mask;
X{
X    register struct variable *svp;
X    int rc,otype;
X    char *ptr,*xname;
X    struct varstack *vs;
X
X    xname = var_subeval(name,0);
X    if (xname == (char *)NULL)
X        ptr = name;
X    else ptr = xname;
X    for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X    {   /* for each variable table */
X        for (svp = vs->table; svp != (struct variable *)NULL; )
X        {   /* look for where to put the variable */
X            rc = var_namecmp(ptr,svp->name);
X            if (rc == 0)
X            {   /* found value, set type */
X                otype = svp->type;
X                if (type == TYPE_DELETED)
X                {   /* remove this variable */
X                    svp->type = type;
X                    free(svp->value);
X                    svp->value = (char *)NULL;
X                    if (xname != (char *)NULL)
X                        free(xname);
X                    return;
X                }
X                svp->type &= ~TYPE_DELETED;
X                svp->type &= mask;
X                svp->type |= type;
X                if (xname != (char *)NULL)
X                    free(xname);
X                if (svp->type != otype)
X                {   /* was there any change? */
X                    var_fixtype(svp,0);
X                    var_reserved(svp);
X                }
X                return;
X            }
X            else if (rc < 0)
X            {   /* go down the left side? */
X                if (svp->left == (struct variable *)NULL)
X                    break;
X                svp = svp->left;
X            }
X            else
X            {   /* go down right side */
X                if (svp->right == (struct variable *)NULL)
X                    break;
X                svp = svp->right;
X            }
X        }
X    }
X    /* variable not found; make dummy one, set its type */
X    if (type == TYPE_DELETED)
X    {   /* if we really don't need to */
X        if (xname != (char *)NULL)
X            free(xname);
X        return;
X    }
X    var_define0(ptr,"",0);
X    var_settype(ptr,type,mask);
X    if (xname != (char *)NULL)
X        free(xname);
X}   /* end of var_settype */
X
Xvoid var_setmisc(name,misc)
Xchar *name;
Xint misc;
X{
X    register struct variable *svp;
X    int rc;
X    char *ptr,*xname;
X    struct varstack *vs;
X
X    xname = var_subeval(name,0);
X    if (xname == (char *)NULL)
X        ptr = name;
X    else ptr = xname;
X    for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X    {   /* for each variable table */
X        for (svp = vs->table; svp != (struct variable *)NULL; )
X        {   /* look for where to put the variable */
X            rc = var_namecmp(ptr,svp->name);
X            if (rc == 0)
X            {   /* found value, set type */
X                if (svp->type == TYPE_DELETED)
X                {   /* really there? */
X                    break;
X                }
X                svp->misc = misc;
X                if (xname != (char *)NULL)
X                    free(xname);
X                var_fixtype(svp,0);
X                var_reserved(svp);
X                return;
X            }
X            else if (rc < 0)
X            {   /* go down the left side? */
X                if (svp->left == (struct variable *)NULL)
X                    break;
X                svp = svp->left;
X            }
X            else
X            {   /* go down right side */
X                if (svp->right == (struct variable *)NULL)
X                    break;
X                svp = svp->right;
X            }
X        }
X    }
X    /* variable not found; make dummy one, set its type */
X    var_define0(ptr,"",0);
X    var_setmisc(ptr,misc);
X    if (xname != (char *)NULL)
X        free(xname);
X}   /* end of var_setmisc */
X
Xchar *var_special(name)
Xregister char *name;
X{
X    int rc,i;
X    char *p;
X    char buf[2];
X    char word[BUFSIZ];
X
X    if (strlen(name) == 2 &&
X        (isdigit(name[1]) || name[1] == '*' || name[1] == '@'))
X    {   /* reference to positional args? */
X        p = (char *)NULL;
X        rc = 0;
X        if (name[1] == '0')
X            p = var_arg0;
X        else if (isdigit(name[1]))
X        {   /* a single arg */
X            if ((name[1]-'0') < var_argc)
X                p = var_argv[name[1]-'0'];
X            else
X            {   /* supply a null string */
X                p = "''";
X            }
X        }
X        else if (name[1] == '*')
X        {   /* all args, separate entities */
X            rc = 0;
X            for (i = 1; i < var_argc; i++)
X                rc += strlen(var_argv[i])+3;
X            rc++;
X            p = new_string(rc);
X            if (p == (char *)NULL)
X            {   /* enough memory? */
X                errmsg(SHERR_NOMEM,LOC("var_special"));
X                return (char *)NULL;
X            }
X            *p = '\0';
X            for (i = 1; i < var_argc; i++)
X            {   /* now tack on args */
X                strcat(p,"'");
X                strcat(p,var_argv[i]);
X                strcat(p,"'");
X                if ((i+1) < var_argc)
X                    strcat(p," ");
X            }
X        }
X        else if (name[1] == '@')
X        {   /* all args, one hunk */
X            buf[0] = base_env.separators[0];
X            buf[1] = '\0';
X            rc = 0;
X            for (i = 1; i < var_argc; i++)
X                rc += strlen(var_argv[i])+1;
X            rc += 3;
X            p = new_string(rc);
X            if (p == (char *)NULL)
X            {   /* enough memory? */
X                errmsg(SHERR_NOMEM,LOC("var_special"));
X                return (char *)NULL;
X            }
X            *p = '\0';
X            strcat(p,"'");
X            for (i = 1; i < var_argc; i++)
X            {   /* now tack on args */
X                strcat(p,var_argv[i]);
X                if ((i+1) < var_argc)
X                    strcat(p,buf);
X            }
X            strcat(p,"'");
X        }
X        if (p == (char *)NULL)
X            p = "";
X        if (rc == 0)
X            p = strcopy(p);
X        return p;
X    }
X    if  (strlen(name) == 2 && name[1] == '?')
X    {   /* status of last cmd */
X        sprintf(word,"%d",cmd_lastrc);
X        return strcopy(word);
X    }
X    if (strlen(name) == 2 && name[1] == '#')
X    {   /* number of args */
X        sprintf(word,"%d",var_argc);
X        return strcopy(word);
X    }
X    if (strlen(name) == 2 && name[1] == '$')
X    {   /* current pid */
X#ifdef  GEMDOS
X        sprintf(word,"%lx",BP);
X#else
X        sprintf(word,"%d",getpid());
X#endif  /* GEMDOS */
X        return strcopy(word);
X    }
X    if (strlen(name) == 2 && name[1] == '!')
X    {   /* last background pid */
X#ifdef  GEMDOS
X        strcpy(word,"0");
X#else
X        sprintf(word,"%d",base_env.background_pid);
X#endif  /* GEMDOS */
X        return strcopy(word);
X    }
X    if (strlen(name) == 2 && name[1] == '-')
X    {   /* status of options */
X        p = word;
X        *p = '\0';
X        if (flag_allexport)
X            *p++ = 'a';
X        if (flag_noglob)
X            *p++ = 'f';
X        if (flag_cmdhash)
X            *p++ = 'h';
X        if (flag_interactive)
X            *p++ = 'i';
X        if (flag_keywords)
X            *p++ = 'k';
X        if (flag_monitor)
X            *p++ = 'm';
X        if (flag_noexec)
X            *p++ = 'n';
X        if (flag_varerr)
X            *p++ = 'u';
X        if (flag_echoinput)
X            *p++ = 'v';
X        if (flag_echoexec)
X            *p++ = 'x';
X        *p = '\0';
X        return strcopy(word);
X    }
X    return (char *)NULL;
X}   /* end of var_special */
X
Xchar *var_normal(name)
Xregister char *name;
X{
X    int rc;
X    register struct variable *svp;
X    struct varstack *vs;
X    long temp;
X    char buffer[32];
X#ifndef GEMDOS
X#ifndef USG
X    extern long random();
X#else
X#endif  /* USG */
X#endif  /* GEMDOS */
X
X    for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X    {   /* for each variable table */
X        for (svp = vs->table; svp != (struct variable *)NULL; )
X        {   /* look for where to get the variable */
X            rc = var_namecmp(&name[1],svp->name);
X            if (rc == 0)
X            {   /* found the variable */
X                if (svp->type == TYPE_DELETED)
X                    break;
X                if (svp->type & TYPE_FUNCTION)
X                {   /* "special" variables, like "RANDOM" and "SECONDS" */
X                    if (strcmp(&name[1],"RANDOM") == 0)
X                    {   /* return a random number */
X#ifndef GEMDOS
X#ifndef USG
X                        temp = random();
X#else
X                        temp = rand();
X#endif  /* USG */
X#else
X                        temp = Random();
X#endif  /* GEMDOS */
X                        sprintf(buffer,"%ld",temp);
X                        return strcopy(buffer);
X                    }
X                    else if (strcmp(&name[1],"SECONDS") == 0)
X                    {   /* how long have we been running? */
X                        temp = time(0L);
X                        sprintf(buffer,"%ld",temp-base_env.start_at);
X                        return strcopy(buffer);
X                    }
X                }
X                return strcopy(svp->value);
X            }
X            else if (rc < 0)
X            {   /* go down the left side? */
X                svp = svp->left;
X            }
X            else
X            {   /* go down right side */
X                svp = svp->right;
X            }
X        }
X    }
X    if (flag_varerr)
X        errmsg(0,LOC("var_normal"),"%s: variable not found",name);
X    return (char *)NULL;
X}   /* end of var_normal */
X
Xstatic int var_0submatches(name,svp)
Xchar *name;
Xregister struct variable *svp;
X{
X    register int matches;
X
X    matches = 0;
X    if (svp->left != (struct variable *)NULL)
X        matches += var_submatches(name,svp->left);
X    if (wild_match(svp->name,&name[1]))
X        matches++;
X    if (svp->right != (struct variable *)NULL)
X        matches += var_submatches(name,svp->right);
X    return matches;
X}   /* end of var_0submatches */
X
Xstatic int var_submatches(name)
Xchar *name;
X{
X    struct varstack *vs;
X    register int accum;
X
X    accum = 0;
X    for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X        accum += var_0submatches(name,vs->table);
X    return accum;
X}   /* end of var_submatches */
X
Xstatic int var_0subgetlen(name,svp)
Xchar *name;
Xregister struct variable *svp;
X{
X    register int length;
X
X    length = 0;
X    if (svp->left != (struct variable *)NULL)
X        length += var_0subgetlen(name,svp->left);
X    if (wild_match(svp->name,&name[1]))
X        length += strlen(svp->value)+3;
X    if (svp->right != (struct variable *)NULL)
X        length += var_0subgetlen(name,svp->right);
X    return length;
X}   /* end of var_0subgetlen */
X
Xstatic int var_subgetlen(name)
Xchar *name;
X{
X    struct varstack *vs;
X    register int accum;                               
X
X    accum = 0;
X    for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X        accum += var_0subgetlen(name,vs->table);
X    return accum;
X}   /* end of var_subgetlen */
X
Xstatic void var_0subfilbuf(name,svp,buf,flag)
Xregister char *name;
Xregister struct variable *svp;
Xregister char *buf;
Xint flag;
X{
X    if (svp->left != (struct variable *)NULL)
X        var_0subfilbuf(name,svp->left,buf,flag);
X    if (wild_match(svp->name,&name[1]))
X    {   /* tack on the value of this name */
X        if (strlen(svp->value) > 0 || flag)
X        {    /* tack this one on? */
X            strcat(buf,svp->value);
X            strcat(buf," ");
X        }
X    }
X    if (svp->right != (struct variable *)NULL)
X        var_0subfilbuf(name,svp->right,buf,flag);
X}   /* end of var_0subfilbuf */
X
Xstatic void var_subfilbuf(name,buf,flag)
Xchar *name;
Xchar *buf;
Xint flag;
X{
X    struct varstack *vs;
X
X    for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X        var_0subfilbuf(name,vs->table,buf,flag);
X}   /* end of var_subfilbuf */
X
Xchar *var_getval();
X
Xchar *var_expression(name)
Xregister char *name;
X{
X    register char *p,*q;
X    char buffer[BUFSIZ];
X    int temp;
X
X    sprintf(buffer,"#*%c[%c*%c]",ESCAPE_CHAR,ESCAPE_CHAR,ESCAPE_CHAR);
X    if (wild_match(&name[1],buffer))
X    {   /* number of matching subscripted vars defined */
X        strcpy(buffer,name);
X        sprintf(&buffer[strlen(buffer)-3],"%c[*%c]",ESCAPE_CHAR,ESCAPE_CHAR);
X        temp = var_submatches(&buffer[1]);
X        sprintf(buffer,"%d",temp);
X        return strcopy(buffer);
X    }
X    if (wild_match(&name[1],"#*"))
X    {   /* get length of var instead */
X        p = var_getval(&name[1],1);
X        strcpy(buffer,"0");
X        if (p != (char *)NULL)
X        {   /* if a value to look at */
X            sprintf(buffer,"%d",strlen(p));
X            free(p);
X        }
X        return strcopy(buffer);
X    }
X    sprintf(buffer,"*%c[%c@%c]",ESCAPE_CHAR,ESCAPE_CHAR,ESCAPE_CHAR);
X    if (wild_match(&name[1],buffer))
X    {   /* all values */
X        strcpy(buffer,name);
X        sprintf(&buffer[strlen(buffer)-3],"%c[*%c]",ESCAPE_CHAR,ESCAPE_CHAR);
X        temp = var_subgetlen(buffer);
X        p = new_string(temp+2);
X        if (p == (char *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("var_expression"));
X            return (char *)NULL;
X        }
X        var_subfilbuf(buffer,p,0);
X        return p;
X    }
X    sprintf(buffer,"*%c[%c*%c]",ESCAPE_CHAR,ESCAPE_CHAR,ESCAPE_CHAR);
X    if (wild_match(&name[1],buffer))
X    {   /* all values */
X        strcpy(buffer,name);
X        sprintf(&buffer[strlen(buffer)-3],"%c[*%c]",ESCAPE_CHAR,ESCAPE_CHAR);
X        temp = var_subgetlen(buffer);
X        p = new_string(temp+2);
X        if (p == (char *)NULL)
X        {   /* enough memory? */
X            errmsg(SHERR_NOMEM,LOC("var_expression"));
X            return (char *)NULL;
X        }
X        var_subfilbuf(buffer,p,1);
X        return p;
X    }
X    sprintf(buffer,"*%c[*%c]",ESCAPE_CHAR,ESCAPE_CHAR);
X    if (wild_match(&name[1],buffer))
X    {   /* a subscripted variable */
X        p = var_subeval(name,1);
X        if (p == (char *)NULL)
X            q = name;
X        else q = p;
X        q = var_getval(q,0);
X        if (p != (char *)NULL)
X            free(p);
X        return q;
X    }
X    if (wild_match(&name[1],"*:-*"))
X    {   /* alternatives, but non-null */
X        p = strchr(name,':');
X        /* ASSERT that p != NULL */
X        while (p[1] != '-')
X            p = strchr(&p[1],':');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q != (char *)NULL && strlen(q) > 0)
X            return q;
X        if (q != (char *)NULL)
X            free(q);
X        return strcopy(&p[2]);
X    }
X    if (wild_match(&name[1],"*-*"))
X    {   /* alternatives */
X        p = strchr(name,'-');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q != (char *)NULL)
X            return q;
X        return strcopy(&p[1]);
X    }
X    if (wild_match(&name[1],"*:=*"))
X    {   /* alternatives, assignment, non-null */
X        p = strchr(name,':');
X        /* ASSERT that p != NULL */
X        while (p[1] != '=')
X            p = strchr(&p[1],':');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q != (char *)NULL && strlen(q) > 0)
X            return q;
X        if (q != (char *)NULL)
X            free(q);
X        var_define0(&buffer[1],&p[2],0);
X        return strcopy(&p[2]);
X    }
X    if (wild_match(&name[1],"*=*"))
X    {   /* alternatives, assignment */
X        p = strchr(name,'=');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q != (char *)NULL)
X            return q;
X        var_define0(&buffer[1],&p[1],0);
X        return strcopy(&p[1]);
X    }
X    sprintf(buffer,"*:%c?*",ESCAPE_CHAR);
X    if (wild_match(&name[1],buffer))
X    {   /* alternatives with printing, non-null */
X        p = strchr(name,':');
X        /* ASSERT that p != NULL */
X        while (p[1] != '?')
X            p = strchr(&p[1],':');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q != (char *)NULL && strlen(q) != 0)
X            return q;
X        if (q != (char *)NULL)
X            free(q);
X        strcpy(buffer,&buffer[1]);
X        if (p[2] != '\0')
X            strcpy(buffer,&p[2]);
X        else    strcat(buffer,": parameter null or not set");
X        io_writestring(0,buffer);
X        io_writestring(0,"\n");
X        return strcopy("");
X    }
X    sprintf(buffer,"*%c??*",ESCAPE_CHAR);
X    if (wild_match(&name[1],buffer))
X    {   /* alternatives with printing */
X        p = strchr(name,'?');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q != (char *)NULL)
X            return q;
X        strcpy(buffer,&buffer[1]);
X        if (p[1] != '\0')
X            strcpy(buffer,&p[1]);
X        else    strcat(buffer,": parameter not set");
X        io_writestring(0,buffer);
X        io_writestring(0,"\n");
X        return strcopy("");
X    }
X    if (wild_match(&name[1],"*:+*"))
X    {   /* substitution, non-null */
X        p = strchr(name,':');
X        /* ASSERT that p != NULL */
X        while (p[1] != '+')
X            p = strchr(&p[1],':');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q != (char *)NULL && strlen(q) != 0)
X        {   /* do substitution */
X            free(q);
X            return strcopy(&p[2]);
X        }
X        if (q != (char *)NULL)
X            free(q);
X        return strcopy("");
X    }
X    if (wild_match(&name[1],"*+*"))
X    {   /* substitution */
X        p = strchr(name,'-');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q != (char *)NULL)
X        {   /* do substitution */
X            free(q);
X            return strcopy(&p[1]);
X        }
X        return strcopy("");
X    }
X    if (wild_match(&name[1],"*##*"))
X    {   /* cut from beginning */
X        p = strchr(name,'#');
X        while (p[1] != '#')
X            p = strchr(&p[1],'#');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q == (char *)NULL)
X            return q;
X        for (temp = strlen(q)-1; temp >= 0; temp--)
X        {   /* looks for first largest portion that matches */
X            strncpy(buffer,q,temp+1);
X            buffer[temp+1] = '\0';
X            if (wild_match(buffer,&p[2]))
X            {   /* found a match? */
X                strcpy(buffer,&q[temp+1]);
X                free(q);
X                return strcopy(buffer);
X            }
X        }
X        return q;
X    }
X    if (wild_match(&name[1],"*#*"))
X    {   /* cut from beginning */
X        p = strchr(name,'#');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q == (char *)NULL)
X            return q;
X        for (temp = 0; temp < strlen(q); temp++)
X        {   /* looks for first portion that matches */
X            strncpy(buffer,q,temp+1);
X            buffer[temp+1] = '\0';
X            if (wild_match(buffer,&p[1]))
X            {   /* found a match? */
X                strcpy(buffer,&q[temp+1]);
X                free(q);
X                return strcopy(buffer);
X            }
X        }
X        return q;
X    }
X    if (wild_match(&name[1],"*%%*"))
X    {   /* cut from beginning */
X        p = strchr(name,'%');
X        while (p[1] != '%')
X            p = strchr(&p[1],'%');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q == (char *)NULL)
X            return q;
X        for (temp = 0; temp < strlen(q); temp++)
X        {   /* looks for first largest portion that matches */
X            strcpy(buffer,&q[temp]);
X            if (wild_match(buffer,&p[2]))
X            {   /* found a match? */
X                strncpy(buffer,q,strlen(q)-strlen(buffer));
X                buffer[strlen(q)-strlen(buffer)] = '\0';
X                free(q);
X                return strcopy(buffer);
X            }
X        }
X        return q;
X    }
X    if (wild_match(&name[1],"*%*"))
X    {   /* cut from beginning */
X        p = strchr(name,'%');
X        /* ASSERT that p != NULL */
X        strncpy(buffer,name,(int)(p-name));
X        buffer[(int)(p-name)] = '\0';
X        q = var_getval(buffer,1);
X        if (q == (char *)NULL)
X            return q;
X        for (temp = strlen(q)-1; temp >= 0; temp--)
X        {   /* looks for first portion that matches */
X            strcpy(buffer,&q[temp]);
X            if (wild_match(buffer,&p[1]))
X            {   /* found a match? */
X                strncpy(buffer,q,strlen(q)-strlen(buffer));
X                buffer[strlen(q)-strlen(buffer)] = '\0';
X                free(q);
X                return strcopy(buffer);
X            }
X        }
X        return q;
X    }
X    
X    return (char *)NULL;
X}   /* end of var_expression */
X
Xchar *var_getval(name,flag)
Xregister char *name;
Xint flag;
X{
X    register char *value;
X
X    if (flag)
X        value = var_expression(name);
X    else value = (char *)NULL;
X    if (value == (char *)NULL)
X        value = var_special(name);
X    if (value == (char *)NULL)
X        value = var_normal(name);
X    return value;
X}   /* end of var_getval */
X
Xchar *var_reference(dont_expand)
Xint dont_expand;
X{
X    int rc,count,bracketed;
X    register char *p;
X    char tomatch;
X    char word[BUFSIZ],buf[2];
X
X    bracketed = 0;
X    buf[1] = '\0';
X    p = word;
X    *p++ = '$';
X    rc = io_getchar(0);
X    if (rc == SYM_EOF)
X    {   /* end of file? */
X        return (char *)NULL;
X    }
X    else if (rc == '{' || rc == '(')
X    {   /* a "quoted" pair */
X        bracketed++;
X        if (rc == '{')
X            tomatch = '}';
X        else    tomatch = ')';
X        rc = io_getchar(0);
X        while (rc != SYM_EOF && rc != tomatch && rc != '\n')
X        {   /* build up the variable name */
X#ifdef  GEMDOS
X            if (rc == '\r')
X                break;
X#endif  /* GEMDOS */
X            *p++ = rc;
X            if (p >= &word[sizeof word])
X            {   /* enough space? */
X                errmsg(0,LOC("var_reference"),"name too long");
X                p--;
X                buf[0] = rc;
X                io_savestring(buf);
X                break;
X            }
X            rc = io_getchar(0);
X        }
X#ifdef  GEMDOS
X        if (rc == '\r' || rc == '\n')
X#else
X        if (rc == '\n')
X#endif  /* GEMDOS */
X        {   /* put eol back */
X            buf[0] = '\n';
X            io_savestring(buf);
X        }
X        if (rc == SYM_EOF)
X            return (char *)NULL;
X    }
X    else if (isdigit(rc))
X    {   /* numerical variable? */
X        *p++ = rc;
X    }
X    else if (isalpha(rc) || rc == '_')
X    {   /* named variable */
X        *p++ = rc;
X        while ((rc = io_getchar(0)) != SYM_EOF)
X        {   /* get rest of word */
X            if (rc == '[')
X            {   /* a bracketed subscript */
X                count = 1;
X                *p++ = rc;
X                if (p >= &word[sizeof word])
X                {   /* enough space? */
X                    errmsg(0,LOC("var_reference"),"name too long");
X                    p--;
X                    buf[0] = rc;
X                    io_savestring(buf);
X                    break;
X                }
X                while ((rc = io_getchar(0)) != SYM_EOF)
X                {   /* rest of subscript... */
X                    if (rc == '[')
X                        count++;
X                    *p++ = rc;
X                    if (p >= &word[sizeof word])
X                    {   /* enough space? */
X                        errmsg(0,LOC("var_reference"),"name too long");
X                        p--;
X                        buf[0] = rc;
X                        io_savestring(buf);
X                        break;
X                    }
X                    if (rc == ']')
X                        count--;
X                    if (count == 0)
X                        break;
X                }
X                if (rc == ']')
X                    rc = io_getchar(0);
X                break;
X            }
X            if (!isalpha(rc) && !isdigit(rc) && rc != '_')
X                break;
X            *p++ = rc;
X            if (p >= &word[sizeof word])
X            {   /* enough space? */
X                errmsg(0,LOC("var_reference"),"name too long");
X                p--;
X                buf[0] = rc;
X                io_savestring(buf);
X                break;
X            }
X        }
X        if (rc == SYM_EOF)
X            return (char *)NULL;
X        buf[0] = rc;
X        io_savestring(buf);
X    }
X    else
X    {   /* special variable */
X        *p++ = rc;
X    }
X    *p = '\0';
X
X    if (!dont_expand)
X    {   /* if to expand variable */
X        p = var_getval(word,1);
X        if (p != (char *)NULL)
X        {   /* if found */
X            io_savestring(p);
X            free(p);
X            return (char *)NULL;
X        }
X    }
X    rc = strlen(word);
X    if (bracketed)
X        rc += 2;
X    p = new_string(rc+1);
X    if (p == (char *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("var_reference"));
X        return (char *)NULL;
X    }
X    if (!bracketed)
X        strcpy(p,word);
X    else sprintf(p,"${%s}",&word[1]);
X    return p;
X}   /* end of var_reference */
X
Xstatic void var_0dump(vp,type,flag)
Xregister struct variable *vp;
Xint type;
Xint flag;
X{
X    if (vp == (struct variable *)NULL)
X        return;
X    if (vp->left != (struct variable *)NULL)
X        var_0dump(vp->left,type,flag);
X    if (vp->type != TYPE_DELETED)
X    {   /* if variable really there */
X        if (type == 0 || ((type & vp->type) != 0))
X            dump_var(vp,flag);
X    }
X    if (vp->right != (struct variable *)NULL)
X        var_0dump(vp->right,type,flag);
X}   /* end of var_0dump */
X
Xvoid var_dump(type,flag)
Xint type;
Xint flag;
X{
X    struct varstack *vs;
X
X    for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X        var_0dump(vs->table,type,flag);
X}   /* end of var_dump */
X
Xvoid var_readenv()
X{
X    extern char **environ;
X    register int i;
X#ifdef  GEMDOS
X    char buffer[16];
X#endif  /* GEMDOS */
X
X#ifdef  GEMDOS
X    sprintf(buffer,"%lx",BP->p_parent);
X    var_define0("PPID",buffer,0);
X#endif  /* GEMDOS */
X    if (environ != (char **)NULL)
X    {   /* if an environment is present */
X        for (i = 0; environ[i] != (char *)NULL; i++)
X        {   /* look through environment variables */
X#ifdef  MWC_ARGV
X            if (strncmp(environ[i],"ARGV=",5) == 0)
X                break;
X#endif  /* MWC_ARGV */
X            var_define(environ[i],TYPE_EXPORTED);
X        }
X    }
X}   /* end of var_readenv */
X
Xstatic  int     e_length;
Xstatic  char    *e_area,*e_last;
X
Xstatic void e_getlength(vp)
Xregister struct variable *vp;
X{
X    if (vp == (struct variable *)NULL)
X        return;
X    if (vp->left != (struct variable *)NULL)
X        e_getlength(vp->left);
X    if (vp->type != TYPE_DELETED)
X    {   /* if variable really there */
X        if ((vp->type & TYPE_EXPORTED) || (vp->cmd == cmd_count))
X            e_length += strlen(vp->name)+strlen(vp->value)+2;
X    }
X    if (vp->right != (struct variable *)NULL)
X        e_getlength(vp->right);
X}   /* end of e_getlength */
X
Xstatic void e_fillarea(vp)
Xregister struct variable *vp;
X{
X    if (vp == (struct variable *)NULL)
X        return;
X    if (vp->left != (struct variable *)NULL)
X        e_fillarea(vp->left);
X    if (vp->type != TYPE_DELETED)
X    {   /* if variable really there */
X        if ((vp->type & TYPE_EXPORTED) || (vp->cmd == cmd_count))
X        {   /* put into table */
X            sprintf(e_last,"%s=%s",vp->name,vp->value);
X            while (*e_last)
X                e_last++;
X            e_last++;
X        }
X    }
X    if (vp->right != (struct variable *)NULL)
X        e_fillarea(vp->right);
X}   /* end of e_fillarea */
X
Xchar *var_makeenv()
X{
X    struct varstack *vs;
X
X    e_length = 2;
X    for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X        e_getlength(vs->table);
X    e_area = new_string(e_length);
X    if (e_area == (char *)NULL)
X    {   /* enough memory */
X        errmsg(SHERR_NOMEM,LOC("var_makeenv"));
X        return (char *)NULL;
X    }
X    e_last = e_area;
X    for (vs = base_env.var; vs != (struct varstack *)NULL; vs = vs->next)
X        e_fillarea(vs->table);
X    *e_last = '\0';
X    return e_area;
X}   /* end of var_makeenv */
X
Xvoid var_tablefree(vp)
Xregister struct variable *vp;
X{
X    if (vp == (struct variable *)NULL)
X        return;
X    if (vp->left != (struct variable *)NULL)
X        var_tablefree(vp->left);
X    if (vp->right != (struct variable *)NULL)
X        var_tablefree(vp->right);
X    if (vp->value != (char *)NULL)
X        free(vp->value);
X    if (vp->name != (char *)NULL)
X        free(vp->name);
X    free(vp);
X}   /* end of var_tablefree */
END_OF_FILE
if test 50361 -ne `wc -c <'var.c'`; then
    echo shar: \"'var.c'\" unpacked with wrong size!
fi
# end of 'var.c'
fi
echo shar: End of archive 11 \(of 11\).
cp /dev/null ark11isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0