diff --git a/Makefile b/Makefile index 580fa5f..1e495ce 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/src/ast.h b/src/ast.h index 9af9d86..4187be7 100644 --- a/src/ast.h +++ b/src/ast.h @@ -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 { diff --git a/src/ast/ast.c b/src/ast/ast.c index 2ee8754..0f40952 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -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) { diff --git a/src/ast/loop.c b/src/ast/loop.c index f53e0e4..b2a5b58 100644 --- a/src/ast/loop.c +++ b/src/ast/loop.c @@ -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); diff --git a/src/builtins.h b/src/builtins.h index d1a0549..a2ee2cd 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -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 */ diff --git a/src/builtins/env.c b/src/builtins/env.c index 8f2f252..3665aab 100644 --- a/src/builtins/env.c +++ b/src/builtins/env.c @@ -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; } diff --git a/src/builtins/foreach.c b/src/builtins/foreach.c new file mode 100644 index 0000000..f24dff3 --- /dev/null +++ b/src/builtins/foreach.c @@ -0,0 +1,181 @@ +/* +** EPITECH PROJECT, 2025 +** 42sh +** File description: +** foreach +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/builtins/local.c b/src/builtins/local.c new file mode 100644 index 0000000..18be358 --- /dev/null +++ b/src/builtins/local.c @@ -0,0 +1,124 @@ +/* +** EPITECH PROJECT, 2025 +** 42sh +** File description: +** local +*/ + +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/builtins/set.c b/src/builtins/set.c new file mode 100644 index 0000000..d766704 --- /dev/null +++ b/src/builtins/set.c @@ -0,0 +1,51 @@ +/* +** EPITECH PROJECT, 2025 +** 42sh +** File description: +** set +*/ + +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/builtins/unset.c b/src/builtins/unset.c new file mode 100644 index 0000000..ced6864 --- /dev/null +++ b/src/builtins/unset.c @@ -0,0 +1,28 @@ +/* +** EPITECH PROJECT, 2025 +** 42sh +** File description: +** unset +*/ + +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/builtins/yes.c b/src/builtins/yes.c index 5678eab..2790f4e 100644 --- a/src/builtins/yes.c +++ b/src/builtins/yes.c @@ -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; } diff --git a/src/builtins_handler.h b/src/builtins_handler.h index 02c4a2a..b0793c3 100644 --- a/src/builtins_handler.h +++ b/src/builtins_handler.h @@ -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, diff --git a/src/exec.c b/src/exec.c index 45129e6..f5c5b45 100644 --- a/src/exec.c +++ b/src/exec.c @@ -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++; diff --git a/src/exec.h b/src/exec.h index 88b75a5..5b56ace 100644 --- a/src/exec.h +++ b/src/exec.h @@ -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 */ diff --git a/src/handle_vars.c b/src/handle_vars.c index f271292..38ff33d 100644 --- a/src/handle_vars.c +++ b/src/handle_vars.c @@ -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; diff --git a/src/local.h b/src/local.h new file mode 100644 index 0000000..ef8f6b2 --- /dev/null +++ b/src/local.h @@ -0,0 +1,26 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef LOCAL_H + #define LOCAL_H + #include + #include + +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 */ diff --git a/src/loop.h b/src/loop.h new file mode 100644 index 0000000..997b3da --- /dev/null +++ b/src/loop.h @@ -0,0 +1,18 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef LOOP_H + #define LOOP_H + #include + #include + +typedef struct { + size_t sz; + size_t cap; + char **local_var; +} usr_cmd_t; +#endif /* LOOP_H */ diff --git a/src/shell.c b/src/shell.c index d972a49..5f25136 100644 --- a/src/shell.c +++ b/src/shell.c @@ -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;