Merge pull request #14 from Savapitech/builtins_adds

Add of foreach, set, unset and local variable gesture.
This commit is contained in:
savalet
2025-04-24 13:24:58 +02:00
committed by GitHub
18 changed files with 465 additions and 22 deletions

View File

@@ -15,6 +15,7 @@ SRC := $(wildcard src/*.c)
SRC += $(wildcard src/builtins/*.c)
SRC += $(wildcard src/ast/*.c)
SRC += $(wildcard src/utils/*.c)
SRC += $(wildcard src/local/*.c)
LIB_SRC := $(wildcard ulib/*.c)
LIB_SRC += $(wildcard ulib/write/printf/*.c)

View File

@@ -37,14 +37,13 @@ typedef enum {
T_IN_REDIRECT = 1 << 14, // <
T_AT = 1 << 15, // <
T_WHILE = 1 << 16, // while
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_EOF = 1 << 22, // \0
T_ARG = 1 << 23,
T_INVALID = 1 << 24
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
} token_type_t;
typedef enum {

View File

@@ -59,7 +59,7 @@ ast_t *fill_cmd_node(ast_ctx_t *ctx)
ast_t *parse_cmd(ast_ctx_t *ctx)
{
if (ctx->act_tok.type != T_ARG) {
if (ctx->act_tok.type & (T_WHILE | T_FOREACH))
if (ctx->act_tok.type & (T_WHILE))
return NULL;
if (!parser_eat(ctx, T_ARG))
return NULL;
@@ -107,7 +107,7 @@ ast_t *parse_condition(ast_ctx_t *ctx)
if (l_node == NULL)
return NULL;
if (ctx->act_tok.type & (T_WHILE | T_FOREACH))
if (ctx->act_tok.type & (T_WHILE))
ctx->ast = parse_loop(ctx);
else {
switch (ctx->act_tok.type) {

View File

@@ -80,12 +80,10 @@ ast_t *increase_buffers(ast_t *node, size_t *buffer_len)
static
ast_t *get_usr_loop_cmd(ast_t *node)
{
char prompt[] = "foreach? ";
char prompt[] = "while? ";
size_t buffer_len;
node->loop.buffers = malloc(sizeof(char *) * node->loop.cap);
if (node->tok.type == T_WHILE)
strcpy(prompt, "while? ");
node = get_first_cmd(node, prompt, &buffer_len);
while (strcmp("end", node->loop.buffers[node->loop.sz - 1])){
printf("%s", prompt);

View File

@@ -30,6 +30,9 @@ int builtins_alias(ef_t *ef, char **args);
int builtins_display_alias(alias_t *alias);
int builtins_repeat(ef_t *ef, char **args);
int builtins_yes(ef_t *ef, char **args);
int builtins_foreach(ef_t *ef, char **args);
int builtins_set(ef_t *ef, char **args);
int builtins_unset(ef_t *ef, char **args);
int builtins_where(ef_t *ef, char **args);
int builtins_which(ef_t *ef, char **args);
#endif /* BUILTIND_H */

View File

@@ -49,7 +49,7 @@ int builtins_unsetenv(ef_t *ef, char **args)
if (args[1] == NULL)
return (WRITE_CONST(STDERR_FILENO, "unsetenv: Too few arguments.\n"),
RETURN_FAILURE);
if (!unset_env(ef->env, args[1]))
return RETURN_FAILURE;
for (int i = 0; args[i]; i++)
unset_env(ef->env, args[1]);
return RETURN_SUCCESS;
}

181
src/builtins/foreach.c Normal file
View File

@@ -0,0 +1,181 @@
/*
** EPITECH PROJECT, 2025
** 42sh
** File description:
** foreach
*/
#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 "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"
static
bool checking_error(ef_t *ef, char **args)
{
if (my_array_len(args) < 3)
return (WRITE_CONST(STDERR_FILENO, "foreach: Too few arguments.\n"),
true);
if (check_local_var(args[1], args[0]))
return true;
return false;
}
static
usr_cmd_t *buffers_realloc(usr_cmd_t *usr)
{
char **new_buffers = u_realloc(usr->local_var, sizeof
*usr->local_var * usr->sz, sizeof
*usr->local_var * (usr->cap << 1));
if (new_buffers == NULL)
return NULL;
usr->local_var = new_buffers;
usr->cap <<= 1;
return usr;
}
static
usr_cmd_t *get_first_cmd(usr_cmd_t *usr, char prompt[], size_t *bf_len)
{
printf("%s", prompt);
usr->local_var[usr->sz] = NULL;
getline(&(usr->local_var[usr->sz]), bf_len, stdin);
*bf_len = u_strlen(usr->local_var[usr->sz]);
usr->local_var[usr->sz][*bf_len - 1] = '\0';
usr->sz += 1;
return usr;
}
static
usr_cmd_t *increase_buffers(usr_cmd_t *usr, size_t *buffer_len)
{
usr->local_var[usr->sz] = NULL;
getline(&(usr->local_var[usr->sz]), buffer_len, stdin);
*buffer_len = u_strlen(usr->local_var[usr->sz]);
usr->local_var[usr->sz][*buffer_len - 1] = '\0';
usr->sz++;
return usr;
}
static
usr_cmd_t *handle_end(usr_cmd_t *us, char prompt[])
{
us->sz--;
if (!us->local_var[us->sz] ||
strcmp("end", us->local_var[us->sz])){
printf("%s: end not found.\n", prompt);
free_array(us->local_var);
us->local_var = NULL;
exit(RETURN_FAILURE);
return NULL;
}
free(us->local_var[us->sz]);
us->local_var[us->sz] = NULL;
return us;
}
static
usr_cmd_t *get_usr_loop_cmd(usr_cmd_t *usr_cmd)
{
char prompt[] = "foreach? ";
size_t buffer_len;
usr_cmd->local_var = malloc(sizeof(char *) * usr_cmd->cap);
if (usr_cmd->local_var == NULL)
return NULL;
usr_cmd = get_first_cmd(usr_cmd, prompt, &buffer_len);
while (strcmp("end", usr_cmd->local_var[usr_cmd->sz - 1])){
printf("foreach? ");
if (usr_cmd->sz >= usr_cmd->cap)
usr_cmd = buffers_realloc(usr_cmd);
if (usr_cmd == NULL)
return NULL;
increase_buffers(usr_cmd, &buffer_len);
}
usr_cmd = handle_end(usr_cmd, prompt);
return usr_cmd;
}
static
int do_a_lap(ef_t *ef, char **args)
{
int status = 0;
for (int i = 0; args[i]; i++)
status = visitor(args[i], ef->exec_ctx);
return status;
}
static
int foreach_loop(ef_t *ef, char **args, usr_cmd_t *usr_cmds)
{
int status = 0;
char **save_cmds = arraydup(usr_cmds->local_var);
if (save_cmds == NULL)
exit(84);
for (int i = 2; args[i]; i++){
if (!set_local(ef->exec_ctx->local, args[1], args[i]))
exit(84);
status = do_a_lap(ef, usr_cmds->local_var);
free_array(usr_cmds->local_var);
usr_cmds->local_var = arraydup(save_cmds);
}
free_array(save_cmds);
return status;
}
static
void launch_loop(ef_t *ef, char **args)
{
int status = RETURN_FAILURE;
usr_cmd_t *usr_cmds = malloc(sizeof(usr_cmd_t));
if (usr_cmds == NULL)
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);
if (usr_cmds == NULL)
exit(84);
status = foreach_loop(ef, args, usr_cmds);
free_array(usr_cmds->local_var);
free(usr_cmds);
exit(status);
}
int builtins_foreach(ef_t *ef, char **args)
{
int status = 0;
pid_t pid;
if (checking_error(ef, args))
return RETURN_FAILURE;
pid = fork();
if (pid == 0)
launch_loop(ef, args);
else
wait(&status);
if (WIFEXITED(status))
ef->history->last_exit_code =
ef->history->last_exit_code ?: WEXITSTATUS(status);
return status;
}

124
src/builtins/local.c Normal file
View File

@@ -0,0 +1,124 @@
/*
** EPITECH PROJECT, 2025
** 42sh
** File description:
** local
*/
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "common.h"
#include "env.h"
#include "exec.h"
#include "u_str.h"
#include "u_mem.h"
bool check_local_var(char *var, char *func_name)
{
if (!isalpha(var[0]))
return (fprintf(stdout, "%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"
" alphanumeric characters.\n", func_name), RETURN_FAILURE);
return RETURN_SUCCESS;
}
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)
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;
}
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;
local.sz++;
return local;
}
static
bool ensure_local_capacity(local_t *local)
{
char **new_ptr = NULL;
if (local->sz < local->cap)
return true;
new_ptr = (char **)u_realloc((void *)local->local_var,
sizeof *local->local_var * local->sz,
sizeof *local->local_var * local->cap << 1);
if (!new_ptr)
return false;
local->cap <<= 1;
local->local_var = new_ptr;
return true;
}
bool set_local(local_t *local, char *var, char *value)
{
char *new_loc = NULL;
size_t key_len = u_strlen(var);
size_t value_len = u_strlen(value);
if (get_local_value(local, var) != NULL)
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)
return false;
u_bzero(new_loc, key_len + value_len + 2);
u_strcpy(new_loc, var);
new_loc[key_len] = '\t';
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;
return true;
}
static
void unset_local_move(local_t *local, size_t i)
{
while (local->local_var[i]) {
local->local_var[i] = local->local_var[i + 1];
i++;
}
}
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)
continue;
if (u_strcspn(local->local_var[i], '\t') != key_len)
continue;
if (u_strcmp(local->local_var[i], var) == 0) {
unset_local_move(local, i);
local->sz--;
return true;
}
}
return false;
}

51
src/builtins/set.c Normal file
View File

@@ -0,0 +1,51 @@
/*
** EPITECH PROJECT, 2025
** 42sh
** File description:
** set
*/
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "common.h"
#include "env.h"
#include "exec.h"
#include "u_str.h"
void print_local(ef_t *ef)
{
for (size_t i = 0; i < ef->exec_ctx->local->sz; i++) {
if (ef->exec_ctx->local->local_var[i] == NULL)
continue;
write(ef->out_fd, ef->exec_ctx->local->local_var[i],
u_strlen(ef->exec_ctx->local->local_var[i]));
WRITE_CONST(ef->out_fd, "\n");
}
}
int builtins_set(ef_t *ef, char **args)
{
char *var = NULL;
if (args[1] == NULL)
return (print_local(ef), RETURN_SUCCESS);
for (int i = 1; args[i]; i++){
if (check_local_var(args[i], args[0]))
return RETURN_FAILURE;
var = args[i];
i++;
if (!args[i])
return (set_local(ef->exec_ctx->local, var, NULL)
, RETURN_SUCCESS);
if (strcmp(args[i], "="))
return RETURN_FAILURE;
i++;
if (!set_local(ef->exec_ctx->local, var, args[i]))
return RETURN_FAILURE;
}
return RETURN_SUCCESS;
}

28
src/builtins/unset.c Normal file
View File

@@ -0,0 +1,28 @@
/*
** EPITECH PROJECT, 2025
** 42sh
** File description:
** unset
*/
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "common.h"
#include "env.h"
#include "exec.h"
#include "u_str.h"
int builtins_unset(ef_t *ef, char **args)
{
if (args[1] == NULL)
return (WRITE_CONST(STDERR_FILENO, "unset: Too few arguments.\n"),
RETURN_FAILURE);
for (int i = 1; args[i]; i++)
unset_local(ef->exec_ctx->local, args[i]);
return RETURN_SUCCESS;
}

View File

@@ -16,6 +16,7 @@
#include "common.h"
#include "exec.h"
#include "u_str.h"
#include "u_mem.h"
static
int len_buffer(char **args)
@@ -37,13 +38,15 @@ char *define_prompt(char **args)
buffer = malloc(sizeof(char) * (len_buffer(args) + 1));
if (buffer == NULL)
return NULL;
u_bzero(buffer, len_buffer(args) + 1);
strcpy(buffer, args[1]);
buffer[strlen(buffer)] = ' ';
for (int i = 2; args[i]; i++){
strcat(buffer, args[i]);
if (args[i + 1] != NULL)
buffer[strlen(buffer) - 1] = ' ';
buffer[strlen(buffer)] = ' ';
}
buffer[strlen(buffer) - 1] = '\n';
buffer[strlen(buffer)] = '\n';
return buffer;
}

View File

@@ -12,12 +12,14 @@
#include "history.h"
#include "shell.h"
#include "alias.h"
#include "local.h"
typedef struct {
env_t *env;
history_t *history;
his_command_t *history_command;
alias_t *alias;
local_t *local;
} exec_ctx_t;
size_t update_command(char **buffer,

View File

@@ -38,6 +38,9 @@ const builtins_funcs_t BUILTINS[] = {
{ "history", &builtins_history},
{ "alias", &builtins_alias},
{ "yes", &builtins_yes },
{ "foreach", &builtins_foreach },
{ "set", &builtins_set },
{ "unset", &builtins_unset },
{ "where", &builtins_where },
{ "which", &builtins_which }
};
@@ -75,7 +78,7 @@ char **parse_args(ef_t *ef, ast_t *node, env_t *env)
if (ef->skip_sz > 0 && i >= ef->skip_i && i < ef->skip_i + ef->skip_sz)
continue;
ensure_args_capacity(&args, sz, &cap);
args[sz] = handle_var_case(node, env, &i);
args[sz] = handle_var_case(node, ef->exec_ctx, &i);
if (args[sz] == NULL)
return free(args), NULL;
sz++;

View File

@@ -51,5 +51,5 @@ int execute(ef_t *ef);
int exec_the_args(ef_t *ef, char **args);
void exit_child(int sig __attribute__((unused)));
int visit_loop(ef_t *ef, ast_t *node);
char *handle_var_case(ast_t *node, env_t *env, size_t *i);
char *handle_var_case(ast_t *node, exec_ctx_t *ctx, size_t *i);
#endif /* EXEC_H */

View File

@@ -8,15 +8,18 @@
#include "ast.h"
#include "env.h"
#include "stdio.h"
#include "local.h"
char *handle_var_case(ast_t *node, env_t *env, size_t *i)
char *handle_var_case(ast_t *node, exec_ctx_t *ctx, size_t *i)
{
char *r_char;
if (node->vector.tokens[*i].type == T_VAR && *i + 1 < node->vector.sz) {
*i += 1;
node->vector.tokens[*i].str[node->vector.tokens[*i].sz] = '\0';
r_char = get_env_value(env, node->vector.tokens[*i].str);
r_char = get_env_value(ctx->env, node->vector.tokens[*i].str);
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", node->vector.tokens[*i].str);
return NULL;

26
src/local.h Normal file
View File

@@ -0,0 +1,26 @@
/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#ifndef LOCAL_H
#define LOCAL_H
#include <stdbool.h>
#include <stddef.h>
typedef struct {
size_t sz;
size_t cap;
char **local_var;
} local_t;
bool set_local(local_t *local, char *var, char *value);
bool change_local(local_t *local, char *var, char *value);
bool check_var(char *local, char *var);
bool check_local_var(char *var, char *func_name);
bool unset_local(local_t *local, char *var);
char *get_local_value(local_t *local, char const *key);
local_t create_local(void);
#endif /* LOCAL_H */

18
src/loop.h Normal file
View File

@@ -0,0 +1,18 @@
/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#ifndef LOOP_H
#define LOOP_H
#include <stdbool.h>
#include <stddef.h>
typedef struct {
size_t sz;
size_t cap;
char **local_var;
} usr_cmd_t;
#endif /* LOOP_H */

View File

@@ -18,6 +18,8 @@
#include "history.h"
#include "shell.h"
#include "u_str.h"
#include "local.h"
#include "loop.h"
__attribute__((unused))
static
@@ -145,7 +147,8 @@ int shell(char **env_ptr)
env_t env = parse_env(env_ptr);
history_t history = { .cmd_history = NULL, 0, .last_chdir = NULL};
his_command_t *cmd_history = init_cmd_history();
exec_ctx_t exec_ctx = {.env = &env,
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;