diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..88d1995 --- /dev/null +++ b/Makefile @@ -0,0 +1,199 @@ +# **************************************************************************** # +# # +# ::: :::::::: # +# Makefile :+: :+: :+: # +# +:+ +:+ +:+ # +# By: kjikuhar +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2025/05/18 21:11:39 by kjikuhar #+# #+# # +# Updated: 2026/04/17 21:12:31 by kjikuhar ### ########.fr # +# # +# **************************************************************************** # + + +#----------------------------------------------------------------------------- +#> options +#----------------------------------------------------------------------------- +BLACK := \033[30m +RED := \033[31m +GREEN := \033[32m +YELLOW := \033[33m +BLUE := \033[96m +MAGENTA := \033[38;5;206m +CYAN := \033[36m +WHITE := \033[37m +RESET := \033[0m +BOLD := \033[1m +DIM := \033[2m +ITALIC := \033[3m +UNDER := \033[4m +BLINK := \033[5m +REVERSE := \033[7m +HIDDEN := \033[8m +PINK := \033[35m + +#----------------------------------------------------------------------------- + +#▸ variables + +#----------------------------------------------------------------------------- + +NAME := minishell + +#─ compile config ──────────────────────────────────────────────────────────── + +CC := cc +CFLAGS := -Wall -Wextra -Werror -Iincludes -Ilibft + +#─ directory name ──────────────────────────────────────────────────────────── + +SRCDIR := src +MAND := mandatory +OBJDIR := obj +LIBFT_DIR := libft +LIBFT_A := $(LIBFT_DIR)/libft.a + +#─ sources & objects ───────────────────────────────────────────────────────── + +#mandatory sources +SRCS_MAND := src/main.c \ + src/builtin/echo/echo.c \ + src/builtin/export/export.c \ + src/builtin/export/export_errors.c \ + src/builtin/pwd/pwd.c \ + src/builtin/unset/unset.c \ + src/builtin/cd/cd.c \ + src/builtin/cd/search_cdpath.c \ + src/builtin/exit/exit.c \ + src/prompt/prompt.c \ + src/callback/on_input.c \ + src/tokenize/tokenize.c \ + src/tokenize/is_specific.c \ + src/tokenize/state/in_normal.c \ + src/tokenize/state/in_normal/by_last.c \ + src/tokenize/state/in_normal/by_space.c \ + src/tokenize/state/in_normal/by_operator.c \ + src/tokenize/state/in_normal/by_quote.c \ + src/tokenize/state/in_normal/by_parenthesis.c \ + src/tokenize/state/in_double_quote.c \ + src/tokenize/state/in_single_quote.c \ + src/tokenize/state/in_operator.c \ + src/tokenize/state/on_success.c \ + src/tokenize/state/on_error.c \ + src/tokenize/store/free_store.c \ + src/tokenize/store/push_token.c \ + src/tokenize/store/add_buffer.c \ + src/parse/parse.c \ + src/parse/parse_list.c \ + src/parse/parse_pipeline.c \ + src/parse/utils/add_to_cmd.c \ + src/parse/utils/new_ast_node.c \ + src/parse/utils/new_cmd.c \ + src/parse/utils/new_redir.c \ + src/parse/utils/token_check.c \ + src/parse/utils/free_ast.c \ + src/component/heredoc/heredoc.c \ + src/component/heredoc/heredoc_prompt.c \ + src/component/heredoc/heredoc_expand.c \ + src/component/heredoc/tmpfile.c \ + src/component/shell_table/shell_hash.c \ + src/component/shell_table/shell_insert.c \ + src/component/shell_table/shell_search.c \ + src/component/shell_table/shell_delete.c \ + src/component/shell_table/shell_destroy.c \ + src/component/shell_table/shell_print.c \ + src/component/shell_table/build_shell_table.c \ + src/component/shell_table/export_envp.c \ + src/execute/exec_ast.c \ + src/execute/exec_cmd.c \ + src/execute/exec_pipe.c \ + src/execute/utils/ft_wexitstatus.c \ + src/execute/utils/ft_wifexited.c \ + src/execute/command/find_exec_path.c \ + src/execute/command/exec_external_cmd.c \ + src/execute/command/exec_builtin_cmd.c \ + src/execute/command/list_to_argv.c \ + src/execute/pipe/exec_pipe_child.c \ + src/execute/redirects/exec_redirs.c \ + src/component/directory/list_directory.c \ + src/component/directory/list_directory_recursive.c \ + src/component/path/to_absolute_path.c \ + src/component/path/join_path.c \ + src/component/path/append_path.c \ + src/component/path/resolve_relative_path.c \ + src/expand/expand.c \ + src/expand/expand_cmd.c \ + src/expand/expand_parameter/expand_parameter.c \ + src/expand/expand_parameter/state/in_normal_expand.c \ + src/expand/expand_parameter/state/in_double_quote_expand.c \ + src/expand/expand_parameter/state/in_single_quote_expand.c \ + src/expand/expand_parameter/state/on_success_expand.c \ + src/expand/expand_parameter/state/on_error_expand.c \ + src/expand/expand_parameter/store/push_token_expand.c \ + src/expand/expand_parameter/store/add_buffer_expand.c \ + src/expand/expand_parameter/store/get_key_length_expand.c \ + src/expand/expand_parameter/store/free_store_expand.c \ + src/expand/expand_tilde/expand_tilde.c \ + src/expand/expand_wildcard/resolve_wildcard.c \ + src/expand/expand_wildcard/match_wildcard.c \ + src/expand/expand_wildcard/expand_wildcard.c \ + src/expand/expand_remove_quotes/expand_remove_quotes.c \ + src/expand/expand_question/expand_question.c \ + src/signal/setup_signal_handlers.c \ + src/signal/set_signals.c \ + +SRCS_BONUS := + +#object files + +OBJS_MAND := $(SRCS_MAND:$(SRCDIR)/%.c=$(OBJDIR)/%.o) +OBJS_BONUS := $(SRCS_BONUS:$(SRCDIR)/%.c=$(OBJDIR)/%.o) +# OBJS := $(OBJS_MAND) $(OBJS_BONUS) + +#----------------------------------------------------------------------------- + +#▸ rules + +#----------------------------------------------------------------------------- + +all: $(NAME) + +$(NAME): $(LIBFT_A) $(OBJS_MAND) + @echo "Compiled with $(GREEN)$(BOLD)$(CFLAGS)$(RESET)" + @$(CC) $(CFLAGS) $(OBJS_MAND) -o $@ $(LIBFT_A) -lreadline + @echo "$(YELLOW)$(BOLD)=========================================" + @echo " You can use My $(NAME)!!" + @echo "=========================================$(RESET)" + +# compile object files + +$(OBJDIR)/%.o: $(SRCDIR)/%.c + @mkdir -p $(dir $@) + @echo "Compiled ✅ $(WHITE) $(BOLD) $^ $(RESET)" + @$(CC) $(CFLAGS) -c -o $@ $^ + +# libft +$(LIBFT_A): + @$(MAKE) -C $(LIBFT_DIR) + +# clean targets +clean: + @$(MAKE) -C $(LIBFT_DIR) clean + @rm -rf $(OBJDIR) + +fclean: clean + @$(MAKE) -C $(LIBFT_DIR) fclean + @rm -f $(NAME) + +re: fclean all + +# bonus: $(LIBFT_A) $(OBJS_BONUS) +# @cp $(LIBFT_A) $(NAME) +# @echo "Compiled with $(GREEN)$(BOLD)$(CFLAGS)$(RESET)" +# @ar rcs $(NAME) $(OBJS_BONUS) +# @echo "$(YELLOW)$(BOLD)=========================================" +# @echo " You can use My Bonus_$(NAME)!!" +# @echo "=========================================$(RESET)" + +.PHONY: all clean fclean re bonus + diff --git a/README.md b/README.md index 9daeafb..6504ba4 100644 --- a/README.md +++ b/README.md @@ -1 +1,100 @@ -test +*This project has been created as part of the 42 curriculum by surayama, kjikuhar.* + +# minishell + +## Description + +Minishell is a simplified shell implementation written in C, inspired by bash. The goal of this project is to understand how a real shell works by building one from scratch: parsing user input, managing environment variables, handling redirections and pipes, and executing commands. + +Key features include: + +- Interactive prompt with command history (GNU readline) +- Tokenization and parsing of shell commands into an AST (Abstract Syntax Tree) +- Pipes (`|`) and redirections (`<`, `>`, `>>`, `<<`) +- Environment variable expansion (`$VAR`, `$?`) +- Quote handling (single and double quotes) +- Here-documents (`<<`) +- Built-in commands: `echo`, `cd`, `pwd`, `export`, `unset`, `env`, `exit` +- Signal handling (`Ctrl+C`, `Ctrl+D`, `Ctrl+\`) + +## Instructions + +### Prerequisites + +- Docker (recommended for building and running) +- Or: GCC, Make, GNU readline library + +### Build and Run + +The recommended way to build and run is inside the Docker environment: + +```bash +# Enter the Docker container +make docker + +# Build inside the container +make + +# Run the shell +./minishell +``` + +### Other Make Targets + +| Command | Description | +|-----------------|--------------------------------------------------| +| `make` | Build the project | +| `make re` | Clean rebuild | +| `make clean` | Remove object files | +| `make fclean` | Remove object files and binary | +| `make norm` | Run norminette style checker | +| `make valgrind` | Rebuild and run with valgrind for leak detection | +| `make test` | Run norminette + valgrind | + +### Usage Examples + +``` +$ ./minishell +minishell$ echo hello world +hello world +minishell$ ls -la | grep Makefile +-rw-r--r-- 1 user staff 2048 Apr 10 12:00 Makefile +minishell$ export FOO=bar +minishell$ echo $FOO +bar +minishell$ cat << EOF +> hello +> world +> EOF +hello +world +minishell$ exit +``` + +## Architecture + +The shell follows an interpreter pipeline: + +``` +Input -> tokenize() -> heredoc() -> variable_expand() -> remove_quotes() -> parse() -> AST -> execute +``` + +1. **Tokenizer** -- State-machine-based lexer that splits input into tokens at operator, space, and quote boundaries. +2. **Variable Expansion** -- Expands `$VAR` syntax while respecting quote context. +3. **Quote Removal** -- Strips quote characters from expanded tokens. +4. **Parser** -- Recursive descent parser that produces a binary AST with `PIPE` (internal) and `CMD` (leaf) nodes. +5. **Executor** -- Walks the AST, sets up pipes and redirections, and executes commands via `fork`/`execve`. + +## Resources + +- [The Open Group Base Specifications -- Shell Command Language](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html) +- [Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html) +- [Writing Your Own Shell (42 tutorial)](https://harm-smits.github.io/42docs/projects/minishell) +- [GNU Readline Library Documentation](https://tiswww.case.edu/php/chet/readline/rltop.html) + +### Use of AI + +AI (Claude Code by Anthropic) was used as a development assistant for the following tasks: + +- Code review and debugging assistance +- Generating boilerplate code and Makefile configurations diff --git a/includes/builtin.h b/includes/builtin.h new file mode 100644 index 0000000..68e3ef0 --- /dev/null +++ b/includes/builtin.h @@ -0,0 +1,30 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* builtin.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/29 17:16:51 by surayama #+# #+# */ +/* Updated: 2026/04/06 07:41:22 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef BUILTIN_H +# define BUILTIN_H + +# include "path.h" +# include "shell_table.h" +# include +# include +# include + +int echo(t_list *argv, t_shell_table *shell_table); +int pwd(t_list *argv, t_shell_table *shell_table); +int export(t_list *argv, t_shell_table *shell_table); +int unset(t_list *argv, t_shell_table *shell_table); +int cd(t_list *argv, t_shell_table *shell_table); +int builtin_exit(t_list *argv, t_shell_table *shell_table); +void print_export_error(const char *assignment); + +#endif diff --git a/includes/constants.h b/includes/constants.h new file mode 100644 index 0000000..91486fd --- /dev/null +++ b/includes/constants.h @@ -0,0 +1,25 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* constants.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/16 23:14:49 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CONSTANTS_H +# define CONSTANTS_H + +// macros +# define SUCCESS 0 +# define ERROR -1 +# define NOT_FOUND -2 +# define NO_PERMISSION -3 +# define NOT_A_DIRECTORY -4 +# define SHELL_NAME "jikussh" +# define PROMPT "jikussh$ " + +#endif diff --git a/includes/directory.h b/includes/directory.h new file mode 100644 index 0000000..59e22da --- /dev/null +++ b/includes/directory.h @@ -0,0 +1,29 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* directory.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/27 15:25:07 by surayama #+# #+# */ +/* Updated: 2025/12/01 03:28:22 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef DIRECTORY_H +# define DIRECTORY_H + +# include "constants.h" +# include +# include +# include "libft.h" +# include +# include +# include + +int list_directory(const char *path, bool include_hidden, + t_list **entries); +int list_directory_recursive(const char *path, bool include_hidden, + t_list **entries); + +#endif diff --git a/includes/execute.h b/includes/execute.h new file mode 100644 index 0000000..036acb6 --- /dev/null +++ b/includes/execute.h @@ -0,0 +1,39 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* execute.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/09/30 16:38:39 by kjikuhar #+# #+# */ +/* Updated: 2026/01/08 10:37:36 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +/* prevent re-include-------------------------------------------------------- */ +#ifndef EXECUTE_H +# define EXECUTE_H + +/* include header file------------------------------------------------------- */ +# include "parse.h" +# include "shell_table.h" + +/* main function------------------------------------------------------------- */ +int exec_ast(t_ast *node, t_shell_table *shell_table); +int exec_builtin_cmd(t_ast *node, t_shell_table *shell_table); +int exec_cmd(t_ast *node, t_shell_table *shell_table); +char *find_exec_path(const char *cmd, \ + t_shell_table *shell_table); +int exec_external_cmd(char **argv, \ + t_shell_table *shell_table); +int exec_pipe(t_ast *node, t_shell_table *shell_table); +void exec_left_child(t_ast *node, \ + t_shell_table *shell_table, int fd[2]); +void exec_right_child(t_ast *node, \ + t_shell_table *shell_table, int fd[2]); +int exec_redirs(t_list *redirs); +char **list_to_argv(t_list *lst); +int ft_wifexited(int exit_status); +int ft_wexitstatus(int exit_status); + +#endif diff --git a/includes/expand.h b/includes/expand.h new file mode 100644 index 0000000..5c93620 --- /dev/null +++ b/includes/expand.h @@ -0,0 +1,31 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* expand.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/03/03 17:13:55 by surayama #+# #+# */ +/* Updated: 2026/04/17 21:31:58 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef EXPAND_H +# define EXPAND_H + +# include "constants.h" +# include "libft.h" +# include "parse.h" +# include "shell_table.h" +# include + +t_list *expand(t_list *tokens, t_shell_table *shell_table, int last_status); +int expand_cmd(t_cmd *cmd, t_shell_table *shell_table); +t_list *expand_tilde(t_list *tokens, t_shell_table *shell_table); +t_list *expand_question(t_list *tokens, int last_exit_status); +t_list *expand_parameter(t_list *tokens, t_shell_table *shell_table); +t_list *expand_wildcard(t_list *tokens); +t_list *expand_remove_quotes(t_list *tokens); +t_list *expand_question(t_list *tokens, int last_exit_status); + +#endif diff --git a/includes/heredoc.h b/includes/heredoc.h new file mode 100644 index 0000000..e977035 --- /dev/null +++ b/includes/heredoc.h @@ -0,0 +1,41 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* heredoc.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/20 14:12:42 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:51 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef HEREDOC_H +# define HEREDOC_H + +# include "constants.h" +# include "libft.h" +# include +# include +# include +# include +# include + +# include "shell_table.h" +# include + +t_list *heredoc(t_list *tokens, t_shell_table *st); +char *heredoc_prompt(const char *delim, bool expand, + t_shell_table *st); + +char *expand_heredoc_line(const char *line, t_shell_table *st); + +// tmpfile +int open_tmpfile(char *tmpfile_path); +char *create_tmpfile(void); + +# define HEREDOC_PROMPT "heredoc> " +# define HEREDOC_TMP_PREFIX "/tmp/minishell_heredoc_" +# define HEREDOC_BUFFER 1024 + +#endif diff --git a/includes/minishell.h b/includes/minishell.h new file mode 100644 index 0000000..6937684 --- /dev/null +++ b/includes/minishell.h @@ -0,0 +1,31 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* minishell.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/09/29 15:14:22 by kjikuhar #+# #+# */ +/* Updated: 2026/04/17 21:21:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef MINISHELL_H +# define MINISHELL_H + +# include "constants.h" +# include "execute.h" +# include "expand.h" +# include "heredoc.h" +# include "libft.h" +# include "parse.h" +# include "prompt.h" +# include "path.h" +# include "shell_table.h" +# include "signal_handler.h" +# include "tokenize.h" + +// callbacks +int on_input(char *input, t_shell_table *shell_table, int last_status); + +#endif diff --git a/includes/parse.h b/includes/parse.h new file mode 100644 index 0000000..7ad5366 --- /dev/null +++ b/includes/parse.h @@ -0,0 +1,87 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/09/30 16:38:39 by kjikuhar #+# #+# */ +/* Updated: 2025/11/27 11:51:05 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef PARSE_H +# define PARSE_H + +# include "libft.h" +# include "constants.h" +# include +# include +# include +# include +# include +# include +# include + +/* define part--------------------------------------------------------------- */ +typedef enum e_ast_type +{ + PIPE, + CMD, + AND, + OR, + SUBSHELL +} t_ast_type; + +typedef enum e_redir_kind +{ + R_IN, + R_OUT_TRUNC, + R_OUT_APPEND, + R_NOT_FOUND +} t_redir_kind; + +typedef struct s_redir +{ + t_redir_kind kind; + char *filename; +} t_redir; + +typedef struct s_cmd +{ + t_list *argv; + t_list *redirs; +} t_cmd; + +typedef struct s_ast +{ + t_ast_type type; + t_cmd *cmd; + struct s_ast *left; + struct s_ast *right; +} t_ast; + +/* main function------------------------------------------------------------- */ +t_ast *parse(t_list *token_head); +t_ast *parse_cmd(t_list **current); +t_ast *parse_pipeline(t_list **current); +t_ast *parse_list(t_list **current); + +/* AST's token utils--------------------------------------------------------- */ +bool is_word(const t_list *node); +bool is_symbol(const t_list *node, const char *literal); +bool is_redir(const t_list *node); + +/* AST's Utils--------------------------------------------------------------- */ +t_ast *new_ast_node(t_ast_type type); +t_cmd *new_cmd(void); +t_redir *new_redir(t_redir_kind kind, const char *filename); +bool add_redir_to_cmd(t_cmd *cmd, t_redir_kind kind, + const char *file); +bool add_argv_to_cmd(t_cmd *cmd, const char *arg); + +/* free utils function------------------------------------------------------- */ +void free_cmd(t_cmd *cmd); +void free_ast(t_ast *node); + +#endif diff --git a/includes/path.h b/includes/path.h new file mode 100644 index 0000000..cea2a5f --- /dev/null +++ b/includes/path.h @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* path.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/01 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/03/03 17:15:58 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef PATH_H +# define PATH_H + +# include "libft.h" +# include +# include +# include +# include + +char *to_absolute_path(const char *path); +t_list *append_path(t_list **dest, const char *content); +t_list *resolve_relative_path(t_list *tokens); +char *join_path(const char *path_before, const char *path_after); + +#endif diff --git a/includes/prompt.h b/includes/prompt.h new file mode 100644 index 0000000..f189078 --- /dev/null +++ b/includes/prompt.h @@ -0,0 +1,26 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* prompt.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/01 15:35:18 by surayama #+# #+# */ +/* Updated: 2026/04/17 21:33:50 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef PROMPT_H +# define PROMPT_H + +# include "constants.h" +# include "shell_table.h" +# include "libft.h" +# include +# include +# include +# include + +int prompt(int (*handler)(char *input, t_shell_table *shell_table, + int last_status), t_shell_table *shell_table); +#endif diff --git a/includes/shell_table.h b/includes/shell_table.h new file mode 100644 index 0000000..2c91ebc --- /dev/null +++ b/includes/shell_table.h @@ -0,0 +1,53 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* shell_table.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/27 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:51 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef SHELL_TABLE_H +# define SHELL_TABLE_H + +# include "constants.h" +# include "libft.h" +# include +# include + +# define SHELL_TABLE_INIT_SIZE 256 + +typedef struct s_shell_node +{ + char *key; + char *value; + bool exported; + struct s_shell_node *next; +} t_shell_node; + +typedef struct s_shell_table +{ + t_shell_node **buckets; + size_t size; + size_t n_nodes; + int last_status; +} t_shell_table; + +// shell_table operations +size_t st_hash(const char *key, size_t table_size); +int st_insert(t_shell_table *table, const char *key, + const char *value, bool exported); +int st_set_exported(t_shell_table *table, const char *key); +char *st_search(t_shell_table *table, const char *key); +int st_delete(t_shell_table *table, const char *key); +void st_destroy(t_shell_table *table); +void st_print_env(t_shell_table *table); + +// shell_table specific operations +t_shell_table *build_shell_table(char *const envp[]); +char **export_envp(t_shell_table *shell_table); + +#endif diff --git a/includes/signal_handler.h b/includes/signal_handler.h new file mode 100644 index 0000000..dbe4c4c --- /dev/null +++ b/includes/signal_handler.h @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* signal_handler.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/06 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/06 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef SIGNAL_HANDLER_H +# define SIGNAL_HANDLER_H + +# include +# include + +extern volatile sig_atomic_t g_signal; + +void setup_signal_handlers(void); +void set_signal_ignore(void); +void set_signal_default(void); +void set_signal_interactive(void); +void set_signal_heredoc(void); + +#endif diff --git a/includes/tokenize.h b/includes/tokenize.h new file mode 100644 index 0000000..ade322e --- /dev/null +++ b/includes/tokenize.h @@ -0,0 +1,20 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* tokenize.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/10 17:09:25 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef TOKENIZE_H +# define TOKENIZE_H + +# include "libft.h" + +t_list *tokenize(char *input); + +#endif diff --git a/libft/Makefile b/libft/Makefile new file mode 100644 index 0000000..45d9f31 --- /dev/null +++ b/libft/Makefile @@ -0,0 +1,116 @@ +# **************************************************************************** # +# # +# ::: :::::::: # +# Makefile :+: :+: :+: # +# +:+ +:+ +:+ # +# By: kjikuhar +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2025/04/04 16:00:02 by kjikuhar #+# #+# # +# Updated: 2026/03/03 13:49:37 by kjikuhar ### ########.fr # +# # +# **************************************************************************** # + +CC = cc +RM = rm -f +CFLAGS = -Wall -Wextra -Werror -I. +NAME = libft.a + +SRCS = conversion/ft_atoi.c \ + conversion/ft_itoa.c \ + conversion/ft_tolower.c \ + conversion/ft_toupper.c \ + judge/ft_isalnum.c \ + judge/ft_isalpha.c \ + judge/ft_isascii.c \ + judge/ft_isdigit.c \ + judge/ft_isprint.c \ + list/ft_lstadd_back.c \ + list/ft_lstadd_front.c \ + list/ft_lstclear.c \ + list/ft_lstdel_back.c \ + list/ft_lstdelone.c \ + list/ft_lstiter.c \ + list/ft_lstlast.c \ + list/ft_lstmap.c \ + list/ft_lstnew.c \ + list/ft_lstsize.c \ + list/ft_lstreplace.c \ + memory/ft_bzero.c \ + memory/ft_calloc.c \ + memory/ft_memchr.c \ + memory/ft_memcmp.c \ + memory/ft_memcpy.c \ + memory/ft_memmove.c \ + memory/ft_memrchr.c \ + memory/ft_memset.c \ + output/ft_putchar_fd.c \ + output/ft_putendl_fd.c \ + output/ft_putnbr_fd.c \ + output/ft_putstr_fd.c \ + string/ft_free_array.c \ + string/ft_split.c \ + string/ft_strchr.c \ + string/ft_strdup.c \ + string/ft_striteri.c \ + string/ft_strjoin.c \ + string/ft_strlcat.c \ + string/ft_strlcpy.c \ + string/ft_strlen.c \ + string/ft_strmapi.c \ + string/ft_strncmp.c \ + string/ft_strnstr.c \ + string/ft_strrchr.c \ + string/ft_strtrim.c \ + string/ft_substr.c \ + original/ft_swap.c \ + original/ft_max.c \ + original/ft_min.c \ + original/ft_isspace.c \ + original/is_blank_line.c + +BLACK := \033[30m +RED := \033[31m +GREEN := \033[32m +YELLOW := \033[33m +BLUE := \033[96m +MAGENTA := \033[38;5;206m +CYAN := \033[36m +WHITE := \033[37m +RESET := \033[0m +BOLD := \033[1m +DIM := \033[2m +ITALIC := \033[3m +UNDER := \033[4m +BLINK := \033[5m +REVERSE := \033[7m +HIDDEN := \033[8m +PINK := \033[35m + +OBJS = $(SRCS:.c=.o) + +all: $(NAME) + +$(NAME): $(OBJS) + @echo "Compiled with $(GREEN)$(BOLD)$(CFLAGS)$(RESET)" + @ar crs $(NAME) $(OBJS) + @echo "$(YELLOW)$(BOLD)=========================================" + @echo " You can use My $(NAME)!!" + @echo "=========================================$(RESET)" + +# ========================================= +# You can use My libft.a!! +# ========================================= + +%.o: %.c + @echo "Compiled ✅ $(CYAN) $(BOLD) $^ $(RESET)" + @$(CC) $(CFLAGS) $(INCLUDE) -c -o $@ $^ + +clean: + @$(RM) $(OBJS) + +fclean: clean + @$(RM) $(NAME) + +re: fclean all + +.PHONY: all clean fclean re diff --git a/libft/conversion/ft_atoi.c b/libft/conversion/ft_atoi.c new file mode 100644 index 0000000..b992826 --- /dev/null +++ b/libft/conversion/ft_atoi.c @@ -0,0 +1,33 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_atoi.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/24 21:08:27 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 15:37:25 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +int ft_atoi(const char *str) +{ + int sign; + int num; + + while (ft_isspace(*str)) + str++; + sign = 1; + if (*str == '+' || *str == '-') + { + if (*str == '-') + sign *= -1; + str++; + } + num = 0; + while (ft_isdigit((int)*str)) + num = 10 * num + (*str++ - '0'); + return (sign * num); +} diff --git a/libft/conversion/ft_itoa.c b/libft/conversion/ft_itoa.c new file mode 100644 index 0000000..727e305 --- /dev/null +++ b/libft/conversion/ft_itoa.c @@ -0,0 +1,79 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_itoa.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/26 11:55:06 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:13 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Convert the integer 'n'to a ASCII string */ +static int number_len_(int number) +{ + int number_len; + + number_len = 1; + if (number == INT_MIN) + { + return (11); + } + if (number < 0) + { + number *= -1; + number_len++; + } + while (number >= 10) + { + number /= 10; + number_len++; + } + return (number_len); +} + +static char *ft_strcpy(char *dest, char *src) +{ + int i; + + i = 0; + while (src[i] != '\0') + { + dest[i] = src[i]; + i++; + } + dest[i] = '\0'; + return (dest); +} + +char *ft_itoa(int n) +{ + char *dest_str; + int i; + + dest_str = malloc(sizeof(char) * (number_len_(n) + 1)); + if (!dest_str) + return (NULL); + if (n == -2147483648) + { + return (ft_strcpy(dest_str, "-2147483648")); + } + i = number_len_(n) - 1; + if (n < 0) + { + dest_str[0] = '-'; + n *= -1; + } + dest_str[i + 1] = '\0'; + while (n >= 10) + { + dest_str[i] = n % 10 + '0'; + n /= 10; + i--; + } + dest_str[i] = n % 10 + '0'; + return (dest_str); +} diff --git a/libft/conversion/ft_tolower.c b/libft/conversion/ft_tolower.c new file mode 100644 index 0000000..9861545 --- /dev/null +++ b/libft/conversion/ft_tolower.c @@ -0,0 +1,20 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_tolower.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 18:47:23 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:17 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +int ft_tolower(int c) +{ + if ('A' <= c && c <= 'Z') + return (c + 'a' - 'A'); + return (c); +} diff --git a/libft/conversion/ft_toupper.c b/libft/conversion/ft_toupper.c new file mode 100644 index 0000000..0758da3 --- /dev/null +++ b/libft/conversion/ft_toupper.c @@ -0,0 +1,20 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_toupper.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 18:47:23 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:19 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +int ft_toupper(int c) +{ + if ('a' <= c && c <= 'z') + return (c + 'A' - 'a'); + return (c); +} diff --git a/libft/judge/ft_isalnum.c b/libft/judge/ft_isalnum.c new file mode 100644 index 0000000..315ae0e --- /dev/null +++ b/libft/judge/ft_isalnum.c @@ -0,0 +1,18 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_isalnum.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/24 21:20:17 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:23 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +int ft_isalnum(int c) +{ + return (ft_isalpha(c) || ft_isdigit(c)); +} diff --git a/libft/judge/ft_isalpha.c b/libft/judge/ft_isalpha.c new file mode 100644 index 0000000..26b373e --- /dev/null +++ b/libft/judge/ft_isalpha.c @@ -0,0 +1,16 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_isalpha.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/24 21:11:06 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:26 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +int ft_isalpha(int c) +{ + return ((unsigned)(c | 0x20) - 'a' < 26); +} diff --git a/libft/judge/ft_isascii.c b/libft/judge/ft_isascii.c new file mode 100644 index 0000000..10ca1f7 --- /dev/null +++ b/libft/judge/ft_isascii.c @@ -0,0 +1,18 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_isascii.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/24 21:20:17 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:29 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#define ASCII_MAX 0x7F /* 127 */ + +int ft_isascii(int c) +{ + return ((unsigned)c <= ASCII_MAX); +} diff --git a/libft/judge/ft_isdigit.c b/libft/judge/ft_isdigit.c new file mode 100644 index 0000000..0cfdf31 --- /dev/null +++ b/libft/judge/ft_isdigit.c @@ -0,0 +1,17 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_isdigit.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/24 21:20:17 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:34 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +/* ASCII only */ +int ft_isdigit(int c) +{ + return ((unsigned)c - '0' < 10); +} diff --git a/libft/judge/ft_isprint.c b/libft/judge/ft_isprint.c new file mode 100644 index 0000000..58df58c --- /dev/null +++ b/libft/judge/ft_isprint.c @@ -0,0 +1,16 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_isprint.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/24 21:20:17 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:37 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +int ft_isprint(int c) +{ + return ((unsigned)c - 0x20 < 0x5f); +} diff --git a/libft/libft.h b/libft/libft.h new file mode 100644 index 0000000..b9a7f69 --- /dev/null +++ b/libft/libft.h @@ -0,0 +1,101 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* libft.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 14:55:13 by kjikuhar #+# #+# */ +/* Updated: 2026/03/03 13:53:04 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef LIBFT_H +# define LIBFT_H + +# include +# include +# include +# include + +typedef struct s_list +{ + void *content; + struct s_list *next; +} t_list; + +// conversion +int ft_atoi(const char *str); +char *ft_itoa(int n); +int ft_tolower(int c); +int ft_toupper(int c); + +// judge +int ft_isalnum(int c); +int ft_isalpha(int c); +int ft_isascii(int c); +int ft_isdigit(int c); +int ft_isprint(int c); + +// list +t_list *ft_lstnew(void *content); +void ft_lstadd_front(t_list **lst, t_list *new); +t_list *ft_lstlast(t_list *lst); +int ft_lstsize(t_list *lst); +void ft_lstadd_back(t_list **lst, t_list *new); +void ft_lstdelone(t_list *lst, void (*del)(void *)); +void ft_lstdel_back(t_list **lst, void (*del)(void *)); +void ft_lstclear(t_list **lst, void (*del)(void *)); +void ft_lstiter(t_list *lst, void (*f)(void *)); +t_list *ft_lstmap(t_list *lst, void *(*f)(void *), + void (*del)(void *)); +void ft_lstreplace(t_list *prev, t_list *target, t_list *new); + +// memory +void ft_bzero(void *s, size_t n); +void *ft_calloc(size_t count, size_t size); +void *ft_memchr(const void *s, int c, size_t n); +int ft_memcmp(const void *s1, const void *s2, size_t n); +void *ft_memcpy(void *dest, const void *src, size_t n); +void *ft_memmove(void *dest, const void *src, size_t len); +void *ft_memrchr(const void *s, int c, size_t n); +void *ft_memset(void *s, int c, size_t n); + +// original functions +void ft_swap(int *a, int *b); +int is_blank_line(const char *s); +int ft_isspace(const char c); +int ft_max_int(int a, int b, int *m); +long ft_max_long(long a, long b); +long long ft_max_long_long(long long a, long long b); +size_t ft_max_size_t(size_t a, size_t b); +int ft_min_int(int a, int b); +long ft_min_long(long a, long b); +long long ft_min_long_long(long long a, long long b); +size_t ft_min_size_t(size_t a, size_t b); + +// output +void ft_putchar_fd(char c, int fd); +void ft_putendl_fd(const char *s, int fd); +void ft_putnbr_fd(int n, int fd); +void ft_putstr_fd(const char *s, int fd); + +// string +void ft_free_array(void **arr); +char **ft_split(char const *s, char c); +char *ft_strchr(const char *s, int c); +char *ft_strdup(const char *s); +void ft_striteri(char *s, void (*f)(unsigned int, char *)); +char *ft_strjoin(char const *s1, char const *s2); +size_t ft_strlcat(char *dst, const char *src, size_t size); +size_t ft_strlcpy(char *dst, const char *src, size_t size); +size_t ft_strlen(const char *s); +char *ft_strmapi(char const *s, char (*f)(unsigned int, char)); +int ft_strncmp(const char *s1, const char *s2, size_t n); +char *ft_strnstr(const char *big, const char *little, + size_t len); +char *ft_strrchr(const char *s, int c); +char *ft_strtrim(char const *s1, char const *set); +char *ft_substr(char const *s, unsigned int start, size_t len); + +#endif diff --git a/libft/list/ft_lstadd_back.c b/libft/list/ft_lstadd_back.c new file mode 100644 index 0000000..d1e81c4 --- /dev/null +++ b/libft/list/ft_lstadd_back.c @@ -0,0 +1,29 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstadd_back.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/28 15:40:06 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:41 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Adds the node new at the end of the list */ +void ft_lstadd_back(t_list **lst, t_list *new) +{ + t_list *last; + + if (!lst || !new) + return ; + if (!*lst) + *lst = new; + else + { + last = ft_lstlast(*lst); + last->next = new; + } +} diff --git a/libft/list/ft_lstadd_front.c b/libft/list/ft_lstadd_front.c new file mode 100644 index 0000000..5a60399 --- /dev/null +++ b/libft/list/ft_lstadd_front.c @@ -0,0 +1,22 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstadd_front.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/27 12:47:59 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:44 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Adds the given node to the beginning of the list. Works with empty lists. */ +void ft_lstadd_front(t_list **lst, t_list *new) +{ + if (!lst || !new) + return ; + new->next = *lst; + *lst = new; +} diff --git a/libft/list/ft_lstclear.c b/libft/list/ft_lstclear.c new file mode 100644 index 0000000..c4e6c2f --- /dev/null +++ b/libft/list/ft_lstclear.c @@ -0,0 +1,33 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstclear_bonus.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/28 15:40:38 by kjikuhar #+# #+# */ +/* Updated: 2025/05/04 18:38:55 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Deletes and frees the given list and its content */ +void ft_lstclear(t_list **lst, void (*del)(void *)) +{ + t_list *current; + t_list *next_list; + + if (!lst || !*lst || !del) + return ; + current = *lst; + while (current) + { + if (current->content) + (*del)(current->content); + next_list = current->next; + free(current); + current = next_list; + } + *lst = NULL; +} diff --git a/libft/list/ft_lstdel_back.c b/libft/list/ft_lstdel_back.c new file mode 100644 index 0000000..671ba78 --- /dev/null +++ b/libft/list/ft_lstdel_back.c @@ -0,0 +1,34 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstdel_back.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/01 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void ft_lstdel_back(t_list **lst, void (*del)(void *)) +{ + t_list *current; + t_list *last; + + if (!lst || !*lst || !del) + return ; + if (!(*lst)->next) + { + ft_lstdelone(*lst, del); + *lst = NULL; + return ; + } + last = ft_lstlast(*lst); + current = *lst; + while (current->next != last) + current = current->next; + current->next = NULL; + ft_lstdelone(last, del); +} diff --git a/libft/list/ft_lstdelone.c b/libft/list/ft_lstdelone.c new file mode 100644 index 0000000..ea51172 --- /dev/null +++ b/libft/list/ft_lstdelone.c @@ -0,0 +1,23 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstdelone.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/28 15:40:38 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:48 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* This function frees the memory of a list node using the given function */ +void ft_lstdelone(t_list *lst, void (*del)(void *)) +{ + if (!lst || !del) + return ; + if (lst->content) + (*del)(lst->content); + free(lst); +} diff --git a/libft/list/ft_lstiter.c b/libft/list/ft_lstiter.c new file mode 100644 index 0000000..ea1e925 --- /dev/null +++ b/libft/list/ft_lstiter.c @@ -0,0 +1,26 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstiter_bonus.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/28 15:40:38 by kjikuhar #+# #+# */ +/* Updated: 2025/05/04 19:00:25 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Iterates the list and applies the function f on the content of each node */ +void ft_lstiter(t_list *lst, void (*f)(void *)) +{ + if (!f) + return ; + while (lst) + { + if (lst->content) + (*f)(lst->content); + lst = lst->next; + } +} diff --git a/libft/list/ft_lstlast.c b/libft/list/ft_lstlast.c new file mode 100644 index 0000000..3b5dc0f --- /dev/null +++ b/libft/list/ft_lstlast.c @@ -0,0 +1,23 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstlast.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/28 15:38:00 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:53 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Returns the last node of the list */ +t_list *ft_lstlast(t_list *lst) +{ + if (!lst) + return (NULL); + while (lst->next) + lst = lst->next; + return (lst); +} diff --git a/libft/list/ft_lstmap.c b/libft/list/ft_lstmap.c new file mode 100644 index 0000000..904327f --- /dev/null +++ b/libft/list/ft_lstmap.c @@ -0,0 +1,56 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstmap.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/28 15:40:38 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:57:58 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +static t_list *init_head(t_list *lst, void *(*f)(void *), void (*del)(void *)) +{ + void *tmp; + t_list *head; + + tmp = (*f)(lst->content); + if (!tmp) + return (NULL); + head = ft_lstnew(tmp); + if (!head) + return (del(tmp), NULL); + return (head); +} + +t_list *ft_lstmap(t_list *lst, void *(*f)(void *), void (*del)(void *)) +{ + t_list *head; + t_list *cur; + t_list *tmp_node; + void *tmp; + + if (!lst || !f || !del) + return (NULL); + head = init_head(lst, f, del); + if (!head) + return (NULL); + cur = head; + lst = lst->next; + while (lst) + { + tmp = (*f)(lst->content); + if (!tmp) + return (ft_lstclear(&head, del), NULL); + tmp_node = ft_lstnew(tmp); + if (!tmp_node) + return (del(tmp), ft_lstclear(&head, del), NULL); + cur->next = tmp_node; + cur = tmp_node; + lst = lst->next; + } + return (head); +} diff --git a/libft/list/ft_lstnew.c b/libft/list/ft_lstnew.c new file mode 100644 index 0000000..e45823f --- /dev/null +++ b/libft/list/ft_lstnew.c @@ -0,0 +1,26 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstnew.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/27 12:42:16 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:04 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Allocates and returns a new node with the given content */ +t_list *ft_lstnew(void *content) +{ + t_list *new; + + new = malloc(sizeof(t_list)); + if (!new) + return (NULL); + new->content = content; + new->next = NULL; + return (new); +} diff --git a/libft/list/ft_lstreplace.c b/libft/list/ft_lstreplace.c new file mode 100644 index 0000000..1854d35 --- /dev/null +++ b/libft/list/ft_lstreplace.c @@ -0,0 +1,29 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstreplace.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 21:36:30 by surayama #+# #+# */ +/* Updated: 2026/02/07 00:15:40 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void ft_lstreplace(t_list *prev, t_list *target, t_list *new) +{ + t_list *next_node; + t_list *last_new; + + if (!target || !new) + return ; + next_node = target->next; + last_new = ft_lstlast(new); + last_new->next = next_node; + free(target->content); + free(target); + if (prev) + prev->next = new; +} diff --git a/libft/list/ft_lstsize.c b/libft/list/ft_lstsize.c new file mode 100644 index 0000000..ebc98ac --- /dev/null +++ b/libft/list/ft_lstsize.c @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_lstsize.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/28 15:34:58 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:07 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Counts the number of nodes in a list */ +int ft_lstsize(t_list *lst) +{ + int i; + + i = 0; + while (lst) + { + i++; + lst = lst->next; + } + return (i); +} diff --git a/libft/memory/ft_bzero.c b/libft/memory/ft_bzero.c new file mode 100644 index 0000000..e2fc6f7 --- /dev/null +++ b/libft/memory/ft_bzero.c @@ -0,0 +1,18 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_bzero.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 15:46:17 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:11 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void ft_bzero(void *s, size_t n) +{ + ft_memset(s, 0, n); +} diff --git a/libft/memory/ft_calloc.c b/libft/memory/ft_calloc.c new file mode 100644 index 0000000..f76fc2f --- /dev/null +++ b/libft/memory/ft_calloc.c @@ -0,0 +1,30 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_calloc.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 17:26:53 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:15 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void *ft_calloc(size_t count, size_t size) +{ + void *p; + size_t total_size; + + if (count == 0 || size == 0) + return (malloc(0)); + if (count > __SIZE_MAX__ / size) + return (NULL); + total_size = count * size; + p = malloc(total_size); + if (!p) + return (NULL); + ft_bzero(p, total_size); + return (p); +} diff --git a/libft/memory/ft_memchr.c b/libft/memory/ft_memchr.c new file mode 100644 index 0000000..bf87470 --- /dev/null +++ b/libft/memory/ft_memchr.c @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_memchr.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 18:30:44 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:19 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void *ft_memchr(const void *s, int c, size_t n) +{ + const unsigned char *us; + + us = (const unsigned char *)s; + while (n--) + { + if (*us == (unsigned char)c) + return ((void *)us); + us++; + } + return (NULL); +} diff --git a/libft/memory/ft_memcmp.c b/libft/memory/ft_memcmp.c new file mode 100644 index 0000000..29013d3 --- /dev/null +++ b/libft/memory/ft_memcmp.c @@ -0,0 +1,30 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_memcmp.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 18:41:27 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:26 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +int ft_memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *s1_str; + const unsigned char *s2_str; + + s1_str = (unsigned char *)s1; + s2_str = (unsigned char *)s2; + while (n--) + { + if (*s1_str != *s2_str) + return (*s1_str - *s2_str); + s1_str++; + s2_str++; + } + return (0); +} diff --git a/libft/memory/ft_memcpy.c b/libft/memory/ft_memcpy.c new file mode 100644 index 0000000..3e0d542 --- /dev/null +++ b/libft/memory/ft_memcpy.c @@ -0,0 +1,28 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_memcpy.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 15:45:59 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:30 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* When memory regions may overlap, you should use memmove instead of memcpy */ +void *ft_memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *dest_str; + const unsigned char *src_str; + + if (n == 0 || dest == src) + return (dest); + dest_str = (unsigned char *)dest; + src_str = (const unsigned char *)src; + while (n--) + *(dest_str++) = *(src_str++); + return (dest); +} diff --git a/libft/memory/ft_memmove.c b/libft/memory/ft_memmove.c new file mode 100644 index 0000000..c4a9e9f --- /dev/null +++ b/libft/memory/ft_memmove.c @@ -0,0 +1,35 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_memmove.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 19:28:19 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:34 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void *ft_memmove(void *dest, const void *src, size_t n) +{ + unsigned char *dest_str; + const unsigned char *src_str; + + if (!dest && !src) + return (NULL); + dest_str = (unsigned char *)dest; + src_str = (const unsigned char *)src; + if (dest < src) + while (n--) + *dest_str++ = *src_str++; + else + { + src_str += n; + dest_str += n; + while (n--) + *--dest_str = *--src_str; + } + return (dest); +} diff --git a/libft/memory/ft_memrchr.c b/libft/memory/ft_memrchr.c new file mode 100644 index 0000000..e766d30 --- /dev/null +++ b/libft/memory/ft_memrchr.c @@ -0,0 +1,25 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_memrchr.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/05 18:16:23 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:37 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* search by decrement loop */ +void *ft_memrchr(const void *s, int c, size_t n) +{ + const unsigned char *us; + + us = (unsigned char *)s; + while (n--) + if (us[n] == (unsigned char)c) + return ((void *)(us + n)); + return (NULL); +} diff --git a/libft/memory/ft_memset.c b/libft/memory/ft_memset.c new file mode 100644 index 0000000..68e7931 --- /dev/null +++ b/libft/memory/ft_memset.c @@ -0,0 +1,26 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_memset.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/24 22:36:41 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:41 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Undefined if s == NULL && n > 0. */ +void *ft_memset(void *s, int c, size_t n) +{ + unsigned char *uc_dst; + + if (!n) + return (s); + uc_dst = (unsigned char *)s; + while (n--) + *uc_dst++ = (unsigned char)c; + return (s); +} diff --git a/libft/original/ft_isspace.c b/libft/original/ft_isspace.c new file mode 100644 index 0000000..24c731e --- /dev/null +++ b/libft/original/ft_isspace.c @@ -0,0 +1,19 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_isspace.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/11 18:12:36 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 15:41:22 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +int ft_isspace(const char c) +{ + return (c == '\a' || c == '\b' || c == '\t' || c == '\n' || c == '\v' + || c == '\f' || c == '\r' || c == ' '); +} diff --git a/libft/original/ft_max.c b/libft/original/ft_max.c new file mode 100644 index 0000000..8df788c --- /dev/null +++ b/libft/original/ft_max.c @@ -0,0 +1,46 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_max.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/11 18:09:27 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:47 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +int ft_max_int(int a, int b, int *m) +{ + if (a <= b) + *m = b; + else + *m = a; + return (*m); +} + +long ft_max_long(long a, long b) +{ + if (a <= b) + return (b); + else + return (a); +} + +long long ft_max_long_long(long long a, long long b) +{ + if (a <= b) + return (b); + else + return (a); +} + +size_t ft_max_size_t(size_t a, size_t b) +{ + if (a <= b) + return (b); + else + return (a); +} diff --git a/libft/original/ft_min.c b/libft/original/ft_min.c new file mode 100644 index 0000000..d0d5722 --- /dev/null +++ b/libft/original/ft_min.c @@ -0,0 +1,45 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_min.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/11 18:05:03 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:50 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +int ft_min_int(int a, int b) +{ + if (a >= b) + return (b); + else + return (a); +} + +long ft_min_long(long a, long b) +{ + if (a >= b) + return (b); + else + return (a); +} + +long long ft_min_long_long(long long a, long long b) +{ + if (a >= b) + return (b); + else + return (a); +} + +size_t ft_min_size_t(size_t a, size_t b) +{ + if (a >= b) + return (b); + else + return (a); +} diff --git a/libft/original/ft_swap.c b/libft/original/ft_swap.c new file mode 100644 index 0000000..9c645c3 --- /dev/null +++ b/libft/original/ft_swap.c @@ -0,0 +1,22 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_swap.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/07 20:23:04 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void ft_swap(int *a, int *b) +{ + int tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} diff --git a/libft/original/is_blank_line.c b/libft/original/is_blank_line.c new file mode 100644 index 0000000..e385beb --- /dev/null +++ b/libft/original/is_blank_line.c @@ -0,0 +1,26 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* is_blank_line.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: urassh +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/13 16:15:16 by surayama #+# #+# */ +/* Updated: 2025/10/13 16:16:46 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +int is_blank_line(const char *s) +{ + if (!s || !*s) + return (1); + while (*s) + { + if (!ft_isspace((unsigned char)*s)) + return (0); + s++; + } + return (1); +} diff --git a/libft/output/ft_putchar_fd.c b/libft/output/ft_putchar_fd.c new file mode 100644 index 0000000..3e0bae9 --- /dev/null +++ b/libft/output/ft_putchar_fd.c @@ -0,0 +1,18 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_putchar_fd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/26 14:16:34 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:58:58 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void ft_putchar_fd(char c, int fd) +{ + write(fd, &c, 1); +} diff --git a/libft/output/ft_putendl_fd.c b/libft/output/ft_putendl_fd.c new file mode 100644 index 0000000..1328998 --- /dev/null +++ b/libft/output/ft_putendl_fd.c @@ -0,0 +1,22 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_putendl_fd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/26 14:29:52 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:03 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void ft_putendl_fd(const char *s, int fd) +{ + if (!s) + return ; + ft_putstr_fd(s, fd); + write(fd, "\n", 1); + return ; +} diff --git a/libft/output/ft_putnbr_fd.c b/libft/output/ft_putnbr_fd.c new file mode 100644 index 0000000..598da7e --- /dev/null +++ b/libft/output/ft_putnbr_fd.c @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_putnbr_fd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/26 14:33:56 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:06 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void ft_putnbr_fd(int n, int fd) +{ + if (n == -2147483648) + return (write(fd, "-2147483648", 11), (void)0); + if (n < 0) + { + write(fd, "-", 1); + n *= -1; + } + if (n > 9) + ft_putnbr_fd(n / 10, fd); + write(fd, &(char){n % 10 + '0'}, 1); +} diff --git a/libft/output/ft_putstr_fd.c b/libft/output/ft_putstr_fd.c new file mode 100644 index 0000000..ec62686 --- /dev/null +++ b/libft/output/ft_putstr_fd.c @@ -0,0 +1,21 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_putstr_fd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/26 14:23:51 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:09 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void ft_putstr_fd(const char *s, int fd) +{ + if (!s) + return ; + write(fd, s, ft_strlen(s)); + return ; +} diff --git a/libft/string/ft_free_array.c b/libft/string/ft_free_array.c new file mode 100644 index 0000000..e77afe6 --- /dev/null +++ b/libft/string/ft_free_array.c @@ -0,0 +1,28 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_free_array.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 23:08:19 by kjikuhar #+# #+# */ +/* Updated: 2026/03/03 14:23:20 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +void ft_free_array(void **arr) +{ + void **temp; + + if (!arr) + return ; + temp = arr; + while (*temp) + { + free(*temp); + temp++; + } + free(arr); +} diff --git a/libft/string/ft_split.c b/libft/string/ft_split.c new file mode 100644 index 0000000..37be22e --- /dev/null +++ b/libft/string/ft_split.c @@ -0,0 +1,111 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_split.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/01 21:07:26 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:18 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +static int ft_strlen_for_split_(char const *target_str, char delimiter) +{ + int i; + + i = 0; + while (target_str[i] != delimiter && target_str[i]) + i++; + return (i); +} + +static int count_words_(char const *target_str, char delimiter) +{ + int in_word_flag; + int count; + + in_word_flag = 0; + count = 0; + while (*target_str) + { + if (in_word_flag == 0) + { + if (*target_str == delimiter) + in_word_flag = 0; + else + { + in_word_flag = 1; + count++; + } + } + else if (in_word_flag == 1) + { + if (*target_str == delimiter) + in_word_flag = 0; + } + target_str++; + } + return (count); +} + +static char *ft_strdup_for_split(char const **src, char delimiter) +{ + char *dest; + int i; + + i = 0; + dest = malloc(ft_strlen_for_split_(*src, delimiter) + 1); + if (!dest) + return (NULL); + while (**src && **src != delimiter) + { + dest[i] = **src; + i++; + (*src)++; + } + dest[i] = '\0'; + return (dest); +} + +/* Helper function to free allocated strings in case of error */ +static void free_split_result(char **arr) +{ + char **temp; + + if (!arr) + return ; + temp = arr; + while (*temp) + { + free(*temp); + temp++; + } + free(arr); +} + +char **ft_split(char const *s, char c) +{ + char **splitted; + char **p; + + splitted = malloc(sizeof(char *) * (count_words_(s, c) + 1)); + if (!splitted) + return (NULL); + p = splitted; + while (*s) + { + while (*s == c) + s++; + if (!*s) + break ; + *splitted = ft_strdup_for_split(&s, c); + if (!*splitted) + return (free_split_result(p), NULL); + splitted++; + } + *splitted = NULL; + return (p); +} diff --git a/libft/string/ft_strchr.c b/libft/string/ft_strchr.c new file mode 100644 index 0000000..1193436 --- /dev/null +++ b/libft/string/ft_strchr.c @@ -0,0 +1,30 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strchr.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 21:03:19 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:21 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Locate first occurrence of character in string */ +char *ft_strchr(const char *s, int c) +{ + unsigned char uc; + + uc = (unsigned char)c; + while (*s) + { + if (*s == uc) + return ((char *)s); + s++; + } + if (uc == 0) + return ((char *)s); + return (NULL); +} diff --git a/libft/string/ft_strdup.c b/libft/string/ft_strdup.c new file mode 100644 index 0000000..0f27d67 --- /dev/null +++ b/libft/string/ft_strdup.c @@ -0,0 +1,32 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strdup.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/03/27 05:14:00 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 15:42:45 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +char *ft_strdup(const char *s) +{ + char *tmp; + char *p; + + tmp = (char *)malloc(sizeof(s[0]) * (ft_strlen(s) + 1)); + if (tmp == NULL) + return (NULL); + p = tmp; + while (*s) + { + *tmp = *s; + s++; + tmp++; + } + *tmp = '\0'; + return (p); +} diff --git a/libft/string/ft_striteri.c b/libft/string/ft_striteri.c new file mode 100644 index 0000000..daa3ea8 --- /dev/null +++ b/libft/string/ft_striteri.c @@ -0,0 +1,25 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_striteri.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/26 14:04:33 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:28 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +void ft_striteri(char *s, void (*f)(unsigned int, char *)) +{ + unsigned int index; + + if (!s || !f) + return ; + index = 0; + while (s[index]) + { + f(index, &s[index]); + index++; + } +} diff --git a/libft/string/ft_strjoin.c b/libft/string/ft_strjoin.c new file mode 100644 index 0000000..84c3609 --- /dev/null +++ b/libft/string/ft_strjoin.c @@ -0,0 +1,61 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strjoin.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/03/28 20:47:54 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:34 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +static char *ft_strcat(char *dest, const char *src) +{ + size_t i; + size_t j; + + i = 0; + while (dest[i]) + i++; + j = 0; + while (src[j]) + { + dest[j + i] = src[j]; + j++; + } + dest[j + i] = '\0'; + return (dest); +} + +static char *ft_strcpy(char *dest, const char *src) +{ + size_t i; + + i = 0; + while (src[i]) + { + dest[i] = src[i]; + i++; + } + dest[i] = '\0'; + return (dest); +} + +char *ft_strjoin(char const *s1, char const *s2) +{ + char *dest; + size_t sum_len; + + if (!s1 || !s2) + return (NULL); + sum_len = ft_strlen(s1) + ft_strlen(s2); + dest = malloc(sizeof(char) * (sum_len + 1)); + if (!dest) + return (NULL); + ft_strcpy(dest, s1); + ft_strcat(dest, s2); + return (dest); +} diff --git a/libft/string/ft_strlcat.c b/libft/string/ft_strlcat.c new file mode 100644 index 0000000..ae136f1 --- /dev/null +++ b/libft/string/ft_strlcat.c @@ -0,0 +1,43 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strlcat.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/03/18 11:41:28 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:36 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +static size_t ft_strnlen(const char *str, size_t n) +{ + size_t i; + + i = 0; + while (str[i] && i < n) + i++; + return (i); +} + +size_t ft_strlcat(char *dst, const char *src, size_t size) +{ + size_t i; + size_t dst_length; + size_t src_length; + + dst_length = ft_strnlen(dst, size); + src_length = ft_strlen(src); + i = 0; + if (dst_length >= size) + return (dst_length + src_length); + while (src[i] && dst_length + i + 1 < size) + { + dst[dst_length + i] = src[i]; + i++; + } + dst[dst_length + i] = '\0'; + return (dst_length + src_length); +} diff --git a/libft/string/ft_strlcpy.c b/libft/string/ft_strlcpy.c new file mode 100644 index 0000000..079f8bd --- /dev/null +++ b/libft/string/ft_strlcpy.c @@ -0,0 +1,33 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strlcpy.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/03/19 20:28:11 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:40 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +size_t ft_strlcpy(char *dst, const char *src, size_t size) +{ + size_t i; + size_t src_len; + + if (!dst || !src) + return (0); + src_len = ft_strlen(src); + if (size == 0) + return (src_len); + i = 0; + while (src[i] && i + 1 < size) + { + dst[i] = src[i]; + i++; + } + dst[i] = '\0'; + return (src_len); +} diff --git a/libft/string/ft_strlen.c b/libft/string/ft_strlen.c new file mode 100644 index 0000000..6ee613d --- /dev/null +++ b/libft/string/ft_strlen.c @@ -0,0 +1,25 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strlen.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/24 22:06:53 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:42 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +size_t ft_strlen(const char *s) +{ + size_t length; + + if (!s) + return (0); + length = 0; + while (s[length]) + length++; + return (length); +} diff --git a/libft/string/ft_strmapi.c b/libft/string/ft_strmapi.c new file mode 100644 index 0000000..0c4d100 --- /dev/null +++ b/libft/string/ft_strmapi.c @@ -0,0 +1,35 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strmapi.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/26 12:57:28 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:45 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +char *ft_strmapi(char const *s, char (*f)(unsigned int, char)) +{ + unsigned int index; + size_t s_length; + char *rtv_str; + + if (!s || !f) + return (NULL); + index = 0; + s_length = ft_strlen(s); + rtv_str = malloc(sizeof(char) * (s_length + 1)); + if (!rtv_str) + return (NULL); + while (s[index]) + { + rtv_str[index] = (*f)(index, s[index]); + index++; + } + rtv_str[index] = '\0'; + return (rtv_str); +} diff --git a/libft/string/ft_strncmp.c b/libft/string/ft_strncmp.c new file mode 100644 index 0000000..d34b479 --- /dev/null +++ b/libft/string/ft_strncmp.c @@ -0,0 +1,28 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strncmp.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/24 22:22:54 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:51 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Compare two strings up to n characters */ +int ft_strncmp(const char *s1, const char *s2, size_t n) +{ + size_t i; + + i = 0; + while (i < n && (s1[i] || s2[i])) + { + if (s1[i] != s2[i]) + return ((unsigned char)s1[i] - (unsigned char)s2[i]); + i++; + } + return (0); +} diff --git a/libft/string/ft_strnstr.c b/libft/string/ft_strnstr.c new file mode 100644 index 0000000..cd2bc59 --- /dev/null +++ b/libft/string/ft_strnstr.c @@ -0,0 +1,40 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strnstr.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 21:07:45 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:54 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Locate substring in a string with length limit */ +char *ft_strnstr(const char *big, const char *little, size_t len) +{ + size_t i; + size_t j; + size_t little_len; + + if (!little || !big) + return (NULL); + little_len = ft_strlen(little); + if (little_len == 0) + return ((char *)big); + if (len == 0 || little_len > len) + return (NULL); + i = 0; + while (big[i] && i < len) + { + j = 0; + while (i + j < len && little[j] && big[i + j] == little[j]) + j++; + if (little[j] == '\0') + return ((char *)&big[i]); + i++; + } + return (NULL); +} diff --git a/libft/string/ft_strrchr.c b/libft/string/ft_strrchr.c new file mode 100644 index 0000000..21485a1 --- /dev/null +++ b/libft/string/ft_strrchr.c @@ -0,0 +1,19 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strrchr.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 21:03:19 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 14:59:58 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +/* Why: +1 so that '\0' is also searched per strrchr spec */ +char *ft_strrchr(const char *s, int c) +{ + return (ft_memrchr(s, c, ft_strlen(s) + 1)); +} diff --git a/libft/string/ft_strtrim.c b/libft/string/ft_strtrim.c new file mode 100644 index 0000000..0f709e0 --- /dev/null +++ b/libft/string/ft_strtrim.c @@ -0,0 +1,44 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_strtrim.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 22:58:35 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 15:00:04 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +static size_t get_trimmed_length(char const *s1, char const *set) +{ + size_t start; + size_t end; + + start = 0; + end = ft_strlen(s1); + while (s1[start] && ft_strchr(set, (unsigned char)s1[start])) + start++; + while (end > start && ft_strchr(set, (unsigned char)s1[end - 1])) + end--; + return (end - start); +} + +char *ft_strtrim(char const *s1, char const *set) +{ + char *dest; + size_t dest_len; + + if (!s1 || !set) + return (NULL); + dest_len = get_trimmed_length(s1, set); + dest = malloc(dest_len + 1); + if (!dest) + return (NULL); + while (*s1 && ft_strchr(set, (unsigned char)*s1)) + s1++; + ft_strlcpy(dest, s1, dest_len + 1); + return (dest); +} diff --git a/libft/string/ft_substr.c b/libft/string/ft_substr.c new file mode 100644 index 0000000..e136db7 --- /dev/null +++ b/libft/string/ft_substr.c @@ -0,0 +1,39 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_substr.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/25 22:00:05 by kjikuhar #+# #+# */ +/* Updated: 2025/09/29 15:00:09 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +char *ft_substr(char const *s, unsigned int start, size_t len) +{ + size_t s_len; + char *substr; + size_t i; + + if (!s) + return (NULL); + s_len = ft_strlen(s); + if (!(start < s_len)) + return (ft_strdup("")); + if (!(start + len < s_len)) + len = s_len - start; + substr = malloc(sizeof(char) * (len + 1)); + if (!substr) + return (NULL); + i = 0; + while (i < len) + { + substr[i] = s[start + i]; + i++; + } + substr[i] = '\0'; + return (substr); +} diff --git a/readline.supp b/readline.supp new file mode 100644 index 0000000..e7ab72c --- /dev/null +++ b/readline.supp @@ -0,0 +1,38 @@ +# Valgrind suppression file for readline library memory leaks +# Usage: valgrind --leak-check=full --suppressions=readline.supp ./minishell + +# readline関数の一般的なメモリ確保を抑制 +# readline()はプログラム終了までメモリを保持する +{ + readline_leak_1 + Memcheck:Leak + match-leak-kinds: reachable + fun:malloc + fun:xmalloc + ... + fun:readline +} + +# キーマップ初期化時のメモリ確保を抑制 +# rl_make_bare_keymap: キーバインド設定用の内部データ構造を作成 +# readline初期化時に1度だけ実行され、プログラム終了まで保持される +{ + readline_leak_2 + Memcheck:Leak + match-leak-kinds: reachable + fun:malloc + fun:xmalloc + fun:rl_make_bare_keymap + ... +} + +# readlineライブラリ全体からのメモリ確保を抑制(最も包括的) +# libreadline.so由来のあらゆるメモリ確保をカバー +{ + readline_leak_3 + Memcheck:Leak + match-leak-kinds: reachable + fun:*alloc + ... + obj:*/libreadline.so* +} diff --git a/src/builtin/cd/cd.c b/src/builtin/cd/cd.c new file mode 100644 index 0000000..1f0f396 --- /dev/null +++ b/src/builtin/cd/cd.c @@ -0,0 +1,114 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* cd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/01 00:48:09 by surayama #+# #+# */ +/* Updated: 2026/04/06 07:48:40 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include + +static int by_move_to_home(t_shell_table *shell_table); +static int by_move_to_oldpwd(t_shell_table *shell_table); +static int by_move_to_arg(const char *arg, t_shell_table *shell_table); +static int move_to_path(const char *absolute_path, t_shell_table *shell_table); +char *search_cdpath(const char *arg, t_shell_table *st); + +int cd(t_list *argv, t_shell_table *shell_table) +{ + int argc; + + argc = ft_lstsize(argv); + if (argc > 2) + { + ft_putstr_fd("Error: cd too many arguments\n", STDERR_FILENO); + return (1); + } + if (argc == 1) + return (by_move_to_home(shell_table)); + if (argc == 2 && argv->next->content) + { + if (ft_strncmp((char *)argv->next->content, "-", 2) == 0) + return (by_move_to_oldpwd(shell_table)); + return (by_move_to_arg((char *)argv->next->content, shell_table)); + } + return (SUCCESS); +} + +static int by_move_to_arg(const char *arg, t_shell_table *shell_table) +{ + char *cdpath_result; + + if (arg[0] != '/') + { + cdpath_result = search_cdpath(arg, shell_table); + if (cdpath_result) + { + ft_putendl_fd(cdpath_result, STDOUT_FILENO); + return (move_to_path(cdpath_result, shell_table)); + } + } + return (move_to_path(to_absolute_path(arg), shell_table)); +} + +static int by_move_to_home(t_shell_table *shell_table) +{ + char *home; + + home = st_search(shell_table, "HOME"); + if (!home) + { + ft_putstr_fd("Error: cd HOME not set\n", STDERR_FILENO); + return (1); + } + return (move_to_path(home, shell_table)); +} + +static int by_move_to_oldpwd(t_shell_table *shell_table) +{ + char *oldpwd; + int result; + + oldpwd = st_search(shell_table, "OLDPWD"); + if (!oldpwd) + { + ft_putstr_fd("Error: cd OLDPWD not set\n", STDERR_FILENO); + return (1); + } + result = move_to_path(oldpwd, shell_table); + if (result == 0) + ft_putendl_fd(oldpwd, STDOUT_FILENO); + return (result); +} + +static int move_to_path(const char *absolute_path, t_shell_table *shell_table) +{ + char *old_pwd; + char *new_pwd; + int has_insert_error; + + old_pwd = getcwd(NULL, 0); + if (!old_pwd) + return (1); + if (chdir(absolute_path) == -1) + { + perror("cd"); + free(old_pwd); + return (1); + } + new_pwd = getcwd(NULL, 0); + if (!new_pwd) + { + free(old_pwd); + return (1); + } + has_insert_error = st_insert(shell_table, "OLDPWD", old_pwd, true) == 0 + || st_insert(shell_table, "PWD", new_pwd, true) == 0; + free(old_pwd); + free(new_pwd); + return (has_insert_error); +} diff --git a/src/builtin/cd/search_cdpath.c b/src/builtin/cd/search_cdpath.c new file mode 100644 index 0000000..afb5a14 --- /dev/null +++ b/src/builtin/cd/search_cdpath.c @@ -0,0 +1,95 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* search_cdpath.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/03/03 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/06 07:44:23 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "builtin.h" + +static t_list *split_cdpath(const char *cdpath); +static char *try_cdpath_entry(const char *entry, const char *arg); + +char *search_cdpath(const char *arg, t_shell_table *st) +{ + char *cdpath; + t_list *entries; + t_list *cur; + char *result; + + cdpath = st_search(st, "CDPATH"); + if (!cdpath) + return (NULL); + entries = split_cdpath(cdpath); + cur = entries; + while (cur) + { + result = try_cdpath_entry((char *)cur->content, arg); + if (result) + { + ft_lstclear(&entries, free); + return (result); + } + cur = cur->next; + } + ft_lstclear(&entries, free); + return (NULL); +} + +static char *try_cdpath_entry(const char *entry, const char *arg) +{ + char *joined; + char *absolute; + + joined = join_path(entry, arg); + if (!joined) + return (NULL); + absolute = to_absolute_path(joined); + free(joined); + if (!absolute) + return (NULL); + if (access(absolute, F_OK) == 0) + return (absolute); + free(absolute); + return (NULL); +} + +static void add_entry(t_list **list, const char *cdpath, size_t start, + size_t end) +{ + char *entry; + + if (start == end) + entry = ft_strdup("."); + else + entry = ft_substr(cdpath, start, end - start); + if (entry) + ft_lstadd_back(list, ft_lstnew(entry)); +} + +static t_list *split_cdpath(const char *cdpath) +{ + t_list *list; + size_t i; + size_t start; + + list = NULL; + i = 0; + start = 0; + while (cdpath[i]) + { + if (cdpath[i] == ':') + { + add_entry(&list, cdpath, start, i); + start = i + 1; + } + i++; + } + add_entry(&list, cdpath, start, i); + return (list); +} diff --git a/src/builtin/echo/echo.c b/src/builtin/echo/echo.c new file mode 100644 index 0000000..cd8c6aa --- /dev/null +++ b/src/builtin/echo/echo.c @@ -0,0 +1,82 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* echo.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/29 17:16:37 by surayama #+# #+# */ +/* Updated: 2025/12/01 03:17:35 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "builtin.h" + +static void skip_head_node(t_list **argv); +static bool is_n_option(t_list *argv); +static void print_argv(t_list *to_print_argv, bool with_newline); + +int echo(t_list *argv, t_shell_table *shell_table) +{ + t_list *echo_argv; + bool with_newline; + + (void)shell_table; + echo_argv = argv; + with_newline = true; + skip_head_node(&echo_argv); + while (is_n_option(echo_argv)) + { + with_newline = false; + skip_head_node(&echo_argv); + } + print_argv(echo_argv, with_newline); + return (0); +} + +static void skip_head_node(t_list **argv) +{ + if (argv && *argv) + *argv = (*argv)->next; +} + +static bool is_n_option(t_list *argv) +{ + t_list *option_node; + char *str; + int i; + + option_node = argv; + if (!option_node || !option_node->content) + return (false); + str = (char *)option_node->content; + if (str[0] != '-' || str[1] == '\0') + return (false); + i = 1; + while (str[i]) + { + if (str[i] != 'n') + return (false); + i++; + } + return (true); +} + +static void print_argv(t_list *to_print_argv, bool with_newline) +{ + t_list *current; + + current = to_print_argv; + while (current) + { + if (current->content) + { + ft_putstr_fd((char *)current->content, STDOUT_FILENO); + if (current->next) + write(STDOUT_FILENO, " ", 1); + } + current = current->next; + } + if (with_newline) + write(STDOUT_FILENO, "\n", 1); +} diff --git a/src/builtin/exit/exit.c b/src/builtin/exit/exit.c new file mode 100644 index 0000000..d0043b2 --- /dev/null +++ b/src/builtin/exit/exit.c @@ -0,0 +1,103 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exit.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/11 17:55:33 by kjikuhar #+# #+# */ +/* Updated: 2026/04/11 17:55:34 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "builtin.h" + +static bool is_numeric(const char *str) +{ + int i; + + i = 0; + if (str[i] == '-' || str[i] == '+') + i++; + if (!str[i]) + return (false); + while (str[i]) + { + if (!ft_isdigit((unsigned char)str[i])) + return (false); + i++; + } + return (true); +} + +static void exit_numeric_error(const char *str) +{ + ft_putstr_fd(SHELL_NAME ": exit: ", STDERR_FILENO); + ft_putstr_fd(str, STDERR_FILENO); + ft_putstr_fd(": numeric argument required\n", STDERR_FILENO); + exit(2); +} + +static long long ft_atoll_check(const char *str, bool *ovf) +{ + long long num; + int sign; + int digit; + + *ovf = false; + sign = 1; + if (*str == '+' || *str == '-') + { + if (*str == '-') + sign = -1; + str++; + } + num = 0; + while (ft_isdigit((unsigned char)*str)) + { + digit = *str - '0'; + if (sign == 1 && num > (LLONG_MAX - digit) / 10) + return (*ovf = true, 0); + if (sign == -1 && num > -(LLONG_MIN + digit) / 10) + return (*ovf = true, 0); + num = num * 10 + digit; + str++; + } + return (num * sign); +} + +static void exit_no_args(t_shell_table *shell_table) +{ + char *str; + + str = st_search(shell_table, "?"); + if (!str) + exit(0); + exit(ft_atoi(str) & 0xFF); +} + +int builtin_exit(t_list *argv, t_shell_table *shell_table) +{ + t_list *args; + char *str; + long long val; + bool ovf; + + write(STDOUT_FILENO, "exit\n", 5); + args = argv->next; + if (!args) + exit_no_args(shell_table); + str = (char *)args->content; + if (!is_numeric(str)) + exit_numeric_error(str); + if (args->next) + { + ft_putstr_fd(SHELL_NAME ": exit: too many arguments\n", + STDERR_FILENO); + return (1); + } + val = ft_atoll_check(str, &ovf); + if (ovf) + exit_numeric_error(str); + exit((unsigned char)val); +} diff --git a/src/builtin/export/export.c b/src/builtin/export/export.c new file mode 100644 index 0000000..65b92fd --- /dev/null +++ b/src/builtin/export/export.c @@ -0,0 +1,98 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* export.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/29 20:29:03 by surayama #+# #+# */ +/* Updated: 2026/03/03 19:31:33 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "builtin.h" + +static void skip_head_node(t_list **argv); +static int insert_assignment(t_shell_table *shell_table, + const char *assignment); +static int insert_key_value(t_shell_table *shell_table, + const char *key, const char *assignment); +static char *get_key_from_assignment(const char *assignment); + +int export(t_list *argv, t_shell_table *shell_table) +{ + if (!argv || !argv->content) + return (1); + skip_head_node(&argv); + if (!argv) + { + st_print_env(shell_table); + return (0); + } + while (argv) + { + if (insert_assignment(shell_table, + (const char *)argv->content) == ERROR) + return (1); + skip_head_node(&argv); + } + return (0); +} + +static void skip_head_node(t_list **argv) +{ + if (argv && *argv) + *argv = (*argv)->next; +} + +static int insert_assignment(t_shell_table *shell_table, + const char *assignment) +{ + char *key; + int result; + + if (!shell_table || !assignment) + return (ERROR); + key = get_key_from_assignment(assignment); + if (!key) + return (ERROR); + if (!ft_strchr(assignment, '=')) + result = st_set_exported(shell_table, key); + else + result = insert_key_value(shell_table, key, assignment); + free(key); + if (result <= 0) + { + print_export_error(assignment); + return (ERROR); + } + return (SUCCESS); +} + +static int insert_key_value(t_shell_table *shell_table, + const char *key, const char *assignment) +{ + char *equal_pos; + char *value; + int result; + + equal_pos = ft_strchr(assignment, '='); + value = ft_strdup(equal_pos + 1); + if (!value) + return (ERROR); + result = st_insert(shell_table, key, value, true); + free(value); + return (result); +} + +static char *get_key_from_assignment(const char *assignment) +{ + char *equal_sign_pos; + size_t key_len; + + equal_sign_pos = ft_strchr(assignment, '='); + if (!equal_sign_pos) + return (ft_strdup(assignment)); + key_len = equal_sign_pos - assignment; + return (ft_substr(assignment, 0, key_len)); +} diff --git a/src/builtin/export/export_errors.c b/src/builtin/export/export_errors.c new file mode 100644 index 0000000..0de2d80 --- /dev/null +++ b/src/builtin/export/export_errors.c @@ -0,0 +1,21 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* export_errors.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/17 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/17 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "builtin.h" + +void print_export_error(const char *assignment) +{ + ft_putstr_fd(SHELL_NAME, STDERR_FILENO); + ft_putstr_fd(": export: `", STDERR_FILENO); + ft_putstr_fd((char *)assignment, STDERR_FILENO); + ft_putendl_fd("': not a valid identifier", STDERR_FILENO); +} diff --git a/src/builtin/pwd/pwd.c b/src/builtin/pwd/pwd.c new file mode 100644 index 0000000..f4ee161 --- /dev/null +++ b/src/builtin/pwd/pwd.c @@ -0,0 +1,31 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* pwd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/29 20:16:00 by surayama #+# #+# */ +/* Updated: 2026/01/07 21:11:50 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "builtin.h" + +int pwd(t_list *argv, t_shell_table *shell_table) +{ + char *pwd_path; + + (void)argv; + (void)shell_table; + pwd_path = getcwd(NULL, 0); + if (!pwd_path) + { + perror("pwd"); + return (EXIT_FAILURE); + } + ft_putstr_fd(pwd_path, STDOUT_FILENO); + write(STDOUT_FILENO, "\n", 1); + free(pwd_path); + return (EXIT_SUCCESS); +} diff --git a/src/builtin/unset/unset.c b/src/builtin/unset/unset.c new file mode 100644 index 0000000..68d8b7d --- /dev/null +++ b/src/builtin/unset/unset.c @@ -0,0 +1,40 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* unset.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/29 20:59:00 by surayama #+# #+# */ +/* Updated: 2026/01/07 21:12:00 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "builtin.h" + +static void skip_head_node(t_list **argv); + +int unset(t_list *argv, t_shell_table *shell_table) +{ + char *key; + int result; + + if (!argv || !argv->content) + return (SUCCESS); + result = SUCCESS; + skip_head_node(&argv); + while (argv) + { + key = (char *)argv->content; + if (key) + st_delete(shell_table, key); + skip_head_node(&argv); + } + return (result); +} + +static void skip_head_node(t_list **argv) +{ + if (argv && *argv) + *argv = (*argv)->next; +} diff --git a/src/callback/on_input.c b/src/callback/on_input.c new file mode 100644 index 0000000..ba31414 --- /dev/null +++ b/src/callback/on_input.c @@ -0,0 +1,54 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* on_input.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/10 17:04:25 by surayama #+# #+# */ +/* Updated: 2026/04/17 21:23:54 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "minishell.h" + +static bool has_valid_heredoc(t_list *tokens) +{ + while (tokens) + { + if (ft_strncmp(tokens->content, "<<", 3) == 0) + if (!tokens->next || !tokens->next->content) + return (false); + tokens = tokens->next; + } + return (true); +} + +int on_input(char *input, t_shell_table *shell_table, int last_status) +{ + t_list *tokens; + t_ast *ast; + int current_status; + + tokens = tokenize(input); + if (!tokens) + return (2); + if (!has_valid_heredoc(tokens)) + { + ft_lstclear(&tokens, free); + return (2); + } + if (!heredoc(tokens, shell_table)) + { + ft_lstclear(&tokens, free); + return (130); + } + shell_table->last_status = last_status; + ast = parse(tokens); + ft_lstclear(&tokens, free); + if (!ast) + return (2); + current_status = exec_ast(ast, shell_table); + free_ast(ast); + return (current_status); +} diff --git a/src/component/directory/list_directory.c b/src/component/directory/list_directory.c new file mode 100644 index 0000000..1b27507 --- /dev/null +++ b/src/component/directory/list_directory.c @@ -0,0 +1,67 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* get_directory_entries.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/27 15:30:40 by surayama #+# #+# */ +/* Updated: 2025/12/01 03:20:12 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "directory.h" + +static int throw_error(void); +static int append_entry(t_list **entries, const char *entry_name); + +int list_directory(const char *path, bool include_hidden, + t_list **entries) +{ + DIR *dir; + struct dirent *entry; + + if (!entries) + return (ERROR); + *entries = NULL; + dir = opendir(path); + if (!dir) + return (throw_error()); + while (true) + { + entry = readdir(dir); + if (!entry) + break ; + if (!include_hidden && entry->d_name[0] == '.') + continue ; + if (append_entry(entries, entry->d_name) == ERROR) + { + closedir(dir); + return (ERROR); + } + } + closedir(dir); + return (SUCCESS); +} + +static int throw_error(void) +{ + if (errno == ENOENT) + return (NOT_FOUND); + else if (errno == EACCES) + return (NO_PERMISSION); + else if (errno == ENOTDIR) + return (NOT_A_DIRECTORY); + return (ERROR); +} + +static int append_entry(t_list **entries, const char *entry_name) +{ + t_list *entry_node; + + entry_node = ft_lstnew(ft_strdup(entry_name)); + if (!entry_node) + return (ERROR); + ft_lstadd_back(entries, entry_node); + return (SUCCESS); +} diff --git a/src/component/directory/list_directory_recursive.c b/src/component/directory/list_directory_recursive.c new file mode 100644 index 0000000..1efc24e --- /dev/null +++ b/src/component/directory/list_directory_recursive.c @@ -0,0 +1,97 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* list_directory_recursive.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/27 15:30:40 by surayama #+# #+# */ +/* Updated: 2025/12/01 03:20:12 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "directory.h" +#include "path.h" + +static int throw_error(void); +static int process_entry(const char *path, const char *name, + bool include_hidden, t_list **entries); +static int read_directory_entries(DIR *dir, const char *path, + bool include_hidden, t_list **entries); + +int list_directory_recursive(const char *path, bool include_hidden, + t_list **entries) +{ + DIR *dir; + int result; + + if (!entries) + return (ERROR); + *entries = NULL; + dir = opendir(path); + if (!dir) + return (throw_error()); + result = read_directory_entries(dir, path, include_hidden, entries); + closedir(dir); + return (result); +} + +static int read_directory_entries(DIR *dir, const char *path, + bool include_hidden, t_list **entries) +{ + struct dirent *entry; + int result; + + while (true) + { + entry = readdir(dir); + if (!entry) + break ; + if (!include_hidden && entry->d_name[0] == '.') + continue ; + result = process_entry(path, entry->d_name, include_hidden, entries); + if (result != SUCCESS) + return (result); + } + return (SUCCESS); +} + +static int process_entry(const char *path, const char *name, + bool include_hidden, t_list **entries) +{ + char *full_path; + struct stat st; + t_list *sub_entries; + int result; + + full_path = join_path(path, name); + if (!full_path) + return (ERROR); + ft_lstadd_back(entries, ft_lstnew(ft_strdup(full_path))); + if (stat(full_path, &st) != 0 || !S_ISDIR(st.st_mode)) + { + free(full_path); + return (SUCCESS); + } + if (ft_strncmp(name, ".", 2) == 0 || ft_strncmp(name, "..", 3) == 0) + { + free(full_path); + return (SUCCESS); + } + result = list_directory_recursive(full_path, include_hidden, &sub_entries); + if (result == SUCCESS && sub_entries) + ft_lstadd_back(entries, sub_entries); + free(full_path); + return (SUCCESS); +} + +static int throw_error(void) +{ + if (errno == ENOENT) + return (NOT_FOUND); + else if (errno == EACCES) + return (NO_PERMISSION); + else if (errno == ENOTDIR) + return (NOT_A_DIRECTORY); + return (ERROR); +} diff --git a/src/component/heredoc/heredoc.c b/src/component/heredoc/heredoc.c new file mode 100644 index 0000000..c1c099b --- /dev/null +++ b/src/component/heredoc/heredoc.c @@ -0,0 +1,93 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* heredoc.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/20 14:13:36 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "heredoc.h" +#include "signal_handler.h" + +static bool is_heredoc_operator(const char *token); +static int rebuild_tokens(t_list *current, t_list *eof_node, + char *tmpfile_path); +static char *strip_quotes(const char *s, bool *was_quoted); + +static char *process_heredoc(t_list *current, t_shell_table *st) +{ + t_list *eof_node; + bool quoted; + char *delim; + char *tmpfile_path; + + eof_node = current->next; + if (eof_node == NULL) + return (NULL); + delim = strip_quotes(eof_node->content, "ed); + if (!delim) + return (NULL); + tmpfile_path = heredoc_prompt(delim, !quoted, st); + free(delim); + if (!tmpfile_path) + return (NULL); + if (rebuild_tokens(current, eof_node, tmpfile_path) == ERROR) + return (NULL); + return (tmpfile_path); +} + +t_list *heredoc(t_list *tokens, t_shell_table *st) +{ + t_list *current; + + current = tokens; + while (current != NULL) + { + if (is_heredoc_operator((char *)current->content)) + if (!process_heredoc(current, st)) + return (NULL); + current = current->next; + } + return (tokens); +} + +static char *strip_quotes(const char *s, bool *was_quoted) +{ + size_t len; + + *was_quoted = false; + len = ft_strlen(s); + if (len >= 2 && ((s[0] == '\'' && s[len - 1] == '\'') + || (s[0] == '"' && s[len - 1] == '"'))) + { + *was_quoted = true; + return (ft_substr(s, 1, len - 2)); + } + return (ft_strdup(s)); +} + +static bool is_heredoc_operator(const char *token) +{ + if (token == NULL) + return (false); + return (ft_strncmp(token, "<<", 3) == 0); +} + +static int rebuild_tokens(t_list *current, t_list *eof_node, + char *tmpfile_path) +{ + char *input_redir; + + input_redir = ft_strdup("<"); + if (!current || !eof_node || !tmpfile_path || !input_redir) + return (ERROR); + free(current->content); + free(eof_node->content); + current->content = input_redir; + eof_node->content = tmpfile_path; + return (SUCCESS); +} diff --git a/src/component/heredoc/heredoc_expand.c b/src/component/heredoc/heredoc_expand.c new file mode 100644 index 0000000..803bb4a --- /dev/null +++ b/src/component/heredoc/heredoc_expand.c @@ -0,0 +1,108 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* heredoc_expand.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/18 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/18 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "heredoc.h" + +char *heredoc_append_str(char *dst, const char *src) +{ + char *result; + + if (!dst) + return (ft_strdup(src)); + result = ft_strjoin(dst, src); + free(dst); + return (result); +} + +static char *expand_var(const char *s, size_t *i, + t_shell_table *st) +{ + char *key; + char *val; + size_t len; + + len = 0; + while (s[*i + 1 + len] + && (ft_isalnum(s[*i + 1 + len]) || s[*i + 1 + len] == '_')) + len++; + if (len == 0) + return (ft_strdup("$")); + key = ft_substr(s, *i + 1, len); + if (!key) + return (NULL); + *i += len; + val = st_search(st, key); + free(key); + if (!val) + return (ft_strdup("")); + return (ft_strdup(val)); +} + +static char *expand_dollar(const char *s, size_t *i, + t_shell_table *st) +{ + char *val; + + if (!s[*i + 1] || s[*i + 1] == ' ' || s[*i + 1] == '\n') + return (ft_strdup("$")); + if (s[*i + 1] == '?') + { + *i += 1; + val = st_search(st, "?"); + if (!val) + return (ft_strdup("0")); + return (ft_strdup(val)); + } + return (expand_var(s, i, st)); +} + +static char *expand_one_char(char *result, const char *line, + size_t *i, t_shell_table *st) +{ + char *expanded; + char buf[2]; + + if (line[*i] == '$') + { + expanded = expand_dollar(line, i, st); + if (!expanded) + return (free(result), NULL); + result = heredoc_append_str(result, expanded); + free(expanded); + } + else + { + buf[0] = line[*i]; + buf[1] = '\0'; + result = heredoc_append_str(result, buf); + } + return (result); +} + +char *expand_heredoc_line(const char *line, t_shell_table *st) +{ + char *result; + size_t i; + + result = ft_strdup(""); + if (!result) + return (NULL); + i = 0; + while (line[i]) + { + result = expand_one_char(result, line, &i, st); + if (!result) + return (NULL); + i++; + } + return (result); +} diff --git a/src/component/heredoc/heredoc_prompt.c b/src/component/heredoc/heredoc_prompt.c new file mode 100644 index 0000000..6a1a15f --- /dev/null +++ b/src/component/heredoc/heredoc_prompt.c @@ -0,0 +1,118 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* heredoc_prompt.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/20 15:05:31 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "heredoc.h" +#include "signal_handler.h" +#include + +static int write_line(int fd, char *line, bool expand, + t_shell_table *st); +static void heredoc_child(const char *delim, int fd, + bool expand, t_shell_table *st); + +static int wait_heredoc_child(pid_t pid) +{ + int wstatus; + + waitpid(pid, &wstatus, 0); + if (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGINT) + { + write(STDOUT_FILENO, "\n", 1); + return (-1); + } + if (WIFEXITED(wstatus)) + return (WEXITSTATUS(wstatus)); + return (-1); +} + +static void *heredoc_interrupted(char *tmpfile_path) +{ + unlink(tmpfile_path); + free(tmpfile_path); + set_signal_interactive(); + g_signal = SIGINT; + return (NULL); +} + +char *heredoc_prompt(const char *delim, bool expand, + t_shell_table *st) +{ + char *tmpfile_path; + int fd; + pid_t pid; + + tmpfile_path = create_tmpfile(); + if (tmpfile_path == NULL) + return (NULL); + fd = open_tmpfile(tmpfile_path); + if (fd == -1) + return (free(tmpfile_path), NULL); + pid = fork(); + if (pid == 0) + { + set_signal_default(); + heredoc_child(delim, fd, expand, st); + } + close(fd); + set_signal_ignore(); + if (wait_heredoc_child(pid) != 0) + return (heredoc_interrupted(tmpfile_path)); + set_signal_interactive(); + return (tmpfile_path); +} + +static void heredoc_child(const char *delim, int fd, + bool expand, t_shell_table *st) +{ + char *line; + int status; + + status = 0; + while (1) + { + line = readline(HEREDOC_PROMPT); + if (!line || ft_strncmp(line, delim, + ft_strlen(delim) + 1) == 0) + break ; + if (write_line(fd, line, expand, st) == ERROR) + { + status = 1; + break ; + } + free(line); + } + free(line); + exit(status); +} + +static int write_line(int fd, char *line, bool expand, + t_shell_table *st) +{ + char *out; + + if (expand) + out = expand_heredoc_line(line, st); + else + out = line; + if (!out) + return (ERROR); + if (write(fd, out, ft_strlen(out)) == -1 + || write(fd, "\n", 1) == -1) + { + if (expand) + free(out); + return (ERROR); + } + if (expand) + free(out); + return (SUCCESS); +} diff --git a/src/component/heredoc/tmpfile.c b/src/component/heredoc/tmpfile.c new file mode 100644 index 0000000..732a51a --- /dev/null +++ b/src/component/heredoc/tmpfile.c @@ -0,0 +1,76 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* tmpfile.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/20 14:28:49 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "heredoc.h" + +static char *get_tmpfile_id(const char *tmp_prefix); + +int open_tmpfile(char *tmpfile_path) +{ + int fd; + + fd = open(tmpfile_path, O_WRONLY | O_APPEND); + if (fd == -1) + { + free(tmpfile_path); + tmpfile_path = NULL; + return (-1); + } + return (fd); +} + +char *create_tmpfile(void) +{ + char *id; + char *path; + int tmp_file_id; + + id = get_tmpfile_id(HEREDOC_TMP_PREFIX); + if (id == NULL) + return (NULL); + path = ft_strjoin(HEREDOC_TMP_PREFIX, id); + free(id); + if (path == NULL) + return (NULL); + tmp_file_id = open(path, O_CREAT | O_EXCL | O_RDWR, 0600); + if (tmp_file_id == -1) + return (NULL); + close(tmp_file_id); + return (path); +} + +static char *get_tmpfile_id(const char *tmp_prefix) +{ + int id; + char *filename; + char *id_str; + + id = 0; + while (id < INT_MAX) + { + id_str = ft_itoa(id); + if (!id_str) + return (NULL); + filename = ft_strjoin(tmp_prefix, id_str); + free(id_str); + if (!filename) + return (NULL); + if (access(filename, F_OK) != 0) + { + free(filename); + return (ft_itoa(id)); + } + free(filename); + id++; + } + return (NULL); +} diff --git a/src/component/path/append_path.c b/src/component/path/append_path.c new file mode 100644 index 0000000..43f30d2 --- /dev/null +++ b/src/component/path/append_path.c @@ -0,0 +1,37 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* append_path.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/01 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/01/22 21:26:48 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "path.h" + +t_list *append_path(t_list **dest, const char *content) +{ + char *content_copy; + + if (!dest || !content) + return (NULL); + if (ft_strncmp(content, "..", 3) == 0) + { + if (*dest) + ft_lstdel_back(dest, free); + return (*dest); + } + else if (ft_strncmp(content, ".", 2) == 0) + return (*dest); + content_copy = ft_strdup(content); + if (!content_copy) + { + ft_lstclear(dest, free); + return (NULL); + } + ft_lstadd_back(dest, ft_lstnew(content_copy)); + return (*dest); +} diff --git a/src/component/path/join_path.c b/src/component/path/join_path.c new file mode 100644 index 0000000..af93257 --- /dev/null +++ b/src/component/path/join_path.c @@ -0,0 +1,30 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* join_path.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/03/03 16:34:11 by kjikuhar #+# #+# */ +/* Updated: 2026/03/03 16:57:28 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "path.h" + +char *join_path(const char *path_before, const char *path_after) +{ + size_t path_len; + size_t cmd_len; + char *joined; + + path_len = ft_strlen(path_before); + cmd_len = ft_strlen(path_after); + joined = malloc(sizeof(char) * (path_len + 1 + cmd_len + 1)); + if (!joined) + return (NULL); + ft_strlcpy(joined, path_before, path_len + 1); + joined[path_len] = '/'; + ft_strlcpy(joined + path_len + 1, path_after, cmd_len + 1); + return (joined); +} diff --git a/src/component/path/resolve_relative_path.c b/src/component/path/resolve_relative_path.c new file mode 100644 index 0000000..1a8a288 --- /dev/null +++ b/src/component/path/resolve_relative_path.c @@ -0,0 +1,43 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* resolve_relative_path.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/03/03 13:00:00 by surayama #+# #+# */ +/* Updated: 2026/03/03 13:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "path.h" +#include + +static bool has_path(const char *str); + +t_list *resolve_relative_path(t_list *tokens) +{ + t_list *current; + char *abs_path; + + current = tokens; + while (current) + { + if (has_path((char *)current->content)) + { + abs_path = to_absolute_path((char *)current->content); + if (abs_path) + { + free(current->content); + current->content = abs_path; + } + } + current = current->next; + } + return (tokens); +} + +static bool has_path(const char *str) +{ + return (ft_strchr(str, '/') != NULL); +} diff --git a/src/component/path/to_absolute_path.c b/src/component/path/to_absolute_path.c new file mode 100644 index 0000000..1a3ba03 --- /dev/null +++ b/src/component/path/to_absolute_path.c @@ -0,0 +1,132 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* to_absolute_path.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/01 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/03/03 13:10:31 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "path.h" + +static t_list *init_resolved_path(bool is_head_absolute, t_list **path_list); +static t_list *split_path_to_list(const char *path); +static void consume_head(t_list **path_list); +static char *list_to_path(t_list *resolved_path); + +char *to_absolute_path(const char *path) +{ + t_list *path_list; + t_list *resolved_path; + t_list *current; + char *result; + + if (!path || !*path) + return (NULL); + if (ft_strncmp(path, "/", 2) == 0) + return (ft_strdup("/")); + path_list = split_path_to_list(path); + if (!path_list && path[0] == '/') + return (ft_strdup("/")); + resolved_path = init_resolved_path(path[0] == '/', &path_list); + current = path_list; + while (current) + { + resolved_path = append_path(&resolved_path, (char *)current->content); + current = current->next; + } + ft_lstclear(&path_list, free); + result = list_to_path(resolved_path); + ft_lstclear(&resolved_path, free); + return (result); +} + +static t_list *init_resolved_path(bool is_head_absolute, t_list **path_list) +{ + char *cwd; + t_list *resolved_path; + + resolved_path = NULL; + if (is_head_absolute) + { + while (*path_list) + { + if (ft_strncmp((char *)(*path_list)->content, ".", 2) == 0 + || ft_strncmp((char *)(*path_list)->content, "..", 3) == 0) + break ; + if (!append_path(&resolved_path, (char *)(*path_list)->content)) + return (NULL); + consume_head(path_list); + } + return (resolved_path); + } + cwd = getcwd(NULL, 0); + if (!cwd) + return (NULL); + resolved_path = split_path_to_list(cwd); + free(cwd); + return (resolved_path); +} + +static void consume_head(t_list **path_list) +{ + t_list *temp; + + temp = *path_list; + *path_list = (*path_list)->next; + ft_lstdelone(temp, free); +} + +static t_list *split_path_to_list(const char *path) +{ + char **segments; + t_list *list; + int i; + + if (!path) + return (NULL); + segments = ft_split(path, '/'); + if (!segments) + return (NULL); + list = NULL; + i = 0; + while (segments[i]) + { + ft_lstadd_back(&list, ft_lstnew(segments[i])); + i++; + } + free(segments); + return (list); +} + +static char *list_to_path(t_list *resolved_path) +{ + t_list *current; + char *result; + char *temp; + + if (!resolved_path) + return (ft_strdup("/")); + result = ft_strdup("/"); + current = resolved_path; + while (current) + { + temp = ft_strjoin(result, (char *)current->content); + if (result) + free(result); + if (!temp) + return (NULL); + result = temp; + if (current->next) + { + temp = ft_strjoin(result, "/"); + free(result); + result = temp; + } + current = current->next; + } + return (result); +} diff --git a/src/component/shell_table/build_shell_table.c b/src/component/shell_table/build_shell_table.c new file mode 100644 index 0000000..2f4e1c8 --- /dev/null +++ b/src/component/shell_table/build_shell_table.c @@ -0,0 +1,99 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* build_shell_table.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/26 16:15:23 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "shell_table.h" + +static t_shell_table *st_create(size_t size); +static int insert_env_entries(char *const envp[], + t_shell_table *table); +static int parse_env_entry(char *env, char **key, char **value); + +t_shell_table *build_shell_table(char *const envp[]) +{ + t_shell_table *table; + + if (!envp) + return (NULL); + table = st_create(SHELL_TABLE_INIT_SIZE); + if (!table) + return (NULL); + if (insert_env_entries(envp, table) == ERROR) + return (NULL); + return (table); +} + +static int insert_env_entries(char *const envp[], t_shell_table *table) +{ + size_t i; + char *key_tmp; + char *value_tmp; + + i = 0; + while (envp[i]) + { + if (parse_env_entry(envp[i], &key_tmp, &value_tmp) == ERROR) + return (ERROR); + if (!st_insert(table, key_tmp, value_tmp, true)) + { + free(key_tmp); + free(value_tmp); + st_destroy(table); + return (ERROR); + } + free(key_tmp); + free(value_tmp); + i++; + } + return (SUCCESS); +} + +static int parse_env_entry(char *env, char **key, char **value) +{ + char *eq_ptr; + size_t key_len; + + eq_ptr = ft_strchr(env, '='); + if (!eq_ptr) + return (ERROR); + key_len = eq_ptr - env; + *key = (char *)malloc(key_len + 1); + if (!*key) + return (ERROR); + ft_strlcpy(*key, env, key_len + 1); + *value = ft_strdup(eq_ptr + 1); + if (!*value) + { + free(*key); + return (ERROR); + } + return (SUCCESS); +} + +static t_shell_table *st_create(size_t size) +{ + t_shell_table *table; + + if (size == 0) + return (NULL); + table = (t_shell_table *)ft_calloc(1, sizeof(t_shell_table)); + if (!table) + return (NULL); + table->buckets = (t_shell_node **)ft_calloc(size, sizeof(t_shell_node *)); + if (!table->buckets) + { + free(table); + return (NULL); + } + table->size = size; + table->n_nodes = 0; + return (table); +} diff --git a/src/component/shell_table/export_envp.c b/src/component/shell_table/export_envp.c new file mode 100644 index 0000000..b79fc48 --- /dev/null +++ b/src/component/shell_table/export_envp.c @@ -0,0 +1,115 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* export_envp.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/11 18:31:48 by kjikuhar #+# #+# */ +/* Updated: 2026/04/11 18:31:49 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "shell_table.h" + +static int count_exported_nodes(t_shell_table *shell_table); +static int export_envp_entries(char **envp, t_shell_table *shell_table); +static char *export_envp_str(t_shell_node *node); +static int throw_export_envp(char **envp, size_t count); + +char **export_envp(t_shell_table *shell_table) +{ + char **envp; + int count; + + if (!shell_table) + return (NULL); + count = count_exported_nodes(shell_table); + envp = (char **)ft_calloc(count + 1, sizeof(char *)); + if (!envp) + return (NULL); + if (export_envp_entries(envp, shell_table) == ERROR) + return (NULL); + return (envp); +} + +static int count_exported_nodes(t_shell_table *shell_table) +{ + size_t index; + t_shell_node *node; + int count; + + count = 0; + index = 0; + while (index < shell_table->size) + { + node = shell_table->buckets[index]; + while (node) + { + if (node->exported) + count++; + node = node->next; + } + index++; + } + return (count); +} + +static int export_envp_entries(char **envp, t_shell_table *shell_table) +{ + size_t index; + size_t env_index; + t_shell_node *node; + + env_index = 0; + index = 0; + while (index < shell_table->size) + { + node = shell_table->buckets[index]; + while (node) + { + if (node->exported) + { + envp[env_index] = export_envp_str(node); + if (!envp[env_index]) + return (throw_export_envp(envp, env_index)); + env_index++; + } + node = node->next; + } + index++; + } + return (SUCCESS); +} + +static char *export_envp_str(t_shell_node *node) +{ + char *result; + size_t key_len; + size_t value_len; + + key_len = ft_strlen(node->key); + value_len = ft_strlen(node->value); + result = (char *)malloc(key_len + value_len + 2); + if (!result) + return (NULL); + ft_strlcpy(result, node->key, key_len + 1); + result[key_len] = '='; + ft_strlcpy(result + key_len + 1, node->value, value_len + 1); + return (result); +} + +static int throw_export_envp(char **envp, size_t count) +{ + size_t i; + + i = 0; + while (i < count) + { + free(envp[i]); + envp[i] = NULL; + i++; + } + free(envp); + return (ERROR); +} diff --git a/src/component/shell_table/shell_delete.c b/src/component/shell_table/shell_delete.c new file mode 100644 index 0000000..cf135bb --- /dev/null +++ b/src/component/shell_table/shell_delete.c @@ -0,0 +1,51 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* shell_delete.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/27 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "shell_table.h" + +static void free_node(t_shell_node *node) +{ + if (!node) + return ; + free(node->key); + free(node->value); + free(node); +} + +int st_delete(t_shell_table *table, const char *key) +{ + size_t index; + t_shell_node *node; + t_shell_node *prev; + + if (!table || !key) + return (0); + index = st_hash(key, table->size); + node = table->buckets[index]; + prev = NULL; + while (node) + { + if (ft_strncmp(node->key, key, ft_strlen(key) + 1) == 0) + { + if (prev) + prev->next = node->next; + else + table->buckets[index] = node->next; + free_node(node); + table->n_nodes--; + return (1); + } + prev = node; + node = node->next; + } + return (0); +} diff --git a/src/component/shell_table/shell_destroy.c b/src/component/shell_table/shell_destroy.c new file mode 100644 index 0000000..4472c38 --- /dev/null +++ b/src/component/shell_table/shell_destroy.c @@ -0,0 +1,46 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* shell_destroy.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/27 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "shell_table.h" + +static void free_chain(t_shell_node *node) +{ + t_shell_node *next; + + while (node) + { + next = node->next; + free(node->key); + free(node->value); + free(node); + node = next; + } +} + +void st_destroy(t_shell_table *table) +{ + size_t i; + + if (!table) + return ; + if (table->buckets) + { + i = 0; + while (i < table->size) + { + free_chain(table->buckets[i]); + i++; + } + free(table->buckets); + } + free(table); +} diff --git a/src/component/shell_table/shell_hash.c b/src/component/shell_table/shell_hash.c new file mode 100644 index 0000000..201ba1a --- /dev/null +++ b/src/component/shell_table/shell_hash.c @@ -0,0 +1,31 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* shell_hash.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/27 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "shell_table.h" + +size_t st_hash(const char *key, size_t table_size) +{ + const size_t inital_hash = 5381; + size_t hash_num; + size_t i; + + if (!key || table_size == 0) + return (0); + hash_num = inital_hash; + i = 0; + while (key[i]) + { + hash_num = ((hash_num << 5) + hash_num) + (unsigned char)key[i]; + i++; + } + return (hash_num % table_size); +} diff --git a/src/component/shell_table/shell_insert.c b/src/component/shell_table/shell_insert.c new file mode 100644 index 0000000..e06eb06 --- /dev/null +++ b/src/component/shell_table/shell_insert.c @@ -0,0 +1,125 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* shell_insert.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/27 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/01/16 14:00:43 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "shell_table.h" + +static bool is_valid_key(const char *key) +{ + size_t i; + + if (!key || !*key) + return (false); + if (!ft_isalpha(key[0]) && key[0] != '_') + return (false); + i = 1; + while (key[i]) + { + if (!ft_isalnum(key[i]) && key[i] != '_') + return (false); + i++; + } + return (true); +} + +static t_shell_node *create_node(const char *key, const char *value, + bool exported) +{ + t_shell_node *node; + + node = (t_shell_node *)ft_calloc(1, sizeof(t_shell_node)); + if (!node) + return (NULL); + node->key = ft_strdup(key); + if (!node->key) + { + free(node); + return (NULL); + } + if (value) + { + node->value = ft_strdup(value); + if (!node->value) + { + free(node->key); + free(node); + return (NULL); + } + } + node->exported = exported; + return (node); +} + +static int update_existing_node(t_shell_node *node, const char *value) +{ + char *new_value; + + new_value = ft_strdup(value); + if (!new_value) + return (0); + free(node->value); + node->value = new_value; + return (1); +} + +int st_insert(t_shell_table *table, const char *key, const char *value, + bool exported) +{ + size_t index; + t_shell_node *node; + t_shell_node *new_node; + + if (!table || !key || !value || !is_valid_key(key)) + return (0); + index = st_hash(key, table->size); + node = table->buckets[index]; + while (node) + { + if (ft_strncmp(node->key, key, ft_strlen(key) + 1) == 0) + return (update_existing_node(node, value)); + node = node->next; + } + new_node = create_node(key, value, exported); + if (!new_node) + return (0); + new_node->next = table->buckets[index]; + table->buckets[index] = new_node; + table->n_nodes++; + return (1); +} + +int st_set_exported(t_shell_table *table, const char *key) +{ + size_t index; + t_shell_node *node; + t_shell_node *new_node; + + if (!table || !key || !is_valid_key(key)) + return (0); + index = st_hash(key, table->size); + node = table->buckets[index]; + while (node) + { + if (ft_strncmp(node->key, key, ft_strlen(key) + 1) == 0) + { + node->exported = true; + return (1); + } + node = node->next; + } + new_node = create_node(key, NULL, true); + if (!new_node) + return (0); + new_node->next = table->buckets[index]; + table->buckets[index] = new_node; + table->n_nodes++; + return (1); +} diff --git a/src/component/shell_table/shell_print.c b/src/component/shell_table/shell_print.c new file mode 100644 index 0000000..a741d4b --- /dev/null +++ b/src/component/shell_table/shell_print.c @@ -0,0 +1,38 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* print_shell_table.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/06 16:11:42 by surayama #+# #+# */ +/* Updated: 2025/12/06 16:17:45 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "shell_table.h" + +void st_print_env(t_shell_table *table) +{ + size_t i; + t_shell_node *node; + + i = 0; + while (i < table->size) + { + node = table->buckets[i]; + while (node) + { + if (node->exported) + { + if (node->value) + printf("declare -x %s=\"%s\"\n", + node->key, node->value); + else + printf("declare -x %s\n", node->key); + } + node = node->next; + } + i++; + } +} diff --git a/src/component/shell_table/shell_search.c b/src/component/shell_table/shell_search.c new file mode 100644 index 0000000..0db5883 --- /dev/null +++ b/src/component/shell_table/shell_search.c @@ -0,0 +1,31 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* shell_search.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/27 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "shell_table.h" + +char *st_search(t_shell_table *table, const char *key) +{ + size_t index; + t_shell_node *node; + + if (!table || !key) + return (NULL); + index = st_hash(key, table->size); + node = table->buckets[index]; + while (node) + { + if (ft_strncmp(node->key, key, ft_strlen(key) + 1) == 0) + return (node->value); + node = node->next; + } + return (NULL); +} diff --git a/src/execute/command/exec_builtin_cmd.c b/src/execute/command/exec_builtin_cmd.c new file mode 100644 index 0000000..74f4a31 --- /dev/null +++ b/src/execute/command/exec_builtin_cmd.c @@ -0,0 +1,50 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exec_builtin_cmd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/11 18:30:04 by kjikuhar #+# #+# */ +/* Updated: 2026/04/11 18:30:05 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "exec_builtin_cmd_private.h" + +static t_builtin_fn find_builtin(const char *name) +{ + static const t_builtin_entry g[] = { + {"echo", echo}, + {"pwd", pwd}, + {"cd", cd}, + {"export", export}, + {"unset", unset}, + {"exit", builtin_exit}, + {NULL, NULL} + }; + int i; + + i = 0; + while (g[i].name) + { + if (ft_strncmp(name, g[i].name, ft_strlen(g[i].name) + 1) == 0) + return (g[i].func); + i++; + } + return (NULL); +} + +int exec_builtin_cmd(t_ast *node, t_shell_table *shell_table) +{ + t_builtin_fn fn; + char *name; + + if (!node->cmd->argv || !node->cmd->argv->content) + return (-1); + name = (char *)node->cmd->argv->content; + fn = find_builtin(name); + if (!fn) + return (-1); + return (fn(node->cmd->argv, shell_table)); +} diff --git a/src/execute/command/exec_builtin_cmd_private.h b/src/execute/command/exec_builtin_cmd_private.h new file mode 100644 index 0000000..8ac80a1 --- /dev/null +++ b/src/execute/command/exec_builtin_cmd_private.h @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exec_builtin_cmd_private.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/11 18:30:58 by kjikuhar #+# #+# */ +/* Updated: 2026/04/11 18:30:59 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef EXEC_BUILTIN_CMD_PRIVATE_H +# define EXEC_BUILTIN_CMD_PRIVATE_H + +# include "builtin.h" +# include "execute.h" + +typedef int (*t_builtin_fn)(t_list *, t_shell_table *); + +typedef struct s_builtin_entry +{ + const char *name; + t_builtin_fn func; +} t_builtin_entry; + +#endif diff --git a/src/execute/command/exec_external_cmd.c b/src/execute/command/exec_external_cmd.c new file mode 100644 index 0000000..0ecd222 --- /dev/null +++ b/src/execute/command/exec_external_cmd.c @@ -0,0 +1,72 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exec_external_cmd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/16 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/16 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "execute.h" +#include + +static int cmd_path_error(char **argv) +{ + struct stat sb; + + ft_putstr_fd("jikussh: ", STDERR_FILENO); + if (stat(argv[0], &sb) == 0 && S_ISDIR(sb.st_mode)) + { + ft_putstr_fd(argv[0], STDERR_FILENO); + ft_putstr_fd(": Is a directory\n", STDERR_FILENO); + } + else + perror(argv[0]); + ft_free_array((void **)argv); + return (126); +} + +static int cmd_not_found(char **argv) +{ + if (ft_strchr(argv[0], '/')) + { + if (access(argv[0], F_OK) == 0) + return (cmd_path_error(argv)); + ft_putstr_fd("jikussh: ", STDERR_FILENO); + perror(argv[0]); + ft_free_array((void **)argv); + return (127); + } + ft_putstr_fd("jikussh: ", STDERR_FILENO); + ft_putstr_fd(argv[0], STDERR_FILENO); + ft_putstr_fd(": command not found\n", STDERR_FILENO); + ft_free_array((void **)argv); + return (127); +} + +int exec_external_cmd(char **argv, t_shell_table *shell_table) +{ + char *cmd_path; + char **new_envp; + + cmd_path = find_exec_path(argv[0], shell_table); + if (!cmd_path) + return (cmd_not_found(argv)); + new_envp = export_envp(shell_table); + if (!new_envp) + { + free(cmd_path); + ft_free_array((void **)argv); + return (1); + } + execve(cmd_path, argv, new_envp); + perror(cmd_path); + free(cmd_path); + st_destroy(shell_table); + ft_free_array((void **)new_envp); + ft_free_array((void **)argv); + return (127); +} diff --git a/src/execute/command/find_exec_path.c b/src/execute/command/find_exec_path.c new file mode 100644 index 0000000..bfe86e5 --- /dev/null +++ b/src/execute/command/find_exec_path.c @@ -0,0 +1,65 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* find_exec_path.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/16 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/16 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "execute.h" +#include "path.h" +#include + +static int is_directory(const char *path) +{ + struct stat sb; + + if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) + return (1); + return (0); +} + +static char *find_in_path(const char *cmd, t_shell_table *shell_table) +{ + char **paths; + char *path_env; + char *full_path; + int i; + + path_env = st_search(shell_table, "PATH"); + if (!path_env) + return (NULL); + paths = ft_split(path_env, ':'); + if (!paths) + return (NULL); + i = 0; + while (paths[i]) + { + full_path = join_path(paths[i], cmd); + if (!full_path) + return (ft_free_array((void **)paths), NULL); + if (access(full_path, X_OK) == 0) + return (ft_free_array((void **)paths), full_path); + free(full_path); + i++; + } + ft_free_array((void **)paths); + return (NULL); +} + +char *find_exec_path(const char *cmd, t_shell_table *shell_table) +{ + if (!cmd || !*cmd) + return (NULL); + if (ft_strchr(cmd, '/')) + { + if (access(cmd, X_OK) == 0 && !is_directory(cmd)) + return (ft_strdup(cmd)); + return (NULL); + } + return (find_in_path(cmd, shell_table)); +} diff --git a/src/execute/command/list_to_argv.c b/src/execute/command/list_to_argv.c new file mode 100644 index 0000000..c0e08dc --- /dev/null +++ b/src/execute/command/list_to_argv.c @@ -0,0 +1,39 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* list_to_argv.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 17:41:14 by kjikuhar #+# #+# */ +/* Updated: 2026/03/03 13:49:37 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "libft.h" + +char **list_to_argv(t_list *lst) +{ + size_t count; + size_t i; + char **out; + + count = ft_lstsize(lst); + out = malloc(sizeof(char *) * (count + 1)); + if (!out) + return (NULL); + i = 0; + while (lst) + { + out[i] = ft_strdup((const char *)lst->content); + if (!out[i]) + { + ft_free_array((void **)out); + return (NULL); + } + i++; + lst = lst->next; + } + out[i] = NULL; + return (out); +} diff --git a/src/execute/exec_ast.c b/src/execute/exec_ast.c new file mode 100644 index 0000000..edcc020 --- /dev/null +++ b/src/execute/exec_ast.c @@ -0,0 +1,64 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exec_ast.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/31 21:10:34 by kjikuhar #+# #+# */ +/* Updated: 2026/01/08 10:07:05 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "execute.h" +#include "signal_handler.h" + +static int exec_logical(t_ast *node, t_shell_table *shell_table) +{ + int status; + + status = exec_ast(node->left, shell_table); + if (node->type == AND && status == 0) + return (exec_ast(node->right, shell_table)); + if (node->type == OR && status != 0) + return (exec_ast(node->right, shell_table)); + return (status); +} + +static int exec_subshell(t_ast *node, t_shell_table *shell_table) +{ + pid_t pid; + int wstatus; + + pid = fork(); + if (pid < 0) + return (perror("fork"), 1); + if (pid == 0) + { + set_signal_default(); + exit(exec_ast(node->right, shell_table)); + } + set_signal_ignore(); + waitpid(pid, &wstatus, 0); + set_signal_interactive(); + if (ft_wifexited(wstatus)) + return (ft_wexitstatus(wstatus)); + if (WTERMSIG(wstatus) == SIGQUIT) + ft_putendl_fd("Quit: 3", STDERR_FILENO); + return (128 + WTERMSIG(wstatus)); +} + +int exec_ast(t_ast *node, t_shell_table *shell_table) +{ + if (!node) + return (EXIT_FAILURE); + if (node->type == PIPE) + return (exec_pipe(node, shell_table)); + if (node->type == CMD) + return (exec_cmd(node, shell_table)); + if (node->type == AND || node->type == OR) + return (exec_logical(node, shell_table)); + if (node->type == SUBSHELL) + return (exec_subshell(node, shell_table)); + return (EXIT_FAILURE); +} diff --git a/src/execute/exec_cmd.c b/src/execute/exec_cmd.c new file mode 100644 index 0000000..ee66f8f --- /dev/null +++ b/src/execute/exec_cmd.c @@ -0,0 +1,101 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exec_cmd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 17:18:25 by kjikuhar #+# #+# */ +/* Updated: 2026/04/16 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "execute.h" +#include "expand.h" +#include "signal_handler.h" + +static int wait_for_child(pid_t pid) +{ + int wstatus; + + set_signal_ignore(); + waitpid(pid, &wstatus, 0); + set_signal_interactive(); + if (ft_wifexited(wstatus)) + return (ft_wexitstatus(wstatus)); + if (WTERMSIG(wstatus) == SIGQUIT) + ft_putendl_fd("Quit: 3", STDERR_FILENO); + return (128 + WTERMSIG(wstatus)); +} + +static int fork_and_exec(t_ast *node, t_shell_table *shell_table) +{ + pid_t pid; + char **argv; + + pid = fork(); + if (pid < 0) + return (perror("fork"), 1); + if (pid == 0) + { + set_signal_default(); + if (node->cmd->redirs) + if (exec_redirs(node->cmd->redirs) != 0) + exit(1); + argv = list_to_argv(node->cmd->argv); + if (!argv || !argv[0]) + exit(127); + exit(exec_external_cmd(argv, shell_table)); + } + return (wait_for_child(pid)); +} + +static int exec_builtin_with_redir(t_ast *node, t_shell_table *st) +{ + int saved_in; + int saved_out; + int status; + + saved_in = dup(STDIN_FILENO); + saved_out = dup(STDOUT_FILENO); + if (node->cmd->redirs + && exec_redirs(node->cmd->redirs) != 0) + status = 1; + else + status = exec_builtin_cmd(node, st); + dup2(saved_in, STDIN_FILENO); + dup2(saved_out, STDOUT_FILENO); + close(saved_in); + close(saved_out); + return (status); +} + +static bool is_builtin(t_ast *node) +{ + static const char *builtins[] = {"echo", "pwd", "cd", + "export", "unset", "exit", NULL}; + int i; + char *name; + + if (!node->cmd->argv || !node->cmd->argv->content) + return (false); + name = (char *)node->cmd->argv->content; + i = 0; + while (builtins[i]) + { + if (ft_strncmp(name, builtins[i], + ft_strlen(builtins[i]) + 1) == 0) + return (true); + i++; + } + return (false); +} + +int exec_cmd(t_ast *node, t_shell_table *shell_table) +{ + if (expand_cmd(node->cmd, shell_table) == ERROR) + return (-1); + if (is_builtin(node)) + return (exec_builtin_with_redir(node, shell_table)); + return (fork_and_exec(node, shell_table)); +} diff --git a/src/execute/exec_pipe.c b/src/execute/exec_pipe.c new file mode 100644 index 0000000..d041228 --- /dev/null +++ b/src/execute/exec_pipe.c @@ -0,0 +1,86 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exec_pipe.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/01 05:46:56 by kjikuhar #+# #+# */ +/* Updated: 2026/01/08 10:19:25 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "execute.h" +#include "signal_handler.h" + +static int exec_pipe_left(t_ast *node, t_shell_table *shell_table, \ + pid_t *left, int fd[2]) +{ + *left = fork(); + if (*left == 0) + exec_left_child(node, shell_table, fd); + else if (*left < 0) + { + close(fd[0]); + close(fd[1]); + return (1); + } + return (0); +} + +static int exec_pipe_right(t_ast *node, t_shell_table *shell_table, \ + pid_t *right, int fd[2]) +{ + *right = fork(); + if (*right == 0) + exec_right_child(node, shell_table, fd); + else if (*right < 0) + { + close(fd[0]); + close(fd[1]); + return (1); + } + return (0); +} + +static int get_exit_status(int wstatus) +{ + if (ft_wifexited(wstatus)) + return (ft_wexitstatus(wstatus)); + if (WTERMSIG(wstatus) == SIGQUIT) + ft_putendl_fd("Quit: 3", STDERR_FILENO); + return (128 + WTERMSIG(wstatus)); +} + +static int wait_pipe_children(pid_t left, pid_t right) +{ + int wstatus_left; + int wstatus_right; + + set_signal_ignore(); + waitpid(left, &wstatus_left, 0); + waitpid(right, &wstatus_right, 0); + set_signal_interactive(); + return (get_exit_status(wstatus_right)); +} + +int exec_pipe(t_ast *node, t_shell_table *shell_table) +{ + int fd[2]; + pid_t left; + pid_t right; + + if (pipe(fd) == -1) + return (1); + if (exec_pipe_left(node, shell_table, &left, fd) != 0) + return (1); + if (exec_pipe_right(node, shell_table, &right, fd) != 0) + { + if (left > 0) + waitpid(left, NULL, 0); + return (1); + } + close(fd[0]); + close(fd[1]); + return (wait_pipe_children(left, right)); +} diff --git a/src/execute/pipe/exec_pipe_child.c b/src/execute/pipe/exec_pipe_child.c new file mode 100644 index 0000000..d359454 --- /dev/null +++ b/src/execute/pipe/exec_pipe_child.c @@ -0,0 +1,62 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exec_pipe_child.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 23:08:24 by kjikuhar #+# #+# */ +/* Updated: 2026/04/11 00:00:00 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "execute.h" +#include "signal_handler.h" + +void exec_left_child(t_ast *node, t_shell_table *shell_table, int fd[2]) +{ + int status; + t_ast *left_node; + t_ast *right_node; + + if (dup2(fd[1], STDOUT_FILENO) == -1) + { + close(fd[0]); + close(fd[1]); + exit(1); + } + close(fd[0]); + close(fd[1]); + set_signal_default(); + left_node = node->left; + right_node = node->right; + free(node); + status = exec_ast(left_node, shell_table); + free_ast(left_node); + free_ast(right_node); + exit(status); +} + +void exec_right_child(t_ast *node, t_shell_table *shell_table, int fd[2]) +{ + int status; + t_ast *left_node; + t_ast *right_node; + + if (dup2(fd[0], STDIN_FILENO) == -1) + { + close(fd[0]); + close(fd[1]); + exit(1); + } + close(fd[1]); + close(fd[0]); + set_signal_default(); + left_node = node->left; + right_node = node->right; + free(node); + status = exec_ast(right_node, shell_table); + free_ast(left_node); + free_ast(right_node); + exit(status); +} diff --git a/src/execute/redirects/exec_redirs.c b/src/execute/redirects/exec_redirs.c new file mode 100644 index 0000000..d71261a --- /dev/null +++ b/src/execute/redirects/exec_redirs.c @@ -0,0 +1,109 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exec_redirs.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 17:41:09 by kjikuhar #+# #+# */ +/* Updated: 2025/11/27 16:45:26 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "execute.h" + +static int exec_redir_in(const char *filename) +{ + int fd; + + fd = open(filename, O_RDONLY); + if (fd == -1) + { + perror(filename); + return (1); + } + if (dup2(fd, STDIN_FILENO) == -1) + { + perror(filename); + close(fd); + return (1); + } + close(fd); + return (0); +} + +static int exec_redir_out_trunc(const char *filename) +{ + int fd; + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) + { + perror(filename); + return (1); + } + if (dup2(fd, STDOUT_FILENO) == -1) + { + perror(filename); + close(fd); + return (1); + } + close(fd); + return (0); +} + +static int exec_redir_out_append(const char *filename) +{ + int fd; + + fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644); + if (fd == -1) + { + perror(filename); + return (1); + } + if (dup2(fd, STDOUT_FILENO) == -1) + { + perror(filename); + close(fd); + return (1); + } + close(fd); + return (0); +} + +static int exec_one_redir(t_redir *redir) +{ + if (redir->kind == R_IN) + { + if (exec_redir_in(redir->filename) != 0) + return (ERROR); + } + else if (redir->kind == R_OUT_TRUNC) + { + if (exec_redir_out_trunc(redir->filename) != 0) + return (ERROR); + } + else if (redir->kind == R_OUT_APPEND) + { + if (exec_redir_out_append(redir->filename) != 0) + return (ERROR); + } + return (SUCCESS); +} + +int exec_redirs(t_list *redirs) +{ + t_list *current; + t_redir *redir; + + current = redirs; + while (current) + { + redir = (t_redir *)current->content; + if (exec_one_redir(redir) != 0) + return (1); + current = current->next; + } + return (0); +} diff --git a/src/execute/utils/ft_wexitstatus.c b/src/execute/utils/ft_wexitstatus.c new file mode 100644 index 0000000..74bc078 --- /dev/null +++ b/src/execute/utils/ft_wexitstatus.c @@ -0,0 +1,16 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_wexitstatus.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 21:45:08 by kjikuhar #+# #+# */ +/* Updated: 2025/11/14 21:45:33 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +int ft_wexitstatus(int exit_status) +{ + return ((exit_status & 0xff00) >> 8); +} diff --git a/src/execute/utils/ft_wifexited.c b/src/execute/utils/ft_wifexited.c new file mode 100644 index 0000000..d81d994 --- /dev/null +++ b/src/execute/utils/ft_wifexited.c @@ -0,0 +1,16 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_wifexited.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 21:45:42 by kjikuhar #+# #+# */ +/* Updated: 2025/11/14 21:45:55 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +int ft_wifexited(int exit_status) +{ + return ((exit_status & 0x7f) == 0); +} diff --git a/src/expand/expand.c b/src/expand/expand.c new file mode 100644 index 0000000..1d0988b --- /dev/null +++ b/src/expand/expand.c @@ -0,0 +1,34 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* expand.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/03/03 17:16:33 by surayama #+# #+# */ +/* Updated: 2026/04/16 22:20:39 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "expand.h" + +t_list *expand(t_list *tokens, t_shell_table *shell_table, \ + int last_exit_status) +{ + tokens = expand_tilde(tokens, shell_table); + if (!tokens) + return (NULL); + tokens = expand_question(tokens, last_exit_status); + if (!tokens) + return (NULL); + tokens = expand_parameter(tokens, shell_table); + if (!tokens) + return (NULL); + tokens = expand_wildcard(tokens); + if (!tokens) + return (NULL); + tokens = expand_remove_quotes(tokens); + if (!tokens) + return (NULL); + return (tokens); +} diff --git a/src/expand/expand_cmd.c b/src/expand/expand_cmd.c new file mode 100644 index 0000000..9a674be --- /dev/null +++ b/src/expand/expand_cmd.c @@ -0,0 +1,67 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* expand_cmd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/18 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/18 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "expand.h" +#include "parse.h" + +static int expand_redir_filename(t_redir *redir, t_shell_table *st) +{ + t_list *tmp; + char *dup; + + dup = ft_strdup(redir->filename); + if (!dup) + return (ERROR); + tmp = ft_lstnew(dup); + if (!tmp) + return (free(dup), ERROR); + tmp = expand(tmp, st, st->last_status); + if (!tmp) + return (ERROR); + free(redir->filename); + redir->filename = ft_strdup(tmp->content); + ft_lstclear(&tmp, free); + if (!redir->filename) + return (ERROR); + return (SUCCESS); +} + +static int expand_redirs(t_list *redirs, t_shell_table *st) +{ + t_list *current; + + current = redirs; + while (current) + { + if (expand_redir_filename(current->content, st) == ERROR) + return (ERROR); + current = current->next; + } + return (SUCCESS); +} + +int expand_cmd(t_cmd *cmd, t_shell_table *shell_table) +{ + if (cmd->argv) + { + cmd->argv = expand(cmd->argv, shell_table, + shell_table->last_status); + if (!cmd->argv) + return (ERROR); + } + if (cmd->redirs) + { + if (expand_redirs(cmd->redirs, shell_table) == ERROR) + return (ERROR); + } + return (SUCCESS); +} diff --git a/src/expand/expand_parameter/expand_parameter.c b/src/expand/expand_parameter/expand_parameter.c new file mode 100644 index 0000000..cab4cb5 --- /dev/null +++ b/src/expand/expand_parameter/expand_parameter.c @@ -0,0 +1,106 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* variable_expand.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/01/22 21:55:17 by surayama #+# #+# */ +/* Updated: 2026/02/05 17:58:51 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "expand.h" +#include "expand_parameter_internal.h" + +static t_list *expand_token(char *token, t_shell_table *shell_table); +static t_list *remove_current_token(t_list *tokens, t_list *prev_token, + t_list *current_token, t_list *next_token); +static t_list *insert_expanded_token(t_list **tokens, t_list *prev_token, + t_list *current_token, t_list *expanded_token); + +t_list *expand_parameter(t_list *tokens, t_shell_table *shell_table) +{ + t_list *prev_token; + t_list *current_token; + t_list *next_token; + t_list *expanded_token; + + current_token = tokens; + prev_token = NULL; + while (current_token && current_token->content) + { + next_token = current_token->next; + expanded_token = expand_token(current_token->content, shell_table); + if (expanded_token) + prev_token = insert_expanded_token(&tokens, prev_token, + current_token, expanded_token); + else + tokens = remove_current_token(tokens, prev_token, current_token, + next_token); + current_token = next_token; + } + return (tokens); +} + +static void initialize(t_expand_store *store, t_expand_state *state, + char **current, char *token) +{ + store->tokens = NULL; + store->buffer = NULL; + store->skip_count = 0; + *current = token; + *state = EXPAND_IN_NORMAL; +} + +static t_list *expand_token(char *token, t_shell_table *shell_table) +{ + t_expand_store store; + t_expand_state state; + char *current; + + initialize(&store, &state, ¤t, token); + while (true) + { + if (state == EXPAND_IN_NORMAL) + in_normal_expand(shell_table, &store, &state, ¤t); + else if (state == EXPAND_IN_DOUBLE_QUOTE) + in_double_quote_expand(shell_table, &store, &state, ¤t); + else if (state == EXPAND_IN_SINGLE_QUOTE) + in_single_quote_expand(shell_table, &store, &state, ¤t); + else if (state == EXPAND_ON_SUCCESS) + return (on_success_expand(&store, token)); + else if (state == EXPAND_ON_ERROR) + return (on_error_expand(&store, token)); + current++; + if (store.skip_count > 0) + { + current += store.skip_count; + store.skip_count = 0; + } + } +} + +static t_list *insert_expanded_token(t_list **tokens, t_list *prev_token, + t_list *current_token, t_list *expanded_token) +{ + t_list *replaced_prev; + + replaced_prev = ft_lstlast(expanded_token); + ft_lstreplace(prev_token, current_token, expanded_token); + if (!prev_token) + *tokens = expanded_token; + return (replaced_prev); +} + +static t_list *remove_current_token(t_list *tokens, t_list *prev_token, + t_list *current_token, t_list *next_token) +{ + if (prev_token) + prev_token->next = next_token; + else + tokens = next_token; + free(current_token->content); + free(current_token); + return (tokens); +} diff --git a/src/expand/expand_parameter/expand_parameter_internal.h b/src/expand/expand_parameter/expand_parameter_internal.h new file mode 100644 index 0000000..fb58925 --- /dev/null +++ b/src/expand/expand_parameter/expand_parameter_internal.h @@ -0,0 +1,52 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* variable_expand_internal.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 16:28:45 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef EXPAND_PARAMETER_INTERNAL_H +# define EXPAND_PARAMETER_INTERNAL_H + +# include "libft.h" +# include "shell_table.h" + +typedef enum e_expand_state +{ + EXPAND_IN_NORMAL, + EXPAND_IN_DOUBLE_QUOTE, + EXPAND_IN_SINGLE_QUOTE, + EXPAND_ON_ERROR, + EXPAND_ON_SUCCESS, +} t_expand_state; + +typedef struct s_expand_store +{ + t_list *tokens; + t_list *buffer; + size_t skip_count; +} t_expand_store; + +// store +int push_token_expand(t_expand_store *store, + t_shell_table *shell_table); +void free_store_expand(t_expand_store *store); +int add_buffer_expand(t_expand_store *store, char c); +size_t get_key_length(const char *from); + +// state handler +void in_normal_expand(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current); +void in_double_quote_expand(t_shell_table *shell_table, + t_expand_store *store, t_expand_state *state, char **current); +void in_single_quote_expand(t_shell_table *shell_table, + t_expand_store *store, t_expand_state *state, char **current); +t_list *on_success_expand(t_expand_store *store, char *token); +t_list *on_error_expand(t_expand_store *store, char *token); + +#endif diff --git a/src/expand/expand_parameter/state/in_double_quote_expand.c b/src/expand/expand_parameter/state/in_double_quote_expand.c new file mode 100644 index 0000000..2f3d673 --- /dev/null +++ b/src/expand/expand_parameter/state/in_double_quote_expand.c @@ -0,0 +1,91 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* in_double_quote.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 22:14:06 by surayama #+# #+# */ +/* Updated: 2026/02/07 00:15:01 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../expand_parameter_internal.h" +#include "expand.h" + +static void by_dollar(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current); +static void by_double_quote_end(t_shell_table *shell_table, + t_expand_store *store, t_expand_state *state, char **current); +static void add_buffer_with_expanded(t_expand_store *store, + t_expand_state *state, char *expanded); + +void in_double_quote_expand(t_shell_table *shell_table, + t_expand_store *store, t_expand_state *state, char **current) +{ + if (**current == '\0') + *state = EXPAND_ON_ERROR; + else if (**current == '$') + by_dollar(shell_table, store, state, current); + else if (**current == '"') + by_double_quote_end(shell_table, store, state, current); + else if (add_buffer_expand(store, **current) == ERROR) + *state = EXPAND_ON_ERROR; +} + +static void by_dollar(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current) +{ + size_t key_length; + char *key; + char *expanded; + + key_length = get_key_length(*current + 1); + if (key_length == 0) + { + if (add_buffer_expand(store, **current) == ERROR) + *state = EXPAND_ON_ERROR; + return ; + } + key = ft_substr(*current, 1, key_length); + if (!key) + { + *state = EXPAND_ON_ERROR; + return ; + } + expanded = st_search(shell_table, key); + free(key); + add_buffer_with_expanded(store, state, expanded); + if (*state == EXPAND_ON_ERROR) + return ; + store->skip_count = key_length; +} + +static void by_double_quote_end(t_shell_table *shell_table, + t_expand_store *store, t_expand_state *state, char **current) +{ + (void)shell_table; + if (add_buffer_expand(store, **current) == ERROR) + *state = EXPAND_ON_ERROR; + else + *state = EXPAND_IN_NORMAL; +} + +static void add_buffer_with_expanded(t_expand_store *store, + t_expand_state *state, char *expanded) +{ + size_t i; + + if (!expanded) + return ; + i = 0; + while (expanded[i] != '\0') + { + if (add_buffer_expand(store, expanded[i]) == ERROR) + { + *state = EXPAND_ON_ERROR; + return ; + } + i++; + } +} diff --git a/src/expand/expand_parameter/state/in_normal_expand.c b/src/expand/expand_parameter/state/in_normal_expand.c new file mode 100644 index 0000000..e368ddb --- /dev/null +++ b/src/expand/expand_parameter/state/in_normal_expand.c @@ -0,0 +1,105 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* in_normal.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 21:49:36 by surayama #+# #+# */ +/* Updated: 2026/02/07 00:15:04 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../expand_parameter_internal.h" +#include "expand.h" + +static void by_last(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current); +static void by_quote(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current); +static void by_dollar(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current); +static void add_buffer_with_expanded(t_expand_store *store, + t_expand_state *state, char *expanded); + +void in_normal_expand(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current) +{ + if (**current == '\0') + by_last(shell_table, store, state, current); + else if (**current == '"' || **current == '\'') + by_quote(shell_table, store, state, current); + else if (**current == '$') + by_dollar(shell_table, store, state, current); + else if (add_buffer_expand(store, **current) == ERROR) + *state = EXPAND_ON_ERROR; +} + +static void by_last(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current) +{ + (void)current; + if (push_token_expand(store, shell_table) == ERROR) + *state = EXPAND_ON_ERROR; + else + *state = EXPAND_ON_SUCCESS; +} + +static void by_quote(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current) +{ + (void)shell_table; + if (add_buffer_expand(store, **current) == ERROR) + *state = EXPAND_ON_ERROR; + else if (**current == '"') + *state = EXPAND_IN_DOUBLE_QUOTE; + else + *state = EXPAND_IN_SINGLE_QUOTE; +} + +static void by_dollar(t_shell_table *shell_table, t_expand_store *store, + t_expand_state *state, char **current) +{ + size_t key_length; + char *key; + char *expanded; + + key_length = get_key_length(*current + 1); + if (key_length == 0) + { + if (add_buffer_expand(store, **current) == ERROR) + *state = EXPAND_ON_ERROR; + return ; + } + key = ft_substr(*current, 1, key_length); + if (!key) + { + *state = EXPAND_ON_ERROR; + return ; + } + expanded = st_search(shell_table, key); + free(key); + add_buffer_with_expanded(store, state, expanded); + if (*state == EXPAND_ON_ERROR) + return ; + store->skip_count = key_length; +} + +static void add_buffer_with_expanded(t_expand_store *store, + t_expand_state *state, char *expanded) +{ + size_t i; + + if (!expanded) + return ; + i = 0; + while (expanded[i] != '\0') + { + if (add_buffer_expand(store, expanded[i]) == ERROR) + { + *state = EXPAND_ON_ERROR; + return ; + } + i++; + } +} diff --git a/src/expand/expand_parameter/state/in_single_quote_expand.c b/src/expand/expand_parameter/state/in_single_quote_expand.c new file mode 100644 index 0000000..19bee93 --- /dev/null +++ b/src/expand/expand_parameter/state/in_single_quote_expand.c @@ -0,0 +1,38 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* in_single_quote.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 22:22:34 by surayama #+# #+# */ +/* Updated: 2026/02/07 00:46:06 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../expand_parameter_internal.h" +#include "expand.h" + +static void by_single_quote_end(t_shell_table *shell_table, + t_expand_store *store, t_expand_state *state, char **current); + +void in_single_quote_expand(t_shell_table *shell_table, + t_expand_store *store, t_expand_state *state, char **current) +{ + if (**current == '\0') + *state = EXPAND_ON_ERROR; + else if (**current == '\'') + by_single_quote_end(shell_table, store, state, current); + else if (add_buffer_expand(store, **current) == ERROR) + *state = EXPAND_ON_ERROR; +} + +static void by_single_quote_end(t_shell_table *shell_table, + t_expand_store *store, t_expand_state *state, char **current) +{ + (void)shell_table; + if (add_buffer_expand(store, **current) == ERROR) + *state = EXPAND_ON_ERROR; + else + *state = EXPAND_IN_NORMAL; +} diff --git a/src/expand/expand_parameter/state/on_error_expand.c b/src/expand/expand_parameter/state/on_error_expand.c new file mode 100644 index 0000000..46f893f --- /dev/null +++ b/src/expand/expand_parameter/state/on_error_expand.c @@ -0,0 +1,22 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* on_error.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 22:42:03 by surayama #+# #+# */ +/* Updated: 2026/02/07 00:15:10 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../expand_parameter_internal.h" +#include "expand.h" + +t_list *on_error_expand(t_expand_store *store, char *token) +{ + (void)token; + if (store) + free_store_expand(store); + return (NULL); +} diff --git a/src/expand/expand_parameter/state/on_success_expand.c b/src/expand/expand_parameter/state/on_success_expand.c new file mode 100644 index 0000000..08a65ad --- /dev/null +++ b/src/expand/expand_parameter/state/on_success_expand.c @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* on_success.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 22:38:01 by surayama #+# #+# */ +/* Updated: 2026/02/07 00:15:14 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../expand_parameter_internal.h" +#include "expand.h" + +t_list *on_success_expand(t_expand_store *store, char *token) +{ + t_list *expanded_tokens; + + (void)token; + if (!store) + return (NULL); + expanded_tokens = store->tokens; + store->tokens = NULL; + ft_lstclear(&(store->buffer), free); + return (expanded_tokens); +} diff --git a/src/expand/expand_parameter/store/add_buffer_expand.c b/src/expand/expand_parameter/store/add_buffer_expand.c new file mode 100644 index 0000000..9e831dc --- /dev/null +++ b/src/expand/expand_parameter/store/add_buffer_expand.c @@ -0,0 +1,35 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* add_buffer.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 21:10:42 by surayama #+# #+# */ +/* Updated: 2026/02/07 00:14:41 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../expand_parameter_internal.h" +#include "expand.h" + +int add_buffer_expand(t_expand_store *store, char c) +{ + char *char_ptr; + t_list *new_node; + + if (!store) + return (ERROR); + char_ptr = (char *)malloc(sizeof(char)); + if (!char_ptr) + return (ERROR); + *char_ptr = c; + new_node = ft_lstnew(char_ptr); + if (!new_node) + { + free(char_ptr); + return (ERROR); + } + ft_lstadd_back(&(store->buffer), new_node); + return (SUCCESS); +} diff --git a/src/expand/expand_parameter/store/free_store_expand.c b/src/expand/expand_parameter/store/free_store_expand.c new file mode 100644 index 0000000..2f1820d --- /dev/null +++ b/src/expand/expand_parameter/store/free_store_expand.c @@ -0,0 +1,22 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* free_store.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 22:44:15 by surayama #+# #+# */ +/* Updated: 2026/02/07 00:14:47 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../expand_parameter_internal.h" +#include "expand.h" + +void free_store_expand(t_expand_store *store) +{ + if (!store) + return ; + ft_lstclear(&(store->buffer), free); + ft_lstclear(&(store->tokens), free); +} diff --git a/src/expand/expand_parameter/store/get_key_length_expand.c b/src/expand/expand_parameter/store/get_key_length_expand.c new file mode 100644 index 0000000..0283788 --- /dev/null +++ b/src/expand/expand_parameter/store/get_key_length_expand.c @@ -0,0 +1,42 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* get_key_length.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 22:44:15 by surayama #+# #+# */ +/* Updated: 2026/02/14 00:08:47 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "expand.h" + +static bool is_special_char(const char c); + +size_t get_key_length(const char *from) +{ + size_t i; + + if (!from || from[0] == '\0') + return (0); + if (is_special_char(from[0]) || ft_isdigit(from[0])) + return (1); + i = 0; + while (from[i] != '\0') + { + if (ft_isalnum((unsigned char)from[i]) || from[i] == '_') + i++; + else + break ; + } + return (i); +} + +static bool is_special_char(const char c) +{ + if (c == '?' || c == '$' || c == '#' || c == '*' || c == '@' || c == '-' + || c == '!') + return (true); + return (false); +} diff --git a/src/expand/expand_parameter/store/push_token_expand.c b/src/expand/expand_parameter/store/push_token_expand.c new file mode 100644 index 0000000..72f1ad0 --- /dev/null +++ b/src/expand/expand_parameter/store/push_token_expand.c @@ -0,0 +1,68 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* push_token.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/01 21:12:08 by surayama #+# #+# */ +/* Updated: 2026/02/07 00:14:55 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../expand_parameter_internal.h" +#include "expand.h" + +static char *build_token_from_buffer(t_list *buffer) +{ + int size; + char *token; + int i; + t_list *current; + + size = ft_lstsize(buffer); + if (size == 0) + return (NULL); + token = (char *)malloc(sizeof(char) * (size + 1)); + if (!token) + return (NULL); + i = 0; + current = buffer; + while (current) + { + token[i] = *(char *)(current->content); + i++; + current = current->next; + } + token[i] = '\0'; + return (token); +} + +int push_token_expand(t_expand_store *store, t_shell_table *shell_table) +{ + char *token; + t_list *new_node; + + (void)shell_table; + if (!store) + return (ERROR); + if (!store->buffer) + return (SUCCESS); + token = build_token_from_buffer(store->buffer); + if (!token) + { + ft_lstclear(&(store->buffer), free); + store->buffer = NULL; + return (SUCCESS); + } + new_node = ft_lstnew((void *)token); + if (!new_node) + { + free(token); + return (ERROR); + } + ft_lstadd_back(&(store->tokens), new_node); + ft_lstclear(&(store->buffer), free); + store->buffer = NULL; + return (SUCCESS); +} diff --git a/src/expand/expand_question/expand_question.c b/src/expand/expand_question/expand_question.c new file mode 100644 index 0000000..91db1b8 --- /dev/null +++ b/src/expand/expand_question/expand_question.c @@ -0,0 +1,128 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* expand_question.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/16 22:21:07 by kjikuhar #+# #+# */ +/* Updated: 2026/04/16 22:37:34 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "expand.h" + +static bool has_question(const char *tok); +static size_t count_question_len(const char *tok, size_t slen); +static char *copy_and_advance(char *p, const char *status, + size_t slen, const char **tok); +static char *expand_question_token(const char *tok, + const char *status, size_t slen); + +static bool has_question(const char *tok) +{ + char quote; + + quote = 0; + while (*tok) + { + if (!quote && ft_strchr("'\"", *tok)) + quote = *tok; + else if (*tok == quote) + quote = 0; + else if (quote != '\'' && ft_strncmp(tok, "$?", 2) == 0) + return (true); + tok++; + } + return (false); +} + +static size_t count_question_len(const char *tok, size_t slen) +{ + size_t len; + char quote; + + len = 0; + quote = 0; + while (*tok) + { + if (!quote && ft_strchr("'\"", *tok)) + quote = *tok; + else if (*tok == quote) + quote = 0; + else if (quote != '\'' && ft_strncmp(tok, "$?", 2) == 0) + { + len += slen; + tok++; + } + else + len++; + tok++; + } + return (len); +} + +static char *copy_and_advance(char *p, const char *status, + size_t slen, const char **tok) +{ + ft_memcpy(p, status, slen); + (*tok)++; + return (p + slen); +} + +static char *expand_question_token(const char *tok, + const char *status, size_t slen) +{ + char *result; + char *p; + char quote; + + result = malloc(count_question_len(tok, slen) + 1); + if (!result) + return (NULL); + p = result; + quote = 0; + while (*tok) + { + if (!quote && ft_strchr("'\"", *tok)) + quote = *tok; + else if (*tok == quote) + quote = 0; + else if (quote != '\'' && ft_strncmp(tok, "$?", 2) == 0) + p = copy_and_advance(p, status, slen, &tok); + else + *p++ = *tok; + tok++; + } + *p = '\0'; + return (result); +} + +t_list *expand_question(t_list *tokens, int last_exit_status) +{ + char *status; + size_t slen; + t_list *current; + char *expanded; + + status = ft_itoa(last_exit_status); + if (!status) + return (NULL); + slen = ft_strlen(status); + current = tokens; + while (current) + { + if (current->content && has_question(current->content)) + { + expanded = expand_question_token(current->content, status, slen); + if (expanded) + { + free(current->content); + current->content = expanded; + } + } + current = current->next; + } + free(status); + return (tokens); +} diff --git a/src/expand/expand_remove_quotes/expand_remove_quotes.c b/src/expand/expand_remove_quotes/expand_remove_quotes.c new file mode 100644 index 0000000..16a22cc --- /dev/null +++ b/src/expand/expand_remove_quotes/expand_remove_quotes.c @@ -0,0 +1,87 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* remove_quotes.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/07 00:51:36 by surayama #+# #+# */ +/* Updated: 2026/04/16 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "expand.h" +#include + +static int count_unquoted_len(const char *str); +static char *remove_quotes_from_string(const char *str); + +t_list *expand_remove_quotes(t_list *tokens) +{ + t_list *current; + char *new_content; + char *old_content; + + if (!tokens) + return (NULL); + current = tokens; + while (current) + { + if (current->content) + { + new_content = remove_quotes_from_string((char *)current->content); + if (!new_content) + return (NULL); + old_content = current->content; + current->content = new_content; + free(old_content); + } + current = current->next; + } + return (tokens); +} + +static int count_unquoted_len(const char *str) +{ + int len; + char quote; + + len = 0; + quote = 0; + while (*str) + { + if (!quote && (*str == '\'' || *str == '"')) + quote = *str; + else if (*str == quote) + quote = 0; + else + len++; + str++; + } + return (len); +} + +static char *remove_quotes_from_string(const char *str) +{ + char *result; + char quote; + int j; + + result = malloc(sizeof(char) * (count_unquoted_len(str) + 1)); + if (!result) + return (NULL); + quote = 0; + j = 0; + while (*str) + { + if (!quote && (*str == '\'' || *str == '"')) + quote = *str; + else if (*str == quote) + quote = 0; + else + result[j++] = *str; + str++; + } + result[j] = '\0'; + return (result); +} diff --git a/src/expand/expand_tilde/expand_tilde.c b/src/expand/expand_tilde/expand_tilde.c new file mode 100644 index 0000000..627beff --- /dev/null +++ b/src/expand/expand_tilde/expand_tilde.c @@ -0,0 +1,59 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* expand_tilde.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/03/03 18:00:00 by surayama #+# #+# */ +/* Updated: 2026/03/03 18:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "expand.h" + +static bool is_tilde_prefix(const char *token); +static char *expand_tilde_token(const char *token, t_shell_table *table); + +t_list *expand_tilde(t_list *tokens, t_shell_table *shell_table) +{ + t_list *current; + char *expanded; + char *old; + + current = tokens; + while (current) + { + if (current->content && is_tilde_prefix(current->content)) + { + expanded = expand_tilde_token(current->content, shell_table); + if (expanded) + { + old = current->content; + current->content = expanded; + free(old); + } + } + current = current->next; + } + return (tokens); +} + +static bool is_tilde_prefix(const char *token) +{ + if (token[0] != '~') + return (false); + return (token[1] == '\0' || token[1] == '/'); +} + +static char *expand_tilde_token(const char *token, t_shell_table *table) +{ + char *home; + + home = st_search(table, "HOME"); + if (!home) + return (NULL); + if (token[1] == '\0') + return (ft_strdup(home)); + return (ft_strjoin(home, token + 1)); +} diff --git a/src/expand/expand_wildcard/expand_wildcard.c b/src/expand/expand_wildcard/expand_wildcard.c new file mode 100644 index 0000000..9fd523d --- /dev/null +++ b/src/expand/expand_wildcard/expand_wildcard.c @@ -0,0 +1,53 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* resolve_wildcard_path.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/03/03 13:00:00 by surayama #+# #+# */ +/* Updated: 2026/03/03 18:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "expand.h" +#include "expand_wildcard_internal.h" + +static bool has_wildcard(const char *str); +static t_list *handle_wildcard(t_list *prev, t_list *current, t_list **head); + +t_list *expand_wildcard(t_list *tokens) +{ + t_list *prev; + t_list *current; + + prev = NULL; + current = tokens; + while (current) + { + if (has_wildcard((char *)current->content)) + prev = handle_wildcard(prev, current, &tokens); + else + prev = current; + current = prev->next; + } + return (tokens); +} + +static bool has_wildcard(const char *str) +{ + return (ft_strchr(str, WILDCARD) != NULL); +} + +static t_list *handle_wildcard(t_list *prev, t_list *current, t_list **head) +{ + t_list *resolved; + + resolved = resolve_wildcard((char *)current->content); + if (!resolved) + return (current); + ft_lstreplace(prev, current, resolved); + if (!prev) + *head = resolved; + return (ft_lstlast(resolved)); +} diff --git a/src/expand/expand_wildcard/expand_wildcard_internal.h b/src/expand/expand_wildcard/expand_wildcard_internal.h new file mode 100644 index 0000000..59ff1fc --- /dev/null +++ b/src/expand/expand_wildcard/expand_wildcard_internal.h @@ -0,0 +1,23 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* wildcard_expand_internal.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/03/03 17:21:58 by surayama #+# #+# */ +/* Updated: 2026/03/03 18:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef EXPAND_WILDCARD_INTERNAL_H +# define EXPAND_WILDCARD_INTERNAL_H + +# include "libft.h" +# include + +# define WILDCARD '*' + +t_list *resolve_wildcard(const char *token); + +#endif diff --git a/src/expand/expand_wildcard/match_wildcard.c b/src/expand/expand_wildcard/match_wildcard.c new file mode 100644 index 0000000..dffa31c --- /dev/null +++ b/src/expand/expand_wildcard/match_wildcard.c @@ -0,0 +1,56 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* match_wildcard.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/16 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/16 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "expand_wildcard_internal.h" + +static bool match_pattern(const char *str, const char *pattern) +{ + if (*pattern == '\0') + return (*str == '\0'); + if (*pattern == WILDCARD) + { + while (*str) + { + if (match_pattern(str, pattern + 1)) + return (true); + str++; + } + return (match_pattern(str, pattern + 1)); + } + if (*str == *pattern) + return (match_pattern(str + 1, pattern + 1)); + return (false); +} + +t_list *filter_pattern(t_list *source, const char *pattern) +{ + t_list *result; + t_list *current; + t_list *new_node; + + if (!source || !pattern) + return (NULL); + result = NULL; + current = source; + while (current) + { + if (match_pattern((const char *)current->content, pattern)) + { + new_node = ft_lstnew(ft_strdup((const char *)current->content)); + if (!new_node) + return (ft_lstclear(&result, free), NULL); + ft_lstadd_back(&result, new_node); + } + current = current->next; + } + return (result); +} diff --git a/src/expand/expand_wildcard/resolve_wildcard.c b/src/expand/expand_wildcard/resolve_wildcard.c new file mode 100644 index 0000000..edac7e5 --- /dev/null +++ b/src/expand/expand_wildcard/resolve_wildcard.c @@ -0,0 +1,96 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* resolve_wildcard.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/03/03 13:00:00 by surayama #+# #+# */ +/* Updated: 2026/03/03 13:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "path.h" +#include "directory.h" +#include "expand_wildcard_internal.h" + +static t_list *get_matches(const char *abs_dir, const char *pattern); +static t_list *build_full_paths(t_list *matches, const char *abs_dir); +t_list *filter_pattern(t_list *source, const char *pattern); + +t_list *resolve_wildcard(const char *token) +{ + char *slash; + char *dir; + char *abs_dir; + t_list *result; + + slash = ft_strrchr(token, '/'); + if (slash) + dir = ft_substr(token, 0, slash - token + 1); + else + dir = ft_strdup("."); + abs_dir = to_absolute_path(dir); + free(dir); + if (!abs_dir) + return (NULL); + if (slash) + result = get_matches(abs_dir, slash + 1); + else + result = get_matches(abs_dir, token); + if (slash && result) + result = build_full_paths(result, abs_dir); + free(abs_dir); + return (result); +} + +static t_list *get_matches(const char *abs_dir, const char *pattern) +{ + t_list *entries; + t_list *result; + + if (list_directory(abs_dir, pattern[0] == '.', &entries) != SUCCESS) + return (NULL); + result = filter_pattern(entries, pattern); + ft_lstclear(&entries, free); + return (result); +} + +static void append_full_path(t_list **result, const char *prefix, + const char *name) +{ + char *full_path; + t_list *node; + + full_path = ft_strjoin(prefix, name); + if (!full_path) + return ; + node = ft_lstnew(full_path); + if (!node) + { + free(full_path); + return ; + } + ft_lstadd_back(result, node); +} + +static t_list *build_full_paths(t_list *matches, const char *abs_dir) +{ + t_list *result; + t_list *current; + char *dir_slash; + + result = NULL; + dir_slash = ft_strjoin(abs_dir, "/"); + if (!dir_slash) + return (NULL); + current = matches; + while (current) + { + append_full_path(&result, dir_slash, (char *)current->content); + current = current->next; + } + free(dir_slash); + ft_lstclear(&matches, free); + return (result); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..9ef7abc --- /dev/null +++ b/src/main.c @@ -0,0 +1,29 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* main.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/09/29 15:13:47 by kjikuhar #+# #+# */ +/* Updated: 2026/04/16 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "minishell.h" + +int main(int argc, char const **argv, char *const envp[]) +{ + t_shell_table *shell_table; + int last_status; + + (void)argc; + (void)argv; + shell_table = build_shell_table(envp); + if (!shell_table) + return (1); + setup_signal_handlers(); + last_status = prompt(on_input, shell_table); + st_destroy(shell_table); + return (last_status); +} diff --git a/src/parse/parse.c b/src/parse/parse.c new file mode 100644 index 0000000..094fbf4 --- /dev/null +++ b/src/parse/parse.c @@ -0,0 +1,88 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 18:30:40 by kjikuhar #+# #+# */ +/* Updated: 2025/11/27 11:55:22 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "parse.h" + +static t_redir_kind find_redir_kind(t_list *current) +{ + if (is_symbol(current, "<")) + return (R_IN); + else if (is_symbol(current, ">")) + return (R_OUT_TRUNC); + else if (is_symbol(current, ">>")) + return (R_OUT_APPEND); + else + return (R_NOT_FOUND); +} + +static int parse_redir(t_list **current, t_cmd *cmd) +{ + t_redir_kind kind; + + kind = find_redir_kind(*current); + if (kind == R_NOT_FOUND) + return (ERROR); + *current = (*current)->next; + if (!*current || !is_word(*current)) + return (ERROR); + if (!add_redir_to_cmd(cmd, kind, (*current)->content)) + return (ERROR); + return (SUCCESS); +} + +static int parse_token(t_list **current, t_cmd *cmd) +{ + if (is_redir(*current)) + { + if (parse_redir(current, cmd) == ERROR) + return (ERROR); + } + else if (is_word(*current)) + { + if (!add_argv_to_cmd(cmd, (*current)->content)) + return (ERROR); + } + else + return (ERROR); + *current = (*current)->next; + return (SUCCESS); +} + +static bool is_cmd_end(t_list *current) +{ + if (!current) + return (true); + return (is_symbol(current, "|") || is_symbol(current, "&&") + || is_symbol(current, "||") || is_symbol(current, ")")); +} + +t_ast *parse_cmd(t_list **current) +{ + t_ast *ast; + t_cmd *cmd; + + cmd = new_cmd(); + if (!cmd) + return (NULL); + while (!is_cmd_end(*current)) + { + if (parse_token(current, cmd) == ERROR) + return (free_cmd(cmd), NULL); + } + if (!cmd->argv && !cmd->redirs) + return (free_cmd(cmd), NULL); + ast = new_ast_node(CMD); + if (!ast) + return (free_cmd(cmd), NULL); + ast->cmd = cmd; + return (ast); +} diff --git a/src/parse/parse_list.c b/src/parse/parse_list.c new file mode 100644 index 0000000..a3a637a --- /dev/null +++ b/src/parse/parse_list.c @@ -0,0 +1,57 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse_list.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/16 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/16 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "parse.h" + +static t_ast_type get_logical_type(t_list *current) +{ + if (is_symbol(current, "&&")) + return (AND); + if (is_symbol(current, "||")) + return (OR); + return (CMD); +} + +t_ast *parse_list(t_list **current) +{ + t_ast *left; + t_ast *node; + t_ast_type type; + + left = parse_pipeline(current); + if (!left) + return (NULL); + while (*current) + { + type = get_logical_type(*current); + if (type != AND && type != OR) + break ; + *current = (*current)->next; + node = new_ast_node(type); + if (!node) + return (free_ast(left), NULL); + node->left = left; + node->right = parse_pipeline(current); + if (!node->right) + return (free_ast(node), NULL); + left = node; + } + return (left); +} + +t_ast *parse(t_list *token_head) +{ + t_list *current; + + current = token_head; + return (parse_list(¤t)); +} diff --git a/src/parse/parse_pipeline.c b/src/parse/parse_pipeline.c new file mode 100644 index 0000000..2aa2014 --- /dev/null +++ b/src/parse/parse_pipeline.c @@ -0,0 +1,60 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse_pipeline.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/16 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/16 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "parse.h" + +static t_ast *parse_subshell(t_list **current) +{ + t_ast *node; + + *current = (*current)->next; + node = new_ast_node(SUBSHELL); + if (!node) + return (NULL); + node->right = parse_list(current); + if (!node->right) + return (free_ast(node), NULL); + if (!*current || !is_symbol(*current, ")")) + return (free_ast(node), NULL); + *current = (*current)->next; + return (node); +} + +static t_ast *parse_primary(t_list **current) +{ + if (*current && is_symbol(*current, "(")) + return (parse_subshell(current)); + return (parse_cmd(current)); +} + +t_ast *parse_pipeline(t_list **current) +{ + t_ast *left; + t_ast *pipe_node; + + left = parse_primary(current); + if (!left) + return (NULL); + if (*current && is_symbol(*current, "|")) + { + *current = (*current)->next; + pipe_node = new_ast_node(PIPE); + if (!pipe_node) + return (free_ast(left), NULL); + pipe_node->left = left; + pipe_node->right = parse_pipeline(current); + if (!pipe_node->right) + return (free_ast(pipe_node), NULL); + return (pipe_node); + } + return (left); +} diff --git a/src/parse/utils/add_to_cmd.c b/src/parse/utils/add_to_cmd.c new file mode 100644 index 0000000..15de621 --- /dev/null +++ b/src/parse/utils/add_to_cmd.c @@ -0,0 +1,50 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* add_to_cmd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 18:29:22 by kjikuhar #+# #+# */ +/* Updated: 2025/11/19 23:39:17 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "parse.h" + +bool add_redir_to_cmd(t_cmd *cmd, t_redir_kind kind, const char *file) +{ + t_redir *redir; + t_list *node; + + redir = new_redir(kind, file); + if (!redir) + return (false); + node = ft_lstnew(redir); + if (!node) + { + free(redir->filename); + free(redir); + return (false); + } + ft_lstadd_back(&cmd->redirs, node); + return (true); +} + +bool add_argv_to_cmd(t_cmd *cmd, const char *arg) +{ + char *dup; + t_list *node; + + dup = ft_strdup(arg); + if (!dup) + return (false); + node = ft_lstnew(dup); + if (!node) + { + free(dup); + return (false); + } + ft_lstadd_back(&cmd->argv, node); + return (true); +} diff --git a/src/parse/utils/free_ast.c b/src/parse/utils/free_ast.c new file mode 100644 index 0000000..041575a --- /dev/null +++ b/src/parse/utils/free_ast.c @@ -0,0 +1,48 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* free_ast.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/01 14:38:13 by kjikuhar #+# #+# */ +/* Updated: 2025/11/19 23:52:47 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "parse.h" + +static void free_redir(void *ptr) +{ + t_redir *redir; + + redir = (t_redir *)ptr; + if (!redir) + return ; + if (redir->filename) + free(redir->filename); + free(redir); +} + +void free_cmd(t_cmd *cmd) +{ + if (!cmd) + return ; + ft_lstclear(&cmd->argv, free); + ft_lstclear(&cmd->redirs, free_redir); + free(cmd); +} + +void free_ast(t_ast *ast) +{ + if (!ast) + return ; + if (ast->type == CMD) + free_cmd(ast->cmd); + else + { + free_ast(ast->left); + free_ast(ast->right); + } + free(ast); +} diff --git a/src/parse/utils/new_ast_node.c b/src/parse/utils/new_ast_node.c new file mode 100644 index 0000000..0d99e94 --- /dev/null +++ b/src/parse/utils/new_ast_node.c @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* new_ast_node.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 18:29:33 by kjikuhar #+# #+# */ +/* Updated: 2025/11/19 23:39:24 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "parse.h" + +t_ast *new_ast_node(t_ast_type type) +{ + t_ast *node; + + node = malloc(sizeof(t_ast)); + if (!node) + return (NULL); + node->type = type; + node->cmd = NULL; + node->left = NULL; + node->right = NULL; + return (node); +} diff --git a/src/parse/utils/new_cmd.c b/src/parse/utils/new_cmd.c new file mode 100644 index 0000000..070e50d --- /dev/null +++ b/src/parse/utils/new_cmd.c @@ -0,0 +1,25 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* new_cmd.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 18:29:38 by kjikuhar #+# #+# */ +/* Updated: 2025/11/19 23:39:34 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "parse.h" + +t_cmd *new_cmd(void) +{ + t_cmd *cmd; + + cmd = malloc(sizeof(t_cmd)); + if (!cmd) + return (NULL); + cmd->argv = NULL; + cmd->redirs = NULL; + return (cmd); +} diff --git a/src/parse/utils/new_redir.c b/src/parse/utils/new_redir.c new file mode 100644 index 0000000..7f37194 --- /dev/null +++ b/src/parse/utils/new_redir.c @@ -0,0 +1,30 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* new_redir.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 17:40:45 by kjikuhar #+# #+# */ +/* Updated: 2025/11/19 23:39:39 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "parse.h" + +t_redir *new_redir(t_redir_kind kind, const char *filename) +{ + t_redir *redir; + + redir = malloc(sizeof(t_redir)); + if (!redir) + return (NULL); + redir->kind = kind; + redir->filename = ft_strdup(filename); + if (!redir->filename) + { + free(redir); + return (NULL); + } + return (redir); +} diff --git a/src/parse/utils/token_check.c b/src/parse/utils/token_check.c new file mode 100644 index 0000000..3adfab6 --- /dev/null +++ b/src/parse/utils/token_check.c @@ -0,0 +1,54 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* token_check.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/11/14 18:29:48 by kjikuhar #+# #+# */ +/* Updated: 2025/11/27 11:51:11 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "parse.h" + +bool is_word(const t_list *node) +{ + const char *s; + + if (!node || !node->content) + return (false); + s = (const char *)node->content; + if (ft_strncmp(s, "<", 2) == 0 || \ + ft_strncmp(s, ">", 2) == 0 || \ + ft_strncmp(s, ">>", 3) == 0 || \ + ft_strncmp(s, "|", 2) == 0 || \ + ft_strncmp(s, "&&", 3) == 0 || \ + ft_strncmp(s, "||", 3) == 0 || \ + ft_strncmp(s, "(", 2) == 0 || \ + ft_strncmp(s, ")", 2) == 0 || \ + ft_strncmp(s, "EOF", 4) == 0) + return (false); + return (true); +} + +bool is_symbol(const t_list *node, const char *literal) +{ + size_t len; + + if (!node || \ + !node->content || \ + !literal) + return (false); + len = ft_strlen(literal); + return (ft_strncmp((const char *)node->content, literal, len + 1) == 0); +} + +bool is_redir(const t_list *node) +{ + if (!node) + return (false); + return (is_symbol(node, "<") || \ + is_symbol(node, ">") || \ + is_symbol(node, ">>")); +} diff --git a/src/prompt/prompt.c b/src/prompt/prompt.c new file mode 100644 index 0000000..3a95901 --- /dev/null +++ b/src/prompt/prompt.c @@ -0,0 +1,43 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* prompt.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/01 15:35:00 by surayama #+# #+# */ +/* Updated: 2026/04/17 21:22:08 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "prompt.h" +#include "signal_handler.h" + +int prompt(int (*handler)(char *input, t_shell_table *shell_table, + int last_status), t_shell_table *shell_table) +{ + char *input; + int last_status; + + last_status = 0; + while (1) + { + input = readline(PROMPT); + if (!input) + { + write(STDOUT_FILENO, "exit\n", 5); + break ; + } + if (is_blank_line(input)) + { + free(input); + continue ; + } + if (*input) + add_history(input); + if (handler) + last_status = handler(input, shell_table, last_status); + } + rl_clear_history(); + return (last_status); +} diff --git a/src/signal/set_signals.c b/src/signal/set_signals.c new file mode 100644 index 0000000..224b466 --- /dev/null +++ b/src/signal/set_signals.c @@ -0,0 +1,60 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* set_signals.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/06 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/06 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "signal_handler.h" +#include +#include + +void set_signal_ignore(void) +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + sigaction(SIGINT, &sa, NULL); +} + +void set_signal_default(void) +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); +} + +void set_signal_interactive(void) +{ + setup_signal_handlers(); +} + +static void sigint_heredoc_handler(int sig) +{ + g_signal = sig; + write(STDOUT_FILENO, "\n", 1); + rl_done = 1; +} + +void set_signal_heredoc(void) +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = sigint_heredoc_handler; + sigaction(SIGINT, &sa, NULL); + sa.sa_handler = SIG_IGN; + sigaction(SIGQUIT, &sa, NULL); +} diff --git a/src/signal/setup_signal_handlers.c b/src/signal/setup_signal_handlers.c new file mode 100644 index 0000000..39aaf52 --- /dev/null +++ b/src/signal/setup_signal_handlers.c @@ -0,0 +1,51 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* setup_signal_handlers.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/04/06 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/04/06 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "signal_handler.h" +#include "libft.h" +#include +#include +#include + +volatile sig_atomic_t g_signal; + +static void sigint_handler(int sig) +{ + g_signal = sig; + write(STDOUT_FILENO, "\n", 1); + rl_on_new_line(); + rl_replace_line("", 0); + rl_redisplay(); +} + +static void disable_echoctl(void) +{ + struct termios term; + + ft_memset(&term, 0, sizeof(term)); + tcgetattr(STDIN_FILENO, &term); + term.c_lflag &= ~(ECHOCTL); + tcsetattr(STDIN_FILENO, TCSANOW, &term); +} + +void setup_signal_handlers(void) +{ + struct sigaction sa; + + disable_echoctl(); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = sigint_handler; + sigaction(SIGINT, &sa, NULL); + sa.sa_handler = SIG_IGN; + sigaction(SIGQUIT, &sa, NULL); +} diff --git a/src/tokenize/is_specific.c b/src/tokenize/is_specific.c new file mode 100644 index 0000000..004b63b --- /dev/null +++ b/src/tokenize/is_specific.c @@ -0,0 +1,60 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* is_specific.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/13 01:15:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "tokenize_private.h" + +bool is_quote(char c) +{ + if (c == '"' || c == '\'') + return (true); + return (false); +} + +bool is_operator_char(char c) +{ + if (c == '&' || c == '|' || c == '<' || c == '>') + return (true); + return (false); +} + +size_t operator_length(const char *str) +{ + if (ft_strncmp(str, "<<<", 3) == 0 || \ + ft_strncmp(str, "<<-", 3) == 0) + return (3); + if (ft_strncmp(str, "&&", 2) == 0 || \ + ft_strncmp(str, "||", 2) == 0 || \ + ft_strncmp(str, "<<", 2) == 0 || \ + ft_strncmp(str, ">>", 2) == 0 || \ + ft_strncmp(str, ">&", 2) == 0 || \ + ft_strncmp(str, "<&", 2) == 0 || \ + ft_strncmp(str, ">|", 2) == 0 || \ + ft_strncmp(str, "<>", 2) == 0) + return (2); + if (is_operator_char(str[0])) + return (1); + return (0); +} + +bool is_operator(const char *str) +{ + if (operator_length(str) > 0) + return (true); + return (false); +} + +bool is_parenthesis(char c) +{ + if (c == '(' || c == ')') + return (true); + return (false); +} diff --git a/src/tokenize/state/in_double_quote.c b/src/tokenize/state/in_double_quote.c new file mode 100644 index 0000000..33f6aa1 --- /dev/null +++ b/src/tokenize/state/in_double_quote.c @@ -0,0 +1,28 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* in_double_quote.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/06 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../tokenize_private.h" + +void in_double_quote(t_token_store *store, t_token_state *state, + char current) +{ + if (current == '\0') + { + ft_putstr_fd("jikussh: unexpected EOF while looking" + " for matching `\"'\n", STDERR_FILENO); + *state = ON_ERROR; + } + else if (add_buffer(store, current) == ERROR) + *state = ON_ERROR; + else if (current == '"') + *state = IN_NORMAL; +} diff --git a/src/tokenize/state/in_normal.c b/src/tokenize/state/in_normal.c new file mode 100644 index 0000000..31503fd --- /dev/null +++ b/src/tokenize/state/in_normal.c @@ -0,0 +1,36 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* in_normal.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/13 00:30:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../tokenize_private.h" + +void by_last(t_token_store *store, t_token_state *state, char current); +void by_space(t_token_store *store, t_token_state *state, char current); +void by_quote(t_token_store *store, t_token_state *state, char current); +void by_operator(t_token_store *store, t_token_state *state, char current); +void by_parenthesis(t_token_store *store, t_token_state *state, + char current); + +void in_normal(t_token_store *store, t_token_state *state, char current) +{ + if (current == '\0') + by_last(store, state, current); + else if (ft_isspace(current)) + by_space(store, state, current); + else if (is_parenthesis(current)) + by_parenthesis(store, state, current); + else if (is_operator_char(current)) + by_operator(store, state, current); + else if (is_quote(current)) + by_quote(store, state, current); + else if (add_buffer(store, current) == ERROR) + *state = ON_ERROR; +} diff --git a/src/tokenize/state/in_normal/by_last.c b/src/tokenize/state/in_normal/by_last.c new file mode 100644 index 0000000..d9f9e8b --- /dev/null +++ b/src/tokenize/state/in_normal/by_last.c @@ -0,0 +1,22 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* by_last.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/01/17 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../../tokenize_private.h" + +void by_last(t_token_store *store, t_token_state *state, char current) +{ + (void)current; + if (push_token(store) == ERROR) + *state = ON_ERROR; + else + *state = ON_SUCCESS; +} diff --git a/src/tokenize/state/in_normal/by_operator.c b/src/tokenize/state/in_normal/by_operator.c new file mode 100644 index 0000000..f6a3c5a --- /dev/null +++ b/src/tokenize/state/in_normal/by_operator.c @@ -0,0 +1,21 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* by_operator.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/01/17 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../../tokenize_private.h" + +void by_operator(t_token_store *store, t_token_state *state, char current) +{ + if (push_token(store) == ERROR || add_buffer(store, current) == ERROR) + *state = ON_ERROR; + else + *state = IN_OPERATOR; +} diff --git a/src/tokenize/state/in_normal/by_parenthesis.c b/src/tokenize/state/in_normal/by_parenthesis.c new file mode 100644 index 0000000..d5591e1 --- /dev/null +++ b/src/tokenize/state/in_normal/by_parenthesis.c @@ -0,0 +1,23 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* by_parenthesis.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/01/17 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../../tokenize_private.h" + +void by_parenthesis(t_token_store *store, t_token_state *state, + char current) +{ + if (push_token(store) == ERROR || add_buffer(store, current) == ERROR + || push_token(store) == ERROR) + *state = ON_ERROR; + else + *state = IN_NORMAL; +} diff --git a/src/tokenize/state/in_normal/by_quote.c b/src/tokenize/state/in_normal/by_quote.c new file mode 100644 index 0000000..83f36d7 --- /dev/null +++ b/src/tokenize/state/in_normal/by_quote.c @@ -0,0 +1,23 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* by_quote.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/01/17 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../../tokenize_private.h" + +void by_quote(t_token_store *store, t_token_state *state, char current) +{ + if (add_buffer(store, current) == ERROR) + *state = ON_ERROR; + else if (current == '"') + *state = IN_DOUBLE_QUOTE; + else if (current == '\'') + *state = IN_SINGLE_QUOTE; +} diff --git a/src/tokenize/state/in_normal/by_space.c b/src/tokenize/state/in_normal/by_space.c new file mode 100644 index 0000000..82e6b86 --- /dev/null +++ b/src/tokenize/state/in_normal/by_space.c @@ -0,0 +1,22 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* by_space.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/01/17 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../../tokenize_private.h" + +void by_space(t_token_store *store, t_token_state *state, char current) +{ + (void)current; + if (push_token(store) == ERROR) + *state = ON_ERROR; + else + *state = IN_NORMAL; +} diff --git a/src/tokenize/state/in_operator.c b/src/tokenize/state/in_operator.c new file mode 100644 index 0000000..f862d90 --- /dev/null +++ b/src/tokenize/state/in_operator.c @@ -0,0 +1,49 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* in_operator.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/13 00:30:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../tokenize_private.h" + +static void by_operator(t_token_store *store, t_token_state *state, + char current); +static void by_normal(t_token_store *store, t_token_state *state, char current); + +void in_operator(t_token_store *store, t_token_state *state, char current) +{ + if (current == '\0' && push_token(store)) + *state = ON_SUCCESS; + else if (is_parenthesis(current)) + by_normal(store, state, current); + else if (is_operator_char(current)) + by_operator(store, state, current); + else + by_normal(store, state, current); +} + +static void by_operator(t_token_store *store, t_token_state *state, + char current) +{ + if (add_buffer(store, current) == ERROR) + *state = ON_ERROR; + else + *state = IN_OPERATOR; +} + +static void by_normal(t_token_store *store, t_token_state *state, char current) +{ + if (push_token(store) == ERROR) + *state = ON_ERROR; + else + { + *state = IN_NORMAL; + in_normal(store, state, current); + } +} diff --git a/src/tokenize/state/in_single_quote.c b/src/tokenize/state/in_single_quote.c new file mode 100644 index 0000000..7dcb7b3 --- /dev/null +++ b/src/tokenize/state/in_single_quote.c @@ -0,0 +1,28 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* in_single_quote.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/06 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../tokenize_private.h" + +void in_single_quote(t_token_store *store, t_token_state *state, + char current) +{ + if (current == '\0') + { + ft_putstr_fd("jikussh: unexpected EOF while looking" + " for matching `''\n", STDERR_FILENO); + *state = ON_ERROR; + } + else if (add_buffer(store, current) == ERROR) + *state = ON_ERROR; + else if (current == '\'') + *state = IN_NORMAL; +} diff --git a/src/tokenize/state/on_error.c b/src/tokenize/state/on_error.c new file mode 100644 index 0000000..d481a83 --- /dev/null +++ b/src/tokenize/state/on_error.c @@ -0,0 +1,21 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* on_error.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/13 00:35:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../tokenize_private.h" + +t_list *on_error(t_token_store *store, char *input) +{ + if (input) + free(input); + free_store(store); + return (NULL); +} diff --git a/src/tokenize/state/on_success.c b/src/tokenize/state/on_success.c new file mode 100644 index 0000000..151da46 --- /dev/null +++ b/src/tokenize/state/on_success.c @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* on_success.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/13 00:35:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../tokenize_private.h" + +t_list *on_success(t_token_store *store, char *input) +{ + t_list *rtv_tokens; + + if (!store) + return (NULL); + if (input) + free(input); + rtv_tokens = store->tokens; + store->tokens = NULL; + ft_lstclear(&(store->buffer), free); + return (rtv_tokens); +} diff --git a/src/tokenize/store/add_buffer.c b/src/tokenize/store/add_buffer.c new file mode 100644 index 0000000..60eea50 --- /dev/null +++ b/src/tokenize/store/add_buffer.c @@ -0,0 +1,34 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* add_buffer.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/17 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../tokenize_private.h" + +int add_buffer(t_token_store *store, char c) +{ + char *char_ptr; + t_list *new_node; + + if (!store) + return (ERROR); + char_ptr = (char *)malloc(sizeof(char)); + if (!char_ptr) + return (ERROR); + *char_ptr = c; + new_node = ft_lstnew(char_ptr); + if (!new_node) + { + free(char_ptr); + return (ERROR); + } + ft_lstadd_back(&(store->buffer), new_node); + return (SUCCESS); +} diff --git a/src/tokenize/store/free_store.c b/src/tokenize/store/free_store.c new file mode 100644 index 0000000..5ddf393 --- /dev/null +++ b/src/tokenize/store/free_store.c @@ -0,0 +1,21 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* free_store.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/16 23:57:51 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../tokenize_private.h" + +void free_store(t_token_store *store) +{ + if (!store) + return ; + ft_lstclear(&(store->buffer), free); + ft_lstclear(&(store->tokens), free); +} diff --git a/src/tokenize/store/push_token.c b/src/tokenize/store/push_token.c new file mode 100644 index 0000000..61736d1 --- /dev/null +++ b/src/tokenize/store/push_token.c @@ -0,0 +1,121 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* push_token.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/12 22:13:34 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../tokenize_private.h" + +static char *build_token_from_buffer(t_list *buffer) +{ + int size; + char *token; + int i; + t_list *current; + + size = ft_lstsize(buffer); + if (size == 0) + return (NULL); + token = (char *)malloc(sizeof(char) * (size + 1)); + if (!token) + return (NULL); + i = 0; + current = buffer; + while (current) + { + token[i] = *(char *)(current->content); + i++; + current = current->next; + } + token[i] = '\0'; + return (token); +} + +static char *consume_operator_token(char **token_ptr) +{ + char *initial_token; + char *operator_token; + char *remained_token; + size_t operator_token_length; + + initial_token = *token_ptr; + operator_token_length = operator_length(initial_token); + operator_token = ft_substr(initial_token, 0, operator_token_length); + remained_token = NULL; + if (!operator_token) + return (NULL); + if (ft_strlen(initial_token) - operator_token_length > 0) + { + remained_token = ft_strdup(initial_token + operator_token_length); + if (!remained_token) + { + free(operator_token); + operator_token = NULL; + } + } + free(initial_token); + *token_ptr = remained_token; + return (operator_token); +} + +static int add_back_token(char *token, t_token_store *store) +{ + t_list *new_node; + + new_node = ft_lstnew(token); + if (!new_node) + { + free(token); + return (ERROR); + } + ft_lstadd_back(&(store->tokens), new_node); + return (SUCCESS); +} + +static int push_operator_tokens(t_token_store *store, char *token) +{ + char *operator_token; + + while (token) + { + operator_token = consume_operator_token(&token); + if (!operator_token) + { + free(token); + return (ERROR); + } + if (add_back_token(operator_token, store) == ERROR) + { + free(token); + return (ERROR); + } + } + return (SUCCESS); +} + +int push_token(t_token_store *store) +{ + char *token; + int result; + + if (!store) + return (ERROR); + if (!store->buffer) + return (SUCCESS); + token = build_token_from_buffer(store->buffer); + if (!token) + return (ERROR); + if (is_operator(token)) + result = push_operator_tokens(store, token); + else + result = add_back_token(token, store); + ft_lstclear(&(store->buffer), free); + store->buffer = NULL; + return (result); +} diff --git a/src/tokenize/tokenize.c b/src/tokenize/tokenize.c new file mode 100644 index 0000000..6972030 --- /dev/null +++ b/src/tokenize/tokenize.c @@ -0,0 +1,53 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* tokenize.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: kjikuhar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/10 17:14:04 by surayama #+# #+# */ +/* Updated: 2026/01/16 13:59:52 by kjikuhar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "tokenize_private.h" + +static void initialize(t_token_store *store, t_token_state *state, + char **current, char *input); + +t_list *tokenize(char *input) +{ + t_token_store store; + t_token_state state; + char *current; + + if (!input) + return (NULL); + initialize(&store, &state, ¤t, input); + while (true) + { + if (state == IN_NORMAL) + in_normal(&store, &state, *current); + else if (state == IN_DOUBLE_QUOTE) + in_double_quote(&store, &state, *current); + else if (state == IN_SINGLE_QUOTE) + in_single_quote(&store, &state, *current); + else if (state == IN_OPERATOR) + in_operator(&store, &state, *current); + else if (state == ON_SUCCESS) + return (on_success(&store, input)); + else if (state == ON_ERROR) + return (on_error(&store, input)); + current++; + } +} + +static void initialize(t_token_store *store, t_token_state *state, + char **current, char *input) +{ + store->tokens = NULL; + store->buffer = NULL; + *current = input; + *state = IN_NORMAL; + input = NULL; +} diff --git a/src/tokenize/tokenize_private.h b/src/tokenize/tokenize_private.h new file mode 100644 index 0000000..393cf8a --- /dev/null +++ b/src/tokenize/tokenize_private.h @@ -0,0 +1,59 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* tokenize_private.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: surayama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/02/05 00:00:00 by surayama #+# #+# */ +/* Updated: 2026/02/05 00:00:00 by surayama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef TOKENIZE_PRIVATE_H +# define TOKENIZE_PRIVATE_H + +# include "tokenize.h" +# include "constants.h" +# include + +typedef enum e_token_state +{ + IN_NORMAL, + IN_DOUBLE_QUOTE, + IN_SINGLE_QUOTE, + IN_OPERATOR, + ON_SUCCESS, + ON_ERROR, +} t_token_state; + +typedef struct s_token_store +{ + t_list *tokens; + t_list *buffer; +} t_token_store; + +// is specific +bool is_quote(char c); +bool is_operator_char(char c); +bool is_operator(const char *str); +size_t operator_length(const char *str); +bool is_parenthesis(char c); + +// store +int push_token(t_token_store *store); +int add_buffer(t_token_store *store, char c); +void free_store(t_token_store *store); + +// state handler +void in_normal(t_token_store *store, t_token_state *state, char current); +void in_double_quote(t_token_store *store, t_token_state *state, + char current); +void in_single_quote(t_token_store *store, t_token_state *state, + char current); +void in_operator(t_token_store *store, t_token_state *state, + char current); +t_list *on_success(t_token_store *store, char *input); +t_list *on_error(t_token_store *store, char *input); + +#endif