mirror of
https://github.com/Savapitech/42sh.git
synced 2026-01-19 01:27:51 +01:00
Merge pull request #16 from Savapitech/Line_edition_S
Basic line edition
This commit is contained in:
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
|
||||
|
||||
@@ -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*/
|
||||
|
||||
20
src/ast.h
20
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,14 +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_STAR = 1 << 21, // *
|
||||
T_EOF = 1 << 22, // \0
|
||||
T_ARG = 1 << 23,
|
||||
T_INVALID = 1 << 24
|
||||
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 {
|
||||
|
||||
@@ -165,15 +165,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;
|
||||
@@ -193,7 +193,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;
|
||||
|
||||
@@ -33,6 +33,7 @@ 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" }
|
||||
};
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
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 */
|
||||
76
src/shell.c
76
src/shell.c
@@ -11,14 +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 "visitor.h"
|
||||
|
||||
__attribute__((unused))
|
||||
static
|
||||
@@ -57,45 +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)
|
||||
buff->sz = 0;
|
||||
if (!readline(buff))
|
||||
return false;
|
||||
tmp_buff = (*buffer);
|
||||
buffer_len = update_command(&tmp_buff, &buffer_sz, exec_ctx);
|
||||
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 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)
|
||||
if (visitor(tmp_buff, exec_ctx) == RETURN_FAILURE
|
||||
&& !exec_ctx->history->last_exit_code)
|
||||
exec_ctx->history->last_exit_code = RETURN_FAILURE;
|
||||
free(tmp_buff);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
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,21 +143,6 @@ 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();
|
||||
@@ -158,9 +160,9 @@ int shell(char **env_ptr)
|
||||
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);
|
||||
}
|
||||
@@ -148,7 +148,7 @@ int visit_expression(ef_t *ef, ast_t *node)
|
||||
|
||||
if (node->type == N_LOP)
|
||||
result = visit_loop(ef, node);
|
||||
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);
|
||||
@@ -180,6 +180,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",)
|
||||
|
||||
Reference in New Issue
Block a user