diff --git a/.gitignore b/.gitignore index f9b5175..ccbc1e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ # Project specific 42sh -testsuite -debug # Prerequisites *.d @@ -134,51 +132,3 @@ $RECYCLE.BIN/ *.msm *.msp *.lnk - -# Autotools (since it disappeared for some reason) - -Makefile.in -/ar-lib -/mdate-sh -/py-compile -/test-driver -/ylwrap -.deps/ -.dirstamp - -autom4te.cache -/autoscan.log -/autoscan-*.log -/aclocal.m4 -/compile -/config.cache -/config.guess -/config.h.in -/config.log -/config.status -/config.sub -/configure -/configure.scan -/depcomp -/install-sh -/missing -/stamp-h1 - -/libtool -/ltmain.sh -.libs/ - -/texinfo.tex - -m4/libtool.m4 -m4/ltoptions.m4 -m4/ltsugar.m4 -m4/ltversion.m4 -m4/lt~obsolete.m4 - -Makefile -*.svg - - -.venv/ -*.py \ No newline at end of file diff --git a/README.md b/README.md index 0b91b92..bbab8c7 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,39 @@ # 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` + autoreconf --force --verbose --install ### Test run this command: - `./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla'` + ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla' then: - `make` + 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'` - + ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -g -fsanitize=address' 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. - + make check ## Authors -- Guillem George - Matteo Flebus -- Jean Herail +- Jean Hérail - William Valenduc +- Guillem George + +## Project status + +WIP + +## TODO + +# Autotools +implement functions in all .c files to see if everything compiles. diff --git a/build_flemme.sh b/build_flemme.sh deleted file mode 100755 index e16b9f0..0000000 --- a/build_flemme.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# build_only.sh - Automates build for 42sh with pretty output (no tests) - -set -e - -# Colors -GREEN="\033[1;32m" -RED="\033[1;31m" -YELLOW="\033[1;33m" -RESET="\033[0m" - -VERBOSE=0 -if [[ "$1" == "--verbose" ]]; then - VERBOSE=1 -fi - -run_cmd() { - local desc="$1" - shift - if [[ $VERBOSE -eq 1 ]]; then - echo -e "${YELLOW}${desc}...${RESET}" - "$@" - else - echo -ne "${YELLOW}${desc}...${RESET}" - "$@" &> /dev/null && echo -e " ${GREEN}done${RESET}" || { echo -e " ${RED}failed${RESET}"; exit 1; } - fi -} - -run_cmd "Running autoreconf" autoreconf --force --verbose --install - -if [[ "$(uname)" == "Darwin" ]]; then - run_cmd "Configuring for MacOS" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -I/opt/homebrew/include' LDFLAGS='-L/opt/homebrew/lib' -else - run_cmd "Configuring for Linux" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -g -fsanitize=address' -fi - -run_cmd "Cleaning build" make clean - -if [[ $VERBOSE -eq 1 ]]; then - echo -e "${YELLOW}Building...${RESET}" - make -else - echo -ne "${YELLOW}Building...${RESET}" - echo - make -fi diff --git a/check_flemme.sh b/check_flemme.sh deleted file mode 100755 index 0543001..0000000 --- a/check_flemme.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash - -# build_and_test.sh - Automates build and test for 42sh with pretty output - -set -e - -# Colors -GREEN="\033[1;32m" -RED="\033[1;31m" -YELLOW="\033[1;33m" -RESET="\033[0m" - -VERBOSE=0 -if [[ "$1" == "--verbose" ]]; then - VERBOSE=1 -fi - -run_cmd() { - local desc="$1" - shift - if [[ $VERBOSE -eq 1 ]]; then - echo -e "${YELLOW}${desc}...${RESET}" - "$@" - else - echo -ne "${YELLOW}${desc}...${RESET}" - "$@" &> /dev/null && echo -e " ${GREEN}done${RESET}" || { echo -e " ${RED}failed${RESET}"; exit 1; } - fi -} - -run_cmd "Running autoreconf" autoreconf --force --verbose --install - -if [[ "$(uname)" == "Darwin" ]]; then - run_cmd "Configuring for MacOS" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -I/opt/homebrew/include' LDFLAGS='-L/opt/homebrew/lib' -else - run_cmd "Configuring for Linux" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -g -fsanitize=address' -fi - -run_cmd "Cleaning build" make clean -if [[ $VERBOSE -eq 1 ]]; then - echo -e "${YELLOW}Running tests...${RESET}" - make check -else - echo -ne "${YELLOW}Running tests...${RESET}" - echo - make check -fi diff --git a/configure.ac b/configure.ac index 14a03fd..f9684ed 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,8 @@ AC_PROG_CC # List Makefiles in subdirectories AC_CONFIG_FILES([ - Makefile src/Makefile + src/ast/Makefile src/parser/Makefile src/lexer/Makefile src/io_backend/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 5f9fa1d..411f4cb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,4 @@ +# define the subdirectories SUBDIRS = \ parser \ lexer \ @@ -8,8 +9,6 @@ SUBDIRS = \ bin_PROGRAMS = 42sh -42sh_CFLAGS = -std=c99 -Werror -Wall -Wextra -Wvla - 42sh_SOURCES = main.c 42sh_CPPFLAGS = -I%D% @@ -18,45 +17,22 @@ bin_PROGRAMS = 42sh parser/libparser.a \ lexer/liblexer.a \ io_backend/libio_backend.a \ - utils/libutils.a \ + expansion/libexpansion.a \ execution/libexecution.a \ - expansion/libexpansion.a + utils/libutils.a -# ================ TESTS ================ -check: - ../tests/wrap.sh # "$(COVERAGE)" "$(OUTPUT_FILE)" - -# ------------- Unit tests -------------- - -check_PROGRAMS = testsuite - -#testsuite_CFLAGS = $(42sh_CFLAGS) -#testsuite_CFLAGS += $(CRITERION_CFLAGS) - -testsuite_SOURCES = ../tests/unit/utils/utils_tests.c \ - ../tests/unit/expansion/parse_var.c \ - ../tests/unit/utils/args.c \ - ../tests/unit/utils/hash_map.c \ - ../tests/unit/utils/insert_into.c - -# ../tests/unit/lexer/lexer_tests.c -# ../tests/unit/expansion/expand.c -# ../tests/unit/io_backend/io_backend.c - -testsuite_CPPFLAGS = $(42sh_CPPFLAGS) - -testsuite_LDADD = $(42sh_LDADD) -lcriterion - -# ------------- asan debug -------------- - -# check_PROGRAMS += debug - -#debug_CFLAGS = $(42sh_CFLAGS) -#debug_CFLAGS += -fsanitize=address -g - -# debug_SOURCES = $(42sh_SOURCES) -# -# debug_CPPFLAGS = $(42sh_CPPFLAGS) -# -# debug_LDADD = $(42sh_LDADD) +################################################# Test +# +#42sh_asan_SOURCES = main.c +# +#42sh_asan_CPPFLAGS = -I%D% +# +#42sh_asan_LDADD = \ +# ast/lib_asan_ast.a \ +# parser/lib_asan_parser.a \ +# lexer/lib_asan_lexer.a \ +# io_backend/lib_asan_io_backend.a \ +# expansion/lib_asan_expansion.a \ +# execution/lib_asan_execution.a \ +# utils/lib_asan_utils.a diff --git a/src/ast/Makefile.am b/src/ast/Makefile.am new file mode 100644 index 0000000..fa41e3d --- /dev/null +++ b/src/ast/Makefile.am @@ -0,0 +1,9 @@ +lib_LIBRARIES = libast.a + +libast_a_SOURCES = \ + ast.c \ + ast.h + +libast_a_CPPFLAGS = -I$(top_srcdir)/src + +noinst_LIBRARIES = libast.a diff --git a/src/ast/ast.c b/src/ast/ast.c new file mode 100644 index 0000000..e69de29 diff --git a/src/ast/ast.h b/src/ast/ast.h new file mode 100644 index 0000000..4c14043 --- /dev/null +++ b/src/ast/ast.h @@ -0,0 +1,22 @@ +#ifndef AST_H +#define AST_H + + +enum ast_type +{ + AST_NULL = 0, + ATS_IF, + AST_CMD +}; + +union ast_union +{ +}; + +struct ast +{ + enum ast_type type; + union ast_union data; +}; + +#endif /* ! AST_H */ diff --git a/src/execution/Makefile.am b/src/execution/Makefile.am index f061dfc..5a71cbb 100644 --- a/src/execution/Makefile.am +++ b/src/execution/Makefile.am @@ -2,9 +2,7 @@ lib_LIBRARIES = libexecution.a libexecution_a_SOURCES = \ execution.c \ - execution.h \ - execution_helpers.c \ - execution_helpers.h + execution.h libexecution_a_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/execution/execution.c b/src/execution/execution.c index 28726e5..e69de29 100644 --- a/src/execution/execution.c +++ b/src/execution/execution.c @@ -1,73 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "execution.h" - -#include -#include -#include -#include -#include - -#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" - -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; - 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; - } - case AST_IF: - res = exec_ast_if(ast_get_if(ast), vars); - break; - case AST_LIST: - res = exec_ast_list(ast_get_list(ast), vars); - break; - case AST_AND_OR: - res = exec_ast_and_or(ast_get_and_or(ast), vars); - break; - 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; - 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; - } -} diff --git a/src/execution/execution.h b/src/execution/execution.h index c3e0080..e773de0 100644 --- a/src/execution/execution.h +++ b/src/execution/execution.h @@ -1,16 +1,4 @@ #ifndef EXECUTION_H #define EXECUTION_H -#include "../utils/ast/ast.h" -#include "../utils/hash_map/hash_map.h" -#include "../utils/lists/lists.h" - -/** - * @brief Execute the AST - * - * @param ast Pointer to the AST structure - * @return int Execution status code of the last command - */ -int execution(struct ast *ast, struct hash_map *vars); - #endif /* ! EXECUTION_H */ diff --git a/src/execution/execution_helpers.c b/src/execution/execution_helpers.c deleted file mode 100644 index 12d4e1f..0000000 --- a/src/execution/execution_helpers.c +++ /dev/null @@ -1,597 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "execution_helpers.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "../utils/hash_map/hash_map.h" -#include "../utils/lists/lists.h" -#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; - if (redir->type == AST_REDIR_TYPE_GREAT - || redir->type == AST_REDIR_TYPE_CLOBBER) - { - *flags = O_WRONLY | O_CREAT | O_TRUNC; - return open(redir->filename, *flags, *mode); - } - else if (redir->type == AST_REDIR_TYPE_DGREAT) - { - *flags = O_WRONLY | O_CREAT | O_APPEND; - return open(redir->filename, *flags, *mode); - } - else if (redir->type == AST_REDIR_TYPE_LESS) - { - *flags = O_RDONLY; - return open(redir->filename, *flags); - } - return -3; // not a file open -} - -static int set_all_redir(struct list *redir_list) -{ - while (redir_list) - { - struct ast *redir_node = (struct ast *)redir_list->data; - struct ast_redir *redir = ast_get_redir(redir_node); - int target_fd; - if (redir->io_number != -1) - { - target_fd = redir->io_number; - } - else - { - if (redir->type == AST_REDIR_TYPE_LESS - || redir->type == AST_REDIR_TYPE_LESSGREAT - || redir->type == AST_REDIR_TYPE_LESSAND) - { - target_fd = 0; - } - else - { - target_fd = 1; - } - } - redir->saved_fd = dup(target_fd); - int new_fd = -1; - int flags = 0; - int mode = 0644; - if (redir->type == AST_REDIR_TYPE_GREAT - || redir->type == AST_REDIR_TYPE_CLOBBER - || redir->type == AST_REDIR_TYPE_DGREAT - || redir->type == AST_REDIR_TYPE_LESS) - { - new_fd = open_redir_file(redir, &flags, &mode); - if (new_fd == -1) - { - perror("open"); - return -1; - } - } - else if (redir->type == AST_REDIR_TYPE_GREATAND - || redir->type == AST_REDIR_TYPE_LESSAND) - { - new_fd = atoi(redir->filename); - } - if (dup2(new_fd, target_fd) == -1) - { - perror("dup2"); - if (new_fd != -1) - { - close(new_fd); - } - return -1; - } - if (new_fd != -1) - { - close(new_fd); - } - redir_list = redir_list->next; - } - return 0; -} - -static char **list_to_argv(struct list *command_list) -{ - size_t len = 0; - struct list *cur = command_list; - while (cur) - { - len++; - cur = cur->next; - } - char **argv = calloc(len + 1, sizeof(char *)); - if (!argv) - return NULL; - cur = command_list; - for (size_t i = 0; i < len; i++) - { - argv[i] = (char *)cur->data; - cur = cur->next; - } - argv[len] = NULL; - 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) -{ - while (assignment_list != NULL) - { - if (!ast_is_assignment(assignment_list->data)) - { - fprintf(stderr, "list of assignements contains something else"); - return 1; - } - 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); - set_all_redir(command->redirections); - - if (!command || !(command->command)) - { - return 1; - } - - char **argv = list_to_argv(command->command); - if (!argv || !(argv[0])) - { - free(argv); - unset_all_redir(command->redirections); - return 0; - } - - int builtin_ret = try_builtin(argv, vars); - if (builtin_ret != -1) - { - free(argv); - unset_all_redir(command->redirections); - return builtin_ret; - } - - pid_t pid = fork(); - if (pid < 0) - { - perror("fork"); - free(argv); - unset_all_redir(command->redirections); - return 1; - } - - if (pid == 0) - { - execvp(argv[0], argv); - perror("execvp"); - unset_all_redir(command->redirections); - _exit(127); - } - - int status = 0; - waitpid(pid, &status, 0); - free(argv); - unset_all_redir(command->redirections); - - if (WIFEXITED(status)) - { - return WEXITSTATUS(status); - } - - return 1; -} - -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) - return cond; - if (cond == 0) - { - int r = execution(if_node->then_clause, vars); - return r; - } - else - { - int r = execution(if_node->else_clause, vars); - 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; - int ret = 0; - while (cur) - { - struct ast *child = (struct ast *)cur->data; - if (!ast_is_void(child)) - { - int child_ret = execution(child, vars); - if (child_ret == EXEC_SIGNAL_BREAK - || child_ret == EXEC_SIGNAL_CONTINUE - || child_ret == EXEC_SIGNAL_EXIT) - return child_ret; - ret = child_ret; - } - cur = cur->next; - } - return ret; -} - -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) - return left_ret; - if (ao_node->type == AST_AND_OR_TYPE_AND) - { - if (left_ret == 0) - { - int right_ret = execution(ao_node->right, vars); - return right_ret; - } - return left_ret; - } - else - { - if (left_ret != 0) - { - int right_ret = execution(ao_node->right, vars); - return right_ret; - } - return left_ret; - } -} - -void unset_all_redir(struct list *redir_list) -{ - while (redir_list) - { - struct ast *redir_node = (struct ast *)redir_list->data; - struct ast_redir *redir = ast_get_redir(redir_node); - int target_fd; - if (redir->io_number != -1) - { - target_fd = redir->io_number; - } - else - { - if (redir->type == AST_REDIR_TYPE_LESS - || redir->type == AST_REDIR_TYPE_LESSGREAT - || redir->type == AST_REDIR_TYPE_LESSAND) - { - target_fd = 0; - } - else - { - target_fd = 1; - } - } - if (redir->saved_fd != -1) - { - dup2(redir->saved_fd, target_fd); - close(redir->saved_fd); - redir->saved_fd = -1; - } - redir_list = redir_list->next; - } -} - -int exec_ast_loop(struct ast_loop *loop_node, struct hash_map *vars) -{ - int res = 0; - while (execution(loop_node->condition, vars) == 0) - { - res = execution(loop_node->body, vars); - if (res == EXEC_SIGNAL_BREAK) - { - res = 0; - break; - } - else if (res == EXEC_SIGNAL_CONTINUE) - { - res = 0; - continue; - } - } - - return res; -} - -// --- Builtins --- - -static void print_with_escapes(const char *str) -{ - while (*str) - { - if (*str == '\\') - { - str++; - if (*str == 'n') - putchar('\n'); - else if (*str == 't') - putchar('\t'); - else if (*str == '\\') - putchar('\\'); - else if (*str == 'a') - putchar('\a'); - else if (*str == 'b') - putchar('\b'); - else if (*str == 'f') - putchar('\f'); - else if (*str == 'r') - putchar('\r'); - else if (*str == 'v') - putchar('\v'); - else if (*str == 'c') - return; // stop printing - else - { - // unrecognized escape, print "\"" and the char - putchar('\\'); - if (*str) - putchar(*str); - } - } - else - { - putchar(*str); - } - str++; - } -} - -static int builtin_echo(char **argv) -{ - bool newline = true; - bool interpret_escapes = false; - int i = 1; - - // Parse options - while (argv[i] && argv[i][0] == '-') - { - char *opt = argv[i] + 1; // skip "-" - bool valid_option = false; - while (*opt) - { - if (*opt == 'n') - { - newline = false; - valid_option = true; - } - else if (*opt == 'e') - { - interpret_escapes = true; - valid_option = true; - } - else if (*opt == 'E') - { - interpret_escapes = false; - valid_option = true; - } - else - { - // invalid option so euh treat as regular argument - valid_option = false; - break; - } - opt++; - } - if (!valid_option) - break; // stop parsing options - i++; - } - - // Print arguments - for (; argv[i]; i++) - { - if (interpret_escapes) - print_with_escapes(argv[i]); - else - printf("%s", argv[i]); - if (argv[i + 1]) - printf(" "); - } - if (newline) - printf("\n"); - - fflush(stdout); - return 0; -} - -static int builtin_break(char **argv) -{ - (void)argv; - return EXEC_SIGNAL_BREAK; -} - -static int builtin_continue(char **argv) -{ - (void)argv; - return EXEC_SIGNAL_CONTINUE; -} - -static int builtin_true(char **argv) -{ - (void)argv; - return 0; -} - -static int builtin_false(char **argv) -{ - (void)argv; - return 1; -} - -static int builtin_exit(char **argv, struct hash_map *vars) -{ - 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; -} - -static int builtin_cd(char **argv, struct hash_map *vars) -{ - const char *path = argv[1]; - if (!path) - { - path = getenv("HOME"); - if (!path) - { - fprintf(stderr, "cd: HOME not set\n"); - return 1; - } - } - // char *pwd = getcwd("", ""); - char *pwd = get_var_or_env(vars, "PWD"); - if (chdir(path) != 0) - { - perror("cd"); - return 1; - } - set_var_copy(vars, "OLD_PWD", pwd); - set_var_copy(vars, "PWD", path); - 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 - * - * @param argv Array of command arguments - * @return int Exit status of the builtin command, or -1 if not a builtin - */ -static int try_builtin(char **argv, struct hash_map *vars) -{ - if (!argv || !argv[0]) - return 0; - - 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) - return builtin_false(argv); - if (strcmp(argv[0], "break") == 0) - return builtin_break(argv); - if (strcmp(argv[0], "continue") == 0) - return builtin_continue(argv); - if (strcmp(argv[0], "exit") == 0) - return builtin_exit(argv, vars); - if (strcmp(argv[0], "cd") == 0) - return builtin_cd(argv, vars); - - return -1; -} diff --git a/src/execution/execution_helpers.h b/src/execution/execution_helpers.h deleted file mode 100644 index 8a56e18..0000000 --- a/src/execution/execution_helpers.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef EXECUTION_HELPERS_H -#define EXECUTION_HELPERS_H - -#include "../utils/ast/ast.h" -#include "../utils/hash_map/hash_map.h" - -// 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..e69de29 100644 --- a/src/expansion/expansion.c +++ b/src/expansion/expansion.c @@ -1,228 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "expansion.h" - -#include -#include -#include -#include -#include - -#include "../utils/ast/ast.h" -#include "../utils/hash_map/hash_map.h" -#include "../utils/string_utils/string_utils.h" -#include "../utils/vars/vars.h" - -static bool is_var_start_char(char c) -{ - return isalpha(c) || c == '_'; -} - -static bool is_var_char(char c) -{ - return isalnum(c) || c == '_'; -} - -static bool is_special_var_char(char c) -{ - return c == '@' || c == '*' || c == '?' || c == '$' || isdigit(c) - || c == '#'; -} - -size_t parse_var_name(char *str, char **res) -{ - char *brace = NULL; - size_t i = 1; // skip the '$' - - if (str[i] == '{' && str[i + 1] != 0 && str[i + 1] != '}') - { - if (is_special_var_char(str[i + 1]) && str[i + 2] == '}') - { - // Special variable like ${1}, ${?} - *res = strndup(str + i + 1, 1); - return 4; // length of ${X} - } - - brace = str + i; - i++; // skip the '{' - } - else if (is_special_var_char(str[i])) - { - *res = strndup(str + i, 1); - return 2; // length of $X - } - - if (!is_var_start_char(str[i])) - { - // Not a valid variable start - *res = NULL; - return 0; - } - - while (1) - { - if (str[i] == '}' && *brace == '{') - { - *res = strndup(str + 2, i - 2); - return i + 1; - } - else if (!is_var_char(str[i])) - { - if (brace != NULL) - { - // Missing closing '}' - *res = NULL; - return 0; - } - break; - } - i++; - } - - *res = strndup(str + 1, i - 1); - return i; -} - -static bool expand_var(char **str, size_t pos, const struct hash_map *vars) -{ - char *var_name = NULL; - size_t r = parse_var_name(*str + pos, &var_name); - if (r > 0 && var_name != NULL) - { - char *value; - char rnd_str[10]; // max 5 digits + null terminator - if (strcmp(var_name, "RANDOM") == 0) - { - short rnd = short_random(); - snprintf(rnd_str, 10, "%d", rnd); - value = rnd_str; - } - else - { - value = get_var_or_env(vars, var_name); - if (value == NULL) - // Undefined variable: expand to empty string - value = ""; - } - - char *p = insert_into(*str, value, pos, r); - free(var_name); - if (p == NULL) - { - // error: insertion failed - return false; - } - *str = p; - return true; - } - 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) - return false; - - char *str; - size_t len; - enum quote_state quotes; - struct list *l = command->command; - - while (l != NULL) - { - quotes = NO_QUOTE; - str = (char *)l->data; - len = strlen(str); - - for (size_t i = 0; str[i] != 0; i++) - { - if (str[i] == '\'' || 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 - memmove(str + i, str + i + 1, strlen(str + i + 1) + 1); - i--; - } - else if (quotes == SINGLE_QUOTE) - { - continue; // do nothing - } - else if (str[i] == '$' && str[i + 1] != 0 && !isspace(str[i + 1])) - { - // variable expansion - bool r = expand_var(&str, i, vars); - if (r == false || str == NULL) - return false; - - i--; // -1 because loop will increment i - } - } - - // if (quotes != NO_QUOTE) - // { - // // 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; - } - - l = l->next; - } - return true; -} diff --git a/src/expansion/expansion.h b/src/expansion/expansion.h index ce22e38..b10b198 100644 --- a/src/expansion/expansion.h +++ b/src/expansion/expansion.h @@ -1,43 +1,4 @@ #ifndef EXPANSION_H #define EXPANSION_H -#include -#include - -#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 '$'. - * @param res Pointer to a char pointer that will be set to the extracted - * variable name. - * @return The number of characters processed in the input string. - */ -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. - * @param vars The hash map containing variables. - * @return A new AST command with variables expanded, or NULL on error. - */ -bool expand(struct ast_command *command, const struct hash_map *vars); - #endif /* ! EXPANSION_H */ diff --git a/src/io_backend/io_backend.c b/src/io_backend/io_backend.c index 1ce256e..e69de29 100644 --- a/src/io_backend/io_backend.c +++ b/src/io_backend/io_backend.c @@ -1,138 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "io_backend.h" - -#include -#include -#include - -// === Static variables - -#include "../utils/args/args.h" - -static struct iob_context context; -static FILE *input = NULL; -static char *stream_buf = NULL; -static size_t stream_buf_size = 0; -static enum iob_state state = IOB_STATE_NOT_INITIALIZED; - -// === Functions - -int iob_init(struct iob_context *ctx) -{ - if (state != IOB_STATE_NOT_INITIALIZED) - return IOB_ERROR_MODULE_ALREADY_INITIALIZED; - - context = *ctx; - - switch (context.mode) - { - case IOB_MODE_STDIN: - input = stdin; - state = IOB_STATE_READY; - return 0; - - case IOB_MODE_SCRIPT: - if (context.args == NULL) - return IOB_ERROR_BAD_ARG; - input = fopen(context.args, "r"); - if (input == NULL) - return IOB_ERROR_CANNOT_OPEN_FILE; - state = IOB_STATE_READY; - return 0; - - case IOB_MODE_CMD: - if (context.args == NULL) - return IOB_ERROR_BAD_ARG; - state = IOB_STATE_READY; - return 0; - - default: - return IOB_ERROR_BAD_ARG; - } -} - -void iob_close(void) -{ - fclose(input); - if ((context.mode == IOB_MODE_STDIN || context.mode == IOB_MODE_SCRIPT) - && stream_buf != NULL) - { - free(stream_buf); - stream_buf_size = 0; - } - state = IOB_STATE_NOT_INITIALIZED; -} - -ssize_t stream_read(char **stream) -{ - // Check args - if (stream == NULL) - return IOB_ERROR_BAD_ARG; - - // Check env - if (state == IOB_STATE_NOT_INITIALIZED) - return IOB_ERROR_MODULE_NOT_INITIALIZED; - if (state == IOB_STATE_FINISHED) - return 0; - if (state == IOB_STATE_ERROR) - return IOB_ERROR_GENERIC; - - // Use input - if (context.mode == IOB_MODE_STDIN || context.mode == IOB_MODE_SCRIPT) - { - ssize_t nread = getline(&stream_buf, &stream_buf_size, input); - if (nread == -1) - { - state = IOB_STATE_FINISHED; - // MAGNIFICO - // malloc(1); - *stream_buf = EOF; - *stream = stream_buf; - return 1; - } - else if (nread < 0) - state = IOB_STATE_ERROR; - - *stream = stream_buf; - return nread; - } - // Use args - else if (context.mode == IOB_MODE_CMD) - { - *stream = context.args; - size_t len = strlen(context.args); - context.args[len] = EOF; - return len + 1; - } - else - { - *stream = NULL; - return IOB_ERROR_GENERIC; - } -} - -int iob_config_from_args(struct args_options *args, struct iob_context *ctx) -{ - switch (args->type) - { - case INPUT_STDIN: - ctx->mode = IOB_MODE_STDIN; - ctx->args = NULL; - break; - - case INPUT_FILE: - ctx->mode = IOB_MODE_SCRIPT; - ctx->args = (char *)args->input_source; - break; - - case INPUT_CMD: - ctx->mode = IOB_MODE_CMD; - ctx->args = (char *)args->input_source; - break; - - default: - return IOB_ERROR_GENERIC; - } - return 0; -} diff --git a/src/io_backend/io_backend.h b/src/io_backend/io_backend.h index 686d604..d202b1a 100644 --- a/src/io_backend/io_backend.h +++ b/src/io_backend/io_backend.h @@ -1,73 +1,4 @@ #ifndef IO_BACKEND_H #define IO_BACKEND_H -#include - -#include "../utils/args/args.h" - -// Error codes -#define IOB_ERROR_GENERIC -1 -#define IOB_ERROR_BAD_ARG -2 -#define IOB_ERROR_MODULE_NOT_INITIALIZED -3 -#define IOB_ERROR_MODULE_ALREADY_INITIALIZED -4 -#define IOB_ERROR_CANNOT_OPEN_FILE -5 - -enum iob_mode -{ - IOB_MODE_NULL = 0, - IOB_MODE_STDIN, - IOB_MODE_SCRIPT, - IOB_MODE_CMD -}; - -enum iob_state -{ - IOB_STATE_NOT_INITIALIZED, - IOB_STATE_READY, - IOB_STATE_FINISHED, - IOB_STATE_ERROR -}; - -/* @struct iob_context - * @var mode - * @var args contains - * the script name when mode is set to IOB_MODE_SCRIPT, - * the command to execute when mode is set to IOB_MODE_CMD - */ -struct iob_context -{ - enum iob_mode mode; - char *args; -}; - -/** - * @brief Converts struct arg_options to iob_context - * - * @param args The arguments options struct - * @param ctx The IO Backend context struct to populate - * @return int 0 on success, negative value on error - */ -int iob_config_from_args(struct args_options *args, struct iob_context *ctx); - -/* - * @brief Initializes the IO Backend module - * - * @param context contains the input mode and the args - * @return 0 on success, the corresponding error code otherwise - */ -int iob_init(struct iob_context *context); - -/* @brief Closes the opened buffers and the module gracefully - */ -void iob_close(void); - -/* @brief reads at most one line of the input and stores it into *stream - * - * @param stream is a pointer that will be set to a string to parse - * @return the number of read characters if positive, - * zero if finished (reached EOF), - * the error code otherwise - */ -ssize_t stream_read(char **stream); - #endif /* ! IO_BACKEND_H */ diff --git a/src/lexer/Makefile.am b/src/lexer/Makefile.am index 4dad036..dd11411 100644 --- a/src/lexer/Makefile.am +++ b/src/lexer/Makefile.am @@ -2,7 +2,7 @@ lib_LIBRARIES = liblexer.a liblexer_a_SOURCES = \ lexer.c \ - lexer_utils.c + lexer.h liblexer_a_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index fd5764d..e69de29 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -1,218 +0,0 @@ -#include "lexer.h" - -#include -#include -#include -#include -#include -#include - -#include "../io_backend/io_backend.h" -#include "../utils/string_utils/string_utils.h" -#include "lexer_utils.h" - -/* @brief: sets the ctx->current_token to [tok]. - * this function is called by token_peek(). - */ -static void update_current_token(struct token *tok, struct lexer_context *ctx) -{ - ctx->current_token = tok; -} - -/* @brief: frees the last token and sets it to [tok]. - * Also sets ctx->current_token to NULL. - * this function is called by token_pop(). - */ -static void update_previous_token(struct token *tok, struct lexer_context *ctx) -{ - free_token(&ctx->previous_token); - ctx->previous_token = tok; -} -/* @brief: updates the current position in the stream. - * [stream] += [i] - * Also frees the last sent token, and sets it to ctx->current_token. - * Current token is then set to NULL. - * This function is called by token_pop(). - */ -static void save_state(char *stream, ssize_t i, struct lexer_context *ctx) -{ - ctx->remaining_chars -= i; - ctx->end_previous_token = stream + i; - - update_previous_token(ctx->current_token, ctx); - update_current_token(NULL, ctx); -} - -/* - * @brief: Updates the lexing_mode to LEXER_NORMAL - * if the SECOND quote is found at stream[i]. - * Updates the lexing_mode to the corresponding quote type - * if the FIRST quote of any type is found. - * - * @return: true if an update was done. false otherwise. - */ -static bool update_lexing_mode(char *stream, ssize_t i, - enum lexing_mode *lexing_mode) -{ - enum lexing_mode mode_before_update = *lexing_mode; - - // FIRST quote - if (*lexing_mode == LEXER_NORMAL) - { - if (stream[i] == '"') - *lexing_mode = LEXER_DOUBLE_QUOTE; - - if (stream[i] == '\'') - *lexing_mode = LEXER_QUOTE; - } - - // SECOND quote - else - { - if (*lexing_mode == LEXER_QUOTE && stream[i] == '\'') - *lexing_mode = LEXER_NORMAL; - if (*lexing_mode == LEXER_DOUBLE_QUOTE && stream[i] == '"') - *lexing_mode = LEXER_NORMAL; - } - - return *lexing_mode != mode_before_update; -} - -/* @brief: updates the flags only_digits and has_equal. - * according to the character at stream[i]. - */ -static void update_flags(char *stream, ssize_t i, struct token_info *info) -{ - if (stream[i] == '=' && !info->has_equal) - { - if (i == 0) - { - perror("Syntax error: word start with a '='"); - return; - } - else - info->has_equal = true; - } - else if (!isdigit(stream[i]) && info->only_digits) - { - info->only_digits = false; - } -} - -struct token *peek_token(struct lexer_context *ctx) -{ - stream_init(ctx); - - // Usefull to know if we are inside a quote or double quote - enum lexing_mode lexing_mode = LEXER_NORMAL; - struct token_info info = { true, 0 }; - char *stream = ctx->end_previous_token; - ssize_t i = 0; - - // we already created the upcoming token during the previous call to peek() - if (ctx->current_token != NULL) - { - return ctx->current_token; - } - - while (i < ctx->remaining_chars) - { - // true if we didn't encounter a quote of any type at stream[i] - // AND we are not inside quotes - if (!update_lexing_mode(stream, i, &lexing_mode) - && lexing_mode == LEXER_NORMAL) - { - update_flags(stream, i, &info); - if (is_special_char(stream, i)) - { - if (i == 0) // where we create spe_char token - i += len_op_sepchar(stream, i); - break; - } - if (isblank(stream[i])) - { - break; - } - } - else if (stream[i] == EOF) - { - fprintf(stderr, "Lexing error: unmatched quote\n"); - - // error handling - return NULL; - } - i++; - } - - struct token *tok = new_token(stream, i, &info); - // if token is comment, we don't want it - - if (tok->type == TOKEN_COMMENT) - { - // Find next newline or EOF. - go_end_of_line(ctx); - - free_token(&tok); - tok = peek_token(ctx); - } - update_current_token(tok, ctx); - return tok; -} - -struct token *pop_token(struct lexer_context *ctx) -{ - stream_init(ctx); - - // Usefull to know if we are inside a quote or double quote - enum lexing_mode lexing_mode = LEXER_NORMAL; - struct token_info info = { true, 0 }; - char *stream = ctx->end_previous_token; - ssize_t i = 0; - - if (ctx->current_token != NULL && ctx->current_token->type == TOKEN_EOF) - { - // we reached end of input, frees all the token still allocated. - free_token(&ctx->previous_token); - free_token(&ctx->current_token); - return NULL; - } - - while (i < ctx->remaining_chars) - { - // true if we didn't encounter a quote of any type at stream[i] - // AND we are not inside quotes - if (!update_lexing_mode(stream, i, &lexing_mode) - && lexing_mode == LEXER_NORMAL) - { - update_flags(stream, i, &info); - if (is_special_char(stream, i)) - { - if (i == 0) // where we create spe_char token - i += len_op_sepchar(stream, i); - break; - } - if (isblank(stream[i])) - { - break; - } - } - else if (stream[i] == EOF) - { - fprintf(stderr, "Lexing error: unmatched quote\n"); - - // error handling - return NULL; - } - i++; - } - - // just in case peek() was not called before poping. - // (this should never happen) - if (ctx->current_token == NULL) - { - ctx->current_token = new_token(stream, i, &info); - } - save_state(stream, i, ctx); - - return ctx->previous_token; -} diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index 8c5b93e..9e7cd67 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -1,37 +1,4 @@ #ifndef LEXER_H #define LEXER_H -#include - -#include "lexer_utils.h" - -/* - * @brief returns true if token is a redir type, false otherwise - */ -bool is_token_redir(struct token *token); - -/* - * @brief: returns the next (newly allocated) token without consuming it. - * if end of input is reached, returns a token of type TOKEN_EOF. - */ -struct token *peek_token(struct lexer_context *ctx); - -/* - * @brief: returns the next (newly allocated) token and consumes it. - * if end of input is reached, returns a token of type TOKEN_EOF. - * It also frees the last token created if there was one. - * - * @warning: if the last returned token was a token EOF, it frees it - * and returns NULL. This means that after peeking a token EOF - * in the parser, there must be EXACTLY ONE call to pop_token(). - */ -struct token *pop_token(struct lexer_context *ctx); - -/* @note: maybe usefull for subshells. - * - * @warning: NOT IMPLEMENTED. - */ - -struct token *get_token_str(void); - #endif /* ! LEXER_H */ diff --git a/src/lexer/lexer_utils.c b/src/lexer/lexer_utils.c deleted file mode 100644 index 53c3603..0000000 --- a/src/lexer/lexer_utils.c +++ /dev/null @@ -1,351 +0,0 @@ -#include "lexer_utils.h" - -#include -#include - -#include "../io_backend/io_backend.h" -#include "../utils/string_utils/string_utils.h" - -/* @brief: if a special character is found at [begin], - * [tok->token_type] is set accordingly - */ -static void set_token_spechar(struct token *tok, char *begin, ssize_t size) -{ - if (size != 1) - return; - switch (begin[0]) - { - case EOF: - tok->type = TOKEN_EOF; - break; - case ';': - tok->type = TOKEN_SEMICOLON; - break; - case '\n': - tok->type = TOKEN_NEWLINE; - break; - case '`': - tok->type = TOKEN_GRAVE; - break; - case '#': - tok->type = TOKEN_COMMENT; - break; - case '\\': - tok->type = TOKEN_BACKSLASH; - break; - case '(': - tok->type = TOKEN_LEFT_PAREN; - break; - case ')': - tok->type = TOKEN_RIGHT_PAREN; - break; - case '{': - tok->type = TOKEN_LEFT_BRACKET; - break; - case '}': - tok->type = TOKEN_RIGHT_BRACKET; - break; - case '*': - tok->type = TOKEN_STAR; - break; - default: - break; - } -} - -/* @brief: if a keyword is found at [begin], - * [tok->token_type] is set accordingly - */ -static void set_token_keyword(struct token *tok, char *begin, ssize_t size) -{ - if (tok->type != TOKEN_NULL || size == 0) - return; - if (strncmp(begin, "!", size) == 0 && size == 1) - tok->type = TOKEN_NEGATION; - else if (strncmp(begin, "if", size) == 0 && size == 2) - tok->type = TOKEN_IF; - else if (strncmp(begin, "fi", size) == 0 && size == 2) - tok->type = TOKEN_FI; - else if (strncmp(begin, "then", size) == 0 && size == 4) - tok->type = TOKEN_THEN; - else if (strncmp(begin, "else", size) == 0 && size == 4) - tok->type = TOKEN_ELSE; - else if (strncmp(begin, "elif", size) == 0 && size == 4) - tok->type = TOKEN_ELIF; - else if (strncmp(begin, "for", size) == 0 && size == 3) - 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) - tok->type = TOKEN_UNTIL; - else if (strncmp(begin, "do", size) == 0 && size == 2) - tok->type = TOKEN_DO; - else if (strncmp(begin, "done", size) == 0 && size == 4) - tok->type = TOKEN_DONE; - else if (strncmp(begin, "export", size) == 0 && size == 6) - tok->type = TOKEN_EXPORT; - - // no keywords found. - if (tok->type == TOKEN_NULL) - return; - - tok->data = calloc(size + 1, sizeof(char)); - if (tok->data == NULL) - { - perror("could not allocate memory in lexer"); - return; - } - strncpy(tok->data, begin, size); -} - -/* @brief: if an operator is found at [begin], - * [tok->token_type] is set accordingly - */ -static void set_token_operator(struct token *tok, char *begin, ssize_t size) -{ - if (tok->type != TOKEN_NULL) - return; - if (strncmp(begin, "&&", size) == 0 && size == 2) - { - tok->type = TOKEN_AND; - } - else if (strncmp(begin, "||", size) == 0 && size == 2) - { - tok->type = TOKEN_OR; - } - - else if (strncmp(begin, ">", size) == 0 && size == 1) - { - tok->type = TOKEN_REDIR_RIGHT; - } - else if (strncmp(begin, "<", size) == 0 && size == 1) - { - tok->type = TOKEN_REDIR_LEFT; - } - else if (strncmp(begin, ">>", size) == 0 && size == 2) - { - tok->type = TOKEN_REDIR_DOUBLE_RIGHT; - } - else if (strncmp(begin, ">&", size) == 0 && size == 2) - { - tok->type = TOKEN_REDIR_RIGHT_AMP; - } - else if (strncmp(begin, ">|", size) == 0 && size == 2) - { - tok->type = TOKEN_REDIR_RIGHT_PIPE; - } - else if (strncmp(begin, "<&", size) == 0 && size == 2) - { - tok->type = TOKEN_REDIR_LEFT_AMP; - } - else if (strncmp(begin, "<>", size) == 0 && size == 2) - { - tok->type = TOKEN_REDIR_LEFT_RIGHT; - } - else if (strncmp(begin, "|", size) == 0 && size == 1) - { - tok->type = TOKEN_PIPE; - } -} - -/* @brief: if token_type has not yet been set, then it is a TOKEN_WORD - * Also allocates the data and fills it. - */ -static void set_token_word(struct token *tok, char *begin, ssize_t size) -{ - if (tok->type == TOKEN_NULL && size != 0) - { - tok->type = TOKEN_WORD; - tok->data = calloc(size + 1, sizeof(char)); - if (tok->data == NULL) - return; - strncpy(tok->data, begin, size); - } -} - -/* @brief: Sets the token to an assignment_word - * Also allocates the data and fills it. - */ -static void set_token_assignment(struct token *tok, char *begin, ssize_t size) -{ - if (tok->type == TOKEN_NULL && size != 0) - { - tok->type = TOKEN_ASSIGNMENT_WORD; - tok->data = calloc(size + 1, sizeof(char)); - if (tok->data == NULL) - return; - strncpy(tok->data, begin, size); - } -} - -/* @brief: Sets the token to an IO number - * Also allocates the data and fills it. - */ -static void set_token_ION(struct token *tok, char *begin, ssize_t size) -{ - if (tok->type == TOKEN_NULL && size != 0) - { - tok->type = TOKEN_IONUMBER; - tok->data = calloc(size + 1, sizeof(char)); - if (tok->data == NULL) - return; - strncpy(tok->data, begin, size); - } -} - -/* @brief: check if [c] is a delimiter for end of line. - * @return: true if [c] == '\n' or EOF. false otherwise. - */ -static bool is_end_of_line(char c) -{ - return c == EOF || c == '\n'; -} - -// === Functions - -bool is_token_redir(struct token *token) -{ - switch (token->type) - { - case TOKEN_REDIR_LEFT: - case TOKEN_REDIR_RIGHT: - case TOKEN_REDIR_LEFT_RIGHT: - case TOKEN_REDIR_DOUBLE_RIGHT: - case TOKEN_REDIR_LEFT_AMP: - case TOKEN_REDIR_RIGHT_AMP: - case TOKEN_REDIR_RIGHT_PIPE: - return true; - default: - return false; - } -} - -bool is_special_char(char *stream, ssize_t i) -{ - char c = stream[i]; - if (c == EOF) - return true; - if (i > 0 && c == '#' && stream[i - 1] == '$') - return false; // the edge case of $# - if (i > 0 && stream[i - 1] == '\\') - return false; // TODO handle backslash better - // this doesnt work, ex : echo \\#comment - // (need to count the previous consequtive backslashes) - - char special_chars[] = "\n'\"`;#|&(){}<>*"; - return strchr(special_chars, c) != NULL; -} - -struct token *new_token(char *begin, ssize_t size, struct token_info *info) -{ - struct token *tok = calloc(1, sizeof(struct token)); - if (tok == NULL) - return NULL; - - if (info->only_digits) - set_token_ION(tok, begin, size); - else if (info->has_equal) - set_token_assignment(tok, begin, size); - else - { - set_token_keyword(tok, begin, size); - set_token_operator(tok, begin, size); - set_token_spechar(tok, begin, size); - set_token_word(tok, begin, size); - } - - return tok; -} - -void destroy_lexer_context(struct lexer_context *ctx) -{ - struct token *prev = ctx->previous_token; - struct token *cur = ctx->current_token; - if (ctx == NULL) - return; - if (prev != NULL) - free_token(&prev); - if (cur != NULL) - free_token(&cur); - free(ctx); -} - -void free_token(struct token **tok) -{ - if (tok == NULL || *tok == NULL) - return; - if ((*tok)->data != NULL) - free((*tok)->data); - free(*tok); - *tok = NULL; -} - -void stream_init(struct lexer_context *ctx) -{ - char *stream; - - if (ctx->remaining_chars == 0) // at the begining - { - ctx->remaining_chars = stream_read(&stream); - } - else - { - stream = ctx->end_previous_token; - } - - char *trimed_stream = trim_blank_left(stream); - ctx->remaining_chars -= trimed_stream - stream; - - ctx->end_previous_token = trimed_stream; -} - -ssize_t len_op_sepchar(char *stream, ssize_t i) -{ - if (!is_special_char(stream, i)) - return -1; // should never happen - - // OR - if (stream[i] == '|' && stream[i + 1] == '|') - return 2; - - // AND - if (stream[i] == '&' && stream[i + 1] == '&') - return 2; - - // special chars - if (stream[i] != '>' && stream[i] != '<') - return 1; - - // REDIRS - - if (stream[i] == '<') - { - if (stream[i + 1] == '&' || stream[i + 1] == '>') - return 2; // <&, <> - } - else if (stream[i + 1] == '>' || stream[i + 1] == '|' - || stream[i + 1] == '&') - return 2; // >>, >&, >| - - return 1; // >, < -} - -void go_end_of_line(struct lexer_context *ctx) -{ - if (ctx == NULL || ctx->end_previous_token == NULL) - return; - - ssize_t i = 0; - while (!is_end_of_line(ctx->end_previous_token[i])) - { - i++; - } - ctx->end_previous_token += i; - ctx->remaining_chars -= i; -} - -void get_next_stream(struct lexer_context *ctx) -{ - ctx->remaining_chars = 0; - stream_init(ctx); -} diff --git a/src/lexer/lexer_utils.h b/src/lexer/lexer_utils.h deleted file mode 100644 index 32b3793..0000000 --- a/src/lexer/lexer_utils.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef LEXER_UTILS_H -#define LEXER_UTILS_H - -#include -#include -#include - -struct lexer_context -{ - char *end_previous_token; - ssize_t remaining_chars; - - struct token *previous_token; - struct token *current_token; -}; - -/* @brief: frees all fields of ctx and sets ctx to NULL. - */ -void destroy_lexer_context(struct lexer_context *ctx); - -enum lexing_mode -{ - LEXER_NORMAL, - LEXER_QUOTE, - LEXER_DOUBLE_QUOTE -}; - -enum token_type -{ - // Blanks - TOKEN_NULL = 0, - TOKEN_EOF, - TOKEN_NEWLINE, - - // words - TOKEN_WORD, - TOKEN_ASSIGNMENT_WORD, - - // Special characters - TOKEN_GRAVE, - TOKEN_SEMICOLON, - TOKEN_COMMENT, - TOKEN_STAR, - TOKEN_BACKSLASH, - TOKEN_DOLLAR, - TOKEN_LEFT_PAREN, - TOKEN_RIGHT_PAREN, - TOKEN_LEFT_BRACKET, - TOKEN_RIGHT_BRACKET, - TOKEN_PIPE, - TOKEN_NEGATION, - - // Redirections - TOKEN_REDIR_LEFT, - TOKEN_REDIR_RIGHT, - TOKEN_REDIR_LEFT_RIGHT, - TOKEN_REDIR_DOUBLE_RIGHT, - TOKEN_REDIR_LEFT_AMP, - TOKEN_REDIR_RIGHT_AMP, - TOKEN_REDIR_RIGHT_PIPE, - - TOKEN_IONUMBER, - - // Keywords - TOKEN_IF, - TOKEN_THEN, - TOKEN_ELSE, - TOKEN_FI, - TOKEN_ELIF, - TOKEN_AND, - TOKEN_OR, - TOKEN_FOR, - TOKEN_WHILE, - TOKEN_UNTIL, - TOKEN_CASE, - TOKEN_EXPORT, - TOKEN_DO, - TOKEN_DONE -}; - -struct token -{ - enum token_type type; - char *data; -}; - -// used to give info from lexing when creating a new token. -struct token_info -{ - // usefull to detect IO numbers. - // tells us if we only lexed digits in current token. - bool only_digits; - - // usefull to detect assignments, and syntax errors with '='. - bool has_equal; -}; - -/* @return: true if a special character from the grammar was found at stream[i], - * false otherwise. - */ -bool is_special_char(char *stream, ssize_t i); - -/* @brief: return a newly allocated token, with the type corresponding - * to the info given in arguments. - * The data contains [size] char, starting from [begin]. - * - * @return: NULL on error, a token otherwise. - */ -struct token *new_token(char *begin, ssize_t size, struct token_info *info); - -/* @brief: frees the token given in argument - */ -void free_token(struct token **tok); - -/* - * @brief: checks if the stream used for the last token creation is empty. - * If it is, it calls stream_read() from IO_backend, - * and sets [remaining_chars]. - * If not, it starts from the end of the last token. - * Also trims left blanks before returning. - * - * @return: char* stream from which we tokenise. - */ -void stream_init(struct lexer_context *ctx); - -/* @brief: finds the next '\n' or EOF character, - * starting at [ctx->end_previous_token], - * and updates the stream and remaining_chars accordingly. - * - * @note: Daft Punk. bang. - */ -void go_end_of_line(struct lexer_context *ctx); - -/* @brief: this function is called when we found a special character - * in the stream. This can either be an operator (ig '>>' or '<&' etc), - * or a special char (ig '\' or '#' etc). - * @return: the length of the operator/special char found (can be 1, 2 or 3). - * -1 on error. - */ -ssize_t len_op_sepchar(char *stream, ssize_t i); - -/* @brief: drops the current stream and asks IOB for a new one - */ -void get_next_stream(struct lexer_context *ctx); - -#endif /* LEXER_UTILS_H */ diff --git a/src/main.c b/src/main.c index cf3b791..e05c6f6 100644 --- a/src/main.c +++ b/src/main.c @@ -1,71 +1,6 @@ -// === Includes -#include -#include - -#include "execution/execution.h" -#include "io_backend/io_backend.h" -#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" +// all includes int main(int argc, char **argv) { - struct hash_map *vars = vars_init(); - if (vars == NULL) - { - fprintf(stderr, "Error: Failed to initialize variables hash map\n"); - return ERR_MALLOC; - } - - // Create the options struct (with argument handler) - struct args_options options; - int return_code = args_handler(argc, argv, &options, vars); - if (return_code != 0) - { - print_usage(stderr, argv[0]); - return err_input(&vars, NULL); - } - // args_print(&options); - - // Initialize variables hash map - - // Create the IO-Backend context struct - struct iob_context io_context = { 0 }; - - // Convert args_options to iob_context - return_code = iob_config_from_args(&options, &io_context); - if (return_code != 0) - { - fprintf(stderr, - "Error: Failed to configure IO Backend from arguments\n"); - return err_input(&vars, NULL); - } - - // Init IO Backend (with the context struct) - return_code = iob_init(&io_context); - if (return_code != 0) - { - fprintf(stderr, - "Error: IO Backend initialization failed with code %d\n", - return_code); - return err_input(&vars, NULL); - } - - // 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; + return 0; } diff --git a/src/parser/Makefile.am b/src/parser/Makefile.am index 642414b..dd13901 100644 --- a/src/parser/Makefile.am +++ b/src/parser/Makefile.am @@ -2,9 +2,7 @@ lib_LIBRARIES = libparser.a libparser_a_SOURCES = \ parser.c \ - grammar.c \ - grammar_basic.c \ - grammar_advanced.c + parser.h libparser_a_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/parser/grammar.c b/src/parser/grammar.c deleted file mode 100644 index e2a4887..0000000 --- a/src/parser/grammar.c +++ /dev/null @@ -1,299 +0,0 @@ -// === Includes -#include "grammar.h" - -#include -#include - -#include "grammar_basic.h" - -// === Static variables - -// rule-indexed array containing firsts -static struct firsts_list *firsts_map = NULL; - -// === Static functions - -/* @brief Add a token to a rule's firsts (in firsts_map) - * - * @arg rule the rule to which add a first - * @arg token the token to add to the rule's firsts - * @return true on success, false on error - */ -static bool add_first(enum rule rule, enum token_type token) -{ - struct firsts_list *item = &firsts_map[rule]; - if (item->tokens != NULL) - { - // Check for duplicates - for (size_t i = 0; i < item->list_length; i++) - { - if (item->tokens[i] == token) - return true; - } - - // Append - item->list_length++; - item->tokens = realloc(item->tokens, - (item->list_length) * sizeof(enum token_type)); - } - else - { - // Create entry - item->list_length = 1; - item->tokens = calloc(1, sizeof(enum token_type)); - } - - // Check for alloc error - if (item->tokens == NULL) - { - item->list_length = 0; - return false; - } - - // Fill - item->tokens[item->list_length - 1] = token; - - return true; -} - -/* @brief Add a list of tokens to a rule's firsts (in firsts_map) - * - * @arg rule the rule to which add a first - * @arg tokens_list the list of tokens to add to the rule's firsts - * @return true on success, false on error - */ -static bool add_firsts(enum rule rule, struct firsts_list *tokens_list) -{ - for (size_t i = 0; i < tokens_list->list_length; i++) - { - bool res = add_first(rule, tokens_list->tokens[i]); - if (!res) - return false; - } - return true; -} - -/* @brief initializes the firsts_map static variable (does not populate it) - * @return true on success, false on error - */ -static bool init_firsts_map(void) -{ - firsts_map = calloc(NUMBER_OF_RULES, sizeof(struct firsts_list)); - if (firsts_map == NULL) - { - perror("Internal error: couldn't create the firsts_map (is your memory " - "full ?)"); - return false; - } - - return true; -} - -static void add_first_redir(void) -{ - add_first(RULE_REDIRECTION, TOKEN_IONUMBER); - add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_RIGHT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_DOUBLE_RIGHT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_AMP); - add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_AMP); - add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_PIPE); -} - -// Adds only direct tokens to rules firsts into the firsts map -static void add_firsts_tokens(void) -{ - // Redirection - add_first_redir(); - // %RIP Matteo 30/01/2026 - // %RAX Guillem 30/01/2026 hehe - - // If - add_first(RULE_IF, TOKEN_IF); - - // Else clause - add_first(RULE_ELSE_CLAUSE, TOKEN_ELSE); - add_first(RULE_ELSE_CLAUSE, TOKEN_ELIF); - - // For - add_first(RULE_FOR, TOKEN_FOR); - - // While - add_first(RULE_WHILE, TOKEN_WHILE); - - // Until - add_first(RULE_UNTIL, TOKEN_UNTIL); - - // Case - add_first(RULE_CASE, TOKEN_CASE); - - // Case item - add_first(RULE_CASE_ITEM, TOKEN_LEFT_PAREN); - add_first(RULE_CASE_ITEM, TOKEN_WORD); - - // Shell command - add_first(RULE_SHELL_COMMAND, TOKEN_LEFT_BRACKET); - add_first(RULE_SHELL_COMMAND, TOKEN_LEFT_PAREN); - - // Simple command - add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD); - add_first(RULE_SIMPLE_COMMAND, TOKEN_EXPORT); - - // Element - add_first(RULE_ELEMENT, TOKEN_WORD); - add_first(RULE_ELEMENT, TOKEN_ASSIGNMENT_WORD); - - // Prefix - add_first(RULE_PREFIX, TOKEN_ASSIGNMENT_WORD); - - // Pipeline - add_first(RULE_PIPELINE, TOKEN_WORD); - - // Compound list - add_first(RULE_COMPOUND_LIST, TOKEN_NEWLINE); - - // Input - add_first(RULE_INPUT, TOKEN_NEWLINE); - add_first(RULE_INPUT, TOKEN_EOF); - - // Funcdec - add_first(RULE_FUNCDEC, TOKEN_WORD); -} - -// Adds only firsts that depend on other rules to the firsts map -// WARNING order matters -static void add_firsts_rec(void) -{ - // Case clause - add_firsts(RULE_CASE_CLAUSE, first(RULE_CASE_ITEM)); - - // Element - add_firsts(RULE_ELEMENT, first(RULE_REDIRECTION)); - - // Prefix - add_firsts(RULE_PREFIX, first(RULE_REDIRECTION)); - - // Shell command - add_firsts(RULE_SHELL_COMMAND, first(RULE_IF)); - add_firsts(RULE_SHELL_COMMAND, first(RULE_FOR)); - add_firsts(RULE_SHELL_COMMAND, first(RULE_WHILE)); - add_firsts(RULE_SHELL_COMMAND, first(RULE_UNTIL)); - add_firsts(RULE_SHELL_COMMAND, first(RULE_CASE)); - - // Simple command - add_firsts(RULE_SIMPLE_COMMAND, first(RULE_PREFIX)); - - // Command - add_firsts(RULE_COMMAND, first(RULE_SIMPLE_COMMAND)); - add_firsts(RULE_COMMAND, first(RULE_SHELL_COMMAND)); - add_firsts(RULE_COMMAND, first(RULE_FUNCDEC)); - - // Pipeline - add_firsts(RULE_PIPELINE, first(RULE_COMMAND)); - - // And Or - add_firsts(RULE_AND_OR, first(RULE_PIPELINE)); - - // Compound list - add_firsts(RULE_COMPOUND_LIST, first(RULE_AND_OR)); - - // List - add_firsts(RULE_LIST, first(RULE_AND_OR)); - - // Input - add_firsts(RULE_INPUT, first(RULE_LIST)); -} - -// === Functions - -int grammar_init(void) -{ - // Initialize the firsts map - bool success = init_firsts_map(); - if (success != true) - return false; - - // Populate the firsts map - add_firsts_tokens(); - add_firsts_rec(); - - return true; -} - -void grammar_close(void) -{ - // Deep free firsts map - for (int i = 0; i < NUMBER_OF_RULES; i++) - { - if (firsts_map[i].tokens != NULL) - { - free(firsts_map[i].tokens); - } - } - free(firsts_map); - firsts_map = NULL; -} - -struct firsts_list *first(enum rule rule) -{ - if (firsts_map == NULL || firsts_map[rule].tokens == NULL) - { - perror("Internal error: attempted to get the firsts of a rule without " - "properly initializing the firsts map"); - return NULL; - } - - return &firsts_map[rule]; -} - -bool is_first(struct token token, enum rule rule) -{ - struct firsts_list *firsts = &firsts_map[rule]; - for (size_t i = 0; i < firsts->list_length; i++) - { - if (firsts->tokens[i] == token.type) - return true; - } - - return false; -} - -struct ast *parse_input(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - - if (token->type == TOKEN_EOF) - { - POP_TOKEN(); - return ast_create_end(); - } - - if (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - return ast_create_list(NULL); - } - - struct ast *ast = parse_list(ctx); - token = PEEK_TOKEN(); - - if (ast == NULL) - { - if (token != NULL && token->type == TOKEN_EOF) - { - POP_TOKEN(); - } - return NULL; - } - - if (token->type == TOKEN_NEWLINE || token->type == TOKEN_EOF) - { - POP_TOKEN(); - return ast; - } - - perror("Syntax error: expected newline or EOF after list"); - ast_free(&ast); - return NULL; -} diff --git a/src/parser/grammar.h b/src/parser/grammar.h deleted file mode 100644 index 020d733..0000000 --- a/src/parser/grammar.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef GRAMMAR_H -#define GRAMMAR_H - -#include - -#include "../lexer/lexer.h" - -// === Macros - -#define PEEK_TOKEN() \ - peek_token(ctx); \ - if (token == NULL) \ - { \ - perror("Internal error: cannot get the following token"); \ - return NULL; \ - } - -#define POP_TOKEN() \ - pop_token(ctx); \ - if (token == NULL) \ - { \ - perror("Internal error: cannot get the following token"); \ - return NULL; \ - } - -// === Structures - -enum rule -{ - RULE_NULL = 0, - RULE_INPUT, - RULE_LIST, - RULE_AND_OR, - RULE_PIPELINE, - RULE_COMMAND, - RULE_SIMPLE_COMMAND, - RULE_SHELL_COMMAND, - RULE_IF, - RULE_COMPOUND_LIST, - RULE_ELSE_CLAUSE, - RULE_ELEMENT, - RULE_REDIRECTION, - RULE_PREFIX, - RULE_FUNCDEC, - RULE_WHILE, - RULE_UNTIL, - RULE_FOR, - RULE_CASE, - RULE_CASE_CLAUSE, - RULE_CASE_ITEM, - NUMBER_OF_RULES -}; - -struct firsts_list -{ - enum token_type *tokens; // Heap allocated array - size_t list_length; -}; - -// === Functions - -/* - * @brief Initializes the grammar submodule - * @return PARSER_INIT_SUCCESS on success PARSER_INIT_ERROR on error - * @warning Do not use outside the parser - */ -int grammar_init(void); - -/* - * @brief Closes the grammar submodule - * @warning Do not use outside the parser - */ -void grammar_close(void); - -/* - * @brief get the first accepted tokens of a rule - * - * @arg r the rule - * @return the accepted tokens as a firsts_list struct - */ -struct firsts_list *first(enum rule r); - -/* - * @brief tells is token belong to the firsts of a specific rule - * - * @arg token - * @arg rule - * @return true if token belongs to rule's firsts, false otherwise - */ -bool is_first(struct token token, enum rule rule); - -/* @brief Acts as the entry point of the parser, calls parse_list - * - * @code input = list '\n' - * | list EOF - * | '\n' - * | EOF - * ; - * @first first(list), '\n', EOF - */ -struct ast *parse_input(struct lexer_context *ctx); - -#endif /* ! GRAMMAR_H */ diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c deleted file mode 100644 index d1b7efa..0000000 --- a/src/parser/grammar_advanced.c +++ /dev/null @@ -1,240 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "grammar_advanced.h" - -#include -#include -#include - -#include "grammar.h" -#include "grammar_basic.h" - -// === Static functions - -static enum ast_redir_type redir_tok_to_ast_type(enum token_type tok_type) -{ - switch (tok_type) - { - case TOKEN_REDIR_LEFT: - return AST_REDIR_TYPE_LESS; - case TOKEN_REDIR_RIGHT: - return AST_REDIR_TYPE_GREAT; - case TOKEN_REDIR_DOUBLE_RIGHT: - return AST_REDIR_TYPE_DGREAT; - case TOKEN_REDIR_LEFT_RIGHT: - return AST_REDIR_TYPE_LESSGREAT; - case TOKEN_REDIR_LEFT_AMP: - return AST_REDIR_TYPE_LESSAND; - case TOKEN_REDIR_RIGHT_AMP: - return AST_REDIR_TYPE_GREATAND; - case TOKEN_REDIR_RIGHT_PIPE: - return AST_REDIR_TYPE_CLOBBER; - default: - return AST_REDIR_TYPE_NULL; - } -} - -/* - * @brief parses a while/until loop starting with the condition - * (after the while/until keyword) - * @arg negate_condition Set to true for until loops, false for while loops - */ -static struct ast *parse_loop(struct lexer_context *ctx, bool negate_condition) -{ - // condition - struct ast *condition = parse_compound_list(ctx); - if (condition == NULL) - return NULL; - if (negate_condition) - { - condition = - ast_create_neg(true, condition); // TODO check result (beware to not - // exceed the function lines limit) - } - - struct token *token = PEEK_TOKEN(); - - // 'do' - if (token->type != TOKEN_DO) - { - ast_free(&condition); - perror("Syntax error: expected the 'do' keyowrd but got a different " - "token"); - return NULL; - } - POP_TOKEN(); - token = PEEK_TOKEN(); - - // body - struct ast *body = parse_compound_list(ctx); - if (body == NULL) - { - ast_free(&condition); - return NULL; - } - token = PEEK_TOKEN(); - - // 'done' - if (token->type != TOKEN_DONE) - { - ast_free(&condition); - perror("Syntax error: expected the 'done' keyowrd but got a different " - "token"); - return NULL; - } - POP_TOKEN(); - - struct ast *result = ast_create_loop(condition, body); - if (result == NULL) - { - ast_free(&condition); - ast_free(&body); - perror("Internal error: could not create ast node (is your memory full " - "?)"); - return NULL; - } - - return result; -} - -// === Functions - -struct ast *parse_redirection(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - int io_number = -1; - if (token->type == TOKEN_IONUMBER) - { - io_number = atoi(token->data); - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - if (!is_token_redir(token)) - { - perror("Syntax error: expected a redirection token but got something " - "else"); - return NULL; - } - - enum ast_redir_type redir_type = redir_tok_to_ast_type(token->type); - POP_TOKEN(); - - token = PEEK_TOKEN(); - if (token->type != TOKEN_WORD) - { - perror("Syntax error: expected a word after redirection"); - return NULL; - } - char *target = strdup(token->data); - POP_TOKEN(); - - return ast_create_redir(target, io_number, redir_type); -} - -struct ast *parse_prefix(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - if (token->type == TOKEN_ASSIGNMENT_WORD) - { - token = POP_TOKEN(); - return ast_create_assignment(token->data, false); - } - else if (is_first(*token, RULE_REDIRECTION)) - return parse_redirection(ctx); - else - { - perror("Syntax error: expected a prefix (redirection or assignment)"); - return NULL; - } -} - -struct ast *parse_funcdec(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - struct ast *value = NULL; - char *func_name = NULL; - - if (token->type != TOKEN_WORD) - { - return NULL; - } - - // word -> func name - POP_TOKEN(); - func_name = strdup(token->data); - - // ( - token = PEEK_TOKEN(); - if (token->type != TOKEN_LEFT_PAREN) - { - free(func_name); - return NULL; - } - POP_TOKEN(); - - // ) - token = PEEK_TOKEN(); - if (token->type != TOKEN_RIGHT_PAREN) - { - free(func_name); - return NULL; - } - POP_TOKEN(); - token = PEEK_TOKEN(); - - // { \n } - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // shell_command -> value - value = parse_shell_command(ctx); - if (value == NULL) - { - free(func_name); - return NULL; - } - return ast_create_function(func_name, value); -} - -struct ast *parse_for(struct lexer_context *ctx) -{ - (void)ctx; - perror("Error: usage of a not implemented function (parse_for)"); - return NULL; -} - -struct ast *parse_while(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - - // 'while' - if (token->type != TOKEN_WHILE) - { - perror( - "Internal error: expected a TOKEN_WHILE but got a different type"); - return NULL; - } - POP_TOKEN(); - - return parse_loop(ctx, false); -} - -struct ast *parse_until(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - - // 'until' - if (token->type != TOKEN_UNTIL) - { - perror( - "Internal error: expected a TOKEN_UNTIL but got a different type"); - return NULL; - } - POP_TOKEN(); - - return parse_loop(ctx, true); -} diff --git a/src/parser/grammar_advanced.h b/src/parser/grammar_advanced.h deleted file mode 100644 index 3829c49..0000000 --- a/src/parser/grammar_advanced.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef GRAMMAR_ADVANCED_H -#define GRAMMAR_ADVANCED_H - -#include "grammar.h" - -// === Functions - -/* - * @brief parses a redirection rule - * - * @code redirection = [IONUMBER] ( '>' | '<' | '>>' | '>&' | '<&' | '>|' | '<>' - * ) WORD ; - * - * @first TOKEN_IONUMBER, TOKEN_REDIRECTION - */ -struct ast *parse_redirection(struct lexer_context *ctx); - -/* - * @brief parses a prefix rule - * - * @code prefix = redirection ; - * - * @first first(redirection) - */ -struct ast *parse_prefix(struct lexer_context *ctx); - -/* - * @brief parses a funcdec rule - * @warning Work in progress - * - * @code funcdec = WORD '(' ')' {'\n'} shell_command ; - * - * @first WORD - */ -struct ast *parse_funcdec(struct lexer_context *ctx); - -/* - * @brief parses a for rule - * - * @code rule_for = 'for' WORD - * ( [';'] | [ {'\n'} 'in' { WORD } ( ';' | '\n' ) ] ) - * {'\n'} 'do' compound_list 'done' ; - * - * @first TOKEN_FOR - */ -struct ast *parse_for(struct lexer_context *ctx); - -/* - * @brief parses a while rule - * @warning NOT IMPLEMENTED - * - * @code rule_while = 'while' compound_list 'do' compound_list 'done' ; - * - * @first TOKEN_WHILE - */ -struct ast *parse_while(struct lexer_context *ctx); - -/* - * @brief parses an until rule - * @warning NOT IMPLEMENTED - * - * @code rule_until = 'until' compound_list 'do' compound_list 'done' ; - * - * @first TOKEN_UNTIL - */ -struct ast *parse_until(struct lexer_context *ctx); - -#endif /* ! GRAMMAR_ADVANCED_H */ diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c deleted file mode 100644 index 92113fe..0000000 --- a/src/parser/grammar_basic.c +++ /dev/null @@ -1,627 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "grammar_basic.h" - -#include -#include -#include - -#include "../utils/lists/lists.h" -#include "grammar.h" -#include "grammar_advanced.h" - -// === Static functions - -static enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type) -{ - switch (tok_type) - { - case TOKEN_AND: - return AST_AND_OR_TYPE_AND; - case TOKEN_OR: - return AST_AND_OR_TYPE_OR; - default: - fprintf(stderr, "and_or impossible to init, wrong token type"); - return AST_AND_OR_NULL; - } -} - -/* @brief: frees all the arguments. (helper func) - * @return: NULL. - */ -static void *err_if_rule(struct ast **cond, struct ast **then_clause, - struct ast **else_clause) -{ - ast_free(cond); - ast_free(then_clause); - ast_free(else_clause); - return NULL; -} - -/* @brief: frees command_elements and redirections lists (helper func) - * @return: NULL - */ -static void *err_s_com(struct list *command_elements, struct list *redirections, - struct list *assignments) -{ - list_deep_destroy(command_elements); - list_deep_destroy(redirections); - list_deep_destroy(assignments); - return NULL; -} - -/* @brief: used when export keyword is found, and expects an assignment after. - * @return: an ast_assignment with the field [global] set to true. - */ -static struct ast *parse_export(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - if (token->type != TOKEN_EXPORT) - { - fprintf(stderr, "expected the export keyword in parse_export"); - return NULL; - } - // export - POP_TOKEN(); - - token = PEEK_TOKEN(); - - if (token->type != TOKEN_ASSIGNMENT_WORD) - { - fprintf(stderr, "in parser: export must be followed by 'x=y'"); - return NULL; - } - - // assignment - POP_TOKEN(); - - return ast_create_assignment(token->data, true); -} - -// === Functions - -struct ast *parse_list(struct lexer_context *ctx) -{ - struct list *result_list = NULL; - struct ast *current_node = NULL; - - struct token *token = PEEK_TOKEN(); - - // and_or - current_node = parse_and_or(ctx); - if (current_node == NULL) - return NULL; - result_list = list_append(result_list, current_node); - - // Following and_or commands - token = PEEK_TOKEN(); - while (token->type == TOKEN_SEMICOLON) - { - // Forward - POP_TOKEN(); - token = PEEK_TOKEN(); - - // TODO seems a little akward (not fully compliant with the grammar) - // but it's time consuming to rewrite to only cover edge cases. - // So it'll probably stay like that for now - if (is_first(*token, RULE_AND_OR)) - { - current_node = parse_and_or(ctx); - if (current_node == NULL) - { - struct ast *tmp = ast_create_list(result_list); - ast_free(&tmp); - return NULL; - } - result_list = list_append(result_list, current_node); - token = PEEK_TOKEN(); - } - } - - return ast_create_list(result_list); -} - -struct ast *parse_and_or(struct lexer_context *ctx) -{ - struct ast *result = parse_pipeline(ctx); - if (result == NULL) - return NULL; - struct token *token = PEEK_TOKEN(); - - while (token->type == TOKEN_AND || token->type == TOKEN_OR) - { - // Build AST (left part) - enum ast_and_or_type type = and_or_tok_to_ast(token->type); - struct ast *left = result; - - POP_TOKEN(); - token = PEEK_TOKEN(); - - // Skip newlines - while (token->type == TOKEN_NEWLINE) - { - token = POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // Right part - struct ast *right = parse_pipeline(ctx); - if (right == NULL) - { - ast_free(&left); - return NULL; - } - token = PEEK_TOKEN(); - - result = ast_create_and_or(left, right, type); - if (result == NULL) - { - ast_free(&left); - ast_free(&right); - return NULL; - } - } - - return result; -} - -struct ast *parse_pipeline(struct lexer_context *ctx) -{ - bool negation = false; - struct token *token = PEEK_TOKEN(); - - // Eventual '!' - if (token->type == TOKEN_NEGATION) - { - negation = true; - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // command rule - struct ast *left = parse_command(ctx); - token = PEEK_TOKEN(); - if (negation) - { - left = ast_create_neg(negation, left); - } - - // Pipes - while (token->type == TOKEN_PIPE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - - // skip newlines - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // command rule - struct ast *right = parse_command(ctx); - token = PEEK_TOKEN(); - - // Create AST - left = ast_create_pipe(left, right); - } - - return left; -} - -struct ast *parse_command(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - struct ast *result = NULL; - - if (is_first(*token, RULE_SIMPLE_COMMAND)) - { - result = parse_simple_command(ctx); - } - else if (is_first(*token, RULE_SHELL_COMMAND)) - { - result = parse_shell_command(ctx); - } - else if (is_first(*token, RULE_FUNCDEC)) - { - result = parse_funcdec(ctx); - } - else - { - perror("Syntax error: unexpected token"); - return NULL; - } - - return result; -} - -struct ast *parse_simple_command(struct lexer_context *ctx) -{ - struct list *command_elements = NULL; - struct list *redirections = NULL; // list of redirection ASTs - struct list *assignments = NULL; - - bool has_prefix = false; - struct token *token = PEEK_TOKEN(); - if (is_first(*token, RULE_PREFIX)) - { - has_prefix = true; - while (is_first(*token, RULE_PREFIX)) - { - struct ast *prefix = parse_prefix(ctx); - if (prefix == NULL) - { - return err_s_com(command_elements, redirections, assignments); - } - if (prefix->type == AST_ASSIGNMENT) - { - assignments = list_append(assignments, prefix); - } - else if (prefix->type == AST_REDIR) - { - redirections = list_append(redirections, prefix); - } - token = PEEK_TOKEN(); - } - } - - if (token->type != TOKEN_WORD) - { - if (!has_prefix && token->type != TOKEN_EXPORT) - { - perror("Expected a command but got a different token type"); - return err_s_com(command_elements, redirections, assignments); - } - if (token->type == TOKEN_EXPORT) - { - struct ast *assignment_export = parse_export(ctx); - if (assignment_export == NULL) - return err_s_com(command_elements, redirections, assignments); - - assignments = list_append(assignments, assignment_export); - } - } - else // TOKEN WORD - { - char *command = strdup(token->data); - command_elements = list_append(command_elements, command); - - POP_TOKEN(); - } - token = PEEK_TOKEN(); - // Eventual elements - while (is_first(*token, RULE_ELEMENT)) - { - // Get element - struct ast *element = parse_element(ctx); - if (element == NULL) - { - return err_s_com(command_elements, redirections, assignments); - } - - // Get element type - if (ast_is_word(element)) - { - // Extract word - struct ast_word *element_word = ast_get_word(element); - char *word = element_word->word; - element_word->word = NULL; // Prevents word to be freed - ast_free(&element); - command_elements = list_append(command_elements, word); - } - else if (ast_is_redir(element)) - { - // append redirections to the list of redirections - redirections = list_append(redirections, element); - } - else - { - perror("Internal error: unexpected return value from " - "parse_element in parse_simple_command"); - return err_s_com(command_elements, redirections, assignments); - } - - // Forward - token = PEEK_TOKEN(); - } - - struct ast *result = - ast_create_command(command_elements, redirections, assignments); - if (result == NULL) - { - return err_s_com(command_elements, redirections, assignments); - } - return result; -} - -struct ast *parse_element(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - if (token->type == TOKEN_WORD || token->type == TOKEN_ASSIGNMENT_WORD) - { - POP_TOKEN(); - - struct ast *result = ast_create_word(token->data); - if (result == NULL) - { - perror("Internal error: could not create ast node (is your memory " - "full ?)"); - return NULL; - } - - return result; - } - else if (token->type == TOKEN_IONUMBER || is_token_redir(token)) - { - return parse_redirection(ctx); - } - else if (token->type == TOKEN_EXPORT) - { - return parse_export(ctx); - } - else - { - perror("Syntax error: unexpected token at parse_element"); - return NULL; - } -} - -struct ast *parse_shell_command(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - struct ast *result = NULL; - - // '{' - if (token->type == TOKEN_LEFT_BRACKET) - { - POP_TOKEN(); - result = parse_compound_list(ctx); - if (result == NULL) - return NULL; - - // '}' - token = PEEK_TOKEN(); - if (token->type == TOKEN_LEFT_BRACKET) - { - ast_free(&result); - perror("Syntax error: bracket 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); - } - else if (is_first(*token, RULE_WHILE)) - { - return parse_while(ctx); - } - else if (is_first(*token, RULE_UNTIL)) - { - return parse_until(ctx); - } - // TODO for and case - else - { - perror("Syntax error: unexpected token in parse_shell_command"); - return NULL; - } -} - -struct ast *parse_if_rule(struct lexer_context *ctx) -{ - // If keyword - struct token *token = POP_TOKEN(); - if (token->type != TOKEN_IF) - { - perror("Internal error: expected a if rule but token has different " - "type"); - return NULL; - } - - // Condition content - struct ast *condition_content = parse_compound_list(ctx); - if (condition_content == NULL) - return NULL; - token = PEEK_TOKEN(); - - // Then keyword - if (token->type != TOKEN_THEN) - { - perror("Syntax error: Expected the 'then' keyword but token has " - "different type"); - return err_if_rule(&condition_content, NULL, NULL); - } - POP_TOKEN(); - - // Then content - struct ast *then_content = parse_compound_list(ctx); - if (then_content == NULL) - { - return err_if_rule(&condition_content, &then_content, NULL); - } - token = PEEK_TOKEN(); - - struct ast *else_content = NULL; - // Eventual else/elif clause(s) - if (is_first(*token, RULE_ELSE_CLAUSE)) - { - else_content = parse_else_clause(ctx); - if (else_content == NULL) - { - return err_if_rule(&condition_content, &then_content, NULL); - } - token = PEEK_TOKEN(); - } - - // Fi keyword - if (token->type != TOKEN_FI) - { - perror("Expected the 'fi' keyword but token has different type"); - return err_if_rule(&condition_content, &then_content, &else_content); - } - POP_TOKEN(); - - // Result - struct ast *result = - ast_create_if(condition_content, then_content, else_content); - if (result == NULL) - { - perror("Internal error: could not create a new AST (AST_IF)"); - return err_if_rule(&condition_content, &then_content, &else_content); - } - - return result; -} - -struct ast *parse_compound_list(struct lexer_context *ctx) -{ - struct list *result_list = NULL; // ast* list - struct ast *current_cmd = NULL; - struct token *token = PEEK_TOKEN(); - - // Skip newlines - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // And/or - current_cmd = parse_and_or(ctx); - if (current_cmd == NULL) - return NULL; - result_list = list_append(result_list, current_cmd); - token = PEEK_TOKEN(); - - // Following commands - while (token->type == TOKEN_SEMICOLON || token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - - // Skip newlines - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // And/or - if (is_first(*token, RULE_AND_OR)) - { - current_cmd = parse_and_or(ctx); - if (current_cmd == NULL) - return NULL; - result_list = list_append(result_list, current_cmd); - token = PEEK_TOKEN(); - } - } - - // Eventual semicolon - if (token->type == TOKEN_SEMICOLON) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // Skip newlines - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - struct ast *result = ast_create_list(result_list); - return result; -} - -struct ast *parse_else_clause(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - - // Eventual elif content - while (token->type == TOKEN_ELIF) - { - // Condition - token = POP_TOKEN(); - struct ast *condition = parse_compound_list(ctx); - - // Then keyword - token = POP_TOKEN(); - if (token->type != TOKEN_THEN) - { - perror("Expected the 'then' keyword but got a different token " - "type"); - return NULL; - } - - struct ast *then_content = parse_compound_list(ctx); - if (then_content == NULL) - { - ast_free(&condition); - return NULL; - } - token = PEEK_TOKEN(); - - // Eventual else clause (recursive) - struct ast *else_content = NULL; - if (token->type == TOKEN_ELSE || token->type == TOKEN_ELIF) - { - else_content = parse_else_clause(ctx); - if (else_content == NULL) - { - ast_free(&then_content); - ast_free(&condition); - return NULL; - } - } - else - { - else_content = ast_create_void(); - } - - return ast_create_if(condition, then_content, else_content); - } - - // Eventual else content - - struct ast *result = NULL; - - if (token->type == TOKEN_ELSE) - { - token = POP_TOKEN(); - result = parse_compound_list(ctx); - if (result == NULL) - return NULL; - } - else - { - result = ast_create_void(); - } - - return result; -} diff --git a/src/parser/grammar_basic.h b/src/parser/grammar_basic.h deleted file mode 100644 index 3ee5355..0000000 --- a/src/parser/grammar_basic.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef GRAMMAR_BASIC_H -#define GRAMMAR_BASIC_H - -#include "../lexer/lexer.h" -#include "../utils/ast/ast.h" - -// === Functions - -/* - * @brief parses a list of [and_or] rules separated by semicolons and that - * ends by a newline - * - * @code list = and_or { ';' and_or } [ ';' ] ; - * - * @first first(and_or) - */ -struct ast *parse_list(struct lexer_context *ctx); - -/* - * @brief Only parses a pipeline rule for the moment - * - * @code and_or = pipeline { ( '&&' | '||' ) {'\n'} pipeline } ; - * - * @first first(pipeline) - */ -struct ast *parse_and_or(struct lexer_context *ctx); - -/* - * @brief Only parses a command rule for the moment - * - * @code pipeline = ['!'] command { '|' {'\n'} command } ; - * - * @first '!', first(command) - */ -struct ast *parse_pipeline(struct lexer_context *ctx); - -/* - * @brief Parses a simple command rule or a shell command rule depending on - * the first token. - * @note - * TOKEN_WORD => simple_command - * TOKEN_IF => shell_command - * - * @code command = simple_command - * | shell_command - * - * ; - * @first first(simple_command), first(shell_command) - */ -struct ast *parse_command(struct lexer_context *ctx); - -/* - * @brief Parses a simple list of words (command and arguments) - * ending by a separator - * - * @code simple_command = WORD { element } ; - * - * @first WORD - */ -struct ast *parse_simple_command(struct lexer_context *ctx); - -/* - * @brief Parses an element rule - * - * @code element = WORD - * | redirection - * ; - * - * @first WORD, first(redirection) - */ -struct ast *parse_element(struct lexer_context *ctx); - -/* - * @brief Only parses if rules for the moment - * - * @code shell_command = '{' compound_list '}' - * | '(' compound_list ')' - * | if_rule - * ; - * - * @first first(if_rule) - */ -struct ast *parse_shell_command(struct lexer_context *ctx); - -/* - * @brief Parses a if rule (condition, then-clause, elif-clause, else-clause) - * - * @code if_rule = 'if' compound_list 'then' compound_list [else_clause] 'fi' ; - * - * @first TOKEN_IF - */ -struct ast *parse_if_rule(struct lexer_context *ctx); - -/* - * @brief parses commands inside if/else clauses and returns the corresponding - * AST list - * - * @code compound_list = {'\n'} and_or { ( ';' | '\n' ) {'\n'} and_or } [';'] - * {'\n'} ; - * - * @first TOKEN_NEWLINE, first(and_or) - */ -struct ast *parse_compound_list(struct lexer_context *ctx); - -/* - * @brief parses an else clause rule (inside if) - * - * @code else_clause = 'else' compound_list - * | 'elif' compound_list 'then' compound_list [else_clause] - * ; - * - * @first TOKEN_ELSE, TOKEN_ELIF - */ -struct ast *parse_else_clause(struct lexer_context *ctx); - -#endif /* ! GRAMMAR_BASIC_H */ diff --git a/src/parser/parser.c b/src/parser/parser.c index d1f0ebc..e69de29 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -1,67 +0,0 @@ -#include "parser.h" - -#include - -#include "grammar.h" - -// === Static variables - -static enum parser_state state = PARSER_STATE_NOT_INITIALIZED; - -// === Functions - -int parser_init(void) -{ - if (state == PARSER_STATE_READY) - { - perror("Internal error: tried to initialize the parser module twice."); - return false; - } - int success = grammar_init(); - if (success == false) - return false; - - state = PARSER_STATE_READY; - return true; -} - -void parser_close(void) -{ - if (state != PARSER_STATE_READY) - { - perror("trying to close parser which was not opened"); - } - state = PARSER_STATE_CLOSED; - // TODO close grammar -} - -struct ast *get_ast(struct lexer_context *ctx) -{ - if (ctx == NULL) - { - perror("Internal error: called parser with no lexer context (NULL " - "pointer). Aborting."); - return NULL; - } - if (state == PARSER_STATE_NOT_INITIALIZED) - { - perror("Internal error: attempted to call parser without initializing " - "it. Aborting."); - return NULL; - } - if (state == PARSER_STATE_CLOSED) - { - perror("Internal error: attempted to call parser after closing it. " - "Aborting."); - return NULL; - } - - return parse_input(ctx); -} - -// TODO -struct ast *get_ast_str(char *command) -{ - (void)command; - return NULL; -} diff --git a/src/parser/parser.h b/src/parser/parser.h index dc01a5e..ef5a449 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -1,45 +1,4 @@ #ifndef PARSER_H #define PARSER_H -#include - -#include "../lexer/lexer.h" -#include "../utils/ast/ast.h" - -enum parser_state -{ - PARSER_STATE_NOT_INITIALIZED = 0, - PARSER_STATE_READY, - PARSER_STATE_CLOSED -}; - -/* @brief Initializes the parser module - * @warning parser needs to be closed after use with parser_close() - * - * @return Returns false on error and true on success - */ -int parser_init(void); - -/* @brief Closes the parser module after use - */ -void parser_close(void); - -/* @brief Builds the AST representation of the next command to execute. - * - * @return Returns the AST representation of the next command to execute. - * If there is no command left to execute, retuns an AST_END node. - * - * @warning NOT IMPLEMENTED - */ -struct ast *get_ast(struct lexer_context *ctx); - -/* @brief Builds the AST representation of the given command string. - * - * @return Returns the AST representation of the given command string. - * Returns an AST_END node if the given command is empty. - * - * @warning NOT IMPLEMENTED - */ -struct ast *get_ast_str(char *command); - #endif /* ! PARSER_H */ diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index e40612e..5b72df3 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -1,29 +1,7 @@ lib_LIBRARIES = libutils.a libutils_a_SOURCES = \ - lists/lists1.c \ - lists/lists2.c \ - lists/lists3.c \ - hash_map/hash_map.c \ - string_utils/string_utils.c \ - ast/ast.c \ - ast/ast_if.c \ - ast/ast_command.c \ - ast/ast_list.c \ - ast/ast_and_or.c \ - ast/ast_redir.c \ - ast/ast_void.c \ - ast/ast_end.c \ - ast/ast_word.c \ - ast/ast_neg.c \ - ast/ast_pipe.c \ - 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 + string_utils.c libutils_a_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/utils/args/args.c b/src/utils/args/args.c deleted file mode 100644 index 69374e2..0000000 --- a/src/utils/args/args.c +++ /dev/null @@ -1,155 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "./args.h" - -#include -#include -#include -#include - -#include "../lists/lists.h" -#include "../string_utils/string_utils.h" -#include "../vars/vars.h" - -static void strlen_acc(void *acc, void *data) -{ - size_t *len = (size_t *)acc; - char *str = (char *)data; - *len += strlen(str); -} - -static char *concat_list_str(struct list *list) -{ - char *res = NULL; - size_t total_len = list_length(list) + 1; // + spaces + null terminator - - list_fold(list, &total_len, strlen_acc); - - res = malloc(total_len); - if (res != NULL) - { - res[0] = 0; - for (struct list *it = list; it != NULL; it = it->next) - { - strcat(res, (char *)it->data); - if (it->next != NULL) - strcat(res, " "); - } - res[total_len - 1] = 0; - } - - return res; -} - -static void args_in_var(struct hash_map *vars, struct list *args_list) -{ - int arg_index = 1; - char index_str[11]; - - for (struct list *it = args_list; it != NULL; it = it->next) - { - int_to_str(arg_index++, index_str); - set_var_copy(vars, index_str, (char *)it->data); - } - - char *concated_args = concat_list_str(args_list); - set_var_copy(vars, "*", concated_args); - - char *key = strdup("@"); - set_var(vars, key, concated_args, NULL); - // key and concated_args consumed by hash_map - - int_to_str(arg_index - 1, index_str); - set_var_copy(vars, "#", index_str); -} - -int args_handler(int argc, char **argv, struct args_options *options, - struct hash_map *vars) -{ - options->type = INPUT_UNDEFINED; - options->input_source = NULL; - // options->pretty_print = false; - options->verbose = false; - - struct list *args_list = NULL; - - set_var_copy(vars, "0", argv[0]); - - for (int i = 1; i < argc; i++) - { - /* if (strcmp(argv[i], "--pretty-print") == 0) - { - options->pretty_print = true; - } */ - if (strcmp(argv[i], "--verbose") == 0) - { - options->verbose = true; - } - else if (strcmp(argv[i], "-c") == 0) - { - if (options->type != INPUT_UNDEFINED) - { - fprintf(stderr, "Multiple input sources specified: %s\n", - argv[i + 1]); - return 1; - } - else if (i + 1 >= argc) - { - fprintf(stderr, "No command provided after -c\n"); - return 1; - } - - options->type = INPUT_CMD; - options->input_source = argv[i + 1]; - i++; - } - else if (argv[i][0] == '-') - { - fprintf(stderr, "Unknown option: %s\n", argv[i]); - return 1; - } - else if (options->type == INPUT_UNDEFINED) - { - options->type = INPUT_FILE; - options->input_source = argv[i]; - } - else - { - // All remaining arguments are treated as additional arguments - args_list = list_append(args_list, argv[i]); - continue; - } - } - - args_in_var(vars, args_list); - list_destroy(&args_list); - - if (options->type == INPUT_UNDEFINED) - options->type = INPUT_STDIN; - - return 0; -} - -void args_print(struct args_options *options) -{ - printf("Input type: %s\n", - options->type == INPUT_CMD ? "COMMAND" - : options->type == INPUT_FILE ? "FILE" - : options->type == INPUT_STDIN ? "STDIN" - : "UNDEFINED"); - printf("Input source: %s\n", - options->input_source ? options->input_source : "NULL"); - // printf("Pretty print: %s\n", options->pretty_print ? "true" : "false"); - printf("Verbose: %s\n", options->verbose ? "true" : "false"); -} - -void print_usage(FILE *std, const char *program_name) -{ - fprintf(std, "Usage: %s [OPTIONS] [SCRIPT] [ARGUMENTS...]\n", program_name); - fprintf(std, "Options:\n"); - fprintf(std, " -c [SCRIPT] Execute the given command string.\n"); - // fprintf(std, " --pretty-print Enable pretty printing of - // outputs.\n"); - fprintf(std, " --verbose Enable verbose mode.\n"); - fprintf(std, - "If no SCRIPT is provided, input is read from standard input.\n"); -} diff --git a/src/utils/args/args.h b/src/utils/args/args.h deleted file mode 100644 index 3c02fc4..0000000 --- a/src/utils/args/args.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef ARGS_H -#define ARGS_H - -#include -#include - -#include "../hash_map/hash_map.h" - -enum input_type -{ - INPUT_UNDEFINED, - INPUT_FILE, - INPUT_CMD, - INPUT_STDIN -}; - -struct args_options -{ - /** Source of the input, filename or command string depending on type, NULL - * if INPUT_STDIN */ - const char *input_source; - /** Type of the input source */ - enum input_type type; - /** Enable or disable pretty printing of outputs */ - // bool pretty_print; - /** Enable or disable verbose mode */ - bool verbose; -}; - -/** - * Handles command-line arguments and populates the args_options structure. - * @param argc The argument count. - * @param argv The argument vector. - * @param options Pointer to args_options structure to be populated. - * @param vars Pointer to the variables hash map. - * @return 0 on success, non-zero on failure. - */ -int args_handler(int argc, char **argv, struct args_options *options, - struct hash_map *vars); - -/** Prints the parsed arguments for debugging purposes. - * @param options Pointer to args_options structure containing parsed options. - */ -void args_print(struct args_options *options); - -/** Prints the usage information for the program. - * @param std The output stream to print to (e.g., stdout or stderr). - * @param program_name The name of the program. - */ -void print_usage(FILE *std, const char *program_name); - -#endif /* ARGS_H */ diff --git a/src/utils/ast/ast.c b/src/utils/ast/ast.c deleted file mode 100644 index d16a9cb..0000000 --- a/src/utils/ast/ast.c +++ /dev/null @@ -1,174 +0,0 @@ - -#define _POSIX_C_SOURCE 200809L -#include "ast.h" - -#include -#include -#include -#include - -void ast_free(struct ast **node) -{ - if (node == NULL || *node == NULL) - { - fprintf(stderr, - "WARNING: Internal error: failed to free AST node (NULL argument)"); - return; - } - - switch ((*node)->type) - { - case AST_IF: - ast_free_if(ast_get_if(*node)); - break; - case AST_CMD: - ast_free_command(ast_get_command(*node)); - break; - case AST_LIST: - ast_free_list(ast_get_list(*node)); - break; - case AST_AND_OR: - ast_free_and_or(ast_get_and_or(*node)); - break; - case AST_REDIR: - ast_free_redir(ast_get_redir(*node)); - break; - case AST_PIPE: - ast_free_pipe(ast_get_pipe(*node)); - break; - case AST_WORD: - ast_free_word(ast_get_word(*node)); - break; - case AST_ASSIGNMENT: - ast_free_assignment(ast_get_assignment(*node)); - break; - case AST_NEG: - ast_free_neg(ast_get_neg(*node)); - break; - case AST_LOOP: - ast_free_loop(ast_get_loop(*node)); - break; - 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)"); - return; - } - - free(*node); - *node = NULL; -} - -struct ast *ast_create(enum ast_type type, void *data) -{ - struct ast *node = malloc(sizeof(struct ast)); - if (!node) - return NULL; - - node->type = type; - node->data = data; - - return node; -} - -/* // TODO handle new types (AST_WORD, AST_PIPE, etc.) -static void ast_print_dot_recursive(struct ast *node, FILE *out) -{ - if (!node) - return; - - switch (node->type) - { - case AST_LIST: { - struct ast_list *ast_list = ast_get_list(node); - fprintf(out, " node%p [label=\"LIST\"];\n", (void *)node); - - struct list *elt = ast_list->children; - while (elt != NULL) - { - struct ast *child = (struct ast *)elt->data; - fprintf(out, " node%p -> node%p;\n", (void *)node, (void *)child); - ast_print_dot_recursive(child, out); - elt = elt->next; - } - break; - } - case AST_IF: { - struct ast_if *if_data = ast_get_if(node); - fprintf(out, " node%p [label=\"IF\"];\n", (void *)node); - - if (if_data->condition) - { - fprintf(out, " node%p -> node%p;\n", (void *)node, - (void *)if_data->condition); - fprintf(out, - " node%p [fillcolor=\"lightyellow\", style=\"filled\"];\n", - (void *)if_data->condition); - ast_print_dot_recursive(if_data->condition, out); - - if (if_data->then_clause) - { - fprintf(out, " node%p -> node%p [label=\"true\"];\n", - (void *)if_data->condition, - (void *)if_data->then_clause); - ast_print_dot_recursive(if_data->then_clause, out); - } - - if (if_data->else_clause) - { - fprintf(out, " node%p -> node%p [label=\"false\"];\n", - (void *)if_data->condition, - (void *)if_data->else_clause); - ast_print_dot_recursive(if_data->else_clause, out); - } - } - break; - } - case AST_CMD: { - struct ast_command *command_data = ast_get_command(node); - fprintf(out, " node%p [label=\"", (void *)node); - struct list *l = command_data->command; - while (l) - { - fprintf(out, "%s", (char *)l->data); - if (l->next) - fprintf(out, " "); - l = l->next; - } - fprintf(out, "\"];\n"); - break; - } - case AST_END: - fprintf(out, " node%p [label=\"END\"];\n", (void *)node); - break; - default: - break; - } -} - -void ast_print_dot(struct ast *ast) -{ - if (!ast) - return; - - FILE *dot_pipe = popen("dot -Tsvg -o ast.svg", "w"); - if (!dot_pipe) - { - return; - } - - fprintf(dot_pipe, "digraph AST {\n"); - ast_print_dot_recursive(ast, dot_pipe); - fprintf(dot_pipe, "}\n"); - pclose(dot_pipe); -} - */ diff --git a/src/utils/ast/ast.h b/src/utils/ast/ast.h deleted file mode 100644 index 2eac62f..0000000 --- a/src/utils/ast/ast.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef AST_H -#define AST_H - -#include "ast_and_or.h" -#include "ast_assignment.h" -#include "ast_base.h" -#include "ast_command.h" -#include "ast_end.h" -#include "ast_function.h" -#include "ast_if.h" -#include "ast_list.h" -#include "ast_loop.h" -#include "ast_neg.h" -#include "ast_pipe.h" -#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_and_or.c b/src/utils/ast/ast_and_or.c deleted file mode 100644 index 9dea5dd..0000000 --- a/src/utils/ast/ast_and_or.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "ast_and_or.h" - -#include - -bool ast_is_and_or(struct ast *node) -{ - return node != NULL && node->type == AST_AND_OR; -} - -struct ast_and_or *ast_get_and_or(struct ast *node) -{ - if (ast_is_and_or(node)) - return (struct ast_and_or *)node->data; - return NULL; -} - -struct ast *ast_create_and_or(struct ast *left, struct ast *right, - enum ast_and_or_type type) -{ - struct ast_and_or *and_or = malloc(sizeof(struct ast_and_or)); - if (!and_or) - return NULL; - and_or->left = left; - and_or->right = right; - and_or->type = type; - - return ast_create(AST_AND_OR, and_or); -} - -void ast_free_and_or(struct ast_and_or *and_or) -{ - if (!and_or) - return; - ast_free(&and_or->left); - ast_free(&and_or->right); - free(and_or); -} diff --git a/src/utils/ast/ast_and_or.h b/src/utils/ast/ast_and_or.h deleted file mode 100644 index 4813592..0000000 --- a/src/utils/ast/ast_and_or.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef AST_AND_OR_H -#define AST_AND_OR_H - -#include "ast_base.h" - -enum ast_and_or_type -{ - AST_AND_OR_NULL, - AST_AND_OR_TYPE_AND, - AST_AND_OR_TYPE_OR -}; - -struct ast_and_or -{ - struct ast *left; - struct ast *right; - enum ast_and_or_type type; -}; - -bool ast_is_and_or(struct ast *node); -struct ast_and_or *ast_get_and_or(struct ast *node); -struct ast *ast_create_and_or(struct ast *left, struct ast *right, - enum ast_and_or_type type); -void ast_free_and_or(struct ast_and_or *and_or); - -#endif /* ! AST_AND_OR_H */ diff --git a/src/utils/ast/ast_assignment.c b/src/utils/ast/ast_assignment.c deleted file mode 100644 index 72cfdb6..0000000 --- a/src/utils/ast/ast_assignment.c +++ /dev/null @@ -1,56 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "ast_assignment.h" - -#include -#include - -bool ast_is_assignment(struct ast *node) -{ - return node != NULL && node->type == AST_ASSIGNMENT; -} - -struct ast_assignment *ast_get_assignment(struct ast *node) -{ - if (node == NULL || node->type != AST_ASSIGNMENT) - return NULL; - return (struct ast_assignment *)node->data; -} - -/* @brief: splits the assignement 'name=value' into 2 parts, - * and fills the fields of ast_assignment with it. - */ -static void init_assignments(struct ast_assignment *ast_assignment, - char *assignment) -{ - if (assignment == NULL) - return; - char *split_pos = strchr(assignment, '='); - if (split_pos == NULL) - return; - - *split_pos = '\0'; - ast_assignment->name = strdup(assignment); - ast_assignment->value = strdup(split_pos + 1); -} - -struct ast *ast_create_assignment(char *assignment, bool global) -{ - struct ast_assignment *assignment_data = - calloc(1, sizeof(struct ast_assignment)); - if (!assignment_data) - return NULL; - - init_assignments(assignment_data, assignment); - assignment_data->global = global; - return ast_create(AST_ASSIGNMENT, assignment_data); -} - -void ast_free_assignment(struct ast_assignment *assignment_data) -{ - if (assignment_data == NULL) - return; - free(assignment_data->name); - free(assignment_data->value); - free(assignment_data); -} diff --git a/src/utils/ast/ast_assignment.h b/src/utils/ast/ast_assignment.h deleted file mode 100644 index 3ebac14..0000000 --- a/src/utils/ast/ast_assignment.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AST_ASSIGNMENT_H -#define AST_ASSIGNMENT_H - -#include "ast_base.h" - -struct ast_assignment -{ - char *name; - char *value; - bool global; -}; - -bool ast_is_assignment(struct ast *node); -struct ast_assignment *ast_get_assignment(struct ast *node); -struct ast *ast_create_assignment(char *assignment, bool global); -void ast_free_assignment(struct ast_assignment *assignment_data); - -#endif /* ! AST_ASSIGNMENT_H */ diff --git a/src/utils/ast/ast_base.h b/src/utils/ast/ast_base.h deleted file mode 100644 index de7dcfa..0000000 --- a/src/utils/ast/ast_base.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef AST_BASE_H -#define AST_BASE_H - -#include -#include - -enum ast_type -{ - AST_END, - AST_LIST, - AST_IF, - AST_AND_OR, - AST_REDIR, - AST_VOID, - AST_CMD, - AST_WORD, - AST_PIPE, - AST_NEG, - AST_LOOP, - AST_ASSIGNMENT, - AST_FUNCTION, - AST_SUBSHELL -}; - -struct ast -{ - enum ast_type type; - - /** - * Data associated with this AST node. It can be one of the following: - * - NULL (AST_END) - * - struct ast_if* (AST_IF) - * - struct ast_command* (AST_CMD) - * - struct ast_and_or* (AST_AND_OR) - * - struct ast_redir* (AST_REDIR) - * - and a lot more now... - */ - void *data; -}; - -/* @brief: returns an ast* with corresponding data and type. - * - * @note: this function should only be called by ast_create_[TYPE] functions. - */ -struct ast *ast_create(enum ast_type type, void *data); - -/* @brief: frees the given ast. If ast is NULL, does nothing. - * - */ -void ast_free(struct ast **node); - -#endif /* ! AST_BASE_H */ diff --git a/src/utils/ast/ast_command.c b/src/utils/ast/ast_command.c deleted file mode 100644 index 81dd10e..0000000 --- a/src/utils/ast/ast_command.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "ast_command.h" - -#include -#include - -#include "../lists/lists.h" -#include "ast_list.h" - -struct ast *ast_create_command(struct list *command, struct list *redirections, - struct list *assignments) -{ - struct ast_command *command_data = malloc(sizeof(struct ast_command)); - if (!command_data) - return NULL; - - command_data->command = command; - command_data->redirections = redirections; - command_data->assignments = assignments; - - return ast_create(AST_CMD, command_data); -} - -struct ast_command *ast_get_command(struct ast *node) -{ - if (node == NULL || node->type != AST_CMD) - return NULL; - return (struct ast_command *)node->data; -} - -bool ast_is_command(struct ast *node) -{ - return node != NULL && node->type == AST_CMD; -} - -void ast_free_command(struct ast_command *command_data) -{ - if (command_data == NULL) - return; - list_deep_destroy(command_data->command); - ast_list_deep_destroy(command_data->redirections); - ast_list_deep_destroy(command_data->assignments); - free(command_data); -} diff --git a/src/utils/ast/ast_command.h b/src/utils/ast/ast_command.h deleted file mode 100644 index 7b24a2d..0000000 --- a/src/utils/ast/ast_command.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef AST_COMMAND_H -#define AST_COMMAND_H - -#include "../lists/lists.h" -#include "ast_base.h" - -struct ast_command -{ - struct list *command; // A list of words (char*) - struct list *redirections; // A list of ASTs, all ast_redir - struct list *assignments; // A list of ASTs, all ast_assignment -}; - -/** - * Checks if the given AST node is a command. - */ -bool ast_is_command(struct ast *node); - -/** - * Retrieves the command data from the given AST node. - * Assumes that the node is of type AST_CMD. - */ -struct ast_command *ast_get_command(struct ast *node); - -/** - * Creates a new AST node representing a command. - */ -struct ast *ast_create_command(struct list *command, struct list *redirections, - struct list *assignments); - -/* - * @brief: frees the given ast_command and sets the pointer to NULL. - */ -void ast_free_command(struct ast_command *command_data); - -#endif /* ! AST_COMMAND_H */ diff --git a/src/utils/ast/ast_end.c b/src/utils/ast/ast_end.c deleted file mode 100644 index 0d314bc..0000000 --- a/src/utils/ast/ast_end.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "ast_end.h" - -#include -#include - -bool ast_is_end(struct ast *node) -{ - return node != NULL && node->type == AST_END; -} - -struct ast *ast_create_end(void) -{ - return ast_create(AST_END, NULL); -} diff --git a/src/utils/ast/ast_end.h b/src/utils/ast/ast_end.h deleted file mode 100644 index 55b5322..0000000 --- a/src/utils/ast/ast_end.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AST_END_H -#define AST_END_H - -#include "../lists/lists.h" -#include "ast_base.h" - -/** - * Checks if the given AST node is of type AST_END. - */ -bool ast_is_end(struct ast *node); - -/** - * Creates a new AST node representing the end of input. - * WARNING: data will be a NULL pointer - */ -struct ast *ast_create_end(void); - -#endif /* ! AST_END_H */ diff --git a/src/utils/ast/ast_function.c b/src/utils/ast/ast_function.c deleted file mode 100644 index 62b5016..0000000 --- a/src/utils/ast/ast_function.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "ast_function.h" - -#include -#include - -#include "ast_base.h" - -bool ast_is_function(struct ast *node) -{ - return node != NULL && node->type == AST_FUNCTION; -} - -struct ast_function *ast_get_function(struct ast *node) -{ - if (!ast_is_function(node)) - return NULL; - return (struct ast_function *)node->data; -} - -struct ast *ast_create_function(char *name, struct ast *value) -{ - struct ast_function *function_data = malloc(sizeof(struct ast_function)); - if (!function_data) - return NULL; - - function_data->name = name; - function_data->value = value; - - return ast_create(AST_FUNCTION, function_data); -} - -void ast_free_function(struct ast_function *function_data) -{ - if (function_data) - { - free(function_data->name); - // WARNING: this ast will be stored in the function hashmap. - // thus, it will be freed from the hashmap. - // ast_free(&function_data->value); - free(function_data); - } -} diff --git a/src/utils/ast/ast_function.h b/src/utils/ast/ast_function.h deleted file mode 100644 index 3bbc551..0000000 --- a/src/utils/ast/ast_function.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef AST_FUNCTION_H -#define AST_FUNCTION_H - -#include - -struct ast_function -{ - char *name; - struct ast *value; -}; - -/** - * @brief: Checks if the given AST node is an ast_function - */ -bool ast_is_function(struct ast *node); - -/** - * @brief: Retrieves the function data from the given AST node. - * Assumes that the node is of type AST_function. - */ -struct ast_function *ast_get_function(struct ast *node); - -/** - * @brief: Creates a new AST node representing an AST_function - * @warning: name must be already allocated. - */ -struct ast *ast_create_function(char *name, struct ast *value); -/* - * @brief: frees the given ast_function and sets the pointer to NULL. - */ -void ast_free_function(struct ast_function *function_data); - -#endif /* AST_FUNCTION_H */ diff --git a/src/utils/ast/ast_if.c b/src/utils/ast/ast_if.c deleted file mode 100644 index 6b0ff5d..0000000 --- a/src/utils/ast/ast_if.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "ast_if.h" - -#include -#include - -struct ast *ast_create_if(struct ast *condition, struct ast *then_clause, - struct ast *else_clause) -{ - struct ast_if *if_data = malloc(sizeof(struct ast_if)); - if (!if_data) - return NULL; - - if_data->condition = condition; - if_data->then_clause = then_clause; - if_data->else_clause = else_clause; - - return ast_create(AST_IF, if_data); -} - -struct ast_if *ast_get_if(struct ast *node) -{ - if (node == NULL || node->type != AST_IF) - return NULL; - return node->data; -} - -bool ast_is_if(struct ast *node) -{ - return node != NULL && node->type == AST_IF; -} - -void ast_free_if(struct ast_if *if_data) -{ - if (if_data == NULL) - return; - - ast_free(&if_data->condition); - ast_free(&if_data->then_clause); - ast_free(&if_data->else_clause); - - free(if_data); -} diff --git a/src/utils/ast/ast_if.h b/src/utils/ast/ast_if.h deleted file mode 100644 index f1842bd..0000000 --- a/src/utils/ast/ast_if.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef AST_IF_H -#define AST_IF_H - -#include "ast_base.h" - -struct ast_if -{ - struct ast *condition; - struct ast *then_clause; - struct ast *else_clause; -}; - -/** - * Checks if the given AST node is an if statement. - */ -bool ast_is_if(struct ast *node); - -/** - * Retrieves the if statement data from the given AST node. - * Assumes that the node is of type AST_IF. - */ -struct ast_if *ast_get_if(struct ast *node); - -/** - * Creates a new AST node representing an if statement. - */ -struct ast *ast_create_if(struct ast *condition, struct ast *then_clause, - struct ast *else_clause); -/* - * @brief: frees the given ast_if and sets the pointer to NULL. - */ -void ast_free_if(struct ast_if *if_data); - -#endif /* ! AST_IF_H */ diff --git a/src/utils/ast/ast_list.c b/src/utils/ast/ast_list.c deleted file mode 100644 index cb4aaa6..0000000 --- a/src/utils/ast/ast_list.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "ast_list.h" - -struct ast *ast_create_list(struct list *list) -{ - struct ast_list *ast_list = malloc(sizeof(struct ast_list)); - if (ast_list == NULL) - return NULL; - - ast_list->children = list; - - return ast_create(AST_LIST, ast_list); -} - -struct ast_list *ast_get_list(struct ast *node) -{ - if (node == NULL) - return NULL; - return node->data; -} - -bool ast_is_list(struct ast *node) -{ - return node != NULL && node->type == AST_LIST; -} - -void ast_free_list(struct ast_list *ast_list) -{ - if (ast_list == NULL) - return; - - ast_list_deep_destroy(ast_list->children); - free(ast_list); -} - -void ast_list_deep_destroy(struct list *l) -{ - struct list *elt = l; - struct list *next_elt; - while (elt != NULL) - { - next_elt = elt->next; - - struct ast *node = (struct ast *)elt->data; - ast_free(&node); - free(elt); - elt = next_elt; - } -} diff --git a/src/utils/ast/ast_list.h b/src/utils/ast/ast_list.h deleted file mode 100644 index 21b24fb..0000000 --- a/src/utils/ast/ast_list.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef AST_LIST_H -#define AST_LIST_H - -#include "../lists/lists.h" -#include "ast_base.h" - -struct ast_list -{ - struct list *children; // A list of ASTs (ast*) -}; - -/* -** Release the memory used by the AST list and its AST children -** Does nothing if `list` is `NULL`. -* -* @warning: this function should NEVER be used on a list containing -* anything else than ASTs. -*/ -void ast_list_deep_destroy(struct list *l); - -/** - * Creates a new AST node representing a list of ASTs - */ -struct ast *ast_create_list(struct list *ast_list); - -/** - * Retrieves the command data from the given AST node. - * Assumes that the node is of type AST_LIST. - */ -struct ast_list *ast_get_list(struct ast *node); - -/** - * Checks if the given AST node is a command. - */ -bool ast_is_list(struct ast *node); - -/* @brief: frees the given ast list. - * - * @warning: should only be called by ast_free() function. - */ -void ast_free_list(struct ast_list *ast_list); - -#endif /* ! AST_LIST_H */ diff --git a/src/utils/ast/ast_loop.c b/src/utils/ast/ast_loop.c deleted file mode 100644 index fded922..0000000 --- a/src/utils/ast/ast_loop.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "ast_loop.h" - -#include -#include - -struct ast *ast_create_loop(struct ast *condition, struct ast *body) -{ - struct ast_loop *node_data = malloc(sizeof(struct ast_loop)); - if (!node_data) - return NULL; - - node_data->condition = condition; - node_data->body = body; - - return ast_create(AST_LOOP, node_data); -} - -struct ast_loop *ast_get_loop(struct ast *node) -{ - if (node == NULL || node->type != AST_LOOP) - return NULL; - return (struct ast_loop *)node->data; -} - -bool ast_is_loop(struct ast *node) -{ - return node != NULL && node->type == AST_LOOP; -} - -void ast_free_loop(struct ast_loop *loop_data) -{ - if (loop_data == NULL) - return; - ast_free(&loop_data->condition); - ast_free(&loop_data->body); - free(loop_data); -} diff --git a/src/utils/ast/ast_loop.h b/src/utils/ast/ast_loop.h deleted file mode 100644 index 9718db1..0000000 --- a/src/utils/ast/ast_loop.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef AST_LOOP_H -#define AST_LOOP_H - -#include "ast_base.h" - -struct ast_loop -{ - // Repeat body while condition is true - struct ast *condition; - struct ast *body; -}; - -/** - * Checks if the given AST node is a loop. - */ -bool ast_is_loop(struct ast *node); - -/** - * Retrieves the loop data from the given AST node. - * Assumes that the node is of type AST_LOOP. - */ -struct ast_loop *ast_get_loop(struct ast *node); - -/** - * Creates a new AST node representing a loop. - */ -struct ast *ast_create_loop(struct ast *condition, struct ast *body); - -/* - * @brief: frees the given ast_loop and sets the pointer to NULL. - */ -void ast_free_loop(struct ast_loop *loop_node); - -#endif /* ! AST_LOOP_H */ diff --git a/src/utils/ast/ast_neg.c b/src/utils/ast/ast_neg.c deleted file mode 100644 index 80dc4f3..0000000 --- a/src/utils/ast/ast_neg.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "ast_neg.h" - -#include - -bool ast_is_neg(struct ast *node) -{ - return node != NULL && node->type == AST_NEG; -} - -struct ast_neg *ast_get_neg(struct ast *node) -{ - if (ast_is_neg(node)) - return node->data; - return NULL; -} - -struct ast *ast_create_neg(bool negation, struct ast *child) -{ - struct ast_neg *node = malloc(sizeof(struct ast_neg)); - if (!node) - return NULL; - - node->negation = negation; - node->child = child; - return ast_create(AST_NEG, node); -} - -void ast_free_neg(struct ast_neg *node) -{ - if (!node) - return; - ast_free(&node->child); - free(node); -} diff --git a/src/utils/ast/ast_neg.h b/src/utils/ast/ast_neg.h deleted file mode 100644 index 738c246..0000000 --- a/src/utils/ast/ast_neg.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef AST_NEG_H -#define AST_NEG_H - -#include "ast_base.h" - -struct ast_neg -{ - bool negation; // True negates the child's output - struct ast *child; -}; - -bool ast_is_neg(struct ast *node); -struct ast_neg *ast_get_neg(struct ast *node); -struct ast *ast_create_neg(bool negation, struct ast *child); -void ast_free_neg(struct ast_neg *node); - -#endif /* ! AST_NEG_H */ diff --git a/src/utils/ast/ast_pipe.c b/src/utils/ast/ast_pipe.c deleted file mode 100644 index 92754cf..0000000 --- a/src/utils/ast/ast_pipe.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "ast_pipe.h" - -#include - -bool ast_is_pipe(struct ast *node) -{ - return node != NULL && node->type == AST_REDIR; -} - -struct ast_pipe *ast_get_pipe(struct ast *node) -{ - if (ast_is_pipe(node)) - return node->data; - return NULL; -} - -struct ast *ast_create_pipe(struct ast *left, struct ast *right) -{ - struct ast_pipe *node = malloc(sizeof(struct ast_pipe)); - if (!node) - return NULL; - node->left = left; - node->right = right; - - return ast_create(AST_PIPE, node); -} - -void ast_free_pipe(struct ast_pipe *node) -{ - if (!node) - return; - ast_free(&node->left); - ast_free(&node->right); - free(node); -} diff --git a/src/utils/ast/ast_pipe.h b/src/utils/ast/ast_pipe.h deleted file mode 100644 index 930cb2c..0000000 --- a/src/utils/ast/ast_pipe.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AST_PIPE_H -#define AST_PIPE_H - -#include "ast_base.h" - -struct ast_pipe -{ - struct ast *left; - struct ast *right; - // Output of left will be redirected to right stdin -}; - -bool ast_is_pipe(struct ast *node); -struct ast_pipe *ast_get_pipe(struct ast *node); -struct ast *ast_create_pipe(struct ast *left, struct ast *right); -void ast_free_pipe(struct ast_pipe *node); - -#endif /* ! AST_PIPE_H */ diff --git a/src/utils/ast/ast_redir.c b/src/utils/ast/ast_redir.c deleted file mode 100644 index d1dcedb..0000000 --- a/src/utils/ast/ast_redir.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "ast_redir.h" - -#include - -bool ast_is_redir(struct ast *node) -{ - return node != NULL && node->type == AST_REDIR; -} - -struct ast_redir *ast_get_redir(struct ast *node) -{ - if (ast_is_redir(node)) - return (struct ast_redir *)node->data; - return NULL; -} - -struct ast *ast_create_redir(char *filename, int io_number, - enum ast_redir_type type) -{ - struct ast_redir *redir = malloc(sizeof(struct ast_redir)); - if (!redir) - return NULL; - redir->filename = - filename; // Takes ownership? Usually yes in simple ASTs, or dup. Let's - // assume pointer copy for now, but user must manage memory. - redir->io_number = io_number; - redir->type = type; - - return ast_create(AST_REDIR, redir); -} - -void ast_free_redir(struct ast_redir *redir) -{ - if (!redir) - return; - free(redir->filename); - free(redir); -} diff --git a/src/utils/ast/ast_redir.h b/src/utils/ast/ast_redir.h deleted file mode 100644 index 9d9a9d3..0000000 --- a/src/utils/ast/ast_redir.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef AST_REDIR_H -#define AST_REDIR_H - -#include "ast_base.h" - -enum ast_redir_type -{ - AST_REDIR_TYPE_NULL, - AST_REDIR_TYPE_LESS, // < - AST_REDIR_TYPE_GREAT, // > - AST_REDIR_TYPE_LESSGREAT, // <> - AST_REDIR_TYPE_DGREAT, // >> - AST_REDIR_TYPE_LESSAND, // <& - AST_REDIR_TYPE_GREATAND, // >& - AST_REDIR_TYPE_CLOBBER // >| -}; - -struct ast_redir -{ - char *filename; - int io_number; // The FD being redirected (default -1 if not specified, - // implies 0 or 1 based on type) - enum ast_redir_type type; - int saved_fd; // To store the original FD for restoration (-1 before save) -}; - -bool ast_is_redir(struct ast *node); -struct ast_redir *ast_get_redir(struct ast *node); -struct ast *ast_create_redir(char *filename, int io_number, - enum ast_redir_type type); -void ast_free_redir(struct ast_redir *redir); - -#endif /* ! AST_REDIR_H */ 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/ast/ast_void.c b/src/utils/ast/ast_void.c deleted file mode 100644 index e7d2dee..0000000 --- a/src/utils/ast/ast_void.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "ast_void.h" - -#include -#include - -bool ast_is_void(struct ast *node) -{ - return node != NULL && node->type == AST_VOID; -} - -struct ast *ast_create_void(void) -{ - return ast_create(AST_VOID, NULL); -} diff --git a/src/utils/ast/ast_void.h b/src/utils/ast/ast_void.h deleted file mode 100644 index 05a5933..0000000 --- a/src/utils/ast/ast_void.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AST_VOID_H -#define AST_VOID_H - -#include "../lists/lists.h" -#include "ast_base.h" - -/** - * Checks if the given AST node is of type AST_VOID. - */ -bool ast_is_void(struct ast *node); - -/** - * Creates a new AST node representing NOTHING - * WARNING: data will be a NULL pointer - */ -struct ast *ast_create_void(void); - -#endif /* ! AST_VOID_H */ diff --git a/src/utils/ast/ast_word.c b/src/utils/ast/ast_word.c deleted file mode 100644 index d83489c..0000000 --- a/src/utils/ast/ast_word.c +++ /dev/null @@ -1,49 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "ast_word.h" - -#include -#include -#include - -struct ast *ast_create_word(char *word) -{ - struct ast_word *ast_node = malloc(sizeof(struct ast_word)); - if (ast_node == NULL) - return NULL; - - ast_node->type = AST_WORD; - ast_node->word = strdup(word); - struct ast *res = ast_create(AST_WORD, ast_node); - if (res == NULL) - { - free(ast_node->word); - free(ast_node); - return NULL; - } - - return res; -} - -struct ast_word *ast_get_word(struct ast *node) -{ - if (node == NULL || node->type != AST_WORD) - return NULL; - - return node->data; -} - -bool ast_is_word(struct ast *node) -{ - return node && node->type == AST_WORD; -} - -void ast_free_word(struct ast_word *ast_node) -{ - if (ast_node == NULL) - return; - - if (ast_node->word != NULL) - free(ast_node->word); - - free(ast_node); -} diff --git a/src/utils/ast/ast_word.h b/src/utils/ast/ast_word.h deleted file mode 100644 index a049003..0000000 --- a/src/utils/ast/ast_word.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef AST_WORD_H -#define AST_WORD_H - -#include "ast_base.h" - -struct ast_word -{ - enum ast_type type; - char *word; -}; - -/** - * Checks if the given AST node is a command. - */ -bool ast_is_word(struct ast *node); - -/** - * Retrieves the command data from the given AST node. - * Assumes that the node is of type AST_CMD. - */ -struct ast_word *ast_get_word(struct ast *node); - -/** - * Creates a new AST node representing a command. - */ -struct ast *ast_create_word(char *word); - -/* - * @brief: frees the given ast_command and sets the pointer to NULL. - */ -void ast_free_word(struct ast_word *ast_node); - -#endif /* ! AST_WORD_H */ diff --git a/src/utils/hash_map/hash_map.c b/src/utils/hash_map/hash_map.c deleted file mode 100644 index 0da4fc4..0000000 --- a/src/utils/hash_map/hash_map.c +++ /dev/null @@ -1,216 +0,0 @@ -#include "hash_map.h" - -#include -#include -#include -#include -#include -#include - -#include "../ast/ast.h" - -/* -** Hash the key using FNV-1a 32 bits hash algorithm. -*/ -static size_t hash(const char *key) -{ - if (key == NULL) - return 0; - - uint32_t hash = 2166136261; // FNV offset basis - uint32_t prime = 16777619; // FNV prime - - while (*key != 0) - { - hash ^= *key; - hash *= prime; - key++; - } - - return hash; -} - -static void destroy_pair_list(struct pair_list **p) -{ - free((char *)(*p)->key); - free((*p)->value); - free((*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)); - if (p != NULL) - { - p->data = calloc(size, sizeof(struct pair_list *)); - if (p->data == NULL) - { - free(p); - return NULL; - } - p->size = size; - } - return p; -} - -bool hash_map_insert(struct hash_map *hash_map, const char *key, void *value, - bool *updated) -{ - if (hash_map == NULL || hash_map->size == 0 || key == NULL || value == NULL) - return false; - - size_t h = hash(key); - struct pair_list **l = hash_map->data; - size_t i = h % hash_map->size; - - if (l[i] != NULL) - { - // check if key is in linked list - struct pair_list *iter = l[i]; - while (iter) - { - if (strcmp(iter->key, key) == 0) - { - // update - free(iter->value); - iter->value = value; - if (updated) - *updated = true; - return true; - } - iter = iter->next; - } - - // if not found => collision - } - struct pair_list *p = malloc(sizeof(struct pair_list)); - if (p == NULL) - return false; - - if (updated) - *updated = false; - - p->key = key; - p->value = value; - p->next = l[i]; - l[i] = p; - l[i] = p; - return true; -} - -void hash_map_free(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(&prev); - } - } - free((*hash_map)->data); - free(*hash_map); - *hash_map = NULL; - } -} - -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 *)) -{ - if (hash_map == NULL) - return; - - struct pair_list *iter; - for (size_t i = 0; i < hash_map->size; i++) - { - iter = hash_map->data[i]; - while (iter != NULL) - { - fn(iter->key, iter->value); - iter = iter->next; - } - } -} - -const void *hash_map_get(const struct hash_map *hash_map, const char *key) -{ - if (hash_map == NULL || hash_map->size == 0) - return NULL; - - size_t h = hash(key); - size_t i = h % hash_map->size; - struct pair_list *l = hash_map->data[i]; - while (l != NULL) - { - if (strcmp(l->key, key) == 0) - return l->value; - l = l->next; - } - return NULL; -} - -bool hash_map_remove(struct hash_map *hash_map, const char *key) -{ - if (hash_map == NULL || hash_map->size == 0) - return false; - - size_t h = hash(key); - size_t i = h % hash_map->size; - struct pair_list *l = hash_map->data[i]; - struct pair_list *p = NULL; - - while (l != NULL) - { - if (strcmp(l->key, key) == 0) - { - if (p != NULL) - p->next = l->next; - else - hash_map->data[i] = l->next; - destroy_pair_list(&l); - return true; - } - p = l; - l = l->next; - } - return false; -} diff --git a/src/utils/hash_map/hash_map.h b/src/utils/hash_map/hash_map.h deleted file mode 100644 index 048a882..0000000 --- a/src/utils/hash_map/hash_map.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef HASH_MAP_H -#define HASH_MAP_H - -#include -#include - -struct pair_list -{ - const char *key; - void *value; - struct pair_list *next; -}; - -struct hash_map -{ - struct pair_list **data; - size_t size; -}; - -struct hash_map *hash_map_init(size_t size); - -/** - * @brief Inserts a key-value pair into the hash map. Key and value are expected - * to be on the heap and will be managed by the hash map. It means they are - * consumed by this function. If the key already exists, its value is updated - * and the key is not consumed so it must be freed by the caller. - * - * @param hash_map The hash map. - * @param key The key to insert. - * @param value The value to insert. - * @param updated If not NULL, set to true if the key was already present and - * updated, false if the key was newly inserted. - * @return true on success, false on failure. - */ -bool hash_map_insert(struct hash_map *hash_map, const char *key, void *value, - bool *updated); - -void hash_map_free(struct hash_map **hash_map); - -void hash_map_foreach(struct hash_map *hash_map, - void (*fn)(const char *, const void *)); - -const void *hash_map_get(const struct hash_map *hash_map, const char *key); - -bool hash_map_remove(struct hash_map *hash_map, const char *key); - -#endif /* ! HASH_MAP_H */ diff --git a/src/utils/lists/lists.h b/src/utils/lists/lists.h deleted file mode 100644 index 9f8cebb..0000000 --- a/src/utils/lists/lists.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef LISTS_H -#define LISTS_H - -#include - -struct list -{ - void *data; - struct list *next; -}; - -/* -** @brief Insert a node containing `value` at the beginning of the list. -** @return `NULL` if an error occured. -*/ -struct list *list_prepend(struct list *list, void *value); - -/* -** Return the lenght of the list. -** Return `0` if the list is empty. -*/ -size_t list_length(struct list *list); - -/* -** Display the list contents on `stdout`. -** Nothing is displayed if the list is empty. -*/ -void list_print(struct list *list); - -/* -** Release the memory used by the list. -** Does nothing if `list` is `NULL`. -*/ -void list_destroy(struct list **list); - -/* -** Release the memory used by the list and its content -** Does nothing if `list` is `NULL`. -*/ -void list_deep_destroy(struct list *l); - -/* -** Append a node containing `value` at the end of the list. -** Return `NULL` if an error occured. -*/ -// START PROTO list_append -struct list *list_append(struct list *list, void *value); -// END PROTO list_append - -/* -** Insert a node containing `value` at the index `index` in the list. -** If the index is greater than the length of the list, the behaviour is the -** same as `list_append`. -** Return `NULL` if an error occured. -*/ -// START PROTO list_insert -struct list *list_insert(struct list *list, void *value, size_t index); -// END PROTO list_insert - -/* -** Remove the element at the index `index`. -** Return `NULL` if an error occured. -*/ -// START PROTO list_remove -struct list *list_remove(struct list *list, size_t index); -// END PROTO list_remove - -/* -** Return the position of the first node containing `value`. -** Return `-1` if nothing is found. -*/ -// START PROTO list_find -int list_find(struct list *list, void *value); -// END PROTO list_find - -/* -** Concatenate the list `list2` at the end of the list `list`. -** Return `list2` if `list` is `NULL`. -*/ -// START PROTO list_concat -// struct list *list_concat(struct list *list, struct list *list2); -// END PROTO list_concat - -/* -** Sort the elements of the list in ascending order. -** Return the new list. -*/ -// START PROTO list_sort -// struct list *list_sort(struct list *list); -// END PROTO list_sort - -/* -** Invert the order of the elements of the list. -** Return the new list. -*/ -// START PROTO list_reverse -// struct list *list_reverse(struct list *list); -// END PROTO list_reverse - -/* -** Split the list at index `index`. -** First part goes in `list` and contains the element at `index`. -** Second part is returned. -** Return `NULL` if `list` is `NULL` or `index` is invalid. -*/ -// START PROTO list_split -// struct list *list_split(struct list *list, size_t index); -// END PROTO list_split - -/** - * @brief: Folds the list from left to right using func and an accumulator. - */ -void list_fold(struct list *list, void *acc, void (*func)(void *, void *)); - -#endif /* ! LISTS_H */ diff --git a/src/utils/lists/lists1.c b/src/utils/lists/lists1.c deleted file mode 100644 index 521e131..0000000 --- a/src/utils/lists/lists1.c +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include -#include - -#include "lists.h" - -struct list *list_prepend(struct list *list, void *value) -{ - struct list *new_elt = malloc(sizeof(struct list)); - if (new_elt == NULL) - { - return NULL; - } - new_elt->next = list; - new_elt->data = value; - - return new_elt; -} - -size_t list_length(struct list *list) -{ - size_t len = 0; - while (list != NULL) - { - len++; - list = list->next; - } - return len; -} - -void list_print(struct list *list) -{ - if (list == NULL) - { - return; - } - - while (list != NULL) - { - if (list->next != NULL) - { - printf("%p ", list->data); - } - else - { - printf("%p\n", list->data); - } - list = list->next; - } -} - -void list_destroy(struct list **list) -{ - struct list *elt = *list; - struct list *next_elt; - while (elt != NULL) - { - next_elt = elt->next; - free(elt); - elt = next_elt; - } - *list = NULL; -} - -struct list *list_append(struct list *list, void *value) -{ - if (list == NULL) - { - struct list *new_elt = malloc(sizeof(struct list)); - new_elt->data = value; - new_elt->next = NULL; - return new_elt; - } - - struct list *elt = list; - - while (elt->next != NULL) - { - elt = elt->next; - } - - struct list *new_elt = malloc(sizeof(struct list)); - new_elt->data = value; - new_elt->next = NULL; - elt->next = new_elt; - - return list; -} diff --git a/src/utils/lists/lists2.c b/src/utils/lists/lists2.c deleted file mode 100644 index 745098a..0000000 --- a/src/utils/lists/lists2.c +++ /dev/null @@ -1,107 +0,0 @@ -#include - -#include "lists.h" - -/* - * - ******************* - * Advanced * - ******************* - * - */ - -struct list *list_insert(struct list *list, void *value, size_t index) -{ - if (list == NULL || index == 0) - { - struct list *new_elt = malloc(sizeof(struct list)); - new_elt->data = value; - new_elt->next = list; - return new_elt; - } - - struct list *elt = list; - - for (size_t i = 0; i < index - 1; i++) - { - if (elt->next == NULL) - { - break; - } - elt = elt->next; - } - - struct list *new_elt = malloc(sizeof(struct list)); - new_elt->data = value; - new_elt->next = elt->next; - elt->next = new_elt; - - return list; -} - -struct list *list_remove(struct list *list, size_t index) -{ - struct list *elt = list; - struct list *prev_elt; - - if (index == 0) - { - struct list *res = elt->next; - free(elt); - return res; - } - - for (size_t i = 0; i < index; i++) - { - if (elt == NULL) - { - return list; - } - prev_elt = elt; - elt = elt->next; - } - if (elt == NULL) - { - return list; - } - - prev_elt->next = elt->next; - free(elt); - - return list; -} - -int list_find(struct list *list, void *value) -{ - if (list == NULL) - { - return -1; - } - - int res = 0; - while (list->data != value) - { - list = list->next; - res++; - if (list == NULL) - { - return -1; - } - } - return res; -} - -struct list *list_concat(struct list *list, struct list *list2) -{ - if (list == NULL) - { - return list2; - } - struct list *elt = list; - while (elt->next != NULL) - { - elt = elt->next; - } - elt->next = list2; - return list; -} diff --git a/src/utils/lists/lists3.c b/src/utils/lists/lists3.c deleted file mode 100644 index c18c7be..0000000 --- a/src/utils/lists/lists3.c +++ /dev/null @@ -1,127 +0,0 @@ -#include - -#include "lists.h" -/* - * - ****************** - * Ultimate * - ****************** - * - */ - -static void swap_next(struct list *elt) -{ - void *c = elt->next->data; - elt->next->data = elt->data; - elt->data = c; -} -struct list *list_sort(struct list *list) -{ - // Bubble sort go ! - if (list == NULL) - { - return list; - } - struct list *elt = list; - int len = 0; - while (elt->next != NULL) - { - if (elt->data > elt->next->data) - { - swap_next(elt); - } - elt = elt->next; - len++; - } - - for (int i = 1; i < len; i++) - { - elt = list; - while (elt->next != NULL) - { - if (elt->data > elt->next->data) - { - swap_next(elt); - } - elt = elt->next; - } - } - - return list; -} - -// Old proto -// WARNING no malloc/free allowed (moulinette issue) -struct list *list_reverse(struct list *list) -{ - if (list == NULL) - { - return list; - } - - // Get len - struct list *elt = list; - int len = 0; - while (elt->next != NULL) - { - len++; - elt = elt->next; - } - elt = list; - - // Bring each elt to end - for (int i = 0; i < len; i++) - { - elt = list; - for (int j = 0; j < len - i; j++) - { - swap_next(elt); - elt = elt->next; - } - } - - return list; -} - -struct list *list_split(struct list *list, size_t index) -{ - struct list *elt = list; - for (size_t i = 0; i < index; i++) - { - if (elt == NULL) - { - return NULL; - } - elt = elt->next; - } - if (elt == NULL) - { - return NULL; - } - struct list *res = elt->next; - elt->next = NULL; - return res; -} - -void list_deep_destroy(struct list *l) -{ - struct list *elt = l; - struct list *next_elt; - while (elt != NULL) - { - next_elt = elt->next; - free(elt->data); - free(elt); - elt = next_elt; - } -} - -void list_fold(struct list *list, void *acc, void (*func)(void *, void *)) -{ - struct list *elt = list; - while (elt != NULL) - { - func(acc, elt->data); - elt = elt->next; - } -} 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/src/utils/string_utils/string_utils.c b/src/utils/string_utils/string_utils.c deleted file mode 100644 index 15ad87e..0000000 --- a/src/utils/string_utils/string_utils.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "string_utils.h" - -#include -#include -#include -#include - -char *trim_blank_left(char *str) -{ - if (str == NULL) - return NULL; - - while (*str != '\0' && isblank(*str)) - str++; - - return str; -} - -char *insert_into(char *dest, const char *src, size_t pos, size_t len) -{ - size_t res_len = strlen(dest); - size_t prefix_len = pos; - size_t suffix_len = res_len - (pos + len); - size_t src_len = strlen(src); - size_t new_len = prefix_len + src_len + suffix_len; - - if (dest == NULL || src == NULL || pos + len > res_len) - return NULL; - - if (res_len < new_len) - { - char *p = realloc(dest, new_len + 1); - if (p == NULL) - return NULL; // allocation failure - dest = p; - } - - memmove(dest + pos + src_len, dest + pos + len, suffix_len); - memcpy(dest + pos, src, src_len); - dest[new_len] = 0; - - if (res_len > new_len) - return realloc(dest, new_len + 1); - return dest; -} - -void int_to_str(int value, char *buffer) -{ - if (buffer == NULL) - return; - - snprintf(buffer, 11, "%d", value); -} diff --git a/src/utils/string_utils/string_utils.h b/src/utils/string_utils/string_utils.h deleted file mode 100644 index 36f23ac..0000000 --- a/src/utils/string_utils/string_utils.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef STRING_UTILS_H -#define STRING_UTILS_H - -#include - -/* - * @brief trims leading blank characters (space and tab) from the input string. - * @param str input string to be trimmed. - * @return pointer to the first non-blank character in the string. If the - * string consists entirely of blank characters, returns a pointer to the null - * terminator at the end of the string. - */ -char *trim_blank_left(char *str); - -/** - * Inserts a substring into a destination string at a specified position, - * replacing a specified length of characters. - */ -char *insert_into(char *dest, const char *src, size_t pos, size_t len); - -/** - * Converts an integer to its string representation. - * @param value The integer value to convert. - * @param buffer A character array where the resulting string will be stored. - * The buffer must be at least 11 bytes long to accommodate the largest - * 32-bit integer and the null terminator. - */ -void int_to_str(int value, char *buffer); - -#endif /* STRING_UTILS_H */ diff --git a/src/utils/vars/vars.c b/src/utils/vars/vars.c deleted file mode 100644 index 1d94355..0000000 --- a/src/utils/vars/vars.c +++ /dev/null @@ -1,86 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "vars.h" - -#include -#include -#include -#include -#include -#include - -#include "../hash_map/hash_map.h" -#include "../string_utils/string_utils.h" - -#define VARS_INITIAL_SIZE 16 - -static void vars_default(struct hash_map *vars) -{ - set_var_copy(vars, "?", "0"); - pid_t pid = getpid(); - char pid_str[11]; - int_to_str(pid, pid_str); - set_var_copy(vars, "$", pid_str); -} - -struct hash_map *vars_init(void) -{ - struct hash_map *vars = hash_map_init(VARS_INITIAL_SIZE); - if (vars != NULL) - { - vars_default(vars); - char uid_str[11]; - int_to_str((int)getuid(), uid_str); - set_var_copy(vars, "UID", uid_str); - } - return vars; -} - -short short_random(void) -{ - static bool seeded = false; - if (!seeded) - { - srand((unsigned)time(NULL)); - seeded = true; - } - return (short)(rand() & 0x7FFF); // force 16 bits positive -} - -char *get_var(const struct hash_map *vars, const char *key) -{ - return (char *)hash_map_get(vars, key); -} - -char *get_var_or_env(const struct hash_map *vars, const char *key) -{ - char *value = (char *)hash_map_get(vars, key); - if (value == NULL) - value = getenv(key); - return value; -} - -bool set_var(struct hash_map *vars, const char *key, const char *value, - bool *updated) -{ - return hash_map_insert(vars, key, (void *)value, updated); -} - -bool set_var_copy(struct hash_map *vars, const char *key, const char *value) -{ - char *key_copy = strdup(key); - char *value_copy = strdup(value); - bool updated; - bool res = set_var(vars, key_copy, value_copy, &updated); - if (updated || !res) - free(key_copy); - if (!res) - free(value_copy); - return res; -} - -bool set_var_int(struct hash_map *vars, const char *key, int value) -{ - char value_str[11]; - int_to_str(value, value_str); - return set_var_copy(vars, key, value_str); -} diff --git a/src/utils/vars/vars.h b/src/utils/vars/vars.h deleted file mode 100644 index 19ffae1..0000000 --- a/src/utils/vars/vars.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef VARS_H -#define VARS_H - -#include - -#include "../hash_map/hash_map.h" - -/** - * Initialize a new variables hash map. - */ -struct hash_map *vars_init(void); - -/** - * Generate a random short integer (16 bits positive [0-32767]). - */ -short short_random(void); - -/** - * Get the value of a variable, NULL if not found. - */ -char *get_var(const struct hash_map *vars, const char *key); - -/** - * Get the value of a variable, from the environment if not found in vars, - * NULL if not found in either. - */ -char *get_var_or_env(const struct hash_map *vars, const char *key); - -/** - * Set the value of a variable. Key and value ownership are transferred to - * the hash_map and need to be on the heap. Returns true on success, false on - * failure. - */ -bool set_var(struct hash_map *vars, const char *key, const char *value, - bool *updated); - -/** - * Same as set_var, but makes copies of key and value so you don't have to worry - * about their memory management. Returns true on success, false on failure. - */ -bool set_var_copy(struct hash_map *vars, const char *key, const char *value); - -/** - * Set the value of a variable to an integer. Behavior is similar to - * set_var_copy. - */ -bool set_var_int(struct hash_map *vars, const char *key, int value); - -#endif /* ! VARS_H */ diff --git a/tests/functional/and_ors.sh b/tests/functional/and_ors.sh deleted file mode 100755 index c2f4cf0..0000000 --- a/tests/functional/and_ors.sh +++ /dev/null @@ -1,17 +0,0 @@ -true -echo 'true =' $? - -false -echo 'false =' $? - -false && true -echo 'false && true =' $? - -true && false -echo 'true && false =' $? - -true || false -echo 'true || false =' $? - -false || true -echo 'false || true =' $? diff --git a/tests/functional/func.sh b/tests/functional/func.sh deleted file mode 100755 index dbd0590..0000000 --- a/tests/functional/func.sh +++ /dev/null @@ -1,21 +0,0 @@ -func() -{ - echo hello -} - -arg_func() -{ - echo first argument is "$1" -} - -func_in_func() -{ - func -} - -func_one_line() { echo "this is on one line"; } - -func -arg_func "HERE" -func_in_func -func_one_line diff --git a/tests/functional/loops.sh b/tests/functional/loops.sh deleted file mode 100755 index c4d9896..0000000 --- a/tests/functional/loops.sh +++ /dev/null @@ -1,20 +0,0 @@ -echo "starting tests" - -while false; -do - echo "should NOT be printed" -done - -a='yes' -while [ "$a" -eq "yes" ]; -do - a="no" - echo "should be printed only once" -done; - -while true; -do - echo "yes" -done; - -echo "tests done" diff --git a/tests/functional/run-tests.sh b/tests/functional/run-tests.sh deleted file mode 100755 index 5592869..0000000 --- a/tests/functional/run-tests.sh +++ /dev/null @@ -1,461 +0,0 @@ -#!/bin/sh - - -################### -# Variables # -################### - -executable="$BIN_PATH" -ref_executable="dash" - -tmp_script="/tmp/test_script.sh" -output="/tmp/42sh_tests.output" -ref_output="/tmp/42sh_tests_ref.output" - -total_tests=0 -errors_count=0 -timeouts_count=0 - - -################## -# Colors # -################## - -# Reset -Color_Off='\033[0m' # Text Reset - -# Regular Colors -Black='\033[0;30m' # Black -Red='\033[0;31m' # Red -Green='\033[0;32m' # Green -Yellow='\033[0;33m' # Yellow -Blue='\033[0;34m' # Blue -Purple='\033[0;35m' # Purple -Cyan='\033[0;36m' # Cyan -White='\033[0;37m' # White - -# Bold -BBlack='\033[1;30m' # Black -BRed='\033[1;31m' # Red -BGreen='\033[1;32m' # Green -BYellow='\033[1;33m' # Yellow -BBlue='\033[1;34m' # Blue -BPurple='\033[1;35m' # Purple -BCyan='\033[1;36m' # Cyan -BWhite='\033[1;37m' # White - -# Underline -UBlack='\033[4;30m' # Black -URed='\033[4;31m' # Red -UGreen='\033[4;32m' # Green -UYellow='\033[4;33m' # Yellow -UBlue='\033[4;34m' # Blue -UPurple='\033[4;35m' # Purple -UCyan='\033[4;36m' # Cyan -UWhite='\033[4;37m' # White - -# Background -On_Black='\033[40m' # Black -On_Red='\033[41m' # Red -On_Green='\033[42m' # Green -On_Yellow='\033[43m' # Yellow -On_Blue='\033[44m' # Blue -On_Purple='\033[45m' # Purple -On_Cyan='\033[46m' # Cyan -On_White='\033[47m' # White - -# High Intensity -IBlack='\033[0;90m' # Black -IRed='\033[0;91m' # Red -IGreen='\033[0;92m' # Green -IYellow='\033[0;93m' # Yellow -IBlue='\033[0;94m' # Blue -IPurple='\033[0;95m' # Purple -ICyan='\033[0;96m' # Cyan -IWhite='\033[0;97m' # White - -# Bold High Intensity -BIBlack='\033[1;90m' # Black -BIRed='\033[1;91m' # Red -BIGreen='\033[1;92m' # Green -BIYellow='\033[1;93m' # Yellow -BIBlue='\033[1;94m' # Blue -BIPurple='\033[1;95m' # Purple -BICyan='\033[1;96m' # Cyan -BIWhite='\033[1;97m' # White - -# High Intensity backgrounds -On_IBlack='\033[0;100m' # Black -On_IRed='\033[0;101m' # Red -On_IGreen='\033[0;102m' # Green -On_IYellow='\033[0;103m' # Yellow -On_IBlue='\033[0;104m' # Blue -On_IPurple='\033[0;105m' # Purple -On_ICyan='\033[0;106m' # Cyan -On_IWhite='\033[0;107m' # White - - - -################## -# Wrappers # -################## - - -# @arg test command -# @arg actual code -# @arg ref code -check_result() { - - command="$1" - actual_code="$2" - ref_code="$3" - - test_failed=0 - - # Check return code - if [[ "$actual_code" -eq 124 ]]; then - echo -e $BRed "TIMEOUT" $Color_Off - echo -e ' ' "on '$command'" - echo -e ' ' "Expected code $ref_code but got $actual_code" - ((timeouts_count++)) - test_failed=1 - elif [[ "$actual_code" -ne "$ref_code" ]]; then - echo -e $BRed "FAILED" $Color_Off - echo -e ' ' "on '$command'" - echo -e ' ' "Expected code $ref_code but got $actual_code" - test_failed=1 - # Check output - elif [[ $(diff $output $ref_output > /dev/null) -ne 0 ]]; then - echo -e $BRed "FAILED" $Color_Off - echo -e ' ' "on '$command'" - echo -e ' ' "Output is not the one expected" - test_failed=1 - else - echo -e $BGreen OK $Color_Off - fi - - if [[ "$test_failed" -eq 1 ]]; then - ((errors_count++)) - fi - -} - - -# @arg test name -# @arg input string -test_str() { - - # Check input - if [[ -z "$1" || -z "$2" ]]; then - echo -e $BRed "\n\n" "Issue in the testsuite: test_str: One or more argument is empty" $Color_Off - exit 2 - fi - - echo -e $BBlue "================== $1 ==================" $Color_Off - - echo -e "$2" > $tmp_script - - # Arg - echo -e -n $Blue "= [ARG] " $Color_Off - timeout 0.2 $executable -c "$2" &> $output - actual_code=$? - $ref_executable -c "$2" &> $ref_output - ref_code=$? - ((total_tests++)) - check_result "$2" "$actual_code" "$ref_code" - - # Script - echo -e -n $Blue "= [SCRIPT]" $Color_Off - timeout 0.2 $executable "$tmp_script" &> $output - actual_code=$? - $ref_executable "$tmp_script" &> $ref_output - ref_code=$? - ((total_tests++)) - check_result "$2" "$actual_code" "$ref_code" - - # Stdin - echo -e -n $Blue "= [STDIN] " $Color_Off - timeout 0.2 $executable < "$tmp_script" &> $output - actual_code=$? - $ref_executable < "$tmp_script" &> $ref_output - ref_code=$? - ((total_tests++)) - check_result "$2" "$actual_code" "$ref_code" - - echo -e "\n" - # echo -e $BBlue "===========================================" $Color_Off "\n" -} - -# @arg test name -# @arg input script -test_script() { - - # Check input - if [[ -z "$1" || -z "$2" ]]; then - echo -e $BRed "\n\n" "Issue in the testsuite: test_script: One or more argument is empty" $Color_Off - exit 2 - fi - if [[ ! -f "$2" ]]; then - echo -e $BRed "\n\n" "Issue in the testsuite: test_script: Second argument is not a file" $Color_Off - exit 2 - fi - - echo -e $BBlue "================== $1 ==================" $Color_Off - - # Script - echo -e -n "= [SCRIPT] " - timeout 0.2 $executable "$tmp_script" &> $output - actual_code=$? - $ref_executable "$tmp_script" &> $ref_output - ref_code=$? - ((total_tests++)) - check_result "$2" "$actual_code" "$ref_code" - - # Stdin - echo -e -n "= [STDIN] " - timeout 0.2 $executable < "$tmp_script" &> $output - actual_code=$? - $ref_executable < "$2" &> $ref_output - ref_code=$? - ((total_tests++)) - check_result "$tmp_script" "$actual_code" "$ref_code" - - echo -e "\n" - # echo -e $BBlue "===========================================" $Color_Off "\n" -} - -summarize() { - - # Compute statistics - (( passed_tests = $total_tests - $errors_count )) - (( tests_percentage = 100 * $passed_tests / $total_tests )) - - # Adapt color depending on results - - # Percentage - if [[ tests_percentage -gt 80 ]]; then - coverage_color=$BGreen - elif [[ tests_percentage -gt 50 ]]; then - coverage_color=$BYellow - elif [[ tests_percentage -gt 30 ]]; then - coverage_color=$BOrange - else - coverage_color=$BRed - fi - # Timeouts - if [[ $timeouts_count -eq 0 ]]; then - timeouts_color=$BGreen - else - timeouts_color=$BRed - fi - - # Print - echo -e $BWhite "\n\n""===========" $UWhite"Summary"$Color_Off "\n" - echo -e " Passed $coverage_color$passed_tests/$total_tests$Color_Off tests ($coverage_color$tests_percentage%$Color_Off)" - echo -e " Got $timeouts_color$timeouts_count timeout(s)$Color_Off" - if [ "$OUTPUT_FILE" != "" ]; then - echo $tests_percentage > "$OUTPUT_FILE"; - fi - echo - -} - -# *********************************************************** -################# -# Tests # -################# - -echo -e "\n\n""===$BGreen TestsuitatorX Ultra Pro Max+ 365 Premium Gris Sidéral" "\n\n"$Color_Off - - - -echo -e "\n$BBlue=== Builtins ===$Color_Off" -# echo -test_str "Hello" "echo Hello" -test_str "Hello there" "echo Hello there;" -test_str "Hello there;" "echo Hello there;" -test_str "Hello; there;" "echo Hello; echo there;" -test_str "'Hello'" "echo 'Hello there'" -test_str "Hello;Hello" "echo Hello; echo Wesh attends quoi; echo pouquoi je suis une ligne en dessous" -test_str "Echo -n" "echo -n Hello" -test_str "Echo -e" "echo -e 'Hello\nThere'" -test_str "Empty echo" "echo" -test_str "Spaced echo" " echo spaced " - -test_str "Exit 0" "exit 0" -test_str "Exit 1" "exit 1" - -test_str "True" "true" -test_str "False" "false" - -test_str "cd basic" "cd /tmp; pwd" -test_str "cd maison" "cd; pwd" - -test_str "Alias basic" "alias foo=echo; foo bar" - -test_str "cat $0" "alias foo=echo; foo bar" - - -echo -e "\n$BBlue=== Programs ===$Color_Off" -test_str "LS" "ls" -test_str "Wrong ls" "sl" -test_str "IPA ma gueule" "ip a" -test_str "Wrong ls" "sl --bachibouzouk" -test_str "ls a b c" "ls a b c" -test_str "ls -a --best" "ls -a --best" -test_str "ls -a --help" "ls -a --help" - - -echo -e "\n$BBlue=== Quotes ===$Color_Off" -test_str "Single quotes" "echo 'Single Quote'" -test_str "Double quotes" "echo \"Double Quote\"" -test_str "Mixed quotes 1" "echo \"Mixed 'Quotes'\"" -test_str "Mixed quotes 2" "echo 'Mixed \"Quotes\"'" -test_str "Escaped double quote" "echo \"Escaped \\\"Quote\\\"\"" -test_str "Variable in double quotes" "VAR=val; echo \"Variable \$VAR\"" -test_str "Variable in single quotes" "VAR=val; echo 'Variable \$VAR'" -test_str "Backslash in double quotes" "echo \"Backslash \\\\\"" -test_str "Newline in string" "echo \"New\nline\"" -test_str "Empty quotes" "echo '' \"\"" -test_str "Concatenated quotes" "echo 'a'\"b\"'c'" - - -echo -e "\n$BBlue=== Comments ===$Color_Off" -test_str "Hello commentaire" "echo Hello # Commentaire" -test_str "Comment only" "# Comment only" -test_str "Comment after space" "echo foo #bar" -test_str "Hash inside word" "echo foo#bar" -test_str "Comment with special chars" "# echo 'hidden' $PATH" - - -echo -e "\n$BBlue=== Pipelines ===$Color_Off" -test_str "Simple pipe" "echo Hello | cat" -test_str "Double pipe" "echo Hello | rev | rev" # Pas mal non ? c'est frenssé -test_str "Pipe with grep" "echo 'a\nb\nc' | grep b" -test_str "Pipe exit code (là c'est dur)" "true | false | true" -test_str "Pipe sequence" "echo a | echo b" -test_str "Pipe with chiottes" "ls | wc -l" - - -echo -e "\n$BBlue=== Redirections ===$Color_Off" -test_str "Redirect output" "echo hello > /tmp/test_redir; cat /tmp/test_redir; rm /tmp/test_redir" -test_str "Append output" "echo Hello > /tmp/test_redir; echo World >> /tmp/test_redir; cat /tmp/test_redir; rm /tmp/test_redir" -test_str "Redirect input" "echo Hello > /tmp/test_in; cat < /tmp/test_in; rm /tmp/test_in" -test_str "Redirect stderr" "echo Error >&2" -test_str "Redirect to null" "echo Hello > /dev/null" -test_str "Redirect both" "ls > /dev/null 2> /dev/null" -test_str "Redirect fd" "echo foo 2>&1" -test_str "Clobbering" "echo foo > file; echo bar > file; cat file; rm file" -test_str "Pipe and redirect" "echo foo | cat > file; cat file; rm file" -test_str "Heredoc basic" "cat << EOF\nhello\nEOF" - - -echo -e "\n$BBlue=== And/Or ===$Color_Off" -test_str "AND true" "true && echo Oui" -test_str "AND false" "false && echo Non" -test_str "OR true" "true || echo Non" -test_str "OR false" "false || echo Oui" -test_str "AND OR mixed 1" "true && false || echo Oui" -test_str "AND OR mixed 2" "false || true && echo Oui" -test_str "Sequence AND" "echo a && echo b" -test_str "Sequence OR" "echo a || echo b" -test_str "Negation true" "! true" -test_str "Negation false" "! false" - - -echo -e "\n$BBlue=== If ===$Color_Off" -test_str "If true" "if true; then echo Yes; fi" -test_str "If false else" "if false; then echo No; else echo Yes; fi" -test_str "If elif" "if false; then echo No; elif true; then echo Yes; fi" -test_str "Nested if" "if true; then if true; then echo Nested; fi; fi" -test_str "If multiple commands" "if true; then echo a; echo b; fi" -test_str "If complex condition" "if true && true; then echo Yes; fi" -test_str "If with negation" "if ! false; then echo Yes; fi" -test_str "If faut aller niquer sa mere" "if false; ! false; then echo Embrasse moi; fi" - - -echo -e "\n$BBlue=== Loops ===$Color_Off" -test_str "While false" "while false; do false; done" -test_str "While(false) true" "while false; do true; done" -test_str "Until(true) false" "until true; do false; done" -test_str "Until true" "until true; do true; done" -# test_str "While var" "a=2; while [ \$a -eq 2 ]; do \$a=3; done" -test_str "While arithmetic" "i=0; while [ \$i -lt 3 ]; do echo \$i; i=\$((i+1)); done" -test_str "Until arithmetic" "i=0; until [ \$i -ge 3 ]; do echo \$i; i=\$((i+1)); done" -test_str "While break" "while true; do echo break; break; done" -test_str "While continue" "i=0; while [ \$i -lt 3 ]; do i=\$((i+1)); if [ \$i -eq 2 ]; then continue; fi; echo \$i; done" -test_str "For loop basic" "for i in a b c; do echo \$i; done" -test_str "For loop glob" "for i in *; do echo \$i; done" -test_str "For loop break" "for i in 1 2 3; do break; done" -test_str "Azy continue la con de ta mère" "for i in 1 2 3; do continue; done" -test_str "For loop empty" "for i in; do echo \$i; done" -test_str "For loop variable" "VAR='a b'; for i in \$VAR; do echo \$i; done" - - -echo -e "\n$BBlue=== Case ===$Color_Off" -test_str "Case simple" "case a in a) echo Yes;; esac" -test_str "Case basique" "case z in a) echo No;; *) echo Default;; esac" -test_str "Case multiple patterns" "case a in a|b) echo Yes;; esac" -test_str "Case no match (c pcq t'es moche)" "case z in a) echo No;; esac" -test_str "Case with variable" "v=foo; case \$v in foo) echo Yes;; esac" -test_str "Case nested" "case a in a) case b in b) echo Nested;; esac;; esac" - - -echo -e "\n$BBlue=== Variables ===$Color_Off" -test_str "Set and get" "var=value; echo \$var" -test_str "Braces" "var=value; echo \${var}" -test_str "Multi-word value" "var='a b'; echo \$var" -test_str "Unset" "var=value; unset var; echo \$var" -test_str "Export" "export VAR=val; env | grep VAR" -test_str "Assignment return" "a=1" -test_str "Multiple assignment (ouais askip c possible)" "a=1 b=2; echo \$a \$b" -test_str "Default value" "unset v; echo \${v:-default}" -test_str "Assign default" "unset v; echo \${v:=default}; echo \$v" -test_str "Alternative value" "v=val; echo \${v:+alt}" -test_str "Use default if unset" "echo \${unset_var-default}" - -test_str "\$@" "echo \$@" -test_str "\$*" "echo \$*" -test_str "\$?" "echo \$?" -test_str "\$$" "echo \$$" -test_str "\$1" "echo \$1" -test_str "\$2" "echo \$2" -test_str "\${10}" "echo \${10}" -test_str "\$#" "echo \$#" -test_str "\$RANDOM" "echo \$RANDOM" -test_str "\$UID" "echo \$UID" -test_str "\$OLDPWD" "echo \$OLDPWD" -test_str "\$PWD" "echo \$PWD" -test_str "\$IFS" "echo \$IFS" - -test_str "Default exit status" "echo \$?" -test_str "Exit status" "true; echo \$?" -test_str "PID" "echo \$\$" -test_str "Arg count" "echo \$#" - - -echo -e "\n$BBlue=== Arithmetic expansions de fou furieux ===$Color_Off" -test_str "Arithmetic add" "echo \$((1 + 1))" -test_str "Arithmetic mul" "echo \$((2 * 3))" -test_str "Arithmetic div" "echo \$((10 / 2))" -test_str "Arithmetic nested" "echo \$(( (1+2) * 3 ))" -test_str "Arithmetic mod" "echo \$(( 5 % 2 ))" -test_str "Arithmetic var" "var=1; echo \$(( var + 1 ))" -test_str "Command subst" "echo \$(echo command)" -test_str "Backticks" "echo \`echo backticks\`" -test_str "Tilde" "echo ~" -test_str "Length" "v=abc; echo \${#v}" - - -echo -e "\n$BBlue=== Subshells du démon ===$Color_Off" -test_str "Subshell basic" "(echo a; echo b)" -test_str "Subshell isolation" "var=1; (var=2; echo \$var); echo \$var" -test_str "Subshell exit" "(exit 1); echo \$?" -test_str "Grouping basic" "{ echo a; echo b; }" -test_str "Grouping side effect" "var=1; { var=2; }; echo \$var" -test_str "Nested subshells" "( ( echo nested ) )" -test_str "Subshell redirect" "(echo a) > /tmp/sub; cat /tmp/sub; rm /tmp/sub" -test_str "Group redirect" "{ echo a; } > /tmp/grp; cat /tmp/grp; rm /tmp/grp" - - -summarize diff --git a/tests/unit/expansion/expand.c b/tests/unit/expansion/expand.c deleted file mode 100644 index 777083e..0000000 --- a/tests/unit/expansion/expand.c +++ /dev/null @@ -1,272 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include - -#include "../../../src/expansion/expansion.h" -#include "../../../src/utils/ast/ast.h" -#include "../../../src/utils/hash_map/hash_map.h" -#include "../../../src/utils/vars/vars.h" - -TestSuite(expand); - -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_command *ast_command = ast_get_command(ast); - - bool ret = expand(ast_command, NULL); - cr_expect(ret, "expansion failed with %s", str); - cr_expect_str_eq((char *)ast_command->command->data, "echo something", - "String without variables should remain unchanged"); - ast_free(&ast); -} - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - set_var_copy(vars, "VAR", "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 $VAR", - "Variable should not expand inside single quotes"); - ast_free(&ast); - hash_map_free(&vars); -} - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - set_var_copy(vars, "VAR", "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 $ sign", - "Variable should not expand inside single quotes"); - ast_free(&ast); - hash_map_free(&vars); -} - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - set_var_copy(vars, "VAR", "expanded"); - - bool ret = expand(ast_command, vars); - cr_expect(ret == false, "expansion should fail with %s", str); - ast_free(&ast); - hash_map_free(&vars); -} - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - set_var_copy(vars, "VAR", "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", - "Variable should expand correctly"); - ast_free(&ast); - hash_map_free(&vars); -} - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - set_var_copy(vars, "VAR1", "expanded"); - set_var_copy(vars, "VAR2", "values"); - set_var_copy(vars, "VAR3", "here"); - - 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 values here", - "Multiple variables should expand correctly"); - ast_free(&ast); - hash_map_free(&vars); -} - -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_command *ast_command = ast_get_command(ast); - - setenv("MY_ENV_VAR", "environment", 0); - - bool ret = expand(ast_command, NULL); - cr_expect(ret, "expansion failed with %s", str); - cr_expect_str_eq((char *)ast_command->command->data, "echo environment", - "Environment variable should expand correctly"); - ast_free(&ast); -} - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - - bool ret = expand(ast_command, vars); - cr_expect(ret, "expansion failed with %s", str); - cr_expect_str_eq((char *)ast_command->command->data, "echo ", - "Undefined variable should expand to empty string"); - ast_free(&ast); - hash_map_free(&vars); -} - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - set_var_copy(vars, "A", "expanded"); - set_var_copy(vars, "B", "$A"); - - 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", - "Nested variable should expand correctly"); - ast_free(&ast); - 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); - -// 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); -// } - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - set_var_copy(vars, "VAR1", "hello"); - set_var_copy(vars, "VAR2", "world"); - - bool ret = expand(ast_command, vars); - cr_expect(ret, "expansion failed with %s", str); - cr_expect_str_eq((char *)ast_command->command->data, "echo helloworld", - "Adjacent variables should expand correctly"); - ast_free(&ast); - hash_map_free(&vars); -} - -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_command *ast_command = ast_get_command(ast); - - bool ret = expand(ast_command, NULL); - cr_expect(ret, "expansion failed with %s", str); - int rnd = atoi((char *)ast_command->command->data); - cr_assert(rnd >= 0 && rnd <= 32767, - "RANDOM variable should expand to a value between 0 and 32767"); - ast_free(&ast); -} - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - bool ret = expand(ast_command, vars); - cr_expect(ret, "expansion failed with %s", str); - int pid = atoi((char *)ast_command->command->data); - cr_assert(pid == getpid(), - "$$ variable should expand to the pid of the process"); - ast_free(&ast); - hash_map_free(&vars); -} - -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_command *ast_command = ast_get_command(ast); - - struct hash_map *vars = vars_init(); - bool ret = expand(ast_command, vars); - cr_expect(ret, "expansion failed with %s", str); - int code = atoi((char *)ast_command->command->data); - cr_assert(code == 0, - "$? variable should expand to the last exit code (default 0)"); - ast_free(&ast); - hash_map_free(&vars); -} 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 deleted file mode 100644 index 4dc9e08..0000000 --- a/tests/unit/expansion/parse_var.c +++ /dev/null @@ -1,143 +0,0 @@ -#include -#include - -#include "../../../src/expansion/expansion.h" - -TestSuite(parse_var_name); - -Test(parse_var_name, basic_variable) -{ - char *input = "$MY_VAR"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 7); - cr_expect_str_eq(extracted_var, "MY_VAR"); - free(extracted_var); -} - -Test(parse_var_name, multi_basic_variable) -{ - char *input = "$MY$VAR"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 3); - cr_expect_str_eq(extracted_var, "MY"); - free(extracted_var); - - input += r; - r = parse_var_name(input, &extracted_var); - - cr_expect(r == 4); - cr_expect_str_eq(extracted_var, "VAR"); - free(extracted_var); -} - -Test(parse_var_name, variable_with_braces) -{ - char *input = "${MY_VAR}"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 9); - cr_expect_str_eq(extracted_var, "MY_VAR"); - free(extracted_var); -} - -Test(parse_var_name, special_variable) -{ - char *input = "$1"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 2); - cr_expect_str_eq(extracted_var, "1"); - free(extracted_var); -} - -Test(parse_var_name, special_variable_with_braces) -{ - char *input = "${1}"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 4); - cr_expect_str_eq(extracted_var, "1"); - free(extracted_var); -} - -Test(parse_var_name, incomplete_braces) -{ - char *input = "${MY_VAR"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 0); - cr_expect(extracted_var == NULL); -} - -Test(parse_var_name, empty_braces) -{ - char *input = "${}"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 0); - cr_expect(extracted_var == NULL); -} - -Test(parse_var_name, dollar_sign_only) -{ - char *input = "$"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 0); - cr_expect(extracted_var == NULL); -} - -Test(parse_var_name, variable_followed_by_dollar) -{ - char *input = "$MY$VAR$"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 3); - cr_expect_str_eq(extracted_var, "MY"); - free(extracted_var); - - input += r; - r = parse_var_name(input, &extracted_var); - - cr_expect(r == 4); - cr_expect_str_eq(extracted_var, "VAR"); - free(extracted_var); - - input += r; - r = parse_var_name(input, &extracted_var); - - cr_expect(r == 0); - cr_expect(extracted_var == NULL); -} - -Test(parse_var_name, special_variable_followed_by_text) -{ - char *input = "$1VAR"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 2); - cr_expect_str_eq(extracted_var, "1"); - free(extracted_var); -} - -Test(parse_var_name, bad_variable_with_braces) -{ - char *input = "${1VAR}"; - char *extracted_var = NULL; - size_t r = parse_var_name(input, &extracted_var); - - cr_expect(r == 0); - cr_expect(extracted_var == NULL); -} diff --git a/tests/unit/io_backend/io_backend.c b/tests/unit/io_backend/io_backend.c deleted file mode 100644 index 4449a57..0000000 --- a/tests/unit/io_backend/io_backend.c +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include -#include -#include - -TestSuite(IO_Backend); - -// IOB Init - -Test(IO_Backend, init_null) -{ - struct iob_context ctx = { .mode = IOB_MODE_NULL, .args = NULL }; - int actual = iob_init(&ctx); - int expected = IOB_ERROR_BAD_ARG; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); -} - -Test(IO_Backend, init_stdin) -{ - struct iob_context ctx = { .mode = IOB_MODE_STDIN, .args = NULL }; - int actual = iob_init(&ctx); - int expected = 0; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); - iob_close(); -} - -// WARNING: this one could fail because of iob_close in the previous test -// Same applies for other tests -Test(IO_Backend, init_script) -{ - char *script_name = "script.tmp"; - struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = script_name }; - // Create file - FILE *f = fopen(script_name, "w"); - fclose(f); - - int actual = iob_init(&ctx); - int expected = 0; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); - iob_close(); - remove(script_name); -} - -Test(IO_Backend, init_script_not_a_file) -{ - char *script_name = "not_a_file.tmp"; - struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = script_name }; - int actual = iob_init(&ctx); - int expected = IOB_ERROR_CANNOT_OPEN_FILE; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); -} - -Test(IO_Backend, init_script_null) -{ - struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = NULL }; - int actual = iob_init(&ctx); - int expected = IOB_ERROR_CANNOT_OPEN_FILE; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); -} - -Test(IO_Backend, init_cmd) -{ - char *cmd = "iamacommand --yesido"; - struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = cmd }; - int actual = iob_init(&ctx); - int expected = 0; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); - iob_close(); -} - -Test(IO_Backend, init_cmd_null) -{ - struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = NULL }; - int actual = iob_init(&ctx); - int expected = IOB_ERROR_BAD_ARG; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); -} - -Test(IO_Backend, init_already_init) -{ - char *cmd = "iamacommand --yesido"; - struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = cmd }; - iob_init(&ctx); - int actual = iob_init(&ctx); - int expected = IOB_ERROR_MODULE_ALREADY_INITIALIZED; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); - iob_close(); -} - -Test(IO_Backend, close_not_init) -{ - iob_close(); // Shouldn't do anything -} - -// IOB Stream -// TODO diff --git a/tests/unit/lexer/lexer_tests.c b/tests/unit/lexer/lexer_tests.c deleted file mode 100644 index ecf662f..0000000 --- a/tests/unit/lexer/lexer_tests.c +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "io_backend/io_backend.h" -#include "lexer/lexer.h" - -TestSuite(peek_token); - -Test(peek_token, basic_empty) -{ - // simulates input - char command[] = ""; - struct iob_context context = { IOB_MODE_CMD, command }; - iob_init(&context); - - struct lexer_context ctx = { .end_previous_token = NULL, - .remaining_chars = 0, - .only_digits = false, - .previous_token = NULL, - .current_token = NULL }; - - // test - struct token *tok = peek_token(&ctx); - - // expected - enum token_type type_expected = TOKEN_EOF; - char *string_expected = NULL; - - cr_assert(tok->data == string_expected); - cr_assert(type_expected == tok->type); - - free_token(&tok); -} - -Test(peek_token, basic_word) -{ - // simulates input - char command[] = "hello"; - struct iob_context context = { IOB_MODE_CMD, command }; - iob_init(&context); - - struct lexer_context ctx = { .end_previous_token = NULL, - .remaining_chars = 0, - .only_digits = false, - .previous_token = NULL, - .current_token = NULL }; - - // test - struct token *tok = peek_token(&ctx); - - // expected - enum token_type type_expected = TOKEN_WORD; - char string_expected[] = "hello"; - - cr_assert(eq(str, string_expected, tok->data)); - cr_assert(type_expected == tok->type); - free_token(&tok); -} \ No newline at end of file diff --git a/tests/unit/utils/args.c b/tests/unit/utils/args.c deleted file mode 100644 index 9f601bf..0000000 --- a/tests/unit/utils/args.c +++ /dev/null @@ -1,178 +0,0 @@ -#include "../../../src/utils/args/args.h" - -#include -#include -#include - -#include "../../../src/utils/hash_map/hash_map.h" -#include "../../../src/utils/vars/vars.h" - -TestSuite(utils_args); - -Test(utils_args, basic_command) -{ - int argc = 3; - struct args_options options; - char *input[] = { "program", "-c", "echo Hello, World!" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r == 0); - // cr_expect(options.pretty_print == false); - cr_expect(options.verbose == false); - cr_expect(options.type == INPUT_CMD); - cr_expect(eq(options.input_source, "echo Hello, World!")); - hash_map_free(&vars); -} - -Test(utils_args, basic_command_with_flags) -{ - int argc = 5; - struct args_options options; - /* char *input[] = { "program", "--pretty-print", "-c", "echo Hello, - World!", - "--verbose" };*/ - char *input[] = { "program", "-c", "echo Hello, World!", "--verbose" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r == 0); - // cr_expect(options.pretty_print == true); - cr_expect(options.verbose == true); - cr_expect(options.type == INPUT_CMD); - cr_expect(eq(options.input_source, "echo Hello, World!")); - hash_map_free(&vars); -} - -Test(utils_args, basic_file_input) -{ - int argc = 2; - struct args_options options; - char *input[] = { "program", "input.txt" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r == 0); - // cr_expect(options.pretty_print == false); - cr_expect(options.verbose == false); - cr_expect(options.type == INPUT_FILE); - cr_expect(eq(options.input_source, "input.txt")); - hash_map_free(&vars); -} - -Test(utils_args, basic_file_input_with_flags) -{ - int argc = 4; - struct args_options options; - // char *input[] = { "program", "--verbose", "input.txt", "--pretty-print" - // }; - char *input[] = { "program", "--verbose", "input.txt" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r == 0); - // cr_expect(options.pretty_print == true); - cr_expect(options.verbose == true); - cr_expect(options.type == INPUT_FILE); - cr_expect(eq(options.input_source, "input.txt")); - hash_map_free(&vars); -} - -Test(utils_args, basic_stdin_input) -{ - int argc = 1; - struct args_options options; - char *input[] = { "program" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r == 0); - // cr_expect(options.pretty_print == false); - cr_expect(options.verbose == false); - cr_expect(options.type == INPUT_STDIN); - cr_expect(options.input_source == NULL); - hash_map_free(&vars); -} - -Test(utils_args, pretty_print_and_verbose_flags) -{ - int argc = 3; - struct args_options options; - // char *input[] = { "program", "--pretty-print", "--verbose" }; - char *input[] = { "program", "--verbose" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r == 0); - // cr_expect(options.pretty_print == true); - cr_expect(options.verbose == true); - cr_expect(options.type == INPUT_STDIN); - cr_expect(options.input_source == NULL); - hash_map_free(&vars); -} - -Test(utils_args, missing_command_after_c, .init = cr_redirect_stderr) -{ - int argc = 2; - struct args_options options; - char *input[] = { "program", "-c" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r != 0); - cr_assert_stderr_eq_str("No command provided after -c\n"); - hash_map_free(&vars); -} - -Test(utils_args, unknown_option, .init = cr_redirect_stderr) -{ - int argc = 2; - struct args_options options; - char *input[] = { "program", "--unknown" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r != 0); - cr_assert_stderr_eq_str("Unknown option: --unknown\n"); - hash_map_free(&vars); -} - -Test(utils_args, multiple_command, .init = cr_redirect_stderr) -{ - int argc = 4; - struct args_options options; - char *input[] = { "program", "exec.sh", "-c", "echo World" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r != 0); - cr_assert_stderr_eq_str("Multiple input sources specified: echo World\n"); - hash_map_free(&vars); -} - -Test(utils_args, command_with_additional_arguments) -{ - int argc = 5; - struct args_options options; - char *input[] = { "program", "-c", "echo Hello", "arg1", "arg2" }; - - struct hash_map *vars = vars_init(); - int r = args_handler(argc, input, &options, vars); - - cr_expect(r == 0); - cr_expect(options.type == INPUT_CMD); - cr_expect(eq(options.input_source, "echo Hello")); - cr_expect_str_eq(get_var(vars, "0"), "program"); - cr_expect_str_eq(get_var(vars, "1"), "arg1"); - cr_expect_str_eq(get_var(vars, "2"), "arg2"); - hash_map_free(&vars); -} diff --git a/tests/unit/utils/hash_map.c b/tests/unit/utils/hash_map.c deleted file mode 100644 index 3c3bd04..0000000 --- a/tests/unit/utils/hash_map.c +++ /dev/null @@ -1,217 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "../../../src/utils/hash_map/hash_map.h" - -#include -#include -#include -#include - -TestSuite(utils_hash_map); - -Test(utils_hash_map, init_free) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - hash_map_free(&map); -} - -Test(utils_hash_map, insert_basic) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool updated = false; - bool res = hash_map_insert(map, strdup("key1"), strdup("value1"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, false); - - hash_map_free(&map); -} - -Test(utils_hash_map, insert_multiple) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool updated = false; - bool res = hash_map_insert(map, strdup("key1"), strdup("value1"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, false); - res = hash_map_insert(map, strdup("key2"), strdup("value2"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, false); - - hash_map_free(&map); -} - -Test(utils_hash_map, insert_update) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool updated = false; - bool res = hash_map_insert(map, strdup("key1"), strdup("value1"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, false); - res = hash_map_insert(map, "key1", strdup("value2"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, true); - - hash_map_free(&map); -} - -Test(utils_hash_map, insert_update_multiple) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool updated = false; - bool res = hash_map_insert(map, strdup("key1"), strdup("value1"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, false); - res = hash_map_insert(map, strdup("key2"), strdup("value2"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, false); - res = hash_map_insert(map, "key1", strdup("value2"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, true); - res = hash_map_insert(map, "key1", strdup("value3"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, true); - - hash_map_free(&map); -} - -Test(utils_hash_map, get_basic) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool updated = false; - bool res = hash_map_insert(map, strdup("key1"), strdup("value1"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, false); - - char *value = (char *)hash_map_get(map, "key1"); - cr_expect_str_eq(value, "value1"); - - hash_map_free(&map); -} - -Test(utils_hash_map, get_after_update) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool updated = false; - bool res = hash_map_insert(map, strdup("key1"), strdup("value1"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, false); - - res = hash_map_insert(map, "key1", strdup("value2"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, true); - - char *value = (char *)hash_map_get(map, "key1"); - cr_expect_str_eq(value, "value2"); - - hash_map_free(&map); -} - -Test(utils_hash_map, get_unknown_key) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - char *value = (char *)hash_map_get(map, "unknown_key"); - cr_expect_null(value); - - hash_map_free(&map); -} - -Test(utils_hash_map, delete_key) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool updated = false; - bool res = hash_map_insert(map, strdup("key1"), strdup("value1"), &updated); - cr_expect_eq(res, true); - cr_expect_eq(updated, false); - - res = hash_map_remove(map, "key1"); - cr_expect_eq(res, true); - - char *value = (char *)hash_map_get(map, "key1"); - cr_expect_null(value); - - hash_map_free(&map); -} - -Test(utils_hash_map, delete_unknown_key) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool res = hash_map_remove(map, "unknown_key"); - cr_expect_eq(res, false); - - hash_map_free(&map); -} - -Test(utils_hash_map, free_nonnull_map) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool res = hash_map_insert(map, strdup("key1"), strdup("value1"), NULL); - cr_expect_eq(res, true); - res = hash_map_insert(map, strdup("key2"), strdup("value2"), NULL); - cr_expect_eq(res, true); - - hash_map_free(&map); -} - -Test(utils_hash_map, free_null_map) -{ - hash_map_free(NULL); -} - -static size_t count = 0; -void foreach_fn(const char *key, const void *value) -{ - printf("Key: %s, Value: %s\n", key, (const char *)value); - count++; -} - -Test(utils_hash_map, foreach, .init = cr_redirect_stdout) -{ - struct hash_map *map = hash_map_init(10); - cr_expect_not_null(map); - cr_expect_eq(map->size, 10); - - bool res = hash_map_insert(map, strdup("key1"), strdup("value1"), NULL); - cr_expect_eq(res, true); - res = hash_map_insert(map, strdup("key2"), strdup("value2"), NULL); - cr_expect_eq(res, true); - - count = 0; - hash_map_foreach(map, foreach_fn); - fflush(stdout); - cr_expect_eq(count, 2); - cr_expect_stdout_eq_str( - "Key: key2, Value: value2\nKey: key1, Value: value1\n"); - - hash_map_free(&map); -} diff --git a/tests/unit/utils/insert_into.c b/tests/unit/utils/insert_into.c deleted file mode 100644 index 0bcc833..0000000 --- a/tests/unit/utils/insert_into.c +++ /dev/null @@ -1,85 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include - -#include "../../../src/utils/string_utils/string_utils.h" - -TestSuite(insert_into); - -Test(insert_into, basic) -{ - char *dest = strdup("The is nice."); - const char *src = "weather"; - size_t pos = 4; - - char *result = insert_into(dest, src, pos, 6); - - cr_expect(result != NULL); - cr_expect(eq(str, result, "The weather is nice.")); - - if (result) - free(result); -} - -Test(insert_into, begin) -{ - char *dest = strdup("Hello World!"); - const char *src = "Hi"; - size_t pos = 0; - - char *result = insert_into(dest, src, pos, 5); - - cr_expect(result != NULL); - cr_expect(eq(str, result, "Hi World!")); - - if (result) - free(result); -} - -Test(insert_into, end) -{ - char *dest = strdup("The number is 1024"); - const char *src = "2048"; - size_t pos = 14; - - char *result = insert_into(dest, src, pos, 4); - - cr_expect(result != NULL); - cr_expect(eq(str, result, "The number is 2048")); - - if (result) - free(result); -} - -Test(insert_into, big) -{ - char *dest = strdup("I could insert [VAR] here."); - const char *src = "a very very long string"; - size_t pos = 15; - - char *result = insert_into(dest, src, pos, 5); - - cr_expect(result != NULL); - cr_expect(eq(str, result, "I could insert a very very long string here.")); - - if (result) - free(result); -} - -Test(insert_into, small) -{ - char *dest = strdup("I could insert [VARNAME_IS_SO_LONG] string here."); - const char *src = "a short"; - size_t pos = 15; - - char *result = insert_into(dest, src, pos, 20); - - cr_expect(result != NULL); - cr_expect(eq(str, result, "I could insert a short string here.")); - - if (result) - free(result); -} diff --git a/tests/unit/utils/lists.c b/tests/unit/utils/lists.c deleted file mode 100644 index c852803..0000000 --- a/tests/unit/utils/lists.c +++ /dev/null @@ -1,305 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "../../../src/utils/lists/lists.h" - -#include -#include -#include -#include -#include -#include -#include - -TestSuite(lists); - -Test(lists, append_empty) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, prepend_empty) -{ - struct list *lst = NULL; - lst = list_prepend(lst, (void *)1); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, insert_empty) -{ - struct list *lst = NULL; - lst = list_insert(lst, (void *)1, 0); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, insert_out_of_bounds) -{ - struct list *lst = NULL; - lst = list_insert(lst, (void *)1, 5); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, remove_out_of_bounds) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_remove(lst, 5); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, append_multiple) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - lst = list_append(lst, (void *)3); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next->data == (void *)2); - cr_expect(lst->next->next->data == (void *)3); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, prepend_multiple) -{ - struct list *lst = NULL; - lst = list_prepend(lst, (void *)1); - lst = list_prepend(lst, (void *)2); - lst = list_prepend(lst, (void *)3); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)3); - cr_expect(lst->next->data == (void *)2); - cr_expect(lst->next->next->data == (void *)1); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, insert_multiple) -{ - struct list *lst = NULL; - lst = list_insert(lst, (void *)1, 0); - lst = list_insert(lst, (void *)3, 1); - lst = list_insert(lst, (void *)2, 1); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next->data == (void *)2); - cr_expect(lst->next->next->data == (void *)3); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, append) -{ - struct list *lst = NULL; - lst = list_prepend(lst, (void *)2); - lst = list_prepend(lst, (void *)1); - - lst = list_append(lst, (void *)3); - - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next->data == (void *)2); - cr_expect(lst->next->next->data == (void *)3); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, prepend) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - - lst = list_prepend(lst, (void *)0); - - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)0); - cr_expect(lst->next->data == (void *)1); - cr_expect(lst->next->next->data == (void *)2); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, insert) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)3); - - lst = list_insert(lst, (void *)2, 1); - lst = list_insert(lst, (void *)0, 0); - - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)0); - cr_expect(lst->next->data == (void *)1); - cr_expect(lst->next->next->data == (void *)2); - cr_expect(lst->next->next->next->data == (void *)3); - cr_expect(lst->next->next->next->next == NULL); - cr_expect(list_length(lst) == 4); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, remove) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - lst = list_append(lst, (void *)3); - lst = list_append(lst, (void *)4); - - lst = list_remove(lst, 1); // remove 2 - lst = list_remove(lst, 2); // remove 4 - lst = list_remove(lst, 0); // remove 1 - - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)3); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, destroy_null) -{ - struct list *lst = NULL; - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, deep_destroy_null) -{ - struct list *lst = NULL; - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, deep_destroy) -{ - struct list *lst = NULL; - lst = list_append(lst, strdup("string1")); - lst = list_append(lst, strdup("string2")); - lst = list_append(lst, strdup("string3")); - - list_deep_destroy(lst); -} - -Test(lists, length_empty) -{ - struct list *lst = NULL; - cr_expect(list_length(lst) == 0); -} - -Test(lists, print_empty, .init = cr_redirect_stdout) -{ - struct list *lst = NULL; - list_print(lst); - cr_expect_stdout_eq_str(""); -} - -Test(lists, print_non_empty, .init = cr_redirect_stdout) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - lst = list_append(lst, (void *)3); - - list_print(lst); - fflush(stdout); - cr_expect_stdout_eq_str("0x1 0x2 0x3\n"); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, find_empty) -{ - struct list *lst = NULL; - cr_expect(list_find(lst, (void *)1) == -1); -} - -Test(lists, find_non_empty) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - lst = list_append(lst, (void *)3); - - cr_expect(list_find(lst, (void *)1) == 0); - cr_expect(list_find(lst, (void *)2) == 1); - cr_expect(list_find(lst, (void *)3) == 2); - cr_expect(list_find(lst, (void *)4) == -1); // not found - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -static void fold_func(void *acc, void *data) -{ - *(int *)acc += *(int *)data; -} - -Test(lists, fold) -{ - struct list *lst = NULL; - int v1 = 10, v2 = 20, v3 = 30; - lst = list_append(lst, &v1); - lst = list_append(lst, &v2); - lst = list_append(lst, &v3); - - int sum = 0; - list_fold(lst, &sum, fold_func); - cr_expect(sum == 60); - - list_destroy(&lst); - cr_expect(lst == NULL); -} diff --git a/tests/unit/utils/utils_tests.c b/tests/unit/utils/utils_tests.c deleted file mode 100644 index b7c326f..0000000 --- a/tests/unit/utils/utils_tests.c +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "../../../src/utils/string_utils/string_utils.h" - -TestSuite(string_utils); - -Test(string_utils, skipblank_basic) -{ - char input[] = " Hello World"; - char expected_str[] = "Hello World"; - - char *trimmed = trim_blank_left(input); - ssize_t offset = trimmed - input; - ssize_t expected = 2; - cr_expect(eq(str, trimmed, expected_str)); - cr_expect(offset == expected); -} - -Test(string_utils, skipblank_noblank) -{ - char input[] = "Hello World"; - char expected_str[] = "Hello World"; - - char *trimmed = trim_blank_left(input); - ssize_t offset = trimmed - input; - ssize_t expected = 0; - cr_expect(eq(str, trimmed, expected_str)); - cr_expect(offset == expected); -} - -Test(string_utils, skipblank_tab) -{ - char input[] = "\tHello World"; - char expected_str[] = "Hello World"; - - char *trimmed = trim_blank_left(input); - ssize_t offset = trimmed - input; - ssize_t expected = 1; - cr_expect(eq(str, trimmed, expected_str)); - cr_expect(offset == expected); -} - -Test(string_utils, skipblank_space_tab) -{ - char input[] = " \tHello World"; - char expected_str[] = "Hello World"; - - char *trimmed = trim_blank_left(input); - ssize_t offset = trimmed - input; - ssize_t expected = 2; - cr_expect(eq(str, trimmed, expected_str)); - cr_expect(offset == expected); -} - -Test(string_utils, skipblank_2tab_1space) -{ - char input[] = "\t \tHello World"; - char expected_str[] = "Hello World"; - - char *trimmed = trim_blank_left(input); - ssize_t offset = trimmed - input; - ssize_t expected = 3; - cr_expect(eq(str, trimmed, expected_str)); - cr_expect(offset == expected); -} - -Test(string_utils, skipblank_a_lot) -{ - char input[] = "\t \t \tHello World"; - char expected_str[] = "Hello World"; - - char *trimmed = trim_blank_left(input); - ssize_t offset = trimmed - input; - ssize_t expected = 8; - cr_expect(eq(str, trimmed, expected_str)); - cr_expect(offset == expected); -} - -Test(string_utils, skipblank_newline) -{ - char input[] = "\nHello World"; - char expected_str[] = "\nHello World"; - - char *trimmed = trim_blank_left(input); - ssize_t offset = trimmed - input; - ssize_t expected = 0; - cr_expect(eq(str, trimmed, expected_str)); - cr_expect(offset == expected); -} - -Test(string_utils, skipblank_nul) -{ - char *input = NULL; - - char *trimmed = trim_blank_left(input); - ssize_t offset = trimmed - input; - ssize_t expected = 0; - cr_expect(input == NULL); - cr_expect(offset == expected); -} diff --git a/tests/unit/utils/vars.c b/tests/unit/utils/vars.c deleted file mode 100644 index 73784cf..0000000 --- a/tests/unit/utils/vars.c +++ /dev/null @@ -1,85 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "../../../src/utils/vars/vars.h" - -#include -#include -#include -#include -#include - -#include "../../../src/utils/hash_map/hash_map.h" -#include "../../../src/utils/string_utils/string_utils.h" - -TestSuite(utils_vars); - -Test(utils_vars, init_free) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - hash_map_free(&map); -} - -Test(utils_vars, get_defaults) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - cr_assert_str_eq(get_var(map, "?"), "0"); - char int_str[11]; - int_to_str((int)getpid(), int_str); - cr_assert_str_eq(get_var(map, "$"), int_str); - int_to_str((int)getuid(), int_str); - cr_assert_str_eq(get_var(map, "UID"), int_str); - - hash_map_free(&map); -} - -Test(utils_vars, set_vars) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - set_var_copy(map, "key1", "value1"); - cr_assert_str_eq(get_var(map, "key1"), "value1"); - set_var_copy(map, "key2", "value2"); - cr_assert_str_eq(get_var(map, "key2"), "value2"); - - hash_map_free(&map); -} - -Test(utils_vars, get_env_vars) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - cr_assert_eq(get_var_or_env(map, "ENV_TEST"), NULL); - setenv("ENV_TEST", "value1", 0); - cr_assert_str_eq(get_var_or_env(map, "ENV_TEST"), "value1"); - setenv("ENV_TEST", "value2", 1); - cr_assert_str_eq(get_var_or_env(map, "ENV_TEST"), "value2"); - - hash_map_free(&map); -} - -Test(utils_vars, set_vars_update) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - set_var_copy(map, "key", "value1"); - cr_assert_str_eq(get_var(map, "key"), "value1"); - set_var_copy(map, "key", "value2"); - cr_assert_str_eq(get_var(map, "key"), "value2"); - - hash_map_free(&map); -} - -Test(utils_vars, set_vars_int) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - set_var_int(map, "key1", 100); - cr_assert_str_eq(get_var(map, "key1"), "100"); - set_var_int(map, "key1", 200); - cr_assert_str_eq(get_var(map, "key1"), "200"); - set_var_int(map, "key2", 10); - cr_assert_str_eq(get_var(map, "key2"), "10"); - - hash_map_free(&map); -} diff --git a/tests/wrap.sh b/tests/wrap.sh deleted file mode 100755 index 58519d3..0000000 --- a/tests/wrap.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -if [ "$BIN_PATH" = "" ]; -then export BIN_PATH="$(pwd)/42sh" -fi - -if [ "$COVERAGE" = "yes" ]; #coverage -then (./testsuite || true) && ../tests/functional/run-tests.sh -else ../tests/functional/run-tests.sh -fi -echo bin path: "$BIN_PATH" -echo output file: "$OUTPUT_FILE"