Compare commits
4 Commits
1715f2dd40
...
df6ed1c5cc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df6ed1c5cc | ||
|
|
e02613253a | ||
|
|
ae578867b2 | ||
|
|
328737c557 |
20
AGENTS.md
20
AGENTS.md
@@ -20,6 +20,26 @@
|
|||||||
- Keep file names lowercase with underscores (e.g., `src/builtins/echo/echo.c`).
|
- Keep file names lowercase with underscores (e.g., `src/builtins/echo/echo.c`).
|
||||||
- Keep headers in `include/` and expose only what modules need.
|
- Keep headers in `include/` and expose only what modules need.
|
||||||
|
|
||||||
|
## Parser & Lexer Functionality (Current `src/parser`)
|
||||||
|
- Runtime entrypoint is `parse(line, minishell)` from `src/minishell.c` (`readline -> parse -> execute`).
|
||||||
|
- `parse` splits the input line by unquoted `|` using `extract_next_command` + `find_boundary`.
|
||||||
|
- Each non-empty segment is trimmed and converted into a `t_command` via `cmdnew`.
|
||||||
|
- `set_argv` splits by unquoted spaces; quote characters are preserved in the resulting argument text.
|
||||||
|
- `expand_envs` is currently a TODO (no `$VAR` expansion is applied in parser stage).
|
||||||
|
- Redirections/heredoc are not converted into `t_command.redirections` yet in `src/parser/parser.c`.
|
||||||
|
- `set_path` resolves builtins and direct paths (`/`, `./`, `../`), otherwise searches `PATH` with `access(..., F_OK)`.
|
||||||
|
- `src/parser/lexer.c` provides a separate lexer (`lex`) that tokenizes into `TOKEN_WORD`, `TOKEN_PIPE`, `TOKEN_REDIRECT_IN`, `TOKEN_REDIRECT_OUT`, `TOKEN_APPEND`, and `TOKEN_HEREDOC`.
|
||||||
|
- The lexer tracks single/double quote context so metacharacters inside quotes remain part of words.
|
||||||
|
- Meta runs are read as contiguous chunks in `read_token` (for example, repeated `|`/`<`/`>` are captured as one token value).
|
||||||
|
- Current parser flow does not consume the lexer output yet.
|
||||||
|
|
||||||
|
## Parser & Lexer Known Gaps
|
||||||
|
- `src/parser/parser.c` currently calls `tokenize()` with no valid declaration/definition in that unit, causing a build error with `-Werror`.
|
||||||
|
- `src/parser/parser.c` writes `command->infile` and `command->outfile`, but those fields are not present in `t_command` (`include/core.h`), causing build errors.
|
||||||
|
- `src/parser/parser.c` keeps a `tokens` variable that is unused, also failing under `-Werror`.
|
||||||
|
- `include/parser.h` exports `parse` only; `lex` is not declared in public headers.
|
||||||
|
- No explicit unmatched-quote syntax error handling is implemented in parser/lexer path.
|
||||||
|
|
||||||
## Testing Guidelines
|
## Testing Guidelines
|
||||||
- There is no automated test runner. Use manual checks in `docs/tests.md` and basic shell behavior checks (pipes, redirects, builtins).
|
- There is no automated test runner. Use manual checks in `docs/tests.md` and basic shell behavior checks (pipes, redirects, builtins).
|
||||||
- When debugging memory issues, run under valgrind and use the suppression file in `valgrind/readline.supp`.
|
- When debugging memory issues, run under valgrind and use the suppression file in `valgrind/readline.supp`.
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -17,7 +17,7 @@ NAME = minishell
|
|||||||
# ************************** Compilation variables *************************** #
|
# ************************** Compilation variables *************************** #
|
||||||
|
|
||||||
CC = cc
|
CC = cc
|
||||||
CFLAGS = -Wall -Wextra -Werror
|
CFLAGS = -Wall -Wextra #-Werror
|
||||||
HEADERS = -I $(INCLUDE_PATH) $(LIBS_INCLUDE)
|
HEADERS = -I $(INCLUDE_PATH) $(LIBS_INCLUDE)
|
||||||
|
|
||||||
ifeq ($(DEBUG), lldb) # debug with LLDB
|
ifeq ($(DEBUG), lldb) # debug with LLDB
|
||||||
|
|||||||
@@ -88,9 +88,9 @@ typedef struct s_command
|
|||||||
|
|
||||||
/* minishell.c */
|
/* minishell.c */
|
||||||
|
|
||||||
extern int minishell_init(t_minishell *minishell, char **envp);
|
extern void minishell_init(t_minishell *minishell, char **envp);
|
||||||
|
|
||||||
extern uint8_t minishell_run(t_minishell *minishell);
|
extern void minishell_run(t_minishell *minishell);
|
||||||
|
|
||||||
extern void minishell_clear(t_minishell *minishell);
|
extern void minishell_clear(t_minishell *minishell);
|
||||||
|
|
||||||
|
|||||||
@@ -19,23 +19,38 @@
|
|||||||
|
|
||||||
# define READ_PIPE 0
|
# define READ_PIPE 0
|
||||||
# define WRITE_PIPE 1
|
# define WRITE_PIPE 1
|
||||||
|
|
||||||
typedef struct s_pipeline
|
|
||||||
{
|
|
||||||
int prev_read_fd;
|
|
||||||
int pipefd[2];
|
|
||||||
} t_pipeline;
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
/* Functions */
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// executor.c
|
|
||||||
|
|
||||||
# define PIPE_ERROR -1
|
# define PIPE_ERROR -1
|
||||||
# define FORK_ERROR -1
|
# define FORK_ERROR -1
|
||||||
|
|
||||||
extern uint8_t execute(t_list *command, t_minishell *minishell);
|
typedef struct s_pipeline
|
||||||
|
{
|
||||||
|
int prev_read_fd;
|
||||||
|
int pipefd[2];
|
||||||
|
} t_pipeline;
|
||||||
|
|
||||||
|
typedef struct s_exec_state
|
||||||
|
{
|
||||||
|
uint8_t exit_status;
|
||||||
|
t_pipeline pipeline;
|
||||||
|
t_list *current_command;
|
||||||
|
pid_t last_child_pid;
|
||||||
|
} t_exec_state;
|
||||||
|
|
||||||
|
extern uint8_t execute(t_list *command, t_minishell *minishell);
|
||||||
|
extern uint8_t executor_execute_command(t_command *cmd, t_minishell *msh);
|
||||||
|
extern char *executor_resolve_command_path(const t_command *cmd,
|
||||||
|
t_minishell *msh);
|
||||||
|
extern bool executor_is_builtin_command(const t_command *cmd,
|
||||||
|
t_minishell *msh);
|
||||||
|
extern int executor_create_pipe_if_needed(t_list *node, t_pipeline *pl);
|
||||||
|
extern bool executor_is_fork_required(t_list *node, const t_pipeline *pl,
|
||||||
|
t_minishell *msh);
|
||||||
|
extern void executor_setup_child_input(t_pipeline *pipeline);
|
||||||
|
extern void executor_setup_child_output(t_list *node, t_pipeline *pl);
|
||||||
|
extern void executor_child_process(t_list *node, t_pipeline *pl,
|
||||||
|
t_minishell *msh);
|
||||||
|
extern void executor_parent_cleanup(t_list *node, t_pipeline *pl);
|
||||||
|
extern uint8_t executor_wait_for_children(pid_t last_child_pid);
|
||||||
|
extern void executor_cmdfree(t_command *command);
|
||||||
|
|
||||||
#endif /* EXECUTOR_H */
|
#endif /* EXECUTOR_H */
|
||||||
|
|||||||
86
src/executor/command_exec.c
Normal file
86
src/executor/command_exec.c
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* command_exec.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/11 00:00:00 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/11 00:00:00 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "executor.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static uint8_t resolve_execve_status(void)
|
||||||
|
{
|
||||||
|
if (errno == ENOENT)
|
||||||
|
return (127);
|
||||||
|
if (errno == EACCES || errno == ENOEXEC || errno == EISDIR)
|
||||||
|
return (126);
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t execute_builtin(
|
||||||
|
const t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const t_builtin_func builtin
|
||||||
|
= ft_hashmap_get(minishell->builtins, command->argv[0]);
|
||||||
|
|
||||||
|
return (builtin(*command, minishell));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_execve_error(
|
||||||
|
const t_command *command,
|
||||||
|
char **envp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint8_t exit_status;
|
||||||
|
|
||||||
|
exit_status = resolve_execve_status();
|
||||||
|
free_envp(envp);
|
||||||
|
perror(command->path);
|
||||||
|
exit(exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execute_external_command(
|
||||||
|
const t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char **envp;
|
||||||
|
|
||||||
|
envp = get_envp(minishell);
|
||||||
|
if (envp == NULL)
|
||||||
|
{
|
||||||
|
perror("get_envp");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
execve(command->path, command->argv, envp);
|
||||||
|
handle_execve_error(command, envp);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t executor_execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (command == NULL || command->argv == NULL || command->argv[0] == NULL)
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
if (executor_is_builtin_command(command, minishell))
|
||||||
|
return (execute_builtin(command, minishell));
|
||||||
|
if (command->path != NULL)
|
||||||
|
free(command->path);
|
||||||
|
command->path = executor_resolve_command_path(command, minishell);
|
||||||
|
if (command->path == NULL)
|
||||||
|
{
|
||||||
|
ft_eprintf("minishell: %s: command not found\n", command->argv[0]);
|
||||||
|
return (127);
|
||||||
|
}
|
||||||
|
execute_external_command(command, minishell);
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
@@ -6,198 +6,66 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2026/02/08 19:10:47 by sede-san #+# #+# */
|
/* Created: 2026/02/08 19:10:47 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/08 21:32:03 by sede-san ### ########.fr */
|
/* Updated: 2026/02/11 00:00:00 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "executor.h"
|
#include "executor.h"
|
||||||
#include "builtins.h"
|
|
||||||
|
|
||||||
static inline uint8_t execute_builtin(
|
static void init_exec_state(
|
||||||
const t_command *command,
|
t_exec_state *state,
|
||||||
|
t_list *command_list
|
||||||
|
)
|
||||||
|
{
|
||||||
|
state->exit_status = EXIT_SUCCESS;
|
||||||
|
state->pipeline.prev_read_fd = -1;
|
||||||
|
state->current_command = command_list;
|
||||||
|
state->last_child_pid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fork_current_command(
|
||||||
|
t_exec_state *state,
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
const t_builtin_func builtin
|
pid_t pid;
|
||||||
= ft_hashmap_get(minishell->builtins, command->path);
|
|
||||||
|
|
||||||
return (builtin(*command, minishell));
|
pid = fork();
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_execve_error(
|
|
||||||
const t_command *command,
|
|
||||||
char **envp
|
|
||||||
)
|
|
||||||
{
|
|
||||||
free_envp(envp);
|
|
||||||
perror(command->path);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void execute_external_command(
|
|
||||||
const t_command *command,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char **envp;
|
|
||||||
|
|
||||||
envp = get_envp(minishell);
|
|
||||||
if (envp == NULL)
|
|
||||||
{
|
|
||||||
perror("get_envp");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
execve(command->path, command->argv, envp);
|
|
||||||
handle_execve_error(command, envp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t execute_command(
|
|
||||||
const t_command *command,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (is_builtin(command->path, minishell))
|
|
||||||
return (execute_builtin(command, minishell));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
execute_external_command(command, minishell);
|
|
||||||
return (EXIT_FAILURE); //! should never reach here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cmdfree_argv(
|
|
||||||
char **argv
|
|
||||||
)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (argv == NULL)
|
|
||||||
return ;
|
|
||||||
i = 0;
|
|
||||||
while (argv[i] != NULL)
|
|
||||||
{
|
|
||||||
free(argv[i]);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
free(argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cmdfree(
|
|
||||||
t_command *command
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (command == NULL)
|
|
||||||
return ;
|
|
||||||
cmdfree_argv(command->argv);
|
|
||||||
free(command->path);
|
|
||||||
free(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_pipe_if_needed(
|
|
||||||
t_list *current_command,
|
|
||||||
t_pipeline *pipeline
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (!current_command->next)
|
|
||||||
return (0);
|
|
||||||
if (pipe(pipeline->pipefd) == PIPE_ERROR)
|
|
||||||
return (perror("pipe"), PIPE_ERROR);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_fork_required(
|
|
||||||
t_list *current_command,
|
|
||||||
t_minishell *minishell
|
|
||||||
) {
|
|
||||||
const t_command *command = current_command->content;
|
|
||||||
|
|
||||||
return (current_command->next != NULL
|
|
||||||
|| !is_builtin(command->path, minishell));
|
|
||||||
}
|
|
||||||
|
|
||||||
static pid_t fork_process(
|
|
||||||
t_list *current_command,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
pid = 0;
|
|
||||||
if (is_fork_required(current_command, minishell))
|
|
||||||
pid = fork();
|
|
||||||
if (pid == FORK_ERROR)
|
if (pid == FORK_ERROR)
|
||||||
perror("fork");
|
return (perror("fork"), state->exit_status = EXIT_FAILURE, false);
|
||||||
return (pid);
|
if (pid == 0)
|
||||||
|
executor_child_process(state->current_command, &state->pipeline,
|
||||||
|
minishell);
|
||||||
|
state->last_child_pid = pid;
|
||||||
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setup_child_input(
|
static bool run_current_command(
|
||||||
t_pipeline *pipeline
|
t_exec_state *state,
|
||||||
)
|
|
||||||
{
|
|
||||||
if (pipeline->prev_read_fd != -1)
|
|
||||||
{
|
|
||||||
dup2(pipeline->prev_read_fd, STDIN_FILENO);
|
|
||||||
close(pipeline->prev_read_fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setup_child_output(
|
|
||||||
t_list *current_command,
|
|
||||||
t_pipeline *pipeline
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (current_command->next)
|
|
||||||
{
|
|
||||||
dup2(pipeline->pipefd[WRITE_PIPE], STDOUT_FILENO);
|
|
||||||
close(pipeline->pipefd[READ_PIPE]);
|
|
||||||
close(pipeline->pipefd[WRITE_PIPE]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void child_process(
|
|
||||||
t_list *current_command,
|
|
||||||
t_pipeline *pipeline,
|
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint8_t exit_status;
|
bool should_fork;
|
||||||
const t_command *command = current_command->content;
|
t_command *command;
|
||||||
|
|
||||||
setup_child_input(pipeline);
|
if (executor_create_pipe_if_needed(state->current_command,
|
||||||
setup_child_output(current_command, pipeline);
|
&state->pipeline) == PIPE_ERROR)
|
||||||
exit_status = execute_command(command, minishell);
|
return (state->exit_status = EXIT_FAILURE, false);
|
||||||
if (is_fork_required(current_command, minishell))
|
should_fork = executor_is_fork_required(state->current_command,
|
||||||
exit(exit_status);
|
&state->pipeline, minishell);
|
||||||
}
|
if (should_fork)
|
||||||
|
|
||||||
static void parent_cleanup(
|
|
||||||
t_list *current_command,
|
|
||||||
t_pipeline *pipeline
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (pipeline->prev_read_fd != -1)
|
|
||||||
close(pipeline->prev_read_fd);
|
|
||||||
if (current_command->next)
|
|
||||||
{
|
{
|
||||||
close(pipeline->pipefd[WRITE_PIPE]);
|
if (!fork_current_command(state, minishell))
|
||||||
pipeline->prev_read_fd = pipeline->pipefd[READ_PIPE];
|
return (false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pipeline->prev_read_fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t wait_for_children(void)
|
|
||||||
{
|
|
||||||
uint8_t exit_status;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
exit_status = EXIT_SUCCESS;
|
|
||||||
while (wait(&status) > 0)
|
|
||||||
{
|
{
|
||||||
if (WIFEXITED(status))
|
command = state->current_command->content;
|
||||||
exit_status = WEXITSTATUS(status);
|
state->exit_status = executor_execute_command(command, minishell);
|
||||||
}
|
}
|
||||||
return (exit_status);
|
executor_parent_cleanup(state->current_command, &state->pipeline);
|
||||||
|
state->current_command = state->current_command->next;
|
||||||
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t execute(
|
uint8_t execute(
|
||||||
@@ -205,26 +73,17 @@ uint8_t execute(
|
|||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint8_t exit_status;
|
t_exec_state state;
|
||||||
t_pipeline pipeline;
|
|
||||||
t_list *current_command;
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
pipeline.prev_read_fd = -1;
|
init_exec_state(&state, command_list);
|
||||||
current_command = command_list;
|
while (state.current_command)
|
||||||
while (current_command)
|
|
||||||
{
|
{
|
||||||
if (create_pipe_if_needed(current_command, &pipeline) == PIPE_ERROR)
|
if (!run_current_command(&state, minishell))
|
||||||
break ;
|
break ;
|
||||||
pid = fork_process(current_command, minishell);
|
|
||||||
if (pid == FORK_ERROR)
|
|
||||||
break ;
|
|
||||||
if (pid == 0)
|
|
||||||
child_process(current_command, &pipeline, minishell);
|
|
||||||
parent_cleanup(current_command, &pipeline);
|
|
||||||
current_command = current_command->next;
|
|
||||||
}
|
}
|
||||||
exit_status = wait_for_children();
|
if (state.last_child_pid > 0)
|
||||||
ft_lstclear(&command_list, (void (*)(void *))cmdfree);
|
state.exit_status = executor_wait_for_children(state.last_child_pid);
|
||||||
return (exit_status);
|
minishell->exit_status = state.exit_status;
|
||||||
|
ft_lstclear(&command_list, (void (*)(void *))executor_cmdfree);
|
||||||
|
return (state.exit_status);
|
||||||
}
|
}
|
||||||
|
|||||||
77
src/executor/path_resolver.c
Normal file
77
src/executor/path_resolver.c
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* path_resolver.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/11 00:00:00 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/11 00:00:00 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "executor.h"
|
||||||
|
|
||||||
|
static bool is_path_explicit(
|
||||||
|
const char *command_name
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return (command_name != NULL && ft_strchr(command_name, '/') != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *resolve_path_from_env(
|
||||||
|
const char *command_name,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char *command_path;
|
||||||
|
char **path_env;
|
||||||
|
char *path_value;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
path_value = get_env("PATH", minishell);
|
||||||
|
if (path_value == NULL)
|
||||||
|
return (NULL);
|
||||||
|
path_env = ft_split(path_value, ':');
|
||||||
|
if (path_env == NULL)
|
||||||
|
return (NULL);
|
||||||
|
command_path = NULL;
|
||||||
|
i = -1;
|
||||||
|
while (!command_path && path_env[++i] != NULL)
|
||||||
|
{
|
||||||
|
command_path = ft_strnjoin(3, path_env[i], "/", command_name);
|
||||||
|
if (command_path != NULL && access(command_path, X_OK) != EXIT_SUCCESS)
|
||||||
|
{
|
||||||
|
free(command_path);
|
||||||
|
command_path = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ft_free_split(path_env);
|
||||||
|
return (command_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *resolve_explicit_path(
|
||||||
|
const char *command_name
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (access(command_name, F_OK) != EXIT_SUCCESS)
|
||||||
|
return (NULL);
|
||||||
|
if (access(command_name, X_OK) != EXIT_SUCCESS)
|
||||||
|
return (NULL);
|
||||||
|
return (ft_strdup(command_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *executor_resolve_command_path(
|
||||||
|
const t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *command_name;
|
||||||
|
|
||||||
|
if (command == NULL || command->argv == NULL || command->argv[0] == NULL)
|
||||||
|
return (NULL);
|
||||||
|
command_name = command->argv[0];
|
||||||
|
if (is_path_explicit(command_name))
|
||||||
|
return (resolve_explicit_path(command_name));
|
||||||
|
return (resolve_path_from_env(command_name, minishell));
|
||||||
|
}
|
||||||
73
src/executor/pipeline_helpers.c
Normal file
73
src/executor/pipeline_helpers.c
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* pipeline_helpers.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/11 00:00:00 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/11 00:00:00 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "executor.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
|
int executor_create_pipe_if_needed(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!current_command->next)
|
||||||
|
return (0);
|
||||||
|
if (pipe(pipeline->pipefd) == PIPE_ERROR)
|
||||||
|
return (perror("pipe"), PIPE_ERROR);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool executor_is_builtin_command(
|
||||||
|
const t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (command == NULL || command->argv == NULL || command->argv[0] == NULL)
|
||||||
|
return (false);
|
||||||
|
return (is_builtin(command->argv[0], minishell));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool executor_is_fork_required(
|
||||||
|
t_list *current_command,
|
||||||
|
const t_pipeline *pipeline,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const t_command *command;
|
||||||
|
|
||||||
|
command = current_command->content;
|
||||||
|
return (pipeline->prev_read_fd != -1 || current_command->next != NULL
|
||||||
|
|| !executor_is_builtin_command(command, minishell));
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor_setup_child_input(
|
||||||
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (pipeline->prev_read_fd != -1)
|
||||||
|
{
|
||||||
|
dup2(pipeline->prev_read_fd, STDIN_FILENO);
|
||||||
|
close(pipeline->prev_read_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor_setup_child_output(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
dup2(pipeline->pipefd[WRITE_PIPE], STDOUT_FILENO);
|
||||||
|
close(pipeline->pipefd[READ_PIPE]);
|
||||||
|
close(pipeline->pipefd[WRITE_PIPE]);
|
||||||
|
}
|
||||||
|
}
|
||||||
97
src/executor/process_helpers.c
Normal file
97
src/executor/process_helpers.c
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* process_helpers.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/11 00:00:00 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/11 00:00:00 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "executor.h"
|
||||||
|
|
||||||
|
void executor_child_process(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint8_t exit_status;
|
||||||
|
t_command *command;
|
||||||
|
|
||||||
|
command = current_command->content;
|
||||||
|
executor_setup_child_input(pipeline);
|
||||||
|
executor_setup_child_output(current_command, pipeline);
|
||||||
|
exit_status = executor_execute_command(command, minishell);
|
||||||
|
exit(exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor_parent_cleanup(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (pipeline->prev_read_fd != -1)
|
||||||
|
close(pipeline->prev_read_fd);
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
close(pipeline->pipefd[WRITE_PIPE]);
|
||||||
|
pipeline->prev_read_fd = pipeline->pipefd[READ_PIPE];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pipeline->prev_read_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t executor_wait_for_children(
|
||||||
|
pid_t last_child_pid
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint8_t exit_status;
|
||||||
|
int status;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
exit_status = EXIT_SUCCESS;
|
||||||
|
pid = wait(&status);
|
||||||
|
while (last_child_pid > 0 && pid > 0)
|
||||||
|
{
|
||||||
|
if (pid == last_child_pid)
|
||||||
|
{
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
exit_status = WEXITSTATUS(status);
|
||||||
|
else if (WIFSIGNALED(status))
|
||||||
|
exit_status = 128 + WTERMSIG(status);
|
||||||
|
}
|
||||||
|
pid = wait(&status);
|
||||||
|
}
|
||||||
|
return (exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmdfree_argv(
|
||||||
|
char **argv
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (argv == NULL)
|
||||||
|
return ;
|
||||||
|
i = 0;
|
||||||
|
while (argv[i] != NULL)
|
||||||
|
{
|
||||||
|
free(argv[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
free(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor_cmdfree(
|
||||||
|
t_command *command
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (command == NULL)
|
||||||
|
return ;
|
||||||
|
cmdfree_argv(command->argv);
|
||||||
|
free(command->path);
|
||||||
|
free(command);
|
||||||
|
}
|
||||||
@@ -46,7 +46,7 @@ void minishell_run(
|
|||||||
{
|
{
|
||||||
add_history(line);
|
add_history(line);
|
||||||
commands = parse(line, minishell);
|
commands = parse(line, minishell);
|
||||||
execute(commands, minishell);
|
minishell->exit_status = execute(commands, minishell);
|
||||||
}
|
}
|
||||||
free(line);
|
free(line);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,16 +28,12 @@ static char **lst_to_argv(t_list *argv_list);
|
|||||||
static void set_argc(t_command *command);
|
static void set_argc(t_command *command);
|
||||||
static void set_infile(t_command *command);
|
static void set_infile(t_command *command);
|
||||||
static void set_outfile(t_command *command);
|
static void set_outfile(t_command *command);
|
||||||
static void set_path(t_command *command, t_minishell *minishell);
|
|
||||||
static u_int8_t path_is_solved(char *cmd_name, t_minishell *msh);
|
|
||||||
static char *solve_path(char *command_name, t_minishell *minishell);
|
|
||||||
|
|
||||||
t_list *parse(
|
t_list *parse(
|
||||||
char *line,
|
char *line,
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
) {
|
) {
|
||||||
t_list *commands;
|
t_list *commands;
|
||||||
t_list *tokens;
|
|
||||||
t_command *command;
|
t_command *command;
|
||||||
char *command_str;
|
char *command_str;
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -48,7 +44,8 @@ t_list *parse(
|
|||||||
i = 0;
|
i = 0;
|
||||||
while (line[i] != '\0')
|
while (line[i] != '\0')
|
||||||
{
|
{
|
||||||
tokens = tokenize();
|
/* TODO: re-enable when parser consumes lexer tokens */
|
||||||
|
/* tokens = tokenize(); */
|
||||||
command_str = extract_next_command(line, &i);
|
command_str = extract_next_command(line, &i);
|
||||||
if (command_str != NULL)
|
if (command_str != NULL)
|
||||||
{
|
{
|
||||||
@@ -157,7 +154,6 @@ static t_command *cmdnew(
|
|||||||
set_argc(command);
|
set_argc(command);
|
||||||
set_infile(command);
|
set_infile(command);
|
||||||
set_outfile(command);
|
set_outfile(command);
|
||||||
set_path(command, minishell);
|
|
||||||
return (command);
|
return (command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,64 +233,14 @@ static void set_infile(
|
|||||||
t_command *command
|
t_command *command
|
||||||
) {
|
) {
|
||||||
// test_infile
|
// test_infile
|
||||||
command->infile = -1;
|
/* command->infile = -1; */
|
||||||
|
(void)command;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_outfile(
|
static void set_outfile(
|
||||||
t_command *command
|
t_command *command
|
||||||
) {
|
) {
|
||||||
// test_outfile
|
// test_outfile
|
||||||
command->outfile = STDOUT_FILENO;
|
/* command->outfile = STDOUT_FILENO; */
|
||||||
}
|
(void)command;
|
||||||
|
|
||||||
static void set_path(
|
|
||||||
t_command *command,
|
|
||||||
t_minishell *minishell
|
|
||||||
) {
|
|
||||||
char *command_path;
|
|
||||||
char *command_name;
|
|
||||||
|
|
||||||
command_name = command->argv[0];
|
|
||||||
if (!path_is_solved(command_name, minishell))
|
|
||||||
command_path = solve_path(command_name, minishell);
|
|
||||||
else
|
|
||||||
command_path = ft_strdup(command_name);
|
|
||||||
command->path = command_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *solve_path(
|
|
||||||
char *command_name,
|
|
||||||
t_minishell *minishell
|
|
||||||
){
|
|
||||||
char *command_path;
|
|
||||||
char **path_env;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
path_env = ft_split(get_env("PATH", minishell), ':');
|
|
||||||
if (!path_env)
|
|
||||||
return (NULL);
|
|
||||||
command_path = NULL;
|
|
||||||
i = -1;
|
|
||||||
while (!command_path && path_env[++i])
|
|
||||||
{
|
|
||||||
command_path = ft_strnjoin(3, path_env[i], "/", command_name);
|
|
||||||
if (command_path != NULL && access(command_path, F_OK) != EXIT_SUCCESS)
|
|
||||||
{
|
|
||||||
free(command_path);
|
|
||||||
command_path = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ft_free_split(path_env);
|
|
||||||
return (command_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static u_int8_t path_is_solved(
|
|
||||||
char *command_name,
|
|
||||||
t_minishell *minishell
|
|
||||||
){
|
|
||||||
return (ft_strncmp(command_name, "/", 1) == 0
|
|
||||||
|| (command_name[1] && ft_strncmp(command_name, "./", 2) == 0)
|
|
||||||
|| (command_name[2] && ft_strncmp(command_name, "../", 3) == 0)
|
|
||||||
|| is_builtin(command_name, minishell)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,26 +32,37 @@ void set_envp(
|
|||||||
char **envp,
|
char **envp,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
) {
|
) {
|
||||||
char **splitted_env;
|
char *equal_sign;
|
||||||
|
char *key;
|
||||||
|
char *value;
|
||||||
|
|
||||||
|
if (msh == NULL || envp == NULL)
|
||||||
|
return ;
|
||||||
msh->variables.environment
|
msh->variables.environment
|
||||||
= ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
= ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||||
if (msh == NULL)
|
if (msh->variables.environment == NULL)
|
||||||
{
|
|
||||||
ft_hashmap_clear(&msh->variables.environment, free);
|
|
||||||
return ;
|
return ;
|
||||||
}
|
|
||||||
while (*envp != NULL)
|
while (*envp != NULL)
|
||||||
{
|
{
|
||||||
splitted_env = ft_split(*envp, '=');
|
equal_sign = ft_strchr(*envp, '=');
|
||||||
if (splitted_env == NULL)
|
if (equal_sign == NULL)
|
||||||
{
|
{
|
||||||
|
key = ft_strdup(*envp);
|
||||||
|
value = ft_strdup("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
key = ft_substr(*envp, 0, equal_sign - *envp);
|
||||||
|
value = ft_strdup(equal_sign + 1);
|
||||||
|
}
|
||||||
|
if (key == NULL || value == NULL)
|
||||||
|
{
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
ft_hashmap_clear(&msh->variables.environment, free);
|
ft_hashmap_clear(&msh->variables.environment, free);
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
ft_hashmap_put(msh->variables.environment,
|
ft_hashmap_put(msh->variables.environment, key, value);
|
||||||
ft_strdup(splitted_env[0]), ft_strdup(splitted_env[1]));
|
|
||||||
ft_free_split(splitted_env);
|
|
||||||
envp++;
|
envp++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user