diff --git a/README.md b/README.md index 0b91b92..d3686bf 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # 42sh - A POSIX shell with a bad name -42sh is a project aiming to implement a POSIX-compliant shell written in C with only the standard library. -Source de is fully documented with the doxygen format so you can easily understand how the project works by exploring it. - -> **Note** This is a school project, therefore it probably won't interest you if you are looking for something useful. +42sh is a shcool project aiming to implement a POSIX compliant shell in C. ## Getting started +TODO + ### Build run this command: `autoreconf --force --verbose --install` @@ -17,43 +16,27 @@ run this command: then: `make` -#### Build with ASan +#### asan run this command: `./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -g -fsanitize=address'` - or for MacOS (Jean Here): `./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -I/opt/homebrew/include' LDFLAGS='-L/opt/homebrew/lib'` - + then: `make check` -## Project status - -### Implemented features - -* **Command Execution:** `$PATH` search and binary execution (via `fork` and `execvp`) with error return code handling. -* **Built-ins:** Native implementation of `echo`, `cd`, `exit`, `export`, `unset`, `set`, `.`, `true`, `false`, as well as loop management with `break` and `continue`. -* **Control Structures:** * Conditions: `if / then / elif / else / fi`. - * Loops: `while`, `until` and `for`. -* **Logical Operators:** Command chaining with `&&`, `||` and negation with `!`. -* **Pipelines and Redirections:** * Full management of pipes `|` to connect the output of one process to the input of another. - * Single and multiple redirections: `>`, `<`, `>>`, `>&`, `<&`, `<>`. -* **Variables Management:** Assignment, variable expansion, and special variables handling like `$?` (return code of the last command). -* **Command Grouping:** Execution blocks `{ ... }` and subshells creation `( ... )`. -* **Quoting:** Support for weak (`"`) and strong (`'`) quoting for special characters escaping. - -## Architecture - -The shell operates on a classic compilation/interpretation pipeline: - -1. **Lexer (Lexical Analysis):** Reads standard input (or script) character by character and generates a stream of "Tokens" (Words, Operators, Redirections). -2. **Parser (Syntax Analysis):** Syntax analyzer that transforms the token stream into a complex Abstract Syntax Tree (AST). This module strictly manages the nesting of control structures and enforces the rigid grammar of the Shell Command Language. -3. **Execution (AST Traversal):** The tree is traversed recursively. Redirections modify file descriptors (`dup2`), child processes are created (`fork`), and commands are executed. - - ## Authors -- Guillem George - Matteo Flebus - Jean Herail - William Valenduc +- Guillem George + +## Project status + +WIP + +## TODO + +# Autotools +implement functions in all .c files to see if everything compiles. diff --git a/src/Makefile.am b/src/Makefile.am index 5f9fa1d..210cac1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,9 +18,9 @@ bin_PROGRAMS = 42sh parser/libparser.a \ lexer/liblexer.a \ io_backend/libio_backend.a \ - utils/libutils.a \ execution/libexecution.a \ - expansion/libexpansion.a + expansion/libexpansion.a \ + utils/libutils.a # ================ TESTS ================ diff --git a/src/execution/execution.c b/src/execution/execution.c index 28726e5..46bbfcb 100644 --- a/src/execution/execution.c +++ b/src/execution/execution.c @@ -9,7 +9,6 @@ #include "../expansion/expansion.h" #include "../utils/hash_map/hash_map.h" -#include "../utils/vars/vars.h" // Refactored: delegates to helpers in execution_helpers.c #include "execution_helpers.h" @@ -19,55 +18,27 @@ int execution(struct ast *ast, struct hash_map *vars) if (!ast) return 0; - int res; switch (ast->type) { case AST_VOID: case AST_END: - res = 0; - break; + return 0; case AST_CMD: { struct ast_command *command = ast_get_command(ast); if (!expand(command, vars)) fprintf(stderr, "Error: Variable expansion failed\n"); - res = exec_ast_command(command, vars); - break; + return exec_ast_command(command, vars); } case AST_IF: - res = exec_ast_if(ast_get_if(ast), vars); - break; + return exec_ast_if(ast_get_if(ast), vars); case AST_LIST: - res = exec_ast_list(ast_get_list(ast), vars); - break; + return exec_ast_list(ast_get_list(ast), vars); case AST_AND_OR: - res = exec_ast_and_or(ast_get_and_or(ast), vars); - break; + return exec_ast_and_or(ast_get_and_or(ast), vars); case AST_LOOP: - res = exec_ast_loop(ast_get_loop(ast), vars); - break; - case AST_SUBSHELL: - res = exec_ast_subshell(ast_get_subshell(ast), vars); - break; + return exec_ast_loop(ast_get_loop(ast), vars); default: - res = 127; - break; - } - - if (res == EXEC_SIGNAL_EXIT) - { - char *exit_val_str = get_var(vars, "EXIT_VALUE"); - if (exit_val_str == NULL) - { - fprintf( - stderr, - "Internal error: could not retrieve return value from exit\n"); - return 2; - } - return atoi(exit_val_str); - } - else - { - return res; + return 127; } } diff --git a/src/execution/execution_helpers.c b/src/execution/execution_helpers.c index 12d4e1f..5157290 100644 --- a/src/execution/execution_helpers.c +++ b/src/execution/execution_helpers.c @@ -14,8 +14,6 @@ #include "../utils/vars/vars.h" #include "execution.h" -// === Static functions - static int open_redir_file(const struct ast_redir *redir, int *flags, int *mode) { *mode = 0644; @@ -123,35 +121,10 @@ static char **list_to_argv(struct list *command_list) return argv; } -/* - * @brief parses string and returns the represented (unsigned) number - * @return the number contained by the string or -1 if number is invalid - */ -static int atou(char *str) -{ - if (str == NULL || *str == '\0') - return -1; - - int result = 0; - size_t i = 0; - while (str[i] != '\0') - { - if (str[i] < '0' || str[i] > '9') - return -1; - - result *= 10; - result += str[i] - '0'; - i++; - } - - return result; -} - static int try_builtin(char **argv, struct hash_map *vars); static int builtin_break(char **argv); static int builtin_continue(char **argv); -static int builtin_unset(char **argv, struct hash_map *vars); static int exec_assignment(struct list *assignment_list, struct hash_map *vars) { @@ -165,18 +138,12 @@ static int exec_assignment(struct list *assignment_list, struct hash_map *vars) struct ast_assignment *assignment = ast_get_assignment(assignment_list->data); - if (assignment->global) - { - setenv(assignment->name, assignment->value, 1); - } set_var_copy(vars, assignment->name, assignment->value); assignment_list = assignment_list->next; } return 0; } -// === Functions - int exec_ast_command(struct ast_command *command, struct hash_map *vars) { exec_assignment(command->assignments, vars); @@ -238,48 +205,24 @@ int exec_ast_if(struct ast_if *if_node, struct hash_map *vars) if (if_node == NULL) return 2; int cond = execution(if_node->condition, vars); - if (cond == EXEC_SIGNAL_BREAK || cond == EXEC_SIGNAL_CONTINUE - || cond == EXEC_SIGNAL_EXIT) + if (cond == EXEC_SIGNAL_BREAK || cond == EXEC_SIGNAL_CONTINUE) return cond; if (cond == 0) { int r = execution(if_node->then_clause, vars); + if (r == EXEC_SIGNAL_BREAK || r == EXEC_SIGNAL_CONTINUE) + return r; return r; } else { int r = execution(if_node->else_clause, vars); + if (r == EXEC_SIGNAL_BREAK || r == EXEC_SIGNAL_CONTINUE) + return r; return r; } } -int exec_ast_subshell(struct ast_subshell *subshell_node, - struct hash_map *vars) -{ - pid_t pid = fork(); - if (pid < 0) - { - perror("fork"); - return 1; - } - - if (pid == 0) - { - int res = execution(subshell_node->child, vars); - _exit(res); - } - - int status = 0; - waitpid(pid, &status, 0); - - if (WIFEXITED(status)) - { - return WEXITSTATUS(status); - } - - return 1; -} - int exec_ast_list(struct ast_list *list_node, struct hash_map *vars) { struct list *cur = list_node->children; @@ -291,8 +234,7 @@ int exec_ast_list(struct ast_list *list_node, struct hash_map *vars) { int child_ret = execution(child, vars); if (child_ret == EXEC_SIGNAL_BREAK - || child_ret == EXEC_SIGNAL_CONTINUE - || child_ret == EXEC_SIGNAL_EXIT) + || child_ret == EXEC_SIGNAL_CONTINUE) return child_ret; ret = child_ret; } @@ -304,14 +246,16 @@ int exec_ast_list(struct ast_list *list_node, struct hash_map *vars) int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars) { int left_ret = execution(ao_node->left, vars); - if (left_ret == EXEC_SIGNAL_BREAK || left_ret == EXEC_SIGNAL_CONTINUE - || left_ret == EXEC_SIGNAL_EXIT) + if (left_ret == EXEC_SIGNAL_BREAK || left_ret == EXEC_SIGNAL_CONTINUE) return left_ret; if (ao_node->type == AST_AND_OR_TYPE_AND) { if (left_ret == 0) { int right_ret = execution(ao_node->right, vars); + if (right_ret == EXEC_SIGNAL_BREAK + || right_ret == EXEC_SIGNAL_CONTINUE) + return right_ret; return right_ret; } return left_ret; @@ -321,6 +265,9 @@ int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars) if (left_ret != 0) { int right_ret = execution(ao_node->right, vars); + if (right_ret == EXEC_SIGNAL_BREAK + || right_ret == EXEC_SIGNAL_CONTINUE) + return right_ret; return right_ret; } return left_ret; @@ -507,21 +454,13 @@ static int builtin_false(char **argv) return 1; } -static int builtin_exit(char **argv, struct hash_map *vars) +static int builtin_exit(char **argv) { int exit_val = 0; if (argv[1]) - { - exit_val = atou(argv[1]); - if (exit_val == -1) - { - fprintf(stderr, "exit: Illegal number %s\n", argv[1]); - return 2; - } - } - - set_var_int(vars, "EXIT_VALUE", exit_val); - return EXEC_SIGNAL_EXIT; + exit_val = atoi(argv[1]); + exit(exit_val); + return exit_val; } static int builtin_cd(char **argv, struct hash_map *vars) @@ -548,23 +487,6 @@ static int builtin_cd(char **argv, struct hash_map *vars) return 0; } -static int builtin_unset(char **argv, struct hash_map *vars) -{ - if (!argv) - return 0; - for (int i = 1; argv[i]; i++) - { - const char *name = argv[i]; - if (name == NULL || name[0] == '\0') - continue; - // remove from shell variables - hash_map_remove(vars, name); - // remove from environment variables - unsetenv(name); - } - return 0; -} - /** * @brief Tries to execute a builtin command if the command matches a builtin * @@ -578,8 +500,6 @@ static int try_builtin(char **argv, struct hash_map *vars) if (strcmp(argv[0], "echo") == 0) return builtin_echo(argv); - if (strcmp(argv[0], "unset") == 0) - return builtin_unset(argv, vars); if (strcmp(argv[0], "true") == 0) return builtin_true(argv); if (strcmp(argv[0], "false") == 0) @@ -589,7 +509,7 @@ static int try_builtin(char **argv, struct hash_map *vars) if (strcmp(argv[0], "continue") == 0) return builtin_continue(argv); if (strcmp(argv[0], "exit") == 0) - return builtin_exit(argv, vars); + return builtin_exit(argv); if (strcmp(argv[0], "cd") == 0) return builtin_cd(argv, vars); diff --git a/src/execution/execution_helpers.h b/src/execution/execution_helpers.h index 8a56e18..530b4dc 100644 --- a/src/execution/execution_helpers.h +++ b/src/execution/execution_helpers.h @@ -7,15 +7,12 @@ // Special execution signals used internally to implement loop control #define EXEC_SIGNAL_CONTINUE (-2) #define EXEC_SIGNAL_BREAK (-3) -#define EXEC_SIGNAL_EXIT (-4) int exec_ast_command(struct ast_command *command, struct hash_map *vars); int exec_ast_if(struct ast_if *if_node, struct hash_map *vars); int exec_ast_list(struct ast_list *list_node, struct hash_map *vars); int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars); int exec_ast_loop(struct ast_loop *loop_node, struct hash_map *vars); -int exec_ast_subshell(struct ast_subshell *subshell_node, - struct hash_map *vars); void unset_all_redir(struct list *redir_list); #endif // EXECUTION_HELPERS_H diff --git a/src/expansion/expansion.c b/src/expansion/expansion.c index e8f8577..4fea985 100644 --- a/src/expansion/expansion.c +++ b/src/expansion/expansion.c @@ -1,6 +1,4 @@ #define _POSIX_C_SOURCE 200809L -#include "expansion.h" - #include #include #include @@ -117,40 +115,6 @@ static bool expand_var(char **str, size_t pos, const struct hash_map *vars) return false; } -size_t parse_subshell_str(char *str, char **res) -{ - size_t i = 1; // skip the '(' - int paren_count = 1; - - if (str[i] == ')') - { - // empty subshell - *res = NULL; - return 0; - } - - while (str[i] != 0) - { - if (str[i] == '(') - paren_count++; - else if (str[i] == ')') - { - paren_count--; - - if (paren_count == 0) - { - *res = strndup(str + 1, i - 1); - return i + 1; - } - } - i++; - } - - // error: parenthesis not closed - *res = NULL; - return 0; -} - bool expand(struct ast_command *command, const struct hash_map *vars) { if (command == NULL) @@ -158,42 +122,34 @@ bool expand(struct ast_command *command, const struct hash_map *vars) char *str; size_t len; - enum quote_state quotes; + bool in_quotes; struct list *l = command->command; while (l != NULL) { - quotes = NO_QUOTE; + in_quotes = false; str = (char *)l->data; len = strlen(str); for (size_t i = 0; str[i] != 0; i++) { - if (str[i] == '\'' || str[i] == '\"') + if (str[i] == '\'') { - if (quotes == NO_QUOTE) - { - quotes = (str[i] == '\'') ? SINGLE_QUOTE : DOUBLE_QUOTE; - } - else if ((quotes == SINGLE_QUOTE && str[i] == '\'') - || (quotes == DOUBLE_QUOTE && str[i] == '\"')) - { - quotes = NO_QUOTE; - } - else - { - // inside the other quote type, do nothing - continue; - } - - // remove quote + // remove single quote + in_quotes = !in_quotes; memmove(str + i, str + i + 1, strlen(str + i + 1) + 1); i--; } - else if (quotes == SINGLE_QUOTE) + else if (in_quotes) { continue; // do nothing } + else if (str[i] == '\"') + { + // remove double quote + memmove(str + i, str + i + 1, strlen(str + i + 1) + 1); + i--; + } else if (str[i] == '$' && str[i + 1] != 0 && !isspace(str[i + 1])) { // variable expansion @@ -205,20 +161,21 @@ bool expand(struct ast_command *command, const struct hash_map *vars) } } - // if (quotes != NO_QUOTE) - // { - // // error: quote not closed - // fprintf(stderr, "Error: quote not closed in string: %s\n", str); - // return false; - // } + if (in_quotes) + { + // error: quote not closed + fprintf(stderr, "Error: quote not closed in string: %s\n", str); + return false; + } if (len != strlen(str)) { char *new_str = realloc(str, strlen(str) + 1); if (new_str == NULL) + { // error: realloc fail return false; - + } l->data = new_str; } diff --git a/src/expansion/expansion.h b/src/expansion/expansion.h index ce22e38..420ed02 100644 --- a/src/expansion/expansion.h +++ b/src/expansion/expansion.h @@ -7,13 +7,6 @@ #include "../utils/ast/ast.h" #include "../utils/hash_map/hash_map.h" -enum quote_state -{ - NO_QUOTE, - SINGLE_QUOTE, - DOUBLE_QUOTE -}; - /** * Parse a variable from a string starting with '$'. * @param str The input string starting with '$'. It must start with '$'. @@ -23,15 +16,6 @@ enum quote_state */ size_t parse_var_name(char *str, char **res); -/** - * Parse a subshell string enclosed in parentheses. - * @param str The input string starting with '('. It must start with '('. - * @param res Pointer to a char pointer that will be set to the extracted - * subshell string. - * @return The number of characters processed in the input string. - */ -size_t parse_subshell_str(char *str, char **res); - /** * Expand variables in an AST command using the provided variable map. * @param command The AST command to expand. diff --git a/src/lexer/lexer_utils.c b/src/lexer/lexer_utils.c index 53c3603..1f40a69 100644 --- a/src/lexer/lexer_utils.c +++ b/src/lexer/lexer_utils.c @@ -76,7 +76,7 @@ static void set_token_keyword(struct token *tok, char *begin, ssize_t size) tok->type = TOKEN_FOR; else if (strncmp(begin, "while", size) == 0 && size == 5) tok->type = TOKEN_WHILE; - else if (strncmp(begin, "until", size) == 0 && size == 5) + else if (strncmp(begin, "until", size) == 0 && size == 4) tok->type = TOKEN_UNTIL; else if (strncmp(begin, "do", size) == 0 && size == 2) tok->type = TOKEN_DO; diff --git a/src/main.c b/src/main.c index cf3b791..02320b9 100644 --- a/src/main.c +++ b/src/main.c @@ -7,9 +7,70 @@ #include "lexer/lexer.h" #include "parser/parser.h" #include "utils/args/args.h" -#include "utils/main_loop/main_loop.h" #include "utils/vars/vars.h" +// === Error codes + +#define SUCCESS 0 +#define ERR_INPUT_PROCESSING 2 +#define ERR_MALLOC 3 +#define ERR_GENERIC 4 + +// === Functions + +/* @brief: frees the hash map. + * @return: always ERR_INPUT_PROCESSING. + */ +static int err_input(struct hash_map **vars, struct lexer_context *ctx) +{ + hash_map_free(vars); + destroy_lexer_context(ctx); + return ERR_INPUT_PROCESSING; +} + +static int main_loop(struct lexer_context *ctx, struct hash_map *vars) +{ + int return_code = SUCCESS; + // init parser + if (!parser_init()) + { + perror("parser initialization failed."); + } + + // Retrieve and build first AST + struct ast *command_ast = get_ast(ctx); + + // Main parse-execute loop + while (command_ast != NULL && command_ast->type != AST_END) + { + if (command_ast->type != AST_VOID) + { + // Execute AST + return_code = execution(command_ast, vars); + + // set $? variable + set_var_int(vars, "?", return_code); + } + + ast_free(&command_ast); + + // Retrieve and build next AST + command_ast = get_ast(ctx); + } + + if (command_ast == NULL) + return err_input(&vars, ctx); + + // === free + + ast_free(&command_ast); + parser_close(); + hash_map_free(&vars); + destroy_lexer_context(ctx); + + return return_code; +} + int main(int argc, char **argv) { struct hash_map *vars = vars_init(); @@ -56,16 +117,7 @@ int main(int argc, char **argv) // init lexer context struct lexer_context *ctx = calloc(1, sizeof(struct lexer_context)); - // init parser - if (!parser_init()) - { - perror("parser initialization failed."); - return err_input(&vars, ctx); - } - return_code = main_loop(ctx, vars); - parser_close(); - return return_code; } diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c index d1b7efa..ae652a1 100644 --- a/src/parser/grammar_advanced.c +++ b/src/parser/grammar_advanced.c @@ -220,18 +220,18 @@ struct ast *parse_while(struct lexer_context *ctx) } POP_TOKEN(); - return parse_loop(ctx, false); + return parse_loop(ctx, true); } struct ast *parse_until(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); - // 'until' + // 'while' if (token->type != TOKEN_UNTIL) { perror( - "Internal error: expected a TOKEN_UNTIL but got a different type"); + "Internal error: expected a TOKEN_WHILE but got a different type"); return NULL; } POP_TOKEN(); diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index 92113fe..d86abb6 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -372,45 +372,28 @@ struct ast *parse_shell_command(struct lexer_context *ctx) struct token *token = PEEK_TOKEN(); struct ast *result = NULL; - // '{' - if (token->type == TOKEN_LEFT_BRACKET) + // Grouping + // '(' or '{' + if (token->type == TOKEN_LEFT_BRACKET || token->type == TOKEN_LEFT_PAREN) { POP_TOKEN(); result = parse_compound_list(ctx); if (result == NULL) return NULL; - // '}' + // ')' or '}' token = PEEK_TOKEN(); - if (token->type == TOKEN_LEFT_BRACKET) + if (token->type == TOKEN_LEFT_BRACKET + || token->type == TOKEN_LEFT_PAREN) { ast_free(&result); - perror("Syntax error: bracket mismatch"); + perror("Syntax error: bracket/parenthesis mismatch"); return NULL; } POP_TOKEN(); return result; } - // '(' - else if (token->type == TOKEN_LEFT_PAREN) - { - POP_TOKEN(); - result = parse_compound_list(ctx); - if (result == NULL) - return NULL; - - // ')' - token = PEEK_TOKEN(); - if (token->type == TOKEN_LEFT_PAREN) - { - ast_free(&result); - perror("Syntax error: parenthesis mismatch"); - return NULL; - } - POP_TOKEN(); - return ast_create_subshell(result); - } else if (is_first(*token, RULE_IF)) { return parse_if_rule(ctx); diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index e40612e..17454d2 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -20,9 +20,7 @@ libutils_a_SOURCES = \ ast/ast_loop.c \ args/args.c \ vars/vars.c \ - main_loop/main_loop.c \ ast/ast_assignment.c \ - ast/ast_subshell.c \ ast/ast_function.c libutils_a_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/utils/ast/ast.c b/src/utils/ast/ast.c index d16a9cb..5baaf6a 100644 --- a/src/utils/ast/ast.c +++ b/src/utils/ast/ast.c @@ -11,7 +11,8 @@ void ast_free(struct ast **node) { if (node == NULL || *node == NULL) { - fprintf(stderr, + fprintf( + stderr, "WARNING: Internal error: failed to free AST node (NULL argument)"); return; } @@ -51,16 +52,14 @@ void ast_free(struct ast **node) case AST_FUNCTION: ast_free_function(ast_get_function(*node)); break; - case AST_SUBSHELL: - ast_free_subshell(ast_get_subshell(*node)); - break; case AST_VOID: case AST_END: break; default: - fprintf(stderr, "WARNING: Internal error:" - " failed to free an AST node (Unknown type)"); + fprintf(stderr, + "WARNING: Internal error: failed to free an AST node (Unknown " + "type)"); return; } @@ -171,4 +170,4 @@ void ast_print_dot(struct ast *ast) fprintf(dot_pipe, "}\n"); pclose(dot_pipe); } - */ + */ \ No newline at end of file diff --git a/src/utils/ast/ast.h b/src/utils/ast/ast.h index 2eac62f..12abee5 100644 --- a/src/utils/ast/ast.h +++ b/src/utils/ast/ast.h @@ -15,6 +15,5 @@ #include "ast_redir.h" #include "ast_void.h" #include "ast_word.h" -#include "ast_subshell.h" #endif /* ! AST_H */ diff --git a/src/utils/ast/ast_base.h b/src/utils/ast/ast_base.h index de7dcfa..e1c7b07 100644 --- a/src/utils/ast/ast_base.h +++ b/src/utils/ast/ast_base.h @@ -18,8 +18,7 @@ enum ast_type AST_NEG, AST_LOOP, AST_ASSIGNMENT, - AST_FUNCTION, - AST_SUBSHELL + AST_FUNCTION }; struct ast diff --git a/src/utils/ast/ast_subshell.c b/src/utils/ast/ast_subshell.c deleted file mode 100644 index 36efd6f..0000000 --- a/src/utils/ast/ast_subshell.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "ast_subshell.h" - -bool ast_is_subshell(struct ast *node) -{ - return node != NULL && node->type == AST_SUBSHELL; -} - -struct ast_subshell *ast_get_subshell(struct ast *node) -{ - if (ast_is_subshell(node)) - return (struct ast_subshell *)node->data; - return NULL; -} - -struct ast *ast_create_subshell(struct ast *child) -{ - struct ast_subshell *subshell = calloc(1, sizeof(struct ast_subshell)); - if (subshell == NULL) - return NULL; - subshell->child = child; - - return ast_create(AST_SUBSHELL, subshell); -} - -void ast_free_subshell(struct ast_subshell *subshell) -{ - if (!subshell) - return; - ast_free(&subshell->child); - free(subshell); -} diff --git a/src/utils/ast/ast_subshell.h b/src/utils/ast/ast_subshell.h deleted file mode 100644 index a4648ef..0000000 --- a/src/utils/ast/ast_subshell.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef AST_SUBSHELL_H -#define AST_SUBSHELL_H - -#include "ast_base.h" - -struct ast_subshell -{ - struct ast *child; -}; - -bool ast_is_subshell(struct ast *node); - -struct ast_subshell *ast_get_subshell(struct ast *node); - -struct ast *ast_create_subshell(struct ast *child); - -void ast_free_subshell(struct ast_subshell *subshell); - -#endif /* ! AST_SUBSHELL_H */ \ No newline at end of file diff --git a/src/utils/hash_map/hash_map.c b/src/utils/hash_map/hash_map.c index 0da4fc4..b07b63d 100644 --- a/src/utils/hash_map/hash_map.c +++ b/src/utils/hash_map/hash_map.c @@ -7,8 +7,6 @@ #include #include -#include "../ast/ast.h" - /* ** Hash the key using FNV-1a 32 bits hash algorithm. */ @@ -38,14 +36,6 @@ static void destroy_pair_list(struct pair_list **p) *p = NULL; } -static void destroy_pair_list_ast(struct pair_list **p) -{ - free((char *)(*p)->key); - ast_free((*p)->value); - free((*p)); - *p = NULL; -} - struct hash_map *hash_map_init(size_t size) { struct hash_map *p = malloc(sizeof(struct hash_map)); @@ -130,29 +120,6 @@ void hash_map_free(struct hash_map **hash_map) } } -void hash_map_free_ast(struct hash_map **hash_map) -{ - struct pair_list *l; - struct pair_list *prev; - - if (hash_map != NULL && *hash_map != NULL) - { - for (size_t i = 0; i < (*hash_map)->size; i++) - { - l = (*hash_map)->data[i]; - while (l != NULL) - { - prev = l; - l = l->next; - destroy_pair_list_ast(&prev); - } - } - free((*hash_map)->data); - free(*hash_map); - *hash_map = NULL; - } -} - void hash_map_foreach(struct hash_map *hash_map, void (*fn)(const char *, const void *)) { diff --git a/src/utils/main_loop/main_loop.c b/src/utils/main_loop/main_loop.c deleted file mode 100644 index 362f640..0000000 --- a/src/utils/main_loop/main_loop.c +++ /dev/null @@ -1,111 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "main_loop.h" - -// === Includes -#include -#include -#include -#include -#include -#include - -#include "../../execution/execution.h" -#include "../../io_backend/io_backend.h" -#include "../../lexer/lexer.h" -#include "../../parser/parser.h" -#include "../args/args.h" -#include "../vars/vars.h" - -// === Functions - -int err_input(struct hash_map **vars, struct lexer_context *ctx) -{ - hash_map_free(vars); - destroy_lexer_context(ctx); - return ERR_INPUT_PROCESSING; -} - -int main_loop(struct lexer_context *ctx, struct hash_map *vars) -{ - int return_code = SUCCESS; - - // Retrieve and build first AST - struct ast *command_ast = get_ast(ctx); - - // Main parse-execute loop - while (command_ast != NULL && command_ast->type != AST_END) - { - if (command_ast->type != AST_VOID) - { - // Execute AST - return_code = execution(command_ast, vars); - - // set $? variable - set_var_int(vars, "?", return_code); - } - - ast_free(&command_ast); - - // Retrieve and build next AST - command_ast = get_ast(ctx); - } - - if (command_ast == NULL) - return err_input(&vars, ctx); - - // === free - - ast_free(&command_ast); - hash_map_free(&vars); - destroy_lexer_context(ctx); - - return return_code; -} - -/* @brief: initializes a lexer context from a command string. - * @return: pointer to the lexer context, or NULL on failure. - */ -static struct lexer_context *lexer_init_from_string(char *command) -{ - // Create a lexer context from the command string - struct lexer_context *ctx = calloc(1, sizeof(struct lexer_context)); - if (ctx == NULL) - return NULL; - - ctx->end_previous_token = strdup(command); - if (ctx->end_previous_token == NULL) - { - free(ctx); - return NULL; - } - ctx->remaining_chars = strlen(command); - - return ctx; -} - -int start_subshell(struct hash_map **parent_vars, char *command) -{ - int fd = fork(); - if (fd < 0) - return ERR_GENERIC; - - else if (fd == 0) // Child process - { - struct lexer_context *ctx = lexer_init_from_string(command); - if (ctx == NULL) - return ERR_MALLOC; - int return_code = main_loop(ctx, *parent_vars); - exit(return_code); - } - else // Parent process - { - int status; - if (waitpid(fd, &status, 0) == -1) - return ERR_GENERIC; - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else - return ERR_GENERIC; - } -} diff --git a/src/utils/main_loop/main_loop.h b/src/utils/main_loop/main_loop.h deleted file mode 100644 index 286c5b0..0000000 --- a/src/utils/main_loop/main_loop.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef MAIN_LOOP_H -#define MAIN_LOOP_H - -#include "../../utils/vars/vars.h" -#include "../../lexer/lexer.h" - -// === Error codes -#define SUCCESS 0 -#define ERR_INPUT_PROCESSING 2 -#define ERR_MALLOC 3 -#define ERR_GENERIC 4 - -/* @brief: main loop called from main. - * @return: exit code. - */ -int main_loop(struct lexer_context *ctx, struct hash_map *vars); - -/* - * @brief: frees the hash map and lexer context. - * @return: ERR_INPUT_PROCESSING. - */ -int err_input(struct hash_map **vars, struct lexer_context *ctx); - -/* - * @brief: starts a subshell and builds the intern lexer context - * from the string. - * @return: exit code of the subshell. - */ -int start_subshell(struct hash_map **parent_vars, char *command); - -#endif /* MAIN_LOOP_H */ \ No newline at end of file diff --git a/tests/unit/expansion/expand.c b/tests/unit/expansion/expand.c index 777083e..859256e 100644 --- a/tests/unit/expansion/expand.c +++ b/tests/unit/expansion/expand.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include #include +#include #include #include @@ -16,7 +17,7 @@ Test(expand, no_expansion) char str[] = "echo something"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); bool ret = expand(ast_command, NULL); @@ -31,7 +32,7 @@ Test(expand, single_quotes_no_expansion) char str[] = "echo '$VAR'"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -50,7 +51,7 @@ Test(expand, single_dollar) char str[] = "echo $ sign"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -69,7 +70,7 @@ Test(expand, empty_braces_no_expansion) char str[] = "echo ${}"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -86,7 +87,7 @@ Test(expand, basic_expansion) char str[] = "echo $VAR"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -105,7 +106,7 @@ Test(expand, multiple_expansion) char str[] = "echo $VAR1 $VAR2 ${VAR3}"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -127,7 +128,7 @@ Test(expand, env_variable) char str[] = "echo $MY_ENV_VAR"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); setenv("MY_ENV_VAR", "environment", 0); @@ -144,7 +145,7 @@ Test(expand, undefined_variable) char str[] = "echo $UNDEFINED"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -162,7 +163,7 @@ Test(expand, nested_expansion) char str[] = "echo $B"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -177,34 +178,34 @@ Test(expand, nested_expansion) hash_map_free(&vars); } -// Test(expand, mixed_quotes_expansion) -// { -// char str[] = "echo \"$VAR1 and '$VAR2'\""; -// char *str_heap = strdup(str); -// struct list *list = list_append(NULL, str_heap); -// struct ast *ast = ast_create_command(list, NULL, NULL); -// struct ast_command *ast_command = ast_get_command(ast); +Test(expand, mixed_quotes_expansion) +{ + char str[] = "echo \"$VAR1 and '$VAR2'\""; + char *str_heap = strdup(str); + struct list *list = list_append(NULL, str_heap); + struct ast *ast = ast_create_command(list); + struct ast_command *ast_command = ast_get_command(ast); -// struct hash_map *vars = vars_init(); -// set_var_copy(vars, "VAR1", "expanded"); -// set_var_copy(vars, "VAR2", "not_expanded"); + struct hash_map *vars = vars_init(); + set_var_copy(vars, "VAR1", "expanded"); + set_var_copy(vars, "VAR2", "not_expanded"); -// bool ret = expand(ast_command, vars); -// cr_expect(ret, "expansion failed with %s", str); -// cr_expect_str_eq((char *)ast_command->command->data, -// "echo \"expanded and $VAR2\"", -// "Variable in double quotes should expand, while variable " -// "in single quotes should not"); -// ast_free(&ast); -// hash_map_free(&vars); -// } + bool ret = expand(ast_command, vars); + cr_expect(ret, "expansion failed with %s", str); + cr_expect_str_eq((char *)ast_command->command->data, + "echo \"expanded and $VAR2\"", + "Variable in double quotes should expand, while variable " + "in single quotes should not"); + ast_free(&ast); + hash_map_free(&vars); +} Test(expand, adjacent_variables) { char str[] = "echo $VAR1$VAR2"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -224,7 +225,7 @@ Test(expand, random) char str[] = "$RANDOM"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); bool ret = expand(ast_command, NULL); @@ -240,7 +241,7 @@ Test(expand, pid) char str[] = "$$"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -258,7 +259,7 @@ Test(expand, default_last_exit_code) char str[] = "$?"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); diff --git a/tests/unit/expansion/parse_subshell.c b/tests/unit/expansion/parse_subshell.c deleted file mode 100644 index 34ada43..0000000 --- a/tests/unit/expansion/parse_subshell.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include - -#include "../../../src/expansion/expansion.h" - -TestSuite(parse_subshell_str); - -Test(parse_subshell_str, basic_subshell) -{ - char *input = "(ls -l)"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 7); - cr_expect_str_eq(extracted_var, "ls -l"); - free(extracted_var); -} - -Test(parse_subshell_str, multi_basic_subshell) -{ - char *input = "(echo hello) and (echo world)"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 12); - cr_expect_str_eq(extracted_var, "echo hello"); - free(extracted_var); - - input += r + 5; // skip " and " - r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 12); - cr_expect_str_eq(extracted_var, "echo world"); - free(extracted_var); -} - -Test(parse_subshell_str, incomplete_braces) -{ - char *input = "(echo hello"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 0); - cr_expect(extracted_var == NULL); -} - -Test(parse_subshell_str, empty_braces) -{ - char *input = "()"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 0); - cr_expect(extracted_var == NULL); -} - -Test(parse_subshell_str, nested_subshell) -{ - char *input = "(echo (nested))"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 15); - cr_expect_str_eq(extracted_var, "echo (nested)"); - free(extracted_var); - - char *nested = input + 6; // point to the nested subshell - r = parse_subshell_str(nested, &extracted_var); - cr_expect(r == 8); - cr_expect_str_eq(extracted_var, "nested"); - free(extracted_var); -} diff --git a/tests/unit/expansion/parse_var.c b/tests/unit/expansion/parse_var.c index 4dc9e08..27a4b94 100644 --- a/tests/unit/expansion/parse_var.c +++ b/tests/unit/expansion/parse_var.c @@ -1,5 +1,6 @@ #include #include +#include #include "../../../src/expansion/expansion.h"