From 4078e4c68880ebdcd75c163a9bd924f8fdefb70d Mon Sep 17 00:00:00 2001 From: savalet Date: Wed, 12 Feb 2025 17:30:16 +0100 Subject: [PATCH] Add simple exec with env and path parsing --- src/env.c | 104 +++++++++++++++-------------------- src/env.h | 17 +++--- src/exec.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/exec.h | 26 +++++++++ src/shell.c | 26 ++++----- 5 files changed, 244 insertions(+), 83 deletions(-) create mode 100644 src/exec.c create mode 100644 src/exec.h diff --git a/src/env.c b/src/env.c index b1a2a6a..04215e5 100644 --- a/src/env.c +++ b/src/env.c @@ -16,79 +16,61 @@ #include "u_mem.h" #include "u_str.h" -size_t count_env_entries(char **env) +void debug_env_entries(env_t *env) { - size_t result = 0; - - for (; *env != NULL; env++) - result++; - return result; + for (size_t i = 0; i < env->sz; i++) + U_DEBUG("Env entry [%lu] [%s]\n", i, env->env[i]); } -bool ensure_buff_av_capacity(buff_t *buff, size_t requested) +char *get_env_value(env_t *env, char const *key) { - char *new_str; - size_t endsize = ENV_BUFF_CAP; + for (size_t i = 0; i < env->sz; i++) + if (u_strncmp(env->env[i], key, u_strlen(key)) == 0) + return env->env[i] + u_strlen(key) + 1; + return NULL; +} - if ((buff->sz + requested) < buff->cap) +static __attribute__((nonnull)) +bool ensure_env_capacity(env_t *env) +{ + char **new_ptr; + + if (env->sz < env->cap) return true; - for (; endsize < buff->sz + requested; endsize <<= 1); - if (endsize > buff->cap) { - new_str = u_realloc(buff->str, (sizeof *buff->str) * buff->sz, - (sizeof *buff->str) * endsize); - if (new_str == NULL) - return false; - buff->str = new_str; - buff->cap = endsize; - } + 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 -bool parse_env_populate(char **env, buff_t *env_values, - env_entry_t *env_entries) +void env_bzero(char **env, size_t sz) { - size_t env_size = 0; - size_t i = 0; + for (size_t i = 0; i < sz; i++) + env[i] = NULL; +} +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++) { - env_size = u_strlen(*env); - if (!ensure_buff_av_capacity(env_values, env_size)) - return false; - env_entries[i].ptr = u_memcpy(env_values->str + env_values->sz, *env, - env_size); - env_entries[i].size = env_size; - env_values->sz += env_size; - i++; + 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++; } - return true; -} - -void debug_env_entries(env_entry_t *env_entries, size_t env_size) -{ - size_t keylen __attribute__((unused)); - for (size_t i = 0; i < env_size; i++) { - keylen = u_strcspn(env_entries[i].ptr, '=') + 1; - U_DEBUG("Env entry [%01lu] key [%.*s] value [%.*s]\n", i, - (int)keylen - 1, env_entries[i].ptr, - (int)(env_entries[i].size - keylen), - env_entries[i].ptr + keylen); - } -} - -bool parse_env(char **env, buff_t *env_values, env_entry_t *env_entries) -{ - if (env_values == NULL || env_entries == NULL) - return false; - u_bzero((char *)env_values, sizeof(buff_t)); - env_values->str = malloc(sizeof *env_values->str * ENV_BUFF_CAP); - if (env_values->str == NULL) - return false; - env_values->cap = ENV_BUFF_CAP; - u_bzero(env_values->str, sizeof *env_values->str * env_values->cap); - parse_env_populate(env, env_values, env_entries); - env_values->str[env_values->sz] = '\0'; - U_DEBUG("Parsed %zu env entries (%zu)\n", - count_env_entries(env), env_values->sz); - return true; + 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/src/env.h b/src/env.h index 34eb513..cd8b7f3 100644 --- a/src/env.h +++ b/src/env.h @@ -7,17 +7,18 @@ #ifndef ENV_H #define ENV_H - #include "u_str.h" - #define ENV_BUFF_CAP 128 + #include + #define BASE_ENV_CAP 128 typedef struct { - char *ptr; - size_t size; -} env_entry_t; + size_t sz; + size_t cap; + char **env; +} env_t; // Debug -void debug_env_entries(env_entry_t *env_entries, size_t env_size); +void debug_env_entries(env_t *env); -bool parse_env(char **env, buff_t *env_values, env_entry_t *env_entries); -size_t count_env_entries(char **env); +env_t parse_env(char **env); +char *get_env_value(env_t *env, char const *key); #endif diff --git a/src/exec.c b/src/exec.c new file mode 100644 index 0000000..8ecc276 --- /dev/null +++ b/src/exec.c @@ -0,0 +1,154 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "debug.h" +#include "env.h" +#include "exec.h" +#include "u_mem.h" +#include "u_str.h" + +static +const builtins_t BUILTINS[] = { + { "exit", (void (*)(uint64_t))&exit } +}; + +static +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 +int count_args(char *buffer) +{ + int count = 0; + char *token; + + token = strtok(buffer, " \t\n"); + while (token != NULL) { + count++; + token = strtok(NULL, " \t\n"); + } + return count; +} + +static +void free_args(char **args) +{ + char **old_args = args; + + for (; *args != NULL; args++) + free(*args); + free((void *)old_args); +} + +static +char **parse_args(char *buffer) +{ + int i = 0; + int arg_count = count_args(buffer); + char **args = (char **)malloc((arg_count + 1) * sizeof(char *)); + char *token; + + if (!args) + return NULL; + token = strtok(buffer, " "); + while (token != NULL) { + args[i] = (char *)malloc(u_strlen(token) + 1); + u_bzero(args[i], u_strlen(token) + 1); + if (args[i] == NULL) + return (free((void *)args), NULL); + u_strcpy(args[i], token); + i++; + token = strtok(NULL, " "); + } + args[i] = NULL; + return args; +} + +static +int launch_bin(char *full_bin_path, char **args, char **env) +{ + int status; + pid_t pid = fork(); + + if (pid == 0) { + if (execve(full_bin_path, args, env) < 0) { + WRITE_CONST(STDERR_FILENO, "Command not found.\n"); + exit(127); + } + } + waitpid(pid, &status, 0); + return status; +} + +int execute(char *buffer, env_t *env) +{ + char *path = NULL; + char *full_bin_path; + char **args = parse_args(buffer); + + if (!args) + return RETURN_FAILURE; + for (size_t i = 0; i < BUILTINS_SZ; i++) + if (u_strcmp(buffer, BUILTINS[i].name) == 0) + BUILTINS[i].ptr(0); + buffer[u_strlen(buffer) - 1] = '\0'; + path = get_env_value(env, "PATH"); + full_bin_path = find_binary(path, args[0]); + U_DEBUG("Found bin [%s]\n", full_bin_path); + if (full_bin_path == NULL) + return (free_args(args), RETURN_FAILURE); + launch_bin(full_bin_path, args, NULL); + free(full_bin_path); + free_args(args); + return 0; +} diff --git a/src/exec.h b/src/exec.h new file mode 100644 index 0000000..5729af0 --- /dev/null +++ b/src/exec.h @@ -0,0 +1,26 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef EXEC_H + #define EXEC_H + #include + + #include "env.h" + +typedef struct { + char const *name; + void (*ptr)(uint64_t a); +} builtins_t; + +typedef struct { + char **args; + size_t count; + size_t cap; +} args_t; + +int execute(char *buffer, env_t *env); +#endif /* EXEC_H */ diff --git a/src/shell.c b/src/shell.c index 8f406bc..78e66e2 100644 --- a/src/shell.c +++ b/src/shell.c @@ -5,7 +5,6 @@ ** _ */ -#include #include #include #include @@ -14,41 +13,40 @@ #include "common.h" #include "debug.h" #include "env.h" +#include "exec.h" #include "shell.h" #include "u_str.h" static -int shell_loop(void) +int shell_loop(env_t *env) { int is_a_tty = isatty(STDIN_FILENO); char *buffer = NULL; size_t buffer_sz; - signal(SIGINT, SIG_IGN); while (true) { if (is_a_tty) WRITE_CONST(STDOUT_FILENO, SHELL_PROMPT); if (getline(&buffer, &buffer_sz, stdin) == -1) break; - U_DEBUG("Buffer [%.*s]\n", u_strlen(buffer) - 1, buffer); + if (u_strlen(buffer) < 2) + continue; if (!u_str_is_alnum(buffer)) continue; + U_DEBUG("Buffer [%.*s]\n", u_strlen(buffer) - 1, buffer); + execute(buffer, env); } + WRITE_CONST(STDOUT_FILENO, "exit\n"); free(buffer); return RETURN_SUCCESS; } -int shell(char **env) +int shell(char **env_ptr) { - buff_t env_values = { 0 }; - env_entry_t *env_entries = NULL; - size_t env_size = count_env_entries(env); + env_t env = parse_env(env_ptr); - env_values.str = malloc(ENV_BUFF_CAP); - if (env_values.str == NULL) + if (!env.env) return RETURN_FAILURE; - env_entries = malloc(sizeof *env_entries * env_size); - parse_env(env, &env_values, env_entries); - U_DEBUG_CALL(debug_env_entries, env_entries, env_size); - return shell_loop(); + U_DEBUG_CALL(debug_env_entries, &env); + return shell_loop(&env); }