PASH – A Simple Unix SHell

What is it?

Back in late 2004, I decided to finally write my own Unix SHell from scratch in the C Programming Language. One of the professors that taught Operating Systems at Polytechnic University back when I was an undergrad, actually made his class write their own SHells as one of the projects for the course. Unfortunately, he stopped teach that course when it was my turn to take Operation Systems, so I never got to build one while I was in school.

So, one day while I was working at Lehman Brothers, I decided it was time I finally wrote my own. I was more than 3 years out of college at that time, and working full time, I gave up working on my own commercial projects to focus on some fun development like implementing my own Huffman Compression and Decompression utilities, as well as the PASH SHell.

So what does PASH stand for? – It’s named after my wife: Paula Anglo SHell. Yes, I know, very romantic… evilgrin

Purpose?

Just to learn how a Unix SHell works. I wanted to handle multiple pipes correctly, redirects, etc. It’s not a complete shell, but it works ok for the purpose of learning and experimenting with low level Unix system calls. By writing this shell, I learned all about fork(), exec(), dup2(), pipe() / pipeline, and other Unix system calls, and more importantly, how to use them correctly to create a SHell process that can sit on top of a Unix Operating System and allow the user to execute commands.

Where can I get a copy of the source code?

The entire PASH source code is made available on my web site as a TAR file: http://www.roguelogic.com/pash/pash.tar

You can also browse the source code in the directory: http://www.roguelogic.com/pash/src/

How do I build the binary?

I have never been good at making Makefiles, so there’s just a simple Shell Script.

Simply download and untar pash.tar then run the build.sh shell script. It will build the executable “pash

Here’s a screen capture:

pash

A link back to the original RogueLogic.com page for PASH: http://www.roguelogic.com/pash/index.htm

Just for your viewing (and searching) pleasure, I concatenated all the separate source files into a single Text File. Enjoy! evilgrin

Some other nice things about this code, is that I tried to make it as self contained as possible, therefore it includes my own Linked List, Stack, Queue, String Buffer implementations in C. So I expect that this code will be useful for students of the C language in general, not just for students of Operating Systems or Unix SHells…

I would like to hear back from professors, students, and in general anyone else who is a programmer or interested in programming on this SHell Implementation. I know it’s not complete, it lacks support for it’s own Shell Scripting language, etc, but it does support multiple pipes, redirects, etc, and it a very good educational tool, at least in my opinion. Please feel free to contact me or leave me comments on this post!

//=================================================================>

//pash.c

/*
        Author: Robert C. Ilardi
        Date: 11/5/2004
        Description: The Paula Anglo SHell (PASH) Main Driver Program Implementation
*/

#include "pash.h"

int main(int argc, char *argv[], char *envp[])
{
	int exitCode=1;

	//Set Debug Mode to ON for development
	pashSetDebugMode(false);

	//Install Signal Handlers
	installSignalHandlers();

	//Set Prompt? Not for now, just a comment placeholder

	//Install User Interface Functions in Control Loop
	installUserInterfaceGet(getCmdLine);
	installUserInterfacePrompter(printPrompt);

	//Start Processing User Input until User Exits
	exitCode=runShell();

	return exitCode;
}

//=================================================================>

//pash.h

/*
        Author: Robert C. Ilardi
        Date: 11/5/2004
        Description: PASH Shell Main Driver Program Header
*/

#ifndef PASH_H
#define PASH_H

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include "debugMod.h"
#include "shui.h"
#include "sighandlers.h"

#endif

//=================================================================>

//pash_consts.h

/*
	Author: Robert C. Ilardi
	Date: 11/15/2004
	Description: Contains some Pash Constants
*/

#ifndef PASH_CONSTS_H
#define PASH_CONSTS_H

static const char* PASH_VERSION="0.9";
static const char* PASH_TITLE="The Paula Anglo SHell (PASH)";
static const char* PASH_AUTHOR="Robert C. Ilardi";
static const char* PASH_COPYRIGHT_DATE="2004";
static const char* PASH_COPYRIGHT_HOLDER="Robert C. Ilardi";
static const char* PASH_URL="http://roguelogic.com:3979/";

#endif

//=================================================================>

//shell.c

/*
        Author: Robert C. Ilardi
        Date: 11/15/2004
        Description: PASH Unix Shell Implementation
*/

#include "shell.h"

const int INTERNAL_CMD_SUCCESS=0;
const int INTERNAL_CMD_FAILURE=1;
const int INTERNAL_CMD_UNKNOWN=2;

static void debugCmdStack(struct Stack*);
static void debugCmdLine(struct CmdLine*);
static char** paramListToArray(struct CmdLine*);
static void doRedirects(struct CmdLine*);
static bool createNeededPipe(struct CmdLine*);
static void doPipes(struct CmdLine*);
static void shellWaitAll();
static void closeParentPipes();
static void updatePipeIndexes(struct CmdLine*);
static void clearCmdStack(struct Stack*);

bool _shellNoExec=false;

int* pipePairs;
pid_t* pidList;
int procIndex;
int pidCnt;
int pipeIndex;
int nextOut;
int nextIn;
int prevOut;
int prevIn;
int futureOut;

void executeCmdStack(struct Stack* cmdStack)
{
	struct CmdLine* cmdLine;
	bool execOk;
	struct ListNode* param;

	prevIn=nextIn=0;
	futureOut=prevOut=nextOut=1;
	procIndex=0;
	pipeIndex=0;
	pidList=NULL;
	pipePairs=NULL;
	pidCnt=stackSize(cmdStack);
	if (pidCnt>0)
	{
		pidList=(pid_t*)malloc(sizeof(pid_t)*pidCnt);
		pipePairs=(int*)malloc(sizeof(int)*pidCnt*2);

	        while (!stackIsEmpty(cmdStack))
        	{
                	cmdLine=(struct CmdLine*)stackPop(cmdStack);
			execOk=executeCmd(cmdLine);

			if (!execOk)
			{
				fprintf(stderr, "Command Execution Aborted!\n");
				stackPush(cmdStack, cmdLine); //So clearCmdStack takes care of everything
				break;
			}
		}

		clearCmdStack(cmdStack);

		closeParentPipes();

		if (execOk)
		{
			shellWaitAll();
		}

		if (pipePairs!=NULL)
		{
			free(pipePairs);
		}

		if (pidList!=NULL)
		{
			free(pidList);
		}
	}
}

static void clearCmdStack(struct Stack* cmdStack)
{
	struct CmdLine* cmdLine;

	while (!stackIsEmpty(cmdStack))
        {
        	cmdLine=(struct CmdLine*)stackPop(cmdStack);
		listDestroy(cmdLine->parameters, true);
		free(cmdLine->inFile);
	        free(cmdLine->outFile);
        	free(cmdLine->errFile);
	        free(cmdLine);
	}
}

char** paramListToArray(struct CmdLine* cmdLine)
{
	char** paramArr=NULL;
	struct ListNode* cur;
	int cnt, len;
	struct LinkedList* params=cmdLine->parameters;

	len=listSize(params);

	if (len>0)
	{
		paramArr=(char**)malloc(sizeof(char*)*(len+2));
		paramArr[0]=cmdLine->command; //By Convention
		paramArr[len]=NULL; //NULL Terminator

		cnt=1;
		cur=params->head;
		while(cur!=NULL)
		{
			paramArr[cnt++]=(char*)cur->item;
			cur=cur->next;
		}
	}

	return paramArr;
}

static void shellWaitAll()
{
	int i, status, chdStatus;

	for (i=procIndex-1; i>=0; i--)
	{
 		if (waitpid(pidList[i], &status, 0) < 0)
		{
			fprintf(stderr, "waitpid() failed\n");
			break;
                }
		else if (WIFEXITED(status))
                {
                	chdStatus = WEXITSTATUS(status);
                        //printf("Child Process (PID=%d) has exited with Status=%d.\n", pidList[i], chdStatus);
		}
	}
}

static void closeParentPipes()
{
	int i;

	for (i=0; i<pipeIndex; i++)
	{
		if (pipePairs[i]!=-1)
		{
			close(pipePairs[i]);
		}
	}
}

bool executeCmd(struct CmdLine* cmdLine)
{
	pid_t pid;
	int status;
	bool retVal;

	//Debug Cmd Line Object
	if (pashDebugMode())
	{
		debugCmdLine(cmdLine);
	}

	if (checkBuiltIn(cmdLine)==INTERNAL_CMD_UNKNOWN && !_shellNoExec)
	{
		//Execute External Program as Child Process
		retVal=createNeededPipe(cmdLine); //Create Pipe as needed

		if (retVal)
		{
			pid=fork(); //Fork Child

			if (pid)
			{
				//Parent
				pidList[procIndex]=pid;
				procIndex++;
				updatePipeIndexes(cmdLine);
				retVal=true;
			}
			else if (pid==-1)
			{
				fprintf(stderr, "Child fork() failed!");
				retVal=false;
			}
			else
			{
				//Child
				//printf("Executing Child Process: %s\n", cmdLine->command);
				doRedirects(cmdLine);
				doPipes(cmdLine);
				execvp (cmdLine->command, paramListToArray(cmdLine));
				perror(NULL);
				fprintf(stderr, "Child Process (CMD='%s') Execution Failed!\n", cmdLine->command);
				_exit(1);
			}
		}
	}
	else
	{
		retVal=true;
	}

	listDestroy(cmdLine->parameters, true);
	free(cmdLine->inFile);
	free(cmdLine->outFile);
	free(cmdLine->errFile);
	free(cmdLine);

	return retVal;
}

int checkBuiltIn(struct CmdLine* cmdLine)
{
	int status;

	if (builtInSupport(cmdLine))
	{
		status=(builtInExecute(cmdLine) ? INTERNAL_CMD_SUCCESS : INTERNAL_CMD_FAILURE);
	}
	else
	{
		status=INTERNAL_CMD_UNKNOWN;
	}

	return status;
}

static void doRedirects(struct CmdLine* cmdLine)
{
	FILE *myStdIn, *myStdOut, *myStdErr;

	if (cmdLine->inFile!=NULL)
	{
		myStdIn=fopen(cmdLine->inFile, "r");
		if (dup2(fileno(myStdIn), fileno(stdin))<0)
		{
			perror(NULL);
			_exit(1);
		}
	}

        if (cmdLine->outFile!=NULL)
        {
                myStdOut=fopen(cmdLine->outFile, (cmdLine->appendOut ? "a" : "w"));
                if (dup2(fileno(myStdOut), fileno(stdout))<0)
		{
			perror(NULL);
                        _exit(1);
		}
        }

        if (cmdLine->errFile!=NULL)
        {
                myStdErr=fopen(cmdLine->errFile, "w");
                if (dup2(fileno(myStdErr), fileno(stderr))<0)
		{
			perror(NULL);
                        _exit(1);
		}
        }
}

static bool createNeededPipe(struct CmdLine* cmdLine)
{
	int pipePair[2];
	bool retVal;

	pipePairs[pipeIndex]=-1;
	pipePairs[pipeIndex+1]=-1;

	if (cmdLine->pipedIn)
	{
		if (pipe(pipePair)!=0)
                {
                        fprintf(stderr, "Could NOT create PIPE!\n");
			perror(NULL);
			retVal=false;
                }
		else
		{
			pipePairs[pipeIndex]=pipePair[0];
			pipePairs[++pipeIndex]=pipePair[1];
			pipeIndex++;
			retVal=true;
		}
	}
	else
	{
		retVal=true;
	}

	return retVal;
}

static void updatePipeIndexes(struct CmdLine* cmdLine)
{
        if (cmdLine->pipedIn)
        {
                prevIn=nextIn;
                nextIn+=2;
		prevOut=futureOut;
                futureOut+=2;
        }

        if (cmdLine->pipeOut)
        {
                nextOut+=2;
        }
}

static void doPipes(struct CmdLine* cmdLine)
{
        if (cmdLine->pipedIn && !cmdLine->pipeOut)
        {
                //Single Pipe In
		if (pashDebugMode())
		{
                	printf("%s is using pfd(nextIn = %d): %d\n", cmdLine->command, nextIn, pipePairs[nextIn]);
		}
                if (dup2(pipePairs[nextIn], fileno(stdin))<0)
                {
                        fprintf(stderr, "Could NOT dup2 pipe on stdin!\n");
			perror(NULL);
                        _exit(1);
                }
                close(pipePairs[nextOut]);
        }
        else if (cmdLine->pipedIn && cmdLine->pipeOut)
        {
                //Pipe In and Pipe Out
		if (pashDebugMode())
		{
                	printf("%s is using pfd(nextIn = %d): %d\n", cmdLine->command, nextIn, pipePairs[nextIn]);
		}
                if (dup2(pipePairs[nextIn], fileno(stdin))<0)
                {
                        fprintf(stderr, "Could NOT dup2 pipe on stdin!\n");
			perror(NULL);
                        _exit(1);
                }
		close(pipePairs[futureOut]);

		if (pashDebugMode())
		{
                	printf("%s is using pfd(prevOut = %d): %d\n", cmdLine->command, prevOut, pipePairs[prevOut]);
		}
                if (dup2(pipePairs[prevOut], fileno(stdout))<0)
                {
                        fprintf(stderr, "Could NOT dup2 pipe on stdout!\n");
			perror(NULL);
                        _exit(1);
                }
                close(pipePairs[prevIn]);
        }
        else if (cmdLine->pipeOut)
        {
                //Single Pipe Out
		if (pashDebugMode())
		{
                	printf("%s is using pfd(nextOut = %d): %d\n", cmdLine->command, nextOut, pipePairs[nextOut]);
		}
                if (dup2(pipePairs[nextOut], fileno(stdout))<0)
                {
                        fprintf(stderr, "Could NOT dup2 pipe on stdout!\n");
			perror(NULL);
                        _exit(1);
                }
                close(pipePairs[nextIn]);
        }
}

static void debugCmdLine(struct CmdLine* cmdLine)
{
	struct ListNode* param;

	printf("Command: %s\n", cmdLine->command);

	printf("Parameters: ");
	param=cmdLine->parameters->head;
	while (param!=NULL)
	{
		printf("'%s' ", (char *)param->item);
		param=param->next;
	}
	printf("\n");

	printf("OutFile(%s): %s\n",
	       (cmdLine->appendOut ? "Append" : "Overwrite"),
	       cmdLine->outFile);
	printf("InFile: %s\n", cmdLine->inFile);
	printf("ErrFile: %s\n", cmdLine->errFile);

	printf("Piped In: %s\n", (cmdLine->pipedIn ? "YES" : "NO"));
	printf("Pipe Out: %s\n", (cmdLine->pipeOut ? "YES" : "NO"));
	printf("Background Process: %s\n", (cmdLine->backgroundProcess ? "YES" : "NO"));
}

void setShellNoExec(bool noExec)
{
	_shellNoExec=noExec;
}

//=================================================================>

//shell.h

/*
        Author: Robert C. Ilardi
        Date: 11/15/2004
        Description: PASH Unix Shell Header
*/

#ifndef PASH_SHELL_H
#define PASH_SHELL_H

#include "linkedlist.h"
#include "stack.h"
#include "cmdline.h"
#include "bool.h"
#include "builtin.h"
#include "debugMod.h"

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>

void executeCmdStack(struct Stack*);
int executeCmd(struct CmdLine*);
void setShellNoExec(bool);

#endif

//=================================================================>

//shui.c

/*
        Author: Robert C. Ilardi
        Date: 11/7/2004
        Description: Shell Command Line User Interface Implementation
*/

#include "shui.h"

char* _prompt;

void setPrompt(char* prompt)
{
	_prompt=prompt;
}

char* getCmdLine()
{
	char* cmdLine=(char *)malloc(CMD_LINE_LEN);
	int i;

	//Init cmdLine to all NULLS
	for (i=0; i<CMD_LINE_LEN; i++)
	{
		cmdLine[i]='';
	}

	printPrompt(NULL, NULL);

	fgets(cmdLine, CMD_LINE_LEN, stdin);

	for (i=CMD_LINE_LEN-1; i>=0; i--)
	{
		if (cmdLine[i]=='\n')
		{
			cmdLine[i]='';
			break;
		}
	}

	return cmdLine;
}

void printPrompt(char* prefix, char* suffix)
{
	if (prefix!=NULL)
	{
		write(fileno(stdout), prefix, strlen(prefix));
	}

	if (_prompt != NULL)
        {
                write(fileno(stdout), _prompt, strlen(_prompt));
        }
        else
        {
                write(fileno(stdout), DEFAULT_PROMPT, strlen(DEFAULT_PROMPT));
        }

	if (suffix!=NULL)
	{
		write(fileno(stdout), suffix, strlen(suffix));
	}

	fflush(stdout);
}

//=================================================================>

//shui.h

/*
        Author: Robert C. Ilardi
        Date: 11/7/2004
        Description: Shell Command Line User Interface Header
*/

#ifndef PASH_SHUI_H
#define PASH_SHUI_H

#include <stdio.h>
#include <stdlib.h>

static const int CMD_LINE_LEN=2048;
static const char* DEFAULT_PROMPT="PASH> ";

void setPrompt(char* prompt);
char* getCmdLine();
void printPrompt(char* prefix, char* suffix);

#endif

//=================================================================>

//sighandlers.c

/*
        Author: Robert C. Ilardi
        Date: 11/5/2004
        Description: Signal Handlers Implementation
*/

#include "sighandlers.h"

void installSignalHandlers()
{
	signal(SIGINT, sigIntHandler);
}

void sigIntHandler(int sig)
{
	if (pashDebugMode())
	{
		printf("\nSIGNAL(%d) : I Got YOU Baby...\n", sig);
	}

	if (sig==SIGINT)
	{
		controlPrintPrompt(NULL, NULL);
	}
}

//=================================================================>

//sighandlers.h

/*
        Author: Robert C. Ilardi
        Date: 11/5/2004
        Description: Signal Handlers Header
*/

#ifndef PASH_SIGHANDLERS_H
#define PASH_SIGHANDLERS_H

#include <signal.h>
#include <unistd.h>
#include <stdio.h>

#include "control.h"

void installSignalHandlers();
void sigIntHandler(int);

#endif

//=================================================================>

//bool.h

/*
	Author: Robert C. Ilardi
        Date: 10/27/2004
	Description: This header file defines the type bool.
*/

#ifndef BOOL_H
#define BOOL_H
	#ifndef __cplusplus
		#ifdef CURSES_LOC
			#include CURSES_LOC
		#else
			#ifndef bool
				#define bool int
			#endif
		#endif
		#ifndef true
			#define true 1
			#define false 0
		#endif
	#endif
#endif

//=================================================================>

//builtin.c

/*
        Author: Robert C. Ilardi
        Date: 11/15/2004
        Description: Shell Built In Commands Implementation
*/

#include "builtin.h"
#include "pash.h"

const int BUILT_IN_CMD_CNT=4;
const char *BUILT_IN_CMDS[]={"EXIT", "CD", "DEBUGME", "VER"};

bool builtInSupport(struct CmdLine* cmdLine)
{
	int i;
	bool supported=false;

	for (i=0; !supported && i<BUILT_IN_CMD_CNT; i++)
	{
		supported=(strcasecmp(cmdLine->command, BUILT_IN_CMDS[i])==0);
	}

	return supported;
}

bool builtInExecute(struct CmdLine* cmdLine)
{
	bool success=false;
	char* param1;
	bool tmpB;

	if (strcasecmp(cmdLine->command, "EXIT")==0)
	{
		//The Shell Exit Command
		exit(0);
	}
	else if (strcasecmp(cmdLine->command, "CD")==0)
	{
		if (listSize(cmdLine->parameters)>0)
		{
			param1=(char*)cmdLine->parameters->head->item;
			chdir(param1);
			success=true;
		}
		else
		{
			fprintf(stderr, "Cannot Change Directory to NOTHING! CD Syntax: cd [DIR]\n");
		}
	}
	else if (strcasecmp(cmdLine->command, "DEBUGME")==0)
	{
		pashSetDebugMode(!pashDebugMode());
		if (listSize(cmdLine->parameters)>0)
                {
                        param1=(char*)cmdLine->parameters->head->item;
			if (strcasecmp(param1, "NOEXEC")==0)
			{
				setShellNoExec(true);
			}
		}
		printf("Toggle PASH Shell Debug Mode: %s\n", (pashDebugMode() ? "ON" : "OFF"));
		if (!pashDebugMode())
		{
			setShellNoExec(false);
		}
		success=true;
	}
        else if (strcasecmp(cmdLine->command, "VER")==0)
        {
                //The Shell Version Command
		printf("** %s **\n", PASH_TITLE);
		printf("Written By: %s\n", PASH_AUTHOR);
		printf("Copyright (c) %s By: %s\n", PASH_COPYRIGHT_DATE, PASH_COPYRIGHT_HOLDER);
		printf("Visit %s\n", PASH_URL);
		printf("Version: %s\n", PASH_VERSION);
        }
	else
	{
		puts("Invalid PASH Command!");
	}

	return success;
}

//=================================================================>

//builtin.h

/*
        Author: Robert C. Ilardi
        Date: 11/15/2004
        Description: Built In Shell Commands Header
*/

#ifndef PASH_BUILT_IN_H
#define PASH_BUILT_IN_H

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "bool.h"
#include "cmdline.h"
#include "linkedlist.h"
#include "pash_consts.h"

#include "shell.h"

bool builtInSupport(struct CmdLine*);
bool builtInExecute(struct CmdLine*);

#endif

//=================================================================>

//clparser.c

/*
        Author: Robert C. Ilardi
        Date: 11/8/2004
        Description: Command Line Parser Implementation
*/

#include "clparser.h"

const int CL_DELIMITER_IGNORE=0;
const int CL_DELIMITER_PUSHABLE=1;

const char *CL_DELIMITERS[]={" 2>", ">>", " ", ">", "<", "|", "&", "\t"};
const int CL_DELIMITERS_FLAGS[]={1, 1, 0, 1, 1, 1, 1, 0};

const int CL_DELIMITER_CNT=8; 

const char* CL_TOKEN_AMP="&";
const char* CL_TOKEN_GREATER_THAN=">";
const char* CL_TOKEN_LESS_THAN="<";
const char* CL_TOKEN_OUT_APPENDOR=">>";
const char* CL_TOKEN_PIPE="|";
const char* CL_TOKEN_ERR_REDIRECT="2>";

const int CLP_PIPE_DIRECTION_NONE=-1;
const int CLP_PIPE_DIRECTION_READ=0;
const int CLP_PIPE_DIRECTION_WRITE=1;
const int CLP_PIPE_DIRECTION_READ_WRITE=2;

const int REDIRECTS=3;

static char* trim(const char*);

void pushCmdLine(struct Stack* stack, char *command, struct LinkedList* parameters,
                 char *operators[], char *files[], bool pipedIn, bool pipeOut, bool background)
{
	struct CmdLine* cmdLine;
	int i;

	cmdLine=(struct CmdLine*)malloc(sizeof(struct CmdLine));
	cmdLine->parameters=parameters;
	cmdLine->command=command;
	cmdLine->backgroundProcess=background;
	cmdLine->pipedIn=pipedIn;
	cmdLine->pipeOut=pipeOut;
	cmdLine->inFile=NULL;
	cmdLine->outFile=NULL;
	cmdLine->errFile=NULL;

	for (i=0; i<REDIRECTS; i++)
	{
		if (operators[i]!=NULL && files[i]!=NULL)
		{
			if (strcmp(operators[i], CL_TOKEN_GREATER_THAN)==0 && !pipeOut)
			{
				cmdLine->outFile=files[i];
				cmdLine->appendOut=false;
                        }
                        else if (strcmp(operators[i], CL_TOKEN_LESS_THAN)==0 && !pipedIn)
			{
				cmdLine->inFile=files[i];
                        }
			else if (strcmp(operators[i], CL_TOKEN_ERR_REDIRECT)==0)
			{
				cmdLine->errFile=files[i];
			}
			else if (strcmp(operators[i], CL_TOKEN_OUT_APPENDOR)==0 && !pipeOut)
			{
				cmdLine->outFile=files[i];
				cmdLine->appendOut=true;
			}
                }
        }

	stackPush(stack, cmdLine);
}

bool isDelimiter(char *s)
{
	bool delimiter=false;
	int i;

	for (i=0; i<CL_DELIMITER_CNT; i++)
	{
		if (strcmp(s, trim(CL_DELIMITERS[i]))==0)
		{
			delimiter=true;
			break;
		}
	}

	return delimiter;
}

struct Stack* parseCmdLine(const char* const clStr)
{
	struct CmdLine* cmdLine;
	struct Queue* tokens;
	char *token, *nextToken, *command;
	char **operators, **files;
	bool background, pushed, pipedIn, pipeOut;
	struct Stack* commandStack;
	int i;
	struct LinkedList* parameters;

	tokens=tokenize(clStr);
	commandStack=stackCreate();

	pipedIn=false;
	pipeOut=false;

	while (!queueIsEmpty(tokens))
	{
		//Get Command
		pushed=false;
		command=(char *)queueGetFront(tokens);

		//Parse Parameters
		parameters=listCreate();
		if (!queueIsEmpty(tokens))
		{
			while (!queueIsEmpty(tokens))
			{
				token=(char *)queuePeek(tokens);
				if (!isDelimiter(token))
				{
					listAppend(parameters, token);
					queueGetFront(tokens);
				}
				else
				{
					break;
				}
			}
		}

		//Check for Redirects
		operators=(char **)malloc(sizeof(char*)*REDIRECTS);
		files=(char **)malloc(sizeof(char*)*REDIRECTS);
		for (i=0; i<REDIRECTS; i++)
		{
			operators[i]=NULL;
			files[i]=NULL;
		}
		if (!queueIsEmpty(tokens))
		{
			for (i=0; i<REDIRECTS && !queueIsEmpty(tokens); i++)
			{
				token=(char *)queuePeek(tokens);

				if (strcmp(token, CL_TOKEN_GREATER_THAN)==0
                                    || strcmp(token, CL_TOKEN_LESS_THAN)==0
                                    || strcmp(token, CL_TOKEN_ERR_REDIRECT)==0
                                    || strcmp(token, CL_TOKEN_OUT_APPENDOR)==0)
				{
					operators[i]=token;
					queueGetFront(tokens);
					token=(char *)queueGetFront(tokens);
					files[i]=token;
				}
				else
				{
					break;
				}
			}
		}

		//Check for background process or pipe
		background=false;
		pipeOut=false;
		if (!queueIsEmpty(tokens))
		{
			token=(char *)queuePeek(tokens);
			if (strcmp(token, CL_TOKEN_AMP)==0)
			{
				queueGetFront(tokens);
				background=true;
			}
			else if (strcmp(token, CL_TOKEN_PIPE)==0)
                        {
				queueGetFront(tokens);
                                pipeOut=true;
                        }
		}

		//Add Command to Stack of Commands
		pushCmdLine(commandStack, command, parameters, operators, files, pipedIn, pipeOut, background);
		pushed=true;
		pipedIn=pipeOut;

		if (background || !pipeOut)
		{
			break; //Terminated because in background or syntax error
		}
	}

	if (!pushed)
	{
		free(operators);
		free(files);
	}

        queueDestroy(tokens, false);

	return commandStack;
}

static char* trim(const char* s)
{
	struct StringBuffer* sb=strBufCreate();
	char* tStr=NULL;
	int i;

	for (i=0; i<strlen(s); i++)
	{
		if (s[i]!=' ' && s[i]!='\t')
		{
			strBufAppendChar(sb, s[i]);
		}
	}

	tStr=strBufToString(sb);

	strBufDestroy(sb);

	return tStr;
}

bool checkDelimiter(int* index, const char* const cmdLine,
                    struct StringBuffer* sb, struct Queue* tokens)
{
	bool delimiter=false;
	int i, j, forwardLen=0;
	char *tmp, *s, *delTmp;

	//Loop through delimiters check if one is the next token in the cmd line
	for (i=0; i<CL_DELIMITER_CNT; i++)
	{
		//Do we have enough chars in the cmd line
		//that is if possible to match the i'th delimiter?
		if ((strlen(cmdLine) - (*index)) >= strlen(CL_DELIMITERS[i]))
		{
			//extract the same number of char's from
			//the cmd line as the length ofthe delimiter
			tmp=(char *)malloc(strlen(CL_DELIMITERS[i])+1);
			for (j=0; j<strlen(CL_DELIMITERS[i]); j++)
			{
				tmp[j]=cmdLine[j+(*index)];
			}
			tmp[j]=''; //terminate with null

			//Are they equal?
			if (strcmp(tmp, CL_DELIMITERS[i])==0)
			{
				if (strBufLength(sb)>0)
				{
					s=strBufToString(sb);
					strBufClear(sb);
					queueInsert(tokens, s);
				}

				delimiter=true;
				forwardLen=strlen(CL_DELIMITERS[i])-1;

				if (CL_DELIMITERS_FLAGS[i]==CL_DELIMITER_PUSHABLE)
				{
					delTmp=tmp;
					tmp=trim(tmp);
					free(delTmp);
					queueInsert(tokens, tmp);
				}

				break;
			}
			else
			{
				free(tmp);
			}
		}
	}

	*index+=forwardLen;

	return delimiter;
}

struct Queue* tokenize(const char* const cmdLine)
{
	struct Queue* tokens=queueCreate();
	struct StringBuffer* sb=strBufCreate();
	char* s;
	int i;
	bool inQuotes=false, delimiter;

	for (i=0; i<strlen(cmdLine); i++)
	{
		delimiter=false;

		if(cmdLine[i]=='\"' || cmdLine[i]=='\'')
		{
			inQuotes=!inQuotes;
		}
		else if (!inQuotes)
		{
			delimiter=checkDelimiter(&i, cmdLine, sb, tokens);

			if (!delimiter)
	                {
				//Non-Delimiter Character
        	                strBufAppendChar(sb, cmdLine[i]);
                	}
		} //End !inQuotes Check
		else
		{
			//inQuotes
			strBufAppendChar(sb, cmdLine[i]);
		}
	}

	if (strBufLength(sb)>0)
	{
		s=strBufToString(sb);
		strBufClear(sb);
		queueInsert(tokens, s);
	}

	strBufDestroy(sb);

	return tokens;
}

//=================================================================>

//clparser.h

/*
        Author: Robert C. Ilardi
        Date: 11/8/2004
        Description: Command Line Parser Header
*/

#ifndef PASH_CLPARSER_H
#define PASH_CLPARSER_H

#include <stdlib.h>
#include <string.h>

#include "stack.h"
#include "strbuffer.h"
#include "bool.h"
#include "queue.h"
#include "cmdline.h"

struct Stack* parseCmdLine(const char* const clStr);
struct Queue* tokenize(const char* const s);

#endif

//=================================================================>

//cmdline.h

/*
        Author: Robert C. Ilardi
        Date: 11/8/2004
        Description: Command Line Structure Declaration
*/

#ifndef PASH_CMDLINE_H
#define PASH_CMDLINE_H

#include "bool.h"
#include "linkedlist.h"

struct CmdLine
{
        char* command;
        struct LinkedList* parameters;
        char* outFile;
        char* errFile;
        char* inFile;
        bool appendOut;
        bool backgroundProcess;
        bool pipedIn;
        bool pipeOut;
};

#endif

//=================================================================>

//control.c

/*
        Author: Robert C. Ilardi
        Date: 11/7/2004
        Description: Process Control Implementation
*/

#include "control.h"

static char* (*_uiGetFunctPtr)(void);
static void (*_uiPrompterFunctPtr)(char*, char*);

void installUserInterfaceGet(char* (*uiFunctPtr)(void))
{
	_uiGetFunctPtr=uiFunctPtr;
}

void installUserInterfacePrompter(void (*uiFunctPtr)(char*, char*))
{
	_uiPrompterFunctPtr=uiFunctPtr;
}

void controlPrintPrompt(char* prefix, char* suffix)
{
	_uiPrompterFunctPtr(prefix, suffix);
}

int runShell()
{
	char* cmdLine;
	struct Stack* cmdStack;

	for (;;) //Loop Forever; Well until Shell finds "exit"
	{
		//Get and Parse Cmd Line
		cmdLine=_uiGetFunctPtr();

		if (strlen(cmdLine)==0)
		{
			continue;
		}

		if (pashDebugMode())
		{
			printf("Cmd Line = \"%s\"\n", cmdLine);
			printf("Cmd Line Len = %d\n", strlen(cmdLine));
		}

		cmdStack=parseCmdLine(cmdLine);

		//Evaulate Command Stack and Execute
		executeCmdStack(cmdStack);

		//Free Memory
		free(cmdLine);
		stackDestroy(cmdStack, false); //Items in the stack are free'ed by executeCmdStack
	}

	return 0;

}

//=================================================================>

//control.h

/*
        Author: Robert C. Ilardi
        Date: 11/7/2004
        Description: Process Control Header
*/

#ifndef PASH_CONTROL_H
#define PASH_CONTROL_H

#include <stdio.h>
#include <stdlib.h>

#include "stack.h"
#include "clparser.h"
#include "shell.h"

void installUserInterfaceGet(char* (*uiFunctPtr)(void));
void intallUserInterfacePrompter(void* (*uiFunctPtr)(char*, char*));
int runShell();
void printPrompt();

#endif

//=================================================================>

//debugMod.c

/*
        Author: Robert C. Ilardi
        Date: 11/5/2004
        Description: Debug Utility Implementation
*/

#include "debugMod.h"

static bool _pashDebugMode;

void pashSetDebugMode(bool dm)
{
	_pashDebugMode=dm;
}

bool pashDebugMode()
{
	return _pashDebugMode;
}

//=================================================================>

//debugMod.h

/*
        Author: Robert C. Ilardi
        Date: 11/5/2004
        Description: Debug Utility Header
*/

#ifndef PASH_DEBUG_MOD_H
#define PASH_DEBUG_MOD_H

#include "bool.h"

void pashSetDebugMode(bool);
bool pashDebugMode();

#endif

//=================================================================>

//linkedlist.c

/*
        Author: Robert C. Ilardi
        Date: 10/29/2004
        Description: Doubly Linked List Implementation
*/

#include "linkedlist.h"

struct LinkedList* listCreate()
{
	struct LinkedList* linkedList=(struct LinkedList*)malloc(sizeof(struct LinkedList));
	linkedList->head=NULL;
	linkedList->tail=NULL;
	return linkedList;
}

struct ListNode* listGetHead(struct LinkedList* linkedList)
{
	return linkedList->head;
}

struct ListNode* listGetTail(struct LinkedList* linkedList)
{
        return linkedList->tail;
}

void listInsert(struct LinkedList* linkedList, void* item)
{
	struct ListNode* node=(struct ListNode *)malloc(sizeof(struct ListNode));

	node->item=item;
	node->next=NULL;
	node->previous=NULL;

	if (linkedList->head==NULL)
	{
		linkedList->head=node;
		linkedList->tail=node;
	}
	else
	{
		node->next=linkedList->head;
		linkedList->head->previous=node;
		linkedList->head=node;
	}
}

void listAppend(struct LinkedList* linkedList, void* item)
{
        struct ListNode* node=(struct ListNode *)malloc(sizeof(struct ListNode));

        node->item=item;
        node->next=NULL;
        node->previous=NULL;

        if (linkedList->head==NULL)
        {
                linkedList->head=node;
                linkedList->tail=node;
        }
        else
        {
                node->previous=linkedList->tail;
                linkedList->tail->next=node;
                linkedList->tail=node;
        }
}

void listClear(struct LinkedList* linkedList, bool deleteItems)
{
	while(linkedList->head!=NULL)
	{
		if (deleteItems)
		{
			free(linkedList->head->item);
		}
		free(linkedList->head);
		linkedList->head=linkedList->head->next;
	}

	linkedList->head=NULL;
	linkedList->tail=NULL;
}

void listRemove(struct LinkedList* linkedList, struct ListNode* node, bool deleteNode, bool deleteItem)
{
	if (node!=NULL)
	{
		//Remove NODE from the List!
		if (node->previous!=NULL)
		{
			node->previous->next=node->next;
		}
		else
		{
			linkedList->head=node->next;
		}

		if (node->next!=NULL)
		{
			node->next->previous=node->previous;
		}
		else
		{
			linkedList->tail=node->previous;
		}

		//Delete Item?
		if (deleteItem)
		{
			free(node->item);
		}

		//Delete Node?
		if (deleteNode)
		{
			free(node);
		}

	}
}

void listDestroy(struct LinkedList* linkedList, bool deleteItems)
{
	listClear(linkedList, deleteItems);
	free(linkedList);
}

int listSize(struct LinkedList* linkedList)
{
	int size=0;
	struct ListNode* node;

	node=linkedList->head;

	while(node!=NULL)
	{
		size++;
		node=node->next;
	}

	return size;
}

//=================================================================>

//linkedlist.h

/*
        Author: Robert C. Ilardi
        Date: 10/29/2004
        Description: Doubly Linked List Declaration
*/

#ifndef PASH_LINKEDLIST_H
#define PASH_LINKEDLIST_H

#include <stdlib.h>
#include "bool.h"

struct ListNode
{
	void* item;
	struct ListNode* next;
	struct ListNode* previous;
};

struct LinkedList
{
	struct ListNode* head;
	struct ListNode* tail;
};

struct LinkedList* listCreate();
struct ListNode* listGetHead(struct LinkedList*);
struct ListNode* listGetTail(struct LinkedList*);
void listInsert(struct LinkedList*, void*);
void listAppend(struct LinkedList*, void*);
void listClear(struct LinkedList*, bool);
void listRemove(struct LinkedList* linkedList, struct ListNode*, bool, bool);
void listDestroy(struct LinkedList*, bool);
int listSize(struct LinkedList*);

#endif

//=================================================================>

//queue.c

/*
        Author: Robert C. Ilardi
        Date: 10/29/2004
        Description: Queue Implementation using Doubly Linked List
*/

#include "queue.h"

struct Queue* queueCreate()
{
	struct Queue* queue=(struct Queue*)malloc(sizeof(struct Queue));
	queue->list=listCreate();
	return queue;
}

void queueDestroy(struct Queue* queue, bool deleteItems)
{
	listDestroy(queue->list, deleteItems);
	free(queue);
}

void queueInsert(struct Queue* queue, void* item)
{
	listAppend(queue->list, item);
}

void* queueGetFront(struct Queue* queue)
{
	void* item=queue->list->head->item;
	listRemove(queue->list, queue->list->head, true, false);
	return item;
}

void* queuePeek(struct Queue* queue)
{
	return queue->list->head->item;
}

int queueSize(struct Queue* queue)
{
	return listSize(queue->list);
}

bool queueIsEmpty(struct Queue* queue)
{
	return queue->list->head==NULL;
}

//=================================================================>

//queue.h

/*
        Author: Robert C. Ilardi
        Date: 10/29/2004
        Description: Queue declaration
*/

#ifndef PASH_QUEUE_H
#define PASH_QUEUE_H

#include "linkedlist.h"

struct Queue
{
	struct LinkedList* list;
};

struct Queue* queueCreate();
void queueDestroy(struct Queue*, bool);
void queueInsert(struct Queue*, void*);
void* queueGetFront(struct Queue*);
void* queuePeek(struct Queue*);
int queueSize(struct Queue*);
bool queueIsEmpty(struct Queue*);

#endif

//=================================================================>

//stack.c

/*
        Author: Robert C. Ilardi
        Date: 10/29/2004
        Description: Stack Implementation using Doubly Linked List
*/

#include "stack.h"

struct Stack* stackCreate()
{
	struct Stack* stack=(struct Stack*)malloc(sizeof(struct Stack));
	stack->list=listCreate();
	return stack;
}

void stackDestroy(struct Stack* stack, bool deleteItems)
{
	listDestroy(stack->list, deleteItems);
	free(stack);
}

void stackPush(struct Stack* stack, void* item)
{
	listInsert(stack->list, item);
}

void* stackPop(struct Stack* stack)
{
	void* item=stack->list->head->item;
	listRemove(stack->list, stack->list->head, true, false);
	return item;
}

void* stackPeek(struct Stack* stack)
{
	void* item=stack->list->head->item;
        return item;
}

bool stackIsEmpty(struct Stack* stack)
{
	return stack->list->head==NULL;
}

void stackClear(struct Stack* stack, bool deleteItems)
{
	listClear(stack->list, deleteItems);
}

int stackSize(struct Stack* stack)
{
	return listSize(stack->list);
}

//=================================================================>

//stack.h

/*
        Author: Robert C. Ilardi
        Date: 10/29/2004
        Description: Stack declaration
*/

#ifndef PASH_STACK_H
#define PASH_STACK_H

#include "linkedlist.h"

struct Stack
{
	struct LinkedList* list;
};

struct Stack* stackCreate();
void stackDestroy(struct Stack*, bool);
void stackPush(struct Stack*, void*);
void* stackPop(struct Stack*);
void* stackPeek(struct Stack*);
bool stackIsEmpty(struct Stack*);
void stackClear(struct Stack*, bool);
int stackSize(struct Stack*);

#endif

//=================================================================>

//strbuffer.c

/*
        Author: Robert C. Ilardi
        Date: 10/29/2004
        Description: String Buffer Implementation
*/

#include "strbuffer.h"

const int SB_BUFFER_LEN=10;

struct StringBuffer* strBufCreate()
{
	struct StringBuffer* sb=(struct StringBuffer *)malloc(sizeof(struct StringBuffer));
	sb->list=listCreate();
	sb->pos=0;
	sb->buffer=(char*)malloc(sizeof(char)*SB_BUFFER_LEN+1);
	sb->buffer[0]='';
	return sb;
}

void strBufDestroy(struct StringBuffer* sb)
{
	listDestroy(sb->list, true);
	sb->pos=0;
	free(sb->buffer);
	free(sb);
}

void strBufClear(struct StringBuffer* sb)
{
	listClear(sb->list, true);
	sb->pos=0;
}

void strBufAppendChar(struct StringBuffer* sb, char c)
{
	if (sb->pos<SB_BUFFER_LEN)
	{
		sb->buffer[sb->pos]=c;
		sb->pos++;
		sb->buffer[sb->pos]='';
	}
	else
	{
		listAppend(sb->list, sb->buffer);
		sb->buffer=(char*)malloc(sizeof(char)*SB_BUFFER_LEN+1);
		sb->pos=1;
		sb->buffer[0]=c;
		sb->buffer[1]='';
	}
}

void strBufAppendStr(struct StringBuffer* sb, char* s)
{
	int i;

	for (i=0; i<strlen(s); i++)
	{
	        if (sb->pos<SB_BUFFER_LEN)
        	{
	                sb->buffer[sb->pos]=s[i];
	                sb->pos++;
        	        sb->buffer[sb->pos]='';
	        }
	        else
	        {
	                listAppend(sb->list, sb->buffer);
	                sb->buffer=(char*)malloc(sizeof(char)*SB_BUFFER_LEN+1);
	                sb->pos=1;
	                sb->buffer[0]=s[i];
	                sb->buffer[1]='';
	        }
	}
}

char* strBufToString(struct StringBuffer* sb)
{
	char* str=NULL;
	int len=0;
	struct ListNode* node;

	node=sb->list->head;
	while(node!=NULL)
	{
		len+=SB_BUFFER_LEN;
		node=node->next;
	}

	len+=sb->pos+1;

	if (len>0)
	{
		str=(char*)malloc(sizeof(char)*len);
		str[0]='';
		node=sb->list->head;
	        while(node!=NULL)
	        {
	                strcat(str, (char *)node->item);
	                node=node->next;
	        }

		strcat(str, sb->buffer);
	}

	return str;
}

int strBufLength(struct StringBuffer* sb)
{
        int len=0;

        len+=listSize(sb->list)*SB_BUFFER_LEN;
        len+=sb->pos;

	return len;
}

//=================================================================>

//strbuffer.h

/*
        Author: Robert C. Ilardi
        Date: 10/28/2004
        Description: This file is the header file for the String Buffer.
*/

#ifndef PASH_STRBUFFER_H
#define PASH_STRBUFFER_H

#include <stdlib.h>
#include <string.h>
#include "linkedlist.h"

struct StringBuffer
{
	struct LinkedList* list;
	int pos;
	char* buffer;
};

struct StringBuffer* strBufCreate();
void strBufDestroy(struct StringBuffer*);
void strBufClear(struct StringBuffer*);
void strBufAppendChar(struct StringBuffer*, char);
void strBufAppendStr(struct StringBuffer*, char*);
char* strBufToString(struct StringBuffer*);
int strBufLength(struct StringBuffer*);

#endif

//=================================================================>

syntax highlighted by Code2HTML, v. 0.9.1

Just Another Stream of Random Bits…
– Robert C. Ilardi
 
This entry was posted in Development, Fun Stuff. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s