Merge pull request #16 from Savapitech/Line_edition_S

Basic line edition
This commit is contained in:
savalet
2025-04-29 12:38:07 +02:00
committed by GitHub
20 changed files with 266 additions and 77 deletions

View File

@@ -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

View File

@@ -1 +1,2 @@
[reports]
merge = "multiplier"

5
iftest.sh Normal file
View File

@@ -0,0 +1,5 @@
if ls
echo YES
else
echo NO
endif

3
out.txt Normal file
View File

@@ -0,0 +1,3 @@
plop
again
again

View File

@@ -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;
}

View File

@@ -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*/

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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" }
};

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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
View 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
View 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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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
View 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);
}

View File

@@ -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';
}
}

View File

@@ -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",)