// CSE421 - Project 1 Part 1c #include #include #include #include #include #include #define ASCII_TAB 0x09 #define ASCII_SPACE 0x20 //FUNCTION PROTOTYPES: // Function to parse user input: int parseInput(char* myString, char* &file, char** args, int maxArgs); /////////////////////////////////////////////////////////////////////// int main() { int pid1; // PID of spawned child int i; // temporary int char inputBuffer[256]="\x00"; // buffer for user input char* file; // pointer to a file name or path char* args[64]; // pointer to an arguments array while(true) { // Show prompt, get user input: fputs("shell> ",stdout); if(fgets(inputBuffer, 255, stdin) == NULL) break; // Strip the trailing newline character from the end of string: inputBuffer[strlen(inputBuffer) - 1] = 0x00; // Parse the user input, this will modifly the inputBuffer string as well as the file and args[] pointers if(parseInput(inputBuffer, file, args,64) < 0) { fprintf(stderr,"Unable to parse command! Try again...\n"); continue; } // If user entered "exit" then break out of loop and exit. if(strcmp(file,"exit")==0) break; // Spawn a child to attempt to run the command: pid1=fork(); if(pid1 < 0) { perror("fork"); return -1; } if(pid1 != 0) //PARENT { waitpid(pid1,NULL,0); } else //CHILD { // The file and args pointers should have been set by parseInput (they point into the modifed // user input string), so they can be fed directly into execvp: i=execvp(file, args); if(i < 0) { perror("execvp"); return -1; } exit(0); } } return 0; //redundant & unneccessary? } ////////////////////////////////////////////////////////////////////////////// // parseInput function: // // PARAMETERS: // myString - pointer to an existing input string (this string WILL get modified) // file - address of an existing pointer. the pointer will be set to point to the first argument in myString, // presumably a filename or path. // args - address of an existing array of pointers. These pointers will be set to point to each individual // argument in the input string. // maxArgs - The number of pointers in the existing array of pointers passed into this function. Represents the // maximum number of arguments that can be parsed. // // DESCRIPTION: // Takes a null terminated string pointed to by myString, and modifies this string. First it removes any leading and trailing // spaces/tabs from the string, then replaces any consecutive spaces and/or tabs with a single space. Then changes each // single space to a null byte, effectively breaking the string into several smaller strings which represent the // individual arguments of the original string. It then sets the file and args[] pointers to point to different locations inside // the myString buffer in the following manner: // // file - will be set to point to the first argument // args[0] - will point to the same string as file, but if file contains a '/' symbol, args[0] will point to the first // character after the LAST '/' symbol. For example, if file == "/bin/ls", // then args[0] will be just "ls" // args[1]-args[?] - these point to each subsequent argument in the buffer // // RETURNS 0 for success, and <0 for failure /////////////////////////////////////////////////////////////////////////////// // EXAMPLE: // myString=" /bin/ls -l -a /home " // The myString string would be changed to this (where # is a NULL byte): // myString="/bin/ls#-l#-a#/home#" // ^ ^ ^ ^ ^ // // The ^ symbols above represent the pointed to locations of file, args[0], args[1], args[2], and args[3] respectively. // And args[4] would be set to NULL. // /////////////////////////////////////////////////////////////////////////////// int parseInput(char* myString, char* &file, char** args, int maxArgs) { int i=0; // temp int int j=0; //.temp int int curArg=0; // current argument # being processed file=NULL; // set the file pointer to be NULL (for good measure). if(maxArgs<2) // args will need at least need 2 pointers, one for the command, the 2nd for the null term. return -1; args[0]=NULL; // set the first args[] pointer to be NULL, for good measure // Remove any TRAILING spaces or tabs from myString: i=strlen(myString); while(i>0 && (myString[i-1]==ASCII_SPACE || myString[i-1]==ASCII_TAB)) { myString[i-1]=0; i--; } // Remove any LEADING spaces or tabs from myString: while(myString[0] == ASCII_SPACE || myString[0] == ASCII_TAB) myString++; // increase the pointer by 1 byte, so as to "lose" the 1st char of the string. // if after removeing leading/trailing spaces the buffer is empty, then theres nothing to do: if(strlen(myString) == 0) return -1; // // i is the "read index", j is the "write index" // the string is read, processed, and written back into the same location: // It reads through myString 1 char at a time. If a series of spaces/tabs is encountered, they're replaced with // a SINGLE SPACE. i=j=0; while(myString[i] != 0) { if(myString[i] == ASCII_SPACE || myString[i] == ASCII_TAB) // if the char currently being READ is a space, { if(myString[j-1] == ASCII_SPACE) // check to see if the last char WRITTEN was also a space, { i++; // if so, then continue reading the next char continue; } else // otherwise, write a single space: { myString[j++]=ASCII_SPACE; i++; continue; } } myString[j++]=myString[i++]; // if the char being read is not a space, then just // write it. } myString[j]=0; // add the null termination to the new myString string // position i so that myString[i-1] is the LAST CHAR of the first argument i=0; while(myString[i] != 0 && myString[i] != ASCII_SPACE) i++; // position i so that IF the first argument is a path name such as "/bin/ls" then myString[i] points to just "ls". while(i>0) { if(myString[i-1] == '/') { break; } i--; } args[curArg++] = &(myString[i]); //set args[0] to point to just the file name, ie "ls" not "/bin/ls' // position i again so that myString[i] is the char AFTER the first argument (which will be either a space or a null byte.) while(myString[i]!=0 && myString[i] != ASCII_SPACE) i++; // Change each space in myString to a null byte and set the args[] pointers so that they point to each individual // argument string. while(myString[i]!=0 && curArg < (maxArgs-1)) { if(myString[i]== ASCII_SPACE) // a space can only come BETWEEN 2 arguments, so if one is encountered, // the NEXT char must be the first char of a new argument: { args[curArg++]=&(myString[i+1]); // set the next args[] pointer myString[i]=0; // null terminate the previous arg string } i++; } args[curArg]=NULL; // NULL-terminate the args[] array. // set the file name pointer to first argument which should be the entire file name & maybe path, ie "/bin/ls" file=myString; return 0; }