#include #include #include #include #include #include #include #include "scolor.h" #define CMDLEN (1024 * 100) #define MAX_DIRLEN 1024 char ** split_string(char* start, char delimiter); char* getoutput(char* command); char cwd[MAX_DIRLEN]; char cmd_expanded[CMDLEN]; struct shellvar { char *name, *value; }; struct shellvar *variables; size_t vsize = 64; size_t vcount = 0; int status; char* qstrstr(char* haystack, char needle){ char inquotes = 0; for(int i = 0; i < strlen(haystack); i++) if((haystack[i] == needle) && !inquotes) return haystack+i; else if(haystack[i] == '"') inquotes = !inquotes; return 0; } // *start means the same thing as start[0] char* trim_whitespace(char* start){ while(isspace(*start)) ++start; char *end = start + strlen(start) - 1; while(isspace(*end)){ *end = 0; end--; } return start; } pid_t run_command(char* command, int input, int output){ command = trim_whitespace(command); pid_t pid = fork(); if(pid){ // We are the parent! return pid; // printf("Command %s finished\n", command); } else { // We are the child! char ** argv = split_string(command, 0); if(input != 0) dup2(input, 0); if(output != 1) dup2(output, 1); execvp(command, argv); perror(command); return 0; } } char* expand(char *command){ char *varname; while(varname = qstrstr(command, '$')){ *varname = 0; varname++; char *end = varname; while(*end && !isspace(*end)) end++; if(*end != 0) *end = 0; else end--; if(!strcmp(varname, "?")){ snprintf(cmd_expanded, CMDLEN, "%s %d %s", command, WEXITSTATUS(status), end+1); goto found_it; } for(int i = 0; i < vcount; i++) if(!strcmp(varname, variables[i].name)){ snprintf(cmd_expanded, CMDLEN, "%s %s %s", command, variables[i].value, end+1); goto found_it; } return 0; found_it: strcpy(command, cmd_expanded); } char* first_tic = qstrstr(command, '`'); if(!first_tic) return command; char* second_tic = qstrstr(first_tic + 1, '`'); if(!second_tic) return 0; char* backtic_command = first_tic + 1; *second_tic = 0; char* command_output = getoutput(backtic_command); *first_tic = 0; int size = snprintf(cmd_expanded, CMDLEN, "%s %s %s", command, command_output, second_tic + 1); free(command_output); if(size > CMDLEN-1) return 0; return cmd_expanded; } void process_pipes(char *command){ char* pipe_location = qstrstr(command, '|'); if(pipe_location){ char ** commands = split_string(command, '|'); int command_count = 0; while(commands[++command_count]); // Counts number of commands pid_t pids[command_count]; int last_end = 0; for(int i = 0; ; ){ int pipefds[2]; pipe(pipefds); pids[i] = run_command(commands[i], last_end, pipefds[1]); close(pipefds[1]); if(last_end) close(last_end); last_end = pipefds[0]; if(!commands[++i + 1]) break; } pids[command_count-1] = run_command(commands[command_count-1], last_end, 1); close(last_end); for(int i = 0; i < command_count; i++) waitpid(pids[i], &status, 0); } else { // There's no pipe in the command! Easy! waitpid(run_command(command, 0, 1), &status, 0); } } int check_builtin(char* command){ if(!strncmp(command, "cd", 2)){ command += 2; while(isspace(*command)) command++; chdir(command); getcwd(cwd, MAX_DIRLEN); return 1; } else if(!strncmp(command, "export", 6)){ char *varname = command + 6; while(isspace(*varname)) varname++; char *content = varname; while(*content != '=') content++; *content = 0; content++; for(int i = 0; i < vcount; i++){ if(!strcmp(varname, variables[i].name)){ if(strlen(content) > strlen(variables[i].value)){ free(variables[i].value); variables[i].value = (char*)malloc(strlen(content)); } strcpy(variables[i].value, content); return 1; } } if(vcount == vsize){ vsize += 64; variables = (struct shellvar*)realloc(variables, sizeof(struct shellvar) * vsize); } variables[vcount].name = (char*)malloc(strlen(varname)); strcpy(variables[vcount].name, varname); variables[vcount].value = (char*)malloc(strlen(content)); strcpy(variables[vcount].value, content); vcount++; return 1; } else { return 0; } } int main(){ char cmd_buffer[CMDLEN]; char prompt[] = RED(" >>> "); size_t readlen; variables = (struct shellvar*)malloc(sizeof(struct shellvar) * vsize); getcwd(cwd, MAX_DIRLEN); // Call sigaction, and install a handler for sigint while(1){ write(2, cwd, strlen(cwd)); write(2, prompt, strlen(prompt)); readlen = read(0, cmd_buffer, CMDLEN); if(readlen < 1) break; cmd_buffer[readlen - 1] = 0; char* torun = expand(cmd_buffer); if(!torun){ printf("Cannot expand command!\n"); continue; } if(!check_builtin(torun)) process_pipes(torun); } puts(""); return 0; } /* If delimiter is 0, this will split on whitespace characters * Note: This will modify the original string! */ char ** split_string(char* start, char delimiter){ size_t allocated_places = 100; char ** splits = (char**)calloc(sizeof(char*), 100); int sp = 1; char inquotes = 0; splits[0] = start; for(int i = 0; start[i] != 0; i++){ if(start[i] == '"'){ inquotes = !inquotes; if(inquotes) start[i] = ' '; else start[i] = 0; continue; } if( ((delimiter == 0)? isspace(start[i]):(start[i] == delimiter)) && !inquotes ){ start[i] = 0; if( (delimiter == 0)? isspace(start[i+1]):(start[i+1] == delimiter) ) continue; splits[sp] = 0; splits[sp++] = trim_whitespace(start + i+1); if(allocated_places < sp+2){ allocated_places += 100; splits = (char**)realloc(splits, sizeof(char*) * allocated_places); } } } return splits; } char* getoutput(char* command){ int pipefds[2]; pipe(pipefds); int read_end = pipefds[0]; int write_end = pipefds[1]; pid_t pid = fork(); if(pid){ // We're the parent process close(write_end); char* buffer = (char*)malloc(128); size_t allocation_size = 128; size_t actually_read = 0; size_t readlen = read(read_end, buffer, 128); actually_read += readlen; while(readlen > 0){ // read more out of the pipe! buffer = (char*)realloc(buffer, allocation_size + 128); allocation_size += 128; readlen = read(read_end, buffer+actually_read, 128); actually_read += readlen; } buffer[actually_read] = 0; wait(0); close(read_end); return buffer; } else { // We're the child process close(read_end); dup2(write_end, 1); execl("/bin/sh", "sh", "-c", command, 0); perror("exec"); close(write_end); } }