#include #include #include #include #include #include #include #include /* Parameters: Address of the line to process * Read the function to figure out the rest */ void process_line(char*, int, int, int); void signal_handler(int sigal){ // We don't actually want to do anything here right now } char *remove_whitespace(char *input){ while(isspace(*input)) input++; char *end = input + strlen(input); while(isspace(*(--end))) *end = 0; return input; } char **split(char *tosplit){ size_t length = strlen(tosplit); char **lines = (char**)malloc(100 * sizeof(char*)); size_t line = 0; size_t lines_space_left = 100; lines[0] = tosplit; for(size_t i = 0; i < length; i++){ if(isspace(tosplit[i])){ while(isspace(tosplit[i + 1])) i++; line++; tosplit[i] = 0; lines[line] = &tosplit[i] + 1; // If we have an extra one, revise this lines_space_left -= 1; } if(!lines_space_left){ lines = realloc(lines, (line + 100) * sizeof(char*)); lines_space_left += 100; } } return lines; } /* We'll expect that pipes, variables, etc. have already been processed */ void execute_command(char *command, int input_fd, int output_fd, int err_fd){ /* What if it's a shell builtin? */ /* Parsing for cd lacks good support for multiple or extraneous spaces */ if(!strcmp(command, "cd")){ // Change to home directory chdir(getenv("HOME")); // I'll just leave out error handling return; // Might have to close some fds, etc } else if(!strncmp(command, "cd ", 3)){ if(chdir(command + 3)) perror(command + 3); return; } /* It's not a shell builtin */ pid_t pid = fork(); // In the parent, pid is the pid of the child if(pid){ // We're the parent if(output_fd != 1) close(output_fd); wait(0); } else { // In the child, pid is 0 /* Assemble argv here */ char ** argv = split(command); dup2(input_fd, 0); dup2(output_fd, 1); dup2(err_fd, 2); execvp(argv[0], argv); perror(argv[0]); exit(1); } } /* Returns a pointer that needs to be freed afterwards */ char *do_expansions(char *line) { size_t space = strlen(line) * 2; char *expanded_line = malloc(space); size_t j = 0; for(int i = 0; line[i]; i++){ if(line[i] == '~'){ size_t newlen = strlen(getenv("HOME")); if(newlen + j >= space){ while(space <= newlen + j) space *= 2; expanded_line = realloc(expanded_line, space); } strcpy(expanded_line + j, getenv("HOME")); j += strlen(getenv("HOME")); } /* Other things line[i] might be */ else expanded_line[j++] = line[i]; if(j >= space){ space *= 2; expanded_line = realloc(expanded_line, space); } } expanded_line[j] = 0; return expanded_line; } void process_pipes(char *line, int last_read_end){ /* If there's a | in the line: * Set up redirects * Call process_line on both halves */ /* Easy case! */ char *pipe_location = strchr(line, '|'); if(!pipe_location) { process_line(line, last_read_end, 1, 2); return; } /* Harder case: We found a pipe */ char *command_one = line; *pipe_location = 0; char *command_two = pipe_location + 1; int pipe_fds[2]; pipe(pipe_fds); process_line(command_one, last_read_end, pipe_fds[1], 2); // Is there a third command? process_pipes(command_two, pipe_fds[0]); } void process_line(char *line, int input_fd, int output_fd, int err_fd){ /* Process the line and get it ready to execute (handle >, etc) */ line = remove_whitespace(line); char *gt_place = strchr(line, '>'); if(gt_place) { *gt_place = ' '; while(*(++gt_place) == ' '); char *filename_end = gt_place; while(*filename_end && isspace(*filename_end)) filename_end++; *filename_end = 0; output_fd = open(gt_place, O_WRONLY | O_CREAT | O_TRUNC); while(gt_place < filename_end){ *gt_place = ' '; ++gt_place; } *filename_end = ' '; execute_command(remove_whitespace(line), input_fd, output_fd, output_fd); // close(output_fd); // Closed in execute_command now } else { execute_command(line, input_fd, output_fd, err_fd); } } int main(){ char *line; struct sigaction sa; sa.sa_handler = signal_handler; sigaction(SIGINT, &sa, 0); while(1){ line = readline("prompt>> "); if(!line) { printf("Line was empty, leaving\n"); break; } if(!line[0]) continue; add_history(line); /* After this: We do whatever the shell was actually supposed to do! */ char *expanded_line = do_expansions(line); process_pipes(expanded_line, 0); free(line); free(expanded_line); } }