aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile18
-rw-r--r--include/ast.h19
-rw-r--r--include/eval.h26
-rw-r--r--include/minishell.h36
-rw-r--r--src/ast.c40
-rw-r--r--src/builtin/builtin.c61
-rw-r--r--src/builtin/cd.c2
-rw-r--r--src/builtin/echo.c3
-rw-r--r--src/builtin/env.c3
-rw-r--r--src/builtin/exit.c9
-rw-r--r--src/builtin/export.c12
-rw-r--r--src/builtin/pwd.c4
-rw-r--r--src/builtin/unset.c2
-rw-r--r--src/eval/eval.c85
-rw-r--r--src/eval/exec.c62
-rw-r--r--src/eval/pipe.c48
-rw-r--r--src/main.c8
17 files changed, 334 insertions, 104 deletions
diff --git a/Makefile b/Makefile
index 57a4f39..338a2e6 100644
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,7 @@ all: libft_all prebuild $(NAME)
.PHONY: test
test:
- ./$(TESTEXEC)
+ @./$(TESTEXEC)
.PHONY: prebuild
prebuild:
@@ -52,7 +52,7 @@ prebuild:
$(NAME): $(OBJ)
@echo "Linking: $@"
- $(CC) -o $@ $(OBJ) $(LDFLAGS)
+ @$(CC) -o $@ $(OBJ) $(LDFLAGS)
$(OBJDIR)/%.o: $(SRCDIR)/%.c $(INCLUDE)
@echo "Compiling: $@"
@@ -73,17 +73,25 @@ fclean: libft_fclean
.PHONY: re
re: fclean all
+.PHONY: relocal
+relocal:
+ @echo "Removing objects"
+ @$(RM) -r $(OBJDIR)
+ @echo "Removing exectable"
+ @$(RM) $(NAME)
+relocal: all
+
.PHONY: libft_all
libft_all:
- $(MAKE) -C $(LIBFTDIR) all
+ @$(MAKE) -C $(LIBFTDIR) all
.PHONY: libft_clean
libft_clean:
- $(MAKE) -C $(LIBFTDIR) clean
+ @$(MAKE) -C $(LIBFTDIR) clean
.PHONY: libft_fclean
libft_fclean:
- $(MAKE) -C $(LIBFTDIR) fclean
+ @$(MAKE) -C $(LIBFTDIR) fclean
.PHONY: doc
doc:
diff --git a/include/ast.h b/include/ast.h
index 63dc12c..754956a 100644
--- a/include/ast.h
+++ b/include/ast.h
@@ -27,7 +27,8 @@ typedef enum
SEP_OR,
} t_sep;
-typedef struct s_ast t_ast;
+struct s_ast;
+
/**
** \brief Line struct
** \param left AST to the left of separator
@@ -37,8 +38,8 @@ typedef struct s_ast t_ast;
typedef struct
{
- t_ast *left;
- t_ast *right;
+ struct s_ast *left;
+ struct s_ast *right;
t_sep sep;
} t_line;
@@ -58,6 +59,12 @@ typedef struct
bool is_append;
} t_cmd;
+/**
+** \brief AST node tag (type)
+** \param TAG_CMD Command AST node
+** \param TAG_LINE Line AST node
+*/
+
typedef enum
{
TAG_CMD,
@@ -66,13 +73,13 @@ typedef enum
/**
** \brief AST node struct
-** \param type Node type
+** \param tag Node tag
** \param data Union containning possible node data
** \param data::cmd Command struct
** \param data::line Line struct
*/
-struct s_ast
+typedef struct s_ast
{
t_ast_tag tag;
union
@@ -80,7 +87,7 @@ struct s_ast
t_line line;
t_cmd cmd;
} data;
-};
+} t_ast;
t_ast *ast_new(t_ast_tag tag, void *data);
void ast_destroy(t_ast *ast);
diff --git a/include/eval.h b/include/eval.h
index 0c3240a..55cd497 100644
--- a/include/eval.h
+++ b/include/eval.h
@@ -15,8 +15,8 @@
typedef struct
{
- int in_pipe[2]; // need stack pipe
- int out_pipe[2];
+ int pipe_in[2]; // need stack pipe
+ int pipe_out[2];
t_path path;
t_env env;
} t_eval_state;
@@ -31,13 +31,25 @@ typedef struct
int status;
} t_eval_status;
-
-/**
-** \brief Evaluate an AST
-** \param state State of the evaluation
-** \param ast Abstract syntax tree to evaluate
+/*
+** eval.c
*/
int eval(t_eval_state *state, t_ast *ast);
+/*
+** exec.c
+*/
+
+bool exec_is_path(char *path_str);
+bool exec_is_valid(char *exec_path);
+char *exec_search_path(t_path path, char *path_var, char *exec_name);
+
+/*
+** pipe.c
+*/
+
+int pipe_setup_parent(t_cmd *cmd, int pipe_in[2], int pipe_out[2]);
+int pipe_setup_child(int pipe_in[2], int pipe_out[2]);
+
#endif
diff --git a/include/minishell.h b/include/minishell.h
index 4f33951..a3970dd 100644
--- a/include/minishell.h
+++ b/include/minishell.h
@@ -52,6 +52,8 @@
# define PIPE_READ 0
+# define BUILTIN_NOT_FOUND -2
+
typedef t_ftht* t_path;
typedef t_ftht* t_env;
@@ -72,13 +74,33 @@ char **env_to_array(t_env env);
** builtin*.c - directory with all builtin commands
*/
-int builtin_echo(char **argv);
-int builtin_cd(t_env env, char **argv);
-int builtin_pwd(void);
-int builtin_export(t_env env, char **argv);
-int builtin_unset(t_env env, char **argv);
-int builtin_env(t_env env);
-int builtin_exit(void);
+/**
+** \brief Type of a builtin main function
+*/
+
+typedef int (*t_builtin_func)(char **argv, t_env env);
+
+/**
+** \brief Entry of builtin lookup array
+** \param name Executable name of builtin
+** \param func Associated function
+*/
+
+struct s_builtin_entry
+{
+ char *name;
+ t_builtin_func func;
+};
+
+int builtin_dispatch_run(char **argv, t_env env);
+bool builtin_check_exec_name(char *exec_name);
+int builtin_echo(char **argv, t_env env);
+int builtin_cd(char **argv, t_env env);
+int builtin_pwd(char **argv, t_env env);
+int builtin_export(char **argv, t_env env);
+int builtin_unset(char **argv, t_env env);
+int builtin_env(char **argv, t_env env);
+int builtin_exit(char **argv, t_env env);
/*
** util.c - various utilitary functions
diff --git a/src/ast.c b/src/ast.c
index 7c3fb8f..12761ac 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -1,5 +1,18 @@
+/**
+** \file ast.c
+** \brief AST functions
+*/
+
#include "ast.h"
+/**
+** \brief Create a new AST node according to `tag`
+** \param tag Tag of node
+** \param data Pointer to node data (t_cmd or t_line)
+** which will be copied in ast::data union
+** \return Created node or NULL on error
+*/
+
t_ast *ast_new(t_ast_tag tag, void *data)
{
t_ast *ast;
@@ -17,26 +30,25 @@ t_ast *ast_new(t_ast_tag tag, void *data)
return (ast);
}
-static void cmd_destroy(t_cmd *cmd)
-{
- ft_split_destroy(cmd->argv);
- free(cmd->in);
- free(cmd->out);
-}
-
-static void line_destroy(t_line *line)
-{
- ast_destroy(line->left);
- ast_destroy(line->right);
-}
+/**
+** \brief Destroy an AST node and all his child nodes
+** \param ast AST to destroy
+*/
void ast_destroy(t_ast *ast)
{
if (ast == NULL)
return ;
if (ast->tag == TAG_CMD)
- cmd_destroy(&ast->data.cmd);
+ {
+ ft_split_destroy(ast->data.cmd.argv);
+ free(ast->data.cmd.in);
+ free(ast->data.cmd.out);
+ }
else if (ast->tag == TAG_LINE)
- line_destroy(&ast->data.line);
+ {
+ ast_destroy(ast->data.line.left);
+ ast_destroy(ast->data.line.right);
+ }
free(ast);
}
diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c
new file mode 100644
index 0000000..4889f9c
--- /dev/null
+++ b/src/builtin/builtin.c
@@ -0,0 +1,61 @@
+/**
+** \file builtin.c
+** \brief Builtin functions
+*/
+
+#include "minishell.h"
+
+/**
+** \brief Array storing builtin executable name and associated functions
+*/
+
+static struct s_builtin_entry g_builtin_lookup[] = {
+ {"echo", builtin_echo},
+ {"cd", builtin_cd},
+ {"pwd", builtin_pwd},
+ {"export", builtin_export},
+ {"unset", builtin_unset},
+ {"env", builtin_env},
+ {"exit", builtin_exit},
+};
+
+/**
+** \brief Call builtin function associated with command name
+** \param argv Arguments to the builtin 'main', with argv[0] being the executable name
+** \param env Environment Vector
+** \return Builtin main return status
+*/
+
+int builtin_dispatch_run(char **argv, t_env env)
+{
+ size_t i;
+
+ i = 0;
+ while (i < sizeof(g_builtin_lookup) / sizeof(struct s_builtin_entry))
+ {
+ if (ft_strcmp(g_builtin_lookup[i].name, argv[0]) == 0)
+ return (g_builtin_lookup[i].func(argv, env));
+ i++;
+ }
+ return (BUILTIN_NOT_FOUND);
+}
+
+/**
+** \brief Check if executable name is a builtin
+** \param exec_name Executable name
+** \return True if executable name is a builtin
+*/
+
+bool builtin_check_exec_name(char *exec_name)
+{
+ size_t i;
+
+ i = 0;
+ while (i < sizeof(g_builtin_lookup) / sizeof(struct s_builtin_entry))
+ {
+ if (ft_strcmp(g_builtin_lookup[i].name, exec_name) == 0)
+ return (true);
+ i++;
+ }
+ return (false);
+}
diff --git a/src/builtin/cd.c b/src/builtin/cd.c
index 14c38d2..db629b0 100644
--- a/src/builtin/cd.c
+++ b/src/builtin/cd.c
@@ -5,7 +5,7 @@
#include "minishell.h"
-int builtin_cd(t_env env, char **argv)
+int builtin_cd(char **argv, t_env env)
{
char *path;
diff --git a/src/builtin/echo.c b/src/builtin/echo.c
index b9f2e28..c9e8cc7 100644
--- a/src/builtin/echo.c
+++ b/src/builtin/echo.c
@@ -5,10 +5,11 @@
#include "minishell.h"
-int builtin_echo(char **argv)
+int builtin_echo(char **argv, t_env env)
{
bool newline;
+ (void)env;
newline = ft_strcmp(argv[1], "-n") == 0;
if (newline)
argv++;
diff --git a/src/builtin/env.c b/src/builtin/env.c
index 3869ead..352e2c3 100644
--- a/src/builtin/env.c
+++ b/src/builtin/env.c
@@ -13,8 +13,9 @@ void st_print_env_variable(t_ftht_entry *entry)
ft_putchar('\n');
}
-int builtin_env(t_env env)
+int builtin_env(char **argv, t_env env)
{
+ (void)argv;
ft_htiter(env, st_print_env_variable);
return (0);
}
diff --git a/src/builtin/exit.c b/src/builtin/exit.c
index f4cc4fd..bd41f0f 100644
--- a/src/builtin/exit.c
+++ b/src/builtin/exit.c
@@ -1,4 +1,13 @@
+#include "minishell.h"
+
/**
** \file exit.c
** \brief `exit` builtin
*/
+
+int builtin_exit(char **argv, t_env env)
+{
+ (void)argv;
+ (void)env;
+ return (0);
+}
diff --git a/src/builtin/export.c b/src/builtin/export.c
index c0839b9..1b148a9 100644
--- a/src/builtin/export.c
+++ b/src/builtin/export.c
@@ -5,7 +5,11 @@
#include "minishell.h"
-/* int export(t_env env, char **argv) */
-/* { */
-/* return (0); */
-/* } */
+int builtin_export(char **argv, t_env env)
+{
+ (void)argv;
+ (void)env;
+ /* if (ft_htset(env, ) == NULL) */
+ /* return (-1); */
+ return (0);
+}
diff --git a/src/builtin/pwd.c b/src/builtin/pwd.c
index 813c4c6..6e0971f 100644
--- a/src/builtin/pwd.c
+++ b/src/builtin/pwd.c
@@ -5,10 +5,12 @@
#include "minishell.h"
-int builtin_pwd(void)
+int builtin_pwd(char **argv, t_env env)
{
char buf[PATH_MAX];
+ (void)argv;
+ (void)env;
ft_bzero(buf, PATH_MAX);
if (getcwd(buf, PATH_MAX) == NULL)
return (1);
diff --git a/src/builtin/unset.c b/src/builtin/unset.c
index 64f25f4..1fc5ce1 100644
--- a/src/builtin/unset.c
+++ b/src/builtin/unset.c
@@ -5,7 +5,7 @@
#include "minishell.h"
-int builtin_unset(t_env env, char **argv)
+int builtin_unset(char **argv, t_env env)
{
if (argv[1] == NULL)
return (1);
diff --git a/src/eval/eval.c b/src/eval/eval.c
index 28cb386..d2fab39 100644
--- a/src/eval/eval.c
+++ b/src/eval/eval.c
@@ -5,89 +5,70 @@
#include "eval.h"
+/**
+** \brief Evaluate a line
+** \param state State of the evaluation
+** \param line Line to evaluate
+** \return Last Executed command status or -1 on error
+*/
+
static int eval_line(t_eval_state *state, t_line *line)
{
int status;
if (line->right == NULL)
return (eval(state, line->left));
- status = eval(state, line->left);
+ if ((status = eval(state, line->left)) == -1)
+ return (-1);
if ((line->sep == SEP_AND && status != 0) ||
(line->sep == SEP_OR && status == 0))
return (status);
return (eval(state, line->right));
}
-static bool is_exec_path(char *path_str)
-{
- return (ft_strncmp(path_str, "../", 3) == 0
- || ft_strncmp(path_str, "./", 2) == 0
- || ft_strncmp(path_str, "/", 1) == 0);
-}
-
-static bool is_valid_exec(char *exec_path)
-{
- struct stat statbuf;
-
- if (stat(exec_path, &statbuf) != 0)
- return (false);
- if (!S_ISREG(statbuf.st_mode)) // also need to manage link
- return (false);
- // could test permission but probably handled by execve
- return (true);
-}
-
-static char *search_exec_path(t_path path, char *path_var, char *exec_name)
-{
- char *exec_path;
-
- if (is_exec_path(exec_name))
- return (exec_name);
- // try current first
- if ((exec_path = ft_htget(path, exec_name)) == NULL)
- {
- if (path_update(path, path_var) == NULL) // optimise by not updating not changed path in ht
- return (NULL);
- if ((exec_path = ft_htget(path, exec_name)) == NULL)
- return (NULL);
- }
- return (exec_path);
-}
+/**
+** \brief Evaluate a command
+** \param state Evaluation state
+** \param cmd Command to evaluate
+** \return Executable status or -1 on error
+*/
static int eval_cmd(t_eval_state *state, t_cmd *cmd)
{
int child_pid;
char *exec_path;
+ bool is_builtin;
- if ((exec_path = search_exec_path(state->path,
- ft_htget(state->env, "PATH"), cmd->argv[0])) == NULL)
- return (-1);
- if (cmd->in != NULL)
+ is_builtin = builtin_check_exec_name(cmd->argv[0]);
+ if (!is_builtin)
{
- if ((state->in_pipe[PIPE_WRITE] = open(cmd->in, O_RDONLY)) < 0)
- return (-1);
- }
- if (cmd->out != NULL)
- {
- if ((state->out_pipe[PIPE_READ] = open(cmd->out,
- (cmd->is_append ? O_WRONLY : O_APPEND) | O_CREAT)) < 0)
+ if ((exec_path = exec_search_path(state->path,
+ ft_htget(state->env, "PATH"), cmd->argv[0])) == NULL)
return (-1);
}
+ pipe_setup_parent(cmd, state->pipe_in, state->pipe_out);
if ((child_pid = fork()) == -1)
return (-1);
if (child_pid == 0)
{
- if (state->in_pipe[PIPE_READ] != PIPE_CLOSED)
- dup2(STDIN_FILENO, state->in_pipe[PIPE_READ]);
- if (state->out_pipe[PIPE_WRITE] != PIPE_CLOSED)
- dup2(STDOUT_FILENO, state->out_pipe[PIPE_WRITE]);
- if (execve(exec_path, cmd->argv, NULL /*env_array*/) == -1)
+ pipe_setup_child(state->pipe_in, state->pipe_out);
+ if (is_builtin)
+ exit(builtin_dispatch_run(cmd->argv, state->env));
+ else if (execve(exec_path, cmd->argv, NULL /*env_array*/) == -1)
exit(EXIT_FAILURE);
+ exit(EXIT_SUCCESS);
}
wait(&child_pid);
return (WEXITSTATUS(child_pid));
}
+/**
+** \brief Evaluate an AST
+** \param state State of the evaluation
+** \param ast Abstract syntax tree to evaluate
+** \return Executable status or -1 on error
+*/
+
int eval(t_eval_state *state, t_ast *ast)
{
errno = 0;
diff --git a/src/eval/exec.c b/src/eval/exec.c
new file mode 100644
index 0000000..1ac9754
--- /dev/null
+++ b/src/eval/exec.c
@@ -0,0 +1,62 @@
+/**
+** \file exec.c
+** \brief Executable name and path
+*/
+
+#include "eval.h"
+
+/**
+** \brief Check if executable name is already a path
+** \param exec_name Executable name
+** \return True if valid
+*/
+
+bool exec_is_path(char *exec_name)
+{
+ return (ft_strncmp(exec_name, "../", 3) == 0
+ || ft_strncmp(exec_name, "./", 2) == 0
+ || ft_strncmp(exec_name, "/", 1) == 0);
+}
+
+/**
+** \brief Check if executable path is valid
+** \param exec_path Executable path
+** \return True if valid
+*/
+
+bool exec_is_valid(char *exec_path)
+{
+ struct stat statbuf;
+
+ if (stat(exec_path, &statbuf) != 0)
+ return (false);
+ if (!S_ISREG(statbuf.st_mode)) // also need to manage link
+ return (false);
+ // could test permission but probably handled by execve
+ return (true);
+}
+
+/**
+** \brief Search executable name in path
+** \param path Path hash table
+** \param path_var Path environment string in case we need to update path
+** \param exec_name Executable name to search
+** \return Executable path or NULL if not found or path update error
+*/
+
+char *exec_search_path(t_path path, char *path_var, char *exec_name)
+{
+ char *exec_path;
+
+ if (exec_is_path(exec_name))
+ return (exec_name);
+ // try current first
+ if ((exec_path = ft_htget(path, exec_name)) == NULL)
+ {
+ if (path_update(path, path_var) == NULL) // optimise by not updating not changed path in ht
+ return (NULL);
+ if ((exec_path = ft_htget(path, exec_name)) == NULL)
+ return (NULL);
+ }
+ return (exec_path);
+}
diff --git a/src/eval/pipe.c b/src/eval/pipe.c
new file mode 100644
index 0000000..897a5f2
--- /dev/null
+++ b/src/eval/pipe.c
@@ -0,0 +1,48 @@
+/**
+** \file pipe.c
+** \brief Pipes setup
+*/
+
+#include "eval.h"
+
+/**
+** \brief Setup STDIN and STDOUT pipe in the parent process
+** \param cmd Command to setup
+** \param pipe_in STDIN pipe
+** \param pipe_out STDOUT pipe
+** \return -1 on error, 0 otherwise
+*/
+
+int pipe_setup_parent(t_cmd *cmd, int pipe_in[2], int pipe_out[2])
+{
+ if (cmd->in != NULL)
+ {
+ if ((pipe_in[PIPE_WRITE] = open(cmd->in, O_RDONLY)) < 0)
+ return (-1);
+ }
+ if (cmd->out != NULL)
+ {
+ if ((pipe_out[PIPE_READ] = open(cmd->out,
+ (cmd->is_append ? O_WRONLY : O_APPEND) | O_CREAT)) < 0)
+ return (-1);
+ }
+ return (0);
+}
+
+/**
+** \brief Setup STDIN and STDOUT pipe in the child process
+** \param pipe_in STDIN pipe
+** \param pipe_out STDOUT pipe
+** \return -1 on error, 0 otherwise
+*/
+
+int pipe_setup_child(int pipe_in[2], int pipe_out[2])
+{
+ if (pipe_in[PIPE_READ] != PIPE_CLOSED)
+ if (dup2(STDIN_FILENO, pipe_in[PIPE_READ]) == -1)
+ return (-1);
+ if (pipe_out[PIPE_WRITE] != PIPE_CLOSED)
+ if (dup2(STDOUT_FILENO, pipe_out[PIPE_WRITE]) == -1)
+ return (-1);
+ return (0);
+}
diff --git a/src/main.c b/src/main.c
index 4005bf2..1e42b87 100644
--- a/src/main.c
+++ b/src/main.c
@@ -64,10 +64,10 @@ int main(int argc, char **argv, char **envp)
/* printf("%s\n", ast->data.line.left->data.cmd.argv[0]); */
/* printf("%s\n", ast->data.line.left->data.cmd.argv[1]); */
- state.in_pipe[0] = -1;
- state.in_pipe[1] = -1;
- state.out_pipe[0] = -1;
- state.out_pipe[1] = -1;
+ state.pipe_in[0] = -1;
+ state.pipe_in[1] = -1;
+ state.pipe_out[0] = -1;
+ state.pipe_out[1] = -1;
state.path = path;
state.env = env;
eval(&state, ast);