[comp.sources.amiga] v02i022: shell - cshell-like command processor V2.11, Part01/02

page@swan.ulowell.edu (Bob Page) (10/25/88)

Submitted-by: dillon@cory.berkeley.edu (Matt Dillon)
Posting-number: Volume 2, Issue 22
Archive-name: unix/shell211.1

# This is a shell archive.  Remove anything before this line
# then unpack it by saving it in a file and typing "sh file"
# (Files unpacked will be owned by you and have default permissions).
# This archive contains the following files:
#	examples.txt
#	execom.c
#	fexec1.c
#	fexec2.asm
#	globals.c
#	hat.c
#	main.c
#	run.c
#	set.c
#	shell.h
#	sort.c
#	sub.c
#	tags
#
if `test ! -s examples.txt`
then
echo "writing examples.txt"
cat > examples.txt << '\Rogue\Monster\'
EXAMPLE FILES

------------------------------------------------------------------------
EXAMPLE INTERNAL COMMAND OVERIDING
------------------------------------------------------------------------

#Make CD not only do it's normal function, but also a PWD.

alias cd "%var \\cd $var;pwd"

------------------------------------------------------------------------
.LOGIN
------------------------------------------------------------------------

echo "shells, Matt"
alias l     "%var if $var;echo $var;else;echo *;endif"
alias c     "echo ^l"
alias cc    "cd ram:;assign c: cb:c"
alias wb    "cd ram:;assign c: sys:c"
alias ed    "run ED"
set dest ram:a
set temp ram:
set ld cb:clib
set incdir cb:include
set libs +$ld/lc.lib
set ops  ""


------------------------------------------------------------------------
RAM.SH
------------------------------------------------------------------------
cp c:run ram:; cp c:assign ram:; cp c:cp ram:; assign c: ram:


------------------------------------------------------------------------
C1.SH
------------------------------------------------------------------------
foreach y ( $_passed ) "echo $y;lc1 -o$temp -i$incdir/ -i$incdir/lattice/ $y";echo DONE


------------------------------------------------------------------------
C2.SH
------------------------------------------------------------------------
foreach y ( $_passed ) "echo $y;lc2 -s -v $temp$y";echo DONE


------------------------------------------------------------------------
LD.SH
------------------------------------------------------------------------
blink $ld/lstartup.obj+$_passed library $ld/lc.lib+$ld/amiga.lib $ops to $dest


------------------------------------------------------------------------
LDALL.SH
------------------------------------------------------------------------
set O "";foreach y ( *.o ) "set O $O+$y";strtail O + $O
ld $O;unset O


\Rogue\Monster\
else
  echo "will not over write examples.txt"
fi
if [ `wc -c examples.txt | awk '{printf $1}'` -ne 1870 ]
then
echo `wc -c examples.txt | awk '{print "Got " $1 ", Expected " 1870}'`
fi
if `test ! -s execom.c`
then
echo "writing execom.c"
cat > execom.c << '\Rogue\Monster\'

/*
 * EXECOM.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 *    Handles command parsing.
 *
 *
 */

#include "shell.h"

#define ST_COND   0x01
#define ST_NAME   0x02
#define ST_EXA	  0x04	    /*	exact match required	*/


struct COMMAND {
    int (*func)();
    short minargs;
    short stat;
    int   val;
    char *name;
};

extern char *format_insert_string();
extern char *mpush(), *exarg();

extern int do_run(), do_number();
extern int do_quit(), do_set_var(), do_unset_var(), do_setenv(), do_unsetenv();
extern int do_printenv(), do_echo(), do_source(), do_mv();
extern int do_cd(), do_rm(), do_mkdir(), do_history();
extern int do_mem(), do_cat(), do_dir();
extern int do_foreach(), do_return(), do_if(), do_label(), do_goto();
extern int do_forever(), do_inc(), do_abortline();
extern int do_input(), do_ver(), do_sleep(), do_help();
extern int do_strhead(), do_strtail(), do_cp();
extern int do_comment(), do_shellstat(), do_ipc(), do_cldres();

static struct COMMAND Command[] = {
    do_run	, 0,  ST_NAME,	  0 ,	"\001",
    do_number	, 0,  0,	  0 ,	"\001",
    do_quit	, 0,  ST_EXA,	  0 ,	"quit",
    do_quit	, 0,  ST_EXA,	  0 ,	"exit",
    do_set_var	, 0,  0, LEVEL_SET  ,	"set",
    do_unset_var, 0,  0, LEVEL_SET  ,	"unset",
    do_setenv	, 2,  0,	  0 ,	"setenv",
    do_unsetenv , 0,  0,	  0 ,	"unsetenv",
    do_printenv , 0,  0,	  0 ,	"printenv",
    do_set_var	, 0,  0, LEVEL_ALIAS,	"alias",
    do_unset_var, 0,  0, LEVEL_ALIAS,	"unalias",
    do_echo	, 0,  0,	  0 ,	"echo",
    do_source	, 0,  0,	  0 ,	"source",
    do_mv	, 2,  ST_EXA,	  0 ,	"mv",
    do_cd	, 0,  0,	  0 ,	"cd",
    do_cd	, 0,  0,	 -1 ,	"pwd",
    do_rm	, 0,  ST_EXA,	  0 ,	"rm",
    do_mkdir	, 0,  ST_EXA,	  0 ,	"mkdir",
    do_history	, 0,  0,	  0 ,	"history",
    do_mem	, 0,  0,	  0 ,	"mem",
    do_cat	, 0,  0,	  0 ,	"cat",
    do_cp	, 1,  ST_EXA,	  0 ,	"cp",
    do_dir	, 0,  0,	  0 ,	"dir",
    do_dir	, 0,  0,	 -1 ,	"devinfo",
    do_foreach	, 3,  0,	  0 ,	"foreach",
    do_forever	, 1,  0,	  0 ,	"forever",
    do_return	, 0,  ST_EXA,	  0 ,	"return",
    do_ipc	, 1,  0,	  0 ,	"ipc",
    do_cldres	, 0,  0,	  0 ,	"cldres",
    do_if	, 1,  ST_COND,	  0 ,	"if",
    do_if	, 0,  ST_COND,	  1 ,	"else",
    do_if	, 0,  ST_COND,	  2 ,	"endif",
    do_label	, 1,  ST_COND,	  0 ,	"label",
    do_goto	, 1,  0,	  0 ,	"goto",
    do_strhead	, 3,  0,	  0 ,	"strhead",
    do_strtail	, 3,  0,	  0 ,	"strtail",
    do_inc	, 1,  0,	  1 ,	"inc",
    do_inc	, 1,  0,	  -1,	"dec",
    do_input	, 1,  0,	  0,	"input",
    do_ver	, 0,  0,	  0,	"version",
    do_sleep	, 0,  0,	  0,	"sleep",
    do_help	, 0,  0,	  0,	"help",
    do_abortline, 0,  0,	  0,	"abortline",
    do_comment	, 2,  0,	  0,	"comment",
    do_shellstat, 0,  0,	  0,	"shellstat",
    NULL	, 0,  0,	  0 ,	NULL
};


static unsigned char elast;	     /* last end delimeter */
static char Cin_ispipe, Cout_ispipe;

exec_command(base)
char *base;
{
    register char *scr;
    register int i;
    char buf[32];

    if (!H_stack) {
	add_history(base);
	sprintf(buf, "%ld", H_tail_base + H_len);
	set_var(LEVEL_SET, V_HISTNUM, buf);
    }
    scr = malloc((strlen(base) << 2) + 2);    /* 4X */
    preformat(base, scr);
    i = fcomm(scr, 1);
    return ((i) ? -1 : 1);
}


isalphanum(c)
char c;
{
    if (c >= '0' && c <= '9')
	return (1);
    if (c >= 'a' && c <= 'z')
	return (1);
    if (c >= 'A' && c <= 'Z')
	return (1);
    if (c == '_')
	return (1);
    return (0);
}

preformat(s, d)
register char *s, *d;
{
    register short si, di, qm;

    si = di = qm = 0;
    while (s[si] == ' ' || s[si] == 9)
	++si;
    while (s[si]) {
	if (qm && s[si] != '\"' && s[si] != '\\') {
	    d[di++] = s[si++] | 0x80;
	    continue;
	}
	switch (s[si]) {
	case ' ':
	case 9:
	    d[di++] = ' ';
	    while (s[si] == ' ' || s[si] == 9)
		++si;
	    if (s[si] == 0 || s[si] == '|' || s[si] == ';')
		--di;
	    break;
	case '*':
	case '?':
	    d[di++] = 0x80;	 /* follow thru */
	case '!':
	    d[di++] = s[si++];
	    break;
	case '#':
	    d[di++] = '\0';
	    while (s[si])
		++si;
	    break;
	case ';':
	case '|':
	    d[di++] = s[si++];
	    while (s[si] == ' ' || s[si] == 9)
		++si;
	    break;
	case '\\':
	    d[di++] = s[++si] | 0x80;
	    if (s[si]) ++si;
	    break;
	case '\"':
	    qm = 1 - qm;
	    ++si;
	    break;
	case '^':
	    d[di++] = s[++si] & 0x1F;
	    if (s[si]) ++si;
	    break;
	case '$':         /* search end of var name and place false space */
	    d[di++] = 0x80;
	    d[di++] = s[si++];
	    while (isalphanum(s[si]))
		d[di++] = s[si++];
	    d[di++] = 0x80;
	    break;
	default:
	    d[di++] = s[si++];
	    break;
	}
    }
    d[di++] = 0;
    d[di]   = 0;
#ifdef DEBUG
    if (SDebug & 0x01) {
	fhprintf (Cerr, "PREFORMAT: %ld :%s:\n", strlen(d), d);
    }
#endif
}

/*
 * process formatted string.  ' ' is the delimeter.
 *
 *    0: check '\0': no more, stop, done.
 *    1: check $.     if so, extract, format, insert
 *    2: check alias. if so, extract, format, insert. goto 1
 *    3: check history or substitution, extract, format, insert. goto 1
 *
 *    4: assume first element now internal or disk based command.
 *
 *    5: extract each ' ' or 0x80 delimited argument and process, placing
 *	 in av[] list (except 0x80 args appended).  check in order:
 *
 *	       '$'         insert string straight
 *	       '>'         setup stdout
 *	       '>>'        setup stdout flag for append
 *	       '<'         setup stdin
 *	       '*' or '?'  do directory search and insert as separate args.
 *
 *	       ';' 0 '|'   end of command.  if '|' setup stdout
 *			    -execute command, fix stdin and out (|) sets
 *			     up stdin for next guy.
 */


fcomm(str, freeok)
register char *str;
{
    static short alias_count;
    char *istr;
    char *nextstr;
    char *command;
    char *pend_alias = NULL;
    char err = 0;

    ++alias_count;
    mpush_base();
    if (*str == 0)
	goto done1;
step1:
    if (alias_count == MAXALIAS) {
	Eputs("Alias Loop");
	err = 20;
	goto done1;
    }
    if (*str == '$') {
	if (istr = get_var (LEVEL_SET, str + 1))
	    str = format_insert_string(str, istr, &freeok);
    }
    istr = NULL;
    if (*(unsigned char *)str < 0x80)
	istr = get_var (LEVEL_ALIAS, str);  /* only if not \command */
    *str &= 0x7F;			   /* remove \ teltail	   */
    if (istr) {
	if (*istr == '%') {
	    pend_alias = istr;
	} else {
	    str = format_insert_string(str, istr, &freeok);
	    goto step1;
	}
    }
    if (*str == '!') {
	istr = get_history(str);
	replace_head(istr);
	str = format_insert_string(str, istr, &freeok);
	goto step1;
    }
    nextstr = str;
    command = exarg(&nextstr);
    if (*command == 0)
	goto done0;
    if (pend_alias == 0) {
	register int ccno;
	ccno = find_command(command);
	if (Command[ccno].stat & ST_COND)
	    goto skipgood;
    }
    if (SDisable) {
	while (elast && elast != ';' && elast != '|')
	    exarg(&nextstr);
	goto done0;
    }
skipgood:
    {
	register char *arg, *ptr, *scr;
	short redir;
	short doexpand;
	short cont;
	short inc;

	ac = 1;
	av[0] = command;
step5:						/* ac = nextac */
	if (!elast || elast == ';' || elast == '|')
	    goto stepdone;

	av[ac] = NULL;
	cont = 1;
	doexpand = redir = inc = 0;

	while (cont && elast) {
	    ptr = exarg(&nextstr);
	    inc = 1;
	    arg = "";
	    cont = (elast == 0x80);
	    switch (*ptr) {
	    case '<':
		redir = -2;	     /* -2 so ++ still keeps it negative */
	    case '>':
		++redir;	     /* normal >  */
		arg = ptr + 1;
		if (*arg == '>') {
		    redir = 2;	      /* append >> (not impl yet) */
		    ++arg;
		}
		cont = 1;
		break;
	    case '$':
		if ((arg = get_var(LEVEL_SET, ptr + 1)) == NULL)
		    arg = ptr;
		break;
	     case '*':
	     case '?':
		doexpand = 1;
		arg = ptr;
		break;
	    default:
		arg = ptr;
		break;
	    }

	    /* Append arg to av[ac] */

	    for (scr = arg; *scr; ++scr)
		*scr &= 0x7F;
	    if (av[ac]) {
		register char *old = av[ac];
		av[ac] = mpush(strlen(arg)+1+strlen(av[ac]));
		strcpy(av[ac], old);
		strcat(av[ac], arg);
	    } else {
		av[ac] = mpush(strlen(arg)+1);
		strcpy(av[ac], arg);
	    }
	    if (elast != 0x80)
		break;
	}

	/* process expansion */

	if (doexpand) {
	    char **eav, **ebase;
	    int eac;

	    eav = ebase = expand(av[ac], &eac);
	    inc = 0;
	    if (eav) {
		if (ac + eac + 2 > MAXAV) {
		    ierror (NULL, 506);
		    err = 1;
		} else {
		    QuickSort(eav, eac);
		    for (; eac; --eac, ++eav)
			av[ac++] = strcpy(mpush(strlen(*eav)+1), *eav);
		}
		free_expand (ebase);
	    }
	}

	/* process redirection	*/

	if (redir && !err) {
	    register char *file = (doexpand) ? av[--ac] : av[ac];

	    if (redir < 0) {
		Cin_name = file;
	    } else {
		Cout_name = file;
		Cout_append = (redir == 2);
	    }
	    inc = 0;
	}

	/* check elast for space */

	if (inc) {
	    ++ac;
	    if (ac + 2 > MAXAV) {
		ierror (NULL, 506);
		err = 1;		/* error condition */
		elast = 0;		/* don't process any more arguemnts */
	    }
	}
	if (elast == ' ')
	    goto step5;
    }
stepdone:
    av[ac] = NULL;

    /* process pipes via files */

    if (elast == '|' && !err) {
	static int which;	      /* 0 or 1 in case of multiple pipes */
	which = 1 - which;
	Cout_name = (which) ? Pipe1 : Pipe2;
	Cout_ispipe = 1;
    }

    if (err)
	goto done0;

    {
	register long i, len;
	char save_elast;
	register char *avline;

	save_elast = elast;
	for (i = len = 0; i < ac; ++i)
	    len += strlen(av[i]) + 1;
	avline = malloc(len+2);
	for (len = 0, i = ((pend_alias) ? 1 : 0); i < ac; ++i) {
#ifdef DEBUG
	    if (SDebug & 0x02) fhprintf (Cerr, "AV[%2ld] %ld :%s:\n", i, strlen(av[i]), av[i]);
#endif
	    strcpy(avline + len, av[i]);
	    len += strlen(av[i]);
	    if (i + 1 < ac)
		avline[len++] = ' ';
	}
	avline[len] = 0;
	if (pend_alias) {                               /* special % alias */
	    register char *ptr, *scr;
	    for (ptr = pend_alias; *ptr && *ptr != ' '; ++ptr);
	    set_var (LEVEL_SET, pend_alias + 1, avline);
	    free (avline);
	    scr = malloc((strlen(ptr) << 2) + 2);
	    preformat (ptr, scr);
	    fcomm (scr, 1);
	    unset_var (LEVEL_SET, pend_alias + 1);
	} else {					/* normal command  */
	    register short ccno;
	    register long cin, cout;

	    ccno = find_command (command);
	    if ((Command[ccno].stat & ST_NAME) == 0) {
		if (Cin_name) {
		    cin = Cin;
		    Cin = Open(Cin_name, 1005);
		    if (Cin == 0) {
			ierror (NULL, 504);
			err = 1;
			Cin = cin;
			Cin_name = NULL;
		    }
		}
		if (Cout_name) {
		    cout = Cout;
		    if (Cout_append) {
			if (Cout = Open(Cout_name, 1005))
			Seek(Cout, 0, 1);
		    } else {
			Cout = Open(Cout_name, 1006);
		    }
		    if (Cout == NULL) {
			err = 1;
			ierror (NULL, 504);
			Cout = cout;
			Cout_name = NULL;
			Cout_append = 0;
		    }
		}
	    }
	    if (ac < Command[ccno].minargs + 1) {
		ierror (NULL, 500);
		err = -1;
	    } else {
		i = (*Command[ccno].func)(avline, Command[ccno].val);
		if (i < 0)
		    i = 20;
		err = i;
	    }
	    free (avline);
	    if (Exec_ignoreresult == 0 && Lastresult != err) {
		Lastresult = err;
		seterr();
	    }
	    if ((Command[ccno].stat & ST_NAME) == 0) {
		if (Cin_name) {
		    Close(Cin);
		    Cin = cin;
		}
		if (Cout_name) {
		    Close(Cout);
		    Cout = cout;
		    Cout_append = 0;
		}
	    }
	}
	if (Cin_ispipe && Cin_name)
	    DeleteFile(Cin_name);
	if (Cout_ispipe) {
	    Cin_name = Cout_name;	  /* ok to assign.. static name */
	    Cin_ispipe = 1;
	} else {
	    Cin_name = NULL;
	}
	Cout_name = NULL;
	Cout_ispipe = Cout_append = 0;
	elast = save_elast;
    }
    mpop_tobase();                      /* free arguments   */
    mpush_base();                       /* push dummy base  */

done0:
    {
	register char *str;
#ifdef DEBUG
	if (SDebug & 0x10)
	    printf ("err = %ld, E_stack = %ld\n", err, E_stack);
#endif
	if (err && E_stack == 0) {
	    str = get_var(LEVEL_SET, V_EXCEPT);
	    if (err >= ((str)?atoi(str):1)) {
		if (str) {
		    ++H_stack;
		    ++E_stack;
		    exec_command(str);
		    --E_stack;
		    --H_stack;
		} else {
		  Exec_abortline = 1;
		}
	    }
	}
#ifdef DEBUG
	if (SDebug & 0x10) {
	    printf ("elast = %ld  Exec_abortline = %ld\n", elast, Exec_abortline);
	    printf ("nextstr = %s\n", nextstr);
	}
#endif
	if (elast != 0 && Exec_abortline == 0)
	    err = fcomm(nextstr, 0);
	Exec_abortline = 0;
	if (Cin_name)
	    DeleteFile(Cin_name);
	Cin_name = NULL;
	Cin_ispipe = 0;
    }
done1:
    mpop_tobase();
    if (freeok)
	free(str);
    --alias_count;
    return ((int)err);                  /* TRUE = error occured    */
}


char *
exarg(ptr)
unsigned char **ptr;
{
    register unsigned char *end;
    register unsigned char *start;

    start = end = *ptr;
    while (*end && *end != 0x80 && *end != ';' && *end != '|' && *end != ' ')
	++end;
    elast = *end;
    *end = '\0';
    *ptr = end + 1;
    return ((char *)start);
}

static char **Mlist;

mpush_base()
{
    register char *str;

    str = malloc(5);
    *(char ***)str = Mlist;
    str[4] = 0;
    Mlist = (char **)str;
}

char *
mpush(bytes)
{
    register char *str;

    str = malloc(5 + bytes);
    *(char ***)str = Mlist;
    str[4] = 1;
    Mlist = (char **)str;
    return (str + 5);
}

mpop_tobase()
{
    register char *next;

    while (Mlist) {
	next = *Mlist;
	if (((char *)Mlist)[4] == 0) {
	    free (Mlist);
	    Mlist = (char **)next;
	    break;
	}
	free (Mlist);
	Mlist = (char **)next;
    }
}


/*
 * Insert 'from' string in front of 'str' while deleting the
 * first entry in 'str'.  if freeok is set, then 'str' will be
 * free'd
 */

char *
format_insert_string(str, from, freeok)
char *str;
char *from;
int *freeok;
{
    register char *new1, *new2;
    register unsigned char *strskip;
    register short len;

    for (strskip = (UBYTE *)str; *strskip && *strskip != ' ' && *strskip != ';' && *strskip != '|' && *strskip != 0x80; ++strskip);
    len = strlen(from);
    new1 = malloc((len << 2) + 2);
    preformat(from, new1);
    len = strlen(new1) + strlen(strskip);
    new2 = malloc(len+2);
    strcpy(new2, new1);
    strcat(new2, strskip);
    new2[len+1] = 0;
    free (new1);
    if (*freeok)
	free (str);
    *freeok = 1;
    return (new2);
}


find_command(str)
register char *str;
{
    register short i;
    register short len = strlen(str);
    register struct COMMAND *cmd;

    if (*str >= '0'  &&  *str <= '9')
	return (1);
    for (i = 0, cmd = Command; cmd->func; ++cmd, ++i) {
	if (cmd->stat & ST_EXA) {
	    if (strcmp(str, cmd->name) == 0)
		return((int)i);
	} else {
	    if (strncmp (str, cmd->name, len) == 0)
		return ((int)i);
	}
    }
    return (0);
}


do_help()
{
    register struct COMMAND *com;

    for (com = &Command[2]; com->func; ++com)
	fhprintf (Cout, "%s ", com->name);
    Oputs ("");
    return (0);
}


\Rogue\Monster\
else
  echo "will not over write execom.c"
fi
if [ `wc -c execom.c | awk '{printf $1}'` -ne 14743 ]
then
echo `wc -c execom.c | awk '{print "Got " $1 ", Expected " 14743}'`
fi
if `test ! -s fexec1.c`
then
echo "writing fexec1.c"
cat > fexec1.c << '\Rogue\Monster\'

/*
 * FEXEC1.C
 *
 *  (C)CopyRight 1987 Matthew Dillon, All rights reserved.
 *
 *    wait() and fexecv()
 *
 *    This code originated from Manx's fexecv code.  I claim credit only
 *    for the major modifications I've done to it.
 */

#include "shell.h"
#include <libraries/dosextens.h>

typedef struct CommandLineInterface CLI;
typedef struct FileHandle FH;

static int ret_val;

wait()
{
    return(ret_val);
}


fexecv(cmd, argv, stdin_str, stdout_str, stdout_append)
char *cmd, **argv;
char *stdin_str, *stdout_str;
{
    register CLI *cli;
    register char **ap, *cp, *arg;
    PROC *pp;
    APTR sav_ret;
    BPTR sav_CIS, sav_COS;
    long save_stdin_buf, save_stdin_pos, save_stdin_end;
    long len, seg, sav_seg;
    long openmode;
    long *stk;
    FH *fhp, *stdin, *stdout;
    char buf[40];

    pp = FindTask(0L);
    if ((cli = (CLI *)((long)pp->pr_CLI << 2)) == 0)
	return(-1);
    if ((seg = LoadIt(cmd)) == 0)
	return(-3);
    stdin = (FH *)((stdin_str)? Open(stdin_str, 1005) : pp->pr_CIS);
    if (!stdin) {
	fhprintf(Cerr, "Input redirection error\n");
	return(-4);
    }
    openmode = (stdout_append) ? 1005 : 1006;
    stdout= (FH *)((stdout_str)? Open(stdout_str, openmode) : pp->pr_COS);
    if (!stdout) {
	fhprintf(Cerr, "Output redirection error\n");
	if (stdin_str)
	    Close(stdin);
	return(-5);
    }
    if (stdout_append)
	Seek(stdout, 0, 1);
    sav_seg = cli->cli_Module;
    cli->cli_Module = seg;
    stk = (long *)AllocMem(4 * cli->cli_DefaultStack + 8, 0);
    *stk = 4 * cli->cli_DefaultStack + 8;
    stk = (long *)((long)stk + 4 * cli->cli_DefaultStack);
    stk[0] = 4 * cli->cli_DefaultStack;
    stk[1] = ((long *)pp->pr_ReturnAddr)[1];
    sav_ret = pp->pr_ReturnAddr;
    pp->pr_ReturnAddr = (APTR)stk;

    for (len = 1, ap = argv + 1; *ap; ++ap)  /* length of command line */
	len += strlen(*ap) + 1;
    cp = arg = malloc(len + 1);
    for (ap = argv + 1; *ap; ++ap) {
	strcpy(cp, *ap);
	strcat(cp, " ");
	cp += strlen(cp);
    }
    if (len > 199)                           /* BCPL parameter limit   */
	len = 199;
    arg[len-1] = '\n';                       /* for BCPL               */
    arg[len]   = 0;			     /* for C		       */
    cp = (char *)((long)cli->cli_CommandName << 2);
    movmem(cp, buf, 40);
    strcpy(cp + 1, cmd);
    cp[0] = strlen(cmd);

    fhp = (FH *)((long)stdin << 2);
    save_stdin_buf = fhp->fh_Buf;
    save_stdin_pos = fhp->fh_Pos;
    save_stdin_end = fhp->fh_End;

    fhp->fh_Buf = (long)AllocMem(202, 0) >> 2;
    bmov(arg, fhp->fh_Buf<<2, len);
    fhp->fh_Pos = 0;
    fhp->fh_End = len;

    sav_CIS = pp->pr_CIS;
    sav_COS = pp->pr_COS;

    pp->pr_CIS = (BPTR)stdin;
    pp->pr_COS = (BPTR)stdout;

    /*
     *	pr_Result2 must be NULL or RUN/NEWCLI think the command line is
     *	somewhere other than in the file handle.  The cli_Interactive
     *	field gets cleared sometimes (how???), and for some reason, signal
     *	31 is set sometimes and might cause inproper operation of RUN.
     */

    pp->pr_Result2 = NULL;
    cli->cli_Interactive = -1;
    SetSignal(0L, 0x80000000);

    ret_val = doexec(len, arg, (seg+1)<<2, stk);

    FreeMem(fhp->fh_Buf<<2, 202);
    fhp->fh_Buf = save_stdin_buf;
    fhp->fh_Pos = save_stdin_pos;
    fhp->fh_End = save_stdin_end;

    if (stdin_str)
	Close(stdin);
    if (stdout_str)
	Close(stdout);
    pp->pr_CIS = sav_CIS;
    pp->pr_COS = sav_COS;

    UnLoadSeg(cli->cli_Module);
    pp->pr_ReturnAddr = sav_ret;
    cli->cli_Module = sav_seg;
    free(arg);
    movmem(buf, cp, 40);
    return(0);
}

LoadIt(cmd)
char *cmd;
{
    long seg;

    mountrequest(0);
    if ((seg = LoadSeg(cmd)) == NULL) {
	register long lock;
	char buf[128];

	if (lock = FindIt(cmd, "", buf)) {
	    register long pardir = ParentDir(lock);
	    if (pardir) {
		register long oldir = CurrentDir(pardir);
		seg = LoadSeg(cmd);
		CurrentDir(oldir);
		UnLock(pardir);
	    }
	    UnLock(lock);
	}
    }
    mountrequest(1);
    return(seg);
}

/*
 *  Find a specific command (cmd) with extension (ext).  Returns
 *  a lock if found, else NULL.  Searches both the symbolic path and
 *  the CLI path.  The symbolic path is searched first.
 */

long
FindIt(cmd, ext, buf)
char *cmd;
char *ext;
char *buf;
{
    register long lock = 0;
    register char *p;
    PROC *myproc = FindTask(0);

    strcpy(buf, cmd);
    strcat(buf, ext);
    if (rindex(buf, ':') || rindex(buf, '/'))
	return(Lock(buf, ACCESS_READ));

    if ((p = get_var(LEVEL_SET, V_PATH)) == NULL)
	p = "";

#ifdef DEBUG
    if (SDebug)
	printf ("FindIt: try: %s\n", buf);
#endif
    while ((lock = Lock(buf, ACCESS_READ)) == 0 || !isfile(lock)) {
	register short n;
	if (lock)
	    UnLock(lock);
	if (*p == '\0') {
	    buf[0] = 0;
	    break;
	}
	for (n = 0; p[n] && p[n] != ','; ++n);
	strncpy(buf, p, n);
	buf[n] = 0;
	strcat(buf, cmd);
	strcat(buf, ext);
	p += n + (*p != 0);
#ifdef DEBUG
	if (SDebug)
	    printf ("FindIt: try: %s\n", buf);
#endif
    }
#ifdef DEBUG
    if (SDebug)
	puts ("FindIt 1");
#endif
    if (lock)
	return(lock);

    /*
     *	Search the CLI path by CurrentDir'ing each lock and
     *	attempting to lock the name
     */

#ifdef DEBUG
    if (SDebug)
	puts("SEARCH CLI");
#endif

    strcpy(buf, cmd);
    strcat(buf, ext);
    if (myproc->pr_CLI) {
	long oldir;
	long dupdir;
	register LOCK *dirlock = (LOCK *)(((CLI *)(myproc->pr_CLI << 2))->cli_CommandDir << 2);
	for (; dirlock; dirlock = (LOCK *)(dirlock->fl_Link << 2)) {
	    oldir = CurrentDir(((long *)dirlock)[1]);
	    lock = Lock(buf, ACCESS_READ);
	    CurrentDir(oldir);
	    if (lock)
		break;
	}
    }

#ifdef DEBUG
    if (SDebug)
	puts("FindIt done");
#endif
    return(lock);
}

static
isfile(lock)
long lock;
{
    register FIB *fib = (FIB *)malloc(sizeof(FIB));
    register int result = 0;

    if (fib) {
	if (Examine(lock, fib))
	    result = fib->fib_DirEntryType;
	free(fib);
    }
    return(result < 0);         /*  is it a file?   */
}

\Rogue\Monster\
else
  echo "will not over write fexec1.c"
fi
if [ `wc -c fexec1.c | awk '{printf $1}'` -ne 5948 ]
then
echo `wc -c fexec1.c | awk '{print "Got " $1 ", Expected " 5948}'`
fi
if `test ! -s fexec2.asm`
then
echo "writing fexec2.asm"
cat > fexec2.asm << '\Rogue\Monster\'

;FEXEC2.ASM
;

;  Again, this code originated from Manx, but has been heavily re-written
;

      xdef  _doexec
      xref  _LVOFreeMem
      xref  _SysRegs
      xref  _SysBase
      xref  _savsp
      xref  _savefp


;
;doexec(len, arg, (seg+1)<<2, stk)
;       D0    A0    A4        A7
;

        FAR data                ;because we totally trash A4 within the
        FAR code                ;routine.

_doexec:
   move.l   sp,_savefp            ;Save Frame Pointer
   movem.l  d2-d7/a2-a6,-(sp)    ;save Registers
   move.l   sp,_savsp             ;save current SP
   lea     _SysRegs,A0
   movem.l  (A0),D0-D7/A0-A6     ;Get System Startup Registers (BCPL stuff)
   move.l   _savefp,A0            ;A0 = frame pointer
   movem.l  4(A0),d0/a0/a4/a7    ;load parameters, including new SP
   move.l   d0,12(a1)            ;set length
   move.l   a0,d1                ;copy to dreg
   lsr.l    #2,d1                ;convert to BPTR
   move.l   d1,8(a1)             ;set ptr
   jsr      (a4)                 ;call new program
   movem.l  (sp)+,d2/d3          ;get stk siz and old sp
   move.l   sp,a1                ;save current sp
   move.l   _savsp,sp             ;get back our sp
   move.l   d0,-(sp)             ;save return code so we don't loose it
   sub.l    d2,a1                ;Backup from end of stack to start
   sub.l    #8,a1                ;point over header placed on stack
   move.l   (a1),d0              ;get size to free
   move.l   _SysBase,a6          ;get ExecBase
   jsr      _LVOFreeMem(a6)      ;Free memory allocated for stack
   move.l   (sp)+,d0             ;D0 = return code
   movem.l  (sp)+,D2-D7/A2-A6    ;restore Registers
   rts



\Rogue\Monster\
else
  echo "will not over write fexec2.asm"
fi
if [ `wc -c fexec2.asm | awk '{printf $1}'` -ne 1670 ]
then
echo `wc -c fexec2.asm | awk '{print "Got " $1 ", Expected " 1670}'`
fi
if `test ! -s globals.c`
then
echo "writing globals.c"
cat > globals.c << '\Rogue\Monster\'

/*
 * GLOBALS.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 *
 *    Most global variables.
 *
 */

#include "shell.h"

struct HIST *H_head, *H_tail;			/* HISTORY lists      */

struct PERROR Perror[] = {			/* error code->string */
    103,  "insufficient free storage",
    105,  "task table full",
    120,  "argument line invalid or too long",
    121,  "file is not an object module",
    122,  "invalid resident library during load",
    201,  "no default directory",
    202,  "object in use",
    203,  "object already exists",
    204,  "directory not found",
    205,  "object not found",
    206,  "bad stream name",
    207,  "object too large",
    209,  "action not known",
    210,  "invalid stream component name",
    211,  "invalid object lock",
    212,  "object not of required type",
    213,  "disk not validated",
    214,  "disk write protected",
    215,  "rename across devices",
    216,  "directory not empty",
    217,  "too many levels",
    218,  "device not mounted",
    219,  "seek error",
    220,  "comment too long",
    221,  "disk full",
    222,  "file delete protected",
    223,  "file write protected",
    224,  "file read protected",
    225,  "not a DOS disk",
    226,  "no disk",
    232,  "no more entries in directory",

    /* custom error messages */

    500,  "bad arguments",
    501,  "label not found",
    502,  "must be within source file",
    503,  "Syntax Error",
    504,  "redirection error",
    505,  "pipe error",
    506,  "too many arguments",
    507,  "destination not a directory",
    508,  "cannot mv a filesystem",
    509,  "Operation Aborted",
    510,  "Too many if's",
      0,  NULL
};

char  *av[MAXAV];	    /* Internal argument list		      */
int   Src_base[MAXSRC];     /* file pointers for source files	      */
long  Src_pos[MAXSRC];	    /* seek position storage for same	      */
char  If_base[MAXIF];	    /* If/Else stack for conditionals	      */
short H_len, H_tail_base;   /* History associated stuff 	      */
short H_stack;		    /* AddHistory disable stack 	      */
short E_stack;		    /* Exception disable stack		      */
short Src_stack, If_stack;  /* Stack Indexes			      */
short ac;		    /* Internal argc			      */
short SDebug;		    /* Debug mode			      */
short SDisable; 	    /* Disable com. execution (conditionals)  */
short Verbose;		    /* Verbose mode for source files	      */
long  Lastresult;	    /* Last return code 		      */
short Exec_abortline;	    /* flag to abort rest of line	      */
short Exec_ignoreresult;    /* flag to ignore result		      */
short Quit;		    /* Quit flag			      */
long  Cout, Cin, Cerr;	    /* Current input and output file handles  */
long  Cout_append;	    /* append flag for Cout		      */
long  Uniq;		    /* unique value			      */
char  *Cin_name, *Cout_name;/* redirection input/output name or NULL  */
char  *Pipe1, *Pipe2;	    /* the two pipe temp. files 	      */

short S_histlen = 20;	    /* Max # history entries		      */
short S_ignoreeof;

long SysRegs[16] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 };
long savsp, savefp;


\Rogue\Monster\
else
  echo "will not over write globals.c"
fi
if [ `wc -c globals.c | awk '{printf $1}'` -ne 3086 ]
then
echo `wc -c globals.c | awk '{print "Got " $1 ", Expected " 3086}'`
fi
if `test ! -s hat.c`
then
echo "writing hat.c"
cat > hat.c << '\Rogue\Monster\'

/*
 *    HAT.C
 *
 *    Modify the last history entry ^search^replace.  Called from
 *    MAIN.C
 *
 */

extern char *last_history_entry();

hat_replace(buf)
char *buf;
{
    char res[256];
    char *ptr, *hb, *repstr, *srchstr;
    int searchlen, replacelen;

    srchstr = buf + 1;
    for (repstr = srchstr; *repstr && *repstr != '^'; ++repstr);
    if (*repstr) {
	searchlen = repstr - srchstr;
	*repstr = '\0';
	++repstr;
    }
    replacelen = strlen(repstr);

    for (ptr = hb = last_history_entry(); *ptr; ++ptr) {
	if (strncmp(ptr, srchstr, searchlen) == 0) {
	    bmov(hb, res, ptr - hb);	     /* first part of history	*/
	    res[ptr-hb] = '\0';              /* add 0                   */
	    strcat(res, repstr);	     /* add replace string	*/
	    strcat(res, ptr+searchlen);      /* add rest of history	*/
	    strcpy(buf, res);		     /* copy			*/
	    puts (buf);
	    return(1);			     /* done			*/
	}
    }
    puts ("modifier failed");
    *buf = '\0';
    return(0);
}


\Rogue\Monster\
else
  echo "will not over write hat.c"
fi
if [ `wc -c hat.c | awk '{printf $1}'` -ne 992 ]
then
echo `wc -c hat.c | awk '{print "Got " $1 ", Expected " 992}'`
fi
if `test ! -s main.c`
then
echo "writing main.c"
cat > main.c << '\Rogue\Monster\'

/*
 * MAIN.C
 *
 *  (C)Copyright 1987 Matthew Dillon, All rights reserved
 *
 *  Contains initialization and other stuff.  Note that there is no
 *  support for workbench startup.  This isn't simply a matter of
 *  setting up a window.... to get this baby to work from workbench we
 *  would need to simulate an entire CLI (proc->pr_CLI must be valid).
 *
 */

#include "shell.h"
#include <libraries/dosextens.h>
#include <stdio.h>

extern long SetSignal();

char Inline[256];
static long Orig_dir;

extern int Enable_Abort;

main(argc, argv)
short argc;
register char *argv[];
{
    char *prompt;
    struct Process *proc;
    register short i;

    Enable_Abort = 0;
    proc = (PROC *)FindTask(0);
    if (proc->pr_CLI == NULL)              /* sorry, no WB startup */
	exit(1000);
    Orig_dir = proc->pr_CurrentDir;
    CurrentDir(DupLock(Orig_dir));
    init_vars();
    init();
    seterr();
    do_cd(NULL, -1);
    for (i = 1; i < argc; ++i) {
	if (strcmp(argv[i], "-c") == 0) {
	    Inline[0] = '\0';
	    for (++i; i < argc; ++i) {
		if (*argv[i] == '\"') {             /* CLI quotes?   */
		    strcat(Inline, argv[i]+1);       /* remove quotes */
		    Inline[strlen(Inline)-1] = '\0';
		} else {
		    strcat(Inline, argv[i]);
		}
		if (i + 1 < argc)
		    strcat(Inline, " ");
	    }
	    exec_command(Inline);
	    main_exit(0);
	}
	strcpy (Inline, "source ");
	strcat (Inline, argv[i]);
	av[0] = "source";
	av[1] = argv[i];
	do_source (Inline, 0);
    }
    for (;;) {
	if ((prompt = get_var (LEVEL_SET, V_PROMPT)) == NULL)
	    prompt = "echo -n \"% \"";
	/*
	 *
	 *  Removed, WaitForChar() is buggy
	 *
	if (CHECKBREAK()) {
	    while (WaitForChar(Input(), 10))
	    gets(Inline);
	}
	 */

	++H_stack;
	++Exec_ignoreresult;
	exec_command (prompt);
	--Exec_ignoreresult;
	--H_stack;
	if (Quit)
	    main_exit(0);
	if (gets(Inline) == NULL) {
	    if (IsInteractive(Input()) && !S_ignoreeof)
		main_exit(0);
	    clearerr(stdin);
	}
	resetbreak();
	if (*Inline == '^')
	    hat_replace(Inline);
	if (*Inline)
	    exec_command(Inline);
    }
}

init_vars()
{
    if (IsInteractive(Input()))
	set_var (LEVEL_SET, V_PROMPT, "echo -n \"% \"");
    else
	set_var (LEVEL_SET, V_PROMPT, "");
    set_var (LEVEL_SET, V_HIST, "20");
    set_var (LEVEL_SET, V_PATH, "ram:,ram:c/,c:,df2:c/,df1:c/,df0:c/");
}

init()
{
    static char pipe1[32], pipe2[32];

    Cin = Input();
    Cout= Cerr = Output();
    Uniq= (long)pipe1;         /* address of some global variable */
    Pipe1 = pipe1;
    Pipe2 = pipe2;
    sprintf (pipe1, "ram:pipe1_%ld", Uniq);
    sprintf (pipe2, "ram:pipe2_%ld", Uniq);
}

main_exit(n)
{
    UnLock(CurrentDir(Orig_dir));
    do_cldres();
    exit (n);
}

docheckbreak()
{
    if (checkbreak()) {
	printf("^C\n");
	return(1);
    }
    return(0);
}

\Rogue\Monster\
else
  echo "will not over write main.c"
fi
if [ `wc -c main.c | awk '{printf $1}'` -ne 2786 ]
then
echo `wc -c main.c | awk '{print "Got " $1 ", Expected " 2786}'`
fi
if `test ! -s run.c`
then
echo "writing run.c"
cat > run.c << '\Rogue\Monster\'

/*
 * RUN.C
 *
 *  (C)CopyRight 1987 Matthew Dillon, All rights reserved.
 *
 *  RUN handles running of external commands.
 *
 */

#include "shell.h"
#include "libraries/dos.h"

extern long *SysRegs;
extern struct FileLock *Clock;

do_run(str)
char *str;
{
    int i;

#ifdef DEBUG
    if (SDebug) {
	printf ("RUN: %s '>%s' '<%s' '>>%ld'\n", av[0], (Cin_name)?Cin_name:"&", (Cout_name)?Cout_name:"&", Cout_append);
	printf ("av[0]=%08lx [1]=%08lx [2]=%08lx\n", av[0],av[1], av[2]);
    }
#endif
    if (fexecv(av[0], av, Cin_name, Cout_name, Cout_append) >= 0) {
	i = wait();
    } else {
	register long lock;
	register char *copy;
	char buf[128];

	mountrequest(0);
	lock = FindIt(av[0], ".sh", buf);
	mountrequest(1);
#ifdef DEBUG
	if (SDebug)
	    puts("do_run: 1");
#endif
	if (lock == NULL) {
	    perror(av[0]);      /*  fixed 26 Mar 88 */
	    return (-1);
	}
	av[0] = (char *)-1;
	av[1] = (char *)lock;
	copy = malloc(strlen(str)+3);
	strcpy(copy+2,str);
	copy[0] = 'x';
	copy[1] = ' ';
#ifdef DEBUG
	if (SDebug)
	    printf ("Do_source: %s\n", copy);
#endif
	i = do_source(copy);
	free(copy);
    }
    if (Clock != (struct FileLock *)((struct Process *)FindTask(NULL))->pr_CurrentDir) {
	Clock = (struct FileLock *)((struct Process *)FindTask(NULL))->pr_CurrentDir;
	puts("Warning: Current Directory has changed");
    }
    return (i);
}



\Rogue\Monster\
else
  echo "will not over write run.c"
fi
if [ `wc -c run.c | awk '{printf $1}'` -ne 1353 ]
then
echo `wc -c run.c | awk '{print "Got " $1 ", Expected " 1353}'`
fi
if `test ! -s set.c`
then
echo "writing set.c"
cat > set.c << '\Rogue\Monster\'

/*
 * SET.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * Handles the variable lists for normal variables, aliases, and labels.
 */

#include "shell.h"
#define MAXLEVELS (3 + MAXSRC)

struct MASTER {
    struct MASTER *next;
    struct MASTER *last;
    char *name;
    char *text;
};

static struct MASTER *Mbase[MAXLEVELS];

char *
set_var(level, name, str)
short level;
register char *name, *str;
{
    register struct MASTER *base = Mbase[level];
    register struct MASTER *last;
    register short len;

    for (len = 0; isalphanum(name[len]); ++len);
    while (base != NULL) {
	if (strlen(base->name) == len && strncmp (name, base->name, len) == 0) {
	    Free (base->text);
	    goto gotit;
	}
	last = base;
	base = base->next;
    }
    if (base == Mbase[level]) {
	base = Mbase[level] = (struct MASTER *)malloc(sizeof(struct MASTER));
	base->last = NULL;
    } else {
	base = (struct MASTER *)malloc (sizeof(struct MASTER));
	base->last = last;
	last->next = base;
    }
    base->name = malloc (len + 1);
    bmov (name, base->name, len);
    base->name[len] = 0;
    base->next = NULL;
gotit:
    base->text = malloc (strlen(str) + 1);
    strcpy (base->text, str);
    return (base->text);
}

char *
get_var (level, name)
short level;
register char *name;
{
    register struct MASTER *base = Mbase[level];
    register unsigned char *scr;
    register short len;
    static char *EnvBuf = NULL;
    char buf[128];
    long fh;

    for (scr = (UBYTE *)name; *scr && *scr != 0x80 && *scr != ' ' && *scr != ';' && *scr != '|'; ++scr);
    len = scr - name;

    while (base != NULL) {
	if (strlen(base->name) == len && strncmp (name, base->name, len) == 0)
	    return (base->text);
	base = base->next;
    }
    mountrequest(0);
    strcpy(buf, "ENV:");
    strncat(buf, name, len);
    buf[4+len] = 0;
    if (fh = Open(buf, 1005)) {
	long len = (Seek(fh, 0L, 1), Seek(fh, 0L, 0));
	if (len < 0)
	    len = 256;
	if (EnvBuf)
	    free(EnvBuf);
	if (EnvBuf = malloc(len+1)) {
	    Seek(fh, 0L, -1);
	    len = Read(fh, EnvBuf, len);
	    if (len < 0)
		len = 0;
	    EnvBuf[len] = 0;
	}
	Close(fh);
	mountrequest(1);
	return(EnvBuf);
    }
    mountrequest(1);
    return (NULL);
}


unset_level(level)
short level;
{
    register struct MASTER *base = Mbase[level];
    register struct MASTER *next;

    while (base) {
	next = base->next;
	Free (base->name);
	Free (base->text);
	Free (base);
	base = next;
    }
    Mbase[level] = NULL;
}


unset_var(level, name)
short level;
char *name;
{
    register struct MASTER *base = Mbase[level];
    register struct MASTER *last = NULL;
    register short len;

    for (len = 0; isalphanum(name[len]); ++len);
    while (base) {
	if (strlen(base->name) == len && strncmp (name, base->name, len) == 0) {
	    if (base != Mbase[level])
		last->next = base->next;
	    else
		Mbase[level] = base->next;
	    if (base->next != NULL)
		base->next->last = last;
	    if (base == Mbase[level])
		Mbase[level] = base->next;
	    Free (base->name);
	    Free (base->text);
	    Free (base);
	    return (1);
	}
	last = base;
	base = base->next;
    }
    return (-1);
}


do_unset_var(garbage, level)
short level;
char *garbage;
{
    register short i;
    register char uu = 0;

    for (i = 1; i < ac; ++i) {
	unset_var (level, av[i]);
	if (*av[i] == '_')
	    uu = '_';
    }
    update_under(uu);
    return (0);
}


do_set_var(command, level)
short level;
char *command;
{
    register struct MASTER *base = Mbase[level];
    register char *str;

    if (ac == 1) {
	while (base) {
	    fhprintf (Cout, "%-10s ", base->name);
	    Oputs (base->text);
	    base = base->next;
	}
	return (0);
    }
    if (ac == 2) {
	str = get_var (level, av[1]);
	if (str) {
	    fhprintf (Cout, "%-10s ", av[1]);
	    Oputs(str);
	} else {
	    set_var (level, av[1], "");
	}
    }
    if (ac > 2)
	set_var (level, av[1], next_word (next_word (command)));
    update_under(*av[1]);
    return (0);
}

static
update_under(c)
{
    register char *str;
    if (c == '_') {
	S_histlen = (str = get_var(LEVEL_SET, V_HIST))   ? atoi(str) : 0;
	S_ignoreeof=(str = get_var(LEVEL_SET, V_IGNOREEOF)) ? 1 : 0;
	SDebug	  = (str = get_var(LEVEL_SET, V_DEBUG))  ? atoi(str) : 0;
	Verbose   = (get_var(LEVEL_SET, V_VERBOSE)) ? 1 : 0;
	if (S_histlen < 2)
	    S_histlen = 2;
    }
}



\Rogue\Monster\
else
  echo "will not over write set.c"
fi
if [ `wc -c set.c | awk '{printf $1}'` -ne 4323 ]
then
echo `wc -c set.c | awk '{print "Got " $1 ", Expected " 4323}'`
fi
if `test ! -s shell.h`
then
echo "writing shell.h"
cat > shell.h << '\Rogue\Monster\'

/*
 * SHELL.H
 *
 * (c)1986-1988 Matthew Dillon     18 August 1988
 *
 *
 * SHELL include file.. contains shell parameters and extern's
 *
 *
 */

#include <local/typedefs.h>

/*#define FIBF_ARCHIVE	  (1<<4)*/

#define MAXAV		128	    /* Max. # arguments 	    */
#define MAXSRC		5	    /* Max. # of source file levels */
#define MAXIF		10	    /* Max. # of if levels	    */
#define MAXALIAS	20	    /* Max. # of alias levels	    */


#define LEVEL_SET    0		    /* which variable list to use   */
#define LEVEL_ALIAS  1
#define LEVEL_LABEL  2

#define V_PROMPT     "_prompt"      /* your prompt (ascii command)   */
#define V_HIST	     "_history"     /* set history depth (value)     */
#define V_HISTNUM    "_histnum"     /* set history numbering var     */
#define V_DEBUG      "_debug"       /* set debug mode                */
#define V_VERBOSE    "_verbose"     /* set verbose for source files  */
#define V_STAT	     "_maxerr"      /* worst return value to date    */
#define V_LASTERR    "_lasterr"     /* return value from last comm.  */
#define V_CWD	     "_cwd"         /* current directory             */
#define V_EXCEPT     "_except"      /* "nnn;command"                 */
#define V_PASSED     "_passed"      /* passed arguments to source fle*/
#define V_PATH	     "_path"        /* path prefix,prefix,prefix..   */
#define V_IGNOREEOF  "_ignoreeof"   /* ignore EOF for interactive tty*/
#define V_COPYSILENT "_copysilent"  /* silent copy                   */
#define V_COPYDATE   "_copydate"    /* transport datestamp           */


	    /* EXECOM.C defines */

#define FL_DOLLAR    0x01  /* One of the following */
#define FL_BANG      0x02
#define FL_PERCENT   0x04
#define FL_QUOTE     0x08
#define FL_IDOLLAR   0x10  /* Any or all of the following may be set */
#define FL_EOC	     0x20
#define FL_EOL	     0x40
#define FL_OVERIDE   0x80
#define FL_WILD      0x100
#define FL_MASK      (FL_DOLLAR|FL_BANG|FL_PERCENT|FL_QUOTE)

#define VERSION   "V2.10 (C)Copyright 1986-88 Matthew Dillon, All Rights Reserved, 18 August 1988"

#ifndef NULL
#define NULL 0L
#endif

#define CHECKBREAK()    docheckbreak()

struct HIST {
   struct HIST *next, *prev;	 /* doubly linked list */
   char *line;			 /* line in history    */
};

struct PERROR {
   short errnum;		 /* Format of global error lookup */
   char *errstr;
};

struct DPTR {			 /* Format of directory fetch pointer */
   struct FileLock *lock;	 /* lock on directory	*/
   struct FileInfoBlock *fib;	 /* mod'd fib for entry */
};

extern struct HIST *H_head, *H_tail;
extern struct PERROR Perror[];
extern struct DPTR *dopen();
extern char *set_var(), *get_var(), *next_word();
extern char *get_history(), *compile_av();
extern char *strcpy(), *strcat(), *gets(), *Ogets();
extern char **expand();
extern long FindIt();


extern char *av[];
extern char *Current;
extern short H_len, H_tail_base, H_stack;
extern short E_stack;
extern short Src_stack, If_stack;
extern short ac;
extern short SDebug, Verbose, SDisable, Quit;
extern long Lastresult;
extern short Exec_abortline, Exec_ignoreresult;
extern short S_histlen, S_ignoreeof;
extern long Uniq;
extern long Cin, Cout, Cerr, Cout_append;
extern char *Cin_name, *Cout_name;
extern char  Cin_type,	Cout_type;  /* these variables are in transition */
extern char *Pipe1, *Pipe2;
extern long DResBase;

extern int  Src_base[MAXSRC];
extern long Src_pos[MAXSRC];
extern char If_base[MAXIF];


\Rogue\Monster\
else
  echo "will not over write shell.h"
fi
if [ `wc -c shell.h | awk '{printf $1}'` -ne 3410 ]
then
echo `wc -c shell.h | awk '{print "Got " $1 ", Expected " 3410}'`
fi
if `test ! -s sort.c`
then
echo "writing sort.c"
cat > sort.c << '\Rogue\Monster\'

/*
 * SORT.C
 *
 * a QuickSort is used for speed, simplicity, and small code size.
 *
 */

extern short QSplit();


QuickSort(av, n)
char *av[];
short n;
{
    short b;

    if (n > 0) {
	b = QSplit(av, n);
	QuickSort(av, b);
	QuickSort(av+b+1, n - b - 1);
    }
}


/*
 * QSplit called as a second routine so I don't waste stack on QuickSort's
 * recursivness.
 */


short
QSplit(av, n)
register char *av[];
short n;
{
    register short i, b;
    register char *element, *scr;

    element = av[0];
    for (b = 0, i = 1; i < n; ++i) {
	if (strcmp(av[i], element) < 0) {
	    ++b;
	    scr = av[i]; av[i] = av[b]; av[b] = scr;
	}
    }
    scr = av[0]; av[0] = av[b]; av[b] = scr;
    return (b);
}



\Rogue\Monster\
else
  echo "will not over write sort.c"
fi
if [ `wc -c sort.c | awk '{printf $1}'` -ne 705 ]
then
echo `wc -c sort.c | awk '{print "Got " $1 ", Expected " 705}'`
fi
if `test ! -s sub.c`
then
echo "writing sub.c"
cat > sub.c << '\Rogue\Monster\'

/*
 * SUB.C
 *
 * (C)Copyright 1987 Matthew Dillon,    All rights reserved
 *
 *      Subroutines used throughout the shell (not very descriptive, am I?)
 */


#include <exec/types.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include "shell.h"

#define HM_STR 0              /* various HISTORY retrieval modes */
#define HM_REL 1
#define HM_ABS 2

extern struct FileLock *Lock(), *DupLock(), *CurrentDir();
extern struct FileLock *Clock;

seterr()
{
    char buf[32];
    int stat;

    sprintf(buf, "%ld", Lastresult);
    set_var(LEVEL_SET, V_LASTERR, buf);
    stat = atoi(get_var(LEVEL_SET, V_STAT));
    if (stat < Lastresult)
        stat = Lastresult;
    sprintf(buf, "%ld", stat);
    set_var(LEVEL_SET, V_STAT, buf);
}


char *
next_word(str)
register char *str;
{
    while (*str  &&  *str != ' '  &&  *str != 9)
        ++str;
    while (*str  && (*str == ' ' || *str == 9))
        ++str;
    return (str);
}


char *
compile_av(av, start, end)
short start, end;
char **av;
{
    char *cstr;
    short i, len;

    len = 0;
    for (i = start; i < end; ++i)
        len += strlen(av[i]) + 1;
    cstr = malloc(len + 1);
    *cstr = '\0';
    for (i = start; i < end; ++i) {
        strcat (cstr, av[i]);
        strcat (cstr, " ");
    }
    return (cstr);
}

/*
 * FREE(ptr)   --frees without actually freeing, so the data is still good
 *               immediately after the free.
 */


Free(ptr)
char *ptr;
{
    static char *old_ptr;

    if (old_ptr)
        free (old_ptr);
    old_ptr = ptr;
}

/*
 * Add new string to history (H_head, H_tail, H_len,
 *  S_histlen
 */

add_history(str)
char *str;
{
    register struct HIST *hist;

    while (H_len > S_histlen)
        del_history();
    hist = (struct HIST *)malloc (sizeof(struct HIST));
    if (H_head == NULL) {
        H_head = H_tail = hist;
        hist->next = NULL;
    } else {
        hist->next = H_head;
        H_head->prev = hist;
        H_head = hist;
    }
    hist->prev = NULL;
    hist->line = malloc (strlen(str) + 1);
    strcpy (hist->line, str);
    ++H_len;
}

del_history()
{
    if (H_tail) {
        --H_len;
        ++H_tail_base;
        free (H_tail->line);
        if (H_tail->prev) {
            H_tail = H_tail->prev;
            free (H_tail->next);
            H_tail->next = NULL;
        } else {
            free (H_tail);
            H_tail = H_head = NULL;
        }
    }
}


char *
last_history_entry()
{
    if (H_head)
        return(H_head->line);
    return("");
}


char *
get_history(ptr)
char *ptr;
{
    register struct HIST *hist;
    register short len;
    short mode = HM_REL;
    long num  = 1;
    char *str;
    char *result = NULL;

    if (ptr[1] >= '0' && ptr[1] <= '9') {
        mode = HM_ABS;
        num  = atoi(&ptr[1]);
        goto skip;
    }
    switch (ptr[1]) {
    case '!':
        break;
    case '-':
        num += atoi(&ptr[2]);
        break;
    default:
        mode = HM_STR;
        str  = ptr + 1;
        break;
    }
skip:
    switch (mode) {
    case HM_STR:
        len = strlen(str);
        for (hist = H_head; hist; hist = hist->next) {
            if (strncmp(hist->line, str, len) == 0 && *hist->line != '!') {
                result = hist->line;
                break;
            }
        }
        break;
    case HM_REL:
        for (hist = H_head; hist && num--; hist = hist->next);
        if (hist)
            result = hist->line;
        break;
    case HM_ABS:
        len = H_tail_base;
        for (hist = H_tail; hist && len != num; hist = hist->prev, ++len);
        if (hist)
            result = hist->line;
        break;
    }
    if (result) {
        Eputs (result);
        return(result);
    }
    Eputs ("History substitution failed");
    return ("");
}


replace_head(str)
char *str;
{
    if (str == NULL)
        str = "";
    if (H_head) {
        free (H_head->line);
        H_head->line = malloc (strlen(str)+1);
        strcpy (H_head->line, str);
    }
}

perror(str)
char *str;
{
    ierror(str, IoErr());
}

ierror(str, err)
register char *str;
short err;
{
    register struct PERROR *per = Perror;

    if (err) {
        for (; per->errstr; ++per) {
            if (per->errnum == err) {
                fhprintf (Cerr, "%s%s%s\n",
                    per->errstr,
                    (str) ? ": " : "",
                    (str) ? str : ""
                );
                return(err);
            }
        }
        fhprintf (Cerr, "Unknown DOS error %ld %s\n", err, (str) ? str : "");
   }
   return (err);
}

/*
 * Disk directory routines
 *
 * dptr = dopen(name, stat)
 *    struct DPTR *dptr;
 *    char *name;
 *    int *stat;
 *
 * dnext(dptr, name, stat)
 *    struct DPTR *dptr;
 *    char **name;
 *    int  *stat;
 *
 * dclose(dptr)                  -may be called with NULL without harm
 *
 * dopen() returns a struct DPTR, or NULL if the given file does not
 * exist.  stat will be set to 1 if the file is a directory.  If the
 * name is "", then the current directory is openned.
 *
 * dnext() returns 1 until there are no more entries.  The **name and
 * *stat are set.  *stat = 1 if the file is a directory.
 *
 * dclose() closes a directory channel.
 *
 */

struct DPTR *
dopen(name, stat)
char *name;
register int *stat;
{
    register struct DPTR *dp;

    *stat = 0;
    dp = (struct DPTR *)malloc(sizeof(struct DPTR));
    if (*name == '\0')
        dp->lock = DupLock (Clock);
    else
        dp->lock = Lock (name, ACCESS_READ);
    if (dp->lock == NULL) {
        free (dp);
        return (NULL);
    }
    dp->fib = (struct FileInfoBlock *)
            AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC);
    if (!Examine (dp->lock, dp->fib)) {
        perror (name);
        dclose (dp);
        return (NULL);
    }
    if (dp->fib->fib_DirEntryType >= 0)
        *stat = 1;
    return (dp);
}

dnext(dp, pname, stat)
register struct DPTR *dp;
char **pname;
int *stat;
{
    if (dp == NULL)
        return (0);
    if (ExNext (dp->lock, dp->fib)) {
        *stat = (dp->fib->fib_DirEntryType < 0) ? 0 : 1;
        *pname = dp->fib->fib_FileName;
        return (1);
    }
    return (0);
}


dclose(dp)
register struct DPTR *dp;
{
    if (dp == NULL)
        return (1);
    if (dp->fib)
        FreeMem (dp->fib, sizeof(*dp->fib));
    if (dp->lock)
        UnLock (dp->lock);
    free (dp);
    return (1);
}


isdir(file)
char *file;
{
    register struct DPTR *dp;
    int stat;

    stat = 0;
    if (dp = dopen (file, &stat))
        dclose(dp);
    return (stat == 1);
}


free_expand(av)
register char **av;
{
    register char **base = av;

    if (av) {
        while (*av) {
            free (*av);
            ++av;
        }
        free (base);
    }
}

/*
 * EXPAND(wild_name, pac)
 *    wild_name      - char * (example: "df0:*.c")
 *    pac            - int  *  will be set to # of arguments.
 *
 * Standalone, except in requires Clock to point to the Current-Directory
 * lock.
 */


char **
expand(base, pac)
char *base;
int *pac;
{
    register char *ptr;
    char **eav = (char **)malloc (sizeof(char *));
    short eleft, eac;
    char *name;
    char *bname, *ename, *tail;
    int stat, scr;
    register struct DPTR *dp;

    *pac = eleft = eac = 0;

    base = strcpy(malloc(strlen(base)+1), base);
    for (ptr = base; *ptr && *ptr != '?' && *ptr != '*'; ++ptr);
    for (; ptr >= base && !(*ptr == '/' || *ptr == ':'); --ptr);
    if (ptr < base) {
        bname = strcpy (malloc(1), "");
    } else {
        scr = ptr[1];
        ptr[1] = '\0';
        bname = strcpy (malloc(strlen(base)+1), base);
        ptr[1] = scr;
    }
    ename = ptr + 1;
    for (ptr = ename; *ptr && *ptr != '/'; ++ptr);
    scr = *ptr;
    *ptr = '\0';
    tail = (scr) ? ptr + 1 : NULL;

    if ((dp = dopen (bname, &stat)) == NULL  ||  stat == 0) {
        free (bname);
        free (base);
        free (eav);
        Eputs ("Could not open directory");
        return (NULL);
    }
    while (dnext (dp, &name, &stat)) {
        if (compare_ok(ename, name)) {
            if (tail) {
                int alt_ac;
                char *search, **alt_av, **scrav;
                struct FileLock *lock;

                if (!stat)           /* expect more dirs, but this not a dir */
                    continue;
                lock = CurrentDir (Clock = dp->lock);
                search = malloc(strlen(name)+strlen(tail)+2);
                strcpy (search, name);
                strcat (search, "/");
                strcat (search, tail);
                scrav = alt_av = expand (search, &alt_ac);
                CurrentDir (Clock = lock);
                if (scrav) {
                    while (*scrav) {
                        if (eleft < 2) {
                            char **scrav = (char **)malloc(sizeof(char *) * (eac + 10));
                            bmov (eav, scrav, (eac + 1) << 2);
                            free (eav);
                            eav = scrav;
                            eleft = 10;
                        }
                        eav[eac] = malloc(strlen(bname)+strlen(*scrav)+1);
                        strcpy(eav[eac], bname);
                        strcat(eav[eac], *scrav);
                        free (*scrav);
                        ++scrav;
                        --eleft, ++eac;
                    }
                    free (alt_av);
                }
            } else {
                if (eleft < 2) {
                    char **scrav = (char **)malloc(sizeof(char *) * (eac + 10));
                    bmov (eav, scrav, (eac + 1) << 2);
                    free (eav);
                    eav = scrav;
                    eleft = 10;
                }
                eav[eac] = malloc (strlen(bname)+strlen(name)+1);
                eav[eac] = strcpy(eav[eac], bname);
                strcat(eav[eac], name);
                --eleft, ++eac;
            }
        }
    }
    dclose (dp);
    *pac = eac;
    eav[eac] = NULL;
    free (bname);
    free (base);
    if (eac)
        return (eav);
    free (eav);
    return (NULL);
}

/*
 * Compare a wild card name with a normal name
 */

#define MAXB   8

compare_ok(wild, name)
char *wild, *name;
{
    register char *w = wild;
    register char *n = name;
    char *back[MAXB][2];
    register char s1, s2;
    register short bi = 0;

    while (*n || *w) {
        switch (*w) {
        case '*':
            if (bi == MAXB) {
                Eputs ("Too many levels of '*'");
                return (0);
            }
            back[bi][0] = w;
            back[bi][1] = n;
            ++bi;
            ++w;
            continue;
goback:
            --bi;
            while (bi >= 0 && *back[bi][1] == '\0')
                --bi;
            if (bi < 0)
                return (0);
            w = back[bi][0] + 1;
            n = ++back[bi][1];
            ++bi;
            continue;
        case '?':
            if (!*n) {
                if (bi)
                    goto goback;
                return (0);
            }
            break;
        default:
            s1 = (*n >= 'A' && *n <= 'Z') ? *n - 'A' + 'a' : *n;
            s2 = (*w >= 'A' && *w <= 'Z') ? *w - 'A' + 'a' : *w;
            if (s1 != s2) {
                if (bi)
                    goto goback;
                return (0);
            }
            break;
        }
        if (*n)  ++n;
        if (*w)  ++w;
    }
    return (1);
}


Oputs(str)
char *str;
{
    Write (Cout, str, strlen(str));
    Write (Cout, "\n", 1);
}

Eputs(str)
char *str;
{
    Write (Cerr, str, strlen(str));
    Write (Cerr, "\n", 1);
}

char *
Ogets(str)
char *str;
{
    register int i = 0;

    while (Read(Cin, str + i, 1) == 1) {
        if (str[i] == '\n') {
            str[i] = 0;
            return (str);
        }
        if (++i == 255) {
            str[i] = 0;
            return (str);
        }
    }
    return (NULL);
}



\Rogue\Monster\
else
  echo "will not over write sub.c"
fi
if [ `wc -c sub.c | awk '{printf $1}'` -ne 11900 ]
then
echo `wc -c sub.c | awk '{print "Got " $1 ", Expected " 11900}'`
fi
if `test ! -s tags`
then
echo "writing tags"
cat > tags << '\Rogue\Monster\'
do_sleep comm1.c /^do_sleep(
do_number comm1.c /^do_number(
do_cat comm1.c /^do_cat(
do_comment comm1.c /^do_comment(
do_dir comm1.c /^do_dir(
disp_entry comm1.c /^disp_entry(
do_quit comm1.c /^do_quit(
do_echo comm1.c /^do_echo(
do_source comm1.c /^do_source(
do_cd comm1.c /^do_cd(
attempt_cd comm1.c /^attempt_cd(
rmlast comm1.c /^rmlast(
do_mkdir comm1.c /^do_mkdir(
do_mv comm1.c /^do_mv(
do_rm comm1.c /^do_rm(
rmdir comm1.c /^rmdir(
do_history comm1.c /^do_history(
do_mem comm1.c /^do_mem(
do_foreach comm1.c /^do_foreach(
do_forever comm1.c /^do_forever(
do_abortline comm2.c /^do_abortline(
do_return comm2.c /^do_return(
do_strhead comm2.c /^do_strhead(
do_strtail comm2.c /^do_strtail(
do_if comm2.c /^do_if(
do_label comm2.c /^do_label(
do_goto comm2.c /^do_goto(
do_inc comm2.c /^do_inc(
do_input comm2.c /^do_input(
do_ver comm2.c /^do_ver(
do_cp comm2.c /^do_cp(
copydir comm2.c /^copydir(
copyfile comm2.c /^copyfile(
do_shellstat comm2.c /^do_shellstat(
do_printenv comm2.c /^do_printenv(
do_setenv comm2.c /^do_setenv(
do_unsetenv comm2.c /^do_unsetenv(
exec_command execom.c /^exec_command(
isalphanum execom.c /^isalphanum(
preformat execom.c /^preformat(
fcomm execom.c /^fcomm(
exarg execom.c /^exarg(
mpush_base execom.c /^mpush_base(
mpush execom.c /^mpush(
mpop_tobase execom.c /^mpop_tobase(
format_insert_string execom.c /^format_insert_string(
find_command execom.c /^find_command(
do_help execom.c /^do_help(
wait fexec1.c /^wait(
fexecv fexec1.c /^fexecv(
LoadIt fexec1.c /^LoadIt(
FindIt fexec1.c /^FindIt(
isfile fexec1.c /^isfile(
hat_replace hat.c /^hat_replace(
main main.c /^main(
init_vars main.c /^init_vars(
init main.c /^init(
main_exit main.c /^main_exit(
docheckbreak main.c /^docheckbreak(
do_run run.c /^do_run(
set_var set.c /^set_var(
get_var set.c /^get_var (
unset_level set.c /^unset_level(
unset_var set.c /^unset_var(
do_unset_var set.c /^do_unset_var(
do_set_var set.c /^do_set_var(
update_under set.c /^update_under(
QuickSort sort.c /^QuickSort(
QSplit sort.c /^QSplit(
seterr sub.c /^seterr(
next_word sub.c /^next_word(
compile_av sub.c /^compile_av(
Free sub.c /^Free(
add_history sub.c /^add_history(
del_history sub.c /^del_history(
last_history_entry sub.c /^last_history_entry(
get_history sub.c /^get_history(
replace_head sub.c /^replace_head(
perror sub.c /^perror(
ierror sub.c /^ierror(
dopen sub.c /^dopen(
dnext sub.c /^dnext(
dclose sub.c /^dclose(
isdir sub.c /^isdir(
free_expand sub.c /^free_expand(
expand sub.c /^expand(
compare_ok sub.c /^compare_ok(
Oputs sub.c /^Oputs(
Eputs sub.c /^Eputs(
Ogets sub.c /^Ogets(
\Rogue\Monster\
else
  echo "will not over write tags"
fi
if [ `wc -c tags | awk '{printf $1}'` -ne 2590 ]
then
echo `wc -c tags | awk '{print "Got " $1 ", Expected " 2590}'`
fi
echo "Finished archive 1 of 2"
# if you want to concatenate archives, remove anything after this line
exit
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.