From e8e770ac336d3c8b1b8b6603598e4ea99db4b706 Mon Sep 17 00:00:00 2001 From: savalet Date: Wed, 26 Feb 2025 22:59:05 +0100 Subject: [PATCH] Add bonus --- bonus/Makefile | 126 ++++++++++++++++++ bonus/afunc.txt | 36 ++++++ bonus/src/builtins.c | 150 ++++++++++++++++++++++ bonus/src/builtins.h | 48 +++++++ bonus/src/builtins2.c | 30 +++++ bonus/src/common.h | 14 ++ bonus/src/debug.h | 36 ++++++ bonus/src/env.c | 136 ++++++++++++++++++++ bonus/src/env.h | 30 +++++ bonus/src/exec.c | 242 +++++++++++++++++++++++++++++++++++ bonus/src/exec.h | 23 ++++ bonus/src/main.c | 16 +++ bonus/src/shell.c | 68 ++++++++++ bonus/src/shell.h | 19 +++ bonus/src/vt100_esc_codes.h | 27 ++++ bonus/ulib/lib.h | 1 + bonus/ulib/mem/bzero.c | 18 +++ bonus/ulib/mem/memcpy.c | 15 +++ bonus/ulib/mem/realloc.c | 28 ++++ bonus/ulib/str/str_isalnum.c | 25 ++++ bonus/ulib/str/strcmp.c | 16 +++ bonus/ulib/str/strcpy.c | 16 +++ bonus/ulib/str/strcspn.c | 16 +++ bonus/ulib/str/strdup.c | 23 ++++ bonus/ulib/str/strlen.c | 19 +++ bonus/ulib/u_mem.h | 20 +++ bonus/ulib/u_str.h | 37 ++++++ bonus/utils.mk | 36 ++++++ 28 files changed, 1271 insertions(+) create mode 100644 bonus/Makefile create mode 100644 bonus/afunc.txt create mode 100644 bonus/src/builtins.c create mode 100644 bonus/src/builtins.h create mode 100644 bonus/src/builtins2.c create mode 100644 bonus/src/common.h create mode 100644 bonus/src/debug.h create mode 100644 bonus/src/env.c create mode 100644 bonus/src/env.h create mode 100644 bonus/src/exec.c create mode 100644 bonus/src/exec.h create mode 100644 bonus/src/main.c create mode 100644 bonus/src/shell.c create mode 100644 bonus/src/shell.h create mode 100644 bonus/src/vt100_esc_codes.h create mode 120000 bonus/ulib/lib.h create mode 100644 bonus/ulib/mem/bzero.c create mode 100644 bonus/ulib/mem/memcpy.c create mode 100644 bonus/ulib/mem/realloc.c create mode 100644 bonus/ulib/str/str_isalnum.c create mode 100644 bonus/ulib/str/strcmp.c create mode 100644 bonus/ulib/str/strcpy.c create mode 100644 bonus/ulib/str/strcspn.c create mode 100644 bonus/ulib/str/strdup.c create mode 100644 bonus/ulib/str/strlen.c create mode 100644 bonus/ulib/u_mem.h create mode 100644 bonus/ulib/u_str.h create mode 100644 bonus/utils.mk diff --git a/bonus/Makefile b/bonus/Makefile new file mode 100644 index 0000000..d1f34b3 --- /dev/null +++ b/bonus/Makefile @@ -0,0 +1,126 @@ +## +## EPITECH PROJECT, 2024 +## __ +## File description: +## ./Makefile +## + +MAKEFLAGS += -j + +BIN_NAME := mysh + +LIB_NAME := libu.a + +SRC := $(wildcard src/*.c) + +LIB_SRC := $(wildcard ulib/*.c) +LIB_SRC += $(wildcard ulib/write/printf/*.c) +LIB_SRC += $(wildcard ulib/math/*.c) +LIB_SRC += $(wildcard ulib/mem/*.c) +LIB_SRC += $(wildcard ulib/str/*.c) +LIB_SRC += $(wildcard ulib/write/*.c) + +BUILD_DIR := .build + +CC := gcc + +CFLAGS += -Wall -Wextra -Werror=write-strings -iquote ulib +CFLAGS += -Wno-unused-parameter -Wunused-result -fanalyzer +CFLAGS += -Wp,-U_FORTIFY_SOURCE -Wcast-qual -Wduplicated-branches +CFLAGS += -Wduplicated-cond -Wformat=2 -Wshadow -fno-builtin +CFLAGS += -Wstrict-aliasing=0 -Wstrict-prototypes -Wunreachable-code +CFLAGS += -Wwrite-strings -Werror=declaration-after-statement +CFLAGS += -Werror=format-nonliteral -Werror=int-conversion -Werror=return-type +CFLAGS += -Werror=vla-larger-than=0 -Wno-discarded-qualifiers + +LDFLAGS += -L . +LDLIBS := -lu + +include utils.mk + +.PHONY: _start all +_start: all + +# call mk-profile release, SRC, additional CFLAGS +define mk-profile + +NAME_$(strip $1) := $4 +OBJ_$(strip $1) := $$($(strip $2):%.c=$$(BUILD_DIR)/$(strip $1)/%.o) + +LIB_NAME_$(strip $1) := $(BUILD_DIR)/$(strip $1)/$(LIB_NAME) +LIB_OBJ_$(strip $1) := $$(LIB_SRC:%.c=$$(BUILD_DIR)/$(strip $1)/%.o) + +$$(BUILD_DIR)/$(strip $1)/%.o: %.c + @ mkdir -p $$(dir $$@) + @ $$(CC) $$(CFLAGS) -o $$@ -c $$< + @ $$(LOG_TIME) "$$(C_GREEN) CC $$(C_PURPLE) $$(notdir $$@) $$(C_RESET)" + +$$(LIB_NAME_$(strip $1)): $$(LIB_OBJ_$(strip $1)) + @ ar rc $$@ $$(LIB_OBJ_$(strip $1)) + @ $$(LOG_TIME) "$$(C_CYAN) AR $$(C_PURPLE) $$(notdir $$@) $$(C_RESET)" + +$$(NAME_$(strip $1)): CFLAGS += -L $$(BUILD_DIR)/$(strip $1) $3 +$$(NAME_$(strip $1)): $$(LIB_NAME_$(strip $1)) $$(OBJ_$(strip $1)) + @ $$(CC) $$(CFLAGS) $$(OBJ_$(strip $1)) $$(LDFLAGS) $$(LDLIBS) -o $$@ + @ $$(LOG_TIME) "$$(C_GREEN) CC $$(C_PURPLE) $$(notdir $$@) $$(C_RESET)" + @ $$(LOG_TIME) "$$(C_GREEN) OK Compilation finished $$(C_RESET)" + +endef + +$(eval $(call mk-profile, release, SRC, , $(BIN_NAME))) +$(eval $(call mk-profile, debug, SRC, -D U_DEBUG_MODE -g3, debug)) +$(eval $(call mk-profile, test, SRC, --coverage, test)) +$(eval $(call mk-profile, afl, SRC, -D AFL_MODE, afl_runner)) + +all: $(NAME_release) + +.PHONY: tests_run +tests_run: $(NAME_test) + - find fixtures -name "*.sh" | xargs -i \ + sh -c 'cat {} | env -i PATH="$(dir $(shell which ls))" ./$^' + +.PHONY: cov +cov: tests_run + gcovr . \ + --gcov-ignore-errors=no_working_dir_found \ + --exclude-unreachable-branches \ + --exclude tests + +.PHONY: afl +afl: CC := AFL_USE_ASAN=1 afl-gcc-fast +afl: $(NAME_afl) + +define newline + + +endef + +AFL_FLAGS := -i afl/inputs +AFL_FLAGS += -x afl/tokens +AFL_FLAGS += -o afl/generated + +PROCS ?= $(shell nproc) + +.PHONY: afl_run +afl_run: afl + @ mkdir -p afl/generated + screen -dmS main_instance \ + afl-fuzz $(AFL_FLAGS) -M fuzzer_1 -- ./afl_runner + $(foreach instance, $(shell seq 1 $(PROCS)),\ + screen -dmS afl_$(instance) \ + afl-fuzz $(AFL_FLAGS) -S fuzzer_$(instance) -- ./afl_runner$(newline)) + watch -n 0.25 -- afl-whatsup -s afl/generated + +clean: + @ $(RM) $(OBJ) + @ $(LOG_TIME) "$(C_YELLOW) RM $(C_PURPLE) $(OBJ) $(C_RESET)" + +fclean: + @ $(RM) -r $(NAME_release) $(NAME_debug) $(BUILD_DIR) + @ $(LOG_TIME) "$(C_YELLOW) RM $(C_PURPLE) $(NAME_release) $(NAME_debug) \ + $(C_RESET)" + +.NOTPARALLEL: re +re: fclean all + +.PHONY: all clean fclean re diff --git a/bonus/afunc.txt b/bonus/afunc.txt new file mode 100644 index 0000000..84736e7 --- /dev/null +++ b/bonus/afunc.txt @@ -0,0 +1,36 @@ +malloc +free +exit +opendir +readdir +closedir +getcwd +chdir +fork +stat +lstat +fstat +open +close +getline +strtok +strtok_r +read +write +execve +access +isatty +wait +wait-pid +wait3 +wait4 +signal +kill +getpid +strerror +perror +strsignal +pipe +dup +dup2 +stdin diff --git a/bonus/src/builtins.c b/bonus/src/builtins.c new file mode 100644 index 0000000..526da68 --- /dev/null +++ b/bonus/src/builtins.c @@ -0,0 +1,150 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include +#include +#include +#include + +#include "common.h" +#include "debug.h" +#include "env.h" +#include "shell.h" +#include "u_mem.h" +#include "u_str.h" + +int builtins_exit(env_t *env, char **args __attribute__((unused)), char *buff, + history_t *history) +{ + free_env(env); + free((void *)args); + free(buff); + exit(history->last_exit_code); +} + +int builtins_env(env_t *env, char **args __attribute__((unused)), + char *buff __attribute__((unused)), + history_t *history __attribute__((unused))) +{ + for (size_t i = 0; i < env->sz; i++) { + if (env->env[i] == NULL) + continue; + write(STDOUT_FILENO, env->env[i], u_strlen(env->env[i])); + WRITE_CONST(STDOUT_FILENO, "\n"); + } + return RETURN_SUCCESS; +} + +int builtins_setenv(env_t *env, char **args, + char *buff __attribute__((unused)), + history_t *history __attribute__((unused))) +{ + if (args[1] == NULL) + return builtins_env(env, args, buff, history); + if (args[2] != NULL && args[3] != NULL) { + WRITE_CONST(STDERR_FILENO, "setenv: Too many arguments.\n"); + return RETURN_FAILURE; + } + if (!isalpha(args[1][0])) + return (WRITE_CONST(STDERR_FILENO, "setenv: Variable name must begin" + " with a letter.\n"), RETURN_FAILURE); + if (!u_str_is_only_alnum(args[1])) + return (WRITE_CONST(STDERR_FILENO, "setenv: Variable name must contain" + " alphanumeric characters.\n"), RETURN_FAILURE); + if (!set_env(env, args[1], args[2])) + return RETURN_FAILURE; + return RETURN_SUCCESS; +} + +int builtins_unsetenv(env_t *env, char **args, + __attribute__((unused)) char *buff, + history_t *history __attribute__((unused))) +{ + if (args[1] == NULL) + return (WRITE_CONST(STDERR_FILENO, "unsetenv: Too few arguments.\n"), + RETURN_FAILURE); + if (!unset_env(env, args[1])) + return RETURN_FAILURE; + return RETURN_SUCCESS; +} + +static +void cd_print_error(void) +{ + switch (errno) { + case EACCES: + WRITE_CONST(STDERR_FILENO, ": Permission denied.\n"); + break; + case ENOENT: + WRITE_CONST(STDERR_FILENO, ": No such file or directory.\n"); + break; + case ENOTDIR: + WRITE_CONST(STDERR_FILENO, ": Not a directory.\n"); + break; + default: + WRITE_CONST(STDERR_FILENO, ": Unknown error.\n"); + } +} + +static +char *get_current_dir(void) +{ + size_t size = 64; + char *buffer = malloc(size); + char *new_buffer; + size_t max_it = 100; + + if (!buffer) + return NULL; + while (getcwd(buffer, size) == NULL && max_it > 0) { + if (errno != ERANGE) + return (free(buffer), NULL); + size <<= 1; + new_buffer = u_realloc(buffer, u_strlen(buffer) + 1, size); + if (!new_buffer) + return (free(buffer), NULL); + buffer = new_buffer; + max_it--; + } + return buffer; +} + +static +int builtins_cd_chdir(char *path, history_t *history, char **args, env_t *env) +{ + char *act_pwd; + + if (history->last_chdir != NULL && args[1] != NULL + && u_strcmp(args[1], "-") == 0) + path = history->last_chdir; + act_pwd = get_current_dir(); + U_DEBUG("last chdir %s\n", history->last_chdir); + if (chdir(path) < 0) { + write(STDERR_FILENO, path, u_strlen(path)); + cd_print_error(); + return RETURN_FAILURE; + } + free(history->last_chdir); + history->last_chdir = act_pwd; + return RETURN_SUCCESS; +} + +int builtins_cd(env_t *env, char **args, char *buff __attribute__((unused)), + history_t *history __attribute__((unused))) +{ + char *path = args[1]; + + if (path == NULL || u_strcmp(args[1], "~") == 0) + path = get_env_value(env, "HOME"); + if (path == NULL) + return RETURN_FAILURE; + if (args[1] != NULL && args[2] != NULL) { + WRITE_CONST(STDERR_FILENO, "cd: Too many arguments.\n"); + return RETURN_FAILURE; + } + return builtins_cd_chdir(path, history, args, env); +} diff --git a/bonus/src/builtins.h b/bonus/src/builtins.h new file mode 100644 index 0000000..a7592bf --- /dev/null +++ b/bonus/src/builtins.h @@ -0,0 +1,48 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + + +#ifndef BUILTINS_H + #define BUILTINS_H + #include "env.h" + #include "shell.h" + +typedef struct { + char const *name; + int (*ptr)(env_t *env, char **args, char *buff, history_t *history); +} builtins_t; + +extern const builtins_t BUILTINS[]; +extern const size_t BUILTINS_SZ; + +int builtins_exit(env_t *env, char **args __attribute__((unused)), + char *buff, history_t *history); + +int builtins_env(env_t *env, char **args __attribute__((unused)), + char *buff __attribute__((unused)), + history_t *history __attribute__((unused))); + +int builtins_setenv(env_t *env, char **args, + char *buff __attribute__((unused)), + history_t *history __attribute__((unused))); + +int builtins_unsetenv(env_t *env, char **args, + char *buff __attribute__((unused)), + history_t *history __attribute__((unused))); + +int builtins_cd(env_t *env, char **args, + char *buff __attribute__((unused)), + history_t *history __attribute__((unused))); + +int builtins_builtins(env_t *env __attribute__((unused)), + char **args __attribute__((unused)), char *buff __attribute__((unused)), + history_t *history __attribute__((unused))); + +int builtins_funny_double_dot(env_t *env __attribute__((unused)), + char **args __attribute__((unused)), char *buff __attribute__((unused)), + history_t *history __attribute__((unused))); +#endif /* BUILTIND_H */ diff --git a/bonus/src/builtins2.c b/bonus/src/builtins2.c new file mode 100644 index 0000000..229fc3c --- /dev/null +++ b/bonus/src/builtins2.c @@ -0,0 +1,30 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include + +#include "builtins.h" +#include "common.h" +#include "env.h" +#include "shell.h" +#include "u_str.h" + +int builtins_builtins(env_t *env __attribute__((unused)), + char **args __attribute__((unused)), char *buff __attribute__((unused)), + history_t *history __attribute__((unused))) +{ + for (size_t i = 0; i < BUILTINS_SZ; i++) + write(STDOUT_FILENO, BUILTINS[i].name, u_strlen(BUILTINS[i].name)); + return RETURN_SUCCESS; +} + +int builtins_funny_double_dot(env_t *env __attribute__((unused)), + char **args __attribute__((unused)), char *buff __attribute__((unused)), + history_t *history __attribute__((unused))) +{ + return RETURN_SUCCESS; +} diff --git a/bonus/src/common.h b/bonus/src/common.h new file mode 100644 index 0000000..b404f57 --- /dev/null +++ b/bonus/src/common.h @@ -0,0 +1,14 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef COMMON_H + #define COMMON_H +enum { + RETURN_SUCCESS = 0, + RETURN_FAILURE = 1 +}; +#endif /* COMMON_H */ diff --git a/bonus/src/debug.h b/bonus/src/debug.h new file mode 100644 index 0000000..0e22e16 --- /dev/null +++ b/bonus/src/debug.h @@ -0,0 +1,36 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef DEBUG_H + #define DEBUG_H + #include "vt100_esc_codes.h" + #include + + #define OMIT + + #ifdef U_DEBUG_MODE + #define HEAD __FILE_NAME__, __LINE__ + + #define HEAD_FMT_FILE BOLD BLUE "%s" RESET + #define HEAD_FMT_LINE ":" BOLD PURPLE "%d" RESET + + #define HEAD_FMT HEAD_FMT_FILE HEAD_FMT_LINE " " + + #define ERR(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) + #define DEBUG_INTERNAL(fmt, ...) ERR(HEAD_FMT fmt, HEAD, __VA_ARGS__) + + #define U_DEBUG(fmt, ...) DEBUG_INTERNAL(fmt, __VA_ARGS__) + #define U_DEBUG_MSG(msg) DEBUG_INTERNAL("%s\n", msg) + + #define U_DEBUG_CALL(func, ...) func(__VA_ARGS__) + #else + #define U_DEBUG_CALL(func, ...) OMIT + #define U_DEBUG_MSG(msg) OMIT + #define U_DEBUG(fmt, ...) OMIT + #endif + +#endif /* DEBUG_H */ diff --git a/bonus/src/env.c b/bonus/src/env.c new file mode 100644 index 0000000..9a6045e --- /dev/null +++ b/bonus/src/env.c @@ -0,0 +1,136 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include +#include +#include +#include +#include + +#include "env.h" +#include "u_mem.h" +#include "u_str.h" + +char *get_env_value(env_t *env, char const *key) +{ + int key_len = u_strlen(key); + + for (size_t i = 0; i < env->sz; i++) { + if (env->env[i] == NULL) + continue; + if (u_strcspn(env->env[i], '=') != key_len) + continue; + if (u_strcmp(env->env[i], key) == 0) + return env->env[i] + key_len + 1; + } + return NULL; +} + +static +void unset_env_move(env_t *env, size_t i) +{ + while (env->env[i]) { + env->env[i] = env->env[i + 1]; + i++; + } +} + +bool unset_env(env_t *env, char *key) +{ + int key_len = u_strlen(key); + + for (size_t i = 0; i < env->sz; i++) { + if (env->env[i] == NULL) + continue; + if (u_strcspn(env->env[i], '=') != key_len) + continue; + if (u_strcmp(env->env[i], key) == 0) { + unset_env_move(env, i); + env->sz--; + return true; + } + } + return false; +} + +void free_env(env_t *env) +{ + for (size_t i = 0; i < env->sz; i++) { + if (env->env[i] == NULL) + continue; + free(env->env[i]); + } + free((void *)env->env); +} + +static __attribute__((nonnull)) +bool ensure_env_capacity(env_t *env) +{ + char **new_ptr; + + if (env->sz < env->cap) + return true; + new_ptr = (char **)u_realloc((void *)env->env, sizeof *env->env * env->sz, + sizeof *env->env * env->cap << 1); + if (!new_ptr) + return false; + env->cap <<= 1; + env->env = new_ptr; + return true; +} + +static +void env_bzero(char **env, size_t sz) +{ + for (size_t i = 0; i < sz; i++) + env[i] = NULL; +} + +bool set_env(env_t *env, char *key, char *value) +{ + char *new_env = NULL; + size_t key_len = u_strlen(key); + size_t value_len = u_strlen(value); + + if (get_env_value(env, key) != NULL) + unset_env(env, key); + env->sz++; + if (!ensure_env_capacity(env)) + return false; + new_env = malloc(sizeof(char) * (key_len + value_len + 2)); + if (new_env == NULL) + return false; + u_bzero(new_env, key_len + value_len + 2); + u_strcpy(new_env, key); + new_env[key_len] = '='; + if (value_len > 0) + u_strcpy(new_env + key_len + 1, value); + env->env[env->sz - 1] = new_env; + return true; +} + +env_t parse_env(char **env) +{ + env_t new_env = { 0, .cap = BASE_ENV_CAP }; + + new_env.env = (char **)malloc(sizeof(char *) * new_env.cap); + if (!new_env.env) + return (env_t){ 0, .env = NULL }; + env_bzero(new_env.env, new_env.sz); + for (; *env != NULL; env++) { + if (!ensure_env_capacity(&new_env)) + return (free((void *)new_env.env), (env_t){ 0 }); + new_env.env[new_env.sz] = u_strdup(*env); + if (new_env.env[new_env.sz] == NULL) + return (free((void *)new_env.env), (env_t){ 0 }); + new_env.sz++; + } + if (!ensure_env_capacity(&new_env)) + return (free((void *)new_env.env), (env_t){ 0 }); + new_env.env[new_env.sz] = NULL; + return new_env; +} diff --git a/bonus/src/env.h b/bonus/src/env.h new file mode 100644 index 0000000..9eaec00 --- /dev/null +++ b/bonus/src/env.h @@ -0,0 +1,30 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef ENV_H + #define ENV_H + #include + #include + #define BASE_ENV_CAP 128 + +typedef struct { + size_t sz; + size_t cap; + char **env; +} env_t; + +__attribute__((unused)) +void free_env(env_t *env); +__attribute__((unused)) +env_t parse_env(char **env); +__attribute__((unused)) +char *get_env_value(env_t *env, char const *key); +__attribute__((unused)) +bool unset_env(env_t *env, char *key); +__attribute__((nonnull(1, 2))) +bool set_env(env_t *env, char *key, char *value); +#endif diff --git a/bonus/src/exec.c b/bonus/src/exec.c new file mode 100644 index 0000000..d71183d --- /dev/null +++ b/bonus/src/exec.c @@ -0,0 +1,242 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "builtins.h" +#include "common.h" +#include "debug.h" +#include "env.h" +#include "exec.h" +#include "u_mem.h" +#include "u_str.h" + +const builtins_t BUILTINS[] = { + { "builtins", &builtins_builtins }, + { "cd", &builtins_cd }, + { "chdir", &builtins_cd }, + { "env", &builtins_env }, + { "printenv", &builtins_env }, + { "setenv", &builtins_setenv }, + { "unsetenv", &builtins_unsetenv }, + { ":", &builtins_funny_double_dot }, + { "exit", &builtins_exit } +}; + +const size_t BUILTINS_SZ = sizeof BUILTINS / sizeof *BUILTINS; + +static +char *build_full_path(const char *token, const char *binary) +{ + size_t len_token = u_strlen(token); + size_t len_bin = u_strlen(binary); + char *full_path = malloc(len_token + len_bin + 2); + + if (!full_path) + return NULL; + u_strcpy(full_path, token); + full_path[len_token] = '/'; + u_strcpy(full_path + len_token + 1, binary); + full_path[len_token + len_bin + 1] = '\0'; + return full_path; +} + +static +char *find_binary(const char *path_env, const char *binary) +{ + static char *saveptr = NULL; + static char *path = NULL; + char *token; + char *full_path; + + if (path_env) { + path = u_strdup(path_env); + if (!path) + return NULL; + token = strtok_r(path, ":", &saveptr); + } else + token = strtok_r(NULL, ":", &saveptr); + if (!token) + return free(path), u_strdup(binary); + full_path = build_full_path(token, binary); + if (!full_path) + return NULL; + return access(full_path, X_OK) == 0 ? full_path : (free(full_path), + find_binary(NULL, binary)); +} + +static __attribute__((nonnull)) +bool ensure_args_capacity(char ***args, size_t const sz, size_t *cap) +{ + char **new_ptr; + + if (sz < *cap) + return true; + new_ptr = (char **)u_realloc((void *)*args, sizeof *args * sz, + sizeof *args * *cap << 1); + if (!new_ptr) + return false; + *cap <<= 1; + *args = new_ptr; + return true; +} + +static +char **parse_args(char *buffer) +{ + size_t sz = 0; + size_t cap = DEFAULT_ARGS_CAP; + char **args = (char **)malloc(sizeof *args * cap); + char *token; + + if (!args) + return NULL; + token = strtok(buffer, " \t\v"); + while (token != NULL) { + ensure_args_capacity(&args, sz, &cap); + args[sz] = token; + U_DEBUG("Args [%lu] [%s]\n", sz, args[sz]); + sz++; + token = strtok(NULL, " \t\v"); + } + ensure_args_capacity(&args, sz, &cap); + args[sz] = NULL; + return args; +} + +static +int command_error(char *cmd, char **args, int error) +{ + struct stat st; + + if (access(cmd, F_OK) == -1) { + write(STDERR_FILENO, args[0], u_strlen(args[0])); + WRITE_CONST(STDERR_FILENO, ": Command not found.\n"); + return 84; + } + stat(cmd, &st); + if (S_ISDIR(st.st_mode) || access(cmd, X_OK)) { + write(STDERR_FILENO, args[0], u_strlen(args[0])); + WRITE_CONST(STDERR_FILENO, ": Permission denied.\n"); + return 84; + } + if (error == ENOEXEC) { + write(STDERR_FILENO, args[0], u_strlen(args[0])); + WRITE_CONST(STDERR_FILENO, ": Exec format error." + " Binary file not executable.\n"); + return 0; + } + return 84; +} + +static +int launch_bin(char *full_bin_path, char **args, env_t *env, char *buff) +{ + int status; + pid_t pid = fork(); + + if (pid == 0) { +#if defined(AFL_MODE) + exit(0); +#else + if (execve(full_bin_path, args, env->env) < 0) { + status = command_error(full_bin_path, args, errno); + free_env(env); + free((void *)args); + free(buff); + exit(status); + } +#endif + } + waitpid(pid, &status, 0); + return status; +} + +static +void status_handler(int status, history_t *history) +{ + char *strsig; + + if (WIFEXITED(status)) + history->last_exit_code = WEXITSTATUS(status); + if (!WIFEXITED(status) && WIFSIGNALED(status)) { + if (WTERMSIG(status) != SIGFPE && WTERMSIG(status) != SIGINT && + WTERMSIG(status) != SIGTRAP) { + strsig = strsignal(WTERMSIG(status)); + write(STDERR_FILENO, strsig, u_strlen(strsig)); + } + if (WTERMSIG(status) == SIGTRAP) + WRITE_CONST(STDERR_FILENO, "Trace/BPT trap"); + if (WTERMSIG(status) == SIGFPE) + WRITE_CONST(STDERR_FILENO, "Floating exception"); + if (WCOREDUMP(status)) + WRITE_CONST(STDERR_FILENO, " (core dumped)"); + WRITE_CONST(STDERR_FILENO, "\n"); + } + U_DEBUG("Exit code [%d]\n", history->last_exit_code); +} + +static +bool builtins_launcher(char *buffer, env_t *env, history_t *history, + char **args) +{ + int buffer_l = u_strlen(buffer); + + for (size_t i = 0; i < BUILTINS_SZ; i++) { + if (u_strlen(BUILTINS[i].name) != buffer_l) + continue; + if (u_strcmp(BUILTINS[i].name, buffer) == 0) { + history->last_exit_code = + BUILTINS[i].ptr(env, args, buffer, history); + return true; + } + } + return false; +} + +static +char *parse_full_bin_path(env_t *env, char *bin_name) +{ + char const *path = get_env_value(env, "PATH"); + char *full_bin_path; + + if (path == NULL) + path = DEFAULT_PATH; + U_DEBUG("Used path [%s]\n", path); + full_bin_path = find_binary(path, bin_name); + U_DEBUG("Exec bin [%s]\n", full_bin_path); + if (full_bin_path == NULL) + return NULL; + return full_bin_path; +} + +int execute(char *buffer, env_t *env, history_t *history) +{ + char *full_bin_path; + char **args = parse_args(buffer); + int status; + + if (!args) + return RETURN_FAILURE; + if (builtins_launcher(buffer, env, history, args)) + return RETURN_SUCCESS; + full_bin_path = parse_full_bin_path(env, args[0]); + if (full_bin_path == NULL) + return (free((void *)args), RETURN_FAILURE); + U_DEBUG("Found bin [%s]\n", full_bin_path); + status = launch_bin(full_bin_path, args, env, buffer); + status_handler(status, history); + free(full_bin_path); + free((void *)args); + return RETURN_SUCCESS; +} diff --git a/bonus/src/exec.h b/bonus/src/exec.h new file mode 100644 index 0000000..135567c --- /dev/null +++ b/bonus/src/exec.h @@ -0,0 +1,23 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef EXEC_H + #define EXEC_H + #include "env.h" + #include "shell.h" + #define DEFAULT_ARGS_CAP 1 + #define DEFAULT_PATH "/usr/bin:." + +typedef struct { + char **args; + size_t count; + size_t cap; +} args_t; + +__attribute__((nonnull)) +int execute(char *buffer, env_t *env, history_t *history); +#endif /* EXEC_H */ diff --git a/bonus/src/main.c b/bonus/src/main.c new file mode 100644 index 0000000..277de29 --- /dev/null +++ b/bonus/src/main.c @@ -0,0 +1,16 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include "debug.h" +#include "shell.h" + +int main(int ac __attribute__((unused)), char **av __attribute__((unused)), + char **env) +{ + U_DEBUG_MSG("Debug mode activated."); + return shell(env); +} diff --git a/bonus/src/shell.c b/bonus/src/shell.c new file mode 100644 index 0000000..0a026b7 --- /dev/null +++ b/bonus/src/shell.c @@ -0,0 +1,68 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include +#include +#include +#include + +#include "common.h" +#include "debug.h" +#include "env.h" +#include "exec.h" +#include "shell.h" +#include "u_str.h" + +__attribute__((unused)) +static +void debug_env_entries(env_t *env) +{ + for (size_t i = 0; i < env->sz; i++) { + if (env->env[i] == NULL) + continue; + U_DEBUG("Env entry [%lu] [%s]\n", i, env->env[i]); + } +} + +static +int shell_loop(env_t *env, int is_a_tty, history_t *history) +{ + char *buffer = NULL; + size_t buffer_sz; + size_t buffer_len; + + while (true) { + if (is_a_tty) + WRITE_CONST(STDOUT_FILENO, SHELL_PROMPT); + if (getline(&buffer, &buffer_sz, stdin) == -1) + break; + buffer_len = u_strlen(buffer); + if (buffer_len < 2 || !u_str_is_alnum(buffer)) + continue; + if (buffer[buffer_len - 1] == '\n') + buffer[buffer_len - 1] = '\0'; + U_DEBUG("Buffer [%lu] [%s]\n", buffer_len, buffer); + execute(buffer, env, history); + } + if (is_a_tty) + WRITE_CONST(STDOUT_FILENO, "exit\n"); + return (free(buffer), history->last_exit_code); +} + +int shell(char **env_ptr) +{ + env_t env = parse_env(env_ptr); + history_t history = { .cmd_history = NULL, 0, .last_chdir = NULL }; + int shell_result; + + if (!env.env) + return RETURN_FAILURE; + U_DEBUG_CALL(debug_env_entries, &env); + shell_result = shell_loop(&env, isatty(STDIN_FILENO), &history); + free_env(&env); + return shell_result; +} diff --git a/bonus/src/shell.h b/bonus/src/shell.h new file mode 100644 index 0000000..237ad1c --- /dev/null +++ b/bonus/src/shell.h @@ -0,0 +1,19 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef SHELL_H + #define SHELL_H + #include "vt100_esc_codes.h" + #define SHELL_PROMPT RED "|> " RESET + +typedef struct { + char **cmd_history; + int last_exit_code; + char *last_chdir; +} history_t; +int shell(char **env); +#endif /* SHELL_H */ diff --git a/bonus/src/vt100_esc_codes.h b/bonus/src/vt100_esc_codes.h new file mode 100644 index 0000000..a2860b7 --- /dev/null +++ b/bonus/src/vt100_esc_codes.h @@ -0,0 +1,27 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef CR_VT100_ESC_CODES_H + #define CR_VT100_ESC_CODES_H + + #define ESC "\033" + #define CFMT(n) ESC "[" #n "m" + + // move + #define MOVE_RIGHT(n) ESC "[" #n "C" + + // colors + #define RESET CFMT(0) + #define BOLD CFMT(1) + + #define RED CFMT(31) + #define GREEN CFMT(32) + #define YELLOW CFMT(33) + #define BLUE CFMT(34) + #define PURPLE CFMT(35) + #define CYAN CFMT(36) +#endif diff --git a/bonus/ulib/lib.h b/bonus/ulib/lib.h new file mode 120000 index 0000000..cf10654 --- /dev/null +++ b/bonus/ulib/lib.h @@ -0,0 +1 @@ +../../include/lib.h \ No newline at end of file diff --git a/bonus/ulib/mem/bzero.c b/bonus/ulib/mem/bzero.c new file mode 100644 index 0000000..e0ad0da --- /dev/null +++ b/bonus/ulib/mem/bzero.c @@ -0,0 +1,18 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include +#include + +__attribute__((nonnull(1))) +bool u_bzero(char *restrict str, size_t sz) +{ + if (sz > 0) + for (size_t i = 0; i < sz; i++) + str[i] = 0; + return true; +} diff --git a/bonus/ulib/mem/memcpy.c b/bonus/ulib/mem/memcpy.c new file mode 100644 index 0000000..8c6c361 --- /dev/null +++ b/bonus/ulib/mem/memcpy.c @@ -0,0 +1,15 @@ +/* +** EPITECH PROJECT, 2024 +** CPoolDay08 +** File description: +** ./u_realloc.c +*/ + +#include + +void *u_memcpy(char *dst, char const *src, size_t sz) +{ + for (size_t i = 0; i < sz; i++) + dst[i] = src[i]; + return dst; +} diff --git a/bonus/ulib/mem/realloc.c b/bonus/ulib/mem/realloc.c new file mode 100644 index 0000000..eee7ebd --- /dev/null +++ b/bonus/ulib/mem/realloc.c @@ -0,0 +1,28 @@ +/* +** EPITECH PROJECT, 2024 +** CPoolDay08 +** File description: +** ./u_realloc.c +*/ + +#include +#include + +#include "u_mem.h" + +void *u_realloc(void *ptr, size_t actual_size, size_t new_size) +{ + void *new; + + if (ptr == NULL) + return ptr; + if (!actual_size && !new_size) + return NULL; + new = malloc(new_size); + if (new == NULL) + return NULL; + if (actual_size > 0 && new_size > 0) + u_memcpy(new, ptr, actual_size); + free(ptr); + return new; +} diff --git a/bonus/ulib/str/str_isalnum.c b/bonus/ulib/str/str_isalnum.c new file mode 100644 index 0000000..7e976ee --- /dev/null +++ b/bonus/ulib/str/str_isalnum.c @@ -0,0 +1,25 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include +#include + +bool u_str_is_alnum(char *str) +{ + for (; *str != '\0'; str++) + if (isalnum(*str)) + return true; + return false; +} + +bool u_str_is_only_alnum(char *str) +{ + for (; *str != '\0'; str++) + if (!isalnum(*str)) + return false; + return true; +} diff --git a/bonus/ulib/str/strcmp.c b/bonus/ulib/str/strcmp.c new file mode 100644 index 0000000..667afc0 --- /dev/null +++ b/bonus/ulib/str/strcmp.c @@ -0,0 +1,16 @@ +/* +** EPITECH PROJECT, 2024 +** B-CPE-100-REN-1-1-cpoolday06-savinien.petitjean +** File description: +** Task 1 +*/ + +int u_strcmp(char const *s1, char const *s2) +{ + int i = 0; + + for (; s1[i] == s2[i] && s1[i] != '\0'; i++); + if (i > 0 && (s1[i] == '\0' || s2[i] == '\0')) + return s1[i - 1] - s2[i - 1]; + return s1[i] - s2[i]; +} diff --git a/bonus/ulib/str/strcpy.c b/bonus/ulib/str/strcpy.c new file mode 100644 index 0000000..ae515fe --- /dev/null +++ b/bonus/ulib/str/strcpy.c @@ -0,0 +1,16 @@ +/* +** EPITECH PROJECT, 2024 +** B-CPE-100-REN-1-1-cpoolday06-savinien.petitjean +** File description: +** Task 1 +*/ + +__attribute__((nonnull(1, 2))) +char *u_strcpy(char *dest, char const *src) +{ + int i = 0; + + for (; src[i] != '\0'; i++) + dest[i] = src[i]; + return (dest); +} diff --git a/bonus/ulib/str/strcspn.c b/bonus/ulib/str/strcspn.c new file mode 100644 index 0000000..6cd20d3 --- /dev/null +++ b/bonus/ulib/str/strcspn.c @@ -0,0 +1,16 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +int u_strcspn(char *str, char c) +{ + char *old_str = str; + + for (; *str != '\0'; str++) + if (*str == c) + return str - old_str; + return 0; +} diff --git a/bonus/ulib/str/strdup.c b/bonus/ulib/str/strdup.c new file mode 100644 index 0000000..cebd182 --- /dev/null +++ b/bonus/ulib/str/strdup.c @@ -0,0 +1,23 @@ +/* +** EPITECH PROJECT, 2024 +** CPoolDay08 +** File description: +** ./u_strdup.c +*/ + +#include + +#include "u_mem.h" +#include "u_str.h" + +char *u_strdup(char const *src) +{ + char *dest; + int len = u_strlen(src); + + dest = malloc(sizeof(char) * (len + 1)); + if (dest == NULL) + return NULL; + u_bzero(dest, len + 1); + return u_strcpy(dest, src); +} diff --git a/bonus/ulib/str/strlen.c b/bonus/ulib/str/strlen.c new file mode 100644 index 0000000..6ce8a58 --- /dev/null +++ b/bonus/ulib/str/strlen.c @@ -0,0 +1,19 @@ +/* +** EPITECH PROJECT, 2024 +** B-CPE-100-REN-1-1-cpoolday06-savinien.petitjean +** File description: +** Task 3 +*/ + +#include +#include + +int u_strlen(char const *str) +{ + char const *p = str; + + if (str == NULL) + return 0; + for (; *p != '\0'; p++); + return (p - str); +} diff --git a/bonus/ulib/u_mem.h b/bonus/ulib/u_mem.h new file mode 100644 index 0000000..d0a42a5 --- /dev/null +++ b/bonus/ulib/u_mem.h @@ -0,0 +1,20 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef MEM_H + #define MEM_H + #include + #include + #define IDX_OF(array, i, mem_s) (array + ((i) * (mem_s))) + +void *u_memcpy(char *dst, char const *src, size_t sz); +void *u_realloc(void *ptr, size_t actual_size, size_t new_size); +void u_swap(int *, int *); +bool u_bzero(char *restrict str, size_t sz); +int swap_uint32(int src); + +#endif /* MEM_H */ diff --git a/bonus/ulib/u_str.h b/bonus/ulib/u_str.h new file mode 100644 index 0000000..6189d26 --- /dev/null +++ b/bonus/ulib/u_str.h @@ -0,0 +1,37 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef STRING_H + #define STRING_H + #include + #include + #define WRITE_CONST(fd, str) write(fd, str, sizeof str - 1) + +typedef struct { + char *str; + size_t sz; + size_t cap; +} buff_t; + +int u_getnbr(char const *); +char *u_strcat(char *, char const *); +char *u_strncat(char *, char const *, int); +char *u_strdup(char const *); +int u_strlen(char const *); +char *u_strcpy(char *, char const *); +char *u_strncpy(char *, char const *, int); +char *u_revstr(char *); +char *u_strstr(char *, char const *); +int u_strcmp(char const *, char const *); +int u_strncmp(char const *, char const *, int); +char *u_numstr(char *, int); +int u_spacelen(char const *str); +int u_strcspn(char *str, char c); +bool u_str_is_alnum(char *str); +bool u_str_is_only_alnum(char *str); + +#endif /* STRING_H */ diff --git a/bonus/utils.mk b/bonus/utils.mk new file mode 100644 index 0000000..3ddee9f --- /dev/null +++ b/bonus/utils.mk @@ -0,0 +1,36 @@ +## +## EPITECH PROJECT, 2024 +## template +## File description: +## utils.mk +## + +ifneq ($(shell command -v tput),) + ifneq ($(shell tput colors),0) + +C_RESET := \033[00m +C_BOLD := \e[1m +C_RED := \e[31m +C_GREEN := \e[32m +C_YELLOW := \e[33m +C_BLUE := \e[34m +C_PURPLE := \e[35m +C_CYAN := \e[36m + +C_BEGIN := \033[A + + endif +endif + +NOW = $(shell date +%s%3N) +STIME := $(call NOW) +TIME_NS = $(shell expr $(call NOW) - $(STIME)) +TIME_MS = $(shell expr $(call TIME_NS)) + +BOXIFY = "[$(C_BLUE)$(1)$(C_RESET)] $(2)" + +ifneq ($(shell command -v printf),) + LOG_TIME = printf $(call BOXIFY, %6s , %b\n) "$(call TIME_MS)" +else + LOG_TIME = echo -e $(call BOXIFY, $(call TIME_MS) ,) +endif