mirror of
https://github.com/Savapitech/42sh.git
synced 2026-03-18 21:50:35 +01:00
Merge branch 'main' into builtins_adds
This commit is contained in:
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
@@ -43,9 +43,6 @@ jobs:
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@v4
|
||||
|
||||
- name: Run the Magic Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@v2
|
||||
|
||||
- name: Run coding style checker
|
||||
run: |
|
||||
nix run github:Sigmapitech/cs \
|
||||
@@ -55,6 +52,8 @@ jobs:
|
||||
- name: Build project
|
||||
run: make
|
||||
|
||||
- name: Run tester
|
||||
run: ./validator.py
|
||||
|
||||
sync_repository:
|
||||
needs: [ check_the_repository_state ]
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -46,3 +46,5 @@ unit_tests
|
||||
|
||||
# AFL
|
||||
afl/generated
|
||||
|
||||
out.txt
|
||||
|
||||
2
Makefile
2
Makefile
@@ -35,7 +35,7 @@ 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 += -Wno-discarded-qualifiers
|
||||
CFLAGS += -Wno-discarded-qualifiers --std=gnu2x
|
||||
|
||||
LDFLAGS += -L .
|
||||
LDLIBS := -lu
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
- [ ] `time`
|
||||
- [ ] `trap`
|
||||
- [ ] `wc`
|
||||
- [ ] `which`/`where`
|
||||
- [x] `which`/`where`
|
||||
- [x] `yes`
|
||||
|
||||
- [x] pipes
|
||||
@@ -41,8 +41,7 @@
|
||||
- [ ] `-n` (dry run mode)
|
||||
- [ ] `-h` help (open man?)
|
||||
- [ ] autocompletion of commands
|
||||
- [x] globbing (*)
|
||||
- [ ] globbing
|
||||
- [x] globbing
|
||||
- [ ] var interpreter
|
||||
- [ ] inhibitor
|
||||
- [ ] magic quotes
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
|
||||
[reports]
|
||||
merge = "multiplier"
|
||||
|
||||
@@ -4,29 +4,27 @@
|
||||
** File description:
|
||||
** parse_alias
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "common.h"
|
||||
#include "env.h"
|
||||
#include "exec.h"
|
||||
#include "u_mem.h"
|
||||
#include "u_str.h"
|
||||
#include "history.h"
|
||||
#include "alias.h"
|
||||
#include "common.h"
|
||||
#include "history.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int skip_blank(char *buffer, int i)
|
||||
static
|
||||
int skip_blank(char *buffer, int i)
|
||||
{
|
||||
for (; buffer[i] != 0 && isblank(buffer[i]); i++);
|
||||
return i;
|
||||
}
|
||||
|
||||
static int skip_to_next_token(char *buffer, int i)
|
||||
static
|
||||
int skip_to_next_token(char *buffer, int i)
|
||||
{
|
||||
for (; buffer[i] != 0 && is_a_token(buffer, i) == false; i++);
|
||||
return i;
|
||||
@@ -97,3 +95,18 @@ int parse_alias(char **buffer, size_t *buffer_len, alias_t *alias)
|
||||
need_to_replace = replace_alias(buffer, alias);
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
alias_t init_alias(void)
|
||||
{
|
||||
alias_t alias;
|
||||
|
||||
alias.size = 1;
|
||||
alias.alias_array = malloc(sizeof(char *) * alias.size);
|
||||
alias.alias_to_replace = malloc(sizeof(char *) * alias.size);
|
||||
if (!alias.alias_array || !alias.alias_to_replace)
|
||||
return alias;
|
||||
alias.alias_array[0] = NULL;
|
||||
alias.alias_to_replace[0] = NULL;
|
||||
alias.size = 0;
|
||||
return alias;
|
||||
}
|
||||
@@ -7,9 +7,7 @@
|
||||
|
||||
#ifndef ALIAS_H
|
||||
#define ALIAS_H
|
||||
#include "env.h"
|
||||
#include "history.h"
|
||||
#include "shell.h"
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct alias_s {
|
||||
size_t size;
|
||||
@@ -19,5 +17,5 @@ typedef struct alias_s {
|
||||
|
||||
void free_alias(alias_t *alias);
|
||||
int parse_alias(char **buffer, size_t *buffer_len, alias_t *alias);
|
||||
|
||||
alias_t init_alias(void);
|
||||
#endif /* ALIAS*/
|
||||
|
||||
19
src/ast.h
19
src/ast.h
@@ -19,7 +19,7 @@
|
||||
#define IF_PROMPT "if? "
|
||||
#define T_ALL 0xff
|
||||
|
||||
typedef enum {
|
||||
typedef enum : size_t {
|
||||
T_SEMICOLON = 1 << 0, // ;
|
||||
T_LEFT_QUOTE = 1 << 1, // "
|
||||
T_RIGHT_QUOTE = 1 << 2, // "
|
||||
@@ -37,13 +37,16 @@ typedef enum {
|
||||
T_IN_REDIRECT = 1 << 14, // <
|
||||
T_AT = 1 << 15, // <
|
||||
T_WHILE = 1 << 16, // while
|
||||
T_IF = 1 << 17, // if
|
||||
T_THEN = 1 << 18, // then
|
||||
T_ELSE = 1 << 19, // else
|
||||
T_ENDIF = 1 << 20, // endif
|
||||
T_EOF = 1 << 21, // \0
|
||||
T_ARG = 1 << 22,
|
||||
T_INVALID = 1 << 23
|
||||
T_FOREACH = 1 << 17, // foreach
|
||||
T_IF = 1 << 18, // if
|
||||
T_THEN = 1 << 19, // then
|
||||
T_ELSE = 1 << 20, // else
|
||||
T_ENDIF = 1 << 21, // endif
|
||||
T_STAR = 1 << 22, // *
|
||||
T_NEWLINE = 1 << 23, // \n
|
||||
T_EOF = 1 << 24, // \0
|
||||
T_ARG = 1 << 25,
|
||||
T_INVALID = 1 << 26
|
||||
} token_type_t;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -20,8 +20,12 @@ 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.str == '\\') {
|
||||
ctx->act_tok = get_next_token(ctx);
|
||||
ctx->act_tok.type = T_ARG;
|
||||
}
|
||||
if (ctx->act_tok.type & (T_ARG | T_REDIRECT | T_APPEND |
|
||||
T_IN_REDIRECT | T_HEREDOC | T_VAR)) {
|
||||
T_IN_REDIRECT | T_HEREDOC | T_VAR | T_STAR)) {
|
||||
if (!ensure_node_cap(node))
|
||||
return NULL;
|
||||
node->vector.tokens[node->vector.sz] = ctx->act_tok;
|
||||
@@ -159,15 +163,15 @@ ast_t *create_semi_node(ast_ctx_t *ctx, ast_t *l_node)
|
||||
static
|
||||
ast_t *fill_semi_node(ast_ctx_t *ctx, ast_t *node)
|
||||
{
|
||||
while (ctx->act_tok.type == T_SEMICOLON) {
|
||||
while (ctx->act_tok.type & (T_SEMICOLON | T_NEWLINE)) {
|
||||
ctx->act_tok = get_next_token(ctx);
|
||||
if (ctx->act_tok.type == T_SEMICOLON)
|
||||
if (ctx->act_tok.type & (T_SEMICOLON | T_NEWLINE))
|
||||
continue;
|
||||
if (!ensure_list_cap(node))
|
||||
return false;
|
||||
return NULL;
|
||||
node->list.nodes[node->list.sz] = parse_semi(ctx);
|
||||
if (node->list.nodes[node->list.sz] == NULL)
|
||||
return false;
|
||||
return NULL;
|
||||
node->list.sz++;
|
||||
}
|
||||
return node;
|
||||
@@ -187,7 +191,7 @@ ast_t *parse_expression(ast_ctx_t *ctx)
|
||||
l_node = parse_semi(ctx);
|
||||
if (l_node == NULL)
|
||||
return ctx->ast;
|
||||
if (ctx->act_tok.type == T_SEMICOLON) {
|
||||
if (ctx->act_tok.type & (T_SEMICOLON | T_NEWLINE)) {
|
||||
node = create_semi_node(ctx, l_node);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
|
||||
@@ -32,6 +32,8 @@ const tokens_list_t TOKENS_LIST[] = {
|
||||
{ T_THEN, "then", 4, "T_THEN"},
|
||||
{ T_ELSE, "else", 4, "T_ELSE"},
|
||||
{ T_ENDIF, "endif", 5, "T_ENDIF"},
|
||||
{ T_NEWLINE, "\n", 1, "T_NEWLINE"},
|
||||
{ T_STAR, "*", 1, "T_STAR"},
|
||||
{ T_EOF, "\0", 1, "T_EOF" }
|
||||
};
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
bool check_local_var(char *var, char *func_name)
|
||||
{
|
||||
if (!isalpha(var[0]))
|
||||
return (fprintf(stdout, "%s: Variable name must begin"
|
||||
return (fprintf(stderr, "%s: Variable name must begin"
|
||||
" with a letter.\n", func_name), RETURN_FAILURE);
|
||||
if (!u_str_is_only_alnum(var))
|
||||
return (fprintf(stdout, "%s: Variable name must contain"
|
||||
return (fprintf(stderr, "%s: Variable name must contain"
|
||||
" alphanumeric characters.\n", func_name), RETURN_FAILURE);
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -8,17 +8,21 @@
|
||||
|
||||
#ifndef BUILTINS_HANDLER_H
|
||||
#define BUILTINS_HANDLER_H
|
||||
#include <termios.h>
|
||||
|
||||
#include "alias.h"
|
||||
#include "env.h"
|
||||
#include "history.h"
|
||||
#include "shell.h"
|
||||
#include "alias.h"
|
||||
#include "local.h"
|
||||
#include "shell.h"
|
||||
|
||||
typedef struct {
|
||||
env_t *env;
|
||||
history_t *history;
|
||||
his_command_t *history_command;
|
||||
alias_t *alias;
|
||||
bool is_running;
|
||||
struct termios saved_term_settings;
|
||||
local_t *local;
|
||||
} exec_ctx_t;
|
||||
|
||||
|
||||
@@ -7,8 +7,12 @@
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
#include "exec.h"
|
||||
|
||||
enum {
|
||||
RETURN_SUCCESS = 0,
|
||||
RETURN_FAILURE = 1
|
||||
};
|
||||
|
||||
void free_everything(exec_ctx_t *exec_ctx);
|
||||
#endif /* COMMON_H */
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "path.h"
|
||||
#include "u_mem.h"
|
||||
#include "u_str.h"
|
||||
#include "alias.h"
|
||||
|
||||
const builtins_funcs_t BUILTINS[] = {
|
||||
{ "builtins", &builtins_builtins },
|
||||
|
||||
@@ -29,15 +29,17 @@ bool process_globbing(char *pattern, args_t *args)
|
||||
{
|
||||
glob_t globs;
|
||||
int glob_result;
|
||||
char *vl;
|
||||
|
||||
glob_result = glob(pattern, GLOB_ERR, NULL, &globs);
|
||||
if (!check_glob_result(glob_result, args->args[0]))
|
||||
return false;
|
||||
for (size_t i = 0; i < globs.gl_pathc; i++) {
|
||||
ensure_args_capacity(args);
|
||||
args->args[args->sz] = strdup(globs.gl_pathv[i]);
|
||||
if (args->args[args->sz] == NULL)
|
||||
vl = strdup(globs.gl_pathv[i]);
|
||||
if (vl == NULL)
|
||||
return globfree(&globs), false;
|
||||
args->args[args->sz] = vl;
|
||||
args->sz++;
|
||||
}
|
||||
globfree(&globs);
|
||||
@@ -48,7 +50,11 @@ bool process_args(ast_t *node, args_t *args, size_t *toks_i, ef_t *ef)
|
||||
{
|
||||
token_t tok = node->vector.tokens[*toks_i];
|
||||
|
||||
if (strchr(tok.str, '*') != NULL)
|
||||
if (strchr(tok.str, '\\') != NULL) {
|
||||
args->args[args->sz] = tok.str;
|
||||
return true;
|
||||
}
|
||||
if (tok.type == T_STAR || strcspn(tok.str, "[]?") != strlen(tok.str))
|
||||
return (process_globbing(tok.str, args));
|
||||
if (!ensure_args_capacity(args))
|
||||
return false;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "env.h"
|
||||
@@ -22,7 +23,7 @@ char *handle_var_case(ast_t *node, exec_ctx_t *ctx, size_t *i)
|
||||
if (r_char == NULL)
|
||||
r_char = get_local_value(ctx->local, node->vector.tokens[*i].str);
|
||||
if (r_char == NULL) {
|
||||
printf("%s: Undefined variable.\n",
|
||||
dprintf(STDERR_FILENO, "%s: Undefined variable.\n",
|
||||
node->vector.tokens[*i].str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
118
src/readline.c
Normal file
118
src/readline.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "readline.h"
|
||||
#include "u_str.h"
|
||||
|
||||
static
|
||||
bool str_printable(char const *str, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; i++)
|
||||
if (!isprint(str[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool ensure_buff_av_capacity(buff_t *buff, size_t requested)
|
||||
{
|
||||
char *new_str;
|
||||
size_t endsize = BUFF_INIT_SZ;
|
||||
|
||||
if ((buff->sz + requested) < buff->cap)
|
||||
return true;
|
||||
for (; endsize < buff->sz + requested; endsize <<= 1);
|
||||
if (endsize > buff->cap) {
|
||||
new_str = realloc(buff->str, (sizeof *buff->str) * endsize);
|
||||
if (new_str == NULL)
|
||||
return false;
|
||||
buff->str = new_str;
|
||||
buff->cap = endsize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool ensure_buff_capacity(buff_t *buff)
|
||||
{
|
||||
char *new_str;
|
||||
|
||||
if (buff->str == NULL) {
|
||||
new_str = malloc((sizeof *buff->str) * BUFF_INIT_SZ);
|
||||
if (new_str == NULL)
|
||||
return false;
|
||||
buff->str = new_str;
|
||||
buff->cap = BUFF_INIT_SZ;
|
||||
}
|
||||
if (buff->sz == buff->cap) {
|
||||
new_str = realloc(buff->str, (sizeof *buff->str) * buff->cap << 1);
|
||||
if (new_str == NULL)
|
||||
return false;
|
||||
buff->str = new_str;
|
||||
buff->cap <<= 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool append_null_terminator(buff_t *buff)
|
||||
{
|
||||
if (!ensure_buff_av_capacity(buff, 1))
|
||||
return false;
|
||||
buff->str[buff->sz - 1] = '\0';
|
||||
buff->sz++;
|
||||
if (isatty(STDIN_FILENO))
|
||||
WRITE_CONST(STDOUT_FILENO, "\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
int8_t handle_line_buff(buff_t *buff, char *read_buff, ssize_t read_size)
|
||||
{
|
||||
if (*read_buff == CTRL('d')) {
|
||||
buff->sz = 0;
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
if (isatty(STDIN_FILENO) && str_printable(read_buff, read_size))
|
||||
write(STDOUT_FILENO, read_buff, read_size);
|
||||
if (!ensure_buff_av_capacity(buff, read_size))
|
||||
return RETURN_FAILURE;
|
||||
strncpy(buff->str + buff->sz,
|
||||
read_buff, read_size);
|
||||
buff->sz += read_size;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool readline(buff_t *buff)
|
||||
{
|
||||
char read_buff[2] = "";
|
||||
ssize_t read_size = 0;
|
||||
|
||||
if (!ensure_buff_capacity(buff))
|
||||
return false;
|
||||
while (strchr(read_buff, '\n') == NULL && *read_buff != '\r') {
|
||||
memset(read_buff, '\0', sizeof read_buff);
|
||||
read_size = read(STDIN_FILENO, &read_buff, sizeof read_buff - 1);
|
||||
if (read_size < 0)
|
||||
return false;
|
||||
if (read_size == 0)
|
||||
return true;
|
||||
if (handle_line_buff(buff, read_buff, read_size) > -1)
|
||||
return true;
|
||||
}
|
||||
return append_null_terminator(buff);
|
||||
}
|
||||
16
src/readline.h
Normal file
16
src/readline.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#ifndef READLINE
|
||||
#define READLINE
|
||||
#define BUFF_INIT_SZ 16
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "u_str.h"
|
||||
|
||||
bool readline(buff_t *buff);
|
||||
#endif /* READLINE */
|
||||
92
src/shell.c
92
src/shell.c
@@ -11,15 +11,16 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "builtins_handler.h"
|
||||
#include "common.h"
|
||||
#include "debug.h"
|
||||
#include "env.h"
|
||||
#include "history.h"
|
||||
#include "local.h"
|
||||
#include "readline.h"
|
||||
#include "shell.h"
|
||||
#include "u_str.h"
|
||||
#include "local.h"
|
||||
#include "loop.h"
|
||||
#include "visitor.h"
|
||||
|
||||
__attribute__((unused))
|
||||
static
|
||||
@@ -58,44 +59,60 @@ void write_prompt(int is_a_tty)
|
||||
}
|
||||
|
||||
static
|
||||
bool change_shell_command(char **buffer, exec_ctx_t *exec_ctx,
|
||||
size_t buffer_sz)
|
||||
bool change_shell_command(buff_t *buff, exec_ctx_t *exec_ctx)
|
||||
{
|
||||
size_t buffer_len = 0;
|
||||
char *tmp_buff = NULL;
|
||||
size_t buffer_len;
|
||||
|
||||
if (getline(buffer, &buffer_sz, stdin) == -1)
|
||||
return true;
|
||||
tmp_buff = (*buffer);
|
||||
buffer_len = update_command(&tmp_buff, &buffer_sz, exec_ctx);
|
||||
buff->sz = 0;
|
||||
if (!readline(buff))
|
||||
return false;
|
||||
if (!buff->sz)
|
||||
return false;
|
||||
tmp_buff = buff->str;
|
||||
buffer_len = update_command(&tmp_buff, &buff->sz, exec_ctx);
|
||||
if (buffer_len == 0)
|
||||
return false;
|
||||
if (buffer_len < 1 || !u_str_is_alnum(tmp_buff)) {
|
||||
check_basic_error(tmp_buff);
|
||||
free(tmp_buff);
|
||||
return false;
|
||||
return true;
|
||||
if (buffer_len < 1 || !u_str_is_alnum(tmp_buff))
|
||||
return check_basic_error(tmp_buff), true;
|
||||
U_DEBUG("Buffer [%lu] [%s]\n", buffer_len, tmp_buff);
|
||||
if (visitor(tmp_buff, exec_ctx) == RETURN_FAILURE
|
||||
&& !exec_ctx->history->last_exit_code)
|
||||
exec_ctx->history->last_exit_code = RETURN_FAILURE;
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
void init_shell_repl(exec_ctx_t *exec_ctx)
|
||||
{
|
||||
struct termios repl_settings;
|
||||
|
||||
exec_ctx->is_running = true;
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
tcgetattr(STDIN_FILENO, &repl_settings);
|
||||
exec_ctx->saved_term_settings = repl_settings;
|
||||
repl_settings.c_iflag = IXON;
|
||||
repl_settings.c_lflag = ~(ECHO | ICANON);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &repl_settings);
|
||||
}
|
||||
U_DEBUG("Buffer [%lu] [%s]\n", buffer_len, buffer);
|
||||
visitor(tmp_buff, exec_ctx);
|
||||
free(tmp_buff);
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
int shell_loop(int is_a_tty, exec_ctx_t *exec_ctx)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
size_t buffer_sz = 0;
|
||||
buff_t buff = { .str = NULL, 0, .cap = BUFF_INIT_SZ };
|
||||
|
||||
init_shell_repl(exec_ctx);
|
||||
while (true) {
|
||||
write_prompt(is_a_tty);
|
||||
if (change_shell_command(&buffer, exec_ctx, buffer_sz) == true)
|
||||
if (!change_shell_command(&buff, exec_ctx))
|
||||
return exec_ctx->history->last_exit_code;
|
||||
}
|
||||
free(exec_ctx->history_command);
|
||||
return (free(buffer), exec_ctx->history->last_exit_code);
|
||||
return free(buff.str), exec_ctx->history->last_exit_code;
|
||||
}
|
||||
|
||||
static
|
||||
his_command_t *init_cmd_history(void)
|
||||
{
|
||||
his_command_t *cmd_history = malloc(sizeof(his_command_t) * 100);
|
||||
@@ -126,41 +143,26 @@ bool error_in_init(exec_ctx_t *exec_ctx)
|
||||
return false;
|
||||
}
|
||||
|
||||
alias_t init_alias(void)
|
||||
{
|
||||
alias_t alias;
|
||||
|
||||
alias.size = 1;
|
||||
alias.alias_array = malloc(sizeof(char *) * alias.size);
|
||||
alias.alias_to_replace = malloc(sizeof(char *) * alias.size);
|
||||
if (!alias.alias_array || !alias.alias_to_replace)
|
||||
return alias;
|
||||
alias.alias_array[0] = NULL;
|
||||
alias.alias_to_replace[0] = NULL;
|
||||
alias.size = 0;
|
||||
return alias;
|
||||
}
|
||||
|
||||
int shell(char **env_ptr)
|
||||
{
|
||||
alias_t alias = init_alias();
|
||||
env_t env = parse_env(env_ptr);
|
||||
history_t history = { .cmd_history = NULL, 0, .last_chdir = NULL};
|
||||
history_t history = { .cmd_history = NULL, .last_exit_code = 0,
|
||||
.last_chdir = NULL};
|
||||
his_command_t *cmd_history = init_cmd_history();
|
||||
local_t local = create_local();
|
||||
exec_ctx_t exec_ctx = {.env = &env, .local = &local,
|
||||
.history = &history, .history_command = cmd_history, .alias = &alias};
|
||||
int shell_result;
|
||||
|
||||
if (error_in_init(&exec_ctx) == true){
|
||||
if (error_in_init(&exec_ctx) == true)
|
||||
return RETURN_FAILURE;
|
||||
}
|
||||
U_DEBUG_CALL(debug_env_entries, &env);
|
||||
signal(SIGINT, ignore_sigint);
|
||||
shell_result = shell_loop(isatty(STDIN_FILENO), &exec_ctx);
|
||||
if (isatty(STDIN_FILENO))
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
WRITE_CONST(STDOUT_FILENO, "exit\n");
|
||||
free_env(exec_ctx.env);
|
||||
free_alias(exec_ctx.alias);
|
||||
return shell_result;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &exec_ctx.saved_term_settings);
|
||||
}
|
||||
return free_everything(&exec_ctx), shell_result;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef SHELL_H
|
||||
#define SHELL_H
|
||||
#include "vt100_esc_codes.h"
|
||||
|
||||
#define SHELL_PROMPT RED "|> " RESET
|
||||
|
||||
typedef struct {
|
||||
@@ -15,5 +16,6 @@ typedef struct {
|
||||
int last_exit_code;
|
||||
char *last_chdir;
|
||||
} history_t;
|
||||
|
||||
int shell(char **env);
|
||||
#endif /* SHELL_H */
|
||||
|
||||
@@ -41,7 +41,6 @@ size_t update_command(char **buffer,
|
||||
buffer_len = u_strlen(*buffer);
|
||||
if (buffer_len < 2)
|
||||
return RETURN_FAILURE;
|
||||
(*buffer)[buffer_len - 1] = '\0';
|
||||
if (parse_history(buffer, &buffer_len,
|
||||
buffer_sz, &exec_ctx->history_command) == 84)
|
||||
return RETURN_SUCCESS;
|
||||
|
||||
17
src/utils/free.c
Normal file
17
src/utils/free.c
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "exec.h"
|
||||
|
||||
void free_everything(exec_ctx_t *exec_ctx)
|
||||
{
|
||||
free_env(exec_ctx->env);
|
||||
free_alias(exec_ctx->alias);
|
||||
free(exec_ctx->history_command);
|
||||
}
|
||||
@@ -146,7 +146,7 @@ int visit_expression(ef_t *ef, ast_t *node)
|
||||
{
|
||||
int result = RETURN_FAILURE;
|
||||
|
||||
if (node->tok.type == T_SEMICOLON)
|
||||
if (node->tok.type & (T_SEMICOLON | T_NEWLINE))
|
||||
result = visit_semi(ef, node);
|
||||
if (node->tok.type & (T_IF | T_AND | T_OR))
|
||||
result = visit_condition(ef, node);
|
||||
@@ -178,6 +178,8 @@ void remove_trailing_semi(char *str)
|
||||
break;
|
||||
if (str[len] == ';')
|
||||
str[len] = '\0';
|
||||
if (str[len] == '\n')
|
||||
str[len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ TESTS = [
|
||||
key="PATH",
|
||||
name="path handing",
|
||||
cmds=[
|
||||
"/bin/sh --version\n",
|
||||
"/../bin/sh --version\n",
|
||||
"~/../../bin/sh --version\n",
|
||||
"/bin/ls\n",
|
||||
"/../bin/ls\n",
|
||||
# "~/../../bin/sh --version\n",
|
||||
"fixtures/exec.sh\n",
|
||||
],
|
||||
depends_on=("ARGS",)
|
||||
@@ -101,4 +101,75 @@ TESTS = [
|
||||
],
|
||||
depends_on=("ARGS",)
|
||||
),
|
||||
|
||||
Test(
|
||||
key="REDIR",
|
||||
name="I/O redirections",
|
||||
cmds=[
|
||||
"echo plop > out.txt\n",
|
||||
"cat < out.txt\n",
|
||||
"echo again >> out.txt\n",
|
||||
"cat < out.txt\n",
|
||||
"cat nofile.txt\n",
|
||||
"echo test > /no/perm/file\n",
|
||||
],
|
||||
depends_on=("ARGS",)
|
||||
),
|
||||
|
||||
Test(
|
||||
key="PIPE",
|
||||
name="pipes",
|
||||
cmds=[
|
||||
"echo plop | cat\n",
|
||||
"echo kek | grep kek\n",
|
||||
"ls | grep Makefile\n",
|
||||
"who | wc -l\n",
|
||||
"ls | | cat\n", # Syntax error
|
||||
],
|
||||
depends_on=("ARGS",)
|
||||
),
|
||||
|
||||
Test(
|
||||
key="ENV_EXPANSION",
|
||||
name="environment variable expansion",
|
||||
cmds=[
|
||||
"echo $HOME\n",
|
||||
"echo $PATH\n",
|
||||
"setenv TESTVAR bonjour;echo $TESTVAR;unsetenv TESTVAR;echo $TESTVAR\n",
|
||||
],
|
||||
depends_on=("ENV","SEMICOLON",)
|
||||
),
|
||||
|
||||
Test(
|
||||
key="EXIT",
|
||||
name="exit command",
|
||||
cmds=[
|
||||
"exit\n",
|
||||
],
|
||||
depends_on=("ARGS",)
|
||||
),
|
||||
|
||||
Test(
|
||||
key="MULTICMD",
|
||||
name="multiple commands per line",
|
||||
cmds=[
|
||||
"echo one ; echo two ; echo three\n",
|
||||
"ls ; pwd ; whoami\n",
|
||||
"cd src/ ; pwd ; cd - ; pwd\n",
|
||||
],
|
||||
depends_on=("SEMICOLON",)
|
||||
),
|
||||
|
||||
Test(
|
||||
key="PARSING_ERR",
|
||||
name="bad parsing cases",
|
||||
cmds=[
|
||||
"ls |\n",
|
||||
"ls >\n",
|
||||
"; ls\n",
|
||||
";; ls\n",
|
||||
"ls ;; ls\n",
|
||||
],
|
||||
depends_on=("PIPE", "REDIR", "SEMICOLON",)
|
||||
),
|
||||
]
|
||||
|
||||
24
validator.py
Normal file → Executable file
24
validator.py
Normal file → Executable file
@@ -1,8 +1,12 @@
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i python3 -p python3 tcsh
|
||||
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
|
||||
import difflib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -84,21 +88,25 @@ class Test:
|
||||
print("\033[31m.\033[0m", end='') # ]]
|
||||
return cmd, result_42sh, result_tcsh
|
||||
|
||||
def run(self, test_map):
|
||||
def run(self, test_map) -> bool:
|
||||
if self.has_run:
|
||||
return
|
||||
return True
|
||||
|
||||
self.has_run = True
|
||||
|
||||
success = True
|
||||
for dep_name in self.depends_on:
|
||||
dep = test_map.get(dep_name)
|
||||
|
||||
if dep is None:
|
||||
print("\033[33mOKWarning\033[0m:" # ]]
|
||||
print("\033[33mWarning\033[0m:" # ]]
|
||||
"Missing dependency:", dep_name)
|
||||
continue
|
||||
|
||||
if not dep.has_run:
|
||||
dep.run(test_map)
|
||||
success &= dep.run(test_map)
|
||||
if not success:
|
||||
return False
|
||||
|
||||
print(self.name, end=" ")
|
||||
failures = []
|
||||
@@ -108,19 +116,23 @@ class Test:
|
||||
failures.append(failure)
|
||||
if not failures:
|
||||
print(" \033[32mOK\033[0m") # ]]
|
||||
return True
|
||||
else:
|
||||
print()
|
||||
for fail in failures:
|
||||
print_diff(*fail)
|
||||
return False
|
||||
|
||||
def main():
|
||||
from validation_tests import TESTS
|
||||
|
||||
test_map = {test.key: test for test in TESTS}
|
||||
|
||||
success = True
|
||||
for test in TESTS:
|
||||
test.run(test_map=test_map)
|
||||
success &= test.run(test_map=test_map)
|
||||
|
||||
return not success
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
sys.exit(main())
|
||||
|
||||
Reference in New Issue
Block a user