#include <sys/unix.h>
#include "main.h"
#include "parse.h"

extern int ERRNO;

void mainExecute();

static char history[HISTLEN][LINELEN];
static int histpos=0;

#define SKIPME (1)

char	*sigmsg[16] = 
{
	"",
	"Hangup\n",
	"Interrupt\n",
	"Quit\n",
	"Illegal instruction\n",
	"Trap\n",
	"iot\n",
	"emt\n",
	"Floating point exception\n",
	"Kill\n",
	"Bus error\n",
	"Segmentation fault\n",
	"Bad syscall parameter\n",
	"Write to readerless pipe\n",
	"Alarm\n",
	"Terminate\n"
};


int main( argc, argv, envp )
int	argc;
char	*argv[];
char	*envp;
{
	int	i;
	char	**estrs = (char**)&envp;

	signal( SIGINT, SIG_IGN );
	
	for(i=0;i<HISTLEN;i++)
		history[i][0]=0;

	/* Read the environment variables */
	i = 0;
	while( estrs[i] )
	{
		char	*val, *name;
		name = val = estrs[i];
		while( *val && (*val!='=') )
			val++;
		if( !val )
		{
			fprintf(stderr,"winsh: error in environment strings\n");
			break;
		}
		*val = 0;	/* Slappy wristy; poking the originals */
		parseAddName( name, val+1 );
		i++;
	}

	while( mainDoOne() );

	return( 0 );
}


void mainGetLine( line )
char	*line;
{
	char	*pos = line;

	fprintf( stdout, "winsh%% " );
	
	while( 1 )
	{
		while( 0 == read( stdin, pos, 1 ) );
		if( *pos == '\n' )
			break;
		pos++;
	}
	*pos = 0;
}


char *realword( word, words )
int	word;
char	*words[];
{
	int	i=0;
	while( word && words[i] )
	{
		i++;
		if( words[i]!=(char*)SKIPME )
			word--;
	}
	return words[i];
}


int mainDoOne()
{
	char	line[LINELEN],
		*words[32];
	int	i;
	int	foreground = 1;
	void mainSubVars();
	char	*sout, *sin, *serr;


	mainGetLine( line );

	if( line[0]=='!' && line[1]=='-' )
	{
		i = atoi( &line[2] );
		if( i>0 && i<HISTLEN )
			strcpy( line, history[ (histpos-i)<0 ? (histpos-i)+HISTLEN : histpos-i ] );
		else
			strcpy( line, ";Unknown history" );
	}

	strcpy( history[histpos], line );

	if( line[0]==';' )
		goto done;

	mainSubVars( line );

	parseSeparate( line, words );

	if( words[0] == 0 )
		return 1;

	/* Pull out i/o redirection words and ampersand*/
	sout = sin = serr = (char*)0;
	for(i=1;words[i];i++)
	{
		if( strcmp( words[i], "&" ) == 0 )
		{
			foreground = 0;
			words[i] = (char*)SKIPME;
		}
		else if( words[i][0]=='>' && words[i][1]=='>' )
		{
			serr = &words[i][2];
			words[i] = (char*)SKIPME;
		}
		else if( words[i][0]=='>' )
		{
			sout = &words[i][1];
			words[i] = (char*)SKIPME;
		}
		else if( words[i][0]=='<' )
		{
			sin = &words[i][1];
			words[i] = (char*)SKIPME;
		}
	}

	if( !strcmp(words[0],"set") && realword(1,words) && realword(2,words) )
	{
		parseAddName( realword(1,words), realword(2,words) );
		goto done;
	}
	else if( !strcmp(words[0],"history") )
	{
		int	pos = histpos;
		for(i=0;i<HISTLEN;i++)
		{
			fprintf(stdout,"-%i:%s\n",i+1,history[pos--]);
			pos = (pos<0) ? HISTLEN-1 : pos;
		}
		goto done;
	}
	else if( !strcmp( words[0], "logout" ) )
		return 0;
	else if( !strcmp( words[0], "cd" ) && realword(1,words) )
	{
		if( chdir(words[1]), ERRNO )
			fprintf(stderr,"cd: could not change to directory\n");
		goto done;
	}
		
	/* End of builtins, try to run external command */
	mainExecute( words, sin, sout, serr, foreground );

done:
	histpos = (histpos+1)%HISTLEN;
	return 1;
}



void mainSubVars( line )
char	*line;
{
	char	newline[LINELEN*2];
	char	*pos, *wpos = newline;

	/* Step 1: expand variables */
	pos = line;
	while( *pos )
	{
		if( *pos == '$' )
		{
			char	*end = pos+1,
				str[32], *spos=str;
			variable *var;
			while( *end && !parseCheckIsWhite(*end) && (*end!='$') )
				*spos++ = *end++;
			*spos=0;
			if(*end=='$')
				end++;
			if( var = parseFindName( str ) )
			{
				strcpy( wpos, var->exp );
				wpos += strlen( var->exp );
				pos = end;
			}
			else
			{
				fprintf(stderr,"Variable %s unknown",str);
				*wpos++ = '$';
				pos++;
			}
		}
		else
			*wpos++ = *pos++;
	}
	*wpos = 0;
	strcpy( line, newline );
}


int warn( msg )
char	*msg;
{
	fprintf( stderr, "winsh warning: %s\n", msg );
}


void mainExecute( words, sin, sout, serr, foreground )
char	*words[];
char	*sin, *sout, *serr;
int	foreground;
{
	int		pid;	/* Child's process id */
	int		i;
	int		nsin, nsout, nserr;
	char		*args[32];
	char		*envs[32];
	char		*command;
	variable	*path = (char*)parseFindName( "path" );
	struct stat	statbuf;


	/* Try to find the command in the path */
	if( path==0 )
		command = words[0];
	else
	{
		char	head[128];
		char	*here = path->exp;
		command = words[0];	/* Default */
		while( *here )
		{
			i=0;
			while( *here && *here != ':' )
				head[i++] = *here++;
			head[i] = 0;
			strcat( head, "/" );
			strcat( head, words[0] );
			if( (stat(head,&statbuf)==0) && (ERRNO==0) )
			{
				command = head;
				break;
			}
			if( *here )
				here++;
		}
	}


	/* Separate the shell from the command */
	if( pid=fork() )
	{
		/* We're the parent */
		int	retval,
			fpid;
		if( foreground )
		{
			fpid = wait(&retval);
			while( fpid != pid )
			{
				if( retval & 255 )
					fprintf(stdout,"p:%i s%i - %s",fpid, retval&255,sigmsg[retval&15]);
				fpid = wait(&retval);
			}
			fprintf(stdout,"%s",sigmsg[retval&15]);
		}
		return;
	}

	/* We're the child */

	/* Redirect standard streams */
	if( sin && ((nsin=open(sin,O_RDONLY))>0) )
	{
		close(0);
		dup2( nsin, 0 );
		close( nsin );
	}
	if( sout && ((nsout=creat(sout,0666))>0) )
	{
		close(1);
		dup2( nsout, 1 );
		close( nsout );
	}
	if( serr && ((nserr=creat(serr,0666))>0) )
	{
		close(2);
		dup2( nserr, 2 );
		close( nserr );
	}

	envs[0]=(char*)0;	/* No environment yet */

	i = 0;
	while( realword(i,words) && (i<31) )
	{
		args[i] = realword(i,words);
		i++;
	}
	args[i] = (char*)0;

	/* Enable ^C for shell commands */
	/* Those put in the background do not have this done */
	if( foreground )
		signal( SIGINT, SIG_DFL );

	execve( command, args, envs );

	fprintf(stderr,"execve of [%s] failed (%i)\n",command,ERRNO);
	_exit();
}

