From 52b653454ff0428f4eac753a855a0d46fef1688a Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:26:52 +0200 Subject: [PATCH 01/12] Adding new feature backticks (command substitution) --- src/parsing/backticks.c | 138 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/parsing/backticks.c 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; +} From 3429ba0783f64352997f76888ab6d436570b4d16 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:33:09 +0200 Subject: [PATCH 02/12] {~} Fixing one_backtick function --- src/parsing/backticks.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parsing/backticks.c b/src/parsing/backticks.c index 0f0e248..01f84ad 100644 --- a/src/parsing/backticks.c +++ b/src/parsing/backticks.c @@ -115,7 +115,6 @@ static char *one_backtick(char *current, int i, mysh_t *shell) if (end == -1) return current; - new_line = replace_backtick(current, i, end, shell); free(current); return new_line; } From ece23e8e450f959c18642cb4aa89d1d3e765258a Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:37:07 +0200 Subject: [PATCH 03/12] {+} adding variable in function --- src/parsing/backticks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parsing/backticks.c b/src/parsing/backticks.c index 01f84ad..0f0e248 100644 --- a/src/parsing/backticks.c +++ b/src/parsing/backticks.c @@ -115,6 +115,7 @@ static char *one_backtick(char *current, int i, mysh_t *shell) if (end == -1) return current; + new_line = replace_backtick(current, i, end, shell); free(current); return new_line; } From 759ce0d6ac7e35504025f08ace612cfef294c263 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:38:34 +0200 Subject: [PATCH 04/12] {+} adding unit test --- tests/unit_tests.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 234a3d8..46236ac 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1372,3 +1372,19 @@ 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, basic_substitution) +{ + mysh_t shell; + char *envp[] = {"PATH=/bin:/usr/bin", NULL}; + char *result; + + shell.env = env_to_list(envp); + shell.alias = NULL; + shell.last_status = 0; + result = expand_backticks("echo `echo hello`", &shell); + cr_assert_not_null(result); + cr_assert_str_eq(result, "echo hello"); + free(result); + free_env_list(shell.env); +} From 7b5c9a52f7e419d1616905a4e238e83739dec440 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:38:58 +0200 Subject: [PATCH 05/12] {+} adding unit test --- tests/unit_tests.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 46236ac..057c4c0 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1388,3 +1388,18 @@ Test(backticks, basic_substitution) free(result); free_env_list(shell.env); } + +Test(backticks, no_backticks) +{ + mysh_t shell; + char *envp[] = {"PATH=/bin:/usr/bin", NULL}; + char *result; + + shell.env = env_to_list(envp); + shell.alias = 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); +} \ No newline at end of file From b64d085b30da45785d1a63ed27e6f98dec6e8a0e Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:39:16 +0200 Subject: [PATCH 06/12] {+} adding unit test --- tests/unit_tests.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 057c4c0..143d498 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1389,6 +1389,21 @@ Test(backticks, basic_substitution) free_env_list(shell.env); } +Test(backticks, no_backticks) +{ + mysh_t shell; + char *envp[] = {"PATH=/bin:/usr/bin", NULL}; + char *result; + + shell.env = env_to_list(envp); + shell.alias = 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, no_backticks) { mysh_t shell; From b077ac3968e55d3ae8dd6deccfd1c4d971507ec6 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:40:06 +0200 Subject: [PATCH 07/12] {+} adding function in Makefile --- Makefile | 1 + 1 file changed, 1 insertion(+) 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 \ From 3810a22bee7c6563419dbf6c1ee807103fcd4de0 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:40:58 +0200 Subject: [PATCH 08/12] {+} adding function in my.h --- include/my.h | 1 + 1 file changed, 1 insertion(+) 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); From 4592ba57455477da06b024f793bec0020cc607db Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:43:51 +0200 Subject: [PATCH 09/12] {+} adding backticks in core --- src/core/mysh.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/mysh.c b/src/core/mysh.c index 6d64d7a..a2551f0 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,6 +62,8 @@ static char *prepare_line(char *line, mysh_t *shell) my_putstr("\n"); } history_add(shell->history, expanded); + with_backticks = expand_backticks(expanded, shell); + free(expanded); return expanded; } From c50d698f104cb3dc78ee95fd76b259ea6cfffbaa Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:45:32 +0200 Subject: [PATCH 10/12] {~} fixing function in core --- src/core/mysh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/mysh.c b/src/core/mysh.c index a2551f0..aeeb5ab 100644 --- a/src/core/mysh.c +++ b/src/core/mysh.c @@ -64,7 +64,7 @@ static char *prepare_line(char *line, mysh_t *shell) history_add(shell->history, expanded); with_backticks = expand_backticks(expanded, shell); free(expanded); - return expanded; + return with_backticks; } static int process_ast(char *expanded, mysh_t *shell, char **env) From c75786caa807d4949d4cc9bb288249e22261ed19 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 15:53:47 +0200 Subject: [PATCH 11/12] {+} adding quotes check in core --- src/core/mysh.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/mysh.c b/src/core/mysh.c index aeeb5ab..5cb2594 100644 --- a/src/core/mysh.c +++ b/src/core/mysh.c @@ -62,6 +62,10 @@ static char *prepare_line(char *line, mysh_t *shell) my_putstr("\n"); } history_add(shell->history, expanded); + if (quotes_check(expanded) == 0) { + free(expanded); + return NULL; + } with_backticks = expand_backticks(expanded, shell); free(expanded); return with_backticks; From b9f53b73f8dddb1f404375d68a537cd47437db7b Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 5 May 2026 16:14:07 +0200 Subject: [PATCH 12/12] {+} adding unit_tests.c --- tests/unit_tests.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 143d498..6929812 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1373,23 +1373,7 @@ Test(builtin_where, multiple_args, .init = redirect_all_std) cr_assert_stderr_eq_str(""); } -Test(backticks, basic_substitution) -{ - mysh_t shell; - char *envp[] = {"PATH=/bin:/usr/bin", NULL}; - char *result; - - shell.env = env_to_list(envp); - shell.alias = NULL; - shell.last_status = 0; - result = expand_backticks("echo `echo hello`", &shell); - cr_assert_not_null(result); - cr_assert_str_eq(result, "echo hello"); - free(result); - free_env_list(shell.env); -} - -Test(backticks, no_backticks) +Test(backticks, no_backticks_returns_copy) { mysh_t shell; char *envp[] = {"PATH=/bin:/usr/bin", NULL}; @@ -1397,6 +1381,7 @@ Test(backticks, no_backticks) 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"); @@ -1404,7 +1389,7 @@ Test(backticks, no_backticks) free_env_list(shell.env); } -Test(backticks, no_backticks) +Test(backticks, empty_string) { mysh_t shell; char *envp[] = {"PATH=/bin:/usr/bin", NULL}; @@ -1412,9 +1397,10 @@ Test(backticks, no_backticks) 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"); + result = expand_backticks("", &shell); + cr_assert_str_eq(result, ""); free(result); free_env_list(shell.env); -} \ No newline at end of file +}