#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>

/*
	SpenceShell by Phil "CrazySpence" Spencer
	
	I'm sick of going to my friends and my cousins house and using god dmaned windows
	so I'm making a floppy distro but first I'm making myself some tiny basic tools.
	Eventually this shell will have built in ls cd commands and maybe a small editor.

	History:
   V0.1 10/07/2000 - started skeleton code spsh.c
	10/08/2000 - Executes commands with paremeters
		   - I need to add ChangeDir to the shell some1 remind me...maybe ill do it before bed
	           - Shows Current working Directory
		   - dir.c contains ChangeDir and will eventually have other file/dir functions like rm and mv
	 	   - added a lil user greeting to the beginning..very pointless but im jus playing around
		     with some shit in this linux C programming book I have			
	10/10/2000 - I made the mainline more efficient with tokens to parse user input at the prompt
		     this will come in handy later when i make a few more built in commands
   V0.2 10/11/2000 - changed program execution code from system() to fork and execve() because system() cheats
		     and uses bash to load things anyways the new way makes it more independant :)
  V0.25 10/12/2000 - added stat() to check if the bin requested exists
		   - SpenceShell grabs the PATH enviroment and scans the directories in it for the bin	
		   - if / or . start a command it skips that PATH parsing BUT theres a neat lil bug i found but it only
	             happened when i tried to run ./squake with improper permissions
  V0.3 01/07/2001  - Discovered a bug where cd by itself made it segfault. fixed it with a simple if else statment
*/

extern int cd(char thedir[256]);
extern char **environ;

int main(int argc,char **argv)
{
	static char *my_argv[64];
	char cmdbuf[256];
	int my_argc;
	char buf[256];
	int dieshell = 0;
  	char *svptr;
	char *s;
	static const char delim[] = " \t\n";
	int i = 0;
	struct passwd *pwp;
	pid_t pid;
	pid_t wpid;
        int status;
        struct stat sbuf;
        static char *path_argv[64];
	static const char path_delim[] = " =:";
	int path_argc = 0;
	char cmd[256];

	if ( !(pwp = getpwuid(getuid())))     //get username of person running me
	{
		puts("What the hell...you don't exist"); //if this happens your not in /etc/passwd somehow
		abort();	
	} else {
		printf("Hello %s\n",pwp->pw_name);  //lame greeting i added jus so i could use the passwd struct
        	printf("-=Welcome to SpenceShell V0.3=-\n\n");
	}
	while(dieshell != 1) {
	        for(i = 0 ; i < 64 ; i++) {
			//wipe out my_argv before using it
			my_argv[1] = 0;
		}
		getcwd(buf,sizeof buf); //grab the current path
		printf("%s $: ",buf);
		fgets(cmdbuf,sizeof cmdbuf,stdin); //get input from the prompt
		s = strtok_r(cmdbuf,delim,&svptr);
		my_argc = 0;
		while(s != 0) {			
			my_argv[my_argc++] = s;  //split input into seperate parms
			s = strtok_r(NULL,delim,&svptr);
		}
		if (strstr(my_argv[0],"cd")) {
			if(my_argv[1] == 0)
				cd("~");
			else
				cd(my_argv[1]); //in cd's case parms is the pathname :)
		} else if (strstr(my_argv[0],"exit")) //escape code
			dieshell = 1;
		else {
			pid = fork(); //divide and conquer bwahahahahah!!!!!
			if( pid == -1) {
				fprintf(stderr,"%s: Failed to fork()\n",strerror(errno));
				return 1;
			} else if(pid == 0) {
				//I'm the Child
				strcpy(cmd,"");
				strcpy(cmd,my_argv[0]);
				if(cmd[0] == '.') {
					execve(cmd,my_argv,environ); //A . command gets passed directly to execve	
				        //If im here it broke
					fprintf(stderr,"%s:Error executing\n",strerror(errno));
					return 1;
				} else if(cmd[0] == '/') {
					execve(cmd,my_argv,environ); //A / command gets passed directly to execve	
				        //If im here it broke
					fprintf(stderr,"%s:Error executing\n",strerror(errno));
					return 1;	
				}	
				i = 0;
				while(!strstr(environ[i],"PATH=") && environ[i] != 0) {   //scanning for PATH ennviroment variables
					i++;
				}
				if(environ[i] == 0) {
					printf("Error: Mysteriously you have no PATH shell variables\n"); //somthings very wrong if this happens
	                        	return 1;
				}
				s = strtok_r(environ[i],path_delim,&svptr);
				while(s != 0){
					path_argv[path_argc++] = s;       //parsing PATH into path_argv array
					s = strtok_r(NULL,path_delim,&svptr);
				}
				path_argv[path_argc] = 0;
				i = 0;
				while(path_argv[i] != 0) {   //this while loop puts the PATH vars and the command together and tests them to see if they exist
					strcpy(cmd,"");
					strcpy(cmd,path_argv[i]);
					strcat(cmd,"/");
					strcat(cmd,my_argv[0]);
					if(stat(cmd,&sbuf) == 0)
						execve(cmd,my_argv,environ); //If it actually exists replace forked process with new bin	
					i++;
				}
				printf("%s: No such file\n",my_argv[0]); //No such file exit the child process
				return 1;
			} else {
				//I'm the parent
				wait(&status);
				if(wpid == -1) {
					fprintf(stderr,"%s: wait()\n",strerror(errno));	
					return 1;
				}		
			}
		}
	}
	printf("Thank You for using SpenceShell\nI don't know why your using me...but thanks. lol");
	printf("\n");
	sleep(1);
	return 0;
}

