#include "rc.h"
#include "exec.h"
#include "io.h"
#include "getflags.h"
#include "fns.h"
int getnext(void);

int
wordchr(int c)
{
	return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF;
}

int
idchr(int c)
{
	/*
	 * Formerly:
	 * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9'
	 *	|| c=='_' || c=='*';
	 */
	return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c);
}
int future = EOF;
int doprompt = 1;
int inquote;
int incomm;
/*
 * Look ahead in the input stream
 */

int
nextc(void)
{
	if(future==EOF)
		future = getnext();
	return future;
}
/*
 * Consume the lookahead character.
 */

int
advance(void)
{
	int c = nextc();
	lastc = future;
	future = EOF;
	return c;
}
/*
 * read a character from the input stream
 */	

int
getnext(void)
{
	int c;
	static int peekc = EOF;
	if(peekc!=EOF){
		c = peekc;
		peekc = EOF;
		return c;
	}
	if(runq->eof)
		return EOF;
	if(doprompt)
		pprompt();
	c = rchr(runq->cmdfd);
	if(!inquote && c=='\\'){
		c = rchr(runq->cmdfd);
		if(c=='\n' && !incomm){		/* don't continue a comment */
			doprompt = 1;
			c=' ';
		}
		else{
			peekc = c;
			c='\\';
		}
	}
	doprompt = doprompt || c=='\n' || c==EOF;
	if(c==EOF)
		runq->eof++;
	else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c);
	return c;
}

void
pprompt(void)
{
	var *prompt;
	if(runq->iflag){
		pstr(err, promptstr);
		flush(err);
		prompt = vlook("prompt");
		if(prompt->val && prompt->val->next)
			promptstr = prompt->val->next->word;
		else
			promptstr="\t";
	}
	runq->lineno++;
	doprompt = 0;
}

void
skipwhite(void)
{
	int c;
	for(;;){
		c = nextc();
		/* Why did this used to be  if(!inquote && c=='#') ?? */
		if(c=='#'){
			incomm = 1;
			for(;;){
				c = nextc();
				if(c=='\n' || c==EOF) {
					incomm = 0;
					break;
				}
				advance();
			}
		}
		if(c==' ' || c=='\t')
			advance();
		else return;
	}
}

void
skipnl(void)
{
	int c;
	for(;;){
		skipwhite();
		c = nextc();
		if(c!='\n')
			return;
		advance();
	}
}

int
nextis(int c)
{
	if(nextc()==c){
		advance();
		return 1;
	}
	return 0;
}

char*
addtok(char *p, int val)
{
	if(p==0)
		return 0;
	if(p==&tok[NTOK-1]){
		*p = 0;
		yyerror("token buffer too short");
		return 0;
	}
	*p++=val;
	return p;
}

char*
addutf(char *p, int c)
{
	p = addtok(p, c);
	if(twobyte(c))	 /* 2-byte escape */
		return addtok(p, advance());
	if(threebyte(c)){	/* 3-byte escape */
		p = addtok(p, advance());
		return addtok(p, advance());
	}
	if(fourbyte(c)){	/* 4-byte escape */
		p = addtok(p, advance());
		p = addtok(p, advance());
		return addtok(p, advance());
	}
	return p;
}
int lastdol;	/* was the last token read '$' or '$#' or '"'? */
int lastword;	/* was the last token read a word or compound word terminator? */

int
yylex(void)
{
	int c, d = nextc();
	char *w = tok;
	struct tree *t;
	yylval.tree = 0;
	/*
	 * Embarassing sneakiness:  if the last token read was a quoted or unquoted
	 * WORD then we alter the meaning of what follows.  If the next character
	 * is `(', we return SUB (a subscript paren) and consume the `('.  Otherwise,
	 * if the next character is the first character of a simple or compound word,
	 * we insert a `^' before it.
	 */
	if(lastword){
		lastword = 0;
		if(d=='('){
			advance();
			strcpy(tok, "( [SUB]");
			return SUB;
		}
		if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
			strcpy(tok, "^");
			return '^';
		}
	}
	inquote = 0;
	skipwhite();
	switch(c = advance()){
	case EOF:
		lastdol = 0;
		strcpy(tok, "EOF");
		return EOF;
	case '$':
		lastdol = 1;
		if(nextis('#')){
			strcpy(tok, "$#");
			return COUNT;
		}
		if(nextis('"')){
			strcpy(tok, "$\"");
			return '"';
		}
		strcpy(tok, "$");
		return '$';
	case '&':
		lastdol = 0;
		if(nextis('&')){
			skipnl();
			strcpy(tok, "&&");
			return ANDAND;
		}
		strcpy(tok, "&");
		return '&';
	case '|':
		lastdol = 0;
		if(nextis(c)){
			skipnl();
			strcpy(tok, "||");
			return OROR;
		}
	case '<':
	case '>':
		lastdol = 0;
		/*
		 * funny redirection tokens:
		 *	redir:	arrow | arrow '[' fd ']'
		 *	arrow:	'<' | '<<' | '>' | '>>' | '|'
		 *	fd:	digit | digit '=' | digit '=' digit
		 *	digit:	'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
		 * some possibilities are nonsensical and get a message.
		 */
		*w++=c;
		t = newtree();
		switch(c){
		case '|':
			t->type = PIPE;
			t->fd0 = 1;
			t->fd1 = 0;
			break;
		case '>':
			t->type = REDIR;
			if(nextis(c)){
				t->rtype = APPEND;
				*w++=c;
			}
			else t->rtype = WRITE;
			t->fd0 = 1;
			break;
		case '<':
			t->type = REDIR;
			if(nextis(c)){
				t->rtype = HERE;
				*w++=c;
			} else if (nextis('>')){
				t->rtype = RDWR;
				*w++=c;
			} else t->rtype = READ;
			t->fd0 = 0;
			break;
		}
		if(nextis('[')){
			*w++='[';
			c = advance();
			*w++=c;
			if(c<'0' || '9'<c){
			RedirErr:
				*w = 0;
				yyerror(t->type==PIPE?"pipe syntax"
						:"redirection syntax");
				return EOF;
			}
			t->fd0 = 0;
			do{
				t->fd0 = t->fd0*10+c-'0';
				*w++=c;
				c = advance();
			}while('0'<=c && c<='9');
			if(c=='='){
				*w++='=';
				if(t->type==REDIR)
					t->type = DUP;
				c = advance();
				if('0'<=c && c<='9'){
					t->rtype = DUPFD;
					t->fd1 = t->fd0;
					t->fd0 = 0;
					do{
						t->fd0 = t->fd0*10+c-'0';
						*w++=c;
						c = advance();
					}while('0'<=c && c<='9');
				}
				else{
					if(t->type==PIPE)
						goto RedirErr;
					t->rtype = CLOSE;
				}
			}
			if(c!=']'
			|| t->type==DUP && (t->rtype==HERE || t->rtype==APPEND))
				goto RedirErr;
			*w++=']';
		}
		*w='\0';
		yylval.tree = t;
		if(t->type==PIPE)
			skipnl();
		return t->type;
	case '\'':
		lastdol = 0;
		lastword = 1;
		inquote = 1;
		for(;;){
			c = advance();
			if(c==EOF)
				break;
			if(c=='\''){
				if(nextc()!='\'')
					break;
				advance();
			}
			w = addutf(w, c);
		}
		if(w!=0)
			*w='\0';
		t = token(tok, WORD);
		t->quoted = 1;
		yylval.tree = t;
		return t->type;
	}
	if(!wordchr(c)){
		lastdol = 0;
		tok[0] = c;
		tok[1]='\0';
		return c;
	}
	for(;;){
		/* next line should have (char)c==GLOB, but ken's compiler is broken */
		if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB)
			w = addtok(w, GLOB);
		w = addutf(w, c);
		c = nextc();
		if(lastdol?!idchr(c):!wordchr(c)) break;
		advance();
	}

	lastword = 1;
	lastdol = 0;
	if(w!=0)
		*w='\0';
	t = klook(tok);
	if(t->type!=WORD)
		lastword = 0;
	t->quoted = 0;
	yylval.tree = t;
	return t->type;
}
