#include <dos.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <libraries/dosextens.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "AXsh.h"
#include "internal.h"

#ifndef FIBF_HIDDEN
#define FIBB_HIDDEN	7        /* hidden from the directory [Not by C=] */
#define FIBF_HIDDEN	(1<<FIBB_HIDDEN)
#endif

extern struct User user;
extern char upload[];
extern char currentdir[];

extern int NumberofHDs,Numberoffiles;
extern long actualquota;
extern int Columns;
extern BOOL globallogin;

int  docommand(char **args,char *command,int argc,char **argv,BPTR inhandle,BPTR outhandle);

struct InternalNode Internals[]=
{
	{"alias",	&DOalias},
	{"cd",		&DOcd},
	{"echo",	&DOecho},
	{"execute",	&DOexecute},
	{"exit",	NULL},			/* just a dummy-command for which */
	{"if",		&DOif},
	{"internal",&DOinternal},
	{"kick",	&DOkick},
	{"login",	NULL},			/* just a dummy-command for which */
	{"protect",	&DOprotect},
	{"quota",	&DOquota},
	{"quit",	&DOquit},
	{"set",		&DOset},
	{"sysinfo",	&DOsysinfo},
	{"unalias",	&DOunalias},
	{"unset",	&DOunset},
	{"uptime",	&DOuptime},
	{"wait",	&DOwait},
	{"which",	&DOwhich},
	{"who",		&DOwho},
	{"whoami",	&DOwhoami},
	{"write",	&DOwrite},
	{NULL,NULL}
};


int DOalias(int argc,char *argv[],char *argstr,BPTR output)
{
	switch(argc)
	{
	case 1:
		alias(NULL,NULL,ALIAS_ADD,0);
		break;
	case 2:
		if(!strcmp(argv[1],"clear"))
			alias(NULL,NULL,ALIAS_FREE,0);
		else
			alias(argv[1],NULL,ALIAS_ADD,0);
		break;
	case 3:
		alias(argv[1],argv[2],ALIAS_ADD,0);
		break;
	default:
		return 10;
	}
	return 0;
}

int DOcd(int argc,char *argv[],char *argstr,BPTR output)
{	char temp[ARGLEN];
	int i;
	long mylock;
	struct	FileInfoBlock *fib;

	/* cd is better be configured with f-flag, so it won't traverse twice */
	if(argc==2)
	{
		strcpy(temp,argv[1]);
		i=strlen(temp)-1;
		if(i>=0 && temp[i]!='/' && temp[i]!=':')
			strcat(temp,"/");

		if(argv[1][0])
		{
			if(verbosetraverse(temp))
			{
				return 20;
			}
		}
	}
	else
	{
		strcpy(temp,user.Home);
	}

	if(!(mylock=Lock(temp,ACCESS_READ)))	/* mylock is a local variable here */
	{
		printf(printout(MSG_NO_SUCH_FILE_OR_DIR),temp);
		return 10;
	}
	if(fib=(struct FileInfoBlock *)AllocMem(sizeof(*fib),MEMF_PUBLIC | MEMF_CLEAR))
	{
		if(Examine(mylock,fib) && (fib->fib_DirEntryType>0))
		{
			FreeMem(fib,sizeof(*fib));
		}
		else	/* if it is not a directory... */
		{
			printf(printout(MSG_NO_SUCH_DIR),temp);
			UnLock(mylock);
			FreeMem(fib,sizeof(*fib));
			return 10;
		}
	}
	else
	{
		UnLock(mylock);
		printf(printout(MSG_MEM_ALLOC_ERROR));
		return 42;
	}

	realpath(currentdir,temp);
	/*strcpy(currentdir,temp);*/
	UnLock(CurrentDir(mylock));
	who(COM_SET,0,"cwd",currentdir);	/* set current working directory */
	return 0;
}

int DOecho(int argc, char *argv[],char *argstr,BPTR output)
{	int i,start=1,stop=argc,noline=0;

	if(argc>start && !strcmp(argv[start],"-n"))
	{
		noline=1;
		++start;
	}
	else
	if(argc && !strcmp(argv[argc-1],"noline"))
	{
		noline=1;
		--stop;
	}

	if(start<stop)
	{
		Write(output,argv[start],strlen(argv[start]));	/*printf("%s",argv[start]);*/
		for(i=start+1;i<stop;i++)
		{
			Write(output," ",1);
			Write(output,argv[i],strlen(argv[i]));		/*printf(" %s",argv[i]);*/
		}
	}
	if(!noline)
		Write(output,"\n",1);	/*printf("\n");*/

	return 0;
}


int DOexecute(int argc,char *argv[],char *argstr,BPTR output)
{	FILE *fp;
	int i,j,linecount,error=0;
	char temp[ARGLEN],apu[ARGLEN],**args;
	static int recursion=0;

	if(argc>1)
	{
		if(fp=fopen(argv[1],"r"))
		{
			if(!(args=(char **)AllocMem((ARGMAX+1)*sizeof(char *),MEMF_CLEAR)))
			{
				printf(printout(MSG_MEM_ALLOC_ERROR));
				fclose(fp);
				return 42;
			}

			if(!(*args=(char *)AllocMem(6*ARGLEN+ARGBUF,MEMF_CLEAR)))
			{
				FreeMem(args,(ARGMAX+1)*sizeof(char *));
				printf(printout(MSG_MEM_ALLOC_ERROR));
				fclose(fp);
				return 42;
			}

			linecount=0;
			while(!feof(fp))
			{
				linecount++;
				if(!globallogin && (SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C))
				{
					printf("^C");
					error= 20;
					goto exitexecute;
				}
				if(fgets(temp,ARGLEN-2,fp)==NULL)
					break;
				if(strlen(temp)>0)
					temp[strlen(temp)-1]='\0';

				if(temp[0]=='#')
					continue;

				i=0;
				while(temp[i]!=';' && temp[i])
				{
					if(temp[i]=='"')	/* don't touch anything between "-marks */
					{
						i++;
						while(temp[i]!='"' && temp[i])	i++;
					}
					i++;
				}
				temp[i]='\0';	/* discard ';' (comments) */

				i=0;
				while(temp[i]!=' ' && temp[i])	i++;	/* discard 'data: ' (labels) */
				if(i && temp[i-1]==':')
					strcpy(temp,temp+i+1);

				i=0;
				while(temp[i] && (temp[i]==' ' || temp[i]=='\t'))
					i++;

				if(!strncmp(temp+i,"goto",4))
				{
					sprintf(apu,"%s:",temp+i+5);
					fseek(fp,0L,0);	/* goto the start of file */
					linecount=0;
					while(!feof(fp))
					{
						linecount++;
						if(fgets(temp,ARGLEN-2,fp)==NULL)
							break;
						if(strlen(temp)>0)
							temp[strlen(temp)-1]='\0';
						i=0;
						while(temp[i] && (temp[i]==' ' || temp[i]=='\t'))
							i++;
						if(!strncmp(temp+i,apu,strlen(apu)))
							break;
					}
					if(feof(fp))
					{
						printf(printout(MSG_NO_SUCH_LABEL),apu);
						error= 11;
						goto exitexecute;
					}
					strcpy(temp,temp+strlen(apu));	/* forget the label */
					i=0;
					while(temp[i]!=';' && temp[i])
					{
						if(temp[i]=='"')	/* don't touch anything between "-marks */
						{
							i++;
							while(temp[i]!='"' && temp[i])
								i++;
						}
						i++;
					}
					temp[i]='\0';	/* discard comments */
					i=0;
					while(temp[i] && (temp[i]==' ' || temp[i]=='\t'))
						i++;
				}
				if(!strcmp(temp+i,"else"))
				{
					j=1;
					while(!feof(fp))
					{
						linecount++;
						if(fgets(temp,ARGLEN-2,fp)==NULL)
							break;
						i=0;
						while(temp[i] && (temp[i]==' ' || temp[i]=='\t'))
							i++;
						if(!strncmp(temp+i,"if",2))		j++;
						if(!strncmp(temp+i,"endif",5))	j--;
						if(j==0)	break;
					}
					if(feof(fp))
					{
						printf(printout(MSG_ENDIF_MISSING));
						error= 12;
						goto exitexecute;
					}
				}
				if(!strcmp(temp+i,"quit"))	break;	/* exit from script */
				if(temp[i] && strncmp(temp+i,"endif",5))
				{
					if(recursion>8)
					{
						printf(printout(MSG_RECURSION_DEPTH_REACHED));
						error= 20;
						goto exitexecute;
					}

					while(temp[0])
					{
						++recursion;
						i=docommand(args,temp,argc,argv,Input(),output);
						--recursion;

						if(i==-2 || i==-3)
						{
							error= i;
							goto exitexecute;
						}
						if(i>5)
						{
							printf(printout(MSG_ERROR_IN_LINE),linecount,argv[1]);
							error=i;
							break;	/* if error, stop */
						}
						if(i==-10)	/* if-construct jump */
						{
							j=1;
							while(!feof(fp))
							{
								linecount++;
								if(fgets(temp,ARGLEN-2,fp)==NULL)
									break;
								i=0;
								while(temp[i] && (temp[i]==' ' || temp[i]=='\t'))
									i++;
								if(!strncmp(temp+i,"else",4) && j==1)
									break;
								if(!strncmp(temp+i,"if",2))
									j++;
								if(!strncmp(temp+i,"endif",5))
									j--;
								if(j==0)	break;
							}
							if(feof(fp))
							{
								printf(printout(MSG_ENDIF_MISSING));
								error= 12;
								goto exitexecute;
							}
							temp[0]='\0';
						}
					}
				}
			}
exitexecute:
			fclose(fp);

			if(*args)	FreeMem(*args,6*ARGLEN+ARGBUF);
			if(args)	FreeMem(args,(ARGMAX+1)*sizeof(char *));
			return error;
		}
		else
		{
			printf(printout(MSG_NOT_FOUND),argv[1]);
			return 10;
		}
	}
	else
	{
		printf(printout(MSG_EXECUTE_USAGE));
		return 20;
	}
}


int DOif(int argc,char *argv[],char *argstr,BPTR output)
{	long lock;

	switch(argc)
	{
		case 1:
			return -10;
			break;
		case 2:
			if(strlen(argv[1]))
				return 0;
			else
				return -10;
			break;
		case 3:
			if(!strcmp(argv[1],"exists"))
			{
				if(lock=Lock(argv[2],ACCESS_READ))
				{
					UnLock(lock);
					return 0;
				}
				else
					return -10;
			}
			else
				return 20;
			break;
		case 4:
			if(!strcmp(argv[2],"=="))
			{
				if(!strcmp(argv[1],argv[3]))
					return 0;
				else
					return -10;
			}
			else
			if(!strcmp(argv[2],"!="))
			{
				if(!strcmp(argv[1],argv[3]))
					return -10;
				else
					return 0;
			}
			else
				return 20;
			break;
		default:
			return -10;
	}
}


int DOinternal(int argc,char *argv[],char *argstr,BPTR output)
{	int i=0,j=0;

	printf(printout(MSG_INTERNAL_LIST));
	while(Internals[i].name)
	{
		printf("%-12s",Internals[i].name);
		if(++j>Columns/12-1)
		{
			printf("\n");
			j=0;
		}
		i++;
	}
	if(j)
		printf("\n");
	return 0;
}


int DOprotect(int argc,char *argv[],char *argstr,BPTR output)
{	ULONG protection,bit,ormask=0,andmask=0xffffffff,xormask=0;
	struct FileInfoBlock *fib;
	long lock;
	char *a;
	int mode='+',group=0,error=0,set=0,addmode=0;

	if(argc<3)
	{
		printf(printout(MSG_PROTECT_USAGE));
		return 5;
	}
	else
	{
		a=argv[1];
		while(*a && !error)
		{
			switch(*a)
			{
			case 'u':
				group=addmode*group | 0x0001;
				addmode=1;
				break;
			case 'g':
				group=addmode*group | 0x0100;
				addmode=1;
				break;
			case 'o':
				group=addmode*group | 0x1000;
				addmode=1;
				break;
			case '+':
			case '-':
			case '=':
				mode=*a;
				break;
			case 'h':
				bit=FIBF_HIDDEN;
				group=0x0001;
				set=-1;
				break;
			case 's':
				bit=FIBF_SCRIPT;
				group=0x0001;
				set=-1;
				break;
			case 'p':
				bit=FIBF_PURE;
				group=0x0001;
				set=-1;
				break;
			case 'a':
				bit=FIBF_ARCHIVE;
				group=0x0001;
				set=-1;
				break;
			case 'r':
				bit=FIBF_READ;
				set=-1;
				break;
			case 'w':
				bit=FIBF_WRITE;
				set=-1;
				break;
			case 'e':
				bit=FIBF_EXECUTE;
				set=-1;
				break;
			case 'd':
				set=-1;
				bit=FIBF_DELETE;
				break;
			default:
				error=5;
				break;
			}
			if(*a!='u' && *a!='g' && *a!='o')
				addmode=0;

			a++;
			if(!error && set)
			{
				if(!group)
					group=0x0001;

				if(mode=='+')
				{
					ormask|= (bit*group);
				}
				else
				if(mode=='-')
				{
					andmask&=~(bit*group);
				}
				else
				if(mode=='=')
				{
					xormask= (bit*group);
				}
				set=0;
			}
		}

		if(error)
		{
			printf(printout(MSG_PROTECT_USAGE));
			return error;
		}

		if(fib=(struct FileInfoBlock *)AllocMem(sizeof(*fib),MEMF_PUBLIC | MEMF_CLEAR))
		{
			for(set=2;set<argc;set++)
			{
				if((lock=Lock(argv[set],ACCESS_READ)) && Examine(lock,fib))
				{
					/* invert those 4 bits so that things are not so complicated */
					protection=fib->fib_Protection ^ 0x000f;
					UnLock(lock);

					protection= ((protection^xormask)|ormask)&andmask;

					/* invert those 4 bits back to normal */
					protection^=0x000f;
					SetProtection(argv[set],protection);
				}
				else
				{
					printf(printout(MSG_CANNOT_LOCK_OR_EXAMINE),argv[set]);
					error= 10;
				}
			}
			FreeMem(fib,sizeof(*fib));
		}
		else
		{
			printf(printout(MSG_MEM_ALLOC_ERROR));
			error= 20;
		}
	}
	return error;
}

int DOquota(int argc,char *argv[],char *argstr,BPTR output)
{
	printf(printout(MSG_DISK_QUOTAS_FOR),user.Loginname,user.Fullname);
	Numberoffiles=0;
	NumberofHDs=0;

	printf(printout(MSG_QUOTA_LIST),user.Home,Numberoffiles,actualquota=checkquota(user.Home)/2,user.Quota);

	if(actualquota>user.Quota && user.Quota)	return 5;
	return 0;
}

int DOquit(int argc,char *argv[],char *argstr,BPTR output)
{
	return -20;
}

int DOsysinfo(int argc,char *argv[],char *argstr,BPTR output)
{
	sysinfo();
	return 0;
}


int DOunalias(int argc,char *argv[],char *argstr,BPTR output)
{	int i;

	for(i=1;i<argc;i++)
	{
		alias(argv[i],NULL,ALIAS_REMOVE,0);
	}
	if(argc>1)
		return 0;
	else
		return 5;
}


int DOuptime(int argc,char *argv[],char *argstr,BPTR output)
{	int size=2048,i=0;
	char *buffer;

	if(buffer=(char *)AllocMem(size,MEMF_PUBLIC | MEMF_CLEAR))
	{
		who(COM_WHO,0,buffer,(char *)size);
		while(buffer[i] && buffer[i]!='\n')
			i++;
		Write(output,buffer,i+1);
		FreeMem(buffer,size);
		return 0;
	}
	else
		return 42;
}


int DOwait(int argc,char *argv[],char *argstr,BPTR output)
{	int i;

	if(argc==2 && (i=atoi(argv[1]))<240 && i>0)
		WaitForChar(Input(),1000000*i);
	else
		printf(printout(MSG_WAIT_USAGE));
	return 0;
}


int DOwhich(int argc,char *argv[],char *argstr,BPTR output)
{	int j;
	extern struct List aliaslist;
	struct AliasNode *node;

	if(node=(struct AliasNode *)FindName(&aliaslist,argstr))
	{
		printf(printout(MSG_AXSH_ALIAS));
	}
	j=0;
	while(Internals[j].name)
	{
		if(!strcmp(Internals[j].name,argstr))
		{
			printf(printout(MSG_AXSH_INTERNAL));
		}
		j++;
	}
	MyRunCommand("c:which",argstr,0,output,-1);
	return 0;
}

int DOwho(int argc,char *argv[],char *argstr,BPTR output)
{	int size=2048;
	char *buffer;

	if(buffer=(char *)AllocMem(size,MEMF_PUBLIC | MEMF_CLEAR))
	{
		who(COM_WHO,0,buffer,(char *)size);
		Write(output,buffer,strlen(buffer));
		FreeMem(buffer,size);
		return 0;
	}
	else
		return 42;
}


int DOwhoami(int argc,char *argv[],char *argstr,BPTR output)
{
	printf("Loginname: %s\n",user.Loginname);
	return 0;
}


int DOwrite(int argc,char *argv[],char *argstr,BPTR output)
{	int i;

	if(argc>1)
	{
		if(argc==2)	i=who(COM_WRITE,0,argv[1],NULL);
		else		i=who(COM_WRITE,0,argv[1],argstr+strlen(argv[1])+1);
		if(i==1)
			printf(printout(MSG_COULD_NOT_ACCESS_TTY));
		if(i==2)
			printf(printout(MSG_USER_NOT_LOGGED_IN));
		if(i)		return 5;
	}
	else
	{	printf(printout(MSG_WRITE_USAGE));
		return 10;
	}
	return 0;
}

int DOkick(int argc,char *argv[],char *argstr,BPTR output)
{	int i;

	if(argc==2)
	{
		i = who(COM_KICK,0,argv[1],NULL);
		if(i==1)
			printf(printout(MSG_COULD_NOT_ACCESS_TTY));
		if(i==2)
			printf(printout(MSG_USER_NOT_LOGGED_IN));
		if(i)		return 5;
	}
	else
	{
/* TODO: usage */
		printf("Usage: kick <user>\n");
		return 10;
	}
	return 0;
}
