Skip to content
Merged
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
1 change: 1 addition & 0 deletions include/my.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 8 additions & 1 deletion src/core/mysh.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
138 changes: 138 additions & 0 deletions src/parsing/backticks.c
Original file line number Diff line number Diff line change
@@ -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;
}
32 changes: 32 additions & 0 deletions tests/unit_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}