mirror of
https://github.com/Savapitech/42sh.git
synced 2026-03-18 21:50:35 +01:00
Add simple exec with env and path parsing
This commit is contained in:
104
src/env.c
104
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;
|
||||
}
|
||||
|
||||
17
src/env.h
17
src/env.h
@@ -7,17 +7,18 @@
|
||||
|
||||
#ifndef ENV_H
|
||||
#define ENV_H
|
||||
#include "u_str.h"
|
||||
#define ENV_BUFF_CAP 128
|
||||
#include <stddef.h>
|
||||
#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
|
||||
|
||||
154
src/exec.c
Normal file
154
src/exec.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
26
src/exec.h
Normal file
26
src/exec.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#ifndef EXEC_H
|
||||
#define EXEC_H
|
||||
#include <stdint.h>
|
||||
|
||||
#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 */
|
||||
26
src/shell.c
26
src/shell.c
@@ -5,7 +5,6 @@
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user