#include #include #include #include #include #include #include #include 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 /* For active learning: Download shell.c, name it al1028.c, add the sucess message bit */ /* Define an int */ wait(0); /* Use the address of your int as the paramter for wait */ /* If the variable has the value 0, print a success message, else failure message */ /* To test, there are programs named "true" and "false". "true" always returns sucess, "false" always returns failure */ } else { // In the child, pid is 0 /* Assemble argv here */ char ** argv = split(command); dup2(output_fd, 1); execvp(argv[0], argv); perror(argv[0]); exit(1); } } void process_line(char *line){ /* Process the line and get it ready to execute (handle >, etc) */ 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; int 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), 0, output_fd, 2); close(output_fd); } else { execute_command(line, 0, 1, 2); } free(line); } 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! */ process_line(line); } }