#include "error.h" #include "run.h" #include "main.h" #include "util.h" #include #include #include #include #include int get_alias(Alias *alias, State *state, char *name); int add_alias(State* state, char *name, char *substitution); int del_alias(State *state, char *name); int repl(State* state, FILE *input); int run_command(State *state, int argc, char *argv[]); int get_array_element(ArrayElement **ret, GrowingArray* array, char* key); int del_array_element(GrowingArray* array, char* key); int expand_command(State *state, int *argc, char** ret_argv[]); GrowingArray get_growing_array(size_t num_elements, size_t size_elem) { GrowingArray arr; arr.inner = calloc(num_elements, size_elem); arr.space = num_elements; arr.elements = 0; return arr; } int get_alias(Alias *alias, State *state, char *name) { for (int i = 0; i < state->num_aliases; i++) { if (!strcmp(name, state->aliases[i].name)) { *alias = state->aliases[i]; return ER_SUCCESS; } } return ER_FAILURE; } int get_array_element(ArrayElement **ret, GrowingArray* array, char* key) { for (int i = 0; i < array->elements; i++) { if (!strcmp(key, array->inner[i].key)) { *ret = array->inner + i; return ER_SUCCESS; } } return ER_NOT_EXISTS; } int check_array_space(GrowingArray* array) { int resize = 0; if (array->elements > MIN_ARRAY_SIZE) { if (array->elements >= array->space - 2) { resize = 2 * array->space; } else if (array->elements < (array->space / 3)) { resize = array->space / 2; } } if (resize) { void *new = reallocarray(array->inner, resize, sizeof(ArrayElement)); if (new) { array->space = resize; array->inner = new; return ER_SUCCESS; } else { return ER_FAILURE; } } else { return ER_SUCCESS; } } int set_array_element(GrowingArray* array, char* key, void* value, size_t valsize) { ArrayElement *arr; if (!get_array_element(&arr, array, key)) { free(arr->value); arr->value = dualloc(value, valsize); return ER_SUCCESS; } else { check_array_space(array); void *val = dualloc(value, valsize); void *nkey = dualloc(key, (strlen(key) + 1) * sizeof(char)); if (value && key) { array->inner[array->elements].value = val; array->inner[array->elements].key = nkey; array->elements++; return ER_SUCCESS; } } return ER_FAILURE; } int del_array_element(GrowingArray* array, char* key) { ArrayElement *elem; int err = get_array_element(&elem, array, key); if (err) { return err; } else { bool move = false; for (int i = 0; i < array->elements; i++) { if (move) { if (i == array->elements - 1) { memset(&array->inner[i], 0, sizeof(ArrayElement)); } else { array->inner[i] = array->inner[i+1]; } } if (!strcmp(array->inner[i].key, key)) { if (!move) { free(array->inner[i].key); free(array->inner[i].value); move = true; // move if (i == array->elements - 1) { memset(&array->inner[i], 0, sizeof(ArrayElement)); } else { array->inner[i] = array->inner[i+1]; } } } } array->elements--; return ER_SUCCESS; } return ER_FAILURE; } int add_alias(State* state, char* name, char* substitution) { Alias alias; strip_char(name, ' '); alias.name = calloc(strlen(name) + 1, sizeof(char)); alias.substitution = calloc(strlen(substitution) + 1, sizeof(char)); strcpy(alias.name, name); strcpy(alias.substitution, substitution); Alias existing; if (get_alias(&existing, state, name)) { // add it if it doesnt exist state->aliases[state->num_aliases] = alias; state->num_aliases++; } else { del_alias(state, name); free(alias.name); free(alias.substitution); add_alias(state, name, substitution); } return ER_SUCCESS; } int del_alias(State *state, char *name) { // read the list until the alias is found, then // shuffle all the alias definitions down by one bool move = false; for (int i = 0; i < state->num_aliases; i++) { if (move) { if (i < state->num_aliases -1) { state->aliases[i] = state->aliases[i + 1]; } else { memset(&state->aliases[i], 0, sizeof(Alias)); } } else { if (!strcmp(name, state->aliases[i].name)) { move = true; } } } if (move) { state->num_aliases--; return ER_SUCCESS; } else { return ER_FAILURE; } } int list_aliases(State *state) { for (int i = 0; i < state->num_aliases; i++) { Alias alias = state->aliases[i]; fprintf(state->output, "alias %s=%s\n", alias.name, alias.substitution); } return ER_SUCCESS; } int list_variables(State *state) { for (int i = 0; i < state->variables.elements; i++) { char *key = state->variables.inner[i].key; char *value = state->variables.inner[i].value; fprintf(state->output, "%s=%s\n", key, value); } return ER_SUCCESS; } void shutdown(State *state) { for (int i = 0; i < state->num_aliases; i++) { free(state->aliases[i].name); free(state->aliases[i].substitution); } free(state->ps1); free(state); exit(0); } int run_pipes(State *state, int num_args, char *args []) { char *joined = 0; int err = join_string(&joined, num_args, args, " "); if (err) { errx(1, "no"); } char * separated_by_pipe; int pipe_count = 0; for (int i = 0; i < strlen(joined); i++) { if (joined[i] == '|') { pipe_count++; } } int num_commands = pipe_count + 1; char **sep_commands = calloc(pipe_count + 1, sizeof(char *)); int i = 0; char *piece = strtok(joined, "|"); do { sep_commands[i] = calloc(strlen(piece) + 1, sizeof(char)); strcpy(sep_commands[i], piece); i++; } while ((piece = (char *)strtok(NULL, "|")) != 0); if (num_commands == 1) { for (int i = 0; i < num_commands; i++) { free(sep_commands[i]); } free(sep_commands); free(joined); // free everything return ER_NOT_EXISTS; } int *pipes = calloc(pipe_count * 2, sizeof(int)); for (int i = 0; i < pipe_count; i++) { pipe(pipes + (2 * i)); } pid_t child; int in; int out; for (int i = 0; i < num_commands; i++) { char **command; int words; int err = split_string(&command, &words, sep_commands[i], ' '); if (i == 0) { in = 0; out = pipes[2*i + 1]; } else if (i == num_commands - 1) { in = pipes[2*(i-1)]; out = 1; } else { in = pipes[2*(i-1)]; out = pipes[2*i + 1]; } int command_len = 0; for (int i = 0; command[i] != 0; i++) { command_len++; } //free(command[i]); // free the null pointer on the end expand_command(state, &command_len, &command); for (int i = 0; i < command_len; i++) { printf("%s ", command[i]); } printf("\n"); command = reallocarray(command, command_len+1, sizeof(char*)); command[command_len] = 0; execute(in, out, command, &child); for (int i = 0; i < command_len; i++) { free(command[i]); } free(command); } int status; while (!wait(&status)); free(pipes); for (int i = 0; i < num_commands; i++) { free(sep_commands[i]); } free(sep_commands); free(joined); return ER_SUCCESS; } int check_builtins(State *state, int num_args, char *args[]) { char *builtins[NUM_BUILTINS] = { "cd", "alias", "unalias" }; if (!strcmp(args[0], "cd")) { if (num_args == 1) { chdir(getenv("HOME")); return ER_SUCCESS; } if (num_args != 2) { return ER_FAILURE; } chdir(args[1]); } if (!strcmp(args[0], "set")) { if (num_args >= 2) { char *joined; int err = join_string(&joined, num_args -1, args + 1, " "); char **split; int num; err = split_string(&split, &num, joined, '='); printf("%s\n", joined); if (num == 2) { char * key = split[0]; char * value = split[1]; set_array_element(&state->variables, key, value, (strlen(value) + 1) * sizeof(char)); return ER_SUCCESS; } else { return ER_FAILURE; } } } if (!strcmp(args[0], "listvars")) { list_variables(state); return ER_SUCCESS; } if (!strcmp(args[0], "drop")) { if (num_args == 2) { char *key = args[1]; int err = del_array_element(&state->variables, key); if (err) { return ER_FAILURE; } else { return ER_SUCCESS; } } return ER_FAILURE; } if (!strcmp(args[0], "alias")) { Alias alias; // list aliases if (args[1] == 0) { list_aliases(state); return ER_SUCCESS; } // add new alias if (num_args >= 2) { if (num_args == 2) { if (!get_alias(&alias, state, args[1])) { fprintf(state->output, "alias %s=%s\n", alias.name, alias.substitution); return ER_SUCCESS; } } if (num_args >= 2) { char *substitution; char **new_split; int err = join_string(&substitution, num_args - 1, args + 1, " "); if (err) { free(substitution); return err; } int eqcount = 0; int num_parts; err = split_string(&new_split, &num_parts, substitution, '='); // exit if alias contains an = if (num_parts != 2 || err) { free(substitution); for (int i =0; i < num_parts; i++) { free(new_split[i]); } free(new_split); return ER_FAILURE; } err = add_alias(state, new_split[0], new_split[1]); free(substitution); for (int i =0; i < num_parts; i++) { free(new_split[i]); } free(new_split); return err; } } } if (!strcmp(args[0], "unalias")) { Alias alias; if (num_args == 2) { if (!del_alias(state, args[1])) { return ER_SUCCESS; } } } if (!strcmp(args[0], "run")) { if (num_args == 2) { FILE *file = fopen(args[1], "r"); if (!file) { return ER_FAILURE; } bool temp_repl_state = state->interactive; state->interactive = false; int err; if ((err = repl(state, file))) { perror("Config file error"); return err; } state->interactive = temp_repl_state; fclose(file); return ER_SUCCESS; } } if (!strcmp(args[0], "exit")) { if (num_args == 1) { shutdown(state); return ER_FAILURE; } } return ER_NOT_EXISTS; } int check_aliase(State *state, char **substitution, char *name) { Alias alias; for (int i = 0; i < state->num_aliases; i++) { if (!get_alias(&alias, state, name)) { *substitution = alias.substitution; return ER_SUCCESS; } } return ER_FAILURE; } int expand_command(State *state, int* argc, char** ret_argv[]) { if (*argc == 0) { return ER_FAILURE; } char **argv = *ret_argv; char *substitution; for (int i = 0; i < *argc; i++) { if (!check_aliase(state, &substitution, argv[i])) { strip_char(argv[i], ' '); // copy substitution into argv array *ret_argv[i] = reallocarray(*ret_argv[i], strlen(substitution) + 1, sizeof(char)); strcpy(*ret_argv[0], substitution); // expand substituted piece into the erray splitting over spaces char *joined; char **new_argv; join_string(&joined, *argc, *ret_argv, " "); split_string(&new_argv, argc, joined, ' '); for (int j = 0 ; j < *argc;j ++) { free(*ret_argv[j]); } free(*ret_argv); *ret_argv = new_argv; /***************************************/ } } return ER_SUCCESS; } int run_command(State *state, int argc, char *argv[]) { pid_t child; if (check_builtins(state, argc, argv) == ER_NOT_EXISTS) { if (!execute(0,1, argv, &child)) waitpid(child, NULL, 0); } return ER_SUCCESS; } /* read eval print loop until SIGTERM or end of non-interact file*/ int repl(State* state, FILE *input) { while (true) { char *line; if (state->interactive) { char ** ps1_sep; int num; split_string(&ps1_sep, &num, state->ps1, ' '); run_command(state, num, ps1_sep); for (int i = 0; i < num; i ++) { free(ps1_sep[i]); } free(ps1_sep); line = readline("$ "); if (!line) { printf("EOF\n"); return ER_SUCCESS; } add_history(line); } else { if (get_line(input, &line)) { if (line) free(line); return ER_SUCCESS; } } char **sep_string; int num_words; split_string(&sep_string, &num_words, line, ' '); free(line); if (num_words == 0 // line is empty || sep_string[0][0] == '#') { // line is comment for (int i = 0; i <= num_words; i ++) free(sep_string[i]); free(sep_string); continue; // skip execution } int err = run_pipes(state, num_words, sep_string); if (err == ER_NOT_EXISTS) { expand_command(state, &num_words, &sep_string); run_command(state, num_words, sep_string); } for (int i = 0; i < num_words; i ++) { free(sep_string[i]); } free(sep_string); } } int startup(State *state, int argc, char **argv) { state->output = stdout; state->ps1 = calloc(3, sizeof(char)); strcpy(state->ps1, "pwd"); state->variables = get_growing_array(INITIAL_NUM_VARIABLES, sizeof(char*)); char *config_home; char *config = calloc(500, sizeof(char)); if ((config_home = getenv("XDG_CONFIG_HOME"))) { strcpy(config, config_home); int len = strlen(config_home); strcpy(config + len, "/chicken/chickenrc"); } else { config_home = getenv("HOME"); strcpy(config, config_home); int len = strlen(config_home); strcpy(config + len, "/.chickenrc"); } int err; FILE* rc = fopen(config, "r"); free(config); if (rc) { state->interactive = false; state->input = rc; if ((err = repl(state, rc))) { perror("Config file error"); return err; } fclose(rc); } if (argc == 2) { FILE *script_text = fopen(argv[1], "r"); if (script_text) { state->code = script_text; } else { perror("Failed to load script"); return ER_FAILURE; } } return ER_SUCCESS; } int main(int argc, char** argv) { State *state = calloc(1, sizeof(State)); state->aliases = calloc(INITIAL_NUM_ALIASES, sizeof(struct Alias)); state->num_aliases = 0; int err = startup(state, argc, argv); if (err) { return err; } if (state->code) { state->interactive = false; repl(state, state->code); } else { state->interactive = true; print_chicken(state); repl(state, stdin); } return ER_SUCCESS; }