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…
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:
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!
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