Compare commits
3 Commits
solo
...
feature/bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d39eca2c94 | ||
|
|
084fa4759c | ||
|
|
778e0c0481 |
13
AGENTS.md
13
AGENTS.md
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
## Project Structure & Module Organization
|
## Project Structure & Module Organization
|
||||||
- `src/` holds the shell implementation, with submodules for `builtins/`, `executor/`, `parser/`, and `variables/`.
|
- `src/` holds the shell implementation, with submodules for `builtins/`, `executor/`, `parser/`, and `variables/`.
|
||||||
|
- `src/builtins/` currently includes: `cd`, `echo`, `env`, `exit`, `export`, `pwd`, `unset`.
|
||||||
- `include/` contains public headers used across modules.
|
- `include/` contains public headers used across modules.
|
||||||
- `lib/` is populated at build time with third-party 42 libraries (libft, get_next_line, ft_printf, ft_args).
|
- `lib/` is populated at build time with third-party 42 libraries (libft, get_next_line, ft_printf, ft_args).
|
||||||
- `docs/` stores project references and manual test notes (see `docs/tests.md`).
|
- `docs/` stores project references and manual test notes (see `docs/tests.md`).
|
||||||
@@ -19,11 +20,23 @@
|
|||||||
- The codebase follows 42 **Norminette v4** rules. Run `norminette *.c *.h` (or on specific files) before submitting changes.
|
- The codebase follows 42 **Norminette v4** rules. Run `norminette *.c *.h` (or on specific files) before submitting changes.
|
||||||
- Keep file names lowercase with underscores (e.g., `src/builtins/echo/echo.c`).
|
- Keep file names lowercase with underscores (e.g., `src/builtins/echo/echo.c`).
|
||||||
- Keep headers in `include/` and expose only what modules need.
|
- Keep headers in `include/` and expose only what modules need.
|
||||||
|
- Before adding or changing code, check `allowed.txt` to know which functions are permitted.
|
||||||
|
- Any function not listed in `allowed.txt` is not allowed in this project.
|
||||||
|
|
||||||
## Testing Guidelines
|
## Testing Guidelines
|
||||||
- There is no automated test runner. Use manual checks in `docs/tests.md` and basic shell behavior checks (pipes, redirects, builtins).
|
- There is no automated test runner. Use manual checks in `docs/tests.md` and basic shell behavior checks (pipes, redirects, builtins).
|
||||||
|
- A local builtin edge-case script exists at `tests/builtins_edge_cases.sh` (expects a compiled `./minishell`).
|
||||||
- When debugging memory issues, run under valgrind and use the suppression file in `valgrind/readline.supp`.
|
- When debugging memory issues, run under valgrind and use the suppression file in `valgrind/readline.supp`.
|
||||||
|
|
||||||
|
## Builtins Status
|
||||||
|
- `cd`: handles `HOME` fallback, `cd -` via `OLDPWD`, updates `PWD`/`OLDPWD`, and returns failure on invalid usage (`too many arguments`, missing `HOME`/`OLDPWD`).
|
||||||
|
- `echo`: supports repeated `-n` flags (`-n`, `-nnn`) and prints remaining args preserving spaces between tokens.
|
||||||
|
- `env`: prints current environment as `KEY=VALUE`; returns failure when called with extra arguments.
|
||||||
|
- `exit`: validates numeric argument (with overflow checks), returns `1` on `too many arguments` without exiting, and exits with `2` on non-numeric argument.
|
||||||
|
- `export`: supports `NAME=VALUE` and `NAME` (stored as empty value), validates identifiers, and returns failure when any identifier is invalid.
|
||||||
|
- `pwd`: prints working directory using dynamic `getcwd` and returns failure if `getcwd` fails.
|
||||||
|
- `unset`: validates identifiers and removes matching variables from the environment map.
|
||||||
|
|
||||||
## Commit & Pull Request Guidelines
|
## Commit & Pull Request Guidelines
|
||||||
- Commit messages in this repo use a simple `type: summary` format (examples: `update: ...`, `fix: ...`). Keep summaries short and specific.
|
- Commit messages in this repo use a simple `type: summary` format (examples: `update: ...`, `fix: ...`). Keep summaries short and specific.
|
||||||
- For PRs, include:
|
- For PRs, include:
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -3,10 +3,10 @@
|
|||||||
# ::: :::::::: #
|
# ::: :::::::: #
|
||||||
# Makefile :+: :+: :+: #
|
# Makefile :+: :+: :+: #
|
||||||
# +:+ +:+ +:+ #
|
# +:+ +:+ +:+ #
|
||||||
# By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ #
|
# By: marcnava <marcnava@student.42madrid.com +#+ +:+ +#+ #
|
||||||
# +#+#+#+#+#+ +#+ #
|
# +#+#+#+#+#+ +#+ #
|
||||||
# Created: 2025/07/30 20:22:21 by sede-san #+# #+# #
|
# Created: 2025/07/30 20:22:21 by sede-san #+# #+# #
|
||||||
# Updated: 2026/02/05 21:06:52 by sede-san ### ########.fr #
|
# Updated: 2026/02/09 22:44:34 by marcnava ### ########.fr #
|
||||||
# #
|
# #
|
||||||
# **************************************************************************** #
|
# **************************************************************************** #
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ NAME = minishell
|
|||||||
# ************************** Compilation variables *************************** #
|
# ************************** Compilation variables *************************** #
|
||||||
|
|
||||||
CC = cc
|
CC = cc
|
||||||
CFLAGS = -Wall -Wextra -Werror
|
CFLAGS = -Wall -Wextra #-Werror
|
||||||
HEADERS = -I $(INCLUDE_PATH) $(LIBS_INCLUDE)
|
HEADERS = -I $(INCLUDE_PATH) $(LIBS_INCLUDE)
|
||||||
|
|
||||||
ifeq ($(DEBUG), lldb) # debug with LLDB
|
ifeq ($(DEBUG), lldb) # debug with LLDB
|
||||||
|
|||||||
177
allowed.txt
Normal file
177
allowed.txt
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
[minishell_allowed]
|
||||||
|
readline
|
||||||
|
rl_clear_history
|
||||||
|
rl_on_new_line
|
||||||
|
rl_replace_line
|
||||||
|
rl_redisplay
|
||||||
|
add_history
|
||||||
|
printf
|
||||||
|
malloc
|
||||||
|
free
|
||||||
|
write
|
||||||
|
access
|
||||||
|
open
|
||||||
|
read
|
||||||
|
close
|
||||||
|
fork
|
||||||
|
wait
|
||||||
|
waitpid
|
||||||
|
wait3
|
||||||
|
wait4
|
||||||
|
signal
|
||||||
|
sigaction
|
||||||
|
sigemptyset
|
||||||
|
sigaddset
|
||||||
|
kill
|
||||||
|
exit
|
||||||
|
getcwd
|
||||||
|
chdir
|
||||||
|
stat
|
||||||
|
lstat
|
||||||
|
fstat
|
||||||
|
unlink
|
||||||
|
execve
|
||||||
|
dup
|
||||||
|
dup2
|
||||||
|
pipe
|
||||||
|
opendir
|
||||||
|
readdir
|
||||||
|
closedir
|
||||||
|
strerror
|
||||||
|
perror
|
||||||
|
isatty
|
||||||
|
ttyname
|
||||||
|
ttyslot
|
||||||
|
ioctl
|
||||||
|
getenv
|
||||||
|
tcsetattr
|
||||||
|
tcgetattr
|
||||||
|
tgetent
|
||||||
|
tgetflag
|
||||||
|
tgetnum
|
||||||
|
tgetstr
|
||||||
|
tgoto
|
||||||
|
tputs
|
||||||
|
|
||||||
|
[libft]
|
||||||
|
ft_atoi
|
||||||
|
ft_atoi_base
|
||||||
|
ft_atol
|
||||||
|
ft_bzero
|
||||||
|
ft_calloc
|
||||||
|
ft_cdlstadd_back
|
||||||
|
ft_cdlstadd_front
|
||||||
|
ft_cdlstclear
|
||||||
|
ft_cdlstdelone
|
||||||
|
ft_cdlstiter
|
||||||
|
ft_cdlstlast
|
||||||
|
ft_cdlstmap
|
||||||
|
ft_cdlstnew
|
||||||
|
ft_cdlstsize
|
||||||
|
ft_clstadd_back
|
||||||
|
ft_clstadd_front
|
||||||
|
ft_clstclear
|
||||||
|
ft_clstdelone
|
||||||
|
ft_clstiter
|
||||||
|
ft_clstlast
|
||||||
|
ft_clstmap
|
||||||
|
ft_clstnew
|
||||||
|
ft_clstsize
|
||||||
|
ft_dlstadd_back
|
||||||
|
ft_dlstadd_front
|
||||||
|
ft_dlstclear
|
||||||
|
ft_dlstdelone
|
||||||
|
ft_dlstiter
|
||||||
|
ft_dlstlast
|
||||||
|
ft_dlstmap
|
||||||
|
ft_dlstnew
|
||||||
|
ft_dlstsize
|
||||||
|
ft_eputchar
|
||||||
|
ft_eputendl
|
||||||
|
ft_eputnbr
|
||||||
|
ft_eputstr
|
||||||
|
ft_free
|
||||||
|
ft_free_split
|
||||||
|
ft_hashmap_clear
|
||||||
|
ft_hashmap_clear_keys
|
||||||
|
ft_hashmap_contains_key
|
||||||
|
ft_hashmap_entries
|
||||||
|
ft_hashmap_get
|
||||||
|
ft_hashmap_hashstr
|
||||||
|
ft_hashmap_new
|
||||||
|
ft_hashmap_put
|
||||||
|
ft_hashmap_remove
|
||||||
|
ft_hashmap_strcmp
|
||||||
|
ft_hashstr
|
||||||
|
ft_iabs
|
||||||
|
ft_imin
|
||||||
|
ft_isalnum
|
||||||
|
ft_isalpha
|
||||||
|
ft_isascii
|
||||||
|
ft_iscntrl
|
||||||
|
ft_isdigit
|
||||||
|
ft_islower
|
||||||
|
ft_isprint
|
||||||
|
ft_isspace
|
||||||
|
ft_isupper
|
||||||
|
ft_itoa
|
||||||
|
ft_itoa_base
|
||||||
|
ft_lstadd_back
|
||||||
|
ft_lstadd_front
|
||||||
|
ft_lstclear
|
||||||
|
ft_lstclear_nodes
|
||||||
|
ft_lstdelone
|
||||||
|
ft_lstiter
|
||||||
|
ft_lstlast
|
||||||
|
ft_lstmap
|
||||||
|
ft_lstnew
|
||||||
|
ft_lstsize
|
||||||
|
ft_ltoa
|
||||||
|
ft_memchr
|
||||||
|
ft_memcmp
|
||||||
|
ft_memcpy
|
||||||
|
ft_memmove
|
||||||
|
ft_memset
|
||||||
|
ft_nsplit
|
||||||
|
ft_pow
|
||||||
|
ft_putchar
|
||||||
|
ft_putchar_fd
|
||||||
|
ft_putendl
|
||||||
|
ft_putendl_fd
|
||||||
|
ft_putnbr
|
||||||
|
ft_putnbr_fd
|
||||||
|
ft_putstr
|
||||||
|
ft_putstr_fd
|
||||||
|
ft_realloc
|
||||||
|
ft_split
|
||||||
|
ft_strchr
|
||||||
|
ft_strcmp
|
||||||
|
ft_strdup
|
||||||
|
ft_strisnum
|
||||||
|
ft_striteri
|
||||||
|
ft_strjoin
|
||||||
|
ft_strlcat
|
||||||
|
ft_strlcpy
|
||||||
|
ft_strlen
|
||||||
|
ft_strmapi
|
||||||
|
ft_strncmp
|
||||||
|
ft_strncpy
|
||||||
|
ft_strnjoin
|
||||||
|
ft_strnstr
|
||||||
|
ft_strrchr
|
||||||
|
ft_strtrim
|
||||||
|
ft_substr
|
||||||
|
ft_swap
|
||||||
|
ft_tolower
|
||||||
|
ft_toupper
|
||||||
|
ft_uitoa
|
||||||
|
ft_uitoa_base
|
||||||
|
ft_ultoa_base
|
||||||
|
|
||||||
|
[get_next_line]
|
||||||
|
get_next_line
|
||||||
|
|
||||||
|
[ft_printf]
|
||||||
|
ft_eprintf
|
||||||
|
ft_fprintf
|
||||||
|
ft_printf
|
||||||
151
docs/builtins_fixes.md
Normal file
151
docs/builtins_fixes.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# Correcciones en Builtins
|
||||||
|
|
||||||
|
Este documento resume los fallos detectados en los builtins actuales y la
|
||||||
|
solucion aplicada en codigo.
|
||||||
|
|
||||||
|
## 1) Infraestructura de builtins (`include/builtins.h`, `src/builtins/builtins.c`)
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- Uso inconsistente de tipos (`u_int8_t`/`unsigned char` frente a `uint8_t`).
|
||||||
|
- `set_builtins` no comprobaba fallos de registro por builtin (duplicado de
|
||||||
|
clave o insercion en hashmap), pudiendo dejar estado parcial.
|
||||||
|
- `is_builtin` podia dereferenciar punteros nulos.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- `u_int8_t` no es el tipo estandar C99 y depende de plataforma/headers.
|
||||||
|
- Si falla una insercion, la tabla quedaba inicializada parcialmente sin
|
||||||
|
rollback.
|
||||||
|
- En errores de inicializacion, consultar `is_builtin` podia romper.
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Unificacion de firmas a `uint8_t`.
|
||||||
|
- Nuevo helper `register_builtin()` con validacion tras `ft_hashmap_put`.
|
||||||
|
- Si falla cualquier alta: limpieza de `minishell->builtins` y retorno de
|
||||||
|
error.
|
||||||
|
- Guardas nulas en `is_builtin`.
|
||||||
|
|
||||||
|
## 2) `cd` (`src/builtins/cd/cd.c`)
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- `cd` con demasiados argumentos devolvia `2` (bash devuelve `1`).
|
||||||
|
- `cd` sin `HOME` acababa llamando a `chdir(NULL)`.
|
||||||
|
- El manejo de error usaba comprobaciones invertidas (`access`) y codigos
|
||||||
|
incorrectos.
|
||||||
|
- No se actualizaban `PWD` y `OLDPWD` tras `chdir` exitoso.
|
||||||
|
- `cd -` (usar `OLDPWD`) no estaba soportado.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- Codigos de salida incompatibles con el comportamiento esperado del shell.
|
||||||
|
- `HOME` no definido no se controlaba antes del `chdir`.
|
||||||
|
- La logica de `access` estaba al reves y mezclaba condiciones.
|
||||||
|
- Variables de entorno del directorio quedaban desincronizadas.
|
||||||
|
- Faltaba resolver el caso especial de `-` hacia `OLDPWD`.
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Refactor en `resolve_cd_path()` para validar argumentos y `HOME`.
|
||||||
|
- Retorno `EXIT_FAILURE` en `too many arguments` y `HOME not set`.
|
||||||
|
- Error de `chdir` simplificado a `perror("minishell: cd")` + retorno `1`.
|
||||||
|
- Actualizacion de `OLDPWD` y `PWD` mediante `getcwd(NULL, 0)` + `set_env()`.
|
||||||
|
- Soporte de `cd -`: usa `OLDPWD`, valida `OLDPWD not set` e imprime el nuevo
|
||||||
|
directorio tras el cambio.
|
||||||
|
|
||||||
|
## 3) `exit` (`src/builtins/exit/exit.c`)
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- Habia un `printf` de debug en ejecucion real.
|
||||||
|
- `exit <no_numerico>` devolvia `2` pero no cerraba el shell.
|
||||||
|
- `exit n m` devolvia `2`; en bash es `1` y no sale del shell.
|
||||||
|
- Validacion numerica basada en `ft_strisnum` sin control de overflow.
|
||||||
|
- Se mostraba `exit` incluso en contexto no interactivo.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- Debug residual contamina salida.
|
||||||
|
- Semantica de `exit` incompleta respecto a bash.
|
||||||
|
- Valores fuera de rango podian tratarse como validos por conversion directa.
|
||||||
|
- Mensaje `exit` debe mostrarse solo en shell interactivo.
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Eliminado debug print.
|
||||||
|
- Nuevo flujo `resolve_exit_status()`:
|
||||||
|
- Sin argumentos: usa `msh->exit_status`.
|
||||||
|
- Argumento no numerico o fuera de `long`: mensaje
|
||||||
|
`numeric argument required`, `msh->exit = true`, estado `2`.
|
||||||
|
- Demasiados argumentos: mensaje de error y estado `1`, sin salir.
|
||||||
|
- Parser numerico propio (`get_uint8_from_num` + `has_overflow`) con soporte
|
||||||
|
de signo y control de overflow.
|
||||||
|
- `ft_eputendl("exit")` solo si `isatty(STDIN_FILENO)`.
|
||||||
|
|
||||||
|
## 4) `pwd` (`src/builtins/pwd/pwd.c`)
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- Si `getcwd` fallaba, el builtin devolvia `EXIT_SUCCESS`.
|
||||||
|
- Uso de buffer fijo (`PATH_MAX`) menos robusto para rutas largas.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- El shell reportaba exito aunque no pudiera obtener el directorio.
|
||||||
|
- Un buffer fijo puede truncar o fallar en escenarios de rutas profundas.
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Cambio a `getcwd(NULL, 0)` con memoria dinamica.
|
||||||
|
- Si falla, `perror("minishell: pwd")` y retorno `EXIT_FAILURE`.
|
||||||
|
- `free()` del buffer dinamico tras imprimir.
|
||||||
|
|
||||||
|
## 5) `echo` (`src/builtins/echo/echo.c`, `src/builtins/echo/echo_def.c`)
|
||||||
|
|
||||||
|
### Cambio aplicado
|
||||||
|
- Ajuste de tipos de retorno auxiliares a `uint8_t` para mantener consistencia
|
||||||
|
con `builtins.h`.
|
||||||
|
|
||||||
|
### Nota
|
||||||
|
- No se detectaron fallos funcionales criticos adicionales en la logica actual
|
||||||
|
de `echo` durante esta revision.
|
||||||
|
|
||||||
|
## 6) Builtins faltantes: `env`, `export`, `unset`
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- Los builtins `env`, `export` y `unset` no estaban implementados ni
|
||||||
|
registrados en `set_builtins`.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- Comandos basicos de shell no existian en la tabla de builtins.
|
||||||
|
- Cualquier prueba/flujo que dependiera de gestion de variables exportadas
|
||||||
|
fallaba (listar, crear y eliminar variables de entorno).
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Nuevos builtins implementados:
|
||||||
|
- `src/builtins/env/env.c`
|
||||||
|
- `src/builtins/export/export.c`
|
||||||
|
- `src/builtins/unset/unset.c`
|
||||||
|
- Registro en `src/builtins/builtins.c` (tabla ampliada de 4 a 7 entradas).
|
||||||
|
- Nuevos prototipos en `include/builtins.h`.
|
||||||
|
- Soporte de borrado real de entorno mediante `unset_env`:
|
||||||
|
- Declaracion en `include/core.h`
|
||||||
|
- Implementacion en `src/variables/environment_unset.c`
|
||||||
|
|
||||||
|
## 7) Comportamiento aplicado en los nuevos builtins
|
||||||
|
|
||||||
|
### `env`
|
||||||
|
- Si recibe argumentos, devuelve error (`minishell: env: too many arguments`)
|
||||||
|
y estado `1`.
|
||||||
|
- Sin argumentos, lista `KEY=VALUE` de las variables de entorno actuales.
|
||||||
|
|
||||||
|
### `export`
|
||||||
|
- `export NAME=VALUE`: crea/actualiza la variable.
|
||||||
|
- `export NAME`: crea/actualiza con valor vacio (`NAME=`) para que aparezca
|
||||||
|
en `env`.
|
||||||
|
- Identificadores invalidos (`1A=2`, etc.) devuelven error
|
||||||
|
`not a valid identifier` y estado `1`.
|
||||||
|
- Sin argumentos, reutiliza el listado de `env`.
|
||||||
|
|
||||||
|
### `unset`
|
||||||
|
- Elimina variables validas del entorno en memoria.
|
||||||
|
- Identificadores invalidos devuelven error
|
||||||
|
`not a valid identifier` y estado `1`.
|
||||||
|
- Si el identificador es valido y no existe, no falla (comportamiento shell).
|
||||||
|
|
||||||
|
## Validacion realizada
|
||||||
|
|
||||||
|
- `norminette` ejecutado sobre los archivos modificados: `OK`.
|
||||||
|
- Build completa no ejecutable en este entorno por falta de acceso de red al
|
||||||
|
clonado de librerias (`github.com`), por lo que no se pudo validar runtime
|
||||||
|
del binario en esta sesion.
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# include "minishell.h"
|
# include "minishell.h"
|
||||||
# include "core.h"
|
# include "core.h"
|
||||||
|
|
||||||
typedef unsigned char (*t_builtin_func)(t_command cmd, t_minishell *minishell);
|
typedef uint8_t (*t_builtin_func)(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Functions */
|
/* Functions */
|
||||||
@@ -25,24 +25,36 @@ typedef unsigned char (*t_builtin_func)(t_command cmd, t_minishell *minishell)
|
|||||||
|
|
||||||
/* builtins.c */
|
/* builtins.c */
|
||||||
|
|
||||||
extern u_int8_t set_builtins(t_minishell *minishell);
|
extern uint8_t set_builtins(t_minishell *minishell);
|
||||||
|
|
||||||
extern u_int8_t is_builtin(const char *command_name, t_minishell *minishell);
|
extern uint8_t is_builtin(const char *command_name, t_minishell *minishell);
|
||||||
|
|
||||||
/* cd.c */
|
/* cd.c */
|
||||||
|
|
||||||
extern u_int8_t builtin_cd(t_command cmd, t_minishell *minishell);
|
extern uint8_t builtin_cd(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/* echo.c */
|
/* echo.c */
|
||||||
|
|
||||||
extern u_int8_t builtin_echo(t_command cmd, t_minishell *minishell);
|
extern uint8_t builtin_echo(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/* exit.c */
|
/* exit.c */
|
||||||
|
|
||||||
extern u_int8_t builtin_exit(t_command cmd, t_minishell *minishell);
|
extern uint8_t builtin_exit(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/* pwd.c */
|
/* pwd.c */
|
||||||
|
|
||||||
extern u_int8_t builtin_pwd(t_command cmd, t_minishell *minishell);
|
extern uint8_t builtin_pwd(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
|
/* env.c */
|
||||||
|
|
||||||
|
extern uint8_t builtin_env(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
|
/* export.c */
|
||||||
|
|
||||||
|
extern uint8_t builtin_export(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
|
/* unset.c */
|
||||||
|
|
||||||
|
extern uint8_t builtin_unset(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
#endif /* BUILTINS_H */
|
#endif /* BUILTINS_H */
|
||||||
|
|||||||
@@ -88,9 +88,9 @@ typedef struct s_command
|
|||||||
|
|
||||||
/* minishell.c */
|
/* minishell.c */
|
||||||
|
|
||||||
extern int minishell_init(t_minishell *minishell, char **envp);
|
extern void minishell_init(t_minishell *minishell, char **envp);
|
||||||
|
|
||||||
extern uint8_t minishell_run(t_minishell *minishell);
|
extern void minishell_run(t_minishell *minishell);
|
||||||
|
|
||||||
extern void minishell_clear(t_minishell *minishell);
|
extern void minishell_clear(t_minishell *minishell);
|
||||||
|
|
||||||
@@ -107,4 +107,6 @@ extern void free_envp(char **envp);
|
|||||||
|
|
||||||
extern char *get_env(const char *env_name, t_minishell *msh);
|
extern char *get_env(const char *env_name, t_minishell *msh);
|
||||||
|
|
||||||
|
extern void unset_env(const char *env_name, t_minishell *msh);
|
||||||
|
|
||||||
#endif /* CORE_H */
|
#endif /* CORE_H */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/22 19:03:51 by sede-san #+# #+# */
|
/* Created: 2025/10/22 19:03:51 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/09 21:19:02 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 20:36:19 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -17,6 +17,12 @@
|
|||||||
# include "core.h"
|
# include "core.h"
|
||||||
# include "builtins.h"
|
# include "builtins.h"
|
||||||
|
|
||||||
|
# define PIPE_STR "|"
|
||||||
|
# define REDIRECT_IN_STR "<"
|
||||||
|
# define REDIRECT_OUT_STR ">"
|
||||||
|
# define APPEND_STR ">>"
|
||||||
|
# define HEREDOC_STR "<<"
|
||||||
|
|
||||||
# define TOKENS_COUNT 5
|
# define TOKENS_COUNT 5
|
||||||
|
|
||||||
typedef enum e_token_type
|
typedef enum e_token_type
|
||||||
|
|||||||
@@ -12,23 +12,53 @@
|
|||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
u_int8_t set_builtins(
|
static uint8_t register_builtin(
|
||||||
t_minishell *minishell
|
t_minishell *minishell,
|
||||||
) {
|
const char *name,
|
||||||
minishell->builtins
|
t_builtin_func builtin
|
||||||
= ft_hashmap_new(4, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
)
|
||||||
if (minishell->builtins == NULL)
|
{
|
||||||
|
char *key;
|
||||||
|
|
||||||
|
key = ft_strdup(name);
|
||||||
|
if (key == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
ft_hashmap_put(minishell->builtins, ft_strdup("cd"), builtin_cd);
|
ft_hashmap_put(minishell->builtins, key, builtin);
|
||||||
ft_hashmap_put(minishell->builtins, ft_strdup("echo"), builtin_echo);
|
if (!ft_hashmap_contains_key(minishell->builtins, name))
|
||||||
ft_hashmap_put(minishell->builtins, ft_strdup("exit"), builtin_exit);
|
{
|
||||||
ft_hashmap_put(minishell->builtins, ft_strdup("pwd"), builtin_pwd);
|
free(key);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
u_int8_t is_builtin(
|
uint8_t set_builtins(
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
minishell->builtins
|
||||||
|
= ft_hashmap_new(7, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||||
|
if (minishell->builtins == NULL)
|
||||||
|
return (0);
|
||||||
|
if (!register_builtin(minishell, "cd", builtin_cd)
|
||||||
|
|| !register_builtin(minishell, "echo", builtin_echo)
|
||||||
|
|| !register_builtin(minishell, "env", builtin_env)
|
||||||
|
|| !register_builtin(minishell, "exit", builtin_exit)
|
||||||
|
|| !register_builtin(minishell, "export", builtin_export)
|
||||||
|
|| !register_builtin(minishell, "pwd", builtin_pwd)
|
||||||
|
|| !register_builtin(minishell, "unset", builtin_unset))
|
||||||
|
{
|
||||||
|
ft_hashmap_clear_keys(&minishell->builtins);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t is_builtin(
|
||||||
const char *command_name,
|
const char *command_name,
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
) {
|
) {
|
||||||
|
if (command_name == NULL || minishell == NULL
|
||||||
|
|| minishell->builtins == NULL)
|
||||||
|
return (0);
|
||||||
return (ft_hashmap_contains_key(minishell->builtins, command_name));
|
return (ft_hashmap_contains_key(minishell->builtins, command_name));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,43 +12,104 @@
|
|||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
static u_int8_t handle_error(t_command cmd, t_minishell *msh, char *path);
|
static uint8_t handle_error(void);
|
||||||
|
static void update_pwd_vars(t_minishell *msh, char *oldpwd);
|
||||||
|
static char *get_path_from_env(
|
||||||
|
t_minishell *msh,
|
||||||
|
const char *env_name,
|
||||||
|
const char *error,
|
||||||
|
uint8_t *status
|
||||||
|
);
|
||||||
|
static char *resolve_cd_path(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh,
|
||||||
|
uint8_t *status
|
||||||
|
);
|
||||||
|
|
||||||
u_int8_t builtin_cd(
|
uint8_t builtin_cd(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
){
|
){
|
||||||
char *path;
|
char *path;
|
||||||
|
char *oldpwd;
|
||||||
|
uint8_t status;
|
||||||
|
bool show_pwd;
|
||||||
|
|
||||||
if (cmd.argc > 2)
|
path = resolve_cd_path(cmd, msh, &status);
|
||||||
{
|
if (status != EXIT_SUCCESS)
|
||||||
ft_eputendl("minishell: cd: too many arguments");
|
return (status);
|
||||||
return (2);
|
show_pwd = (cmd.argc == 2 && ft_strcmp(cmd.argv[1], "-") == 0);
|
||||||
}
|
oldpwd = getcwd(NULL, 0);
|
||||||
else if (cmd.argc == 1)
|
|
||||||
path = get_env("HOME", msh);
|
|
||||||
else
|
|
||||||
path = cmd.argv[1];
|
|
||||||
if (chdir(path) == -1)
|
if (chdir(path) == -1)
|
||||||
return (handle_error(cmd, msh, path));
|
{
|
||||||
|
free(oldpwd);
|
||||||
|
return (handle_error());
|
||||||
|
}
|
||||||
|
update_pwd_vars(msh, oldpwd);
|
||||||
|
if (show_pwd && get_env("PWD", msh) != NULL)
|
||||||
|
ft_putendl(get_env("PWD", msh));
|
||||||
|
free(oldpwd);
|
||||||
return (EXIT_SUCCESS);
|
return (EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u_int8_t handle_error(
|
static uint8_t handle_error(void)
|
||||||
|
{
|
||||||
|
perror("minishell: cd");
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_pwd_vars(
|
||||||
|
t_minishell *msh,
|
||||||
|
char *oldpwd
|
||||||
|
){
|
||||||
|
char *newpwd;
|
||||||
|
|
||||||
|
if (oldpwd != NULL)
|
||||||
|
set_env("OLDPWD", oldpwd, msh);
|
||||||
|
newpwd = getcwd(NULL, 0);
|
||||||
|
if (newpwd != NULL)
|
||||||
|
{
|
||||||
|
set_env("PWD", newpwd, msh);
|
||||||
|
free(newpwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *resolve_cd_path(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh,
|
t_minishell *msh,
|
||||||
char *path
|
uint8_t *status
|
||||||
){
|
){
|
||||||
u_int8_t exit_code;
|
if (cmd.argc > 2)
|
||||||
|
{
|
||||||
(void)msh;
|
ft_eputendl("minishell: cd: too many arguments");
|
||||||
exit_code = 0;
|
*status = EXIT_FAILURE;
|
||||||
if (access(path, F_OK) != -1)
|
return (NULL);
|
||||||
// No such file or directory
|
}
|
||||||
exit_code = 1;
|
if (cmd.argc == 2 && ft_strcmp(cmd.argv[1], "-") == 0)
|
||||||
else if (access(path, X_OK) == -1)
|
return (get_path_from_env(msh, "OLDPWD",
|
||||||
// Permission denied
|
"minishell: cd: OLDPWD not set", status));
|
||||||
exit_code = 2;
|
if (cmd.argc == 1)
|
||||||
perror(cmd.argv[0]);
|
return (get_path_from_env(msh, "HOME",
|
||||||
return (exit_code);
|
"minishell: cd: HOME not set", status));
|
||||||
|
*status = EXIT_SUCCESS;
|
||||||
|
return (cmd.argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *get_path_from_env(
|
||||||
|
t_minishell *msh,
|
||||||
|
const char *env_name,
|
||||||
|
const char *error,
|
||||||
|
uint8_t *status
|
||||||
|
){
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
path = get_env(env_name, msh);
|
||||||
|
if (path == NULL)
|
||||||
|
{
|
||||||
|
ft_eputendl((char *)error);
|
||||||
|
*status = EXIT_FAILURE;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
*status = EXIT_SUCCESS;
|
||||||
|
return (path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
#include "echo_def.h"
|
#include "echo_def.h"
|
||||||
|
|
||||||
u_int8_t builtin_echo(
|
uint8_t builtin_echo(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
){
|
){
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include "echo_def.h"
|
#include "echo_def.h"
|
||||||
|
|
||||||
static void assign_flags(t_args *args, t_command cmd, size_t *i);
|
static void assign_flags(t_args *args, t_command cmd, size_t *i);
|
||||||
static u_int8_t is_valid_flag(t_args *args, char *flag);
|
static uint8_t is_valid_flag(t_args *args, char *flag);
|
||||||
|
|
||||||
static void assign_default_flags(
|
static void assign_default_flags(
|
||||||
t_args *args
|
t_args *args
|
||||||
@@ -51,7 +51,7 @@ static void assign_flags(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static u_int8_t is_valid_flag(
|
static uint8_t is_valid_flag(
|
||||||
t_args *args,
|
t_args *args,
|
||||||
char *flag
|
char *flag
|
||||||
){
|
){
|
||||||
|
|||||||
58
src/builtins/env/env.c
vendored
Normal file
58
src/builtins/env/env.c
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* env.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 22:05:00 by codex #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 22:05:00 by codex ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
|
static void print_entry(t_map_entry *entry);
|
||||||
|
static void print_env(t_minishell *msh);
|
||||||
|
|
||||||
|
uint8_t builtin_env(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (cmd.argc > 1)
|
||||||
|
{
|
||||||
|
ft_eputendl("minishell: env: too many arguments");
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
print_env(msh);
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_entry(
|
||||||
|
t_map_entry *entry
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ft_putstr((char *)entry->key);
|
||||||
|
ft_putchar('=');
|
||||||
|
if (entry->value != NULL)
|
||||||
|
ft_putstr((char *)entry->value);
|
||||||
|
ft_putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_env(
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
t_list *entries;
|
||||||
|
t_list *current;
|
||||||
|
|
||||||
|
entries = ft_hashmap_entries(msh->variables.environment);
|
||||||
|
current = entries;
|
||||||
|
while (current != NULL)
|
||||||
|
{
|
||||||
|
print_entry((t_map_entry *)current->content);
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
ft_lstclear_nodes(&entries);
|
||||||
|
}
|
||||||
@@ -11,7 +11,19 @@
|
|||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
#include <stdint.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
static uint8_t get_uint8_from_num(const char *arg, uint8_t *status);
|
||||||
|
static uint8_t has_overflow(
|
||||||
|
uint64_t n,
|
||||||
|
char digit,
|
||||||
|
uint64_t limit
|
||||||
|
);
|
||||||
|
static uint8_t resolve_exit_status(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh,
|
||||||
|
uint8_t *exit_status
|
||||||
|
);
|
||||||
|
|
||||||
uint8_t builtin_exit(
|
uint8_t builtin_exit(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
@@ -19,28 +31,78 @@ uint8_t builtin_exit(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint8_t exit_status;
|
uint8_t exit_status;
|
||||||
|
|
||||||
ft_eputendl("exit");
|
|
||||||
if (cmd.argc == 1)
|
|
||||||
exit_status = msh->exit_status;
|
|
||||||
else if (!ft_strisnum(cmd.argv[1]))
|
|
||||||
{
|
|
||||||
ft_eprintf(
|
|
||||||
"minishell: exit: %s: numeric argument required\n",
|
|
||||||
cmd.argv[1]);
|
|
||||||
return (2);
|
|
||||||
}
|
|
||||||
else if (cmd.argc > 2)
|
|
||||||
{
|
|
||||||
ft_eputendl("exit: too many arguments");
|
|
||||||
return (2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
exit_status = (uint8_t)ft_atol(cmd.argv[1]);
|
|
||||||
|
|
||||||
printf("builtin_exit: exit_status=%d\n", exit_status); // Debug print
|
if (isatty(STDIN_FILENO))
|
||||||
|
ft_eputendl("exit");
|
||||||
msh->exit = 1;
|
if (!resolve_exit_status(cmd, msh, &exit_status))
|
||||||
|
return (msh->exit_status);
|
||||||
|
msh->exit = true;
|
||||||
msh->exit_status = exit_status;
|
msh->exit_status = exit_status;
|
||||||
return (exit_status);
|
return (exit_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t resolve_exit_status(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh,
|
||||||
|
uint8_t *exit_status
|
||||||
|
){
|
||||||
|
if (cmd.argc == 1)
|
||||||
|
*exit_status = msh->exit_status;
|
||||||
|
else if (!get_uint8_from_num(cmd.argv[1], exit_status))
|
||||||
|
{
|
||||||
|
ft_eprintf("minishell: exit: %s: numeric argument required\n",
|
||||||
|
cmd.argv[1]);
|
||||||
|
msh->exit = true;
|
||||||
|
msh->exit_status = 2;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
else if (cmd.argc > 2)
|
||||||
|
{
|
||||||
|
ft_eputendl("minishell: exit: too many arguments");
|
||||||
|
msh->exit_status = EXIT_FAILURE;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t get_uint8_from_num(
|
||||||
|
const char *arg,
|
||||||
|
uint8_t *status
|
||||||
|
){
|
||||||
|
uint64_t n;
|
||||||
|
uint64_t limit;
|
||||||
|
int sign;
|
||||||
|
|
||||||
|
if (arg == NULL || *arg == '\0')
|
||||||
|
return (0);
|
||||||
|
n = 0;
|
||||||
|
sign = 1;
|
||||||
|
if (*arg == '+' || *arg == '-')
|
||||||
|
if (*arg++ == '-')
|
||||||
|
sign = -1;
|
||||||
|
if (*arg == '\0')
|
||||||
|
return (0);
|
||||||
|
limit = LONG_MAX;
|
||||||
|
if (sign == -1)
|
||||||
|
limit = (uint64_t)LONG_MAX + 1;
|
||||||
|
while (*arg != '\0')
|
||||||
|
{
|
||||||
|
if (!ft_isdigit(*arg) || has_overflow(n, *arg, limit))
|
||||||
|
return (0);
|
||||||
|
n = (n * 10) + (*arg++ - '0');
|
||||||
|
}
|
||||||
|
*status = (uint8_t)(n * sign);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t has_overflow(
|
||||||
|
uint64_t n,
|
||||||
|
char digit,
|
||||||
|
uint64_t limit
|
||||||
|
){
|
||||||
|
if (n > (limit / 10))
|
||||||
|
return (1);
|
||||||
|
if (n == (limit / 10) && (uint64_t)(digit - '0') > (limit % 10))
|
||||||
|
return (1);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|||||||
79
src/builtins/export/export.c
Normal file
79
src/builtins/export/export.c
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* export.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 22:05:00 by codex #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 22:05:00 by codex ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
|
static uint8_t is_valid_identifier(const char *arg, size_t name_len);
|
||||||
|
static uint8_t export_one(char *arg, t_minishell *msh);
|
||||||
|
|
||||||
|
uint8_t builtin_export(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint8_t status;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (cmd.argc == 1)
|
||||||
|
return (builtin_env(cmd, msh));
|
||||||
|
status = EXIT_SUCCESS;
|
||||||
|
i = 0;
|
||||||
|
while (cmd.argv[++i] != NULL)
|
||||||
|
if (export_one(cmd.argv[i], msh) != EXIT_SUCCESS)
|
||||||
|
status = EXIT_FAILURE;
|
||||||
|
return (status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t is_valid_identifier(
|
||||||
|
const char *arg,
|
||||||
|
size_t name_len
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (name_len == 0 || arg == NULL)
|
||||||
|
return (0);
|
||||||
|
if (!ft_isalpha(arg[0]) && arg[0] != '_')
|
||||||
|
return (0);
|
||||||
|
i = 0;
|
||||||
|
while (++i < name_len)
|
||||||
|
if (!ft_isalnum(arg[i]) && arg[i] != '_')
|
||||||
|
return (0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t export_one(
|
||||||
|
char *arg,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char *eq_pos;
|
||||||
|
char *name;
|
||||||
|
size_t name_len;
|
||||||
|
|
||||||
|
eq_pos = ft_strchr(arg, '=');
|
||||||
|
name_len = ft_strlen(arg);
|
||||||
|
if (eq_pos != NULL)
|
||||||
|
name_len = (size_t)(eq_pos - arg);
|
||||||
|
if (!is_valid_identifier(arg, name_len))
|
||||||
|
return (ft_eprintf("minishell: export: `%s': not a valid identifier\n",
|
||||||
|
arg), EXIT_FAILURE);
|
||||||
|
name = ft_substr(arg, 0, name_len);
|
||||||
|
if (name == NULL)
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
if (eq_pos != NULL)
|
||||||
|
set_env(name, eq_pos + 1, msh);
|
||||||
|
else
|
||||||
|
set_env(name, "", msh);
|
||||||
|
free(name);
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
@@ -12,15 +12,21 @@
|
|||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
u_int8_t builtin_pwd(
|
uint8_t builtin_pwd(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
){
|
){
|
||||||
char cwd[PATH_MAX];
|
char *cwd;
|
||||||
|
|
||||||
(void)cmd;
|
(void)cmd;
|
||||||
(void)msh;
|
(void)msh;
|
||||||
if (getcwd(cwd, PATH_MAX) != NULL)
|
cwd = getcwd(NULL, 0);
|
||||||
ft_putendl(cwd);
|
if (cwd == NULL)
|
||||||
|
{
|
||||||
|
perror("minishell: pwd");
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
ft_putendl(cwd);
|
||||||
|
free(cwd);
|
||||||
return (EXIT_SUCCESS);
|
return (EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|||||||
61
src/builtins/unset/unset.c
Normal file
61
src/builtins/unset/unset.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* unset.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 22:05:00 by codex #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 22:05:00 by codex ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
|
static uint8_t is_valid_identifier(const char *arg);
|
||||||
|
static uint8_t unset_one(char *arg, t_minishell *msh);
|
||||||
|
|
||||||
|
uint8_t builtin_unset(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint8_t status;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
status = EXIT_SUCCESS;
|
||||||
|
i = 0;
|
||||||
|
while (cmd.argv[++i] != NULL)
|
||||||
|
if (unset_one(cmd.argv[i], msh) != EXIT_SUCCESS)
|
||||||
|
status = EXIT_FAILURE;
|
||||||
|
return (status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t is_valid_identifier(
|
||||||
|
const char *arg
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (arg == NULL || *arg == '\0')
|
||||||
|
return (0);
|
||||||
|
if (!ft_isalpha(arg[0]) && arg[0] != '_')
|
||||||
|
return (0);
|
||||||
|
i = 0;
|
||||||
|
while (arg[++i] != '\0')
|
||||||
|
if (!ft_isalnum(arg[i]) && arg[i] != '_')
|
||||||
|
return (0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t unset_one(
|
||||||
|
char *arg,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!is_valid_identifier(arg))
|
||||||
|
return (ft_eprintf("minishell: unset: `%s': not a valid identifier\n",
|
||||||
|
arg), EXIT_FAILURE);
|
||||||
|
unset_env(arg, msh);
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2026/02/09 18:56:41 by sede-san #+# #+# */
|
/* Created: 2026/02/09 18:56:41 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/09 23:09:28 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 20:42:50 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -15,157 +15,65 @@
|
|||||||
|
|
||||||
static t_token *tokenize(const char *line, size_t *start);
|
static t_token *tokenize(const char *line, size_t *start);
|
||||||
static t_token_type get_token_type(const char *str);
|
static t_token_type get_token_type(const char *str);
|
||||||
static t_token *token_new(t_token_type type, const char *text);
|
|
||||||
static void token_clear(t_token *token);
|
|
||||||
static t_token *read_token(t_token_type type, const char *line, size_t *i);
|
|
||||||
static t_token *read_word(const char *line, size_t *i);
|
|
||||||
static inline bool is_meta(char c);
|
|
||||||
|
|
||||||
/**
|
t_list *lex(
|
||||||
* @brief Converts a command line string into a list of tokens.
|
const char *line
|
||||||
*
|
) {
|
||||||
* @return A list of tokens or NULL on error.
|
t_list *tokens;
|
||||||
*/
|
|
||||||
t_list *lex(
|
|
||||||
const char *line)
|
|
||||||
{
|
|
||||||
t_list *tokens;
|
|
||||||
t_token *token;
|
t_token *token;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
tokens = NULL;
|
tokens = NULL;
|
||||||
i = 0;
|
i = 0;
|
||||||
while (line[i] != '\0')
|
while (line[i] != '\0')
|
||||||
{
|
{
|
||||||
|
// ignore spaces
|
||||||
while (ft_isspace(line[i]))
|
while (ft_isspace(line[i]))
|
||||||
i++;
|
i++;
|
||||||
if (line[i] == '\0')
|
// create token
|
||||||
break;
|
|
||||||
token = tokenize(line, &i);
|
token = tokenize(line, &i);
|
||||||
ft_lstadd_back(&tokens, ft_lstnew(token));
|
// add token to list
|
||||||
if (token == NULL)
|
if (token != NULL)
|
||||||
{
|
ft_lstadd_back(&tokens, ft_lstnew(token));
|
||||||
ft_lstclear(&tokens, (void (*)(void *))token_clear);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return (tokens);
|
return (tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static t_token *tokenize(const char *line, size_t *start) {
|
||||||
* @return A new token or NULL on error.
|
t_token *token;
|
||||||
*/
|
t_token_type type;
|
||||||
static t_token *tokenize(
|
|
||||||
const char *line,
|
token = NULL;
|
||||||
size_t *start)
|
|
||||||
{
|
|
||||||
t_token *token;
|
|
||||||
t_token_type type;
|
|
||||||
|
|
||||||
if (line == NULL || line[*start] == '\0')
|
if (line == NULL || line[*start] == '\0')
|
||||||
return (NULL);
|
return (NULL);
|
||||||
type = get_token_type(line + *start);
|
type = get_token_type(line + *start);
|
||||||
if (type != TOKEN_WORD)
|
(void)type;
|
||||||
token = read_token(type, line, start);
|
// if (type != TOKEN_WORD)
|
||||||
else
|
// token = token_new(type, NULL);
|
||||||
token = read_word(line, start);
|
// else
|
||||||
|
// token = read_word(line, start);
|
||||||
|
// if (token == NULL)
|
||||||
|
// (*start) += ft_strlen(token->value);
|
||||||
return (token);
|
return (token);
|
||||||
}
|
}
|
||||||
|
|
||||||
static t_token_type get_token_type(
|
static t_token_type get_token_type(const char *str)
|
||||||
const char *str
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (str == NULL || str[0] == '\0')
|
size_t i;
|
||||||
return (TOKEN_WORD);
|
static const t_map_entry tokens[TOKENS_COUNT] = {
|
||||||
if (str[0] == '|')
|
{PIPE_STR, (void *)TOKEN_PIPE},
|
||||||
return (TOKEN_PIPE);
|
{REDIRECT_IN_STR, (void *)TOKEN_REDIRECT_IN},
|
||||||
if (str[0] == '<')
|
{REDIRECT_OUT_STR, (void *)TOKEN_REDIRECT_OUT},
|
||||||
|
{APPEND_STR, (void *)TOKEN_APPEND},
|
||||||
|
{HEREDOC_STR, (void *)TOKEN_HEREDOC}
|
||||||
|
};
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < TOKENS_COUNT)
|
||||||
{
|
{
|
||||||
if (str[1] == '<')
|
if (ft_strcmp(str, tokens[i].key) == 0)
|
||||||
return (TOKEN_HEREDOC);
|
return ((t_token_type)tokens[i].value);
|
||||||
return (TOKEN_REDIRECT_IN);
|
i++;
|
||||||
}
|
|
||||||
if (str[0] == '>')
|
|
||||||
{
|
|
||||||
if (str[1] == '>')
|
|
||||||
return (TOKEN_APPEND);
|
|
||||||
return (TOKEN_REDIRECT_OUT);
|
|
||||||
}
|
}
|
||||||
return (TOKEN_WORD);
|
return (TOKEN_WORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
static t_token *token_new(
|
|
||||||
t_token_type type,
|
|
||||||
const char *text)
|
|
||||||
{
|
|
||||||
t_token *token;
|
|
||||||
|
|
||||||
token = (t_token *)malloc(sizeof(t_token));
|
|
||||||
if (token == NULL)
|
|
||||||
return (NULL);
|
|
||||||
ft_putendl("malloc");
|
|
||||||
token->type = type;
|
|
||||||
token->value = text;
|
|
||||||
if (token->type == TOKEN_WORD && token->value == NULL)
|
|
||||||
{
|
|
||||||
free(token);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
return (token);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void token_clear(
|
|
||||||
t_token *token)
|
|
||||||
{
|
|
||||||
if (token != NULL)
|
|
||||||
{
|
|
||||||
if (token->value != NULL)
|
|
||||||
{
|
|
||||||
free(token->value);
|
|
||||||
ft_putendl("free");
|
|
||||||
}
|
|
||||||
free(token);
|
|
||||||
ft_putendl("free");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static t_token *read_token(
|
|
||||||
t_token_type type,
|
|
||||||
const char *line,
|
|
||||||
size_t *i)
|
|
||||||
{
|
|
||||||
while (ft_isspace(line[*i]) || is_meta(line[*i]))
|
|
||||||
(*i)++;
|
|
||||||
return (token_new(type, NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
static t_token *read_word(
|
|
||||||
const char *line,
|
|
||||||
size_t *i)
|
|
||||||
{
|
|
||||||
const size_t start = *i;
|
|
||||||
bool in_single_quote;
|
|
||||||
bool in_double_quote;
|
|
||||||
|
|
||||||
in_single_quote = false;
|
|
||||||
in_double_quote = false;
|
|
||||||
while (line[*i] != '\0')
|
|
||||||
{
|
|
||||||
char c = line[*i];
|
|
||||||
(void)c;
|
|
||||||
if (line[*i] == '\'' && !in_double_quote)
|
|
||||||
in_single_quote = !in_single_quote;
|
|
||||||
else if (line[*i] == '"' && !in_single_quote)
|
|
||||||
in_double_quote = !in_double_quote;
|
|
||||||
else if (!in_single_quote && !in_double_quote && (isspace(line[*i]) || is_meta(line[*i])))
|
|
||||||
break;
|
|
||||||
(*i)++;
|
|
||||||
}
|
|
||||||
return (token_new(TOKEN_WORD, ft_substr(line, start, *i - start)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool is_meta(char c)
|
|
||||||
{
|
|
||||||
return (c == '|' || c == '<' || c == '>');
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ static void set_argv(t_command *command, char *line, t_minishell *minishell);
|
|||||||
static void expand_envs(char *arg, t_minishell *minishell);
|
static void expand_envs(char *arg, t_minishell *minishell);
|
||||||
static char **lst_to_argv(t_list *argv_list);
|
static char **lst_to_argv(t_list *argv_list);
|
||||||
static void set_argc(t_command *command);
|
static void set_argc(t_command *command);
|
||||||
static void set_infile(t_command *command);
|
// static void set_infile(t_command *command);
|
||||||
static void set_outfile(t_command *command);
|
// static void set_outfile(t_command *command);
|
||||||
static void set_path(t_command *command, t_minishell *minishell);
|
static void set_path(t_command *command, t_minishell *minishell);
|
||||||
static u_int8_t path_is_solved(char *cmd_name, t_minishell *msh);
|
static u_int8_t path_is_solved(char *cmd_name, t_minishell *msh);
|
||||||
static char *solve_path(char *command_name, t_minishell *minishell);
|
static char *solve_path(char *command_name, t_minishell *minishell);
|
||||||
@@ -37,7 +37,7 @@ t_list *parse(
|
|||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
) {
|
) {
|
||||||
t_list *commands;
|
t_list *commands;
|
||||||
t_list *tokens;
|
// t_list *tokens;
|
||||||
t_command *command;
|
t_command *command;
|
||||||
char *command_str;
|
char *command_str;
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -48,7 +48,7 @@ t_list *parse(
|
|||||||
i = 0;
|
i = 0;
|
||||||
while (line[i] != '\0')
|
while (line[i] != '\0')
|
||||||
{
|
{
|
||||||
tokens = tokenize();
|
// tokens = tokenize();
|
||||||
command_str = extract_next_command(line, &i);
|
command_str = extract_next_command(line, &i);
|
||||||
if (command_str != NULL)
|
if (command_str != NULL)
|
||||||
{
|
{
|
||||||
@@ -155,8 +155,8 @@ static t_command *cmdnew(
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
set_argc(command);
|
set_argc(command);
|
||||||
set_infile(command);
|
// set_infile(command);
|
||||||
set_outfile(command);
|
// set_outfile(command);
|
||||||
set_path(command, minishell);
|
set_path(command, minishell);
|
||||||
return (command);
|
return (command);
|
||||||
}
|
}
|
||||||
@@ -233,19 +233,19 @@ static void set_argc(
|
|||||||
command->argc = argc;
|
command->argc = argc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_infile(
|
// static void set_infile(
|
||||||
t_command *command
|
// t_command *command
|
||||||
) {
|
// ) {
|
||||||
// test_infile
|
// // test_infile
|
||||||
command->infile = -1;
|
// command->infile = -1;
|
||||||
}
|
// }
|
||||||
|
|
||||||
static void set_outfile(
|
// static void set_outfile(
|
||||||
t_command *command
|
// t_command *command
|
||||||
) {
|
// ) {
|
||||||
// test_outfile
|
// // test_outfile
|
||||||
command->outfile = STDOUT_FILENO;
|
// command->outfile = STDOUT_FILENO;
|
||||||
}
|
// }
|
||||||
|
|
||||||
static void set_path(
|
static void set_path(
|
||||||
t_command *command,
|
t_command *command,
|
||||||
|
|||||||
@@ -118,7 +118,9 @@ char **get_envp(
|
|||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
env_list = ft_hashmap_entries(msh->variables.environment);
|
env_list = ft_hashmap_entries(msh->variables.environment);
|
||||||
envp = (char **)malloc((msh->variables.environment->size + 1) * sizeof(char *));
|
envp = (char **)malloc(
|
||||||
|
(msh->variables.environment->size + 1) * sizeof(char *)
|
||||||
|
);
|
||||||
if (envp != NULL)
|
if (envp != NULL)
|
||||||
{
|
{
|
||||||
i = 0;
|
i = 0;
|
||||||
|
|||||||
41
src/variables/environment_unset.c
Normal file
41
src/variables/environment_unset.c
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* environment_unset.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 22:16:00 by codex #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 22:16:00 by codex ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
|
void unset_env(
|
||||||
|
const char *env_name,
|
||||||
|
t_minishell *msh
|
||||||
|
){
|
||||||
|
t_hashmap *new_env;
|
||||||
|
t_list *entries;
|
||||||
|
t_list *current;
|
||||||
|
t_map_entry *entry;
|
||||||
|
|
||||||
|
new_env = ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||||
|
if (new_env == NULL)
|
||||||
|
return ;
|
||||||
|
entries = ft_hashmap_entries(msh->variables.environment);
|
||||||
|
current = entries;
|
||||||
|
while (current != NULL)
|
||||||
|
{
|
||||||
|
entry = current->content;
|
||||||
|
if (ft_strcmp(entry->key, env_name) != 0)
|
||||||
|
ft_hashmap_put(new_env,
|
||||||
|
ft_strdup(entry->key), ft_strdup(entry->value));
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
ft_lstclear_nodes(&entries);
|
||||||
|
ft_hashmap_clear(&msh->variables.environment, free);
|
||||||
|
msh->variables.environment = new_env;
|
||||||
|
}
|
||||||
204
tests/builtins_edge_cases.sh
Executable file
204
tests/builtins_edge_cases.sh
Executable file
@@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd -- "$(dirname -- "$0")/.." && pwd)"
|
||||||
|
MSH_BIN="$ROOT_DIR/minishell"
|
||||||
|
TMP_DIR="$(mktemp -d /tmp/minishell-builtins-tests.XXXXXX)"
|
||||||
|
|
||||||
|
PASS_COUNT=0
|
||||||
|
FAIL_COUNT=0
|
||||||
|
CASE_OUT=""
|
||||||
|
CASE_ERR=""
|
||||||
|
CASE_EC=0
|
||||||
|
CASE_NAME=""
|
||||||
|
CASE_FAILED=0
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
pass() {
|
||||||
|
printf "PASS: %s\n" "$1"
|
||||||
|
PASS_COUNT=$((PASS_COUNT + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
printf "FAIL: %s\n" "$1"
|
||||||
|
FAIL_COUNT=$((FAIL_COUNT + 1))
|
||||||
|
CASE_FAILED=1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_exit_code() {
|
||||||
|
local expected="$1"
|
||||||
|
if [ "$CASE_EC" -ne "$expected" ]; then
|
||||||
|
fail "$CASE_NAME -> exit code esperado $expected, obtenido $CASE_EC"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_err_contains() {
|
||||||
|
local text="$1"
|
||||||
|
if ! grep -Fq -- "$text" "$CASE_ERR"; then
|
||||||
|
fail "$CASE_NAME -> stderr no contiene: $text"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_out_contains() {
|
||||||
|
local text="$1"
|
||||||
|
if ! grep -Fq -- "$text" "$CASE_OUT"; then
|
||||||
|
fail "$CASE_NAME -> stdout no contiene: $text"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_out_regex_count() {
|
||||||
|
local regex="$1"
|
||||||
|
local expected="$2"
|
||||||
|
local count
|
||||||
|
|
||||||
|
count="$(grep -Ec -- "$regex" "$CASE_OUT" || true)"
|
||||||
|
if [ "$count" -ne "$expected" ]; then
|
||||||
|
fail "$CASE_NAME -> regex [$regex] esperado $expected, obtenido $count"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_case() {
|
||||||
|
local name="$1"
|
||||||
|
local input="$2"
|
||||||
|
local expected_exit="$3"
|
||||||
|
local clean_env="${4:-0}"
|
||||||
|
|
||||||
|
CASE_NAME="$name"
|
||||||
|
CASE_OUT="$TMP_DIR/$name.out"
|
||||||
|
CASE_ERR="$TMP_DIR/$name.err"
|
||||||
|
CASE_EC=0
|
||||||
|
CASE_FAILED=0
|
||||||
|
|
||||||
|
if [ "$clean_env" -eq 1 ]; then
|
||||||
|
printf "%b" "$input" | env -i PATH="$PATH" "$MSH_BIN" >"$CASE_OUT" 2>"$CASE_ERR"
|
||||||
|
else
|
||||||
|
printf "%b" "$input" | "$MSH_BIN" >"$CASE_OUT" 2>"$CASE_ERR"
|
||||||
|
fi
|
||||||
|
CASE_EC=$?
|
||||||
|
assert_exit_code "$expected_exit"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_pwd_deleted_dir_case() {
|
||||||
|
local tmpd
|
||||||
|
|
||||||
|
tmpd="$TMP_DIR/pwd_deleted_dir"
|
||||||
|
mkdir -p "$tmpd"
|
||||||
|
run_case "pwd_deleted_dir" \
|
||||||
|
"cd $tmpd\n/bin/rmdir $tmpd\npwd\nexit\n" \
|
||||||
|
1
|
||||||
|
assert_err_contains "minishell: pwd:"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if [ ! -x "$MSH_BIN" ]; then
|
||||||
|
printf "Compilando minishell...\n"
|
||||||
|
if ! make -C "$ROOT_DIR" >/dev/null; then
|
||||||
|
printf "No se pudo compilar minishell\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "exit_non_numeric" "exit abc\n" 2
|
||||||
|
assert_err_contains "numeric argument required"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "exit_sign_only" "exit +\n" 2
|
||||||
|
assert_err_contains "numeric argument required"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "exit_negative_wrap" "exit -1\n" 255
|
||||||
|
|
||||||
|
run_case "exit_too_many_args" "exit 7 8\npwd\nexit\n" 0
|
||||||
|
assert_err_contains "too many arguments"
|
||||||
|
assert_out_contains "$ROOT_DIR"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "cd_home_not_set" "cd\nexit\n" 1 1
|
||||||
|
assert_err_contains "HOME not set"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "cd_invalid_path" "cd /definitely/not/here\nexit\n" 1
|
||||||
|
assert_err_contains "No such file or directory"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "cd_dash_roundtrip" "cd /tmp\ncd -\npwd\nexit\n" 0
|
||||||
|
assert_out_contains "$ROOT_DIR"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_pwd_deleted_dir_case
|
||||||
|
|
||||||
|
run_case "echo_flags" "echo -nn hi\necho hi-there\nexit\n" 0
|
||||||
|
assert_out_contains "himinishell >"
|
||||||
|
assert_out_contains "hi-there"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "env_builtin_with_arg" "env EXTRA\nexit\n" 1
|
||||||
|
assert_err_contains "too many arguments"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "export_unset_cycle" \
|
||||||
|
"export FOO=bar\n/usr/bin/env\nunset FOO\n/usr/bin/env\nexit\n" \
|
||||||
|
0
|
||||||
|
assert_out_regex_count "^FOO=bar$" 1
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "env_builtin_lists_exported" \
|
||||||
|
"export FOO=bar\nenv\nunset FOO\nenv\nexit\n" \
|
||||||
|
0
|
||||||
|
assert_out_regex_count "^FOO=bar$" 1
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "export_invalid_identifier" "export 1A=2\nexit\n" 1
|
||||||
|
assert_err_contains "not a valid identifier"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "unset_invalid_identifier" "unset 1A\nexit\n" 1
|
||||||
|
assert_err_contains "not a valid identifier"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "export_without_value" "unset ZZZ\nexport ZZZ\n/usr/bin/env\nexit\n" 0
|
||||||
|
assert_out_regex_count "^ZZZ=$" 1
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\nResumen tests builtins: PASS=%d FAIL=%d\n" "$PASS_COUNT" "$FAIL_COUNT"
|
||||||
|
if [ "$FAIL_COUNT" -ne 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user