/*
 * Csh - a shell by PJAC - parse.c the command line parser
 */
#include "csh.h"

#define	LESS_LESS 2
#define	MORE_MORE 3
#define	MORE_AND  4
#define	MORE_MAND 5

parse(in_line, treep)
char	*in_line;
struct	cmd	**treep;
{
	char	*p, **xp;
	char	*q;
	int	c;
	struct	cmd	*cur_tree;
	char	cur_arg[256];
	char	nxt_arg[256];
	int	narg;
	struct	cmd	*newtree();
	struct	cmd	*tree_top, *tp;

	cur_tree = newtree();	/* get a new tree */
	tree_top = cur_tree;
	for(p = in_line ; p = skipsp(p) ; ){
		switch(next_object(cur_arg, &p, nxt_arg, noglob)){
		case COMMENT:
			p = "";	/* YEUCH, cheat to end of line */
			continue;
		case GLOBER:
			do {
				un_spec(nxt_arg);
				Args[Argc++] = strsave(nxt_arg);
			} while(explode( (char *)0, nxt_arg));
			break;
		case NORMAL:	/* normal string */
			un_spec(cur_arg);
			Args[Argc++] = strsave(cur_arg);
			continue;
		case SPECIAL:
			/*
			 * else it's a special char
			 */
			switch(c = *p++){
			case '<':
				if(*p == '<'){
					c = LESS_LESS;
					p++;
					break;
				}
				if(!(p = skipsp(p)))
					goto bad;
				switch(next_object(cur_arg, &p, nxt_arg, noglob)){
				case GLOBER:
					if(!explode( (char *)0, nxt_arg)){
						strcpy(cur_arg, nxt_arg);
						break;
					}
					/* fall through */
				case SPECIAL:
				case COMMENT:
					goto bad;
				}
				un_spec(cur_arg);
				if(cur_tree->cm_ifile)
					goto bad;
				cur_tree->cm_ifile = strsave(cur_arg);
				break;
			case '>':
				if(*p == '>'){
					if(*++p == '&'){
						c = MORE_MAND;
						p++;
					}
					else
						c = MORE_MORE;
				}
				else if(*p == '&'){
					c = MORE_AND;
					p++;
				}
				if(!(p = skipsp(p)))
					goto bad;
				switch(next_object(cur_arg, &p, nxt_arg, noglob)){
				case GLOBER:
					if(!explode( (char *)0, nxt_arg)){
						strcpy(cur_arg, nxt_arg);
						break;
					}
					/* fall through */
				case SPECIAL:
				case COMMENT:
					goto bad;
				}
				un_spec(cur_arg);
				if(cur_tree->cm_ofile)
					goto bad;
				q = mmalloc(strlen(cur_arg) + 1 + 2);
				/* convert from user to internal rep */
				switch(c){
				case '>':
					*q = 0;
					break;
				case MORE_AND:
					strcpy(q, "&");
					break;
				case MORE_MAND:
					strcpy(q, "&>");
					break;
				case MORE_MORE:
					strcpy(q, ">");
					break;
				}
				strcat(q, cur_arg);
				cur_tree->cm_ofile = q;
				break;
			case '|':
				if(cur_tree->cm_ofile)
					free(cur_tree->cm_ofile);
				if(*p == '&'){
					p++;
					cur_tree->cm_ofile = strsave("|&");
				}
				else
					cur_tree->cm_ofile = strsave("|");
				if(save_args(cur_tree))
					goto bad;
				tp = cur_tree;
				cur_tree = newtree();
				tp->cm_next = cur_tree;
				cur_tree->cm_parent = tp->cm_parent;
				cur_tree->cm_ifile = strsave("|");
				break;
			case '(':
				/* if not first char of command then
				 * assume it is like any other char
				 */
				if(Argc != cur_tree->cm_savc){
					Args[Argc++] = strsave("(");
					continue;
				}
				/* if we have been down before, give
				 * a syntax error
				 */
				if(cur_tree->cm_child)
					goto bad;
				tp = cur_tree;
				cur_tree = newtree();
				tp->cm_child = cur_tree;
				cur_tree->cm_parent = tp;
				break;
			case ')':
				if(cur_tree->cm_parent == 0){
					Args[Argc++] = strsave(")");
					continue;
				}
				if(save_args(cur_tree))
					goto bad;
				cur_tree = cur_tree->cm_parent;
				break;
			case ';':
			case '&':	/* treat & same as ';' */
				/*
				 * store away the args,
				 * get a new node
				 * link and remember to copy over the
				 * same parent
				 */
				if(save_args(cur_tree))
					goto bad;
				tp = cur_tree;
				cur_tree = newtree();
				tp->cm_next = cur_tree;
				cur_tree->cm_parent = tp->cm_parent;
				break;
			case '`':
				break;
			}
			/* end of special chars */
			break;
		}
	}
	if(p)
		goto bad;
	for(tp = tree_top ; tp ; tp = tp->cm_next)
		if(tp == cur_tree)
			break;
	if(!tp)
		goto bad;
	if(save_args(cur_tree))
		goto bad;
	if(tree_top->cm_argc || tree_top->cm_next || tree_top->cm_child ||
				tree_top->cm_ifile || tree_top->cm_ofile)
		*treep = tree_top;
	else
		free_tree(tree_top);
	return;
bad:;
	free_tree(tree_top);
	syn_err();
}

save_args(cur_tree)
struct	cmd	*cur_tree;
{
	int	narg;

	narg = Argc - cur_tree->cm_savc;
	if(!narg)
		return(OK);
	if(cur_tree->cm_argv != 0)
		return(ERR);
	cur_tree->cm_argv = (char **)mmalloc(sizeof(char *)* (narg+1));
	cur_tree->cm_argv[narg] = 0;
	cur_tree->cm_argc = narg;
	while(narg)
		cur_tree->cm_argv[--narg] = Args[--Argc];
	return(OK);
}

struct	cmd *
newtree()
{
	struct	cmd	*cp;

	cp = (struct cmd *)mmalloc(sizeof(struct cmd));
	cp->cm_argv = 0;
	cp->cm_next = 0;
	cp->cm_child = 0;
	cp->cm_parent = 0;	/* don't recurse down the parent */
	cp->cm_ifile = 0;
	cp->cm_ofile = 0;
	cp->cm_argc = 0;
	cp->cm_savc = Argc;
	return(cp);
}

free_tree(cp)
struct	cmd	*cp;
{
	int	i;

	if(cp->cm_next)
		free_tree(cp->cm_next);
	if(cp->cm_child)
		free_tree(cp->cm_child);
	if(cp->cm_ifile)
		free(cp->cm_ifile);
	if(cp->cm_ofile)
		free(cp->cm_ofile);
	for(i = 0; i < cp->cm_argc ; i++)
		free(cp->cm_argv[i]);
	free( (char *)cp);
}

/*
 * called on syntax error
 */

syn_err()
{
	while(Argc > 0){
		if(Args[--Argc]){
			free(Args[Argc]);
			Args[Argc] = 0;
		}
	}
	longjmp(env_main, BADPARSE);
}

next_object(buf, strp, nxt_arg, nglob)
char	*buf, **strp, *nxt_arg;
{
	char	*bufp = buf;
	char	*str = *strp;
	int	c;
	int	seenb = FALSE;

	if(*str == '#')
		return(COMMENT);
	if(index(Special, *str) != NULL){
		if(*str == '"' || *str == '\''){
			c = *str++;
			while(*str){
				if(*str == c)
					break;
				*buf++ = *str++ | 0200;
			}
			*buf = 0;
			if(*str++ == '\0')
				syn_err();
			*strp = str;
			return(NORMAL);
		}
		return(SPECIAL);
	}
	*buf++ = *str++;
	while(*str){
		if(index(IFS, *str))
			break;
		if(*str == '{')
			seenb = TRUE;
		else if(*str == '}'){
			if(!seenb)
				break;
		}
		else if(index(Special, *str))
			break;
		*buf++ = *str++;
	}
	*buf = 0;
	*strp = str;
	if(!nglob){
		/*
		 * explode ~ strings
		 */
		if(*bufp == '~' && (bufp[1] == '/' || bufp[1] == '\\' || 
							bufp[1] == '\0')){
			strcpy(nxt_arg, bufp+1);
			strcpy(bufp, HOME);
			strcat(bufp, nxt_arg);
		}
		for(str = "*?[]" ; *str ; str++)
			if(index(bufp, *str)){
				if(explode(bufp, nxt_arg))   /* explode args */
					return(GLOBER);
				break;
			}
	}
	return(NORMAL);
}
