/*  :ts=8  */
/*  $Header: yyy:work/sh/RCS/line.c,v 1.15 90/08/28 01:16:08 john Beta $  */

/*
 *	known bugs: 
 *	o  env var args limited to 20
 *	o  any number of them at end of long line
 *	o  recognises scripts, but doesn't know what to do with them
 */

#include <ctype.h>
#include <string.h>
#include <arpbase.h>
#include <exec/exec.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <functions.h>

#pragma amicall(ArpBase,0x276,BaseName(a0))
#pragma amicall(ArpBase,0xea,FPrintf(d0,a0,a1))
#pragma amicall(ArpBase,0xfc,GADS(a0,d0,a1,a2,a3))
#pragma amicall(ArpBase,0x11a,Getenv(a0,a1,d0))
#pragma amicall(ArpBase,0x14a,PathName(d0,a0,d1))
#pragma amicall(ArpBase,0xe4,Printf(a0,a1))
#pragma amicall(ArpBase,0x210,Strncmp(a0,a1,d0))
#pragma amicall(ArpBase,0x21c,SyncRun(a0,a1,d0,d1))
struct ArpBase *ArpBase;

char *tmpnam (char *_s);

#define CSI		0x9b
#define KEY_UNKNOWN	-1

#define CTRL(c)		('c'&037)

#define WERASE		CTRL(w)
#define KILL		CTRL(u)
#define ESC		'\033'
#define TAB		CTRL(i)
#define BS		CTRL(h)
#define RET		CTRL(j)
#define NL		CTRL(m)
#define QUIT		CTRL(q)
#define AMIKILL		CTRL(x)

#define BIGNUMBER	256
#define MAXLINE		255
#define ALIASLOOP	20
#define BUILTIN		9
#define IGNORE		""
#define PROMPT		"%N> "
#define TMPDIR		"tmp:"
#define STARTUP		"s:.linerc"
#define MAXHIST		20
#define MAXALIAS	20
#define MAXPP		5

#define DOS_TRUE	-1
#define DOS_FALSE	0
#define raw()		ttymode(DOS_TRUE)
#define cooked()	ttymode(DOS_FALSE)

typedef unsigned char byte;
typedef char bool;

char *progname;
bool is_dflt=TRUE;
char *prompt=PROMPT, *ignorelist=IGNORE, *tmpdir=TMPDIR;
char *startup=STARTUP;

struct FileInfoBlock *fib;
struct MsgPort *rp;
struct StandardPacket *packet;

#define A_EMPTY(x)	(!A[x].name)

int a_max=MAXALIAS;

struct alias {
    char *name, *sub;
} *A;

#define hINC(i)		(((i)+1) % h_max)
#define hDEC(i)		(!(i) ? h_max-1 : (i)-1)

int h_num, h_max=MAXHIST, cmd_number;

struct history {
    int num;
    char line[MAXLINE+1];
} *H;

int pp_max=MAXPP, pp_sp=-1;
long *pp_stk;

int errcode=-1;

int fprintf (long _fh, const char *_format, ...)
{
    return (FPrintf (_fh, _format, (char *) &_format + sizeof (char *)));
}

int printf (const char *_format, ...)
{
    return (Printf (_format, (char *) &_format + sizeof (char *)));
}

int do_csi ()
{
    char ch;

    Read (Input(), &ch, 1);
    switch (ch) {
    	case 'A':
    	case 'B':
    	case 'C':
    	case 'D':
    	case 'T':
    	case 'S':
	    break;
    	case '1':
	    Read (Input(), &ch, 1);
    	    if (ch == '~') break;
	    /* else falls through */
    	case '?':
	case ' ':
    	default:
	    Read (Input(), &ch, 1);
    }
    return (KEY_UNKNOWN);
}

/*
 *	The code for ttymode() was garnered directly from code
 *	by Chuck McManis, CBM (Phil Lindsay, Carolyn Scheppner,
 *	&& Andy Finkel), and a tutorial from Michael van Elst.
 */

void ttymode (mode)
long mode;
{
    long fh;
    struct MsgPort *mp;

    fh = Input();
    mp = ((struct FileHandle *)(fh<<2))->fh_Type;
    packet->sp_Msg.mn_Node.ln_Name = (char *) &packet->sp_Pkt;
    packet->sp_Pkt.dp_Link = &packet->sp_Msg;
    packet->sp_Pkt.dp_Port = rp;
    packet->sp_Pkt.dp_Type = ACTION_SCREEN_MODE;
    packet->sp_Pkt.dp_Arg1 = mode;
    PutMsg (mp, (struct Message *) packet);
    WaitPort (rp);
    GetMsg (rp);
}

void cleanup ()
{
    int i;

    if (pp_stk)
    	for (i = 0; i < pp_max; i++) if (pp_stk[i]) UnLock (pp_stk[i]);
    CloseLibrary (ArpBase);
    if (fib) FreeMem (fib, sizeof (struct FileInfoBlock));
    if (packet) FreeMem (packet, sizeof (struct StandardPacket));
    if (rp) DeletePort (rp);
}

void _abort    () { /* stub */ }
void _wb_parse () { /* stub */ }

char *toint (p, i)
char *p;
int *i;
{
    *i = 0;
    while (isdigit (*p))
    	*i = *i * 10 + (*p++ - '0');
    return (p);
}

void makeargs (s, n, args)
char *s, ***args;
int *n;
{
    int i, j;

    i = *n = 0;
    while (s[i]) {
    	while (s[i] && isspace(s[i])) i++;
    	if (s[i]) (*n)++;
    	while (s[i] && !isspace(s[i])) {
	    if (s[i] == '\"') {
	    	i++;
	    	while (s[i] && s[i] != '\"') i++;
	    }
	    i++;
	}
    }
    if ( !(*args = (char **) malloc (*n * sizeof (char *))) ) {
    	printf ("%s: out of memory\n", progname);
    	exit (RETURN_FAIL);
    }
    i = j = 0;
    while (s[i]) {
    	while (s[i] && isspace(s[i])) i++;
    	if (s[i]) {
    	    (*args)[j] = &s[i];
    	    j++;
    	}
    	while (s[i] && !isspace(s[i])) {
	    if (s[i] == '\"') {
	    	i++;
	    	while (s[i] && s[i] != '\"') i++;
	    }
	    i++;
	}
	if (s[i]) s[i++] = '\0';
    }
}

/*
 *	return -1 if error (-2 if it be fatal), 0 if not builtin,
 *	1 otherwise
 */

int builtin (name, args, fh)
char *name, *args;
long fh;
{
    int i, j, k, orig_len;
    char **argv, *p, t[MAXLINE];

    static char rcsid[] = 
	"$Revision: 1.15 $\n"
	"$Date: 90/08/28 01:16:08 $\n";
    /* if adding commands, don't forget to update BUILTIN */
    static char *C[] = {
    	"cls",
    	"history",
	"alias",
	"unalias",
	"dirs",
	"push",
	"pop",
	"ver",
	"exit"
    };

    for (i = 0; i < BUILTIN && strcmp (C[i], name); i++) ;
    switch (i) {
    	case 0:	/* cls */
    	    if (args && *args) goto bi_err;
	    fprintf (fh, "\f");
	    break;
    	case 1:	/* history */
    	    if (args && *args) goto bi_err;
    	    for (i = h_num, j = 0; j < h_max; i = hDEC(i), j++) 
    	    	if (H[i].line[0])
    	    	    fprintf (fh, "%6ld  %s\n", H[i].num, H[i].line);
    	    break;
	case 2:	/* alias */
	    if (!args) {
		for (i = 0; i < a_max; i++)
		    if (!A_EMPTY(i)) 
			fprintf (fh, "%s\t%s\n", A[i].name, A[i].sub);
	    } else {
		orig_len = strlen (args);
		makeargs (args, &i, &argv);
		if (i == 1) {
		    for (j = 0; j < a_max; j++)
			if (!A_EMPTY(j) && !strcmp (A[j].name, argv[0])) 
			    break;
		    if (j != a_max) fprintf (fh, "%s\n", A[j].sub);
		} else {
		    for (j = 0; j < a_max && !A_EMPTY(j) &&
			strcmp (A[j].name, argv[0]); j++) ;
		    if (j == a_max) {
			fprintf (fh, "Too many aliases\n");
			return (-1);
		    } else if (A_EMPTY(j)) {
			if ( !(A[j].name = (char *) malloc 
			    (strlen (argv[0]) + 1)) ) {
				printf ("%s: out of memory\n", progname);
				return (-2);
			}
			strcpy (A[j].name, argv[0]);
		    } else free (A[j].sub);
		    p = argv[1];
		    for (k = 0; k < orig_len; k++)
			if (!args[k]) args[k] = ' ';
		    if ( !(A[j].sub = (char *) malloc (strlen (p) + 1)) ) {
			printf ("%s: out of memory\n", progname);
			return (-2);
		    }
		    strcpy (A[j].sub, p);
		}
		free (argv);
	    }
	    break;
	case 3:	/* unalias */
	    if (!args) break;
	    makeargs (args, &i, &argv);
	    for ( ; i; --i) {
		for (j = 0; j < a_max; j++)
		    if (!A_EMPTY(j) && !strcmp (A[j].name, argv[i-1])) {
			free (A[j].name);
			free (A[j].sub);
			A[j].name = A[j].sub = NULL;
			break;
		    }
	    }
	    free (argv);
	    break;
	case 4:	/* dirs */
	    if (args && *args) goto bi_err;
	    for (i = pp_sp; i >= 0; --i) {
	    	PathName (pp_stk[i], t, BIGNUMBER);
		fprintf (fh, "%s\n", t);
	    }
	    break;
	case 5:	/* push */
	    if (args && *args) goto bi_err;
	    if (pp_sp < pp_max-1) {
		if ( !(pp_stk[++pp_sp] = Lock ("", ACCESS_READ)) ||
		    !PathName (pp_stk[pp_sp], t, BIGNUMBER) ) {
			--pp_sp;
			break;
		}
		fprintf (fh, "(%s)\n", t);
	    } else {
	    	fprintf (fh, "Directory stack full\n");
		return (-1);
	    }
	    break;
	case 6:	/* pop */
	    if (args && *args) goto bi_err;
	    if (pp_sp >= 0) {
	    	CurrentDir ((struct FileLock *) pp_stk[pp_sp]);
		PathName (pp_stk[pp_sp], t, BIGNUMBER);
		pp_stk[pp_sp--] = 0L;
		fprintf (fh, "(%s)\n", t);
	    } else {
	    	fprintf (fh, "Directory stack empty\n");
		return (-1);
	    }
	    break;
	case 7:	/* ver */
	    if (args && *args) goto bi_err;
	    fprintf (fh, "%s... by John D Aycock\n%s", progname, rcsid);
	    break;
	case 8:	/* exit */
	    if (args && *args) goto bi_err;
	    errcode = RETURN_OK;
	    return (-2);
    	case BUILTIN:
    	    return (0);
    }
    return (1);

bi_err:
    printf ("Syntax error\n");
    return (-1);
}

/*
 *	returns NULL if no alias sub done, else char * to
 *	malloc'ed substitution... amount malloc'ed is MAXLINE+1
 */

char *alias (arg)
char *arg;
{
    int i;
    char *ret, *p, *q;

    for (i = 0; i < a_max; i++) {
	if (!A_EMPTY(i) && !strcmp (A[i].name, arg)) {
	    if ( !(ret = (char *) malloc (MAXLINE+1)) ) {
		printf ("%s: out of memory\n", progname);
		exit (RETURN_FAIL);
	    }
	    for (p = A[i].sub, q = ret; *p; )
		if (*p == '\"') p++;
		else if (*p == '!' && *(p+1) == '\'') {
		    p += 2;
		    *q++ = '\"';
		} else *q++ = *p++;
	    *q = '\0';
	    return (ret);
	}
    }
    return (NULL);
}

/*
 *	returns 0 if no history substitution done,
 *	-1 if error, or char * to new (malloc'ed) arg
 */

char *history (arg)
char *arg;
{
    int num, len, s_len, r_len, repl;
    char *new, *s=NULL, *r;
    register int i, j;

    if (!h_max) return (NULL);
    if (*arg == '!') {
    	if (*++arg == '!') arg++, num = -1;
	else if (isdigit (*arg)) arg = toint (arg, &num);
	else if (*arg == '-') {
	    arg = toint (++arg, &num);
	    num = -num;
	} else if (*arg) {
	    len = strlen (arg);
	    for (i = hINC(h_num); i != h_num; i = hINC(i))
	        if (!strncmp (H[i].line, arg, len)) break;
	    if (strncmp (H[i].line, arg, len)) goto h_fail;
	    num = H[i].num;
	    arg += len;
	} else goto h_err;
    } else if (*arg == '^') {
	for (r = s = ++arg; *r && *r != '^'; r++) ;
	if (!*r || !(s_len = r - s) ) goto h_err;
	for (arg = ++r; *arg && *arg != '^'; arg++) ;
	r_len = arg - r;
	if (*arg) arg++;	/* skip optional final '^' */
	num = -1;
    } else return (NULL);
    if (!num) {
h_err:
	printf ("Syntax error\n");
	return ((char *) -1);
    }
    if (num < 0) {
	if ((num = -num) > h_max) goto h_fail;
	for (i = h_num; num; i = hINC(i), --num) ;
    } else {
    	for (i = 0; i < h_max; i++) if (H[i].num == num) break;
	if (i == h_max) goto h_fail;
    }
    if (!H[i].line[0]) {
h_fail:
	printf ("Invalid history substitution\n");
	return ((char *) -1);
    }
    if ( !(new = (char *) malloc (MAXLINE+1)) ) {
	printf ("%s: out of memory\n", progname);
	exit (RETURN_FAIL);
    }
    strcpy (new, H[i].line);
    if (s) {
	for (i = repl = 0; new[i]; i++) { 
	    if (!strncmp (&new[i], s, s_len)) {
		len = strlen (new);
		for (j = s_len + i; j <= len; j++) new[j-s_len] = new[j];
		len -= s_len;
		for (j = len + r_len; j >= i; --j)
		    if (j < MAXLINE+1) new[j] = new[j-r_len];
		new[MAXLINE] = '\0';
		strncpy (&new[i], r, r_len);
		i += r_len;
	    	repl++;
	    }
	}
	if (!repl) goto h_fail;
    }
    strcat (new, arg);
    return (new);
}

#define STDIN		0
#define STDOUT		0
#define PIPE		1
#define REDIRECT	2

#define issep(x)	((*(x)=='|'||*(x)==';')&&*((x)+1)=='\0')

struct command {
    byte I, O;
    char *name, *args, *in_fname, *out_fname;
};

void doit (s, tmpdir)
char *s, *tmpdir;
{
    int argc, n_cmds, cur_cmd, cmd_st, len, arg_st;
    int orig_len, h_len, rv, aliascnt=0, a_len, copy_args;
    long in_fh, out_fh;
    bool h_sub=FALSE, re_eval, aliasing=FALSE, fatal=FALSE;
    char **argv=NULL, *p, ch, *hist, *aka, *q;
    struct command *cmd=NULL;
    register int i, j, cmd_end;

X:
    re_eval = FALSE;
    orig_len = strlen (s);
    makeargs (s, &argc, &argv);
    for (i = 0, n_cmds = 1; i < argc; i++)
    	if (issep (argv[i])) n_cmds++;
    if ( !(cmd = (struct command *) calloc 
    	(n_cmds, sizeof (struct command))) ) {
	    printf ("%s: out of memory\n", progname);
	    exit (RETURN_FAIL);
    }
    for (cur_cmd = cmd_end = 0; cur_cmd < n_cmds; cur_cmd++, cmd_end++) {
    	for (cmd_st = cmd_end, len = 0; cmd_end < argc && 
	    !issep (argv[cmd_end]); ++cmd_end) {
		if (cmd_st == cmd_end)
		    cmd[cur_cmd].name = argv[cmd_st];
		else {
		    if (*argv[cmd_end] == '<') {
			i = cmd_end;
		    	cmd[cur_cmd].in_fname = (*(argv[cmd_end]+1) ? 
			    argv[cmd_end]+1 : argv[++cmd_end]);
			if (cmd_end >= argc || issep (cmd[cur_cmd].in_fname)
			    || cmd[cur_cmd].I) {
			    	printf ("Syntax error\n");
				goto error;
			}
			cmd[cur_cmd].I = REDIRECT;
			argv[i] = argv[cmd_end] = NULL;
		    } else if (*argv[cmd_end] == '>') {
			i = cmd_end;
		    	cmd[cur_cmd].out_fname = (*(argv[cmd_end]+1) ? 
			    argv[cmd_end]+1 : argv[++cmd_end]);
			if (cmd_end >= argc || issep (cmd[cur_cmd].out_fname)
			    || cmd[cur_cmd].O) {
			    	printf ("Syntax error\n");
				goto error;
			}
			cmd[cur_cmd].O = REDIRECT;
			argv[i] = argv[cmd_end] = NULL;
		    } else {
		    	len += strlen (argv[cmd_end]) + 1;
			if (cmd_end - cmd_st == 1) arg_st = cmd_end;
		    }
		}
	}
	if (len) {
	    if ( !(cmd[cur_cmd].args = (char *) malloc (len)) ) {
	        printf ("%s: out of memory\n", progname);
	        exit (RETURN_FAIL);
   	    }
	    for (i = arg_st, *(cmd[cur_cmd].args) = '\0'; i < cmd_end; i++) {
		if (!argv[i]) continue;
	        strcat (cmd[cur_cmd].args, argv[i]);
		for (j = i+1; j <= cmd_end && !argv[j]; j++) ;
	        if (j < cmd_end) strcat (cmd[cur_cmd].args, " ");
	    }
	}
	if (cmd_end < argc && !strcmp (argv[cmd_end], "|")) {
	    if (cmd[cur_cmd].O || cur_cmd+1 >= n_cmds) {
		    printf ("Syntax error\n");
		    goto error;
	    }
	    cmd[cur_cmd].O = cmd[cur_cmd+1].I = PIPE;
	}
    	if (!cmd[cur_cmd].name) {
	    printf ("Syntax error\n");
	    goto error;
	}
    }
    for (i = 0; !aliasing && i < n_cmds; i++) {
    	if ( !(hist = history (cmd[i].name)) ) continue;
	else if ((int) hist == -1) goto error;
	h_len = strlen (cmd[i].name);
	for (j = 0; j < orig_len; j++) if (!s[j]) s[j] = ' ';
	for (j = (cmd[i].name - s) + h_len; j <= orig_len; j++)
	    s[j-h_len] = s[j];
	orig_len -= h_len;
	h_len = strlen (hist);
	for (j = orig_len + h_len; j >= cmd[i].name - s; --j)
	    if (j < MAXLINE+1) s[j] = s[j-h_len];
	s[MAXLINE] = '\0';
	strncpy (&s[j]+1, hist, h_len);
	free (hist);
	h_sub = re_eval = TRUE;
	goto error;	/* loops back up && re-evaluates */
    }
    if (!aliasing && h_max) {
	H[h_num].num = cmd_number;
	for (i = 0; i < orig_len; i++) 
	    H[h_num].line[i] = (!s[i] ? ' ' : s[i]);
	H[h_num].line[i] = '\0';
	if (h_sub) printf ("%s\n", H[h_num].line);
	h_num = hDEC(h_num);
    }
    if (a_max) for (i = 0; i < n_cmds; i++) {
	if ( !(aka = alias (cmd[i].name)) ) continue;
	for (j = copy_args = 0; aka[j]; j++)
	    if (aka[j] == '!' && aka[j+1] == '*') copy_args++;
	for (arg_st = 0; argv[arg_st] != cmd[i].name; arg_st++) ;
	for ( ; arg_st < argc && !issep (argv[arg_st]); arg_st++) ;
	if (arg_st == argc)
	    len = orig_len - (cmd[i].name - s + strlen (cmd[i].name) + 1);
	else
	    len = argv[arg_st] - cmd[i].name - strlen (cmd[i].name) - 2;
	if (len < 0) len = 0;
	if (!len) p = &ch;
	else if ( !(p = (char *) malloc (len + 1)) ) {
	    printf ("%s: out of memory\n", progname);
	    exit (RETURN_FAIL);
	}
	q = cmd[i].name + strlen (cmd[i].name) + 1;
	for (j = 0; j < len; j++) p[j] = (!q[j] ? ' ' : q[j]);
	p[j] = '\0';
	a_len = strlen (cmd[i].name);
	if (copy_args) a_len += len + (len ? 1 : 0);
	for (j = 0; j < orig_len; j++) if (!s[j]) s[j] = ' ';
	for (j = (cmd[i].name - s) + a_len; j <= orig_len; j++)
	    s[j-a_len] = s[j];
	orig_len -= a_len;
	a_len = strlen (aka) - 2*copy_args + copy_args*len;
	for (j = orig_len + a_len; j >= cmd[i].name - s; --j)
	    if (j < MAXLINE+1) s[j] = s[j-a_len];
	s[MAXLINE] = '\0';
	for (j = 0, q = cmd[i].name; aka[j]; j++)
	    if (aka[j] == '!' && aka[j+1] == '*') {
	    	strncpy (q, p, len);
		q += len;
		j++;
	    } else *q++ = aka[j];
	if (len) free (p);
	free (aka);
	aliasing = TRUE;
	if (++aliascnt > ALIASLOOP) {
	    printf ("Alias loop\n");
	    goto error;
	}
	re_eval = TRUE;
	goto error;
    }
    cmd_number++;
    for (i = 0; i < n_cmds; i++) {
	switch (cmd[i].I) {
	    case STDIN:
	    	in_fh = Input ();
		break;
	    case PIPE:
	    	in_fh = out_fh;
		if (Seek (in_fh, 0L, OFFSET_BEGINNING) == -1) {
		    printf ("Error in pipe\n");
		    Close (in_fh);
		    DeleteFile (cmd[i-1].out_fname);
		    free (cmd[i-1].out_fname);
		    goto error;
		}
		break;
	    case REDIRECT:
	    	if ( !(in_fh = Open (cmd[i].in_fname, MODE_OLDFILE)) ) {
		    printf ("Error on input redirection\n");
		    goto error;
		}
		break;
	}
	switch (cmd[i].O) {
	    case STDOUT:
	    	out_fh = Output ();
		break;
	    case PIPE:
	    	p = tmpnam (NULL);
		if ( !(cmd[i].out_fname = (char *) malloc (strlen (tmpdir) +
		    strlen (p) + 1)) ) {
			printf ("%s: out of memory\n", progname);
			if (cmd[i].I) Close (in_fh);
			if (cmd[i].I == PIPE)
			    DeleteFile (cmd[i-1].out_fname);
			exit (RETURN_FAIL);
		}
		strcpy (cmd[i].out_fname, tmpdir);
		ch = tmpdir[strlen(tmpdir)-1];
		if (*tmpdir && ch != ':' && ch != '/')
		    strcat (cmd[i].out_fname, "/");
		strcat (cmd[i].out_fname, p);
		if ( !(out_fh = Open (cmd[i].out_fname, MODE_NEWFILE)) ) {
		    printf ("Error in pipe\n");
		    if (cmd[i].I) Close (in_fh);
		    if (cmd[i].I == PIPE) {
		    	DeleteFile (cmd[i-1].out_fname);
		    	free (cmd[i-1].out_fname);
		    }
		    goto error;
		}
		break;
	    case REDIRECT:
	    	if ( !(out_fh = Open (cmd[i].out_fname, MODE_NEWFILE)) ) {
		    printf ("Error on output redirection\n");
		    if (cmd[i].I) Close (in_fh);
		    if (cmd[i].I == PIPE) {
		    	DeleteFile (cmd[i-1].out_fname);
			free (cmd[i-1].out_fname);
		    }
		    goto error;
		}
		break;
	}
	rv = 0;
	if ( (rv = builtin (cmd[i].name, cmd[i].args, out_fh)) == -2)
	    fatal = TRUE;
	if (rv < 0) goto do_err;
	if (!rv && (rv=SyncRun (cmd[i].name, cmd[i].args, in_fh, out_fh))) {
	    switch (rv) {
	    	case PR_NOFILE:
		    printf ("Unknown command %s\n", cmd[i].name);
		    break;
		case PR_NOEXEC:
		    printf ("Execute bit not set for %s\n", cmd[i].name);
		    break;
		case PR_SCRIPT:
		    printf ("Script bit set for %s\n", cmd[i].name);
		    break;
		case PR_WANTSMESSAGE:
		    printf ("Program returned error #%ld\n", IoErr());
		    break;
		default:
		    if (rv < 0) printf ("Error %ld from SyncRun()\n", rv);
	    }
do_err:
	    if (cmd[i].I) Close (in_fh);
	    if (cmd[i].I == PIPE) {
	    	DeleteFile (cmd[i-1].out_fname);
		free (cmd[i-1].out_fname);
	    }
	    if (cmd[i].O) Close (out_fh);
	    if (cmd[i].O == PIPE) {
	    	DeleteFile (cmd[i].out_fname);
		free (cmd[i].out_fname);
	    }
	    if (fatal) exit (errcode);
	    break;
	}
	if (cmd[i].I) Close (in_fh);
	if (cmd[i].I == PIPE) {
	    DeleteFile (cmd[i-1].out_fname);
	    free (cmd[i-1].out_fname);
	}
	if (cmd[i].O == REDIRECT) Close (out_fh);
    }

error:
    free (argv);
    if (cmd) {
    	for (i = 0; i < n_cmds; i++) if (cmd[i].args) free (cmd[i].args);
	free (cmd);
	cmd = NULL;
    }
    if (re_eval) goto X;
}

bool fignore (s, list)
char *s, *list;
{
    int s_len, e_len;
    char *l_ptr;

    s_len = strlen (s);
    while (*list) {
    	if (l_ptr = strchr (list, '|')) e_len = l_ptr - list;
	else {
	    e_len = strlen (list);
	    l_ptr = " ";  /* makes it look like end of list */
	}
	if ( !Strncmp (list, &s[s_len-e_len], e_len) )
	    return (TRUE);
	list = l_ptr + 1;
    }
    return (FALSE);
}

char *fcomp (s, n, ignorelist)
char *s, *ignorelist;
int *n;	/* RETURN */
{
    int bn_len;
    long lock;
    bool first=TRUE;
    char *p, *q, basename[FCHARS], pathname[FCHARS];

    static char common[FCHARS];

    p = BaseName (s);
    bn_len = strlen (p);
    *n = 0;
    strcpy (basename, p);
    strcpy (pathname, s);
    if (p == s) *pathname = '\0';
    else *(pathname+strlen(s)-bn_len) = '\0';
    if ( !(lock = Lock (pathname, ACCESS_READ)) || 
    	!Examine (lock, (BPTR) fib) ) {
	    if (lock) UnLock (lock);
    	    return ("");
    }
    strcpy (common, basename);
    while (ExNext (lock, (BPTR) fib)) {
    	if (!bn_len || !Strncmp (basename, fib->fib_FileName, bn_len)) {
	    if (fignore (fib->fib_FileName, ignorelist)) continue;
	    if (first) {
		strcpy (common, fib->fib_FileName);
	    	first = FALSE;
	    } else {
	    	for (p = common+bn_len, q = fib->fib_FileName+bn_len;
		    *p && *q && tolower(*p) == tolower(*q); p++, q++) ;
		*p = '\0';
	    }
	}
    }
    UnLock (lock);
    *n = bn_len;
    return (common);
}

char *int2s (p, i)
char *p;
int i;
{
    int off=0;
    char buffer[20];	/* arbitrary */

    do {
    	buffer[off++] = i % 10 + '0';
	i /= 10;
    } while (i);
    while (off > 0) *p++ = buffer[--off];
    return (p);
}

char *makeprompt (format)
char *format;
{
    int len;
    long lock;
    struct Process *proc;
    register char *pf, *pb;
    static char prompt[MAXLINE];

    for (pf = format, pb = prompt; *pf; pf++) {
    	if (*pf != '%') *pb++ = *pf;
	else if (*(pf+1)) {
	    switch (*++pf) {
	    	case 'b':
		    if ( !(lock = Lock ("", ACCESS_READ)) || 
		    	!Examine (lock, (BPTR) fib) ) {
			    if (lock) UnLock (lock);
			    break;
		    }
		    strcpy (pb, fib->fib_FileName);
		    pb += strlen (fib->fib_FileName);
		    UnLock (lock);
		    break;
		case 'c':
		    pb = int2s (pb, cmd_number);
		    break;
		case 'e':
		    *pb++ = ESC;
		    break;
		case 'h':
		    *pb++ = BS;
		    break;
		case 'i':
		    *pb++ = TAB;
		    break;
		case 'n':
		    *pb++ = NL;
		    break;
		case 'p':
		    if ( !(lock = Lock ("", ACCESS_READ)) ||
		    	!(len = PathName (lock, pb, BIGNUMBER)) ) {
		    	    if (lock) UnLock (lock);
			    break;
		    }
		    pb += len;
		    UnLock (lock);
		    break;
		case 'N':
		    proc = (struct Process *) FindTask (NULL);
		    pb = int2s (pb, proc->pr_TaskNum);
		    break;
		default:
		    *pb++ = *pf;
		    break;
	    }
	}
    }
    *pb = '\0';
    return (prompt);
}

void edit (prompt, ignorelist, tmpdir)
char *prompt, *ignorelist, *tmpdir;
{
    int b_i, n;
    char buf[MAXLINE+1], *rest;
    unsigned char ch;
    register int i;

    raw ();
    b_i = 0;
    printf (makeprompt (prompt));

    while (1) {
	if (Read (Input(), (char *) &ch, 1) != 1) ch = QUIT;
	switch (ch) {
	    case CSI:
		(void) do_csi ();
		break;
	    case WERASE:
		for (i = b_i; i && isspace (buf[i-1]); --i) ;
		for ( ; i && !isspace (buf[i-1]); --i) ;
		for ( ; b_i > i; --b_i) printf ("\b \b");
		break;
	    case TAB:
		buf[b_i] = '\0';
		for (i = b_i; i && !isspace (buf[i-1]); --i) ;
		rest = fcomp (&buf[i], &n, ignorelist);
		if (n) printf ("\033[%ldD", n);
		b_i -= n;
		for ( ; b_i < MAXLINE && *rest; rest++) {
		    buf[b_i++] = *rest;
		    Write (Output(), rest, 1);
		}
		break;
	    case AMIKILL:
	    case KILL:
		for (i = 0; i < b_i; i++) printf ("\b \b");
		b_i = 0;
		break;
	    case BS:
		if (b_i) {
		    printf ("\b \b");
		    --b_i;
		}
		break;
	    case RET:
	    case NL:
		for (i = 0; i < b_i && isspace(buf[i]); i++) ;
		printf ("\n");
		if (b_i && i != b_i) {
		    cooked ();
		    buf[b_i] = '\0';
		    doit (buf, tmpdir);
		    b_i = 0;
		    raw ();
		}
		printf (makeprompt (prompt));
		break;
	    case QUIT:
		for (i = 0; i < b_i; i++) printf ("\b \b");
		printf ("\n");
		cooked ();
		return;
	    default:
		if (!iscntrl (ch) && b_i < MAXLINE) {
		    buf[b_i++] = ch;
		    Write (Output(), (char *) &ch, 1);
		}
		break;
	}
    }
}

void parseargs (start_arg, argc, argv)
int start_arg, argc;
char **argv;
{
    int i;
    char *cp;

    for (i = start_arg; i < argc; i++) {
    	if (*(cp = argv[i]) == '-') {
	    switch (*++cp) {
		case 'a':
		    if (*(cp+1)) cp++;
		    else if (i < argc-1) cp = argv[++i];
		    else cp = "0";
		    toint (cp, &a_max);
		    break;
		case 'c':
		    if (*(cp+1)) cp++;
		    else if (i < argc-1) cp = argv[++i];
		    else cp = "0";
		    toint (cp, &h_max);
		    break;
		case 'd':
		    if (*(cp+1)) cp++;
		    else if (i < argc-1) cp = argv[++i];
		    else cp = "0";
		    toint (cp, &pp_max);
		    break;
		case 'f':
		    if (*(cp+1)) ignorelist = cp+1;
		    else if (i < argc-1) ignorelist = argv[++i];
		    else ignorelist = "";
		    break;
		case 'p':
		    if (*(cp+1)) prompt = cp+1;
		    else if (i < argc-1) prompt = argv[++i];
		    else prompt = "";
		    break;
		case 'h':
		    printf ("Usage: %s [-a<maxaliases>] [-c<histsize>] "
		    	"[-d<dirstack>] [-f<ignorelist>] [-p<prompt>] "
			"[-s<startup>] [-t<tmpdir>]\n", progname);
		    printf ("       %s -h[elp]\n", progname);
		    exit (RETURN_OK);
		case 's':
		    if (*(cp+1)) startup = cp+1;
		    else if (i < argc-1) startup = argv[++i];
		    else startup = NULL;
		    is_dflt = FALSE;
		    break;
		case 't':
		    if (*(cp+1)) tmpdir = cp+1;
		    else if (i < argc-1) tmpdir = argv[++i];
		    else tmpdir = "";
		    break;
	    	default:
		    printf ("%s: bad option\n", progname);
		    exit (RETURN_ERROR);
	    }
	} else {
	    printf ("%s: bad arguments\n", progname);
	    exit (RETURN_ERROR);
	}
    }
}

void main (argc, argv)
int argc;
char *argv[];
{
    int i, h_tmp, j, want, actual, lines;
    long fh;
    char ch, t[MAXLINE+1], cmd_buf[MAXLINE+1];

    int E_argc;
    long E_argv[20];
    char E[MAXLINE+1];

    Enable_Abort = FALSE;
    if ( !(ArpBase = (struct ArpBase *) OpenLibrary ("arp.library", 0L)) ) {
	Write (Output(), "Can\'t open arp.library\n", 23);
	exit (RETURN_FAIL);
    }
    atexit (cleanup);
    if ( !(rp = (struct MsgPort *) CreatePort (NULL, 0)) ) {
	printf ("Can\'t create port\n");
	exit (RETURN_FAIL);
    }
    if ( !(packet = (struct StandardPacket *) AllocMem 
    	(sizeof (struct StandardPacket), MEMF_PUBLIC|MEMF_CLEAR)) ||
	!(fib = (struct FileInfoBlock *) AllocMem
	(sizeof (struct FileInfoBlock), 0L)) ) {
    	    printf ("Can\'t allocate memory\n");
	    exit (RETURN_FAIL);
    }
    progname = BaseName (argv[0]);
    if (Getenv ("LINEOPTS", E, MAXLINE+1)) {
	E_argc = GADS (E, strlen(E), NULL, E_argv, ",,,,,,,,,,,,,,,,,,,");
	parseargs (0, E_argc, (char **) E_argv);
    }
    parseargs (1, argc, argv);
    if ( h_max && !(H = (struct history *) calloc (h_max, 
	sizeof (struct history))) ) {
	    printf ("%s: out of memory\n", progname);
	    exit (RETURN_FAIL);
    }
    if ( a_max && !(A = (struct alias *) calloc (a_max,
	sizeof (struct alias))) ) {
	    printf ("%s: out of memory\n", progname);
	    exit (RETURN_FAIL);
    }
    if ( pp_max && !(pp_stk = (long *) calloc (pp_max, sizeof (long))) ) {
    	printf ("%s: out of memory\n", progname);
	exit (RETURN_FAIL);
    }
    if (startup) {
    	if ( !(fh = Open (startup, MODE_OLDFILE)) ) {
	    if (!is_dflt) printf ("Can\'t open %s\n", startup);
	} else {
	    /* temporarily disable history */
	    h_tmp = h_max;  h_max = 0;
	    j = 0;  want = MAXLINE;
	    while ((actual = Read (fh, &t[j], want)) > 0) {
		actual += j;
		for (i = lines = 0; i < actual; ) 
		    if (t[i++]=='\n') lines++;
		if (!lines) {
		    printf ("Error reading %s\n", startup);
		    break;
		}
		for ( ; lines-- ; ) {
		    for (i = 0; t[i] != '\n'; i++) ;
		    t[i] = '\0';
		    for (j = 0; t[j] && isspace(t[j]); j++) ;
		    if (t[j] && t[j] != '#') {
			strcpy (cmd_buf, &t[j]);
		    	doit (cmd_buf, tmpdir);
		    }
		    for (j = 0, i++; i < actual; ) t[j++] = t[i++];
		    actual = j;
		}
		want = MAXLINE - j;
	    }
	    h_max = h_tmp;
	    Close (fh);
	}
    }
    cmd_number = 1;
    edit (prompt, ignorelist, tmpdir);
    exit (RETURN_OK);
}

