mirror of
https://github.com/Savapitech/42sh.git
synced 2026-03-18 21:50:35 +01:00
Del bonus
This commit is contained in:
170
bonus/ast.c
170
bonus/ast.c
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast.h"
|
||||
|
||||
static
|
||||
ast_t *parse_arg(ast_ctx_t *ctx, ast_t *node)
|
||||
{
|
||||
ctx->act_tok = get_next_token(ctx);
|
||||
if (ctx->act_tok.type == T_SEMICOLON)
|
||||
return node;
|
||||
if (ctx->act_tok.type & (T_ARG | T_REDIRECT | T_APPEND |
|
||||
T_IN_REDIRECT | T_HEREDOC)) {
|
||||
if (!ensure_node_cap(node))
|
||||
return NULL;
|
||||
node->vector.tokens[node->vector.sz] = ctx->act_tok;
|
||||
node->vector.sz++;
|
||||
return parse_arg(ctx, node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static
|
||||
ast_t *fill_cmd_node(ast_ctx_t *ctx)
|
||||
{
|
||||
ast_t *node = create_node(ctx);
|
||||
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
node->type = N_CMD;
|
||||
node->vector.cap = 2;
|
||||
node->vector.tokens =
|
||||
malloc(sizeof *node->vector.tokens * node->vector.cap);
|
||||
if (node->vector.tokens == NULL)
|
||||
return NULL;
|
||||
node->tok = ctx->act_tok;
|
||||
node->vector.sz = 0;
|
||||
return parse_arg(ctx, node);
|
||||
}
|
||||
|
||||
static
|
||||
ast_t *parse_cmd(ast_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->act_tok.type == T_EOF)
|
||||
return ctx->ast;
|
||||
if (ctx->act_tok.type != T_ARG)
|
||||
if (!parser_eat(ctx, T_ARG))
|
||||
return NULL;
|
||||
return fill_cmd_node(ctx);
|
||||
}
|
||||
|
||||
static
|
||||
bool parse_pipe_childs(ast_ctx_t *ctx, ast_t *node)
|
||||
{
|
||||
if (!ensure_list_cap(node))
|
||||
return false;
|
||||
node->list.nodes[node->list.sz] = parse_cmd(ctx);
|
||||
if (node->list.nodes[node->list.sz] == NULL)
|
||||
return false;
|
||||
node->list.sz++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
ast_t *parse_pipe(ast_ctx_t *ctx, ast_t *l_node)
|
||||
{
|
||||
ast_t *node = create_node(ctx);
|
||||
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
node->type = N_LST;
|
||||
node->tok = ctx->act_tok;
|
||||
node->list.cap = 2;
|
||||
node->list.nodes = (ast_t **)malloc(sizeof(ast_t *) * node->list.cap);
|
||||
if ((void *)node->list.nodes == NULL)
|
||||
return NULL;
|
||||
node->list.sz = 1;
|
||||
node->list.nodes[0] = l_node;
|
||||
if (!parser_eat(ctx, T_ARG))
|
||||
return NULL;
|
||||
while (ctx->act_tok.type & (T_ARG | T_PIPE))
|
||||
if (!parse_pipe_childs(ctx, node))
|
||||
return NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
static
|
||||
ast_t *parse_semi(ast_ctx_t *ctx)
|
||||
{
|
||||
ast_t *l_node = parse_cmd(ctx);
|
||||
|
||||
if (ctx->act_tok.type == T_PIPE)
|
||||
ctx->ast = parse_pipe(ctx, l_node);
|
||||
else
|
||||
return l_node;
|
||||
return ctx->ast;
|
||||
}
|
||||
|
||||
static
|
||||
ast_t *create_semi_node(ast_ctx_t *ctx, ast_t *l_node)
|
||||
{
|
||||
ast_t *node = create_node(ctx);
|
||||
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
node->type = N_LST;
|
||||
node->list.cap = 2;
|
||||
node->list.nodes = (ast_t **)malloc(sizeof(ast_t *) * node->list.cap);
|
||||
if ((void *)node->list.nodes == NULL)
|
||||
return NULL;
|
||||
node->list.sz = 1;
|
||||
node->list.nodes[0] = l_node;
|
||||
return node;
|
||||
}
|
||||
|
||||
static
|
||||
ast_t *fill_semi_node(ast_ctx_t *ctx, ast_t *node)
|
||||
{
|
||||
while (ctx->act_tok.type == T_SEMICOLON) {
|
||||
ctx->act_tok = get_next_token(ctx);
|
||||
if (ctx->act_tok.type == T_SEMICOLON)
|
||||
continue;
|
||||
if (!ensure_list_cap(node))
|
||||
return false;
|
||||
node->list.nodes[node->list.sz] = parse_semi(ctx);
|
||||
if (node->list.nodes[node->list.sz] == NULL)
|
||||
return false;
|
||||
node->list.sz++;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static
|
||||
void skip_semi(ast_ctx_t *ctx)
|
||||
{
|
||||
while (ctx->act_tok.type == T_SEMICOLON)
|
||||
ctx->act_tok = get_next_token(ctx);
|
||||
}
|
||||
|
||||
ast_t *parse_expression(ast_ctx_t *ctx)
|
||||
{
|
||||
ast_t *l_node;
|
||||
ast_t *node;
|
||||
|
||||
if (ctx->act_tok.type == T_EOF)
|
||||
return ctx->ast;
|
||||
skip_semi(ctx);
|
||||
l_node = parse_semi(ctx);
|
||||
if (l_node == NULL)
|
||||
return ctx->ast;
|
||||
if (ctx->act_tok.type == T_SEMICOLON) {
|
||||
node = create_semi_node(ctx, l_node);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
ctx->ast = fill_semi_node(ctx, node);
|
||||
}
|
||||
ctx->act_tok = get_next_token(ctx);
|
||||
return parse_expression(ctx);
|
||||
}
|
||||
102
bonus/ast.h
102
bonus/ast.h
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#ifndef AST_H
|
||||
#define AST_H
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "shell.h"
|
||||
#include "env.h"
|
||||
|
||||
#define DEFAULT_AST_CAP 128
|
||||
#define T_ALL 0xff
|
||||
|
||||
typedef enum {
|
||||
T_SEMICOLON = 1 << 0, // ;
|
||||
T_AND = 1 << 1, // &&
|
||||
T_OR = 1 << 2, // ||
|
||||
T_PIPE = 1 << 3, // |
|
||||
T_BACKTICK = 1 << 4, // `
|
||||
T_LEFT_PARENT = 1 << 5, // (
|
||||
T_RIGHT_PARENT = 1 << 6, // )
|
||||
T_PREV_CMD = 1 << 7, // !!
|
||||
T_VAR = 1 << 8, // $
|
||||
T_REDIRECT = 1 << 9, // >
|
||||
T_APPEND = 1 << 10, // >>
|
||||
T_HEREDOC = 1 << 11, // <
|
||||
T_IN_REDIRECT = 1 << 12, // <
|
||||
T_EOF = 1 << 13, // \0
|
||||
T_ARG = 1 << 14,
|
||||
T_INVALID = 1 << 15
|
||||
} token_type_t;
|
||||
|
||||
typedef enum {
|
||||
N_LST,
|
||||
N_CMD,
|
||||
N_BIN
|
||||
} node_type_t;
|
||||
|
||||
typedef struct {
|
||||
token_type_t type;
|
||||
char *str;
|
||||
size_t sz;
|
||||
} token_t;
|
||||
|
||||
typedef struct {
|
||||
token_type_t type;
|
||||
char const *str;
|
||||
uint8_t sz;
|
||||
char const *name;
|
||||
} tokens_list_t;
|
||||
|
||||
typedef struct ast_s ast_t;
|
||||
|
||||
typedef struct ast_s {
|
||||
node_type_t type;
|
||||
union {
|
||||
struct {
|
||||
ast_t *left;
|
||||
ast_t *right;
|
||||
} binary;
|
||||
struct {
|
||||
size_t sz;
|
||||
size_t cap;
|
||||
token_t *tokens;
|
||||
} vector;
|
||||
struct {
|
||||
size_t sz;
|
||||
size_t cap;
|
||||
ast_t **nodes;
|
||||
} list;
|
||||
};
|
||||
token_t tok;
|
||||
} ast_t;
|
||||
|
||||
typedef struct {
|
||||
size_t i;
|
||||
size_t sz;
|
||||
char *str;
|
||||
size_t cap;
|
||||
ast_t *ast;
|
||||
token_t act_tok;
|
||||
ast_t *first_node;
|
||||
} ast_ctx_t;
|
||||
|
||||
|
||||
extern const tokens_list_t TOKENS_LIST[];
|
||||
|
||||
ast_t *parse_expression(ast_ctx_t *ctx);
|
||||
void print_ast(ast_ctx_t *ctx, ast_t *ast, size_t depth);
|
||||
token_t get_next_token(ast_ctx_t *ctx);
|
||||
int visitor(char *buffer, env_t *env, history_t *history);
|
||||
ast_t *create_node(ast_ctx_t *ctx);
|
||||
bool ensure_node_cap(ast_t *node);
|
||||
bool ensure_list_cap(ast_t *node);
|
||||
bool parser_eat(ast_ctx_t *ctx, token_type_t expected);
|
||||
void free_ast(ast_ctx_t *ctx);
|
||||
#endif /* AST_H */
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "u_mem.h"
|
||||
#include "u_str.h"
|
||||
|
||||
bool parser_eat(ast_ctx_t *ctx, token_type_t expected)
|
||||
{
|
||||
token_type_t prev_tok_type = ctx->act_tok.type;
|
||||
|
||||
ctx->act_tok = get_next_token(ctx);
|
||||
if (!(ctx->act_tok.type & expected)) {
|
||||
if (prev_tok_type == T_PIPE)
|
||||
WRITE_CONST(STDERR_FILENO, "Invalid null command.\n");
|
||||
else {
|
||||
WRITE_CONST(STDERR_FILENO, "Parse error near \"");
|
||||
write(STDERR_FILENO, ctx->act_tok.str, ctx->act_tok.sz);
|
||||
WRITE_CONST(STDERR_FILENO, "\"\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ast_t *create_node(ast_ctx_t *ctx)
|
||||
{
|
||||
ast_t *new_ast;
|
||||
|
||||
if (ctx->ast == NULL)
|
||||
return NULL;
|
||||
if (ctx->sz + 1 == ctx->cap) {
|
||||
new_ast = u_realloc(ctx->ast, sizeof *ctx->ast * ctx->sz,
|
||||
sizeof *ctx->ast * (ctx->cap << 1));
|
||||
if (new_ast == NULL)
|
||||
return NULL;
|
||||
ctx->ast = new_ast;
|
||||
ctx->cap <<= 1;
|
||||
}
|
||||
ctx->ast[ctx->sz] = (ast_t){ 0 };
|
||||
ctx->sz++;
|
||||
return ctx->ast + ctx->sz - 1;
|
||||
}
|
||||
|
||||
bool ensure_node_cap(ast_t *node)
|
||||
{
|
||||
token_t *temp;
|
||||
|
||||
if (node->vector.sz + 1 < node->vector.cap)
|
||||
return true;
|
||||
temp = u_realloc(node->vector.tokens,
|
||||
sizeof *node->vector.tokens * node->vector.sz,
|
||||
sizeof *node->vector.tokens * node->vector.cap << 1);
|
||||
if (temp == NULL)
|
||||
return false;
|
||||
node->vector.cap <<= 1;
|
||||
node->vector.tokens = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ensure_list_cap(ast_t *node)
|
||||
{
|
||||
ast_t **temp;
|
||||
|
||||
if (node->list.sz + 1 < node->vector.cap)
|
||||
return true;
|
||||
temp = (ast_t **)u_realloc((void *)node->list.nodes,
|
||||
sizeof *node->list.nodes * node->list.sz,
|
||||
sizeof *node->list.nodes * node->vector.cap << 1);
|
||||
if ((void *)temp == NULL)
|
||||
return false;
|
||||
node->list.cap <<= 1;
|
||||
node->list.nodes = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_ast(ast_ctx_t *ctx)
|
||||
{
|
||||
for (size_t i = 0; i < ctx->sz; i++) {
|
||||
if (ctx->first_node[i].type == N_LST)
|
||||
free((void *)ctx->first_node[i].list.nodes);
|
||||
if (ctx->first_node[i].type == N_CMD)
|
||||
free(ctx->first_node[i].vector.tokens);
|
||||
}
|
||||
free(ctx->first_node);
|
||||
}
|
||||
149
bonus/builtins.c
149
bonus/builtins.c
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "env.h"
|
||||
#include "exec.h"
|
||||
#include "u_mem.h"
|
||||
#include "u_str.h"
|
||||
|
||||
int builtins_exit(ef_t *ef, char **args __attribute__((unused)))
|
||||
{
|
||||
if (ef->flags & F_PIPE &&
|
||||
!(ef->out_fd == STDOUT_FILENO || ef->p_i == ef->p_sz - 1))
|
||||
return RETURN_SUCCESS;
|
||||
if (!(ef->flags & F_EXIT)) {
|
||||
ef->flags |= F_EXIT;
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
free_env(ef->env);
|
||||
free(ef->buffer);
|
||||
WRITE_CONST(STDOUT_FILENO, "exit\n");
|
||||
exit(ef->history->last_exit_code);
|
||||
}
|
||||
|
||||
int builtins_env(ef_t *ef, char **args __attribute__((unused)))
|
||||
{
|
||||
for (size_t i = 0; i < ef->env->sz; i++) {
|
||||
if (ef->env->env[i] == NULL)
|
||||
continue;
|
||||
write(ef->out_fd, ef->env->env[i], u_strlen(ef->env->env[i]));
|
||||
WRITE_CONST(ef->out_fd, "\n");
|
||||
}
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
int builtins_setenv(ef_t *ef, char **args)
|
||||
{
|
||||
if (args[1] == NULL)
|
||||
return builtins_env(ef, args);
|
||||
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(ef->env, args[1], args[2]))
|
||||
return RETURN_FAILURE;
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
int builtins_unsetenv(ef_t *ef, char **args)
|
||||
{
|
||||
if (args[1] == NULL)
|
||||
return (WRITE_CONST(STDERR_FILENO, "unsetenv: Too few arguments.\n"),
|
||||
RETURN_FAILURE);
|
||||
if (!unset_env(ef->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(ef_t *ef, char **args, char *path)
|
||||
{
|
||||
char *act_pwd;
|
||||
|
||||
if (ef->history->last_chdir != NULL && args[1] != NULL
|
||||
&& u_strcmp(args[1], "-") == 0)
|
||||
path = ef->history->last_chdir;
|
||||
act_pwd = get_current_dir();
|
||||
if (chdir(path) < 0) {
|
||||
write(STDERR_FILENO, path, u_strlen(path));
|
||||
cd_print_error();
|
||||
return RETURN_FAILURE;
|
||||
}
|
||||
free(ef->history->last_chdir);
|
||||
ef->history->last_chdir = act_pwd;
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
int builtins_cd(ef_t *ef, char **args)
|
||||
{
|
||||
char *path = args[1];
|
||||
|
||||
if (!(ef->out_fd == STDOUT_FILENO || ef->p_i == ef->p_sz - 1))
|
||||
return RETURN_SUCCESS;
|
||||
if (path == NULL || u_strcmp(args[1], "~") == 0)
|
||||
path = get_env_value(ef->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(ef, args, path);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BUILTINS_H
|
||||
#define BUILTINS_H
|
||||
#include "exec.h"
|
||||
|
||||
typedef struct {
|
||||
char const *name;
|
||||
int (*ptr)(ef_t *ef, char **args);
|
||||
} builtins_t;
|
||||
|
||||
extern const builtins_t BUILTINS[];
|
||||
extern const size_t BUILTINS_SZ;
|
||||
|
||||
int builtins_exit(ef_t *ef, char **args);
|
||||
int builtins_env(ef_t *ef, char **args);
|
||||
int builtins_setenv(ef_t *ef, char **args);
|
||||
int builtins_unsetenv(ef_t *ef, char **args);
|
||||
int builtins_cd(ef_t *ef, char **args);
|
||||
int builtins_builtins(ef_t *ef, char **args);
|
||||
int builtins_funny_double_dot(ef_t *ef, char **args);
|
||||
#endif /* BUILTIND_H */
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "builtins.h"
|
||||
#include "common.h"
|
||||
#include "u_str.h"
|
||||
|
||||
int builtins_builtins(ef_t *ef, char **args)
|
||||
{
|
||||
for (size_t i = 0; i < BUILTINS_SZ; i++) {
|
||||
write(ef->out_fd, BUILTINS[i].name, u_strlen(BUILTINS[i].name));
|
||||
WRITE_CONST(ef->out_fd, "\n");
|
||||
}
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
int builtins_funny_double_dot(ef_t *ef, char **args)
|
||||
{
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
enum {
|
||||
RETURN_SUCCESS = 0,
|
||||
RETURN_FAILURE = 1
|
||||
};
|
||||
#endif /* COMMON_H */
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
#include "vt100_esc_codes.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#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 */
|
||||
136
bonus/env.c
136
bonus/env.c
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
30
bonus/env.h
30
bonus/env.h
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#ifndef ENV_H
|
||||
#define ENV_H
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#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
|
||||
209
bonus/exec.c
209
bonus/exec.c
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "builtins.h"
|
||||
#include "common.h"
|
||||
#include "debug.h"
|
||||
#include "env.h"
|
||||
#include "exec.h"
|
||||
#include "path.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 __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(ef_t *ef, ast_t *node, env_t *env)
|
||||
{
|
||||
size_t sz = 1;
|
||||
size_t cap = DEFAULT_ARGS_CAP;
|
||||
char **args = (char **)malloc(sizeof *args * cap);
|
||||
|
||||
if (!args)
|
||||
return NULL;
|
||||
node->tok.str[node->tok.sz] = '\0';
|
||||
args[0] = node->tok.str;
|
||||
for (size_t i = 0; i < node->vector.sz; i++) {
|
||||
if (ef->skip_sz > 0 && i >= ef->skip_i && i < ef->skip_i + ef->skip_sz)
|
||||
continue;
|
||||
ensure_args_capacity(&args, sz, &cap);
|
||||
node->vector.tokens[i].str[node->vector.tokens[i].sz] = '\0';
|
||||
if (*node->vector.tokens[i].str == '$'
|
||||
&& get_env_value(env, node->vector.tokens[i].str + 1) != NULL) {
|
||||
args[sz] = get_env_value(env, node->vector.tokens[i].str + 1);
|
||||
} else if (*(node->vector.tokens[i].str + 1) == '?')
|
||||
asprintf(&args[sz], "%d", ef->history->last_exit_code);
|
||||
else
|
||||
args[sz] = node->vector.tokens[i].str;
|
||||
U_DEBUG("Args [%lu] [%s]\n", sz, args[sz]);
|
||||
sz++;
|
||||
}
|
||||
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
|
||||
void set_fd(ef_t *ef)
|
||||
{
|
||||
U_DEBUG("In fd [%d] out fd [%d]\n", ef->in_fd, ef->out_fd);
|
||||
if (ef->in_fd != STDIN_FILENO) {
|
||||
dup2(ef->in_fd, STDIN_FILENO);
|
||||
close(ef->in_fd);
|
||||
}
|
||||
if (ef->out_fd != STDOUT_FILENO) {
|
||||
dup2(ef->out_fd, STDOUT_FILENO);
|
||||
close(ef->out_fd);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int launch_bin(char *full_bin_path, char **args, ef_t *ef)
|
||||
{
|
||||
int status;
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
set_fd(ef);
|
||||
if (execve(full_bin_path, args, ef->env->env) < 0) {
|
||||
status = command_error(full_bin_path, args, errno);
|
||||
free_env(ef->env);
|
||||
free((void *)args);
|
||||
exit((free(ef->buffer), status));
|
||||
}
|
||||
}
|
||||
if (!(ef->flags & F_PIPE) || ef->p_i == ef->p_sz - 1)
|
||||
waitpid(pid, &status, 0);
|
||||
else
|
||||
waitpid(pid, &status, WNOHANG);
|
||||
if (WIFEXITED(status))
|
||||
ef->history->last_exit_code =
|
||||
ef->history->last_exit_code ?: WEXITSTATUS(status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static
|
||||
void status_handler(int status)
|
||||
{
|
||||
if (!WIFEXITED(status) && WIFSIGNALED(status) && WTERMSIG(status)) {
|
||||
if (WTERMSIG(status) == SIGSEGV)
|
||||
WRITE_CONST(STDERR_FILENO, "Segmentation fault");
|
||||
if (WTERMSIG(status) == SIGTRAP)
|
||||
WRITE_CONST(STDERR_FILENO, "Trace/BPT trap");
|
||||
if (WTERMSIG(status) == SIGFPE)
|
||||
WRITE_CONST(STDERR_FILENO, "Floating exception");
|
||||
if (WCOREDUMP(status) && (WTERMSIG(status) == SIGSEGV ||
|
||||
WTERMSIG(status) == SIGFPE || WTERMSIG(status) == SIGTRAP))
|
||||
WRITE_CONST(STDERR_FILENO, " (core dumped)");
|
||||
if (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGFPE ||
|
||||
WTERMSIG(status) == SIGTRAP)
|
||||
WRITE_CONST(STDERR_FILENO, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
bool builtins_launcher(ef_t *ef, char **args)
|
||||
{
|
||||
int bin_l = u_strlen(args[0]);
|
||||
|
||||
U_DEBUG("In fd [%d] out fd [%d]\n", ef->in_fd, ef->out_fd);
|
||||
for (size_t i = 0; i < BUILTINS_SZ; i++) {
|
||||
if (u_strlen(BUILTINS[i].name) != bin_l)
|
||||
continue;
|
||||
if (u_strcmp(BUILTINS[i].name, args[0]) == 0) {
|
||||
ef->history->last_exit_code =
|
||||
BUILTINS[i].ptr(ef, args);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int execute(ef_t *ef)
|
||||
{
|
||||
char *full_bin_path;
|
||||
char **args;
|
||||
int status;
|
||||
|
||||
args = parse_args(ef, ef->act_node, ef->env);
|
||||
if (!args)
|
||||
return RETURN_FAILURE;
|
||||
if (builtins_launcher(ef, args))
|
||||
return RETURN_SUCCESS;
|
||||
full_bin_path = parse_full_bin_path(ef->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, ef);
|
||||
status_handler(status);
|
||||
U_DEBUG("Exit code [%d]\n", ef->history->last_exit_code);
|
||||
free(full_bin_path);
|
||||
free((void *)args);
|
||||
return ef->history->last_exit_code != 0 ? RETURN_FAILURE : RETURN_SUCCESS;
|
||||
}
|
||||
50
bonus/exec.h
50
bonus/exec.h
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#ifndef EXEC_H
|
||||
#define EXEC_H
|
||||
#include "ast.h"
|
||||
#include "env.h"
|
||||
#include "shell.h"
|
||||
|
||||
#define DEFAULT_ARGS_CAP 1
|
||||
|
||||
typedef struct {
|
||||
char **args;
|
||||
size_t count;
|
||||
size_t cap;
|
||||
} args_t;
|
||||
|
||||
enum flags {
|
||||
F_PIPE = 1 << 0,
|
||||
F_RED = 1 << 1,
|
||||
F_EXIT = 1 << 2
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char *buffer;
|
||||
env_t *env;
|
||||
history_t *history;
|
||||
ast_ctx_t *ctx;
|
||||
ast_t *act_node;
|
||||
size_t skip_i;
|
||||
size_t skip_sz;
|
||||
uint8_t flags;
|
||||
size_t p_i;
|
||||
size_t p_sz;
|
||||
int rin_fd;
|
||||
int rout_fd;
|
||||
int pipes[2];
|
||||
int pin_fd;
|
||||
int pout_fd;
|
||||
int in_fd;
|
||||
int out_fd;
|
||||
} ef_t;
|
||||
|
||||
__attribute__((nonnull))
|
||||
int execute(ef_t *ef);
|
||||
#endif /* EXEC_H */
|
||||
16
bonus/main.c
16
bonus/main.c
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
** 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);
|
||||
}
|
||||
71
bonus/path.c
71
bonus/path.c
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "env.h"
|
||||
#include "path.h"
|
||||
#include "u_str.h"
|
||||
|
||||
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, char **dup_path)
|
||||
{
|
||||
static char *saveptr = NULL;
|
||||
char *token;
|
||||
char *full_path;
|
||||
|
||||
if (path_env) {
|
||||
*dup_path = u_strdup(path_env);
|
||||
if (!*dup_path)
|
||||
return NULL;
|
||||
token = strtok_r(*dup_path, ":", &saveptr);
|
||||
} else
|
||||
token = strtok_r(NULL, ":", &saveptr);
|
||||
if (!token)
|
||||
return 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, dup_path));
|
||||
}
|
||||
|
||||
char *parse_full_bin_path(env_t *env, char *bin_name)
|
||||
{
|
||||
char const *path = get_env_value(env, "PATH");
|
||||
char *dup_path = NULL;
|
||||
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, &dup_path);
|
||||
U_DEBUG("Exec bin [%s]\n", full_bin_path);
|
||||
if (full_bin_path == NULL)
|
||||
return NULL;
|
||||
free(dup_path);
|
||||
return full_bin_path;
|
||||
}
|
||||
15
bonus/path.h
15
bonus/path.h
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#ifndef PATH_H
|
||||
#define PATH_H
|
||||
#include "env.h"
|
||||
|
||||
#define DEFAULT_PATH "/usr/bin:."
|
||||
|
||||
char *parse_full_bin_path(env_t *env, char *bin_name);
|
||||
#endif
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "exec.h"
|
||||
#include "u_str.h"
|
||||
|
||||
bool handle_out_redirect(ef_t *ef, ast_t *node, size_t i, size_t sz)
|
||||
{
|
||||
if (!(node->vector.tokens[i].type & (T_REDIRECT | T_APPEND)))
|
||||
return true;
|
||||
if (i >= sz || node->vector.tokens[i + 1].type != T_ARG)
|
||||
return (WRITE_CONST(STDERR_FILENO,
|
||||
"Missing name for redirect.\n"), false);
|
||||
ef->skip_i = ef->skip_i ?: i;
|
||||
ef->skip_sz += 2;
|
||||
node->vector.tokens[i + 1].str[node->vector.tokens[i + 1].sz] = '\0';
|
||||
ef->rout_fd = open(node->vector.tokens[i + 1].str, O_CREAT | O_WRONLY |
|
||||
(node->vector.tokens[i].type == T_APPEND ? O_APPEND : O_TRUNC), 0644);
|
||||
if (ef->rout_fd < 0)
|
||||
return (puterror(node->vector.tokens[i + 1].str), false);
|
||||
ef->out_fd = ef->rout_fd;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handle_in_redirect(ef_t *ef, ast_t *node, size_t i, size_t sz)
|
||||
{
|
||||
if (node->vector.tokens[i].type != T_IN_REDIRECT)
|
||||
return true;
|
||||
if (i >= sz || node->vector.tokens[i + 1].type != T_ARG)
|
||||
return (WRITE_CONST(STDERR_FILENO,
|
||||
"Missing name for redirect.\n"), false);
|
||||
ef->skip_i = ef->skip_i ?: i;
|
||||
ef->skip_sz += 2;
|
||||
node->vector.tokens[i + 1].str[node->vector.tokens[i + 1].sz] = '\0';
|
||||
ef->rin_fd = open(node->vector.tokens[i + 1].str, O_RDONLY);
|
||||
if (ef->rin_fd < 0)
|
||||
return (puterror(node->vector.tokens[i + 1].str), false);
|
||||
ef->in_fd = ef->rin_fd;
|
||||
return true;
|
||||
}
|
||||
|
||||
int handle_heredoc_loop(ast_t *node, size_t i)
|
||||
{
|
||||
int fds[2];
|
||||
buff_t buffer = { .str = NULL };
|
||||
|
||||
if (pipe(fds) < 0)
|
||||
return (puterror("pipe"), -1);
|
||||
node->vector.tokens[i + 1].str[node->vector.tokens[i + 1].sz] = '\0';
|
||||
WRITE_CONST(STDOUT_FILENO, "? ");
|
||||
while (getline(&buffer.str, &buffer.sz, stdin) != -1) {
|
||||
buffer.str[u_strlen(buffer.str) - 1] = '\0';
|
||||
if (u_strcmp(buffer.str, node->vector.tokens[i + 1].str) == 0)
|
||||
break;
|
||||
write(fds[1], buffer.str, u_strlen(buffer.str));
|
||||
write(fds[1], "\n", 1);
|
||||
WRITE_CONST(STDOUT_FILENO, "? ");
|
||||
}
|
||||
free(buffer.str);
|
||||
close(fds[1]);
|
||||
return fds[0];
|
||||
}
|
||||
|
||||
bool handle_heredoc(ef_t *ef, ast_t *node, size_t i, size_t sz)
|
||||
{
|
||||
if (node->vector.tokens[i].type != T_HEREDOC)
|
||||
return true;
|
||||
if (i >= sz || node->vector.tokens[i + 1].type != T_ARG)
|
||||
return (WRITE_CONST(STDERR_FILENO,
|
||||
"Missing name for redirect.\n"), false);
|
||||
ef->skip_i = ef->skip_i ?: i;
|
||||
ef->skip_sz += 2;
|
||||
ef->in_fd = handle_heredoc_loop(node, i);
|
||||
if (ef->in_fd == -1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#ifndef REDIRECTS_H
|
||||
#define REDIRECTS_H
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "exec.h"
|
||||
|
||||
bool handle_out_redirect(ef_t *ef, ast_t *node, size_t i, size_t sz);
|
||||
bool handle_in_redirect(ef_t *ef, ast_t *node, size_t i, size_t sz);
|
||||
int handle_heredoc_loop(ast_t *node, size_t i);
|
||||
bool handle_heredoc(ef_t *ef, ast_t *node, size_t i, size_t sz);
|
||||
#endif /* REDIRECTS_H */
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "common.h"
|
||||
#include "debug.h"
|
||||
#include "env.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
|
||||
void check_basic_error(char const *buffer)
|
||||
{
|
||||
if (*buffer == '|')
|
||||
WRITE_CONST(STDERR_FILENO, "Invalid null command.\n");
|
||||
if (*buffer == '>' || *buffer == '<')
|
||||
WRITE_CONST(STDERR_FILENO, "Missing name for redirect.\n");
|
||||
}
|
||||
|
||||
static
|
||||
void ignore_sigint(int sig __attribute__((unused)))
|
||||
{
|
||||
WRITE_CONST(STDIN_FILENO, "\n");
|
||||
WRITE_CONST(STDOUT_FILENO, SHELL_PROMPT);
|
||||
}
|
||||
|
||||
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)) {
|
||||
check_basic_error(buffer);
|
||||
continue;
|
||||
}
|
||||
buffer[buffer_len - 1] = '\0';
|
||||
U_DEBUG("Buffer [%lu] [%s]\n", buffer_len, buffer);
|
||||
visitor(buffer, env, history);
|
||||
}
|
||||
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);
|
||||
signal(SIGINT, ignore_sigint);
|
||||
shell_result = shell_loop(&env, isatty(STDIN_FILENO), &history);
|
||||
free_env(&env);
|
||||
WRITE_CONST(STDOUT_FILENO, "exit\n");
|
||||
return shell_result;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
** 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 */
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "debug.h"
|
||||
#include "u_str.h"
|
||||
|
||||
const tokens_list_t TOKENS_LIST[] = {
|
||||
{ T_SEMICOLON, ";", 1, "T_SEMICOLON" },
|
||||
{ T_AND, "&&", 2, "T_AND" },
|
||||
{ T_OR, "||", 2, "T_OR" },
|
||||
{ T_PIPE, "|", 1, "T_PIPE" },
|
||||
{ T_BACKTICK, "`", 1, "T_BACKTICK" },
|
||||
{ T_LEFT_PARENT, "(", 1, "T_LEFT_PARENT" },
|
||||
{ T_RIGHT_PARENT, ")", 1, "T_RIGHT_PARENT" },
|
||||
{ T_PREV_CMD, "!!", 2, "T_PREV_CMD" },
|
||||
{ T_APPEND, ">>", 2, "T_APPEND" },
|
||||
{ T_REDIRECT, ">", 1, "T_REDIRECT" },
|
||||
{ T_HEREDOC, "<<", 2, "T_HEREDOC" },
|
||||
{ T_IN_REDIRECT, "<", 1, "T_IN_REDIRECT" },
|
||||
{ T_EOF, "\0", 1, "T_EOF" }
|
||||
};
|
||||
|
||||
const size_t TOKENS_LIST_SZ = sizeof TOKENS_LIST / sizeof *TOKENS_LIST;
|
||||
|
||||
static
|
||||
void search_token(ast_ctx_t *ctx, int *found_token)
|
||||
{
|
||||
for (size_t i = 0; i < TOKENS_LIST_SZ; i++) {
|
||||
if (u_strncmp(ctx->str, TOKENS_LIST[i].str, TOKENS_LIST[i].sz) == 0) {
|
||||
*found_token = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void get_arg_token(ast_ctx_t *ctx, char **start, int *found_token,
|
||||
size_t *arg_len)
|
||||
{
|
||||
char quote = 0;
|
||||
|
||||
if (*ctx->str == '\'' || *ctx->str == '"') {
|
||||
quote = *ctx->str;
|
||||
ctx->str++;
|
||||
*start = ctx->str;
|
||||
while (*ctx->str && *ctx->str != quote)
|
||||
ctx->str++;
|
||||
if (*ctx->str == quote) {
|
||||
*arg_len = ctx->str - *start;
|
||||
ctx->str++;
|
||||
} else
|
||||
*arg_len = ctx->str - *start;
|
||||
*found_token = 1;
|
||||
return;
|
||||
}
|
||||
search_token(ctx, found_token);
|
||||
if (!*found_token)
|
||||
ctx->str++;
|
||||
}
|
||||
|
||||
static
|
||||
int match_known_token(ast_ctx_t *ctx, token_t *token)
|
||||
{
|
||||
for (size_t i = 0; i < TOKENS_LIST_SZ; i++) {
|
||||
if (u_strncmp(ctx->str, TOKENS_LIST[i].str, TOKENS_LIST[i].sz) == 0) {
|
||||
U_DEBUG("Token %-14s [%.*s]\n", TOKENS_LIST[i].name,
|
||||
(int)TOKENS_LIST[i].sz, ctx->str);
|
||||
ctx->str += TOKENS_LIST[i].sz;
|
||||
*token = (token_t){ TOKENS_LIST[i].type,
|
||||
ctx->str - TOKENS_LIST[i].sz, TOKENS_LIST[i].sz };
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
token_t get_next_token(ast_ctx_t *ctx)
|
||||
{
|
||||
char *start;
|
||||
int found_token = 0;
|
||||
size_t arg_len = 0;
|
||||
token_t token;
|
||||
|
||||
while (*ctx->str != '\0' && isblank(*ctx->str))
|
||||
ctx->str++;
|
||||
if (match_known_token(ctx, &token))
|
||||
return token;
|
||||
start = ctx->str;
|
||||
while (*ctx->str && !isblank(*ctx->str) && !found_token)
|
||||
get_arg_token(ctx, &start, &found_token, &arg_len);
|
||||
if (arg_len == 0)
|
||||
arg_len = ctx->str - start;
|
||||
U_DEBUG("Token T_ARG [%.*s]\n", (int)arg_len, start);
|
||||
return (token_t){ .type = T_ARG, .str = start, .sz = arg_len };
|
||||
}
|
||||
164
bonus/visitor.c
164
bonus/visitor.c
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "builtins.h"
|
||||
#include "common.h"
|
||||
#include "exec.h"
|
||||
#include "redirects.h"
|
||||
#include "u_str.h"
|
||||
|
||||
/*
|
||||
* ef->in_fd = ef->pin_fd;
|
||||
* ef->out_fd = ef->out_fd;
|
||||
* set used fds, to not close the wrong fd in visit pipe or handle redirect
|
||||
*/
|
||||
static
|
||||
int visit_cmd(ef_t *ef)
|
||||
{
|
||||
int result;
|
||||
|
||||
ef->in_fd = ef->pin_fd;
|
||||
ef->out_fd = ef->pout_fd;
|
||||
ef->skip_i = 0;
|
||||
ef->skip_sz = 0;
|
||||
ef->rout_fd = 0;
|
||||
ef->rin_fd = 0;
|
||||
for (size_t i = 0; i < ef->act_node->vector.sz; i++) {
|
||||
if (!handle_in_redirect(ef, ef->act_node, i, ef->act_node->vector.sz)
|
||||
|| !handle_out_redirect(ef, ef->act_node, i, ef->act_node->vector.sz)
|
||||
|| !handle_heredoc(ef, ef->act_node, i, ef->act_node->vector.sz))
|
||||
return -1;
|
||||
}
|
||||
result = execute(ef);
|
||||
if (ef->rout_fd)
|
||||
close(ef->rout_fd);
|
||||
if (ef->rin_fd)
|
||||
close(ef->rin_fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
int visit_pipe(ef_t *ef, size_t i, ast_t *node)
|
||||
{
|
||||
int result = RETURN_FAILURE;
|
||||
|
||||
if (i < node->list.sz - 1)
|
||||
if (pipe(ef->pipes) < 0)
|
||||
return (puterror("pipe"), -1);
|
||||
ef->pout_fd = i == node->list.sz - 1 ? STDOUT_FILENO : ef->pipes[1];
|
||||
ef->act_node = node->list.nodes[i];
|
||||
if (!ef->act_node)
|
||||
return -1;
|
||||
result = visit_cmd(ef);
|
||||
if (result == -1)
|
||||
return RETURN_FAILURE;
|
||||
if (ef->pin_fd != STDIN_FILENO)
|
||||
close(ef->pin_fd);
|
||||
if (i < node->list.sz - 1) {
|
||||
close(ef->pipes[1]);
|
||||
ef->pin_fd = ef->pipes[0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
int visit_pipes(ef_t *ef)
|
||||
{
|
||||
ast_t *node = ef->act_node;
|
||||
int result = RETURN_FAILURE;
|
||||
|
||||
ef->p_i = 0;
|
||||
ef->p_sz = node->list.sz;
|
||||
ef->pin_fd = STDIN_FILENO;
|
||||
ef->flags |= F_PIPE;
|
||||
for (size_t i = 0; i < node->list.sz; i++) {
|
||||
ef->p_i = i;
|
||||
result = visit_pipe(ef, i, node);
|
||||
if (result == -1)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
int visit_list(ef_t *ef, ast_t *node)
|
||||
{
|
||||
int result = RETURN_FAILURE;
|
||||
|
||||
if (node->tok.type == T_PIPE) {
|
||||
ef->act_node = node;
|
||||
return visit_pipes(ef);
|
||||
}
|
||||
for (size_t i = 0; i < node->list.sz; i++) {
|
||||
ef->flags &= ~F_PIPE;
|
||||
ef->act_node = node->list.nodes[i];
|
||||
if (node->list.nodes[i]->type == N_LST &&
|
||||
node->list.nodes[i]->tok.type == T_PIPE)
|
||||
result = visit_pipes(ef);
|
||||
ef->pin_fd = STDIN_FILENO;
|
||||
ef->pout_fd = STDOUT_FILENO;
|
||||
if (node->list.nodes[i]->type == N_CMD)
|
||||
result = visit_cmd(ef);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
int visitor_launcher(ef_t *ef)
|
||||
{
|
||||
int result = RETURN_FAILURE;
|
||||
|
||||
ef->ctx->act_tok = get_next_token(ef->ctx);
|
||||
ef->ctx->ast = parse_expression(ef->ctx);
|
||||
if (ef->ctx->ast == NULL)
|
||||
return RETURN_FAILURE;
|
||||
if (ef->ctx->ast->type == N_LST)
|
||||
result = visit_list(ef, ef->ctx->ast);
|
||||
if (ef->ctx->ast->type == N_CMD) {
|
||||
ef->act_node = ef->ctx->ast;
|
||||
result = visit_cmd(ef);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
void remove_trailing_semi(char *str)
|
||||
{
|
||||
for (size_t len = u_strlen(str); len > 0; len--) {
|
||||
if (str[len] != '\0' && str[len] != '\n' && str[len] != ';')
|
||||
break;
|
||||
if (str[len] == ';')
|
||||
str[len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
int visitor(char *buffer, env_t *env, history_t *history)
|
||||
{
|
||||
ast_ctx_t ctx = { 0, .str = buffer, .cap = u_strlen(buffer) + 10,
|
||||
.ast = malloc(sizeof *ctx.ast * (u_strlen(buffer) + 10)) };
|
||||
ef_t ef = { .buffer = buffer, .env = env,
|
||||
.history = history, .ctx = &ctx, .pout_fd = STDOUT_FILENO,
|
||||
.flags = 0, 0 };
|
||||
int result = RETURN_FAILURE;
|
||||
|
||||
ctx.first_node = ctx.ast;
|
||||
remove_trailing_semi(ctx.str);
|
||||
history->last_exit_code = 0;
|
||||
if (ctx.ast == NULL)
|
||||
return RETURN_FAILURE;
|
||||
result = visitor_launcher(&ef);
|
||||
if (ef.flags & F_EXIT)
|
||||
builtins_exit(&ef, NULL);
|
||||
free_ast(&ctx);
|
||||
return result == -1 ? RETURN_FAILURE : result;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
** 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
|
||||
Reference in New Issue
Block a user