#define _POSIX_C_SOURCE 200809L #include "exec/ast_exec_functions.h" #include #include #include #include #include #include #include #include #include #include "ast/ast_accessors.h" #include "ast/ast_redirect.h" #include "exec/ast_exec_redirs.h" #include "lexer/expansion.h" #include "utils/env.h" #define XCODE_TOENV(R) \ char buf[16] = { 0 }; \ sprintf(buf, "%d", (R)); \ env_set("?", buf) static char **convert_args(struct string **args) { size_t l = 0; while (args[l]) { l++; } char **c_args = calloc(l + 1, sizeof(char *)); if (!c_args) { errx(EXIT_FAILURE, "convert_args: calloc failed."); } for (size_t i = 0; i < l; i++) { c_args[i] = calloc(args[i]->length + 1, sizeof(char)); if (!c_args[i]) { errx(EXIT_FAILURE, "convert_args: calloc failed."); } c_args[i] = strncpy(c_args[i], args[i]->data, args[i]->length); } return c_args; } static void free_args(char **args) { size_t i = 0; while (args[i]) { free(args[i]); i++; } free(args); } static builtin fetch_builtin(struct string *s) { if (!s) { return NULL; } if (STRINGS_ARE_EQUAL(s->data, "false")) { return my_false; } if (STRINGS_ARE_EQUAL(s->data, "true")) { return my_true; } if (STRINGS_ARE_EQUAL(s->data, "echo")) { return echo; } if (STRINGS_ARE_EQUAL(s->data, ".")) { return dot; } if (STRINGS_ARE_EQUAL(s->data, "exit")) { return my_exit; } if (STRINGS_ARE_EQUAL(s->data, "cd")) { return cd; } if (STRINGS_ARE_EQUAL(s->data, "export")) { return export; } return NULL; } static int exec_program(struct string **args) { char **argv = convert_args(args); if (!argv[0]) { free_args(argv); return 127; } pid_t p = fork(); if (!p) { execvp(argv[0], argv); if (errno == ENOEXEC) { fprintf(stderr, "exec_program: the file \'%s\' is not executable.\n", argv[0]); free_args(argv); exit(126); } fprintf(stderr, "exec_program: command \'%s\' not found.\n", argv[0]); free_args(argv); exit(127); } else { int status = 0; waitpid(p, &status, 0); free_args(argv); if (WIFEXITED(status)) { return WEXITSTATUS(status); } return SHELL_FALSE; } } static struct string **_copy_args(struct string **args) { size_t len = 0; while (args[len]) { len++; } struct string **cpy = calloc(len + 1, sizeof(struct string *)); for (size_t i = 0; i < len; i++) { cpy[i] = string_deepcopy(args[i]); } return cpy; } static void _free_str_args(struct string **args) { size_t i = 0; while (args[i]) { string_free(args[i]); i++; } free(args); } int exec_ast_command(struct ast *ast) { union ast_caster ast_cast; ast_cast.ast = ast; struct string **cpy = _copy_args(ast_cast.ast_c->args); if (!ast_cast.ast_c->args) { _free_str_args(cpy); fprintf(stderr, "exec_ast_command: invalid args for a command exec.\n"); XCODE_TOENV(SHELL_FALSE); return SHELL_FALSE; } size_t i = 0; size_t j = 0; while (ast_cast.ast_c->args[i]) { struct string *cv = expand_word(ast_cast.ast_c->args[i]); if (cv->length > 0) { string_free(ast_cast.ast_c->args[j]); ast_cast.ast_c->args[j++] = cv; } else { string_free(cv); } i++; } while (ast_cast.ast_c->args[j]) { string_free(ast_cast.ast_c->args[j]); ast_cast.ast_c->args[j++] = NULL; } if (i == 0) { // No args ! XCODE_TOENV(SHELL_TRUE); return SHELL_TRUE; } builtin f = fetch_builtin(ast_cast.ast_c->args[0]); int r = SHELL_TRUE; if (f != NULL) { r = (*f)(ast_cast.ast_c->args + 1); } else { r = exec_program(ast_cast.ast_c->args); } _free_str_args(ast_cast.ast_c->args); ast_cast.ast_c->args = cpy; XCODE_TOENV(r); return r; } int exec_ast_list(struct ast *ast) { union ast_caster ast_cast; ast_cast.ast = ast; int status = SHELL_TRUE; for (size_t i = 0; i < ast_cast.ast_l->nb_children; i++) { status = eval_ast(get_i(ast_cast.ast, i)); } XCODE_TOENV(status); return status; } int exec_ast_if(struct ast *ast) { int k = SHELL_TRUE; if (eval_ast(get_i(ast, 0)) == SHELL_TRUE) { k = eval_ast(get_i(ast, 1)); } else { k = eval_ast(get_i(ast, 2)); } XCODE_TOENV(k); return k; } int exec_ast_logical(struct ast *ast) { union ast_caster ast_cast; ast_cast.ast = ast; int k = SHELL_TRUE; switch (ast_cast.ast_lo->type) { case TOKEN_NEG: k = !eval_ast(get_left(ast)); break; case TOKEN_AND: if (eval_ast(get_left(ast)) == SHELL_TRUE) { k = eval_ast(get_right(ast)); break; } k = SHELL_FALSE; break; case TOKEN_OR: if (eval_ast(get_left(ast)) == SHELL_FALSE) { k = eval_ast(get_right(ast)); break; } k = SHELL_TRUE; break; default: errx(SHELL_FALSE, "exec_ast_logical: invalid token type for logical operation. Got: " "%d", ast_cast.ast_lo->type); } XCODE_TOENV(k); return k; } int exec_ast_pipe(struct ast *ast) { union ast_caster ast_cast; ast_cast.ast = ast; struct ast *left = ast_cast.ast_p->left; struct ast *right = ast_cast.ast_p->right; if (!left || !right) { XCODE_TOENV(EXIT_FAILURE); return EXIT_FAILURE; } int fds[2]; if (pipe(fds) == -1) { XCODE_TOENV(EXIT_FAILURE); return EXIT_FAILURE; } pid_t p1 = fork(); if (!p1) { close(fds[0]); dup2(fds[1], STDOUT_FILENO); int r1 = eval_ast(left); exit(r1); } pid_t p2 = fork(); if (!p2) { close(fds[1]); dup2(fds[0], STDIN_FILENO); int r2 = eval_ast(right); exit(r2); } close(fds[0]); close(fds[1]); int status = 0; waitpid(p1, &status, 0); waitpid(p2, &status, 0); if (WIFEXITED(status)) { int k = WEXITSTATUS(status); XCODE_TOENV(k); return k; } XCODE_TOENV(SHELL_FALSE); return SHELL_FALSE; } inline static bool _is_a_std_fd(int fd) { return fd >= STDIN_FILENO && fd <= STDERR_FILENO; } static int _exec_ast_redir(struct ast_redirection *ast, int left_fd, int right_fd) { // Save std fds int cpy_stdin = dup(STDIN_FILENO); int cpy_stdout = dup(STDOUT_FILENO); int cpy_stderr = dup(STDERR_FILENO); if (right_fd == CLOSE_FD) { close(left_fd); } else { dup2(right_fd, left_fd); } int r = eval_ast(ast->expr); if (!_is_a_std_fd(right_fd)) { close(right_fd); } if (!_is_a_std_fd(left_fd)) { close(left_fd); } // Restore std fds dup2(cpy_stdin, STDIN_FILENO); dup2(cpy_stdout, STDOUT_FILENO); dup2(cpy_stderr, STDERR_FILENO); XCODE_TOENV(r); return r; } int exec_ast_redirection(struct ast *ast) { union ast_caster ast_cast; ast_cast.ast = ast; int left_fd = 0; int right_fd = 0; find_fds(ast_cast.ast_r->redirect, &left_fd, &right_fd); if (left_fd == BAD_FD || right_fd == BAD_FD) { fprintf(stderr, "redirection: bad fd.\n"); XCODE_TOENV(SHELL_FALSE); return SHELL_FALSE; } int k = _exec_ast_redir(ast_cast.ast_r, left_fd, right_fd); XCODE_TOENV(k); return k; } int exec_ast_while(struct ast *ast) { int k = SHELL_TRUE; while (true) { int r = eval_ast(((struct ast_while *)ast)->cond); if (r != SHELL_TRUE) { break; } k = eval_ast(((struct ast_while *)ast)->body); } XCODE_TOENV(k); return k; } int exec_ast_assign(struct ast *ast) { struct ast_assign *ast_a = (struct ast_assign *)ast; struct string *expanded = expand_word(ast_a->val); env_set(ast_a->name->data, expanded->data); string_free(expanded); XCODE_TOENV(SHELL_TRUE); return SHELL_TRUE; } int exec_ast_for(struct ast *ast) { if (!get_left(ast)) { XCODE_TOENV(SHELL_TRUE); return SHELL_TRUE; } struct string **args = ((struct ast_command *)get_left(ast))->args; struct string *name = ((struct ast_for *)ast)->var; int k = SHELL_TRUE; size_t i = 0; while (args[i]) { struct string *value = args[i]; i++; env_set(name->data, value->data); k = eval_ast(get_right(ast)); } XCODE_TOENV(k); return k; } static int _exec_fork(struct ast *ast) { pid_t child = fork(); if (child == -1) { XCODE_TOENV(SHELL_FALSE); return SHELL_FALSE; } else if (child) { // Parent process int status; // Why did I put 0 last time I used fork(2) waitpid(child, &status, 0); if (!WIFEXITED(status)) { XCODE_TOENV(SHELL_FALSE); return SHELL_FALSE; } return WEXITSTATUS(status); } else { // Child process int ret = eval_ast(ast); // Need to exit or else we will have to exit from the main function // So we will execute the remainder of the ast twice // Which is not what we want exit(ret); } } int exec_ast_subshell(struct ast *ast) { struct ast *sub = get_left(ast); int fork_res = _exec_fork(sub); // TODO:The exit codes might not be the correct one XCODE_TOENV(fork_res); return fork_res; }