diff --git a/Makefile b/Makefile index ae24635..634a243 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,7 @@ SRC = main.c \ src/parsing/tree_search.c \ src/builtins/my_repeat.c \ src/builtins/my_which.c \ + src/parsing/backticks.c \ src/builtins/my_where.c LIB_SRC = lib/my/my_put_nbr.c \ diff --git a/include/my.h b/include/my.h index 19ad99c..d376cd0 100644 --- a/include/my.h +++ b/include/my.h @@ -89,6 +89,7 @@ void history_nav_down(char *buffer, line_state_t *st, history_t *h); history_entry_t *resolve_event(history_t *h, const char *token, int len); char *expand_history_events(const char *line, history_t *h); char *expand_variable(const char *line, mysh_t *shell); +char *expand_backticks(char *line, mysh_t *shell); void free_alias_list(alias_t *head); alias_t *find_alias(alias_t *head, char *alias_name); diff --git a/src/core/mysh.c b/src/core/mysh.c index 6d64d7a..5cb2594 100644 --- a/src/core/mysh.c +++ b/src/core/mysh.c @@ -52,6 +52,7 @@ static char *build_history_path(env_t *env) static char *prepare_line(char *line, mysh_t *shell) { char *expanded; + char *with_backticks; expanded = expand_history_events(line, shell->history); if (expanded == NULL) @@ -61,7 +62,13 @@ static char *prepare_line(char *line, mysh_t *shell) my_putstr("\n"); } history_add(shell->history, expanded); - return expanded; + if (quotes_check(expanded) == 0) { + free(expanded); + return NULL; + } + with_backticks = expand_backticks(expanded, shell); + free(expanded); + return with_backticks; } static int process_ast(char *expanded, mysh_t *shell, char **env) diff --git a/src/parsing/backticks.c b/src/parsing/backticks.c new file mode 100644 index 0000000..0f0e248 --- /dev/null +++ b/src/parsing/backticks.c @@ -0,0 +1,138 @@ +/* +** EPITECH PROJECT, 2026 +** backticks.c +** File description: +** command substitution (backticks) +*/ + +#include "../../include/my.h" + +static void append_to_res(char *result, char *buffer, int n) +{ + buffer[n] = '\0'; + my_strcat(result, buffer); +} + +static char *read_pipe_output(int fd) +{ + char *result = malloc(sizeof(char) * 4096); + char buffer[1024]; + int total = 0; + int n; + + if (result == NULL) + return NULL; + result[0] = '\0'; + n = read(fd, buffer, 1023); + while (n > 0 && total < 3000) { + append_to_res(result, buffer, n); + total += n; + n = read(fd, buffer, 1023); + } + if (total > 0 && result[total - 1] == '\n') + result[total - 1] = '\0'; + return result; +} + +static void run_inner_child(char *cmd, mysh_t *shell, int *pipefd) +{ + token_t *tokens; + ast_node_t *ast; + + dup2(pipefd[1], 1); + close(pipefd[0]); + close(pipefd[1]); + tokens = lexer(cmd); + ast = build_ast(tokens); + if (ast != NULL) { + exec_ast(ast, NULL, shell); + free_ast(ast); + } + exit(0); +} + +static char *exec_inner_command(char *cmd, mysh_t *shell) +{ + int pipefd[2]; + pid_t pid; + char *output; + + if (pipe(pipefd) == -1) + return my_strdup(""); + pid = fork(); + if (pid == 0) + run_inner_child(cmd, shell, pipefd); + close(pipefd[1]); + waitpid(pid, NULL, 0); + output = read_pipe_output(pipefd[0]); + close(pipefd[0]); + return output; +} + +static int find_backtick_end(char *line, int start) +{ + int i = start + 1; + + while (line[i] != '\0') { + if (line[i] == '`') + return i; + i++; + } + return -1; +} + +static char *build_replacement(char *line, char *output, int start, int end) +{ + char *result; + int new_len; + + new_len = start + my_strlen(output) + my_strlen(&line[end + 1]) + 1; + result = malloc(sizeof(char) * new_len); + if (result == NULL) + return NULL; + my_strncpy(result, line, start); + result[start] = '\0'; + my_strcat(result, output); + my_strcat(result, &line[end + 1]); + return result; +} + +static char *replace_backtick(char *line, int start, int end, mysh_t *shell) +{ + char *inner = my_strndup(&line[start + 1], end - start - 1); + char *output = exec_inner_command(inner, shell); + char *result = build_replacement(line, output, start, end); + + free(inner); + free(output); + return result; +} + +static char *one_backtick(char *current, int i, mysh_t *shell) +{ + int end = find_backtick_end(current, i); + char *new_line; + + if (end == -1) + return current; + new_line = replace_backtick(current, i, end, shell); + free(current); + return new_line; +} + +char *expand_backticks(char *line, mysh_t *shell) +{ + char *current = my_strdup(line); + int i = 0; + + if (current == NULL) + return NULL; + while (current != NULL && current[i] != '\0') { + if (current[i] == '`') { + current = one_backtick(current, i, shell); + continue; + } + i++; + } + return current; +} diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 234a3d8..6929812 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1372,3 +1372,35 @@ Test(builtin_where, multiple_args, .init = redirect_all_std) cr_assert_stdout_eq_str("cd is a shell built-in\n"); cr_assert_stderr_eq_str(""); } + +Test(backticks, no_backticks_returns_copy) +{ + mysh_t shell; + char *envp[] = {"PATH=/bin:/usr/bin", NULL}; + char *result; + + shell.env = env_to_list(envp); + shell.alias = NULL; + shell.history = NULL; + shell.last_status = 0; + result = expand_backticks("echo hello", &shell); + cr_assert_str_eq(result, "echo hello"); + free(result); + free_env_list(shell.env); +} + +Test(backticks, empty_string) +{ + mysh_t shell; + char *envp[] = {"PATH=/bin:/usr/bin", NULL}; + char *result; + + shell.env = env_to_list(envp); + shell.alias = NULL; + shell.history = NULL; + shell.last_status = 0; + result = expand_backticks("", &shell); + cr_assert_str_eq(result, ""); + free(result); + free_env_list(shell.env); +}