/*
 * EXECOM.C
 *
 * Matthew Dillon, 10 August 1986
 *    Finally re-written.
 *
 * Version 2.07M by Steve Drew 10-Sep-87
 * Version 4.01A by Carlo Borreo & Cesare Dieni 17-Feb-90
 * Version 5.00L by Urban Mueller 17-Feb-91
 *
 */

#include "shell.h"

/* execom.c */
static void preformat(char *s, char *d);
static void backtrans(char *str);
static int fcomm(char *str);
static char *exarg(char **ptr);
static void mpush_base(void);
static char *mpush(int bytes);
static void mpop_tobase(void);
static char *format_insert_string(char *str, char *from);
static int cmd_stat(char *str);
static int find_command(char *str);
static char *push_cpy(char *s);
static void exec_every(void);
static void show_usage(char *str);
static void get_opt(char **av, int *ac, int ccno);
static int checkav( int n );


int has_wild = 0;                 /* set if any arg has wild card */
char *LastCommand;

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

extern int do_basename(), do_tackon();
extern int do_fltupper(), do_fltlower(), do_linecnt();
extern int do_strleft(), do_strright(), do_strmid(), do_strlen();
extern int do_fornum(), do_forline(), do_exec();
extern int do_diskchange(), do_stack(), do_fault(), do_path(), do_pri();
extern int do_rpn(), do_resident(), do_truerun(), do_aset(), do_howmany();
extern int do_open(), do_close(), do_fileslist(), do_htype(), do_aget();
extern int do_run(), do_number(), do_assign(), do_join();
extern int do_quit(), do_set_var(), do_unset_var();
extern int do_echo(), do_source(), do_mv(), do_addbuffers();
extern int do_cd(), do_pwd(), do_rm(), do_mkdir(), do_history();
extern int do_mem(), do_cat(), do_dir(), do_info(), do_inc();
extern int do_foreach(), do_return(), do_if(), do_label(), do_goto();
extern int do_input(), do_ver(), do_sleep(), do_help();
extern int do_strhead(), do_strtail(), do_relabel();
extern int do_copy(), do_date(), do_protect(), do_ps();
extern int do_forever(), do_abortline(), do_strings(), do_touch();
extern int do_window(), do_search(), do_filenote(), do_rxrec(), do_rxsend();
extern int do_ascii(), do_whereis(), do_sort(), do_qcd(), do_usage();
extern int do_uniq(), do_man(), do_head(), do_tee(), do_menu(), do_readfile();
extern int do_rxreturn(), do_split(), do_which(), do_class();
extern int do_action(), do_keymap();

#define ST_COND   0x01
#define ST_NORED  0x02
#define ST_NOEXP  0x04
#define ST_AV     0x08 /* delimit args within a variable */
#define ST_FUNC   0x10

#define COND  ST_COND
#define NORED ST_NORED
#define NOEXP ST_NOEXP
#define AV    ST_AV
#define FUNC  ST_FUNC

#define ALIAS LEVEL_ALIAS
#define SET   LEVEL_SET

BPTR   OldCin;

struct COMMAND Command[] = {
 do_run,       0,   AV,     0, "\001",       NULL, NULL, /* may call do_source, do_cd */
 do_abortline, 0,    0,     0, "abortline",  NULL, "",
 do_action,    2,    0,     9, "action",     "av", "action file [args]",
 do_addbuffers,2,    0,     0, "addbuffers", NULL, "{drive bufs}",
 do_set_var,   0,    0, ALIAS, "alias",      NULL, "[name [string] ]",/* uses avline */
 do_ascii,     0,    0,     0, "ascii",      "oh", "-oh [string]",
 do_aset,      1,    0,     0, "aset",       NULL, "name value",
 do_assign,    0,    0,     0, "assign",     "ln", ",logical,-ln {logical physical}",
 do_basename,  2, FUNC,     0, "basename",   NULL, "var path",
 do_cat,       0,    0,     0, "cat",        "n",  "-n [file file...]",
 do_cd,        0,    0,     0, "cd",         "g",  "[path],-g path...path",
 do_class,     0,   AV,     0, "class",      "n",  "-n name {type=param} \"actions\" {action=command}",
 do_close,     0,    0,     0, "close",      NULL, "filenumber",
 do_copy,      1,    0,     0, "copy",       "rudpf","-rudpf file file,-ud file...file dir,-ud dir...dir dir",
 do_copy,      1,    0,     0, "cp",         "rudpf","-rudpf file file,-ud file...file dir,-ud dir...dir dir",
 do_date,      0,    0,     0, "date",       "sr", "-sr [date/time]",
 do_inc,       1,    0,    -1, "dec",        NULL, "varname [step]",
 do_rm,        0,    0,     0, "delete",     "rp", "-pr file...file",
 do_dir,       0,NOEXP,     0, "dir",        "sfdcnhltbuikqavo","-abcdfhiklnoqstuv [path...path]",
 do_diskchange,1,    0,     0, "diskchange", NULL, "drive",
 do_echo,      0,   AV,     0, "echo",       "ne", "-ne string",
 do_if,        0, COND,     1, "else",       NULL, "",
 do_if,        0, COND,     2, "endif",      NULL, "",
 do_exec,      1,    0,     0, "exec",       NULL, "command",
 do_fault,     1,    0,     0, "fault",      NULL, "error",
 do_filenote,  1,    0,     0, "filenote",   "s",  "file...file note,-s file...file",
 do_fileslist, 0,    0,     0, "flist",      NULL, "",
 do_fltlower,  0,    0,     0, "fltlower",   NULL, "<in >out",
 do_fltupper,  0,    0,     0, "fltupper",   NULL, "<in >out",
 do_foreach,   3,NORED,     0, "foreach",    "v",  "-v varname ( string ) command",
 do_forever,   1,NORED,     0, "forever",    NULL, "command",
 do_forline,   3,NORED,     0, "forline",    NULL, "var filename command",
 do_fornum,    4,NORED,     0, "fornum",     "vs", "-vs var n1 n2 command",
 do_getenv,    1, FUNC,     0, "getenv",     NULL, "shellvar envvar",
 do_goto,      1,    0,     0, "goto",       NULL, "label",
 do_head,      1,    0,     0, "head",       NULL, "filename [num]",
 do_help,      0,    0,     0, "help",       NULL, "",
 do_history,   0,    0,     0, "history",    NULL, "[partial_string]",
 do_howmany,   0,    0,     0, "howmany",    NULL, "",
 do_htype,     1,    0,     0, "htype",      "r",  "-r file...file",
 do_if,        1,COND|NORED,0, "if",         "rftmdvn","-n arg cond arg,-n arg,-nf file,-nd dir -nm,-nt file...file,-nr rpn_expr,-v varname",
 do_inc,       1,    0,     1, "inc",        NULL, "varname [step]",
 do_info,      0,    0,     0, "info",       NULL, "[drive...drive]",
 do_input,     1,    0,     0, "input",      "sr", "-rs var...var",
 do_join,      2,    0,     1, "join",       "r",  "-r file...file",
 do_keymap,    1,    0,     0, "keymap",     "n",  "-n number {key=function}",
 do_label,     1, COND,     0, "label",      NULL, "name",
 do_linecnt,   0,    0,     0, "linecnt",    NULL, "<in >out",
 do_dir,       0,NOEXP,     0, "ls",         "sfdcnhltbuikqav","-abcdfhiklnqstuv [path...path]",
 do_man,       0,    0,     0, "man",        NULL, "command...command",
 do_mkdir,     0,    0,     0, "md",         NULL, "name...name",
 do_mem,       0,    0,     0, "mem",        "cfqsr","-cfqsr",
 do_menu,      0,    0,     0, "menu",       "n",  "-n [title item...item]",
 do_mkdir,     0,    0,     0, "mkdir",      NULL, "name...name",
 do_mv,        2,    0,     0, "mv",         NULL, "from to,from...from todir",
 do_open,      3,    0,     0, "open",       NULL, "file mode number",
 do_path,      0,    0,     0, "path",       "r",  "-r [dir...dir]",
 do_pri,       2,    0,     0, "pri",        NULL, "clinumber pri,0 pri",
 do_protect,   2,    0,     0, "protect",    NULL, "file...file flags",
 do_ps,        0,    0,     0, "ps",         "le", "-el [commandname...commandname]",
 do_pwd,       0,    0,     0, "pwd",        NULL, "",
 do_qsort,     0,    0,     0, "qsort",      NULL, "<in >out",
 do_quit,      0,NORED,     0, "quit",       NULL, "",
 do_truerun,   1,NORED,     1, "rback",      NULL, "command",
 do_mv,        2,    0,     0, "rename",     NULL, "from to,from...from todir",
 do_readfile,  1,    0,     0, "readfile",   NULL, "varname [filename]",
 do_relabel,   2,    0,     0, "relabel",    NULL, "drive name",
 do_resident,  0,    0,     0, "resident",   "ard",",-ard file...file",
 do_return,    0,    0,     0, "return",     NULL, "[n]",
 do_rm,        0,    0,     0, "rm",         "rp", "-rp file...file",
 do_rpn,       0,NOEXP,     0, "rpn",        NULL, "expression",
 do_rxrec,     0,    0,     0, "rxrec",      NULL, "[portname]",
 do_rxsend,    2,    0,     0, "rxsend",     "rl", "-lc portname string",
 do_truerun,   1,NORED,     0, "run",        NULL, "command",
 do_search,    2,    0,     0, "search",     "rcwneqvbfalo","-abceflnoqrvw file...file string",
 do_set_var,   0,   AV,   SET, "set",        NULL, "[name [string] ]",
 do_setenv,    2,    0,     0, "setenv",     NULL, "var value",
 do_sleep,     0,    0,     0, "sleep",      NULL, "timeout",
 do_split,     1,    0,     0, "split",      NULL, "srcvar dstvar...dstvar",
 do_source,    1,NORED|AV,  0, "source",     NULL, "file", /* uses avline */
 do_stack,     0,    0,     0, "stack",      NULL, "[bytes]",
 do_strhead,   3, FUNC,     0, "strhead",    NULL, "varname breakchar string",
 do_strings,   1,    0,     0, "strings",    "r",  "-r file...file minlength",
 do_strleft,   3, FUNC,     0, "strleft",    NULL, "varname string n",
 do_strlen,    2, FUNC,     0, "strlen",     NULL, "varname string",
 do_strmid,    3, FUNC,     0, "strmid",     NULL, "varname string n1 [n2]",
 do_strright,  3, FUNC,     0, "strright",   NULL, "varname string n",
 do_strtail,   3, FUNC,     0, "strtail",    NULL, "varname breakchar string",
 do_tackon,    3, FUNC,     0, "tackon",     NULL, "var pathname filename",
 do_head,      1,    0,     1, "tail",       NULL, "filename [num]",
 do_tee,       0,    0,     0, "tee",        NULL, "<in >out",
 do_touch,     0,    0,     0, "touch",      NULL, "file...file",
 do_truncate,  0,    0,     0, "truncate",   NULL, "<in >out",
 do_cat,       0,    0,     0, "type",       NULL, "-n [file...file]",
 do_unset_var, 0,    0, ALIAS, "unalias",    NULL, "name...name",
 do_uniq,      0,    0,     0, "uniq",       NULL, "<in >out",
 do_unset_var, 0,    0,   SET, "unset",      NULL, "name...name",
 do_usage,     0,    0,     0, "usage",      NULL, "[command...command]",
 do_ver,       0,    0,     0, "version",    NULL, "",
 do_waitport,  1,    0,     0, "waitforport",NULL, "portname [seconds]",
 do_whereis,   1,NOEXP,     0, "whereis",    "r",  "-r file [path...path]",
 do_window,    0,NOEXP,     0, "window",     "slfbaq","-slfbaq",
 NULL,         0,    0,     0, NULL,         NULL, NULL,
};


/* do_which,     1,    0,     0, "which",      NULL, "command", */

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

#ifdef isalphanum
char isalph[256];
#endif

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

	if (!H_stack && S_histlen>1) {
		add_history(base);
		sprintf(buf, "%d", H_tail_base + H_len);
		set_var(LEVEL_SET, v_histnum, buf);
	}
	scr = malloc((strlen(base) << 2) + 2);
	preformat(base, scr);
	return (fcomm(scr) ? -1 : 1);
}

#ifndef isalphanum
isalphanum( char c )
{
	return (
		(c >= 'a' && c <= 'z') ||
		(c >= 'A' && c <= 'Z') ||
		(c >= '0' && c <= '9') ||
		(c == '_')
	);
}
#endif

#define HOT_GAP    0x80
#define HOT_BLANK  0x81
#define HOT_STAR   0x82
#define HOT_QUES   0x83
#define HOT_EXCL   0x84
#define HOT_SEMI   0x85
#define HOT_PIPE   0x86
#define HOT_DOLLAR 0x87
#define HOT_IN     0x88
#define HOT_OUT    0x89
#define HOT_BSLASH 0x8a
#define HOT_APOSTR 0x8b
#define HOT_USAGE  0x8c
#define HOT_SBLANK 0xA0


static void
preformat( char *s, char *d )
{
	int qm, i;

	qm = 0;
	while (*s == ' ' || *s == 9) ++s;
	if      (*s == '\\' ) { *d++=HOT_BSLASH; if( *d++=*++s ) ++s; }
	else if (*s == '~'  ) { *d++=HOT_APOSTR; s++; }

	while (*s) {
		if (qm ) {
			while( *s && *s != '\"' && *s != '\\')
				*d++ = *s++;
			if( !*s ) break;
		}
		switch (*s) {
		case ' ':
		case 9:
			*d++ = HOT_BLANK;
			while (*s == ' ' || *s == 9) ++s;
			if      (*s == '~'  ) { *d++=HOT_APOSTR; s++; }
			else if (*s == 0 || *s == '|' || *s == ';') --d;
			break;
		case '*':
			*d++ = HOT_GAP;
			*d++ = HOT_STAR;
			++s;
			break;
		case '?':
			*d++ = HOT_GAP;
			*d++ = HOT_QUES;
			++s;
			break;
		case '!':
			*d++ = HOT_EXCL;
			++s;
			break;
		case '#':
			*d++ = '\0';
			while (*s) ++s;
			break;
		case ';':
		case '|':
			*d++= (*s++==';') ? HOT_SEMI : HOT_PIPE;
			while (*s == ' ' || *s == 9) ++s;
			if    (*s == '\\' ) { *d++=HOT_BSLASH; if( *d++=*++s ) ++s; }
			break;
		case '\\':
			if( (i=*++s-'0')>=0 && i<=7 ) {
				if( *++s>='0' && *s<='7' ) {
					i= 8*i + *s++-'0';
					if( *s>='0' && *s<='7' )
						i= 8*i + *s++-'0';
				}
				*d++ = i;
			} else {
				*d++ = *s;
				if (*s) ++s;
			}
			break;
		case '\"':
			qm = 1 - qm;
			++s;
			break;
		case '^':
			*d++ = *++s & 0x1F;
			if (*s) ++s;
			break;
		case '<':
			*d++ = HOT_IN;
			++s;
			break;
		case '>':
			*d++ = HOT_OUT;
			++s;
			break;
		case '$': /* search end of var name and place false space */
			*d++ = HOT_GAP;
			*d++ = HOT_DOLLAR;
			++s;
			while (isalphanum(*s)) *d++ = *s++;
			*d++ = HOT_GAP;
			break;
		default:
			*d++ = *s++;
			break;
		}
	}
	*d++=0;
	*d=0;
	if (debug) fprintf (stderr,"PREFORMAT: %d :%s:\n", strlen(d), d);
}

static void
backtrans( char *str )
{
	while( *str ) {
		while( *(signed char *)str>0 ) str++;
		if( !*str ) break;
		switch( *str) {
			case HOT_GAP   : *str++=0;    break;
			case HOT_BLANK : *str++=' ';  break;
			case HOT_STAR  : *str++='*';  break;
			case HOT_QUES  : *str++='?';  break;
			case HOT_EXCL  : *str++='!';  break;
			case HOT_SEMI  : *str++=';';  break;
			case HOT_PIPE  : *str++='|';  break;
			case HOT_DOLLAR: *str++='$';  break;
			case HOT_IN    : *str++='<';  break;
			case HOT_OUT   : *str++='>';  break;
			case HOT_BSLASH: *str++='\\'; break;
			case HOT_APOSTR: *str++='~';  break;
			default        : str++;       break;
		}
	}
}


void *
mymalloc( int len )
{
	return malloc(len);
}

extern BPTR extOpen();

/*
 * 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.
 */


int
fcomm( char *str )
{
	static int alias_count;
	int p_alias_count;
	char *istr, *nextstr, *command;
	char *pend_alias;
	int err;

	++alias_count;

entry:
	p_alias_count = 0;
	pend_alias = NULL;
	err=0;
	has_wild = 0;

	mpush_base();
	if (*str == 0)
		goto done1;
step1:
	if (alias_count >= MAXALIAS || p_alias_count >= MAXALIAS) {
		fprintf(stderr,"Alias Loop\n");
		err = 20;
		goto done1;
	}

	istr = NULL;
	if ( *str == HOT_BSLASH )
		memmove( str, str+1, strlen(str));
	else 
		istr = get_var (LEVEL_ALIAS, str);  /* only if not \command */

	if (istr) {
		p_alias_count++;
		if (*istr == '%' || *istr=='*') {
			pend_alias = istr;
		} else {
			str = format_insert_string(str, istr );
			goto step1;
		}
	}

	if (*str == HOT_EXCL) {
		char *p, c;                     /* fix to allow !cmd1;!cmd2 */
		for(p = str; *p && *p != HOT_SEMI ; ++p);
		c = *p;
		*p = '\0';
		istr = get_history(str,1);
		*p = c;
		replace_head(istr);
		str = format_insert_string(str, istr );
		goto step1;
	}

	nextstr = str;
	command = exarg(&nextstr);
	if (*command == 0)
		goto done0;
	if (pend_alias == 0) {
		if (cmd_stat(command) & ST_COND)
			goto skipgood;
	}

	if (disable || forward_goto) {
		while (elast && elast != HOT_SEMI && elast != HOT_PIPE)
			exarg(&nextstr);
		goto done0;
	}
skipgood:
	{
		char *arg, *ptr;
		short redir;
		short doexpand;
		short cont;
		short inc;

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

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

		while (cont && elast) {
			int cstat = cmd_stat(command);

			ptr = exarg(&nextstr);
			inc = 1;
			arg = "";
			cont = (elast == 0x80);
			switch (*ptr) {
			case HOT_IN:
				redir = -2;
			case HOT_OUT:
				if (cstat & (ST_NORED | ST_COND)) {     /* don't extract   */
					redir = 0;                          /* <> stuff if its */
					arg = ptr;                          /* external cmd.   */
					break;
				}
				++redir;
				arg = ptr + 1;
				if (*arg == HOT_OUT) {
					redir = 2;        /* append >> */
					++arg;
				}
				cont = 1;
				break;
			case HOT_DOLLAR:
				/* restore args if from set command or pend_alias */
				if ((arg = get_var(LEVEL_SET, ptr + 1)) != NULL) {
					char *pe, sv;
					while (pe = index(arg,0xA0)) {
						sv = *pe;
						*pe = '\0';
						checkav(1);
						av[ac++] = push_cpy(arg);
						*pe = sv;
						av[ac] = NULL;
						arg = pe+1;
					}
				} else
					arg = ptr;
				break;
			case HOT_APOSTR:
				if ((arg = get_var(LEVEL_SET, v_lcd)) != NULL) {
					if( ptr[1] ) {
						strcpy(Buf,arg);
						appendslash(Buf);
						strcat(Buf,ptr+1);
						arg=Buf;
					}
				} else
					arg = ptr;
				break;

			case HOT_STAR:
			case HOT_QUES:
				if((cstat & ST_NOEXP) == 0 && !(pend_alias && *istr=='*'))
					if(ac==1&&(av[1]==NULL||!*av[1])&& *ptr==HOT_QUES&& !ptr[1])
						;
					else 
						doexpand = 1;
				arg = ptr;
				break;
			default:
				arg = ptr;
				break;
			}

			/* Append arg to av[ac] */

			if (av[ac]) {
				char *old = av[ac];
				av[ac] = mpush(strlen(arg)+strlen(av[ac]));
				strcpy(av[ac], old);
				strcat(av[ac], arg);
			} else
				av[ac] = push_cpy(arg);

			if (elast != 0x80)
				break;
		}

		/* process expansion */

		backtrans( av[ac] );
		if (doexpand) {
			char **eav, **ebase;
			int eac;
			has_wild = 1;
			eav = ebase = expand(av[ac], &eac);
			inc = 0;
			if (eav) {
				if( checkav( eac ) ) {
					ierror (NULL, 506);
					err = 1;
				} else {
					QuickSort(eav, eac);
					for (; eac; --eac, ++eav)
					av[ac++] = push_cpy(*eav);
				}
				free_expand (ebase);
			}
		} else if( av[ac][0]==')' ) {
			int i;
			char *pe, sv;
			for( i=ac-1; i>0; i-- )
				if( *av[i]=='@' )
					break;
			if( i>0 && av[i][strlen(av[i])-1]=='(' ) {
				extern int exec_fn_err;
				char *exec_function();
				char *avi=av[i], *last=av[ac];
				av[i]=v_value; av[ac]=NULL;
				arg=exec_function( avi+1, av+i, ac-i );
				av[i]=avi;     av[ac]=last;
				inc=0;
				if( exec_fn_err<0 )
					ac++;
				else if( exec_fn_err>0 || !arg )
					ac=i, av[ac++]="";
				else {
					ac=i;
					while (pe = index(arg,0xA0)) {
						sv = *pe;
						*pe = '\0';
						checkav( 2 );
						av[ac++] = push_cpy(arg);
						*pe = sv;
						arg= pe+1;
					}
					av[ac] = mpush(strlen(arg)+strlen(last+1)+4);
					strcpy(av[ac],arg);
					strcat(av[ac++], last+1 );
				}
			}
		}


		/* process redirection  */

		if (redir && !err) {
			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 == HOT_BLANK)
			goto step5;
	}
stepdone:
	av[ac] = NULL;

	/* process pipes via files */

	if (elast == HOT_PIPE && !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;

	{
		int i;
		char save_elast;
		char *avline;
		char delim = ' ';
		char *larg=av[ac-1];

		if( *larg && larg[strlen(larg)-1]=='&' ) {
			memmove( av+1, av, (ac-1)*sizeof(*av));
			command=av[0]="rback";
			if( strlen(larg)>1 )
				larg[strlen(larg)-1]=0, ac++;
		}
		save_elast = elast;
		if (pend_alias || (cmd_stat(command) & ST_AV))
			delim = 0xA0;
		avline = compile_av(av,((pend_alias) ? 1 : 0), ac, delim, 0);

		if (pend_alias) {                               /* special % alias */
			char *ptr, *scr, *varname, *val=avline, *gap;
			ptr=pend_alias;
			do {                                        /* set all args    */
				for ( varname= ++ptr; *ptr && *ptr!=' ' && *ptr!='%'; ++ptr);
				if( *ptr=='%' && (gap=index(val,0xA0 )) ) *gap=0;
				set_var( LEVEL_SET, varname, val );
				val= gap ? gap+1 : "";
			} while( *ptr=='%' );
			free (avline);

			scr = malloc((strlen(ptr) << 2) + 2);
			preformat (ptr, scr);
			fcomm (scr);
			ptr=pend_alias;
			do {                                        /* unset all args  */
				for ( varname=++ptr; *ptr && *ptr!=' ' && *ptr!='%'; ++ptr);
				unset_var( LEVEL_SET, varname );
			} while( *ptr=='%' );
			unset_var (LEVEL_SET, pend_alias + 1);
		} else {                                        /* normal command  */
			int ccno;
			BPTR  oldcin  = Myprocess->pr_CIS;
			BPTR  oldcout = Myprocess->pr_COS;
			char *Cin_buf;
			struct FileHandle *ci;
			long oldbuf;

			OldCin=oldcin;
			fflush(stdout);
			LastCommand=command;
			ccno = find_command ( command);
			if ((Command[ccno].stat & (ST_NORED | ST_COND)) == 0) {
				if (Cin_name) {
					if ((Cin = (long)extOpen(Cin_name,1005L)) == 0) {
						ierror (NULL, 504);
						err = 1;
						Cin_name = NULL;
					} else {
						Myprocess->pr_CIS = DEVTAB(stdin) = Cin;
						ci = (struct FileHandle *)(((long)Cin)<<2);
						Cin_buf = AllocMem(202L, MEMF_PUBLIC);
						oldbuf = ci->fh_Buf;
						if (ci->fh_Buf == 0) /* fexec expects a CIS buffer */
							ci->fh_Buf = (long)Cin_buf>>2;
					}
				}
				if (Cout_name) {
					if (Cout_append && (Cout =(long)extOpen(Cout_name,1005L))) {
						Seek(Cout, 0L, 1L);
					} else {
						Cout = (long)extOpen(Cout_name,1006L);
					}
					if (Cout == NULL) {
						err = 1;
						ierror (NULL, 504);
						Cout_name = NULL;
						Cout_append = 0;
					} else {
						Myprocess->pr_COS = DEVTAB(stdout) = Cout;
					}
				}
			}
			if (ac < Command[ccno].minargs + 1) {
				show_usage( NULL );
				err = -1;
			} else if (!err) {
				int (*func)(char*,int)=Command[ccno].func;
				get_opt( av, &ac, ccno );
				i=0;
				if( ccno>0 && ac>1 && !strcmp(av[1],"?") )
					show_usage(avline);
				else 
					i = (*func)(avline, Command[ccno].val);
				fflush(stderr);
				if (i < 0)
					i = 20;
				err = i;
			}
			free (avline);
			if (E_stack == 0 && Lastresult != err) {
				Lastresult = err;
				seterr();
			}
			if ((Command[ccno].stat & (ST_NORED | ST_COND)) == 0) {
				if (Cin_name) {
					ci->fh_Buf = oldbuf;
					fflush(stdin);
					clearerr(stdin);
#ifdef AZTEC_C
					stdin->_bp=stdin->_bend;
#else
					stdin->_rcnt=stdin->_wcnt;
#endif
					extClose(Cin);
					FreeMem(Cin_buf, 202L);
				}
				if (Cout_name) {
					fflush(stdout);
					clearerr(stdout);
#ifdef AZTEC_C
					stdout->_flags &= ~_IODIRTY;    /* because of nil: device */
#endif
					extClose(Cout);
					Cout_append = 0;
				}
			}
			Myprocess->pr_CIS = DEVTAB(stdin)  = oldcin;
			Myprocess->pr_COS = DEVTAB(stdout) = oldcout;
		}

		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 = 0;
		elast = save_elast;
	}
	mpop_tobase();                      /* free arguments   */
	mpush_base();                       /* push dummy base  */

done0:
	{
		char *exc;
		if (err && E_stack == 0) {
			exc = get_var(LEVEL_SET, v_except);
			if (err >= ((exc)?atoi(exc):1)) {
				if (exc) {
					++H_stack, ++E_stack;
					a0tospace(exc);
					exec_command(exc);
					--E_stack, --H_stack;
				} else {
					Exec_abortline = 1;
				}
			}
		}
		if (elast != 0 && Exec_abortline == 0) {
			memmove( str, nextstr, strlen(nextstr)+1 );
			goto entry;
		}
		Exec_abortline = 0;
		if (Cin_name)
			DeleteFile(Cin_name);
		Cin_name = NULL;
		Cin_ispipe = 0;
	}
done1:
	mpop_tobase();
	free(str);
	--alias_count;
	return err;                      /* TRUE = error occured    */
}


static char *
exarg(ptr)
char **ptr;
{
	char *end, *start;

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

static char **Mlist;

static void
mpush_base()
{
	char *str;

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

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

	str = malloc(6 + bytes + 2);   /* may need extra 2 bytes in do_run() */
	*(char ***)str = Mlist;
	str[4] = 1;
	Mlist = (char **)str;
	return (str + 5);
}

static void
mpop_tobase()
{
	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
 */

static char *
format_insert_string(char *str, char *from)
{
	char *new1, *new2;
	char *strskip;
	int len;

	for (strskip = str; *(signed char *)strskip>0 ||
		*strskip && *strskip != HOT_BLANK 
		&& *strskip != HOT_SEMI && *strskip != HOT_PIPE
		&& *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);
	free (str);
	return new2;
}

static int
cmd_stat( char *str )
{
	return (int)Command[find_command(str)].stat;
}

char *
find_internal( char *str )
{
	return(Command[find_command(str)].name);
}

static int
find_command( char *str )
{
	int i, len = strlen(str);
	struct COMMAND *com;
	char c=*str;

	for( com=Command, i=0; com->func; com++, i++ )
		if ( c==*com->name && !strncmp(str, com->name, len))
			return i;
	return 0;
}

int exec_fn_err;

extern struct FUNCTION {
	short id, minargs, maxargs;
	char *name;
} Function[];


char *gotfunc( int i, char **fav, int fac );

char *
exec_function( char *str, char **fav, int fac)
{
	int len=strlen(str)-1, i;

	exec_fn_err=0;
	for (i = 0; Command[i].func; ++i)
		if ( Command[i].stat&ST_FUNC && !strncmp(str,Command[i].name,len)) {
			if( fac<Command[i].minargs ) {
				exec_fn_err=20;
				return NULL;
			} else {
				int (*func)( void )=Command[i].func;
				char **oldav=av;
				int  oldac=ac;
				av=fav-1, ac=fac+1;
				exec_fn_err=(*func)();
				av=oldav, ac=oldac;
				return get_var( LEVEL_SET, fav[0] );
			}
		}
	for (i = 0; Function[i].id; ++i)
		if ( len==strlen(Function[i].name)&&!strncmp(str,Function[i].name,len))
			return gotfunc( i,fav,fac );

	exec_fn_err=-1;
	return NULL;
}

int
echofunc(void)
{
	int  i;
	char *str;

	if( !strlen(av[0]) ) return -1;
	exec_fn_err=0;
	for (i = 0; Function[i].id; ++i)
		if ( !strcmp(av[0],Function[i].name)) {
			if(str=gotfunc( i,av,ac ))
				printf("%s\n",str);
			return exec_fn_err;
		}
	return -1;
}


char *
gotfunc( int i, char **fav, int fac )
{
	fac--; fav++;
	if( fac<Function[i].minargs ) {
		fprintf( stderr, "Not enough arguments for @%s\n",
		                  Function[i].name );
		exec_fn_err=20;
		return NULL;
	} else if( fac>Function[i].maxargs ) {
		if( ac > Function[i].maxargs )
			fprintf( stderr, "Too many arguments for @%s\n",
			                  Function[i].name );
		exec_fn_err=20;
		return NULL;
	} else {
		exec_fn_err=dofunc( Function[i].id, fav, fac);
		return get_var( LEVEL_SET, v_value );
	}
	return NULL;
}



do_help()
{
	struct COMMAND *com;
	int i=0;

	for (com = &Command[1]; com->func; ++com) {
		printf ("%-12s", com->name);
		if (++i % 6 == 0) printf("\n");
	}
	printf("\n\nUse   man <command>   for more information\n");
	return 0;
}

static char *
push_cpy(s)
char *s;
{
	return strcpy(mpush(strlen(s)), s);
}

void
exec_every(void)
{
	char *str = get_var(LEVEL_SET, v_every);

	if (str) {
		++H_stack, ++E_stack;
		a0tospace( str );
		exec_command(str);
		--E_stack, --H_stack;
	}
}

char *
a0tospace( str )
	char *str;
{
	char *get=str, *put=str;

	while( *get )
		if( *get==0xA0 )
			*put++=' ', get++;
		else 
			*put++=*get++;
	return str;
}

void
show_usage( str )
	char *str;
{
	int ccno, first=0, err=0;
	char *get, *put, buf[300];

	if( !str )
		str=LastCommand, err=1;
	for( put=str; *put && (*put&127)!=32; put++ ) ;
	*put=0;

	put=buf;
	ccno = find_command (str);
	if( get= Command[ccno].usage ) {
		do {
			put+=sprintf(put, first++?"       %s ":"Usage: %s ",
			             Command[ccno].name );
			if( *get=='-' ) {
				*put++='['; *put++='-';
				get++;
				while( *get && *get!=' ' && *get!=',' )
					*put++=*get++;
				*put++=']';
			}
			while( *get && *get!=','  )
				*put++=*get++;
			*put++='\n';
		} while( *get++ );
		*put=0;
		fprintf( err ? stderr : stdout, "%s", buf );
	}
}

int
execute( char *str )
{
	char  **tav=av, telast=elast;
	int   tac=ac;
	ULONG toptions=options;
	int   thas_wild=has_wild;

	if( !str ) return -1;

	++H_stack;
	exec_command(str);
	--H_stack;

	av=tav; ac=tac; elast=telast; options=toptions; has_wild=thas_wild;

	return 0;
}

do_exec( char *str )
{
	return execute( next_word( str ) );
}

int
interactive( void )
{
	return IsInteractive(Output());
}

static int
checkav( int n )
{
	char **tmp;
	int newac;

	if( ac+n+10>=max_ac ) {
		newac=max_ac+n+40;
		if( tmp=(char **)malloc(newac*sizeof(char *))) {
			memcpy(tmp,av,max_ac*sizeof(char *));
			free(av);
			av=tmp; max_ac=newac;
		} else 
			return 1;
	}
	return 0;
}

/*	Parse the options specified in sw[]
	Setting a bit in global variable options
	for each one found
*/

static void
get_opt( char **av, int *ac, int ccno )
{
	char **get=av+1,**put=av+1, *c, *s;
	int i=1, l, usage=0, nac;
	long oldopts;

	options=0;
	if( !ccno )
		return;

	for( ; i<*ac && *av[i]=='-'; i++, get++ ) {
		if( !*(c=*get+1) )
			goto stop;
		oldopts=options;
		for ( ; *c ; c++) {
			if( *c<'a' || *c>'z' ) 
				{ options=oldopts; goto stop; }
			for( l=0, s=Command[ccno].options; *s && *s != *c; ++s )
				++l;
			if ( *s )
				options |= (1 << l);
			else if( !usage ) {
				usage=1;
				show_usage(NULL);
			}
		}
	}
stop:
	for( nac=1; i<*ac; i++ )
		*put++=*get++, nac++;
	*put=NULL;
	*ac=nac;
}

#if 0
USHORT Options[160];

int
do_options()
{
	for( i=1; i<ac; i+=2 ) {
		if( ac-i<=1 )
			{ ierror( av[i], 500 ); return 20; }
		if( *av[i+1]!='-' )
			{ ierror( av[i+1], 500 ); return 20; }
		options=0;
		parseopts( av[i+1]+1 );
	}
}
#endif
