Merge branch 'main' into magic_quotes

This commit is contained in:
Syvolc
2025-05-05 13:39:47 +02:00
committed by GitHub
48 changed files with 743 additions and 453 deletions

View File

@@ -81,8 +81,7 @@ all: $(NAME_release)
.PHONY: tests_run
tests_run: $(NAME_test)
- find fixtures -name "*.sh" | xargs -i \
sh -c 'cat {} | env -i PATH="$(dir $(shell which ls))" ./$^'
@ - python3 validator.py ./$(NAME_test)
.PHONY: cov
cov: tests_run

View File

@@ -28,10 +28,9 @@
- [x] redirections
- [ ] line editing (move, del, multi-line)
- [ ] input shortcut (^A,^E,^R,^V,^F,^B,...)
- [ ] color highlighting in input
- [ ] foreground/background jobs (`&`), (btln `fg`, `bg`, `jobs`)
- [ ] while/for/foreach loops
- [ ] if / cases
- [x] while/foreach loops
- [x] if
- [x] `||`/`&&`
- [ ] configuration file
- [x] script with shebangs
@@ -40,19 +39,13 @@
- [ ] `-e` exit on failure
- [ ] `-n` (dry run mode)
- [ ] `-h` help (open man?)
- [ ] autocompletion of commands
- [x] globbing
- [ ] var interpreter
- [ ] inhibitor
- [ ] magic quotes
- [ ] parenthesis (sub-commands)
- [ ] direnv/wakatime integration
- [x] var interpreter
- [x] inhibitor
- [x] magic quotes
- [x] parenthesis
- [x] command history (`history` btln)
- [ ] arithmetric expansion (`$(( x + 1 ))`)
- [ ] brace expansion: `{a,b,c}` and `{1..10}`
- [x] heredocs
- [ ] functions (`:() { :|:& }`)
- [ ] special env vars: `$?`, `$$`, ...
- [ ] git integration (branch)
- [ ] `x`, `e`, `o` & pipefail modes
- [ ] file inquiry operators (`-d`, `f`, ...)

View File

@@ -1,36 +0,0 @@
malloc
free
exit
opendir
readdir
closedir
getcwd
chdir
fork
stat
lstat
fstat
open
close
getline
strtok
strtok_r
read
write
execve
access
isatty
wait
wait-pid
wait3
wait4
signal
kill
getpid
strerror
perror
strsignal
pipe
dup
dup2
stdin

View File

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

View File

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

View File

@@ -17,7 +17,7 @@
#include "utils.h"
static
int skip_blank(char *buffer, int i)
int skip_blank(const char *buffer, int i)
{
for (; buffer[i] != 0 && isblank(buffer[i]); i++);
return i;
@@ -26,7 +26,7 @@ int skip_blank(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++);
for (; buffer[i] != 0 && !is_a_token(buffer, i); i++);
return i;
}
@@ -34,17 +34,17 @@ static
char *find_alias(his_variable_t *variable, alias_t *alias, char *buffer)
{
char *cmd = malloc(sizeof(char) * (variable->size_variable + 1));
char *new_cmd = NULL;
char *new_cmd = nullptr;
if (cmd == NULL)
return NULL;
return nullptr;
for (int i = variable->coord_variable; i !=
variable->coord_variable + variable->size_variable; i++)
cmd[i - variable->coord_variable] = buffer[i];
cmd[variable->size_variable] = '\0';
for (size_t i = 0; i != alias->size; i++){
if (alias->alias_array[i] == NULL)
return NULL;
return nullptr;
if (strcmp(cmd, alias->alias_array[i]) == 0){
new_cmd = cat_in_str(variable, buffer, alias->alias_to_replace[i]);
buffer = new_cmd;
@@ -59,10 +59,10 @@ char *get_alias(char *buffer, int i, alias_t *alias)
{
int coord = i;
int size = 0;
his_variable_t variable = {0, 0, 0, NULL, 0};
his_variable_t variable = {0, 0, 0, nullptr, 0};
for (; buffer[i] != 0 && !isblank(buffer[i])
&& is_a_token(buffer, i) == false; i++)
&& !is_a_token(buffer, i); i++)
size++;
variable.coord_variable = coord;
variable.size_variable = size;
@@ -90,7 +90,7 @@ int parse_alias(char **buffer, size_t *buffer_len, alias_t *alias)
{
bool need_to_replace = true;
while (need_to_replace == true)
while (need_to_replace)
need_to_replace = replace_alias(buffer, alias);
return RETURN_SUCCESS;
}
@@ -104,8 +104,8 @@ alias_t init_alias(void)
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.alias_array[0] = nullptr;
alias.alias_to_replace[0] = nullptr;
alias.size = 0;
return alias;
}

View File

@@ -12,8 +12,10 @@
#include <string.h>
#include <unistd.h>
#include "args.h"
#include "debug.h"
#include "exec.h"
#include "globbing.h"
#include "utils.h"
bool check_glob_result(int val, char *bin_name)
{
@@ -31,7 +33,8 @@ bool process_globbing(char *pattern, args_t *args, size_t *toks_i)
int glob_result;
char *vl;
glob_result = glob(pattern, GLOB_ERR, NULL, &globs);
U_DEBUG("Globbing pattern [%s]\n", pattern);
glob_result = glob(pattern, GLOB_ERR, nullptr, &globs);
if (!check_glob_result(glob_result, args->args[0]))
return false;
for (size_t i = 0; i < globs.gl_pathc; i++) {
@@ -43,7 +46,27 @@ bool process_globbing(char *pattern, args_t *args, size_t *toks_i)
args->sz++;
}
globfree(&globs);
*toks_i += 1;
return true;
}
static
bool handle_tilde(ef_t *ef, token_t *tok, args_t *args)
{
char *home;
char *final_str;
size_t tilde_pos = strcspn(tok->str, "~");
tok->str[tok->sz] = '\0';
home = get_env_value(ef->env, "HOME");
if (home != NULL)
final_str = get_env_value(ef->env, "HOME");
else
final_str = strdup("");
args->args[args->sz] = insert_str(tok->str, final_str, tilde_pos);
U_DEBUG("Tilde handling [%s] pos [%lu]\n", final_str, tilde_pos);
if (args->args[args->sz] == NULL)
return false;
args->sz++;
return true;
}
@@ -53,13 +76,15 @@ bool process_args(ast_t *node, args_t *args, size_t *toks_i, ef_t *ef)
if (!ensure_args_capacity(args))
return false;
if (tok.type == T_STAR || strcspn(tok.str, "[]?") != strlen(tok.str))
if (tok.type == T_STAR || strcspn(tok.str, "[]?") != strlen(tok.str)) {
tok.str[tok.sz] = '\0';
return (process_globbing(tok.str, args, toks_i));
}
if (tok.type == T_TILDE)
return handle_tilde(ef, &tok, args);
handle_var_case(node, ef->exec_ctx, toks_i, args);
if (args->args[args->sz] == NULL)
return false;
if (strchr(tok.str, '\\') != NULL)
args->args[args->sz] = tok.str;
args->sz++;
return true;
}

View File

@@ -37,10 +37,12 @@ typedef enum size_t {
T_AT = 1 << 15, // <
T_WHILE = 1 << 16, // while
T_STAR = 1 << 18, // *
T_NEWLINE = 1 << 19, // \n
T_EOF = 1 << 20, // \0
T_ARG = 1 << 21,
T_INVALID = 1 << 22
T_BACKSLASH = 1 << 19,
T_NEWLINE = 1 << 20, // \n
T_TILDE = 1 << 21, // ~
T_EOF = 1 << 22, // \0
T_ARG = 1 << 23,
T_INVALID = 1 << 24
} token_type_t;
typedef enum {
@@ -140,4 +142,6 @@ ast_t *parse_condition(ast_ctx_t *ctx);
ast_t *parse_and(ast_ctx_t *ctx, ast_t *l_node);
ast_t *parse_or(ast_ctx_t *ctx, ast_t *l_node);
ast_t *parse_if(ast_ctx_t *ctx);
ast_t *parse_condition_and(ast_ctx_t *ctx);
ast_t *parse_pipe(ast_ctx_t *ctx, ast_t *l_node);
#endif /* AST_H */

View File

@@ -14,22 +14,34 @@
#include "ast.h"
static
bool parse_special_args(ast_ctx_t *ctx)
{
if (memchr(ctx->act_tok.str, '~', ctx->act_tok.sz) != NULL)
ctx->act_tok.type = T_TILDE;
if (memchr(ctx->act_tok.str, '*', ctx->act_tok.sz) != NULL)
ctx->act_tok.type = T_STAR;
return true;
}
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)
if (ctx->act_tok.type & (T_SEMICOLON | T_NEWLINE))
return node;
if (*ctx->act_tok.str == '\\') {
if (!parse_special_args(ctx))
return nullptr;
if (ctx->act_tok.type == T_BACKSLASH) {
ctx->act_tok = get_next_token(ctx);
if (ctx->act_tok.type == T_EOF)
return node;
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_STAR)) {
T_IN_REDIRECT | T_HEREDOC | T_VAR | T_STAR | T_TILDE)) {
if (!ensure_node_cap(node))
return NULL;
return nullptr;
node->vector.tokens[node->vector.sz] = ctx->act_tok;
node->vector.sz++;
return parse_arg(ctx, node);
@@ -43,13 +55,15 @@ ast_t *fill_cmd_node(ast_ctx_t *ctx)
ast_t *node = create_node(ctx);
if (node == NULL)
return NULL;
return nullptr;
node->type = N_CMD;
node->vector.cap = DEFAULT_N_CMD_CAP;
node->vector.tokens =
malloc(sizeof *node->vector.tokens * node->vector.cap);
if (node->vector.tokens == NULL)
return NULL;
return nullptr;
if (!parse_special_args(ctx))
return nullptr;
node->tok = ctx->act_tok;
node->vector.tokens[0] = ctx->act_tok;
node->vector.sz = 1;
@@ -67,9 +81,9 @@ ast_t *parse_cmd(ast_ctx_t *ctx)
{
if (ctx->act_tok.type != T_ARG) {
if (ctx->act_tok.type & (T_WHILE))
return NULL;
return nullptr;
if (!parser_eat(ctx, T_ARG))
return NULL;
return nullptr;
}
return fill_cmd_node(ctx);
}
@@ -91,58 +105,34 @@ ast_t *parse_pipe(ast_ctx_t *ctx, ast_t *l_node)
ast_t *node = create_node(ctx);
if (node == NULL)
return NULL;
return nullptr;
node->type = N_LST;
node->tok = ctx->act_tok;
node->list.cap = DEFAULT_N_LST_CAP;
node->list.nodes = (ast_t **)malloc(sizeof(ast_t *) * node->list.cap);
if ((void *)node->list.nodes == NULL)
return NULL;
return nullptr;
node->list.sz = 1;
node->list.nodes[0] = l_node;
if (!parser_eat(ctx, T_ARG))
return NULL;
return nullptr;
while (ctx->act_tok.type & (T_ARG | T_PIPE))
if (!parse_pipe_childs(ctx, node))
return NULL;
return nullptr;
return node;
}
ast_t *parse_condition(ast_ctx_t *ctx)
{
ast_t *l_node = parse_cmd(ctx);
if (l_node == NULL)
return NULL;
else {
switch (ctx->act_tok.type) {
case T_PIPE:
ctx->ast = parse_pipe(ctx, l_node);
break;
default:
return l_node;
}
}
return ctx->ast;
}
ast_t *parse_semi(ast_ctx_t *ctx)
{
ast_t *l_node = parse_condition(ctx);
ast_t *l_node = parse_condition_and(ctx);
if (l_node == NULL)
return NULL;
switch (ctx->act_tok.type) {
case T_AND:
ctx->ast = parse_and(ctx, l_node);
break;
case T_OR:
ctx->ast = parse_or(ctx, l_node);
break;
default:
return l_node;
return nullptr;
if (ctx->act_tok.type == T_OR) {
ctx->ast = parse_or(ctx, l_node);
return ctx->ast;
}
return ctx->ast;
return l_node;
}
static
@@ -151,12 +141,12 @@ ast_t *create_semi_node(ast_ctx_t *ctx, ast_t *l_node)
ast_t *node = create_node(ctx);
if (node == NULL)
return NULL;
return nullptr;
node->type = N_LST;
node->list.cap = DEFAULT_N_LST_CAP;
node->list.nodes = (ast_t **)malloc(sizeof(ast_t *) * node->list.cap);
if ((void *)node->list.nodes == NULL)
return NULL;
return nullptr;
node->list.sz = 1;
node->list.nodes[0] = l_node;
node->tok = ctx->act_tok;
@@ -171,10 +161,10 @@ ast_t *fill_semi_node(ast_ctx_t *ctx, ast_t *node)
if (ctx->act_tok.type & (T_SEMICOLON | T_NEWLINE))
continue;
if (!ensure_list_cap(node))
return NULL;
return nullptr;
node->list.nodes[node->list.sz] = parse_semi(ctx);
if (node->list.nodes[node->list.sz] == NULL)
return NULL;
return nullptr;
node->list.sz++;
}
return node;
@@ -195,7 +185,7 @@ ast_t *parse_expression(ast_ctx_t *ctx)
if (ctx->act_tok.type & (T_SEMICOLON | T_NEWLINE)) {
node = create_semi_node(ctx, l_node);
if (node == NULL)
return NULL;
return nullptr;
ctx->ast = fill_semi_node(ctx, node);
}
return parse_expression(ctx);

View File

@@ -19,13 +19,13 @@ ast_t *parse_and(ast_ctx_t *ctx, ast_t *l_node)
ast_t *node = create_node(ctx);
if (node == NULL || l_node == NULL)
return NULL;
return nullptr;
node->tok = ctx->act_tok;
node->type = N_BIN;
node->binary.left = l_node;
node->binary.right = parse_semi(ctx);
node->binary.right = parse_condition_and(ctx);
if (node->binary.right == NULL)
return NULL;
return nullptr;
return node;
}
@@ -34,16 +34,45 @@ ast_t *parse_or(ast_ctx_t *ctx, ast_t *l_node)
ast_t *node = create_node(ctx);
if (node == NULL || l_node == NULL)
return NULL;
return nullptr;
node->tok = ctx->act_tok;
node->type = N_BIN;
node->binary.left = l_node;
node->binary.right = parse_semi(ctx);
if (node->binary.right == NULL)
return NULL;
return nullptr;
return node;
}
ast_t *parse_condition_and(ast_ctx_t *ctx)
{
ast_t *l_node = parse_condition(ctx);
if (l_node == NULL)
return nullptr;
if (ctx->act_tok.type == T_AND) {
ctx->ast = parse_and(ctx, l_node);
return ctx->ast;
}
return l_node;
}
ast_t *parse_condition(ast_ctx_t *ctx)
{
ast_t *l_node = parse_cmd(ctx);
if (l_node == NULL)
return nullptr;
switch (ctx->act_tok.type) {
case T_PIPE:
ctx->ast = parse_pipe(ctx, l_node);
break;
default:
return l_node;
}
return ctx->ast;
}
static
bool fill_else_node(ast_ctx_t *ctx, ast_t *node, buff_t *buff)
{
@@ -52,9 +81,7 @@ bool fill_else_node(ast_ctx_t *ctx, ast_t *node, buff_t *buff)
if (node->cond.nodes2[node->cond.sz2] == NULL)
return false;
node->cond.sz2++;
if (!ensure_cond_cap2(node))
return false;
return true;
return ensure_cond_cap2(node);
}
static
@@ -69,15 +96,13 @@ bool fill_if_node(ast_ctx_t *ctx, ast_t *node, bool fill_else, buff_t *buff)
if (node->cond.nodes[node->cond.sz] == NULL)
return false;
node->cond.sz++;
if (!ensure_cond_cap(node))
return false;
return true;
return ensure_cond_cap(node);
}
static
ast_t *fill_if(ast_ctx_t *ctx, ast_t *node)
{
buff_t buff = { .str = NULL, 0 };
buff_t buff = { .str = nullptr, 0 };
char *old_buff = ctx->str;
bool fill_else = false;
@@ -85,15 +110,15 @@ ast_t *fill_if(ast_ctx_t *ctx, ast_t *node)
if (isatty(STDIN_FILENO))
WRITE_CONST(STDOUT_FILENO, IF_PROMPT);
if (getline(&buff.str, &buff.sz, stdin) < 0)
return NULL;
return nullptr;
buff.str[strlen(buff.str) - 1] = '\0';
if (strncmp(buff.str, "endif", 5) == 0)
break;
if (strncmp(buff.str, "else", 4) == 0)
fill_else = true;
if (!fill_if_node(ctx, node, fill_else, &buff))
return NULL;
buff = (buff_t){ .str = NULL, 0 };
return nullptr;
buff = (buff_t){ .str = nullptr, 0 };
}
ctx->str = old_buff;
return node;
@@ -104,7 +129,7 @@ ast_t *parse_if(ast_ctx_t *ctx)
ast_t *node = create_node(ctx);
if (node == NULL)
return NULL;
return nullptr;
node->tok = ctx->act_tok;
node->type = N_COND;
node->cond.cap = DEFAULT_N_COND_CAP;
@@ -115,7 +140,7 @@ ast_t *parse_if(ast_ctx_t *ctx)
node->cond.nodes2 =
(ast_t **)malloc(sizeof *node->cond.nodes * node->cond.cap2);
if ((void *)node->cond.nodes == NULL || (void *)node->cond.nodes2 == NULL)
return NULL;
return nullptr;
node->cond.exp = parse_semi(ctx);
if (node->cond.exp == NULL)
return WRITE_CONST(STDERR_FILENO, "if: Too few arguments.\n"), NULL;

View File

@@ -28,8 +28,8 @@ const tokens_list_t TOKENS_LIST[] = {
{ T_REDIRECT, ">", 1, "T_REDIRECT" },
{ T_HEREDOC, "<<", 2, "T_HEREDOC" },
{ T_IN_REDIRECT, "<", 1, "T_IN_REDIRECT" },
{ T_BACKSLASH, "\\", 1, "T_BACKSLASH" },
{ T_NEWLINE, "\n", 1, "T_NEWLINE"},
{ T_STAR, "*", 1, "T_STAR"},
{ T_EOF, "\0", 1, "T_EOF" }
};
@@ -147,7 +147,7 @@ token_t get_next_token(ast_ctx_t *ctx)
while ((*ctx->str && !found_token && (!isblank(*ctx->str) ||
check_closable(actual_token))))
get_arg_token(ctx, &found_token, actual_token);
U_DEBUG("Token T_ARG [%.*s]%d\n", (int)(ctx->str - start), start);
U_DEBUG("Token T_ARG [%.*s]\n", (int)(ctx->str - start), start);
return (token_t){ .type = T_ARG, .str = start,
.sz = (size_t)(ctx->str - start) };
}

View File

@@ -9,6 +9,19 @@
#ifndef BUILTINS_H
#define BUILTINS_H
#include "exec.h"
#include "u_str.h"
typedef struct {
char **cmds;
size_t size;
size_t cap;
} cmd_block_t;
typedef struct {
buff_t *buff;
bool in_else;
ef_t *ef;
} if_ctx_t;
typedef struct {
char const *name;

View File

@@ -5,10 +5,15 @@
** builtin_history
*/
#include <stdlib.h>
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "history.h"
#include "u_str.h"
#include "utils.h"
/*
**Il faut deux \0 parce que dans le gettokeniser
@@ -26,31 +31,24 @@
** il vas free le buffer
*/
#include <string.h>
#include "utils.h"
#include <ctype.h>
#include <stddef.h>
#include <stdlib.h>
static char *concat_cmd_arg(char *dest, char *src)
{
int l;
int i;
char *r_value = NULL;
char *r_value = nullptr;
if (!src) {
r_value = u_strdup(dest);
return r_value;
} else {
l = strlen(dest);
i = strlen(src);
r_value = malloc(sizeof(char)* (i + l + 2));
if (r_value != NULL) {
strcpy(r_value, dest);
r_value[l] = ' ';
r_value[l +1] = '\0';
strcat(r_value, src);
}
}
l = strlen(dest);
i = strlen(src);
r_value = malloc(sizeof(char)* (i + l + 2));
if (r_value != NULL) {
strcpy(r_value, dest);
r_value[l] = ' ';
r_value[l +1] = '\0';
strcat(r_value, src);
}
return r_value;
}
@@ -58,12 +56,12 @@ static char *concat_cmd_arg(char *dest, char *src)
char *his_last_command(char *line,
his_variable_t *his_variable, his_command_t *his_command)
{
char *new_line = NULL;
char *new_str = NULL;
char *new_line = nullptr;
char *new_str = nullptr;
if (his_command->sz == 0){
fprintf(stderr, "%d: Event not found.\n", his_command->sz);
return NULL;
return nullptr;
}
new_line = concat_cmd_arg(his_command[his_command->sz - 1].command,
his_command[his_command->sz - 1].arg);
@@ -76,7 +74,7 @@ char *his_last_same_command(char *line,
his_variable_t *his_variable, his_command_t *his_command)
{
char *new_line = &line[his_variable->coord_variable + 1];
char *new_str = NULL;
char *new_str = nullptr;
for (int i = his_command->sz - 1; i > 0; i--) {
if (his_command[i].command == NULL) {
@@ -100,7 +98,7 @@ char *his_id_command(char *line,
{
int id = -1 + atoi(&line[his_variable->coord_variable + 1]);
char *new_line;
char *new_str = NULL;
char *new_str = nullptr;
if (id < 0 || id > 100 || his_command[id].command == NULL){
fprintf(stderr, "%d: Event not found.\n", id + 1);
@@ -116,18 +114,18 @@ char *his_id_command(char *line,
static char *get_last_word(char *str)
{
char *last_word = NULL;
char *last_word = nullptr;
int last_space = 0;
int x = 0;
if (!str)
return NULL;
return nullptr;
while (str[x] != '\0') {
if (isblank(str[x]))
last_space = x + 1;
x++;
}
last_word = malloc(sizeof(char) * (x - last_space) + 1);
last_word = malloc(sizeof(char) * ((x - last_space) + 1));
if (last_word != NULL) {
last_word = strncpy(last_word, &str[last_space], x - last_space);
last_word[x - last_space] = '\0';
@@ -138,15 +136,15 @@ static char *get_last_word(char *str)
char *his_last_word(char *line,
his_variable_t *his_variable, his_command_t *his_command)
{
char *new_line = NULL;
char *new_str = NULL;
char *new_line = nullptr;
char *new_str = nullptr;
if (his_command[his_command->sz - 1].arg == NULL){
new_line = get_last_word(his_command[his_command->sz - 1].command);
} else
new_line = get_last_word(his_command[his_command->sz - 1].arg);
if (!new_line)
return NULL;
return nullptr;
new_str = cat_in_str(his_variable, line, new_line);
printf("%s\n", new_str);
free(new_line);
@@ -158,8 +156,8 @@ char *his_last_arg(char *line,
his_variable_t *his_variable, his_command_t *his_command)
{
int id = his_command->sz - 1;
char *new_line = NULL;
char *new_str = NULL;
char *new_line = nullptr;
char *new_str = nullptr;
if (!his_command[id].arg)
new_line = " ";

View File

@@ -4,15 +4,14 @@
** File description:
** history
*/
#include <string.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "common.h"
#include "env.h"
#include "exec.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alias.h"
#include "common.h"
#include "exec.h"
#include "utils.h"
void free_alias(alias_t *alias)

View File

@@ -43,7 +43,7 @@ char *get_current_dir(void)
size_t max_it = 100;
if (!buffer)
return NULL;
return nullptr;
while (getcwd(buffer, size) == NULL && max_it > 0) {
if (errno != ERANGE)
return (free(buffer), NULL);
@@ -82,7 +82,7 @@ int builtins_cd(ef_t *ef, char **args)
if (!(ef->out_fd == STDOUT_FILENO || ef->p_i == ef->p_sz - 1))
return RETURN_SUCCESS;
if (path == NULL || u_strcmp(args[1], "~") == 0)
if (path == NULL)
path = get_env_value(ef->env, "HOME");
if (path == NULL)
return RETURN_FAILURE;

View File

@@ -12,7 +12,7 @@
#include "env.h"
#include "exec.h"
int builtins_exit(ef_t *ef, char **args __attribute__((unused)))
int builtins_exit(ef_t *ef, char **args[[gnu::unused]])
{
if (ef->flags & F_PIPE &&
!(ef->out_fd == STDOUT_FILENO || ef->p_i == ef->p_sz - 1))

View File

@@ -99,6 +99,30 @@ expr_val_t apply_gt(expr_val_t *left, expr_val_t *right)
return res;
}
static
expr_val_t apply_eq(expr_val_t *left, expr_val_t *right)
{
expr_val_t res = { .type = E_VAL_INT };
if (left->type == E_VAL_INT && right->type == E_VAL_INT)
res.val = left->val == right->val;
else
res.val = !strcmp(left->p, right->p);
return res;
}
static
expr_val_t apply_neq(expr_val_t *left, expr_val_t *right)
{
expr_val_t res = { .type = E_VAL_INT };
if (left->type == E_VAL_INT && right->type == E_VAL_INT)
res.val = left->val != right->val;
else
res.val = strcmp(left->p, right->p);
return res;
}
const expr_op_precedence_t OPERATOR_PRECEDENCE[] = {
{ .name = "+", .prec = 2, apply_add },
{ .name = "-", .prec = 2, apply_sub },
@@ -106,6 +130,9 @@ const expr_op_precedence_t OPERATOR_PRECEDENCE[] = {
{ .name = "/", .prec = 3, apply_div },
{ .name = "<", .prec = 1, apply_lt },
{ .name = ">", .prec = 1, apply_gt },
{ .name = "=", .prec = 1, apply_eq },
{ .name = "==", .prec = 1, apply_eq },
{ .name = "!=", .prec = 1, apply_neq },
};
const size_t OPERATOR_PRECEDENCE_COUNT = COUNT_OF(OPERATOR_PRECEDENCE);

View File

@@ -6,7 +6,6 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
@@ -19,14 +18,16 @@ int builtins_expr(ef_t *ef[[gnu::unused]], char **args)
expr_state_t state;
expr_val_t ret;
for (; args[argc] != NULL; argc++);
for (; args[argc] != nullptr; argc++);
if (argc < 2)
return fprintf(stderr, "%s: missing operand\n", args[0]), -1;
return fprintf(stderr, "%s: missing operand\nTry 'expr --help' for"
" more information.", args[0]), -1;
state = (expr_state_t){ .args = &args[1] };
ret = expr_run(&state, 0, 0);
if (ret.type == E_VAL_ERR)
return printf("%s: %s\n", args[0], ret.str), -1;
if (ret.type == E_VAL_INT && strcmp("if", args[0]) == 0)
if (ret.type == E_VAL_INT && (strcmp("if", args[0]) == 0 ||
strcmp("while", args[0]) == 0))
return ret.val;
if (ret.type == E_VAL_INT)
printf("%ld\n", ret.val);

View File

@@ -5,36 +5,31 @@
** get_loop_cmd
*/
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include "ast.h"
#include "builtins.h"
#include "common.h"
#include "exec.h"
#include "redirects.h"
#include "u_str.h"
#include "u_mem.h"
#include "loop.h"
#include "local.h"
#include "repl.h"
#include "u_mem.h"
#include "u_str.h"
static
usr_cmd_t *buffers_realloc(usr_cmd_t *usr)
{
char **new_buffers = u_realloc(usr->cmds, sizeof
char **new_buffers = (char **)u_realloc((void *)usr->cmds, sizeof
*usr->cmds * usr->sz, sizeof
*usr->cmds * (usr->cap << 1));
if (new_buffers == NULL)
return NULL;
if ((void *)new_buffers == NULL)
return nullptr;
usr->cmds = new_buffers;
usr->cap <<= 1;
return usr;
@@ -43,7 +38,7 @@ usr_cmd_t *buffers_realloc(usr_cmd_t *usr)
static
usr_cmd_t *increase_buffers(usr_cmd_t *usr, size_t *buffer_len)
{
usr->cmds[usr->sz] = NULL;
usr->cmds[usr->sz] = nullptr;
getline(&(usr->cmds[usr->sz]), buffer_len, stdin);
*buffer_len = u_strlen(usr->cmds[usr->sz]);
usr->cmds[usr->sz][*buffer_len - 1] = '\0';
@@ -52,27 +47,28 @@ usr_cmd_t *increase_buffers(usr_cmd_t *usr, size_t *buffer_len)
}
static
usr_cmd_t *handle_end(usr_cmd_t *us, char prompt[])
usr_cmd_t *handle_end(usr_cmd_t *us, char const *prompt)
{
us->sz--;
if (!us->cmds[us->sz] || strcmp("end", us->cmds[us->sz])){
if (!us->cmds[us->sz] || strcmp("end", us->cmds[us->sz]) != 0){
printf("%s: end not found.\n", prompt);
free_array(us->cmds);
us->cmds = NULL;
us->cmds = nullptr;
exit(RETURN_FAILURE);
return NULL;
return nullptr;
}
free(us->cmds[us->sz]);
us->cmds[us->sz] = NULL;
us->cmds[us->sz] = nullptr;
return us;
}
static
usr_cmd_t *get_first_cmd(usr_cmd_t *usr, char prompt[], size_t *bf_len)
usr_cmd_t *get_first_cmd(exec_ctx_t *exec_ctx, usr_cmd_t *usr,
char const *prompt, size_t *bf_len)
{
if (isatty(STDIN_FILENO))
if (isatty(exec_ctx->read_fd))
printf("%s? ", prompt);
usr->cmds[usr->sz] = NULL;
usr->cmds[usr->sz] = nullptr;
getline(&(usr->cmds[usr->sz]), bf_len, stdin);
*bf_len = u_strlen(usr->cmds[usr->sz]);
usr->cmds[usr->sz][*bf_len - 1] = '\0';
@@ -80,23 +76,24 @@ usr_cmd_t *get_first_cmd(usr_cmd_t *usr, char prompt[], size_t *bf_len)
return usr;
}
usr_cmd_t *get_usr_loop_cmd(usr_cmd_t *usr_cmd, char prompt[])
usr_cmd_t *get_usr_loop_cmd(exec_ctx_t *exec_ctx, usr_cmd_t *usr_cmd,
char const *prompt)
{
size_t buffer_len;
if (usr_cmd == NULL)
return NULL;
usr_cmd->cmds = malloc(sizeof(char *) * usr_cmd->cap);
if (usr_cmd->cmds == NULL)
return NULL;
usr_cmd = get_first_cmd(usr_cmd, prompt, &buffer_len);
while (strcmp("end", usr_cmd->cmds[usr_cmd->sz - 1])){
if (isatty(STDIN_FILENO))
return nullptr;
usr_cmd->cmds = (char **)malloc(sizeof(char *) * usr_cmd->cap);
if ((void *)usr_cmd->cmds == NULL)
return nullptr;
usr_cmd = get_first_cmd(exec_ctx, usr_cmd, prompt, &buffer_len);
while (strcmp("end", usr_cmd->cmds[usr_cmd->sz - 1]) != 0){
if (isatty(exec_ctx->read_fd))
printf("%s? ", prompt);
if (usr_cmd->sz >= usr_cmd->cap)
usr_cmd = buffers_realloc(usr_cmd);
if (usr_cmd == NULL)
return NULL;
return nullptr;
increase_buffers(usr_cmd, &buffer_len);
}
usr_cmd = handle_end(usr_cmd, prompt);

View File

@@ -6,8 +6,8 @@
*/
#include <stdio.h>
#include "common.h"
#include "env.h"
#include "exec.h"
int builtins_history(ef_t *ef, char **args __attribute__((unused)))

View File

@@ -14,6 +14,7 @@
#include "common.h"
#include "debug.h"
#include "exec.h"
#include "repl.h"
#include "u_str.h"
static
@@ -21,8 +22,7 @@ int get_argc(char **args)
{
int i = 0;
for (; *args != NULL; args++)
i++;
for (; args[i] != nullptr; i++);
return i;
}
@@ -33,8 +33,8 @@ bool ensure_cmds_cap(char ***cmd, size_t sz, size_t *cap)
if (sz + 1 < *cap)
return true;
tmp = (char **)realloc((void *)*cmd, sizeof(**cmd) * *cap << 1);
if (tmp == nullptr)
tmp = (char **)realloc(*cmd, sizeof(char *) * (*cap << 1));
if (!tmp)
return false;
*cap <<= 1;
*cmd = tmp;
@@ -42,50 +42,124 @@ bool ensure_cmds_cap(char ***cmd, size_t sz, size_t *cap)
}
static
bool if_repl(ef_t *ef, char **args, char *cmd)
bool init_block(cmd_block_t *blk)
{
buff_t buff = { .str = nullptr, 0 };
char **cmds;
size_t cap = DEFAULT_N_CMD_CAP;
size_t sz = 0;
blk->size = 0;
blk->cap = DEFAULT_N_CMD_CAP;
blk->cmds = (char **)malloc(sizeof(char *) * blk->cap);
return blk->cmds != NULL;
}
if (strcmp("then", cmd) != 0)
return visitor(cmd, ef->exec_ctx), true;
cmds = (char **)malloc(cap * sizeof(char **));
if ((void *)cmds == NULL)
static
void free_block(cmd_block_t *blk)
{
for (size_t i = 0; i < blk->size; i++)
free(blk->cmds[i]);
free((void *)blk->cmds);
}
static
bool append_block(cmd_block_t *blk, const char *line)
{
if (!ensure_cmds_cap(&blk->cmds, blk->size, &blk->cap))
return false;
while (true) {
if (isatty(STDIN_FILENO))
WRITE_CONST(ef->out_fd, IF_PROMPT);
if (getline(&buff.str, &buff.sz, stdin) == -1)
break;
if (!ensure_cmds_cap(&cmds, sz, &cap))
return false;
cmds[sz] = strdup(buff.str);
blk->cmds[blk->size] = strdup(line);
blk->size++;
return true;
}
static
bool handle_if_buff(if_ctx_t *ctx, cmd_block_t *then_blk,
cmd_block_t *else_blk, bool *in_else)
{
if (strcmp(ctx->buff->str, "else") == 0) {
*in_else = true;
return true;
}
if (strcmp(ctx->buff->str, "endif") == 0)
return false;
if (!*in_else) {
if (!append_block(then_blk, ctx->buff->str))
return false;
} else
if (!append_block(else_blk, ctx->buff->str))
return false;
return true;
}
static
bool read_if_blocks(ef_t *ef, if_ctx_t *ctx, cmd_block_t *then_blk,
cmd_block_t *else_blk)
{
bool in_else = false;
while (true) {
if (isatty(ef->exec_ctx->read_fd))
WRITE_CONST(ctx->ef->out_fd, IF_PROMPT);
if (getline(&ctx->buff->str, &ctx->buff->sz, stdin) == -1)
return false;
ctx->buff->str[strcspn(ctx->buff->str, "\n")] = '\0';
if (strcmp(ctx->buff->str, "else") == 0) {
in_else = true;
continue;
}
if (strcmp(ctx->buff->str, "endif") == 0)
break;
if (!handle_if_buff(ctx, then_blk, else_blk, &in_else))
return false;
}
return true;
}
static
void exec_block(cmd_block_t *blk, ef_t *ef)
{
for (size_t i = 0; i < blk->size; i++)
visitor(blk->cmds[i], ef->exec_ctx);
}
static
bool handle_if_logic(ef_t *ef, bool cond, char *last)
{
if_ctx_t ctx = {.buff = &(buff_t){ .str = nullptr, .sz = 0 }, .ef = ef};
cmd_block_t then_blk;
cmd_block_t else_blk;
if (strcmp("then", last) != 0)
return cond ? visitor(last, ef->exec_ctx), true : true;
if (!init_block(&then_blk) || !init_block(&else_blk))
return false;
if (!read_if_blocks(ef, &ctx, &then_blk, &else_blk))
return false;
if (cond)
exec_block(&then_blk, ef);
else
exec_block(&else_blk, ef);
free_block(&then_blk);
free_block(&else_blk);
free(ctx.buff->str);
return true;
}
int builtins_if(ef_t *ef, char **args)
{
int result;
char *cmd;
char *last;
if (args[1] == NULL)
return WRITE_CONST(STDERR_FILENO, "if: Too few arguments.\n"),
RETURN_FAILURE;
if (args[2] == NULL)
return WRITE_CONST(STDERR_FILENO, "if: Empty if\n"), RETURN_FAILURE;
cmd = strdup(args[get_argc(args) - 1]);
if (cmd == NULL)
return WRITE_CONST(STDERR_FILENO, "if: Empty if.\n"), RETURN_FAILURE;
last = strdup(args[get_argc(args) - 1]);
if (!last)
return RETURN_FAILURE;
args[get_argc(args) - 1] = nullptr;
result = builtins_expr(ef, args);
U_DEBUG("If expr result [%d]\n", result);
if (result == -1)
return free(cmd), RETURN_FAILURE;
if (result)
if (!if_repl(ef, args, cmd))
return free(cmd), RETURN_FAILURE;
return free(cmd), RETURN_SUCCESS;
return free(last), RETURN_FAILURE;
if (!handle_if_logic(ef, result != 0, last))
return free(last), RETURN_FAILURE;
return free(last), RETURN_SUCCESS;
}

View File

@@ -6,16 +6,15 @@
*/
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "builtins_handler.h"
#include "common.h"
#include "env.h"
#include "exec.h"
#include "u_str.h"
#include "u_mem.h"
#include "u_str.h"
bool check_local_var(char *var, char *func_name)
{
@@ -33,14 +32,14 @@ char *get_local_value(local_t *local, char const *key)
int key_len = u_strlen(key);
for (size_t i = 0; i < local->sz; i++) {
if (local->local_var[i] == NULL)
if (local->local_var[i] == nullptr)
continue;
if (u_strcspn(local->local_var[i], '\t') != key_len)
continue;
if (u_strcmp(local->local_var[i], key) == 0)
return local->local_var[i] + key_len + 1;
}
return NULL;
return nullptr;
}
local_t create_local(void)
@@ -48,9 +47,9 @@ local_t create_local(void)
local_t local = {.sz = 0, .cap = 2};
local.local_var = (char **)malloc(sizeof(char *) * local.cap);
if (local.local_var == NULL)
return (local_t){.sz = 0, .cap = 2, .local_var = NULL};
local.local_var[local.sz] = NULL;
if (local.local_var == nullptr)
return (local_t){.sz = 0, .cap = 2, .local_var = nullptr};
local.local_var[local.sz] = nullptr;
local.sz++;
return local;
}
@@ -58,7 +57,7 @@ local_t create_local(void)
static
bool ensure_local_capacity(local_t *local)
{
char **new_ptr = NULL;
char **new_ptr = nullptr;
if (local->sz < local->cap)
return true;
@@ -74,17 +73,17 @@ bool ensure_local_capacity(local_t *local)
bool set_local(local_t *local, char *var, char *value)
{
char *new_loc = NULL;
char *new_loc = nullptr;
size_t key_len = u_strlen(var);
size_t value_len = u_strlen(value);
if (get_local_value(local, var) != NULL)
if (get_local_value(local, var) != nullptr)
unset_local(local, var);
local->sz++;
if (!ensure_local_capacity(local))
return false;
new_loc = malloc(sizeof(char) * (key_len + value_len + 2));
if (new_loc == NULL)
if (new_loc == nullptr)
return false;
u_bzero(new_loc, key_len + value_len + 2);
u_strcpy(new_loc, var);
@@ -92,7 +91,7 @@ bool set_local(local_t *local, char *var, char *value)
if (value_len > 0)
u_strcpy(new_loc + key_len + 1, value);
local->local_var[local->sz - 1] = new_loc;
local->local_var[local->sz] = NULL;
local->local_var[local->sz] = nullptr;
return true;
}
@@ -110,7 +109,7 @@ bool unset_local(local_t *local, char *var)
int key_len = u_strlen(var);
for (size_t i = 0; i < local->sz; i++) {
if (local->local_var[i] == NULL)
if (local->local_var[i] == nullptr)
continue;
if (u_strcspn(local->local_var[i], '\t') != key_len)
continue;

View File

@@ -4,34 +4,34 @@
** File description:
** foreach
*/
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include "ast.h"
#include "builtins.h"
#include "common.h"
#include "debug.h"
#include "exec.h"
#include "redirects.h"
#include "u_str.h"
#include "u_mem.h"
#include "loop.h"
#include "local.h"
#include "loop.h"
#include "u_str.h"
void exit_child(int sig __attribute__((unused)))
void exit_child(int sig[[gnu::unused]])
{
_exit(sig);
}
static
bool checking_for_error(ef_t *ef, char **args)
bool checking_for_error(char **args)
{
if (my_array_len(args) < 3)
return (WRITE_CONST(STDERR_FILENO, "foreach: Too few arguments.\n"),
@@ -42,7 +42,7 @@ bool checking_for_error(ef_t *ef, char **args)
}
static
bool checking_while_error(ef_t *ef, char **args)
bool checking_while_error(char **args)
{
if (my_array_len(args) < 2)
return (WRITE_CONST(STDERR_FILENO, "while: Too few arguments.\n"),
@@ -69,7 +69,7 @@ int foreach_loop(ef_t *ef, char **args, usr_cmd_t *usr_cmds)
int status = 0;
char **save_cmds = arraydup(usr_cmds->cmds);
if (save_cmds == NULL)
if ((void *)save_cmds == nullptr)
exit(84);
for (int i = 2; args[i]; i++){
if (!set_local(ef->exec_ctx->local, args[1], args[i]))
@@ -83,45 +83,53 @@ int foreach_loop(ef_t *ef, char **args, usr_cmd_t *usr_cmds)
}
static
int while_loop(ef_t *ef, usr_cmd_t *usr_cmds)
int while_loop(ef_t *ef, usr_cmd_t *usr_cmds, char **args)
{
int status = 0;
char **save_cmds = arraydup(usr_cmds->cmds);
int expr_result;
if (save_cmds == NULL)
if ((void *)save_cmds == nullptr)
exit(84);
while (true){
expr_result = builtins_expr(ef, args);
if (expr_result == -1)
return RETURN_FAILURE;
while (expr_result != 0) {
U_DEBUG("While expr result [%d]\n", expr_result);
status = do_a_lap(ef, usr_cmds->cmds);
free_array(usr_cmds->cmds);
usr_cmds->cmds = arraydup(save_cmds);
expr_result = builtins_expr(ef, args);
if (expr_result == -1)
return RETURN_FAILURE;
}
free_array(save_cmds);
return status;
}
static
int choose_loop(ef_t *ef, char **args, usr_cmd_t *usr_cmd, char prompt[])
int choose_loop(ef_t *ef, char **args, usr_cmd_t *usr_cmd, char const *prompt)
{
if (strcmp(prompt, "foreach") == 0)
return foreach_loop(ef, args, usr_cmd);
return while_loop(ef, usr_cmd);
return while_loop(ef, usr_cmd, args);
}
static
void launch_loop(ef_t *ef, char **args, char prompt[])
void launch_loop(ef_t *ef, char **args, char const *prompt)
{
int status = RETURN_FAILURE;
usr_cmd_t *usr_cmds = malloc(sizeof(usr_cmd_t));
ef->exec_ctx->local->in_a_loop = true;
if (usr_cmds == NULL)
if (usr_cmds == nullptr)
exit(84);
usr_cmds->cap = 2;
usr_cmds->sz = 0;
signal(SIGINT, exit_child);
signal(EOF, exit_child);
usr_cmds = get_usr_loop_cmd(usr_cmds, prompt);
if (usr_cmds == NULL)
usr_cmds = get_usr_loop_cmd(ef->exec_ctx, usr_cmds, prompt);
if (usr_cmds == nullptr)
exit(84);
status = choose_loop(ef, args, usr_cmds, prompt);
free_array(usr_cmds->cmds);
@@ -134,7 +142,7 @@ int builtins_foreach(ef_t *ef, char **args)
int status = 0;
pid_t pid;
if (checking_for_error(ef, args))
if (checking_for_error(args))
return RETURN_FAILURE;
pid = fork();
if (pid == 0)
@@ -152,7 +160,7 @@ int builtins_while(ef_t *ef, char **args)
int status = 0;
pid_t pid;
if (checking_while_error(ef, args))
if (checking_while_error(args))
return RETURN_FAILURE;
pid = fork();
if (pid == 0)

View File

@@ -4,13 +4,12 @@
** File description:
** repeat
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "builtins.h"
#include "common.h"
@@ -18,7 +17,7 @@
#include "u_str.h"
static
bool checking_error(ef_t *ef, char **args, long *nb_loop)
bool checking_error(char **args, long *nb_loop)
{
char *end;
@@ -39,7 +38,7 @@ int builtins_repeat(ef_t *ef, char **args)
int status = 0;
pid_t pid;
if (checking_error(ef, args, &nb_loop))
if (checking_error(args, &nb_loop))
return RETURN_FAILURE;
pid = fork();
if (pid == 0){

View File

@@ -5,14 +5,12 @@
** unset
*/
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#include "env.h"
#include "exec.h"
#include "u_str.h"

View File

@@ -31,13 +31,15 @@ bool search_builtin(ef_t *ef, char *arg)
if (u_strlen(BUILTINS[i].name) != (int)strlen(arg))
continue;
if (u_strcmp(BUILTINS[i].name, arg) == 0)
return dprintf(ef->out_fd, "%s is a shell built-in\n", arg), true;
return dprintf(ef->out_fd, "%s: shell built-in command.\n", arg),
true;
}
for (size_t i = 0; i < OOTHER_BUILTINS_SZ; i++) {
if (u_strlen(OOTHER_BUILTINS[i]) != (int)strlen(arg))
continue;
if (u_strcmp(OOTHER_BUILTINS[i], arg) == 0)
return dprintf(ef->out_fd, "%s is a shell built-in\n", arg), true;
return dprintf(ef->out_fd, "%s: shell built-in command.\n", arg),
true;
}
return false;
}
@@ -54,12 +56,13 @@ bool search_cmd(ef_t *ef, char *arg)
return free(alias_path.str), NULL;
parse_alias(&alias_path.str, &alias_path.sz, ef->exec_ctx->alias);
if (strcmp(arg, alias_path.str) != 0)
return dprintf(ef->out_fd, "%s: aliased to %s\n", arg,
return dprintf(ef->out_fd, "%s:\t aliased to %s\n", arg,
alias_path.str), true;
if (search_builtin(ef, arg))
return true;
if (strcmp(arg, bin_path) != 0)
return dprintf(ef->out_fd, "%s\n", bin_path), true;
fprintf(stderr, "%s: Command not found.\n", arg);
free(alias_path.str);
return true;
}

View File

@@ -4,19 +4,20 @@
** File description:
** yes
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "builtins.h"
#include "common.h"
#include "exec.h"
#include "u_str.h"
#include "u_mem.h"
#include "u_str.h"
static
int len_buffer(char **args)
@@ -31,13 +32,13 @@ int len_buffer(char **args)
static
char *define_prompt(char **args)
{
char *buffer = NULL;
char *buffer = nullptr;
if (args[1] == NULL)
return u_strdup("y\n");
buffer = malloc(sizeof(char) * (len_buffer(args) + 1));
if (buffer == NULL)
return NULL;
return nullptr;
u_bzero(buffer, len_buffer(args) + 1);
strcpy(buffer, args[1]);
buffer[strlen(buffer)] = ' ';
@@ -58,14 +59,12 @@ int builtins_yes(ef_t *ef, char **args)
if (buffer == NULL)
return RETURN_FAILURE;
pid = fork();
if (pid == 0){
while (true){
signal(SIGINT, exit_child);
if (pid == 0) {
signal(SIGINT, exit_child);
while (true)
write(ef->out_fd, buffer, strlen(buffer));
}
exit(RETURN_SUCCESS);
} else
wait(NULL);
wait(nullptr);
free(buffer);
return RETURN_SUCCESS;
}

View File

@@ -24,6 +24,9 @@ typedef struct {
bool is_running;
struct termios saved_term_settings;
local_t *local;
opt_t *opt;
int read_fd;
int isatty;
} exec_ctx_t;
size_t update_command(char **buffer,

View File

@@ -7,7 +7,9 @@
#ifndef COMMON_H
#define COMMON_H
#include "exec.h"
#include <stdint.h>
#define PROMPT_HEADER "┌─["
#define IF_PROMPT "if? "
enum {
@@ -15,5 +17,9 @@ enum {
RETURN_FAILURE = 1
};
void free_everything(exec_ctx_t *exec_ctx);
typedef struct {
uint8_t flags;
char *script_file;
char *cmd;
} opt_t;
#endif /* COMMON_H */

View File

@@ -13,13 +13,13 @@
#include <sys/wait.h>
#include <unistd.h>
#include "args.h"
#include "ast.h"
#include "builtins.h"
#include "common.h"
#include "debug.h"
#include "env.h"
#include "exec.h"
#include "globbing.h"
#include "path.h"
#include "repl.h"
#include "u_mem.h"
@@ -75,7 +75,7 @@ char **parse_args(ef_t *ef, ast_t *node)
* DEFAULT_ARGS_CAP), .sz = 0, .cap = DEFAULT_ARGS_CAP };
if (!args.args)
return NULL;
return nullptr;
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;
@@ -83,8 +83,8 @@ char **parse_args(ef_t *ef, ast_t *node)
return free((void *)args.args), NULL;
}
if (!ensure_args_capacity(&args))
return NULL;
args.args[args.sz] = NULL;
return nullptr;
args.args[args.sz] = nullptr;
return args.args;
}

View File

@@ -10,11 +10,14 @@
#include <stdbool.h>
#include <stddef.h>
#include "exec.h"
typedef struct {
size_t sz;
size_t cap;
char **cmds;
} usr_cmd_t;
usr_cmd_t *get_usr_loop_cmd(usr_cmd_t *usr_cmd, char prompt[]);
usr_cmd_t *get_usr_loop_cmd(exec_ctx_t *exec_ctx, usr_cmd_t *usr_cmd,
char const *prompt);
#endif /* LOOP_H */

View File

@@ -5,12 +5,52 @@
** _
*/
#include <getopt.h>
#include <stdlib.h>
#include "common.h"
#include "debug.h"
#include "shell.h"
int main(int ac __attribute__((unused)), char **av __attribute__((unused)),
char **env)
const char OPT_FLAGS[] = "hc:";
static
void print_usages(FILE *file, char *bin)
{
U_DEBUG_MSG("Debug mode activated.\n");
return shell(env);
fprintf(file, "Usage: %s [-c command] [-dixv]"
" [file]\n", bin);
}
static
bool parse_args(opt_t *opt, int ac, char **av)
{
for (int o = getopt(ac, av, OPT_FLAGS); o != -1;
o = getopt(ac, av, OPT_FLAGS)) {
switch (o) {
case 'h':
exit((print_usages(stdout, av[0]), RETURN_SUCCESS));
case 'c':
opt->cmd = optarg;
break;
default:
return print_usages(stderr, av[0]), false;
}
}
if (optind < ac) {
opt->script_file = av[optind];
U_DEBUG("Script file [%s]\n", opt->script_file);
} else {
U_DEBUG_MSG("No script file provided.\n");
}
return true;
}
int main(int ac, char **av, char **env)
{
opt_t opt = { 0, .script_file = nullptr, .cmd = nullptr };
U_DEBUG_MSG("Debug mode activated.\n");
if (!parse_args(&opt, ac, av))
return RETURN_FAILURE;
return shell(&opt, env);
}

View File

@@ -4,20 +4,16 @@
** File description:
** his for history
*/
#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 "u_str.h"
#include "utils.h"
const parsing_history_t tab_fnct[] = {
{"!!", &his_last_command},

View File

@@ -16,6 +16,7 @@
#include "common.h"
#include "readline.h"
#include "debug.h"
#include "repl.h"
#include "u_str.h"
@@ -70,23 +71,24 @@ bool ensure_buff_capacity(buff_t *buff)
}
static
bool append_null_terminator(buff_t *buff)
bool append_null_terminator(buff_t *buff, exec_ctx_t *exec_ctx)
{
if (!ensure_buff_av_capacity(buff, 1))
return false;
buff->str[buff->sz - 1] = '\0';
buff->sz++;
if (isatty(STDIN_FILENO))
if (isatty(exec_ctx->read_fd))
WRITE_CONST(STDOUT_FILENO, "\n");
return true;
}
static
int8_t handle_line_buff(buff_t *buff, char *read_buff, ssize_t read_size)
int8_t handle_line_buff(exec_ctx_t *exec_ctx, buff_t *buff, char *read_buff,
ssize_t read_size)
{
if (handle_keys(buff, read_buff))
if (handle_keys(exec_ctx, buff, read_buff))
return RETURN_SUCCESS;
if (isatty(STDIN_FILENO) && str_printable(read_buff, read_size))
if (isatty(exec_ctx->read_fd) && str_printable(read_buff, read_size))
write(STDOUT_FILENO, read_buff, read_size);
if (!ensure_buff_av_capacity(buff, read_size))
return RETURN_FAILURE;
@@ -96,7 +98,7 @@ int8_t handle_line_buff(buff_t *buff, char *read_buff, ssize_t read_size)
return -1;
}
bool readline(buff_t *buff)
bool readline(exec_ctx_t *exec_ctx, buff_t *buff, int fd)
{
char read_buff[2] = "";
ssize_t read_size = 0;
@@ -105,13 +107,13 @@ bool readline(buff_t *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);
read_size = read(fd, &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)
if (handle_line_buff(exec_ctx, buff, read_buff, read_size) > -1)
return true;
}
return append_null_terminator(buff);
return append_null_terminator(buff, exec_ctx);
}

View File

@@ -10,7 +10,8 @@
#define BUFF_INIT_SZ 16
#include <stdbool.h>
#include "exec.h"
#include "u_str.h"
bool readline(buff_t *buff);
bool readline(exec_ctx_t *exec_ctx, buff_t *buff, int fd);
#endif /* READLINE */

View File

@@ -6,19 +6,47 @@
*/
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include "common.h"
#include "exec.h"
#include "repl.h"
#include "u_str.h"
#include "vt100_esc_codes.h"
void print_shell_prompt(exec_ctx_t *exec_ctx)
{
env_t *env_ptr = exec_ctx->env;
char const *ps1 = get_env_value(env_ptr, "PS1");
char hostname[64];
if (ps1 == nullptr) {
if (gethostname(hostname, 64) < 0)
return;
printf(BLUE PROMPT_HEADER GREEN "%s" RESET "@" CYAN "%s" BLUE "] "
RESET "-" BLUE " [" RESET "%s" BLUE
"] " RESET "-" BLUE " [" YELLOW "%d" BLUE
"]\n└─[" PURPLE "$" BLUE "] " RESET,
get_env_value(env_ptr, "USER"),
hostname,
get_env_value(env_ptr, "PWD"),
exec_ctx->history_command->sz);
} else
printf("%s", ps1);
}
void init_shell_repl(exec_ctx_t *exec_ctx)
{
struct termios repl_settings;
setvbuf(stdout, nullptr, _IONBF, 0);
signal(SIGINT, SIG_IGN);
exec_ctx->is_running = true;
if (isatty(STDIN_FILENO)) {
if (isatty(exec_ctx->read_fd)) {
WRITE_CONST(STDOUT_FILENO, BLINKING_VERTICAL_CURSOR);
tcgetattr(STDIN_FILENO, &repl_settings);
exec_ctx->saved_term_settings = repl_settings;
repl_settings.c_iflag = IXON;
@@ -33,20 +61,20 @@ void restore_term_flags(exec_ctx_t *exec_ctx)
}
static
void ignore_sigint(void)
void ignore_sigint(exec_ctx_t *exec_ctx)
{
WRITE_CONST(STDIN_FILENO, "\n");
WRITE_CONST(STDOUT_FILENO, SHELL_PROMPT);
print_shell_prompt(exec_ctx);
}
bool handle_keys(buff_t *buff, char *read_buff)
bool handle_keys(exec_ctx_t *exec_ctx, buff_t *buff, char const *read_buff)
{
switch (*read_buff) {
case CTRL('d'):
buff->sz = 0;
return true;
case CTRL('c'):
ignore_sigint();
ignore_sigint(exec_ctx);
return false;
default:
return false;

View File

@@ -12,5 +12,6 @@
void init_shell_repl(exec_ctx_t *exec_ctx);
void restore_term_flags(exec_ctx_t *exec_ctx);
bool handle_keys(buff_t *buff, char *read_buff);
bool handle_keys(exec_ctx_t *exec_ctx, buff_t *buff, char const *read_buff);
void print_shell_prompt(exec_ctx_t *exec_ctx);
#endif /* REPL_H */

View File

@@ -5,6 +5,7 @@
** _
*/
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -20,7 +21,7 @@
#include "repl.h"
#include "shell.h"
#include "u_str.h"
#include "visitor.h"
#include "utils.h"
__attribute__((unused))
static
@@ -45,20 +46,20 @@ void check_basic_error(char const *buffer)
}
static
void write_prompt(int is_a_tty)
void write_prompt(int is_a_tty, exec_ctx_t *exec_ctx)
{
if (is_a_tty)
WRITE_CONST(STDOUT_FILENO, SHELL_PROMPT);
print_shell_prompt(exec_ctx);
}
static
bool change_shell_command(buff_t *buff, exec_ctx_t *exec_ctx)
{
char *tmp_buff = NULL;
char *tmp_buff = nullptr;
size_t buffer_len;
buff->sz = 0;
if (!readline(buff))
if (!readline(exec_ctx, buff, exec_ctx->read_fd))
return false;
if (!buff->sz)
return false;
@@ -78,15 +79,14 @@ bool change_shell_command(buff_t *buff, exec_ctx_t *exec_ctx)
static
int shell_loop(int is_a_tty, exec_ctx_t *exec_ctx)
{
buff_t buff = { .str = NULL, 0, .cap = BUFF_INIT_SZ };
buff_t buff = { .str = nullptr, 0, .cap = BUFF_INIT_SZ };
init_shell_repl(exec_ctx);
while (true) {
write_prompt(is_a_tty);
write_prompt(is_a_tty, exec_ctx);
if (!change_shell_command(&buff, exec_ctx))
return exec_ctx->history->last_exit_code;
}
free(exec_ctx->history_command);
return free(buff.str), exec_ctx->history->last_exit_code;
}
@@ -96,10 +96,10 @@ his_command_t *init_cmd_history(void)
his_command_t *cmd_history = malloc(sizeof(his_command_t) * 100);
if (cmd_history == NULL)
return NULL;
return nullptr;
for (int i = 0; i != 100; i++){
cmd_history[i].arg = NULL;
cmd_history[i].command = NULL;
cmd_history[i].arg = nullptr;
cmd_history[i].command = nullptr;
cmd_history[i].id = i;
}
cmd_history->sz = 0;
@@ -121,24 +121,37 @@ bool error_in_init(exec_ctx_t *exec_ctx)
return false;
}
#include <signal.h>
int shell(char **env_ptr)
static
int get_read_fd(opt_t *opt)
{
int fd;
if (opt->script_file == NULL)
return STDIN_FILENO;
fd = open(opt->script_file, O_RDONLY);
if (fd < 0)
return perror(opt->script_file), -1;
return fd;
}
int shell(opt_t *opt, char **env_ptr)
{
alias_t alias = init_alias();
env_t env = parse_env(env_ptr);
history_t history = { .cmd_history = NULL, .last_exit_code = 0,
.last_chdir = NULL};
history_t history = { .cmd_history = nullptr, .last_exit_code = 0,
.last_chdir = nullptr};
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};
exec_ctx_t exec_ctx = {.env = &env, .local = &local, .opt = opt,
.read_fd = get_read_fd(opt), .history = &history,
.history_command = cmd_history, .alias = &alias};
int shell_result;
if (error_in_init(&exec_ctx) == true)
if (exec_ctx.read_fd == -1 || (int)error_in_init(&exec_ctx))
return RETURN_FAILURE;
U_DEBUG_CALL(debug_env_entries, &env);
shell_result = shell_loop(isatty(STDIN_FILENO), &exec_ctx);
if (isatty(STDIN_FILENO)) {
shell_result = shell_loop(isatty(exec_ctx.read_fd), &exec_ctx);
if (isatty(exec_ctx.read_fd)) {
WRITE_CONST(STDOUT_FILENO, "exit\n");
restore_term_flags(&exec_ctx);
}

View File

@@ -7,9 +7,7 @@
#ifndef SHELL_H
#define SHELL_H
#include "vt100_esc_codes.h"
#define SHELL_PROMPT RED "|> " RESET
#include "common.h"
typedef struct {
char **cmd_history;
@@ -17,5 +15,5 @@ typedef struct {
char *last_chdir;
} history_t;
int shell(char **env);
int shell(opt_t *opt, char **env);
#endif /* SHELL_H */

View File

@@ -9,10 +9,12 @@
#ifndef UTILS_H
#define UTILS_H
#include "history.h"
#include "u_str.h"
#include "exec.h"
char *strn_to_ndup(int start, int size, char *str);
bool is_a_token(char *str, int index_str);
char *cat_in_str(his_variable_t *his_variable, char *str, char *cpy);
int len_array(char **array);
char *insert_str(const char *base, const char *insert, size_t pos);
void free_everything(exec_ctx_t *exec_ctx);
#endif /* UTILS_H */

29
src/utils/insert_str.c Normal file
View File

@@ -0,0 +1,29 @@
/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
char *insert_str(const char *base, const char *insert, size_t pos)
{
size_t base_len = strlen(base);
size_t insert_len = strlen(insert);
size_t new_len;
char *res;
if (pos >= base_len)
pos = base_len;
new_len = base_len - 1 + insert_len;
res = malloc(sizeof(char) * (new_len + 1));
if (res == nullptr)
return nullptr;
memcpy(res, base, pos);
strncpy(res + pos, insert, insert_len);
memcpy(res + pos + insert_len, base + pos + 1, base_len - pos - 1 + 1);
return res;
}

View File

@@ -21,13 +21,9 @@ int visit_and(ef_t *ef, ast_t *node)
if (!node->binary.left || !node->binary.right)
return WRITE_CONST(STDERR_FILENO, "Invalid null l/r command.\n"),
RETURN_FAILURE;
result = visit_list(ef, node->binary.left);
if (!result) {
if (node->binary.right->tok.type & (T_AND | T_OR))
result = visit_condition(ef, node->binary.right);
else
result = visit_list(ef, node->binary.right);
}
result = visit_condition(ef, node->binary.left);
if (!result)
result = visit_condition(ef, node->binary.right);
return result;
}
@@ -38,13 +34,10 @@ int visit_or(ef_t *ef, ast_t *node)
if (!node->binary.left || !node->binary.right)
return WRITE_CONST(STDERR_FILENO, "Invalid null l/r command.\n"),
RETURN_FAILURE;
result = visit_list(ef, node->binary.left);
result = visit_condition(ef, node->binary.left);
if (result) {
ef->history->last_exit_code = 0;
if (node->binary.right->tok.type & (T_AND | T_OR))
result = visit_condition(ef, node->binary.right);
else
result = visit_list(ef, node->binary.right);
result = visit_condition(ef, node->binary.right);
}
return result;
}

View File

@@ -116,7 +116,7 @@ int visit_condition(ef_t *ef, ast_t *node)
case T_OR:
return visit_or(ef, node);
default:
return RETURN_FAILURE;
return visit_list(ef, node);
}
}
@@ -200,7 +200,7 @@ int visitor(char *buffer, exec_ctx_t *exec_ctx)
return RETURN_FAILURE;
result = visitor_launcher(&ef);
if (ef.flags & F_EXIT)
builtins_exit(&ef, NULL);
builtins_exit(&ef, nullptr);
free_ast(&ctx);
return result == -1 ? RETURN_FAILURE : result;
}

View File

@@ -11,6 +11,9 @@
#define ESC "\033"
#define CFMT(n) ESC "[" #n "m"
// special
#define BLINKING_VERTICAL_CURSOR ESC "[5 q"
// move
#define MOVE_RIGHT(n) ESC "[" #n "C"

View File

@@ -9,16 +9,16 @@
char **arraydup(char **array)
{
char **dup_array = NULL;
char **dup_array = nullptr;
int i = 0;
if (array == NULL)
return NULL;
dup_array = malloc(sizeof(char *) * (my_array_len(array) + 1));
if (dup_array == NULL)
return NULL;
for (; array[i] != NULL; i++)
if (array == nullptr)
return nullptr;
dup_array = (char **)malloc(sizeof(char *) * (my_array_len(array) + 1));
if (dup_array == nullptr)
return nullptr;
for (; array[i] != nullptr; i++)
dup_array[i] = u_strdup(array[i]);
dup_array[i] = NULL;
dup_array[i] = nullptr;
return dup_array;
}

View File

@@ -39,7 +39,7 @@ TESTS = [
cmds=[
"/bin/ls\n",
"/../bin/ls\n",
# "~/../../bin/sh --version\n",
"~/../../bin/ls\n",
"fixtures/exec.sh\n",
],
depends_on=("ARGS",)
@@ -124,7 +124,7 @@ TESTS = [
"echo kek | grep kek\n",
"ls | grep Makefile\n",
"who | wc -l\n",
"ls | | cat\n", # Syntax error
"ls | | cat\n",
],
depends_on=("ARGS",)
),
@@ -196,59 +196,121 @@ TESTS = [
"aa||aa\n",
"ls||aa\n",
"&&\n",
"ls && ls || ls && ls\n",
"ls && ls a || ls && ls\n",
"ls && ls || ls && ls a\n",
"ls && ls || ls && a\n",
"ls aa && ls || ls\n",
],
depends_on=()
),
Test(
key="QUOTES",
name="quotes handling",
key="BACKTICKS",
name="backticks",
cmds=[
"echo 'plop'\n",
"echo \"plop\"\n",
"echo 'plop kek'\n",
"echo \"plop kek\"\n",
"echo 'plop\"kek'\"\n",
"echo \"plop'kek\"'\n",
"echo \"Hello $USER\"\n",
"echo 'Hello $USER'\n",
"echo Hello $USER\n",
"echo \"$USER\"\n",
"echo '$USER'\n",
],
depends_on=("ENV_EXPANSION",)
"echo `ls`\n",
"echo `ls\n",
"echo ls`\n",
],
depends_on=()
),
Test(
key="VAR_INTERP",
name="variable assignment and interpolation",
key="IF",
name="if",
cmds=[
"setenv FOO bar\n",
"echo $FOO\n",
"echo '$FOO'\n",
"echo \"$FOO\"\n",
"setenv BAR 'quoted value'\n",
"echo $BAR\n",
"unsetenv FOO\n",
"echo $FOO\n",
"if 1 ls\n",
"if 0 ls\n",
"if 1 then\necho YES\nelse\necho NO\nendif\n",
"if 0 then\necho YES\nelse\necho NO\nendif\n",
"if\n",
"if 0\n",
],
depends_on=("ENV", "QUOTES")
depends_on=()
),
##Test(
## key="BACKTICKS",
## name="backticks",
## cmds=[
## "echo `ls`\n",
## "echo `ls`l\n",
## "echo l`ls`\n",
## "echo ``\n",
## "`ls`\n",
## "``\n",
## "echo `ls\n",
## "echo ls`\n",
## "`\n",
## ],
## depends_on=()
##),
Test(
key="EXPR",
name="expr builtin command",
cmds=[
"expr 1 + 2",
"expr 4 - 2",
#"expr 3 \\* 5",
#"expr 5 = 5",
#"expr 5 \\< 10",
#"expr 5 \\> 3",
],
depends_on=("ARGS",)
),
Test(
key="TILDE",
name="tilde",
cmds=[
"echo ~",
],
depends_on=("ARGS",)
),
Test(
key="GLOB",
name="globbing",
cmds=[
"ls *\n",
"echo *.nix\n",
"echo *file*\n",
"echo fixtures/*.sh\n",
"echo doesnotexist*\n",
"ls *.c *.h\n",
"echo *.txt > out.txt\n",
"cat < out.txt\n",
],
depends_on=("REDIR",)
),
Test(
key="REPEAT",
name="repeat loop",
cmds=[
"repeat 3 echo plop\n",
"repeat 3\n",
"repeat\n",
],
depends_on=("ARGS",)
),
Test(
key="FOREACH",
name="foreach loop",
cmds=[
"foreach i (1 2 3)\necho $i\nend\n",
"foreach i",
"foreach",
],
depends_on=("ARGS",)
),
Test(
key="WHICH",
name="which/where commands",
cmds=[
"which ls\n",
"which cd\n",
"alias ll ls\nwhere ll\n",
#"alias ll ls\nwhich ll\n",
],
depends_on=("ALIAS",)
),
Test(
key="BUILTIN_ERR",
name="invalid builtin usage",
cmds=[
"cd too many args here\n",
"unsetenv\n",
"setenv ONLYKEY\n",
],
depends_on=("CD", "ENV")
),
]

View File

@@ -66,7 +66,7 @@ class Test:
key: str,
name: str,
cmds: list[str],
depends_on: tuple[str, ...] = ()
depends_on: tuple[str, ...] = (),
):
self.key = key
self.name = name
@@ -74,8 +74,8 @@ class Test:
self.depends_on = depends_on
self.has_run = False
def _run_each_cmd(self, cmd: str):
result_42sh = run_shell(["./42sh"], cmd)
def _run_each_cmd(self, cmd: str, tested_bin):
result_42sh = run_shell([tested_bin], cmd)
result_tcsh = run_shell(["tcsh"], cmd)
if result_42sh.exit_code == 84 and result_tcsh.exit_code != 0:
@@ -88,7 +88,7 @@ class Test:
print("\033[31m.\033[0m", end='') # ]]
return cmd, result_42sh, result_tcsh
def run(self, test_map) -> bool:
def run(self, test_map, tested_bin) -> bool:
if self.has_run:
return True
@@ -104,7 +104,7 @@ class Test:
continue
if not dep.has_run:
success &= dep.run(test_map)
success &= dep.run(test_map, tested_bin)
if not success:
return False
@@ -112,7 +112,7 @@ class Test:
failures = []
for cmd in self.cmds:
if (failure := self._run_each_cmd(cmd)) is not None:
if (failure := self._run_each_cmd(cmd, tested_bin)) is not None:
failures.append(failure)
if not failures:
print(" \033[32mOK\033[0m") # ]]
@@ -130,7 +130,10 @@ def main():
success = True
for test in TESTS:
success &= test.run(test_map=test_map)
success &= test.run(
test_map=test_map,
tested_bin=sys.argv[1] if len(sys.argv) > 1 else "./42sh"
)
return not success