// shell.c // Taco Shell v1.0 (tsh) // By Brad Pineau #include #include #include #include #define PROMPT "Taco Shell" #define FALSE 0 #define TRUE 1 #define NORMALMODE 0 #define CONCURRENTMODE 1 #define STDIN 0 #define STDOUT 1 #define DEFAULT 2 // structure to hold the command name and arguments typedef struct command_t { char *name; int argc; char **argv; } CommandType; // declaration of functions used in program char* getCommandPath(char* command); void trim(char* name); int runCommand(CommandType* command); int runConcurrentCommand(CommandType* command); int runPipedCommands(CommandType* command, CommandType* command2); int runRedirectedCommand(CommandType* command, char* file, int fileid); char *path; char *ptr; char *input; int pid; int status; int redirectionType; // returns the full path of a command // returns NULL if the command is not found on the path char* getCommandPath(char* command) { FILE *fpin; char p[1024]; char *temppath; char *temp = (char *)calloc(1024, sizeof(char)); path = (char *) getenv("PATH"); strcpy ( p, path ); path = strtok ( p, ":" ); // Find the first PATH while ( path != NULL ) { strcpy ( temp, path ); // Save a copy of substring strcat ( temp, "/" ); // Form a path and file name strcat ( temp, command ); if ( ( fpin = fopen ( temp, "r" ) ) == NULL ) { temp[0] = '\0'; // Start over again path = strtok ( ( char * ) 0, ":" ); // Try next path } else { break; // Must have a good fpin } } if ( fpin == NULL ) { return NULL; } else { return temp; } } // this function was found on groups.google.com // there was no author's name with the code... void trim(char* name) { char* p = name ; size_t n ; while (isspace(*p)) ++p ; if ((n = strlen(p)) > 0) while (isspace(p[n-1])) --n ; ((char*)memmove(name, p, n))[n] = '\0' ; } // the main program... int main() { CommandType *command = (CommandType*) malloc(sizeof(CommandType)); CommandType *command2 = (CommandType*) malloc(sizeof(CommandType)); int count; int runmode; int piped; char* redirectionFilename; char* buffer = (char*)calloc(1024, sizeof(char)); while(TRUE){ piped = FALSE; redirectionType = DEFAULT; input = (char *)calloc(1024, sizeof(char)); redirectionFilename = (char *)calloc(1024, sizeof(char)); // prinit the prompt printf("%s - (%s)> ", PROMPT, getcwd(buffer, 1024)); gets(input); trim(input); // start over if a blank line happens if ((strcmp(input, "") != 0) && (strcmp(input, ".") != 0) && (strcmp(input, "..") != 0)) { // checks of exit or quit, and terminates program if seen if ((strcmp(input, "quit")== 0) || (strcmp(input, "exit")== 0)) { printf("Terminating Shell...\n"); return 0; } // initialize argv array command->argv = (char**)calloc(256, sizeof(char*)); command2->argv = (char**)calloc(256, sizeof(char*)); // parses the command line ptr = strtok(input, " "); command->name = ptr; command->argv[0] = ptr; command->argc = 1; count = 1; runmode = NORMALMODE; ptr = strtok (NULL, " "); while (ptr != NULL) { //command->argv[count++] = ptr; if (strcmp(ptr, "|")==0) { // pipe found if (piped) { printf("Error: Second level piping not supported.\n"); } else { // set up parser to read into the 2nd command piped = TRUE; ptr = strtok (NULL, " "); if (ptr != NULL) { command2->name = ptr; command2->argv[0] = ptr; command2->argc = 1; count = 1; } else { printf("Error: No command to pipe.\n"); } } } else if (strcmp(ptr, ">")==0) { ptr = strtok (NULL, " "); if (ptr != NULL) { redirectionFilename = ptr; redirectionType = STDOUT; ptr = NULL; // used to break while loop } else { printf("Error: No file specified for redirection.\n"); } } else if (strcmp(ptr, "<")==0) { ptr = strtok (NULL, " "); if (ptr != NULL) { redirectionFilename = ptr; redirectionType = STDIN; ptr = NULL; // used to break while loop } else { printf("Error: No file specified for redirection.\n"); } } else { if (piped) { command2->argv[count++] = ptr; command2->argc++; } else { command->argv[count++] = ptr; command->argc++; } } ptr = strtok (NULL, " "); } if (piped) { runPipedCommands(command, command2); } else if (redirectionType != DEFAULT) { runRedirectedCommand(command, redirectionFilename, redirectionType); } else { if (strcmp(command->argv[command->argc-1],"&")==0) { // & found, remove it from argv list runmode = CONCURRENTMODE; command->argv[command->argc-1] = NULL; } // check for built-in commands if (strcmp(command->argv[0], "echo") == 0) { // echo the commandline tokens int i; for (i = 1; i < command->argc; i++) { printf("%s ", command->argv[i]); } printf("\n"); } else if (strcmp(command->argv[0],"cd") == 0) { // change directory if (command->argc == 2) { if (chdir(command->argv[1]) != 0) { printf("Error: Invalid directory.\n"); } } else { printf("Error: Invalid use of cd command.\n"); } } else if (strcmp(command->argv[0],"getenv") == 0) { if (command->argc == 2) { char* variable = (char *) getenv(command->argv[1]); if (variable == NULL) { printf("Error: Invalid environment variable.\n"); } else { printf("%s\n", variable); } } else { printf("Error: Invalid use of getenv command.\n"); } } else if (strcmp(command->argv[0],"setenv") == 0) { if (command->argc == 3) { char* value = (char*)calloc(1024,sizeof(char)); strcpy(value, command->argv[1]); strcat(value, "="); strcat(value, command->argv[2]); putenv(value); } else { printf("Error: Invalid use of getenv command.\n"); } } else { // run the command as regular command.... if (runmode == NORMALMODE) { runCommand(command); } else if (runmode == CONCURRENTMODE) { runConcurrentCommand(command); } } } // if (piped) } } } // runs a command normally int runCommand(CommandType* command) { char* filename = getCommandPath(command->name); if (filename == NULL) { printf("Command not found.\n"); return 1; } // creates child process pid = fork(); // checks for error if (pid < 0) { printf("Error in the fork process.\n"); return 1; } else if (pid == 0) { // Child code execv(filename, command->argv); return 1; } else { // Parent waits while (wait(&status) != pid); } } // runs a command as a background process int runConcurrentCommand(CommandType* command) { char* filename = getCommandPath(command->name); if (filename == NULL) { printf("Command not found.\n"); return 1; } // creates child process pid = fork(); // checks for error if (pid < 0) { printf("Error in the fork process.\n"); return 1; } else if (pid == 0) { // Child code execv(filename, command->argv); return 1; } else { // Parent doesn't wait //while (wait(&status) != pid); } } // pipes one command into another int runPipedCommands(CommandType* command, CommandType* command2) { char* filename = getCommandPath(command->name); if (filename == NULL) { printf("%s command not found.\n", command->name); return 1; } char* filename2 = getCommandPath(command2->name); if (filename2 == NULL) { printf("%s command not found.\n", command2->name); return 1; } int fd[2]; int pid, pid2; if (pipe(fd) == -1) { printf("Error: Pipe failed.\n"); return 1; } if ((pid = fork()) < 0) { printf("Error: Fork failed.\n"); return 1; } if (pid == 0) { // child process close(fd[1]); dup2(fd[0], 0); close(fd[0]); execv(filename2, command2->argv); printf("Error: execv failed.\n"); return 1; } else { // parent process // need to fork again, so the shell isn't replaced if ((pid2 = fork()) < 0) { printf("Error: Fork failed.\n"); return 1; } if (pid2 == 0) { // child process close(fd[0]); dup2(fd[1], 1); close(fd[1]); execv(filename, command->argv); printf("Error: execv failed.\n"); return 1; } else { // parent process (the shell) close(fd[0]); close(fd[1]); while (wait(&status) != pid2); } } return 0; } // redirects STDIN or STDOUT for a command int runRedirectedCommand(CommandType* command, char* file, int fileid) { char* filename = getCommandPath(command->name); if (filename == NULL) { printf("%s command not found.\n", command->name); return 1; } int pid; if ((pid = fork()) < 0) { printf("Error: Fork failed.\n"); return 1; } if (pid == 0) { // child process int fid; if (fileid == STDIN) { fid = open(file, O_RDONLY, 0600); } else if (fileid == STDOUT) { fid = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600); } dup2(fid, fileid); close(fid); execv(filename, command->argv); printf("Error: execv failed.\n"); return 1; } else { // parent process (shell) while (wait(&status) != pid); } }