#include #include #include #include #include #include #include #include #include #define ARG_MAX 100 #define DIRLEN_MAX 1024 int last_status = 0; /* Return value: * If there is another argument, a pointer to that argument * If there isn't, a null */ char* find_next_arg(char *b){ for(; *b && !isspace(*b); b++); if(!*b) return 0; *b = 0; b++; for(; *b && isspace(*b); b++); if(!*b) return 0; return b; } char prompt[128]; void handle_ctrlc(int){ // This function does nothing, but it has to exist } char cwd[DIRLEN_MAX]; void change_directory(char* cbp){ char *next_directory = getenv("HOME"); if(cbp) { next_directory = find_next_arg(cbp); if(!next_directory) next_directory = getenv("HOME"); else find_next_arg(next_directory); } if(chdir(next_directory)){ perror(next_directory); } else { strncpy(cwd, next_directory, DIRLEN_MAX); } } void run_as_child(char *command){ char command_buffer[8196]; strcpy(command_buffer, command); // Expansion // Shell variables char *place; while(place = strchr(command_buffer, '$')) { char *name = place + 1; char *name_end = name; char *value = 0; for(; *name_end && !isspace(*name_end); name_end++); *name_end = 0; if(name[0] == '?'){ sprintf(prompt, "%d", WEXITSTATUS(last_status)); value = prompt; } else value = getenv(name); size_t var_length = 1 + (name_end - name) + 1; size_t value_length = strlen(value); if(var_length > value_length){ strcpy(place, value); *name_end = ' '; char *copy_dst = place + value_length; char *copy_src = name_end; while(*(copy_dst++) = *(copy_src++)); } else if (var_length == value_length){ strcpy(place, value); *name_end = ' '; } else { *name_end = ' '; size_t rest_length = strlen(name_end); char *copy_dst = place + value_length + rest_length; char *copy_src = name_end + rest_length; for(; copy_src != name_end - 1; *(copy_dst--) = *(copy_src--)); memcpy(place, value, value_length); } } // Process the command to generate argv char *argv[ARG_MAX]; size_t arg = 0; char *next_arg = command_buffer; argv[0] = next_arg; arg = 1; while(next_arg = find_next_arg(next_arg)){ argv[arg] = next_arg; arg++; } argv[arg] = 0; // Actually run the command execvp(argv[0], argv); perror("exec"); exit(1); } int main(){ char *command; char *commands[64]; ssize_t readlen; size_t command_count; char *place; struct sigaction sa; sa.sa_handler = handle_ctrlc; sigaction(SIGINT, &sa, 0); getcwd(cwd, DIRLEN_MAX); while(1){ // Read a command from the user snprintf(prompt, 128, "%s & ", cwd); command = readline(prompt); if(!command) // This happens from ctrl+d break; if(!command[0]) continue; add_history(command); // Shell builtins if(!strcmp("exit\n", command)) break; if(!strcmp("cd\n", command)) { change_directory(0); continue; } if(!strncmp("cd ", command, 3)){ change_directory(command); continue; } // Look and see if we have a pipe commands[0] = command; command_count = 1; for(char *pipe_location; pipe_location = strchr(commands[command_count-1], '|');){ *pipe_location = 0; while(isspace(*(++pipe_location))); commands[command_count++] = pipe_location; } int input_fd = 0; int next_input_fd = 0; int output_fd = 1; for(size_t i = 0; i < command_count; i++){ if(i < command_count - 1){ // If we're not the last command int fds[2]; pipe(fds); output_fd = fds[1]; next_input_fd = fds[0]; } pid_t pid = fork(); if(!pid) { // We're the child! // Set input source if(input_fd != 0) dup2(input_fd, 0); // Set output source if(output_fd != 1) dup2(output_fd, 1); run_as_child(commands[i]); } if(input_fd != 0) close(input_fd); if(next_input_fd != 0) input_fd = next_input_fd; if(output_fd != 1){ close(output_fd); output_fd = 1; } } // Only the parent should be here (No Children Allowed!) for(size_t i = 0; i < command_count; i++) wait(&last_status); free(command); } write(2, "\n", 1); return 0; }